- About
- Getting Started
- Deployment
- Usage
- Built Using
- Contributing
- Authors
- Getting the certificate
- Working with git
- Working with code
Main repository for the final project for Class 09
https://www.optimumvoyage.com/
The platform is intended for ship (vessel) operators working in on-shore offices. It is to function as the bridge between our cloud based service, reportings from the ships and the onshore operators of the ships. The operators (users) should at a glance be able toget an overview of their full fleets position, activity and if needed a specific shipβs current status and history. The operator will eventually be able to interact with our cloud service, but the platform will to begin with be mostly a way to convey information from Optimum Voyage to the operators.
The operator is an onshore personal with the day to day responsibility for a shipβs execution of a given voyage. The operator needs to be aware of the shipβs position, fuel consumption, speed and the weather conditions it is experiencing. In collaboration, (to some extent), the operator is also the decision maker regarding route and voyage with the full overview of the economics of each.
Upon departure and arrival a ship will send a standardized email with voyage specifications. The ship will similarly every day at noon send a noon-report containing specifications on current position, speed, consumption and more.
https://www.figma.com/file/KJ60sPBKyAtC7vk8aX2wkd/OV
To get you started we have created a Database schema for you to follow. You can see a preview above or you can go to dbdiagram.io This schema is expected to change over the duration of the project, but since everyone is working on the same project it is important that you follow some guidelines if you need to change the DB.
- First make sure you actually need to change the DB. We have put a lot of effort into modelling the domain, so don't change it just for the sake of changing it, but on the other hand there will likely be things we haved missed.
- Write a migration using Knex.js that will perform the needed Schema updates. Make sure to test it thoroughly on your own branch.
- Take the file
/db/Schema.dbml
and copy into the editor on dbdiagram.io. Make changes according to what you changed in your migration. - Copy paste the updated DBML code from dbdiagram.io and add it back to the
/db/Schema.dbml
. - Export the diagram as pdf. Take a screenshot and use it to replace the file
/db/Schema.png
. - Make sure to save the diagram on your dbdiagram.io account and use the "Share" button to copy the url and paste it into this readme file in the section above.
- Commit the updated Readme, png file and DBML file.
- Make a pull request with the updated schema files along with your migration.
Fork the repo and clone the repo to your local machine using a terminal or a git client.
When running the API, you will automatically be running the API Documentation which is based on Swagger. Just go to localhost:3000/api/documentation to read the specifications for the API.
You can use your api token to authorize in Swagger and you will be able to perform test calls against the API directly from Swagger.
If you need to make changes to the specification, this can be done via the Swagger.json file.
Install it from here:
Windows: https://dev.mysql.com/downloads/windows/installer/
Mac: https://dev.mysql.com/doc/refman/5.7/en/osx-installation-pkg.html
Only thing Windows users have to do is run the following command anywhere in
their terminal (not necessarily the project folder):
npm install -g win-node-env
Refer to this post on stackoverflow for more info.
Refer to this points on how to install on your OS:
Run npm install
to install all required dependencies.
Follow these best practices for coding: https://github.com/HackYourFuture-CPH/curriculum/blob/master/review/review-checklist.md
This can be used as a checklist for every PR you make. Go through the list and see if you have used all the best practices
The Project comes pre setup with Prettier, which is a tool that will automatically format your code. Prettier is currently set up to format your code right before committing to git, so there should be no risk of someone committing something that does not live up to our coding style.
You may, however, optionally install the Prettier VS Code extension to format your code on every single save. This has the benefit that you will always be looking at code in the right format and you will know exactly what gets committed at any time.
In VS Code you can press CTRL+P (CMD+P on Mac) and type
ext install esbenp.prettier-vscode
to install the extension directly or you
can browser and find it in the Marketplace.
Remember to enable "format on save" in VS Code settings. You can also enable formatting on pasting or while editing, depending on your taste.
Environment variables are used for working with sensitive data like passwords and usernames.
Fx connecting to a database, we dont want to commit the code that shows our
password. Therefore we use environment variables instead. The .env
SHOULD
NEVER be commited!!!! Therefore it is in the .gitignore
file (ignored by
git).
Here is how to get started with the .env
file
- Copy and rename the
.env.example
file to.env
:
cp .env.example .env
- Open the
.env
file and update any parameters according to the environment you are running.
Working with sql we use a tool called knex which helps with writing queries and with changing the database structure. Check these out: https://www.quora.com/What-is-Knex-js https://knexjs.org/#Builder-identifier-syntax
- Install knex cli globally with:
npm i -g knex
- Update the connection details to the database in the
.env
file - after the server runs and you see a
connection to <db_name> db successful!
message you are ready to migrate tables - To make a new migration make sure you terminal is where the
knexfile.js
is. Otherwisecd
into that folder! runknex migrate:make MIGRATION_NAME
. Fx if i want to make a new users table i would runknex migrate:make users
. This would set up the migration file (under themigrations
folder) for me to write the users table. - To make a new seed i would run
knex seed:make 01_SEED_NAME
. Fxknex seed:make 01_USERS
. This would create a new seed file in the folderseeds
. - To then actually apply the migrations and the seeds, run
npm run db:setup
- If it was successful you should now have the tables and seeds in the database π
Command | Note |
---|---|
npm run build |
Build the project in production mode. |
npm start |
Runs build and starts the project (to be used on production servers). |
npm run client |
Runs the client application and serves it with webpack development server. |
npm run server |
Runs the server and serves it via Nodemon, meaning that the server will automatically be restarted when you make code changes. |
npm run dev |
Runs client and server concurrently. This is generally the command you run for development. |
npm run db:setup |
Runs knex migrate:latest and knex seed:run . Quick setup to get a fresh database ready for work. |
npm run do |
Utility that allows you to easily run cli commands on npm packages installed in your node_modules folder without having to install them globally. |
npm run storybook |
Run the Storybook user interface. |
Migrations are there to help manage your database when you are working in a team. The goal of a migration is to take your database from one place (state) to another. So it is bit like travelling. Traveling in time even?
We are trying to solve two main things with migrations:
- The pain of keeping the local database in your development environment up to date.
- The challenge of making schema changes to a database with real production data in a predictable manner.
Since are typically managed by writing ad hoc SQL to create the database, create new tables, change tables and change the data inside the database, migrations makes all of these tasks repeatable and predictable by basically adding all of these changes to git. Essentially the solution is to never manipulate the database schema directly with ad hoc queries, but always to out those queries in a script that is stored in git. Furthermore, since git allows you to "travel in time" by checking out past commits, changing branches, etc. we need a way for the database to keep up.
We do so by having two types of migrations. There are up migrations (π«) that describes how to travel somewhere and there are down migrations (π¬) that describe how to get back home again. The down migrations are key, since these are what allows you to take your database back to an old state whenever you are checking out an old commit or a branch from before a certain up migration was applied. A down migration must always be the exact opposite of an up migration. A couple of examples in pseudo code (not real code! refer to the Knex section to learn to write real migrations):
Creating tables:
up migration:
create table products
add columns
"name", string
"price", decimal
down migration:
drop table products
Renaming columns
up migration:
select table products
rename column
"name" > "product name"
down migration:
select table products
rename column
"product name" > "name"
In some cases you might have real data in your database that you need to transform in some way. Again you must be careful to write a down migration to revert those changes:
up migration:
select table products
select "name" from products
replace string "foo" to "bar"
down migration:
select table products
select "name" from products
replace string "bar" to "foo"
Now we have an idea what migrations are it is time to introduce Knex.js. We use Knex.js to facilitate migrations, but it does a few other things for us:
- Manage database connection
- Manage migrations and seed files
- Expose a CLI api to help scaffold migration files and run migration commands
- Provide an api to build SQL queries
Knex.js helps us connect to the database quickly. Take a look at the Knexfile.
The file contains some database credentials (which are referenced from environment variables for security reasons) and some configuration including setting a path to the seeds directory. This file is really all you need to start connecting to your database when using Knex.
So we learned a bit about migrations, but how do you use Knex to make them?
Knex works by scanning the /migrations
folder and looking for migrations that
have not yet been applied to the database. How does it keep track of this?
Easy. It has it's own table in the database knex_migrations
where migrations
are added once they have been run.
In addition to migrations which are generally concerned with updating the structure or the "schema" of your database, Knex.js provides something called "seeds". As the name suggest seeds are scripts that "plant" some data that can later grow to something bigger. Often when you are building an application you need to have some initial data in your application to test that everything works. That might be the initial user that is able to then create more users, organizations and so on, or it could be thousands of lines of data to load up the application with realistic content for testing purposes.
Make sure to always refer to the official Knex documentation for latest updates.
The project comes with the knex npm package pre-installed, but to use the api smoothly it is recommended to install knex globally with:
npm install -g knex
(you might have to prefix the command with sudo if you get a write access error)
Note: When running knex commands you must always do so from the /src/server/
directory since knex will look for your knexfile in the directory you are
currently in.
Here is a list of some important Knex commands to know:
Command | Note |
---|---|
knex migrate:latest |
Run "latest" migrations (i.e. migrations that have not previously been run on your database). |
knex migrate:rollback |
"Roll back" the latest migration. |
knex migrate:make migration_name |
Create a new migration file in the /migrations folder. |
knex seed:run |
Run the seed files from the seeds folder. |
In addition to the commands above, we have a npm script called
npm run db:setup
which is shorthand for running knex migrate:latest
followed
by knex seed:run
.
Knex provides a query builder interface. What it basically allows you to do is to use an api to generate SQL similar to this:
const users = await knex
.from("users")
.select("*")
.where({
email: "hello@test.com
});
Which will essentially generate the following SQL:
select * from `users` where `email` = 'hello@test.com'
In some cases using an API like that might be easier than writing raw SQL, as there is less risk of making mistakes when trying to put together dynamic SQL queries, concatenating strings and values from different sources. But on the other hand, it also requires learning a new API which may add additional overhead. We cannot provide the API documentation here, but you can find all the instructions you need at http://knexjs.org/. Furthermore this Cheat sheet might be helpful.
Using query builders like Knex, or even Object-Relation Mappers (ORMs), is subject to much debate among developers. Some believe it is worth it learning to use an API to interact with the database, others believe it is more value to become good at using SQL, rather than abstracting it away. Using Knex' query builder functionalities are not mandatory for this project, but we find that the API is simple enough that it might be helpful. Using migrations, however, is a mandatory part of this project.
If you do not wish to use the knex query builder api, you can always use the
command knex.raw()
to execute "raw" SQL statements against your database while
still leveraging Knex to connect to the database:
knex.raw('select * from `users` where `email` = "hello@test.com"');
Note: previously we were working with forks, but changed the workflow, so now you should be working directly on the main repo instead. This guide is now updated to the current workflow. To see the old workflow, refer to this version of the readme.
Working with git is a HUGE part of the final project. This is how you should be working with git in the final project:
This repo has two branches:
master
- Used for deployment. This branch should be as clean as is possible. NEVER directly commit to this branch!develop
- A branch where we do all development. That means that all feature branches should be merged into this branch. Very important!
Lets get started with our first feature. Lets say we should build a cookies popup:
-
Clone this repo to get a local copy of this repository.
-
BEFORE WE WRITE ONE SINGLE LINE OF CODE, create the feature branch where we will do all our cookies popup development. First make sure that you are branching out from develop:
git checkout develop
, then create a new branch:git checkout -b feature/cookies-popup
.- If you fail to checkout develop and branch out from master or some other branch, you will see strange changes pop up when you try to make your pull request and merge it into develop. This means you have to "move" all of your changes from this branch to a "fresh" one that has been branched off develop, which is a pain. This is probably the most common beginners mistake when using git!
- The
feature/
prefix is not mandatory for this project, but is used by many comapnies and is a good habit to indicate that this branch will add a new "feature" containing a cookie popup. - In contrast you can prefix a branch with
hotfix/
to indicate that you are fixing an urgent bug that may need to bypass some of the regular testing procedures in order to get onto production quickly (i.e. if production is broken and you are fixing it at 4am). In many companies thehotfix/
prefix will actually automatically bypass some of the QA steps. Since we do not have our product in production while developing we will not need to use hotfixes, though.
-
Now we write all our code in the
cookies-popup
branch. -
Add the files you changed to your git staging area so they are ready to be committed. First do
git status
to make sure that you only changed the files you intended to. If only the files that you intended to commit are listed, feel free to usegit add -A
to add all changed files to the staging area. If you have some temporary file changes that you do not wish to include in the current commit, add the files individually withgit add [path-to-file-you-wish-to-commit]
(repeat for each file).- When adding to git you are basically "composing your commits". Think about keeping commits fairly small, logically grouped and try to make sure that every commit is in a "working state" (i.e. your new feature is obviously not going to work before you have all the commits ready for that feature, but if you are breaking existing functionality along the way - for example by renaming a function - make sure to clean up the mess you made withing the same commit - for example by refactoring all of the calls to that function you renamed). If all commits are "working state" that can help immensely in the case that we deploy a breaking change to production and need to find a suitable commit to roll back to.
-
When you have decided what to commit and added it to the staging area we are finally ready to commit. Think about a good commit message and type
git commit -m "[your-commit-message]"
.- The commit message should communicate how you have changed the codebase in a concise manner.
- The first line in your commit message should be kept under 50 characters. This makes it a lot easier to skim the commit log.
- If you need to add a longer message, make a 50 character headline, skip a line and then make sure that each consecutive line is no longer than 72 characters. This keeps everything neat and readable when people are reading the commit log, no matter if it is in the terminal, on github or somewhere else.
- Avoid unneccessary fill words (at least in the title where space is precious) and try to write in an imperative mood (i.e. as if you were giving commands). You are not writing a novel.
- Humor and cheerful comments are allowed but should not be taking away from communicating what the contents of your commit is.
-
After committing ensure that your working copy is clean (i.e. all changes have been committed) or that only the files that you chose to exclude in step 4 are left uncommitted:
git status
. If you really want to be sure that your commit was succesful, you can review the git log withgit log
(pressq
to exit). -
Push your changes to a branch on github with
git push
.-
The first time git will complain that you have not set the upstream "origin". I usually just run
git push
and copy the suggestion after the error message, to make sure that I don't mistype the branch name on remote. But you can dogit push --set-upstream origin feature/cookies-popup
if you're really into typing ;) -
If you push your changes regularly - even when you are not ready to do a pull request - it makes life easier for everyone. Other students can jump on your branch to see what you are doing in order to understand how to design their features so it all works together and mentors will have an easier time helping you out if they can check out your code and perform debugging on their own machine.
-
-
Repeat step 3-6 for each commit until your feature is done.
-
Once your feature is done, make sure to test the functionality thoroughly. Then create a pull request. If you have recently pushed to github, a yellow bar will pop up suggesting that you make a pull request from that branch. If that is not the case, just press the "New pull request" button, choose your branch as "compare" and "develop" as "base". Before you create the pull request you must read through the "Files changed" section and ensure that you are not committing something by accident.
- A pull request is an indication to the team that you want to merge a new feature (or hotfix) into develop. Creating a pull request implies that you have tested your code and that you have read your file diff looking for obvious mistakes. If fail to do so, your pull request will (hopefully) be rejected, but it also means that you just wasted the time of the reviewer who will have to spend a little time to understand the pull request even if it contains obvious mistakes. Since you understand the work you are committing in depth, obvious mistakes should be much faster for you to pick out.
- "Work in progress pull requests". In some cases you may want to create a pull request before finishing a feature (i.e. you are waiting for code from someone else, but your code can be merged without this feature and without breaking develop). If this is the case, create a pull request, use the description field to explain that you are going to finish the work in another branch and of course keep the issue open in Trello.
- "Feedback needed pull requests". Pull requests provides a great interface for getting feedback on code directly. In some cases you might be stuck and needing another set of eyes on what you are doing even if you are far from getting the feature done and even if your current commit is broken. In that case commit what you have, type "WIP" in your commit message and edit the title of the pull request so it says "DO NOT MEGE: [Title]". Then contact a mentor or a student who may be able to help you and get them to look over the pull request. This way the code is not merged by accident in a non-working state.
- If you see open pull requests, help a fellow student and don't hesitate to review them yourself. Every pull request needs 2 approvals to be merged. One of them must be from a mentor. Even if a pull request already has an approval from another student, you may still have relevant feedback. Here is a nice checklist with everything to look out for when reviewing other peoples code. And since everyone will be held to the same standards, make sure to keep it in mind when you are writing code as well ;)
-
Always check the pull requests tab regularly - ideally multiple times a day.
- If you have a pull request open you may want to check back to see if someone has requested changes or the pull request has been merged.
- If you don't adress requested changes fairly quickly, other pull requests will be merged before yours increasing the risk that you will have merge conflicts in your code. Merge conflicts are inevitable, but the faster you get to merge, the lower the risk.
- If you do not have any open pull requests you still need to check the pull requests tab to stay on top of what is happening in the code base. Got to the "closed" tab to read what has been merged recently. Failing to read other peoples pull requests is one of the most common reasons for "falling behind" on a software team.
-
If your pull request has been merged, check out develop the develop branch, pull latest changes and make sure it still works before closing your task in Trello. Some times features break when different peoples code "meet" on develop.
- Q) How to figure out if I am hooked up to a fork or a branch?
- A) Run `git remote -v` and examine the "origin" path. You should be working directly on the main repo and it should look like this:
origin git@github.com:HackYourFuture-CPH/ov-class09-fp.git (fetch)
origin git@github.com:HackYourFuture-CPH/ov-class09-fp.git (push)
- Q) Why do I get merge conflicts?
- A) Whenever you merge two branches, git will look at the changesets between the two branches and try to determine if they can be merged automatically. Generally if all of your changes are in files nobody else have edited this will always be resolved automatically. If there are changes to the same files it will depend on whether someone has edited the same line as you (including adding a trailing comma to a json object or maybe changing the order or indentation of some lines). Merge conflicts are natural, but can be kept to a minimum by planning work so there is a minimum of overlap between the files you are working in and by only changing as "little as is needed" when you edit code. If people are working in different code styles (i.e. one person likes `"` another likes `'` or one person uses tabs and another uses spaces), that's a huge source of merge conflicts. Fortunately we have Prettier - a little dictator that runs on every commit and makes sure code style is always consistent - so we should not have many merge conflicts as a result of disagreement over code style.
- Q) Why do I get merge conflicts when I run `git pull` - I am not merging anything?
- A) Yes you are :) `git pull` is equivalent to running `git fetch && git merge origin/develop`.
- Q) How to resolve merge conflicts?
- A) Here is a good strategy:
- First, run `git status` to see what files have merge conflicts in them.
- For each file with conflicts your will have to choose between "Current changes" (your stuff) and "Incoming changes" (stuff somebody else added).
- It is recommended to use a good merge tool to get an overview of changes and to make sure you don't leave something like `>>>>> 0004040387473948274` behind in your code.
- Github now offers a tool directly in github that allows you to solve merge conflicts. This is great for getting a quick overview of the conflicts, but since Prettier will not run if you are committing directly from the github interface, DO NOT use github to solve conflicts.
- Review each piece of code and decide whether one of them should replace the other, whether you need both or maybe something needs to be rewritten altogether because the other person touching this file had entirely different assumptions about how to build things than you had.
- If it is a complex conflict, reach out to the other person and try to reach agreement on how to solve the coflict.
- If it is just a matter of simple changes, like two people adding an extra line to a json object and the conflict being over whether there should be a trailing comma or not, fix it yourself.
- If you are in doubt, _always reach out to a fellow student or a mentor_.
- Make sure to go over all of the files with conflicts and check that there are not merge tags (i.e. `<<<<<<< HEAD`) left behind.
- Test that your code is still running. Test any features implicated in the files that had conflicts.
- When you have a merge conflict, git is in a state of being "in the middle of a commit". If you run `git status` you can confirm that you are in the middle of a merge.
- You must tell git that you have fixed the merge conflict by adding the conflicted files to the staging area with `git add [file-path]` for each of the implicated files.
- Finally run `git commit`. This will finish the commit and ommitting the `-m` option will allow git to automatically write a merge commit messages with description of what files you have solved conflicts in.
- ALWAYS run code before a PR is made!
- No committing
console.log
- No committing merge conflicts!
- Work in seperate files! When you make a new feature, the first thing you do is create a new file for that (of course if it makes sense). Especially for components.
There are two servers running:
- Staging server - To deploy push to the
develop
branch - Production server - To deploy push to the
master
branch
- MySQL - Database
- Express - Server Framework
- React - Web Framework
- NodeJs - Server Environment
- Knex - Database management
To get the certificate we need to make sure that you know our stack, for us to recommend you to another company. So how do we figure this out? We look through homework and we look through the commits you create in the final project.
Its important to say that you dont need to be an expert in everything, not at all!! You can be more interested in the frontend or the backend, but we then need to see that interest.
But if you are missing 4 react homeworks and the one you have committed is super basic. AND we dont see some more advanced react usage, well then it becomes super hard for us to recommend you to a company that does react. Because we simply dont know if you have the skills.
If we fell there are gaps, we will talk with you as early as possible, so you have time to show your worth!
If it comes so far that we cannot give you a certificate you can either join for the next final project or make a project (or we give you a project) where you show your skills. We truly want to give you the certificate and will help you as much as we can to get you there!
A little more specific here is what we are looking for:
- Semantic html
- Styling
- Responsiveness
- Layout
- If it looks pretty DOES NOT MATTER!
- Javascript
- Variable naming
- Function scope
- ES6
Classes
,promises
,map
,filter
and all that shabang
- Node
- Express
- Nice structure
- Can create an api using the database. CRUD
- Everything modularized (Split into smaller parts)
- Database
- Create tables
- Queries
- React
- Using the react way of developing, SUPER IMPORTANT! https://reactjs.org/docs/thinking-in-react.html
- Knows how to use state and prop
- Well designed components
- Can use the api created from node