├── .gitignore
├── .mvn
└── wrapper
│ ├── maven-wrapper.jar
│ └── maven-wrapper.properties
├── README.md
├── assets
├── main.png
└── photo.png
├── insomnia
└── Insomnia_2024-03-08.json
├── mvnw
├── mvnw.cmd
├── pom.xml
├── src
├── main
│ ├── java
│ │ └── com
│ │ │ └── spring
│ │ │ └── myfood
│ │ │ ├── MyfoodApplication.java
│ │ │ ├── config
│ │ │ ├── CorsConfig.java
│ │ │ ├── MongoConfig.java
│ │ │ └── SwaggerConfiguration.java
│ │ │ ├── controller
│ │ │ ├── MainController.java
│ │ │ ├── ProductController.java
│ │ │ └── RankingController.java
│ │ │ ├── dtos
│ │ │ ├── request
│ │ │ │ └── RequestProductDTO.java
│ │ │ └── response
│ │ │ │ └── ResponseSearchFoodDTO.java
│ │ │ ├── enums
│ │ │ ├── FoodCategoryEnum.java
│ │ │ └── RankingTypeEnum.java
│ │ │ ├── model
│ │ │ ├── Product.java
│ │ │ └── Ranking.java
│ │ │ ├── mongo
│ │ │ └── MyFoodMongo.java
│ │ │ ├── repository
│ │ │ ├── ProductRepository.java
│ │ │ └── RankingRepository.java
│ │ │ ├── service
│ │ │ ├── ProductService.java
│ │ │ └── RankingService.java
│ │ │ └── utils
│ │ │ └── GlobalExceptionHandler.java
│ └── resources
│ │ └── application.properties
└── test
│ └── java
│ └── com
│ └── spring
│ └── myfood
│ └── MyfoodApplicationTests.java
└── todo.todo
/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | target/
3 | !.mvn/wrapper/maven-wrapper.jar
4 | !**/src/main/**/target/
5 | !**/src/test/**/target/
6 |
7 | ### STS ###
8 | .apt_generated
9 | .classpath
10 | .factorypath
11 | .project
12 | .settings
13 | .springBeans
14 | .sts4-cache
15 |
16 | ### IntelliJ IDEA ###
17 | .idea
18 | *.iws
19 | *.iml
20 | *.ipr
21 |
22 | ### NetBeans ###
23 | /nbproject/private/
24 | /nbbuild/
25 | /dist/
26 | /nbdist/
27 | /.nb-gradle/
28 | build/
29 | !**/src/main/**/build/
30 | !**/src/test/**/build/
31 |
32 | ### VS Code ###
33 | .vscode/
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LadyJessie19/MyFood/7dce8c6d131eb4498e4a9ff366ba29dffaf78bde/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.5/apache-maven-3.9.5-bin.zip
2 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MyFood - Food Ranking 🍔🍕🌭
2 |
3 | ## Discover & Rate Your Favorite Foods
4 |
5 | ### MyFood is a platform that allows users to explore trending foods and rate their favorites, creating a dynamic food ranking system.
6 |
7 |
8 |
9 | **Project Name:** MyFood 🍔🍕🌭
10 |
11 | **Development Start Date:** 03/02/2024 ⏩
12 |
13 | **Development End Date:** 11/03/2024 🏁
14 |
15 | **Status:** Completed ✅
16 |
17 | **Technologies used:** ☕ Spring Boot | 🍃 MongoDB | 🚈 Railway | 🪂 Swagger | 🟣 Insomnia
18 |
19 | 🚈 **Railway Deploy Link:** [MyFood Deploy](https://app-myfood-production.up.railway.app)
20 |
21 | 🪂 **Swagger Link:** [MyFood Swagger](https://app-myfood-production.up.railway.app/swagger-ui/index.html#/)
22 |
23 | 🟣 **Insomnia Import File Link:** [MyFood Insomnia](https://github.com/LadyJessie19/MyFood/tree/main/insomnia/Insomnia_2024-03-08.json)
24 |
25 | ## Project Description 📝
26 |
27 | MyFood is a fictional app that implements a ranking system. The purpose of this project is to showcase the most searched foods and their associated scores within the fictitious app named MyFood.
28 |
29 | ## Key Features 🔧
30 |
31 | - Implementation of a food ranking system.
32 | - Display of most searched foods.
33 | - Integration with MongoDB for data storage.
34 |
35 | ## Railway Deploy/MongoDB Notice ⚠️
36 |
37 | Please note that MongoDB or Railway services may be temporarily unavailable due to inactivity or other technical reasons. If this happens, you may need to clone the repository to your local machine and connect to another MongoDB database.
38 |
39 | ## Installing the Project 🛠️
40 |
41 | Clone the repository:
42 |
43 | ```bash
44 | git clone https://github.com/LadyJessie19/MyFood.git
45 | ```
46 |
47 | ## Running the Application 🚀
48 |
49 | To start the application:
50 |
51 | 1. Navigate to the project directory.
52 | 2. Run the following command to build and start the application:
53 |
54 | ```bash
55 | mvn spring-boot:run
56 | ```
57 |
58 | This will start the application on the default port, typically `8080`. For development purposes, I used the port `8081`.
59 |
60 | ## How to import Insomnia File 📥
61 |
62 | 1. **Download the Insomnia file;**
63 | 2. **Open Insomnia;**
64 | 3. **Import the File:**
65 | - Click on the **`Import/Export`** button in the top-right corner of the Insomnia window.
66 | - Select **`Import Data`** from the dropdown menu.
67 | - Choose **`From File`** and browse to the location where you downloaded the Insomnia file.
68 | - Select the file and click **`Open`** to import it.
69 | 4. **Start Testing;**
70 |
71 | Once imported, you will see the collection of pre-configured requests in Insomnia. You can now start using them to test MyFood API.
72 |
73 | > **Note:** Make sure to set up the environment variable `base_url`. If you're running the project locally the default value is `http://localhost:8080`, but if you're running the project on Railway the default value is `https://app-myfood-production.up.railway.app`.
74 |
75 | ## Development Team 🙋♀️
76 |
77 |
78 |
79 | - Developer: [Jessie M Bentes](https://github.com/LadyJessie19)
80 |
81 | ## How to Contribute 🤝
82 |
83 | If you want to contribute to the project, follow these steps:
84 |
85 | 1. Fork the project.
86 | 2. Create a new branch (`git checkout -b feature/new-feature`).
87 | 3. Commit your changes (`git commit -am 'Add new feature'`).
88 | 4. Push to the branch (`git push origin feature/new-feature`).
89 | 5. Create a new Pull Request.
90 |
91 | ## License 🧐
92 |
93 | This project is licensed under the [MIT License](https://opensource.org/licenses/MIT).
94 |
--------------------------------------------------------------------------------
/assets/main.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LadyJessie19/MyFood/7dce8c6d131eb4498e4a9ff366ba29dffaf78bde/assets/main.png
--------------------------------------------------------------------------------
/assets/photo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LadyJessie19/MyFood/7dce8c6d131eb4498e4a9ff366ba29dffaf78bde/assets/photo.png
--------------------------------------------------------------------------------
/insomnia/Insomnia_2024-03-08.json:
--------------------------------------------------------------------------------
1 | {"_type":"export","__export_format":4,"__export_date":"2024-03-08T15:48:07.451Z","__export_source":"insomnia.desktop.app:v2023.5.8","resources":[{"_id":"req_8cfc65166ef14c848eb1ff89fb1069f7","parentId":"fld_de3307efbe8d4c7cbfd86e8a4ef3b341","modified":1709912747173,"created":1709912677628,"url":"{{ _.base_url }}/health","name":"Health Check","description":"","method":"GET","body":{},"parameters":[],"headers":[{"name":"User-Agent","value":"insomnia/2023.5.8"}],"authentication":{},"metaSortKey":-1709912746945,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"fld_de3307efbe8d4c7cbfd86e8a4ef3b341","parentId":"wrk_9fc76dd77d0a4c34bc9f93dab8300b4b","modified":1709912674252,"created":1709912674252,"name":"Health","description":"","environment":{},"environmentPropertyOrder":null,"metaSortKey":-1709912674252,"_type":"request_group"},{"_id":"wrk_9fc76dd77d0a4c34bc9f93dab8300b4b","parentId":null,"modified":1708548826518,"created":1708548826518,"name":"MyFood","description":"","scope":"collection","_type":"workspace"},{"_id":"req_ce7c673aa7ca420b90a645c7dfa9203b","parentId":"fld_2a55ad4354424cb197b47c4eb253e3b9","modified":1709750763264,"created":1708549115422,"url":"{{ _['base_url'] }}/products/all","name":"List All Foods","description":"","method":"GET","body":{},"parameters":[{"id":"pair_ab9b10e8ea494744bd7909a21a4b2fd7","name":"category","value":"FRUIT","description":"","disabled":true}],"headers":[{"name":"User-Agent","value":"insomnia/2023.5.8"}],"authentication":{},"metaSortKey":-1708549203830,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"fld_2a55ad4354424cb197b47c4eb253e3b9","parentId":"wrk_9fc76dd77d0a4c34bc9f93dab8300b4b","modified":1708549112053,"created":1708549112053,"name":"Products","description":"","environment":{},"environmentPropertyOrder":null,"metaSortKey":-1708549112053,"_type":"request_group"},{"_id":"req_5bb2e7268148451091c804f163115a04","parentId":"fld_2a55ad4354424cb197b47c4eb253e3b9","modified":1709910246724,"created":1708549223434,"url":"{{ _['base_url'] }}/products/register","name":"Register New Food","description":"","method":"POST","body":{"mimeType":"application/json","text":"{\n\t\"name\": \"Mussarela\",\n\t\"description\": \"Lonelly people pizza\",\n\t\"price\": 26,\n\t\"image\": \"https://media.istockphoto.com/id/1168754685/pt/foto/pizza-margarita-with-cheese-top-view-isolated-on-white-background.jpg?s=612x612&w=0&k=20&c=zOBq4jmDcRY9zauo-sfrBwOPbcTJhnFgavKq5CXmLCo=\",\n\t\"category\": \"PIZZA\"\n}"},"parameters":[],"headers":[{"name":"Content-Type","value":"application/json"},{"name":"User-Agent","value":"insomnia/2023.5.8"}],"authentication":{},"metaSortKey":-1708549203730,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"req_de7ef79a7b75409c9a47d0980beac47d","parentId":"fld_2a55ad4354424cb197b47c4eb253e3b9","modified":1709909499319,"created":1708553514654,"url":"{{ _['base_url'] }}/products/65eb262c8b90dd3be2ea7c9b","name":"Find Food By Id","description":"","method":"GET","body":{},"parameters":[],"headers":[{"name":"User-Agent","value":"insomnia/2023.5.8"}],"authentication":{},"metaSortKey":-1708549203630,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"req_ae9834a730e34dac86d0463e85728170","parentId":"fld_4b3bd3b27bce4ac3a3c581d6481c8272","modified":1709910365358,"created":1708549058793,"url":"{{ _['base_url'] }}/rankings/search","name":"Search Foods","description":"","method":"GET","body":{},"parameters":[{"id":"pair_a6ebc7fdea744e18a9f0034f4f087e87","name":"foodTitle","value":"mussarela","description":""}],"headers":[{"name":"User-Agent","value":"insomnia/2023.5.8"}],"authentication":{},"metaSortKey":-1708549058993,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"fld_4b3bd3b27bce4ac3a3c581d6481c8272","parentId":"wrk_9fc76dd77d0a4c34bc9f93dab8300b4b","modified":1708549018452,"created":1708549018452,"name":"Rankings","description":"","environment":{},"environmentPropertyOrder":null,"metaSortKey":-1708549018452,"_type":"request_group"},{"_id":"req_9694503a22224adb9a5035733e8e08c9","parentId":"fld_4b3bd3b27bce4ac3a3c581d6481c8272","modified":1709739037949,"created":1708548927389,"url":"{{ _['base_url'] }}/rankings/top-foods","name":"Most Searched Foods","description":"","method":"GET","body":{},"parameters":[],"headers":[{"name":"User-Agent","value":"insomnia/2023.5.8"}],"authentication":{},"metaSortKey":-1708549058893,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"req_a3625288aeaa44fc901c35071db036d3","parentId":"fld_4b3bd3b27bce4ac3a3c581d6481c8272","modified":1709739044896,"created":1708983188697,"url":"{{ _['base_url'] }}/rankings/top-categories","name":"Most Searched Categories","description":"","method":"GET","body":{},"parameters":[],"headers":[{"name":"User-Agent","value":"insomnia/2023.5.8"}],"authentication":{},"metaSortKey":-1708549058818,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"req_40d8b2bb692e4a669c4f52e02b8023d1","parentId":"fld_4b3bd3b27bce4ac3a3c581d6481c8272","modified":1709909538111,"created":1708549296723,"url":"{{ _['base_url'] }}/rankings/65eb26488b90dd3be2ea7c9c","name":"Check Ranking Score By Id","description":"","method":"GET","body":{},"parameters":[],"headers":[{"name":"User-Agent","value":"insomnia/2023.5.8"}],"authentication":{},"metaSortKey":-1708549058743,"isPrivate":false,"settingStoreCookies":true,"settingSendCookies":true,"settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingRebuildPath":true,"settingFollowRedirects":"global","_type":"request"},{"_id":"env_09ceb19816ae6c59e40195e08ab0cf8881e7e305","parentId":"wrk_9fc76dd77d0a4c34bc9f93dab8300b4b","modified":1708548837362,"created":1708548826719,"name":"Base Environment","data":{},"dataPropertyOrder":{},"color":null,"isPrivate":false,"metaSortKey":1708548826719,"_type":"environment"},{"_id":"jar_09ceb19816ae6c59e40195e08ab0cf8881e7e305","parentId":"wrk_9fc76dd77d0a4c34bc9f93dab8300b4b","modified":1709581028936,"created":1708548826721,"name":"Default Jar","cookies":[{"key":"JSESSIONID","value":"659DD62F7E0628104D4D6E6B61ED4B4E","domain":"localhost","path":"/","httpOnly":true,"hostOnly":true,"creation":"2024-03-04T19:37:08.935Z","lastAccessed":"2024-03-04T19:37:08.935Z","id":"8693064594501603"}],"_type":"cookie_jar"},{"_id":"env_ce2b9d51216e496683b00fdaa93d644e","parentId":"env_09ceb19816ae6c59e40195e08ab0cf8881e7e305","modified":1709739005378,"created":1708548842155,"name":"Local","data":{"base_url":"http://localhost:8081"},"dataPropertyOrder":{"&":["base_url"]},"color":"#ec0404","isPrivate":false,"metaSortKey":1708548842155,"_type":"environment"},{"_id":"env_ced63c56361d4db4b5689b210f6ff566","parentId":"env_09ceb19816ae6c59e40195e08ab0cf8881e7e305","modified":1709912722879,"created":1709738922079,"name":"Deploy","data":{"base_url":"https://app-myfood-production.up.railway.app"},"dataPropertyOrder":{"&":["base_url"]},"color":"#7d69cb","isPrivate":false,"metaSortKey":1709738922079,"_type":"environment"}]}
--------------------------------------------------------------------------------
/mvnw:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # ----------------------------------------------------------------------------
3 | # Licensed to the Apache Software Foundation (ASF) under one
4 | # or more contributor license agreements. See the NOTICE file
5 | # distributed with this work for additional information
6 | # regarding copyright ownership. The ASF licenses this file
7 | # to you under the Apache License, Version 2.0 (the
8 | # "License"); you may not use this file except in compliance
9 | # with the License. You may obtain a copy of the License at
10 | #
11 | # https://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing,
14 | # software distributed under the License is distributed on an
15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | # KIND, either express or implied. See the License for the
17 | # specific language governing permissions and limitations
18 | # under the License.
19 | # ----------------------------------------------------------------------------
20 |
21 | # ----------------------------------------------------------------------------
22 | # Apache Maven Wrapper startup batch script, version 3.2.0
23 | #
24 | # Required ENV vars:
25 | # ------------------
26 | # JAVA_HOME - location of a JDK home dir
27 | #
28 | # Optional ENV vars
29 | # -----------------
30 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven
31 | # e.g. to debug Maven itself, use
32 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
33 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files
34 | # ----------------------------------------------------------------------------
35 |
36 | if [ -z "$MAVEN_SKIP_RC" ] ; then
37 |
38 | if [ -f /usr/local/etc/mavenrc ] ; then
39 | . /usr/local/etc/mavenrc
40 | fi
41 |
42 | if [ -f /etc/mavenrc ] ; then
43 | . /etc/mavenrc
44 | fi
45 |
46 | if [ -f "$HOME/.mavenrc" ] ; then
47 | . "$HOME/.mavenrc"
48 | fi
49 |
50 | fi
51 |
52 | # OS specific support. $var _must_ be set to either true or false.
53 | cygwin=false;
54 | darwin=false;
55 | mingw=false
56 | case "$(uname)" in
57 | CYGWIN*) cygwin=true ;;
58 | MINGW*) mingw=true;;
59 | Darwin*) darwin=true
60 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
61 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
62 | if [ -z "$JAVA_HOME" ]; then
63 | if [ -x "/usr/libexec/java_home" ]; then
64 | JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME
65 | else
66 | JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
67 | fi
68 | fi
69 | ;;
70 | esac
71 |
72 | if [ -z "$JAVA_HOME" ] ; then
73 | if [ -r /etc/gentoo-release ] ; then
74 | JAVA_HOME=$(java-config --jre-home)
75 | fi
76 | fi
77 |
78 | # For Cygwin, ensure paths are in UNIX format before anything is touched
79 | if $cygwin ; then
80 | [ -n "$JAVA_HOME" ] &&
81 | JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
82 | [ -n "$CLASSPATH" ] &&
83 | CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
84 | fi
85 |
86 | # For Mingw, ensure paths are in UNIX format before anything is touched
87 | if $mingw ; then
88 | [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
89 | JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)"
90 | fi
91 |
92 | if [ -z "$JAVA_HOME" ]; then
93 | javaExecutable="$(which javac)"
94 | if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then
95 | # readlink(1) is not available as standard on Solaris 10.
96 | readLink=$(which readlink)
97 | if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then
98 | if $darwin ; then
99 | javaHome="$(dirname "\"$javaExecutable\"")"
100 | javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac"
101 | else
102 | javaExecutable="$(readlink -f "\"$javaExecutable\"")"
103 | fi
104 | javaHome="$(dirname "\"$javaExecutable\"")"
105 | javaHome=$(expr "$javaHome" : '\(.*\)/bin')
106 | JAVA_HOME="$javaHome"
107 | export JAVA_HOME
108 | fi
109 | fi
110 | fi
111 |
112 | if [ -z "$JAVACMD" ] ; then
113 | if [ -n "$JAVA_HOME" ] ; then
114 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
115 | # IBM's JDK on AIX uses strange locations for the executables
116 | JAVACMD="$JAVA_HOME/jre/sh/java"
117 | else
118 | JAVACMD="$JAVA_HOME/bin/java"
119 | fi
120 | else
121 | JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)"
122 | fi
123 | fi
124 |
125 | if [ ! -x "$JAVACMD" ] ; then
126 | echo "Error: JAVA_HOME is not defined correctly." >&2
127 | echo " We cannot execute $JAVACMD" >&2
128 | exit 1
129 | fi
130 |
131 | if [ -z "$JAVA_HOME" ] ; then
132 | echo "Warning: JAVA_HOME environment variable is not set."
133 | fi
134 |
135 | # traverses directory structure from process work directory to filesystem root
136 | # first directory with .mvn subdirectory is considered project base directory
137 | find_maven_basedir() {
138 | if [ -z "$1" ]
139 | then
140 | echo "Path not specified to find_maven_basedir"
141 | return 1
142 | fi
143 |
144 | basedir="$1"
145 | wdir="$1"
146 | while [ "$wdir" != '/' ] ; do
147 | if [ -d "$wdir"/.mvn ] ; then
148 | basedir=$wdir
149 | break
150 | fi
151 | # workaround for JBEAP-8937 (on Solaris 10/Sparc)
152 | if [ -d "${wdir}" ]; then
153 | wdir=$(cd "$wdir/.." || exit 1; pwd)
154 | fi
155 | # end of workaround
156 | done
157 | printf '%s' "$(cd "$basedir" || exit 1; pwd)"
158 | }
159 |
160 | # concatenates all lines of a file
161 | concat_lines() {
162 | if [ -f "$1" ]; then
163 | # Remove \r in case we run on Windows within Git Bash
164 | # and check out the repository with auto CRLF management
165 | # enabled. Otherwise, we may read lines that are delimited with
166 | # \r\n and produce $'-Xarg\r' rather than -Xarg due to word
167 | # splitting rules.
168 | tr -s '\r\n' ' ' < "$1"
169 | fi
170 | }
171 |
172 | log() {
173 | if [ "$MVNW_VERBOSE" = true ]; then
174 | printf '%s\n' "$1"
175 | fi
176 | }
177 |
178 | BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
179 | if [ -z "$BASE_DIR" ]; then
180 | exit 1;
181 | fi
182 |
183 | MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
184 | log "$MAVEN_PROJECTBASEDIR"
185 |
186 | ##########################################################################################
187 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
188 | # This allows using the maven wrapper in projects that prohibit checking in binary data.
189 | ##########################################################################################
190 | wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
191 | if [ -r "$wrapperJarPath" ]; then
192 | log "Found $wrapperJarPath"
193 | else
194 | log "Couldn't find $wrapperJarPath, downloading it ..."
195 |
196 | if [ -n "$MVNW_REPOURL" ]; then
197 | wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
198 | else
199 | wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
200 | fi
201 | while IFS="=" read -r key value; do
202 | # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
203 | safeValue=$(echo "$value" | tr -d '\r')
204 | case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;;
205 | esac
206 | done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
207 | log "Downloading from: $wrapperUrl"
208 |
209 | if $cygwin; then
210 | wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
211 | fi
212 |
213 | if command -v wget > /dev/null; then
214 | log "Found wget ... using wget"
215 | [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
216 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
217 | wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
218 | else
219 | wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
220 | fi
221 | elif command -v curl > /dev/null; then
222 | log "Found curl ... using curl"
223 | [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
224 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
225 | curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
226 | else
227 | curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
228 | fi
229 | else
230 | log "Falling back to using Java to download"
231 | javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
232 | javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
233 | # For Cygwin, switch paths to Windows format before running javac
234 | if $cygwin; then
235 | javaSource=$(cygpath --path --windows "$javaSource")
236 | javaClass=$(cygpath --path --windows "$javaClass")
237 | fi
238 | if [ -e "$javaSource" ]; then
239 | if [ ! -e "$javaClass" ]; then
240 | log " - Compiling MavenWrapperDownloader.java ..."
241 | ("$JAVA_HOME/bin/javac" "$javaSource")
242 | fi
243 | if [ -e "$javaClass" ]; then
244 | log " - Running MavenWrapperDownloader.java ..."
245 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
246 | fi
247 | fi
248 | fi
249 | fi
250 | ##########################################################################################
251 | # End of extension
252 | ##########################################################################################
253 |
254 | # If specified, validate the SHA-256 sum of the Maven wrapper jar file
255 | wrapperSha256Sum=""
256 | while IFS="=" read -r key value; do
257 | case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
258 | esac
259 | done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
260 | if [ -n "$wrapperSha256Sum" ]; then
261 | wrapperSha256Result=false
262 | if command -v sha256sum > /dev/null; then
263 | if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then
264 | wrapperSha256Result=true
265 | fi
266 | elif command -v shasum > /dev/null; then
267 | if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
268 | wrapperSha256Result=true
269 | fi
270 | else
271 | echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
272 | echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
273 | exit 1
274 | fi
275 | if [ $wrapperSha256Result = false ]; then
276 | echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
277 | echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
278 | echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
279 | exit 1
280 | fi
281 | fi
282 |
283 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
284 |
285 | # For Cygwin, switch paths to Windows format before running java
286 | if $cygwin; then
287 | [ -n "$JAVA_HOME" ] &&
288 | JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
289 | [ -n "$CLASSPATH" ] &&
290 | CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
291 | [ -n "$MAVEN_PROJECTBASEDIR" ] &&
292 | MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
293 | fi
294 |
295 | # Provide a "standardized" way to retrieve the CLI args that will
296 | # work with both Windows and non-Windows executions.
297 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*"
298 | export MAVEN_CMD_LINE_ARGS
299 |
300 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
301 |
302 | # shellcheck disable=SC2086 # safe args
303 | exec "$JAVACMD" \
304 | $MAVEN_OPTS \
305 | $MAVEN_DEBUG_OPTS \
306 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
307 | "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
308 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
309 |
--------------------------------------------------------------------------------
/mvnw.cmd:
--------------------------------------------------------------------------------
1 | @REM ----------------------------------------------------------------------------
2 | @REM Licensed to the Apache Software Foundation (ASF) under one
3 | @REM or more contributor license agreements. See the NOTICE file
4 | @REM distributed with this work for additional information
5 | @REM regarding copyright ownership. The ASF licenses this file
6 | @REM to you under the Apache License, Version 2.0 (the
7 | @REM "License"); you may not use this file except in compliance
8 | @REM with the License. You may obtain a copy of the License at
9 | @REM
10 | @REM https://www.apache.org/licenses/LICENSE-2.0
11 | @REM
12 | @REM Unless required by applicable law or agreed to in writing,
13 | @REM software distributed under the License is distributed on an
14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | @REM KIND, either express or implied. See the License for the
16 | @REM specific language governing permissions and limitations
17 | @REM under the License.
18 | @REM ----------------------------------------------------------------------------
19 |
20 | @REM ----------------------------------------------------------------------------
21 | @REM Apache Maven Wrapper startup batch script, version 3.2.0
22 | @REM
23 | @REM Required ENV vars:
24 | @REM JAVA_HOME - location of a JDK home dir
25 | @REM
26 | @REM Optional ENV vars
27 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
28 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
29 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
30 | @REM e.g. to debug Maven itself, use
31 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
32 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
33 | @REM ----------------------------------------------------------------------------
34 |
35 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
36 | @echo off
37 | @REM set title of command window
38 | title %0
39 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
40 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
41 |
42 | @REM set %HOME% to equivalent of $HOME
43 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
44 |
45 | @REM Execute a user defined script before this one
46 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
47 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending
48 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
49 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
50 | :skipRcPre
51 |
52 | @setlocal
53 |
54 | set ERROR_CODE=0
55 |
56 | @REM To isolate internal variables from possible post scripts, we use another setlocal
57 | @setlocal
58 |
59 | @REM ==== START VALIDATION ====
60 | if not "%JAVA_HOME%" == "" goto OkJHome
61 |
62 | echo.
63 | echo Error: JAVA_HOME not found in your environment. >&2
64 | echo Please set the JAVA_HOME variable in your environment to match the >&2
65 | echo location of your Java installation. >&2
66 | echo.
67 | goto error
68 |
69 | :OkJHome
70 | if exist "%JAVA_HOME%\bin\java.exe" goto init
71 |
72 | echo.
73 | echo Error: JAVA_HOME is set to an invalid directory. >&2
74 | echo JAVA_HOME = "%JAVA_HOME%" >&2
75 | echo Please set the JAVA_HOME variable in your environment to match the >&2
76 | echo location of your Java installation. >&2
77 | echo.
78 | goto error
79 |
80 | @REM ==== END VALIDATION ====
81 |
82 | :init
83 |
84 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
85 | @REM Fallback to current working directory if not found.
86 |
87 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
88 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
89 |
90 | set EXEC_DIR=%CD%
91 | set WDIR=%EXEC_DIR%
92 | :findBaseDir
93 | IF EXIST "%WDIR%"\.mvn goto baseDirFound
94 | cd ..
95 | IF "%WDIR%"=="%CD%" goto baseDirNotFound
96 | set WDIR=%CD%
97 | goto findBaseDir
98 |
99 | :baseDirFound
100 | set MAVEN_PROJECTBASEDIR=%WDIR%
101 | cd "%EXEC_DIR%"
102 | goto endDetectBaseDir
103 |
104 | :baseDirNotFound
105 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
106 | cd "%EXEC_DIR%"
107 |
108 | :endDetectBaseDir
109 |
110 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
111 |
112 | @setlocal EnableExtensions EnableDelayedExpansion
113 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
114 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
115 |
116 | :endReadAdditionalConfig
117 |
118 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
121 |
122 | set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
123 |
124 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
125 | IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
126 | )
127 |
128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data.
130 | if exist %WRAPPER_JAR% (
131 | if "%MVNW_VERBOSE%" == "true" (
132 | echo Found %WRAPPER_JAR%
133 | )
134 | ) else (
135 | if not "%MVNW_REPOURL%" == "" (
136 | SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
137 | )
138 | if "%MVNW_VERBOSE%" == "true" (
139 | echo Couldn't find %WRAPPER_JAR%, downloading it ...
140 | echo Downloading from: %WRAPPER_URL%
141 | )
142 |
143 | powershell -Command "&{"^
144 | "$webclient = new-object System.Net.WebClient;"^
145 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
146 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
147 | "}"^
148 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
149 | "}"
150 | if "%MVNW_VERBOSE%" == "true" (
151 | echo Finished downloading %WRAPPER_JAR%
152 | )
153 | )
154 | @REM End of extension
155 |
156 | @REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
157 | SET WRAPPER_SHA_256_SUM=""
158 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
159 | IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
160 | )
161 | IF NOT %WRAPPER_SHA_256_SUM%=="" (
162 | powershell -Command "&{"^
163 | "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
164 | "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
165 | " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
166 | " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
167 | " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
168 | " exit 1;"^
169 | "}"^
170 | "}"
171 | if ERRORLEVEL 1 goto error
172 | )
173 |
174 | @REM Provide a "standardized" way to retrieve the CLI args that will
175 | @REM work with both Windows and non-Windows executions.
176 | set MAVEN_CMD_LINE_ARGS=%*
177 |
178 | %MAVEN_JAVA_EXE% ^
179 | %JVM_CONFIG_MAVEN_PROPS% ^
180 | %MAVEN_OPTS% ^
181 | %MAVEN_DEBUG_OPTS% ^
182 | -classpath %WRAPPER_JAR% ^
183 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
184 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
185 | if ERRORLEVEL 1 goto error
186 | goto end
187 |
188 | :error
189 | set ERROR_CODE=1
190 |
191 | :end
192 | @endlocal & set ERROR_CODE=%ERROR_CODE%
193 |
194 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
195 | @REM check for post script, once with legacy .bat ending and once with .cmd ending
196 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
197 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
198 | :skipRcPost
199 |
200 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
201 | if "%MAVEN_BATCH_PAUSE%"=="on" pause
202 |
203 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
204 |
205 | cmd /C exit /B %ERROR_CODE%
206 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 |
7 | org.springframework.boot
8 | spring-boot-starter-parent
9 | 3.3.0-SNAPSHOT
10 |
11 |
12 | com.spring
13 | myfood
14 | 0.0.1-SNAPSHOT
15 | myfood
16 | This project is meant to demostrate the ranking system of a delivery app
17 |
18 | 17
19 |
20 |
21 |
22 | org.springframework.boot
23 | spring-boot-starter-actuator
24 |
25 |
26 | org.springframework.boot
27 | spring-boot-starter-data-mongodb
28 |
29 |
30 | org.springframework.boot
31 | spring-boot-starter-validation
32 |
33 |
34 | org.springframework.boot
35 | spring-boot-starter-web
36 |
37 |
38 | org.projectlombok
39 | lombok
40 | true
41 |
42 |
43 | org.springframework.boot
44 | spring-boot-starter-test
45 | test
46 |
47 |
48 | org.springdoc
49 | springdoc-openapi-starter-webmvc-ui
50 | 2.1.0
51 |
52 |
53 | org.springdoc
54 | springdoc-openapi-starter-webflux-ui
55 | 2.1.0
56 |
57 |
58 | io.springfox
59 | springfox-boot-starter
60 | 3.0.0
61 |
62 |
63 |
64 |
65 |
66 |
67 | org.springframework.boot
68 | spring-boot-maven-plugin
69 |
70 |
71 |
72 | org.projectlombok
73 | lombok
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | spring-milestones
83 | Spring Milestones
84 | https://repo.spring.io/milestone
85 |
86 | false
87 |
88 |
89 |
90 | spring-snapshots
91 | Spring Snapshots
92 | https://repo.spring.io/snapshot
93 |
94 | false
95 |
96 |
97 |
98 |
99 |
100 | spring-milestones
101 | Spring Milestones
102 | https://repo.spring.io/milestone
103 |
104 | false
105 |
106 |
107 |
108 | spring-snapshots
109 | Spring Snapshots
110 | https://repo.spring.io/snapshot
111 |
112 | false
113 |
114 |
115 |
116 |
117 |
118 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/myfood/MyfoodApplication.java:
--------------------------------------------------------------------------------
1 | package com.spring.myfood;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.context.annotation.ComponentScan;
6 |
7 | @SpringBootApplication
8 | @ComponentScan(basePackages = { "com.spring.myfood.config", "com.spring.myfood.controller", "com.spring.myfood.service",
9 | "com.spring.myfood.model", "com.spring.myfood.repository", "com.spring.myfood.enums",
10 | "com.spring.myfood.mongo" })
11 | public class MyfoodApplication {
12 |
13 | public static void main(String[] args) {
14 | SpringApplication.run(MyfoodApplication.class, args);
15 | }
16 |
17 | }
--------------------------------------------------------------------------------
/src/main/java/com/spring/myfood/config/CorsConfig.java:
--------------------------------------------------------------------------------
1 | package com.spring.myfood.config;
2 |
3 | import org.springframework.context.annotation.Configuration;
4 | import org.springframework.web.servlet.config.annotation.CorsRegistry;
5 | import org.springframework.web.servlet.config.annotation.EnableWebMvc;
6 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
7 |
8 | @Configuration
9 | @EnableWebMvc
10 | public class CorsConfig implements WebMvcConfigurer {
11 | @Override
12 | public void addCorsMappings(CorsRegistry registry) {
13 | registry.addMapping("/**")
14 | .allowedOrigins("*")
15 | .allowedMethods("GET", "POST", "PUT", "DELETE")
16 | .allowedHeaders("*")
17 | .maxAge(3600);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/myfood/config/MongoConfig.java:
--------------------------------------------------------------------------------
1 | package com.spring.myfood.config;
2 |
3 | import java.util.Collection;
4 | import java.util.Collections;
5 |
6 | import org.springframework.context.annotation.Configuration;
7 | import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration;
8 | import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
9 |
10 | import com.mongodb.ConnectionString;
11 | import com.mongodb.MongoClientSettings;
12 | import com.mongodb.client.MongoClient;
13 | import com.mongodb.client.MongoClients;
14 |
15 | @Configuration
16 | @EnableMongoRepositories("com.spring.myfood.repository")
17 | public class MongoConfig extends AbstractMongoClientConfiguration {
18 |
19 | @Override
20 | protected String getDatabaseName() {
21 | return "my-food";
22 | }
23 |
24 | @Override
25 | public MongoClient mongoClient() {
26 | ConnectionString connectionString = new ConnectionString(
27 | "mongodb+srv://jaime:march@second.sreaptl.mongodb.net/my-food");
28 | MongoClientSettings mongoClientSettings = MongoClientSettings.builder()
29 | .applyConnectionString(connectionString)
30 | .build();
31 |
32 | return MongoClients.create(mongoClientSettings);
33 | }
34 |
35 | @Override
36 | public Collection getMappingBasePackages() {
37 | return Collections.singleton("com.spring.myfood.model");
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/myfood/config/SwaggerConfiguration.java:
--------------------------------------------------------------------------------
1 | package com.spring.myfood.config;
2 |
3 | import java.util.List;
4 |
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.context.annotation.Configuration;
7 | import org.springframework.web.servlet.config.annotation.*;
8 | import springfox.documentation.swagger.web.UiConfiguration;
9 | import springfox.documentation.swagger.web.UiConfigurationBuilder;
10 |
11 | import io.swagger.v3.oas.models.OpenAPI;
12 | import io.swagger.v3.oas.models.info.Contact;
13 | import io.swagger.v3.oas.models.info.Info;
14 | import io.swagger.v3.oas.models.servers.Server;
15 |
16 | @Configuration
17 | public class SwaggerConfiguration implements WebMvcConfigurer {
18 |
19 | @Bean
20 | public OpenAPI customOpenAPI() {
21 | return new OpenAPI()
22 | .servers(List.of(new Server().url("https://app-myfood-production.up.railway.app/"),
23 | new Server().url("http://localhost:8080/")))
24 | .info(new Info()
25 | .title("My Food - Ranking System")
26 | .description("This documentation comprises all endpoints for the My Food application. My Food is a fictional app that implements a ranking system. The purpose of this project is to showcase the most searched foods and their associated scores within the fictitious app named MyFood.")
27 | .version("1.1")
28 | .contact(new Contact().name("GitHub Repository")
29 | .url("https://github.com/LadyJessie19/MyFood")));
30 | }
31 |
32 | @Bean
33 | public UiConfiguration uiConfig() {
34 | return UiConfigurationBuilder.builder().build();
35 | }
36 |
37 | @Bean
38 | public WebMvcConfigurer corsConfigurer() {
39 | return new WebMvcConfigurer() {
40 | @Override
41 | public void addCorsMappings(CorsRegistry registry) {
42 | registry.addMapping("/swagger-ui/**").allowedOrigins("*");
43 | }
44 | };
45 | }
46 |
47 | @Override
48 | public void addResourceHandlers(ResourceHandlerRegistry registry) {
49 | registry.addResourceHandler("/swagger-ui/**")
50 | .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/")
51 | .resourceChain(false);
52 | registry.addResourceHandler("/swagger-ui.html")
53 | .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/")
54 | .resourceChain(false);
55 | }
56 | }
--------------------------------------------------------------------------------
/src/main/java/com/spring/myfood/controller/MainController.java:
--------------------------------------------------------------------------------
1 | package com.spring.myfood.controller;
2 |
3 | import org.springframework.web.bind.annotation.GetMapping;
4 | import org.springframework.web.bind.annotation.RequestMapping;
5 | import org.springframework.web.bind.annotation.RestController;
6 |
7 | import io.swagger.v3.oas.annotations.Operation;
8 | import io.swagger.v3.oas.annotations.media.Content;
9 | import io.swagger.v3.oas.annotations.media.Schema;
10 | import io.swagger.v3.oas.annotations.responses.ApiResponse;
11 | import io.swagger.v3.oas.annotations.responses.ApiResponses;
12 | import io.swagger.v3.oas.annotations.tags.Tag;
13 |
14 | @RestController
15 | @RequestMapping("/")
16 | @Tag(name = "Main - Home and Health", description = "Home and Health endpoints")
17 | public class MainController {
18 |
19 | @Operation(summary = "This is the landing route to the application")
20 | @ApiResponses(value = {
21 | @ApiResponse(responseCode = "200", description = "Home is ok", content = {
22 | @Content(mediaType = "text/plain", schema = @Schema(implementation = String.class)) }),
23 | @ApiResponse(responseCode = "404", description = "Error - Not found", content = @Content),
24 | @ApiResponse(responseCode = "500", description = "Internal server error", content = @Content) })
25 | @GetMapping
26 | public String home() {
27 | return "Hi! This is the MyFood API. Check out the docs at https://app-myfood-production.up.railway.app/swagger-ui/index.html#/";
28 | }
29 |
30 | @Operation(summary = "Check if MyFood is running")
31 | @ApiResponses(value = {
32 | @ApiResponse(responseCode = "200", description = "Api is ok", content = {
33 | @Content(mediaType = "text/plain", schema = @Schema(implementation = String.class)) }),
34 | @ApiResponse(responseCode = "404", description = "Api is not ok", content = @Content) })
35 | @GetMapping("health")
36 | public String health() {
37 | return "Is MyFood running? I'm thumbs up!";
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/myfood/controller/ProductController.java:
--------------------------------------------------------------------------------
1 | package com.spring.myfood.controller;
2 |
3 | import org.springframework.web.bind.annotation.RequestMapping;
4 | import org.springframework.web.bind.annotation.RequestParam;
5 | import org.springframework.web.bind.annotation.RestController;
6 |
7 | import com.spring.myfood.dtos.request.RequestProductDTO;
8 | import com.spring.myfood.dtos.response.ResponseSearchFoodDTO;
9 | import com.spring.myfood.enums.FoodCategoryEnum;
10 | import com.spring.myfood.model.Product;
11 | import com.spring.myfood.service.ProductService;
12 |
13 | import io.swagger.v3.oas.annotations.Operation;
14 | import io.swagger.v3.oas.annotations.Parameter;
15 | import io.swagger.v3.oas.annotations.media.Content;
16 | import io.swagger.v3.oas.annotations.media.ExampleObject;
17 | import io.swagger.v3.oas.annotations.media.Schema;
18 | import io.swagger.v3.oas.annotations.responses.ApiResponse;
19 | import io.swagger.v3.oas.annotations.responses.ApiResponses;
20 | import io.swagger.v3.oas.annotations.tags.Tag;
21 |
22 | import java.net.URI;
23 | import java.util.Arrays;
24 | import java.util.List;
25 |
26 | import org.apache.coyote.BadRequestException;
27 | import org.springframework.beans.factory.annotation.Autowired;
28 | import org.springframework.data.domain.Page;
29 | import org.springframework.data.domain.PageRequest;
30 | import org.springframework.data.domain.Pageable;
31 | import org.springframework.http.ResponseEntity;
32 | import org.springframework.web.bind.annotation.GetMapping;
33 | import org.springframework.web.bind.annotation.PathVariable;
34 | import org.springframework.web.bind.annotation.PostMapping;
35 | import org.springframework.web.bind.annotation.RequestBody;
36 |
37 | @RestController
38 | @RequestMapping("/products")
39 | @Tag(name = "Products", description = "The Products endpoints to create and list products")
40 | public class ProductController {
41 |
42 | @Autowired
43 | public ProductService productService;
44 |
45 | @PostMapping("register")
46 | @Operation(summary = "Register a new product")
47 | @ApiResponses(value = {
48 | @ApiResponse(responseCode = "201", description = "Product created", content = {
49 | @Content(mediaType = "application/json", schema = @Schema(implementation = Product.class)) }),
50 | @ApiResponse(responseCode = "404", description = "Product not created", content = @Content),
51 | @ApiResponse(responseCode = "400", description = "Bad request", content = @Content),
52 | @ApiResponse(responseCode = "500", description = "Internal server error", content = @Content) })
53 | public ResponseEntity registerProduct(
54 | @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "Product to register", required = true, content = @Content(mediaType = "application/json", schema = @Schema(implementation = RequestProductDTO.class), examples = @ExampleObject(value = "{\n"
55 | +
56 | " \"name\": \"The product name goes here\",\n" +
57 | " \"description\": \"The product description goes here\",\n" +
58 | " \"price\": 10.0,\n" +
59 | " \"image\": \"An image url\",\n" +
60 | " \"category\": \"FoodCategoryEnum (PIZZA - HAMBURGER - SANDWICH - PASTA - SALAD - SUSHI - BBQ - DESSERT - VEGETARIAN - VEGAN - FRUIT - BEVERAGE )\"\n"
61 | +
62 | "}"))) @RequestBody(required = true) RequestProductDTO product)
63 | throws BadRequestException {
64 |
65 | if (product == null) {
66 | return ResponseEntity.badRequest().body(null);
67 | }
68 |
69 | return ResponseEntity.created(URI.create("/" + product.getName()))
70 | .body(productService.registerNewProduct(product));
71 | }
72 |
73 | @GetMapping("/all")
74 | @Operation(summary = "List all products")
75 | @ApiResponses(value = {
76 | @ApiResponse(responseCode = "200", description = "List of products", content = {
77 | @Content(mediaType = "application/json", schema = @Schema(implementation = Product.class)) }),
78 | @ApiResponse(responseCode = "404", description = "Products not found", content = @Content),
79 | @ApiResponse(responseCode = "400", description = "Bad request / Invalid category", content = @Content),
80 | @ApiResponse(responseCode = "500", description = "Internal server error", content = @Content)
81 | })
82 | public ResponseEntity> listAllProducts(
83 | @Parameter(description = "Page number.") @RequestParam(defaultValue = "0") int page,
84 | @Parameter(description = "Number of products per page.") @RequestParam(defaultValue = "5") int size,
85 | @Parameter(description = "Optional parameter to filter products by category.") @RequestParam(required = false) FoodCategoryEnum category) {
86 |
87 | Pageable pageable = PageRequest.of(page, size);
88 |
89 | if (category != null && this.isValidCategory(category.toString())) {
90 | Page products = productService.findProductsByCategory(category, pageable);
91 | return ResponseEntity.ok().body(products);
92 | } else {
93 | Page products = productService.findAllProducts(pageable);
94 | return ResponseEntity.ok().body(products);
95 | }
96 | }
97 |
98 | @GetMapping("/{id}")
99 | @Operation(summary = "Find a product by id (MongoDB ObjectId)")
100 | @ApiResponses(value = {
101 | @ApiResponse(responseCode = "200", description = "Product found", content = {
102 | @Content(mediaType = "application/json", schema = @Schema(implementation = Product.class)) }),
103 | @ApiResponse(responseCode = "404", description = "Product not found", content = @Content),
104 | @ApiResponse(responseCode = "500", description = "Internal server error", content = @Content) })
105 | public ResponseEntity findProductById(
106 | @Parameter(description = "Product id", required = true) @PathVariable String id) {
107 | return ResponseEntity.ok().body(productService.findProductById(id));
108 | }
109 |
110 | private boolean isValidCategory(String category) {
111 | return Arrays.stream(FoodCategoryEnum.values())
112 | .anyMatch(enumValue -> enumValue.name().equalsIgnoreCase(category));
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/myfood/controller/RankingController.java:
--------------------------------------------------------------------------------
1 | package com.spring.myfood.controller;
2 |
3 | import java.util.Collections;
4 | import java.util.List;
5 |
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.data.domain.PageRequest;
8 | import org.springframework.data.domain.Pageable;
9 | import org.springframework.data.web.PageableDefault;
10 | import org.springframework.http.ResponseEntity;
11 | import org.springframework.web.bind.annotation.GetMapping;
12 | import org.springframework.web.bind.annotation.PathVariable;
13 | import org.springframework.web.bind.annotation.RequestMapping;
14 | import org.springframework.web.bind.annotation.RequestParam;
15 | import org.springframework.web.bind.annotation.RestController;
16 |
17 | import com.spring.myfood.model.Product;
18 | import com.spring.myfood.model.Ranking;
19 | import com.spring.myfood.service.RankingService;
20 |
21 | import io.swagger.v3.oas.annotations.Operation;
22 | import io.swagger.v3.oas.annotations.Parameter;
23 | import io.swagger.v3.oas.annotations.responses.ApiResponses;
24 | import io.swagger.v3.oas.annotations.responses.ApiResponse;
25 | import io.swagger.v3.oas.annotations.media.Content;
26 | import io.swagger.v3.oas.annotations.tags.Tag;
27 | import io.swagger.v3.oas.annotations.media.Schema;
28 |
29 | @RestController
30 | @RequestMapping("/rankings")
31 | @Tag(name = "Ranking", description = "The Ranking endpoints to search and view products and categories scores")
32 | public class RankingController {
33 |
34 | @Autowired
35 | private RankingService rankingService;
36 |
37 | @GetMapping("/search")
38 | @Operation(summary = "Search for products at the database")
39 | @ApiResponses(value = {
40 | @ApiResponse(responseCode = "200", description = "List of products", content = {
41 | @Content(mediaType = "application/json", schema = @Schema(implementation = Product.class)) }),
42 | @ApiResponse(responseCode = "404", description = "Products not found", content = @Content),
43 | @ApiResponse(responseCode = "500", description = "Internal server error", content = @Content)
44 | })
45 | public ResponseEntity> searchFood(
46 | @Parameter(description = "The name of the food", required = true) @RequestParam String foodTitle) {
47 |
48 | if (foodTitle == null || foodTitle.isEmpty()) {
49 | return ResponseEntity.ok().body(Collections.emptyList());
50 | }
51 |
52 | return ResponseEntity.ok().body(rankingService.searchingFoods(foodTitle));
53 | }
54 |
55 | @GetMapping("/top-foods")
56 | @Operation(summary = "List of the 3 top most searched foods")
57 | @ApiResponses(value = {
58 | @ApiResponse(responseCode = "200", description = "List of rankings", content = {
59 | @Content(mediaType = "application/json", schema = @Schema(implementation = Ranking.class)) }),
60 | @ApiResponse(responseCode = "404", description = "The rankings were not found", content = @Content),
61 | @ApiResponse(responseCode = "500", description = "Internal server error", content = @Content)
62 | })
63 | public ResponseEntity> findMostScoredFoods() {
64 | Pageable pageable = PageRequest.of(0, 3);
65 | return ResponseEntity.ok().body(rankingService.mostScoredFoods(pageable));
66 | }
67 |
68 | @GetMapping("/top-categories")
69 | @Operation(summary = "List of the 3 top most searched categories")
70 | @ApiResponses(value = {
71 | @ApiResponse(responseCode = "200", description = "List of rankings", content = {
72 | @Content(mediaType = "application/json", schema = @Schema(implementation = Ranking.class)) }),
73 | @ApiResponse(responseCode = "404", description = "The rankings were not found", content = @Content),
74 | @ApiResponse(responseCode = "500", description = "Internal server error", content = @Content)
75 | })
76 | public ResponseEntity> findMostSearchedCategories() {
77 | Pageable pageable = PageRequest.of(0, 3);
78 | return ResponseEntity.ok().body(rankingService.findMostSearchedCategories(pageable));
79 | }
80 |
81 | @GetMapping("/{id}")
82 | @Operation(summary = "Find a ranking by id (MongoDB ObjectId)")
83 | @ApiResponses(value = {
84 | @ApiResponse(responseCode = "200", description = "Ranking found", content = {
85 | @Content(mediaType = "application/json", schema = @Schema(implementation = Ranking.class)) }),
86 | @ApiResponse(responseCode = "404", description = "Ranking not found", content = @Content),
87 | @ApiResponse(responseCode = "500", description = "Internal server error", content = @Content)
88 | })
89 | public ResponseEntity findRankingById(
90 | @Parameter(description = "The Ranking id", required = true) @PathVariable String id) {
91 | return ResponseEntity.ok().body(rankingService.findRankingById(id));
92 | }
93 | }
--------------------------------------------------------------------------------
/src/main/java/com/spring/myfood/dtos/request/RequestProductDTO.java:
--------------------------------------------------------------------------------
1 | package com.spring.myfood.dtos.request;
2 |
3 | import jakarta.validation.constraints.NotBlank;
4 | import lombok.AllArgsConstructor;
5 | import lombok.Getter;
6 | import lombok.NoArgsConstructor;
7 | import lombok.Setter;
8 |
9 | @Getter
10 | @Setter
11 | @NoArgsConstructor
12 | @AllArgsConstructor
13 | public class RequestProductDTO {
14 | @NotBlank(message = "Name is required")
15 | private String name;
16 |
17 | @NotBlank(message = "Description is required")
18 | private String description;
19 |
20 | @NotBlank(message = "Price is required")
21 | private int price;
22 |
23 | private String image;
24 |
25 | @NotBlank(message = "Category is required")
26 | private String category;
27 |
28 | @Override
29 | public String toString() {
30 | return "RequestProductDTO [ name=" + name + ", price=" + price + ", category=" + category + ", description="
31 | + description + ", image=" + image;
32 | }
33 | }
--------------------------------------------------------------------------------
/src/main/java/com/spring/myfood/dtos/response/ResponseSearchFoodDTO.java:
--------------------------------------------------------------------------------
1 | package com.spring.myfood.dtos.response;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.stereotype.Component;
5 |
6 | import com.spring.myfood.enums.FoodCategoryEnum;
7 | import com.spring.myfood.enums.RankingTypeEnum;
8 | import com.spring.myfood.model.Product;
9 | import com.spring.myfood.model.Ranking;
10 | import com.spring.myfood.repository.RankingRepository;
11 |
12 | import jakarta.annotation.PostConstruct;
13 | import lombok.AllArgsConstructor;
14 | import lombok.NoArgsConstructor;
15 | import lombok.Setter;
16 | import lombok.Getter;
17 |
18 | @Getter
19 | @Setter
20 | @NoArgsConstructor
21 | @AllArgsConstructor
22 | @Component
23 | public class ResponseSearchFoodDTO {
24 | @Autowired
25 | private RankingRepository rankingRepository;
26 |
27 | public ResponseSearchFoodDTO(Product product) {
28 | this.name = product.getName();
29 | this.description = product.getDescription();
30 | this.price = product.getPrice();
31 | this.image = product.getImage();
32 | this.category = product.getCategory();
33 | }
34 |
35 | private String name;
36 | private String description;
37 | private int price;
38 | private String image;
39 | private FoodCategoryEnum category;
40 | private int score = 0;
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/myfood/enums/FoodCategoryEnum.java:
--------------------------------------------------------------------------------
1 | package com.spring.myfood.enums;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Getter;
5 |
6 | @Getter
7 | @AllArgsConstructor
8 | public enum FoodCategoryEnum {
9 | PIZZA("Pizza"),
10 | HAMBURGER("Hamburger"),
11 | SANDWICH("Sandwich"),
12 | PASTA("Pasta"),
13 | SALAD("Salad"),
14 | SUSHI("Sushi"),
15 | BBQ("BBQ"),
16 | DESSERT("Dessert"),
17 | VEGETARIAN("Vegetarian"),
18 | VEGAN("Vegan"),
19 | FRUIT("Fruit"),
20 | BEVERAGE("Beverage");
21 |
22 | private final String category;
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/myfood/enums/RankingTypeEnum.java:
--------------------------------------------------------------------------------
1 | package com.spring.myfood.enums;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Getter;
5 |
6 | @AllArgsConstructor
7 | @Getter
8 | public enum RankingTypeEnum {
9 | FOOD("Food"),
10 | CATEGORY("Category");
11 |
12 | private final String type;
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/myfood/model/Product.java:
--------------------------------------------------------------------------------
1 | package com.spring.myfood.model;
2 |
3 | import java.util.Date;
4 |
5 | import org.springframework.data.annotation.CreatedDate;
6 | import org.springframework.data.annotation.Id;
7 | import org.springframework.data.annotation.LastModifiedDate;
8 | import org.springframework.data.mongodb.core.mapping.Field;
9 |
10 | import com.fasterxml.jackson.annotation.JsonIgnore;
11 | import com.spring.myfood.enums.FoodCategoryEnum;
12 |
13 | import lombok.AllArgsConstructor;
14 | import lombok.Getter;
15 | import lombok.NoArgsConstructor;
16 | import lombok.Setter;
17 |
18 | @Getter
19 | @AllArgsConstructor
20 | @NoArgsConstructor
21 | public class Product {
22 | @Id
23 | private String id;
24 |
25 | @Setter
26 | @Field
27 | private String name;
28 |
29 | @Setter
30 | @Field
31 | private String description;
32 |
33 | @Setter
34 | @Field
35 | private int price;
36 |
37 | @Setter
38 | @Field
39 | private String image;
40 |
41 | @Setter
42 | @Field
43 | private FoodCategoryEnum category;
44 |
45 | @JsonIgnore
46 | @CreatedDate
47 | private Date createdAt;
48 |
49 | @JsonIgnore
50 | @LastModifiedDate
51 | private Date updatedAt;
52 |
53 | @Override
54 | public String toString() {
55 | return "Product{" +
56 | "id=" + id +
57 | ", name='" + name + '\'' +
58 | ", description='" + description + '\'' +
59 | ", price=" + price +
60 | ", image='" + image + '\'';
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/myfood/model/Ranking.java:
--------------------------------------------------------------------------------
1 | package com.spring.myfood.model;
2 |
3 | import java.util.Date;
4 |
5 | import org.springframework.data.annotation.CreatedDate;
6 | import org.springframework.data.annotation.Id;
7 | import org.springframework.data.annotation.LastModifiedDate;
8 | import org.springframework.data.mongodb.core.mapping.Field;
9 |
10 | import com.fasterxml.jackson.annotation.JsonIgnore;
11 | import com.spring.myfood.enums.RankingTypeEnum;
12 |
13 | import lombok.AllArgsConstructor;
14 | import lombok.Getter;
15 | import lombok.NoArgsConstructor;
16 | import lombok.Setter;
17 |
18 | @Getter
19 | @AllArgsConstructor
20 | @NoArgsConstructor
21 | public class Ranking {
22 |
23 | public Ranking(String title, RankingTypeEnum type, int score) {
24 | this.title = title;
25 | this.type = type;
26 | this.score = score;
27 | }
28 |
29 | @Id
30 | private String id;
31 |
32 | @Setter
33 | @Field
34 | private String title;
35 |
36 | @Setter
37 | @Field
38 | private int score = 1;
39 |
40 | @Setter
41 | @Field
42 | private RankingTypeEnum type;
43 |
44 | @JsonIgnore
45 | @CreatedDate
46 | private Date createdAt;
47 |
48 | @JsonIgnore
49 | @LastModifiedDate
50 | private Date updatedAt;
51 |
52 | @Override
53 | public String toString() {
54 | return "Ranking{" +
55 | "id=" + id +
56 | ", title='" + title + '\'' +
57 | ", score=" + score +
58 | ", type='" + type + '\'' +
59 | ", createdAt=" + createdAt +
60 | ", updatedAt=" + updatedAt +
61 | '}';
62 | }
63 | }
--------------------------------------------------------------------------------
/src/main/java/com/spring/myfood/mongo/MyFoodMongo.java:
--------------------------------------------------------------------------------
1 | package com.spring.myfood.mongo;
2 |
3 | import java.util.List;
4 | import java.util.regex.Pattern;
5 |
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.data.domain.Page;
8 | import org.springframework.data.domain.Pageable;
9 | import org.springframework.data.domain.Sort;
10 | import org.springframework.data.mongodb.core.MongoTemplate;
11 | import org.springframework.data.mongodb.core.query.Criteria;
12 | import org.springframework.data.mongodb.core.query.Query;
13 | import org.springframework.data.support.PageableExecutionUtils;
14 | import org.springframework.stereotype.Component;
15 |
16 | import com.spring.myfood.enums.FoodCategoryEnum;
17 | import com.spring.myfood.enums.RankingTypeEnum;
18 | import com.spring.myfood.model.Product;
19 | import com.spring.myfood.model.Ranking;
20 |
21 | @Component
22 | public class MyFoodMongo {
23 |
24 | @Autowired
25 | private MongoTemplate mongoTemplate;
26 |
27 | public Ranking findRankingByTitleAndType(String title, RankingTypeEnum type) {
28 | Query query = new Query();
29 | query.addCriteria(Criteria.where("title").is(title)
30 | .regex(Pattern.compile("^" + title + "$", Pattern.CASE_INSENSITIVE)).and("type").is(type));
31 | return mongoTemplate.findOne(query, Ranking.class);
32 | }
33 |
34 | public List searchFoods(String foodTitle) {
35 | Query query = new Query();
36 | Criteria criteria = Criteria.where("name")
37 | .regex(Pattern.compile(".*" + foodTitle + ".*", Pattern.CASE_INSENSITIVE));
38 | query.addCriteria(criteria);
39 | List products = mongoTemplate.find(query, Product.class);
40 |
41 | return products;
42 | }
43 |
44 | public List findMostScoredFoods(Pageable pageable) {
45 | Query query = new Query(Criteria.where("type").is("FOOD"))
46 | .with(Sort.by(Sort.Order.desc("score")))
47 | .with(pageable);
48 | return mongoTemplate.find(query, Ranking.class);
49 | }
50 |
51 | public List findMostSearchedCategories(Pageable pageable) {
52 | Query query = new Query(Criteria.where("type").is("CATEGORY"))
53 | .with(Sort.by(Sort.Order.desc("score")))
54 | .with(pageable);
55 | return mongoTemplate.find(query, Ranking.class);
56 | }
57 |
58 | public Page findProductsByCategory(FoodCategoryEnum category, Pageable pageable) {
59 | Query query = new Query();
60 | query.addCriteria(Criteria.where("category").is(category));
61 | query.with(pageable);
62 |
63 | // Nova instância de Query apenas para a contagem
64 | Query countQuery = new Query();
65 | countQuery.addCriteria(Criteria.where("category").is(category));
66 |
67 | long total = mongoTemplate.count(countQuery, Product.class);
68 |
69 | return PageableExecutionUtils.getPage(
70 | mongoTemplate.find(query, Product.class),
71 | pageable,
72 | () -> total);
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/myfood/repository/ProductRepository.java:
--------------------------------------------------------------------------------
1 | package com.spring.myfood.repository;
2 |
3 | import org.springframework.data.mongodb.repository.MongoRepository;
4 |
5 | import com.spring.myfood.model.Product;
6 |
7 | public interface ProductRepository extends MongoRepository {
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/myfood/repository/RankingRepository.java:
--------------------------------------------------------------------------------
1 | package com.spring.myfood.repository;
2 |
3 | import org.springframework.data.mongodb.repository.MongoRepository;
4 |
5 | import com.spring.myfood.model.Ranking;
6 |
7 | public interface RankingRepository extends MongoRepository {
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/myfood/service/ProductService.java:
--------------------------------------------------------------------------------
1 | package com.spring.myfood.service;
2 |
3 | import java.util.List;
4 |
5 | import org.apache.coyote.BadRequestException;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.data.domain.Page;
8 | import org.springframework.data.domain.Pageable;
9 | import org.springframework.stereotype.Service;
10 |
11 | import com.spring.myfood.dtos.request.RequestProductDTO;
12 | import com.spring.myfood.dtos.response.ResponseSearchFoodDTO;
13 | import com.spring.myfood.enums.FoodCategoryEnum;
14 | import com.spring.myfood.enums.RankingTypeEnum;
15 | import com.spring.myfood.model.Product;
16 | import com.spring.myfood.model.Ranking;
17 | import com.spring.myfood.mongo.MyFoodMongo;
18 | import com.spring.myfood.repository.ProductRepository;
19 |
20 | @Service
21 | public class ProductService {
22 |
23 | @Autowired
24 | public ProductRepository productRepository;
25 |
26 | @Autowired
27 | private MyFoodMongo myFoodMongo;
28 |
29 | public Product registerNewProduct(RequestProductDTO product) throws BadRequestException {
30 |
31 | String upperCategory = product.getCategory().toUpperCase();
32 |
33 | try {
34 | FoodCategoryEnum.valueOf(upperCategory);
35 | } catch (IllegalArgumentException e) {
36 | throw new BadRequestException("Category not found");
37 | }
38 |
39 | FoodCategoryEnum category = FoodCategoryEnum.valueOf(upperCategory);
40 |
41 | if ((product.getName() == null) && (product.getDescription() == null) && (product.getPrice() == 0)
42 | && (product.getImage() == null)) {
43 | throw new BadRequestException("All fields must be filled");
44 | }
45 |
46 | Product newProduct = new Product();
47 | newProduct.setName(product.getName());
48 | newProduct.setDescription(product.getDescription());
49 | newProduct.setPrice(product.getPrice());
50 | newProduct.setImage(product.getImage());
51 | newProduct.setCategory(category);
52 |
53 | return productRepository.save(newProduct);
54 | }
55 |
56 | public Page findAllProducts(Pageable pageable) {
57 | return productRepository.findAll(pageable);
58 | }
59 |
60 | public Page findProductsByCategory(FoodCategoryEnum category, Pageable pageable) {
61 | return myFoodMongo.findProductsByCategory(category, pageable);
62 | }
63 |
64 | public ResponseSearchFoodDTO findProductById(String id) {
65 | if (id == null) {
66 | throw new IllegalArgumentException("Id cannot be null");
67 | }
68 | Product product = productRepository.findById(id).get();
69 |
70 | if (product == null) {
71 | throw new IllegalArgumentException("Product not found");
72 | }
73 |
74 | ResponseSearchFoodDTO response = new ResponseSearchFoodDTO();
75 | response.setName(product.getName());
76 | response.setDescription(product.getDescription());
77 | response.setPrice(product.getPrice());
78 | response.setImage(product.getImage());
79 | response.setCategory(product.getCategory());
80 |
81 | Ranking foundRanking = myFoodMongo.findRankingByTitleAndType(product.getName(), RankingTypeEnum.FOOD);
82 |
83 | if (foundRanking != null) {
84 | response.setScore(foundRanking.getScore());
85 | } else {
86 | response.setScore(0);
87 | }
88 |
89 | return response;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/main/java/com/spring/myfood/service/RankingService.java:
--------------------------------------------------------------------------------
1 | package com.spring.myfood.service;
2 |
3 | import java.util.List;
4 | import java.util.Optional;
5 |
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.data.domain.Pageable;
8 | import org.springframework.stereotype.Service;
9 |
10 | import com.spring.myfood.enums.FoodCategoryEnum;
11 | import com.spring.myfood.enums.RankingTypeEnum;
12 | import com.spring.myfood.model.Product;
13 | import com.spring.myfood.model.Ranking;
14 | import com.spring.myfood.mongo.MyFoodMongo;
15 | import com.spring.myfood.repository.ProductRepository;
16 | import com.spring.myfood.repository.RankingRepository;
17 |
18 | @Service
19 | public class RankingService {
20 |
21 | @Autowired
22 | public RankingRepository rankingRepository;
23 |
24 | @Autowired
25 | public ProductRepository productRepository;
26 |
27 | @Autowired
28 | private MyFoodMongo myFoodMongo;
29 |
30 | public List searchingFoods(String foodTitle) {
31 | List foundProducts = searchFoods(foodTitle.toLowerCase());
32 |
33 | for (Product product : foundProducts) {
34 | updateFoodRanking(product, RankingTypeEnum.FOOD);
35 | }
36 |
37 | return foundProducts;
38 | }
39 |
40 | public void updateFoodRanking(Product product, RankingTypeEnum type) {
41 |
42 | if (product != null) {
43 |
44 | Ranking existsRanking = myFoodMongo.findRankingByTitleAndType(product.getName(), type);
45 |
46 | if (existsRanking != null) {
47 | updateCategoryRanking(existsRanking.getTitle());
48 | existsRanking.setScore(existsRanking.getScore() + 1);
49 | rankingRepository.save(existsRanking);
50 | } else {
51 | Ranking newRanking = new Ranking(product.getName(), type, 1);
52 | updateCategoryRanking(newRanking.getTitle());
53 | rankingRepository.save(newRanking);
54 | }
55 | } else {
56 | throw new IllegalArgumentException("Product cannot be null");
57 | }
58 | }
59 |
60 | public void updateCategoryRanking(String foodTitle) {
61 |
62 | List foundProduct = myFoodMongo.searchFoods(foodTitle);
63 |
64 | if (foundProduct.isEmpty()) {
65 | return;
66 | }
67 |
68 | String category = foundProduct.get(0).getCategory().toString().toUpperCase();
69 |
70 | if (isValidCategory(category)) {
71 | Ranking existsRanking = myFoodMongo.findRankingByTitleAndType(category, RankingTypeEnum.CATEGORY);
72 |
73 | if (existsRanking != null) {
74 | existsRanking.setScore(existsRanking.getScore() + 1);
75 | rankingRepository.save(existsRanking);
76 | } else {
77 | Ranking newRanking = new Ranking(category, RankingTypeEnum.CATEGORY, 1);
78 | rankingRepository.save(newRanking);
79 | }
80 | } else {
81 | return;
82 | }
83 | }
84 |
85 | private boolean isValidCategory(String category) {
86 | try {
87 | FoodCategoryEnum.valueOf(category);
88 | return true;
89 | } catch (IllegalArgumentException e) {
90 | return false;
91 | }
92 | }
93 |
94 | private List searchFoods(String lowerFoodTitle) {
95 | return myFoodMongo.searchFoods(lowerFoodTitle);
96 | }
97 |
98 | public Ranking findRankingById(String id) {
99 | if (id == null) {
100 | throw new IllegalArgumentException("Id cannot be null");
101 | }
102 | Optional foundRanking = rankingRepository.findById(id);
103 |
104 | if (foundRanking.isEmpty()) {
105 | throw new IllegalArgumentException("Ranking not found");
106 | }
107 |
108 | return foundRanking.get();
109 | }
110 |
111 | public List mostScoredFoods(Pageable pageable) {
112 | return myFoodMongo.findMostScoredFoods(pageable);
113 | }
114 |
115 | public List findMostSearchedCategories(Pageable pageable) {
116 | return myFoodMongo.findMostSearchedCategories(pageable);
117 | }
118 | }
--------------------------------------------------------------------------------
/src/main/java/com/spring/myfood/utils/GlobalExceptionHandler.java:
--------------------------------------------------------------------------------
1 | package com.spring.myfood.utils;
2 |
3 | import org.springframework.data.crossstore.ChangeSetPersister.NotFoundException;
4 | import org.springframework.http.HttpStatus;
5 | import org.springframework.http.ResponseEntity;
6 | import org.springframework.web.bind.annotation.ControllerAdvice;
7 | import org.springframework.web.bind.annotation.ExceptionHandler;
8 |
9 | import jakarta.validation.ConstraintViolationException;
10 |
11 | import org.springframework.web.bind.MethodArgumentNotValidException;
12 | import org.springframework.validation.FieldError;
13 |
14 | import java.nio.file.AccessDeniedException;
15 | import java.util.HashMap;
16 | import java.util.Map;
17 |
18 | @ControllerAdvice
19 | public class GlobalExceptionHandler {
20 |
21 | @ExceptionHandler(Exception.class)
22 | public ResponseEntity handleException(Exception e) {
23 | return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
24 | .body("Ocorreu um erro no servidor: " + e.getMessage());
25 | }
26 |
27 | @ExceptionHandler(NotFoundException.class)
28 | public ResponseEntity handleResourceNotFoundException(NotFoundException e) {
29 | return ResponseEntity.status(HttpStatus.NOT_FOUND)
30 | .body("Recurso não encontrado: " + e.getMessage());
31 | }
32 |
33 | @ExceptionHandler(MethodArgumentNotValidException.class)
34 | public ResponseEntity