{"id":19693047,"url":"https://github.com/mdeket/cheap-flights","last_synced_at":"2025-04-29T09:31:36.683Z","repository":{"id":44853271,"uuid":"255407065","full_name":"mdeket/cheap-flights","owner":"mdeket","description":"SpringBoot app with Neo4j \u0026 H2 that uses Dijkstra algorithm to find the cheapest flight.","archived":false,"fork":false,"pushed_at":"2022-01-21T23:41:09.000Z","size":15257,"stargazers_count":5,"open_issues_count":2,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-05T14:42:03.072Z","etag":null,"topics":["apoc","docker","jenkins","neo4j","springboot","terraform"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mdeket.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-04-13T18:13:33.000Z","updated_at":"2025-02-04T07:45:39.000Z","dependencies_parsed_at":"2022-08-29T17:30:25.942Z","dependency_job_id":null,"html_url":"https://github.com/mdeket/cheap-flights","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdeket%2Fcheap-flights","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdeket%2Fcheap-flights/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdeket%2Fcheap-flights/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mdeket%2Fcheap-flights/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mdeket","download_url":"https://codeload.github.com/mdeket/cheap-flights/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251473329,"owners_count":21595041,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apoc","docker","jenkins","neo4j","springboot","terraform"],"created_at":"2024-11-11T19:15:33.237Z","updated_at":"2025-04-29T09:31:34.712Z","avatar_url":"https://github.com/mdeket.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Flight Advisor a.k.a cheap-flights\n#### API with functionality to find the cheapest flight between two airports.\n\n## Getting started - project description\nApplication has two databases, H2 in-memory relational database and Neo4j graph database. Graph database\nis used because routes between airports and airports themselves can be represented as a graph where airports represent graph nodes\nand routes are connections between nodes. By using Neo4j we can get all kinds of additional functionality since Neo4j has plugins that \ninclude different kinds of search algorithms(e.g. dijkstra and A*). For this case APOC library was used. More about APOC can be found [here](https://neo4j.com/docs/labs/apoc/current/).\nI've used dijsktra this time and the price between airports was used as weight(cost function).\n\nOne of the reasons that I used Neo4j and is because of it's browser gui. If you start the app using dev or docker profile\n(explained below) you will be able to go to http://localhost:7474/browser and using these credientals (neo4j:secret) \nyou will be logged in. Import the initial data (also explained below) and watch how Neo4j represents all the graphs. If you want to \ntest it, Neo4j uses Cypher as its query language. Query for finding the cheapes flight can be found in _AirportGraphRepository_ as value in Query annotation.  \n\n#### Postman collection\nPostman collection is included, so for easier testing import postman collection and environment. Both of these files can be found in \n_postman_ folder in the project root.\nWhen calling ```/authenticate``` endpoint response will be automatically parsed and inserted into access_token variable in postman's environment.\n\n### H2 in-memory\nFlyway was used to handle migrations. It stores data in a file on disk, so data would be persisted longer than in memory (easier for testing).\n\n### Neo4j \nNeo4j doesn't support plugins like APOC if it's used as embedded server, so I had to run with docker and pass plugin to a container.\nSince plugin is needed each time the container has been run I created a new image with ````docker/neo4j.dockerfile````.\n\n### Security\nStandard Spring Security package was used with JWT tokens. User can only have one role, ADMIN or USER. Based on \nrole user can consume some endpoints or not. \n\n### Spring Logging Aspect\nUsed for logging function calls in ````@RestControllers````. Easier for debugging.\n\n### Initial data - routes and airports\nCSV files with ORACLE format are supported. There are two endpoint for importing data:\n1. /api/v1/import/airports\n2. /api/v1/import/routes\n\nBoth endpoint return a response immediately, so they are non-blocking endpoint and file parsing is done in \nnew thread since it can take some time to parse large amount of routes and airports and client could get \"Connection timeout\" which I\nwanted to avoid. There is also one more endpoint which is useful when you import new airports and routes, and that endpoint is:\n````\n/api/v1/import/status?filename=routes.txt\n````\nWhere as query param you can pass filename that you've uploaded and as a response you can get **DONE** or **PARSING**.\n\nI've tested importing of inital data with airports.txt and routes.txt. They can be found in _data_ folder in project root.\n\n## How to run it?\n\nThree spring profiles are included in the project: \n\n1. **dev**\n2. **docker**\n3. **prod**\n\nFor each profile an admin user is created.\n```\nUsername: ironman\n\nPassword: test1234\n```\n#### **Dev** profile\nIn order to run it with 'dev' profile you need to set value of environment variable ```spring_profiles_active``` to ```dev```. \nThat is one way to do it, or if you use IntelliJ as IDE you can set 'Active profiles' in run configuration. This \nprofile will run the app on port 8080. In order to run Neo4j you can use this command to start docker with all necessary variables:\n```\ndocker run --rm -e NEO4J_AUTH=neo4j/secret -p 7474:7474 -v $PWD/plugins:/plugins -e NEO4J_dbms_security_procedures_unrestricted=apoc.\\\\\\* -p 7687:7687 neo4j:3.5\n```\n\n### **Docker** profile\nUsing docker-compose run both, database and api, with docker. Since these two docker images whose dockerfiles can be found in docker folder\nrequire a couple of environment variables. In order to make this easier for you I created a small shell script which will do everything for you.\nThe script is in project root and can be run with:\n```\n./docker-compose.sh\n```\nJust don't forget to make the script executable. If you are on linux user this command:\n```shell script\nchmod +x docker-compose.sh\n```\nIf you have completed all this you can use postman, curl or any other tool to send request to the API. The same as in dev profile app is on port 8080 and database gui can be found on port 7474.\n\n### **Prod** profile\nThis profile is used for AWS. The whole project is deployed on AWS using ECS services, Secrets Manager and ECR.\nAs CI/CD I used jenkins. After the build Jenkins will push the new version of docker container to ECR. ECS service will read that event and try to replace an existing container with the new one.\n\nIn this case you can't use Neo4j's GUI. It can only be reached and login screen opened on the same address with 7474 port,\n but in order to authenticate and continue using it Neo4j GUI client uses their own Bolt protocol which relies on websocket, but \n my current AWS infrastructure supports only HTTP so you will get an error.  \n\n### Jenkinsfile \u0026 Jenkins\nJenkins has been set up on a clean AWS EC2 instance with IAM role that has access to ECR. A pipeline project on Jenkins is used for this project. Jenkins has a webhook on GitHub push events, so every time a change is pushed to GitHub, Jenkins will pull _master_ \nbranch, build database and api containers and push them to ECR.\n\n### Terraform\nHowever, not 100% finished, only networking is ready, there are still ECS tasks, services and roles missing.\n\n## Next steps\n* Add pagination on GET /api/v1/cities\n* Finish terraform so project could easily be transferred from one AWS account to another. \n* Split the API into two microservices, the one with users and cities and, and the other one with the cheapest flight calculation classes and neo4j/graph classes. \nThe project structure is already \"separated\" with _graph_ folders inside some packages.\n\n\n\n \n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmdeket%2Fcheap-flights","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmdeket%2Fcheap-flights","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmdeket%2Fcheap-flights/lists"}