diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..9afe39a8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,9 @@ +# Default ignored files +/shelf/ +/workspace.xml +.idea/ +*.iml +target/ +.classpath +.project +.settings/ \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 00000000..93d5e219 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 00000000..712ab9d9 --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..d3c65fd9 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..7dd09ad8 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/api-root.yaml b/api-root.yaml new file mode 100644 index 00000000..3e98536f --- /dev/null +++ b/api-root.yaml @@ -0,0 +1,96 @@ +openapi: 3.0.0 +info: + title: Combined SimilarProducts API + version: '1.0' +servers: + - url: 'http://localhost:5000' + - url: 'http://localhost:3001' + +paths: + /product: + get: + operationId: getAllProducts + summary: Products + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ProductDetail' + '404': + description: Product Not found + + /product/{productId}/similar: + get: + operationId: getSimilarProductDetails + summary: Similar products + parameters: + - name: productId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ProductDetail' + '404': + description: Product Not found + + /product/{productId}/similarids: + get: + operationId: getSimilarProductsIds + summary: Gets the ids of similar products + parameters: + - name: productId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/SimilarProductIds' + + /product/{productId}: + get: + operationId: getProductId + summary: Gets a product detail + parameters: + - name: productId + in: path + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ProductDetail' + '404': + description: Product Not found + +components: + schemas: + ProductDetail: + type: object + $ref: './similarProducts.yaml#/components/schemas/ProductDetail' + description: List of similar products with full details + SimilarProductIds: + type: array + items: + type: string + description: List of similar product IDs diff --git a/docker-compose.yaml b/docker-compose.yaml index 2b20a5d9..848f7aee 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -4,6 +4,7 @@ services: image: influxdb:1.8.2 ports: - "8086:8086" + - "5005:5005" environment: - INFLUXDB_DB=k6 grafana: @@ -23,6 +24,7 @@ services: volumes: - ./shared/simulado:/app command: ./bin/simulado -f /app/mocks.json + platform: linux/amd64 k6: image: loadimpact/k6:0.28.0 ports: @@ -33,3 +35,10 @@ services: - K6_OUT=influxdb=http://influxdb:8086/k6 extra_hosts: - "host.docker.internal:host-gateway" + backend: + build: . + ports: + - "5000:5000" + depends_on: + - influxdb + - grafana diff --git a/dockerfile b/dockerfile new file mode 100644 index 00000000..759bef9d --- /dev/null +++ b/dockerfile @@ -0,0 +1,18 @@ +FROM maven:3.9.6-eclipse-temurin-17 AS build +WORKDIR /app + +COPY pom.xml . +COPY *.yaml . +COPY src ./src +COPY shared/simulado/mocks.json shared/simulado/mocks.json + +RUN mvn clean package spring-boot:repackage -DskipTests + +FROM eclipse-temurin:17-jre +WORKDIR /app + +COPY --from=build /app/target/*.jar backendDevTest.jar + +EXPOSE 5000 5005 + +CMD ["java", "-jar", "backendDevTest.jar", "--server.port=5000", "--server.address=0.0.0.0"] diff --git a/existingApis.yaml b/existingApis.yaml index cf7805e8..bd94420e 100644 --- a/existingApis.yaml +++ b/existingApis.yaml @@ -5,6 +5,22 @@ info: servers: - url: 'http://localhost:3001' paths: + '/product': + get: + operationId: getAllProducts + summary: Products + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ProductDetail' + '404': + description: Product Not found + '/product/{productId}/similarids': parameters: - schema: diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..cffaaf00 --- /dev/null +++ b/pom.xml @@ -0,0 +1,109 @@ + + 4.0.0 + + org.backendDevTest + backendDevTest + 1.0-SNAPSHOT + jar + + + 17 + 3.3.0 + 7.0.0 + + + + + org.springframework.boot + spring-boot-starter-web + ${spring.boot.version} + + + + org.springframework.boot + spring-boot-starter-validation + ${spring.boot.version} + + + + org.openapitools + jackson-databind-nullable + 0.2.6 + + + + io.swagger.core.v3 + swagger-core-jakarta + 2.2.25 + + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + 2.2.0 + + + + + src/main/java + + + src/main/resources + + + shared/simulado + . + + mocks.json + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.openapitools + openapi-generator-maven-plugin + ${openapi.generator.version} + + + generate-api + + generate + + + ${project.basedir}/api-root.yaml + spring + ${project.build.directory}/generated-sources + org.backendDevTest.infra.api + org.backendDevTest.infra.model + + true + true + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.10.1 + + ${java.version} + ${java.version} + UTF-8 + ${project.build.directory}/generated-sources + + + + + diff --git a/shared/simulado/mocks.json b/shared/simulado/mocks.json index 31f63bc4..44d40583 100644 --- a/shared/simulado/mocks.json +++ b/shared/simulado/mocks.json @@ -73,5 +73,16 @@ "body": "{\"id\":\"10000\",\"name\":\"Leather jacket\",\"price\":89.99,\"availability\":true}", "delay": 50000, "headers": {"Content-Type": "application/json"} + }, + { + "path": "/product/4/similar", + "body": "[{\"message\":\"Product not found\"}]", + "status": 404, + "headers": {"Content-Type": "application/json"} + }, + { + "path": "/product/10000/similar", + "body": "[{\"id\":\"100000\",\"name\":\"Wool jacket\",\"price\":69.99,\"availability\":true}]", + "headers": {"Content-Type": "application/json"} } ] \ No newline at end of file diff --git a/src/main/java/org/backendDevTest/BackendDevTestApplication.java b/src/main/java/org/backendDevTest/BackendDevTestApplication.java new file mode 100644 index 00000000..85c7378c --- /dev/null +++ b/src/main/java/org/backendDevTest/BackendDevTestApplication.java @@ -0,0 +1,12 @@ +package org.backendDevTest; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class BackendDevTestApplication { + + public static void main(String[] args) { + SpringApplication.run(BackendDevTestApplication.class, args); + } +} diff --git a/src/main/java/org/backendDevTest/application/service/ProductService.java b/src/main/java/org/backendDevTest/application/service/ProductService.java new file mode 100644 index 00000000..2b759d59 --- /dev/null +++ b/src/main/java/org/backendDevTest/application/service/ProductService.java @@ -0,0 +1,70 @@ +package org.backendDevTest.application.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.backendDevTest.infra.model.ProductDetail; +import org.backendDevTest.infra.repository.MockRepository; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +@Service +public class ProductService { + + private final MockRepository mockRepository; + private final ObjectMapper mapper = new ObjectMapper(); + + public ProductService(MockRepository mockRepository) { + this.mockRepository = mockRepository; + } + + public List getProducts() throws JsonProcessingException { + String body = mockRepository.getAllProducts(); + return getProductDetails(body); + } + + public ProductDetail getProductId(String productId) throws JsonProcessingException { + return Optional.ofNullable(mapper.readValue(mockRepository.getProduct(productId), ProductDetail.class)) + .orElse(new ProductDetail()); + } + + public List getSimilarProduct(String productId) throws JsonProcessingException { + String body = mockRepository.getSimilarProducts(productId); + return getProductDetails(body); + } + + public List getSimilarIds(String productId) throws JsonProcessingException { + String body = mockRepository.getSimilarIds(productId); + + JsonNode node = mapper.readTree(body); + if (node.isArray()) { + if (!node.isEmpty() && node.get(0).has("message")) { + return List.of(); + } + return mapper.readValue(body, new TypeReference>() {}); + } + + return List.of(); + } + + private List getProductDetails(String body) throws JsonProcessingException { + if (body == null || body.isBlank()) { + return List.of(); + } + + List items = mapper.readValue(body, new TypeReference<>() {}); + List products = new ArrayList<>(); + + for (JsonNode item : items) { + if (item != null && item.isObject() && item.has("id")) { + products.add(mapper.treeToValue(item, ProductDetail.class)); + } + } + + return products; + } +} diff --git a/src/main/java/org/backendDevTest/infra/api/ProductApiControllerImpl.java b/src/main/java/org/backendDevTest/infra/api/ProductApiControllerImpl.java new file mode 100644 index 00000000..98ce95d0 --- /dev/null +++ b/src/main/java/org/backendDevTest/infra/api/ProductApiControllerImpl.java @@ -0,0 +1,63 @@ +package org.backendDevTest.infra.api; + +import com.fasterxml.jackson.core.JsonProcessingException; +import org.backendDevTest.application.service.ProductService; +import org.backendDevTest.infra.model.ProductDetail; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.util.ObjectUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/product") +@CrossOrigin() +public class ProductApiControllerImpl implements ProductApi { + + @Autowired + private ProductService productService; + + @Override + @GetMapping() + public ResponseEntity> getAllProducts() { + try { + return ResponseEntity.ok(productService.getProducts()); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + @Override + @GetMapping("/{productId}") + public ResponseEntity getProductId(@PathVariable("productId") String productId) { + try { + ProductDetail productDetail = productService.getProductId(productId); + return ObjectUtils.isEmpty(productDetail) ? ResponseEntity.notFound().build() : ResponseEntity.ok(productDetail); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + @Override + @GetMapping("/{productId}/similar") + public ResponseEntity> getSimilarProductDetails(@PathVariable("productId") String productId) { + try { + List details = productService.getSimilarProduct(productId); + return details.isEmpty() ? ResponseEntity.notFound().build() : ResponseEntity.ok(details); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + @Override + @GetMapping("/{productId}/similarids") + public ResponseEntity> getSimilarProductsIds(@PathVariable("productId") String productId) { + try { + List similarIds = productService.getSimilarIds(productId); + return similarIds.isEmpty() ? ResponseEntity.notFound().build() : ResponseEntity.ok(similarIds); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/backendDevTest/infra/repository/MockRepository.java b/src/main/java/org/backendDevTest/infra/repository/MockRepository.java new file mode 100644 index 00000000..3f3b9529 --- /dev/null +++ b/src/main/java/org/backendDevTest/infra/repository/MockRepository.java @@ -0,0 +1,68 @@ +package org.backendDevTest.infra.repository; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.annotation.PostConstruct; +import org.springframework.stereotype.Repository; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +@Repository +public class MockRepository { + private final Map productBodies = new HashMap<>(); + private final Map similarIdsBodies = new HashMap<>(); + + @PostConstruct + public void init() { + ObjectMapper mapper = new ObjectMapper(); + byte[] bytes; + try (InputStream is = getClass().getResourceAsStream("/mocks.json")) { + if (is == null) { + throw new IllegalStateException("Could not find mocks.json in classpath"); + } + + bytes = is.readAllBytes(); + + String content = new String(bytes, StandardCharsets.UTF_8); + + JsonNode root = mapper.readTree(content); + + for (JsonNode node : root) { + String path = node.get("path").asText(); + String body = node.has("body") ? node.get("body").asText() : null; + + if (path.endsWith("/similarids")) { + similarIdsBodies.put(path.split("/product/")[1].replace("/similarids", ""), body); + } else if (path.endsWith("/similarProducts")) { + productBodies.put(path.split("/product/")[1].replace("/similarProducts", ""), body); + } else if (path.matches("/product/\\d+")) { + productBodies.put(path.split("/product/")[1], body); + } else { + productBodies.put(path.split("/product/")[1], body); + } + } + } catch (Exception e) { + e.getStackTrace(); + } + } + + public String getAllProducts() throws JsonProcessingException { + return productBodies.values().toString(); + } + + public String getProduct(String id) { + return productBodies.get(id); + } + + public String getSimilarProducts(String id) { + return productBodies.get(id); + } + + public String getSimilarIds(String id) { + return similarIdsBodies.get(id); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 00000000..a5ea1b15 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,9 @@ +server: + port: 8080 + address: 0.0.0.0 +springdoc: + swagger-ui: + path: / +logging: + level: + org.springframework.web: DEBUG \ No newline at end of file diff --git a/target/backendDevTest-1.0-SNAPSHOT.jar b/target/backendDevTest-1.0-SNAPSHOT.jar new file mode 100644 index 00000000..15873eba Binary files /dev/null and b/target/backendDevTest-1.0-SNAPSHOT.jar differ diff --git a/target/classes/application.yml b/target/classes/application.yml new file mode 100644 index 00000000..a5ea1b15 --- /dev/null +++ b/target/classes/application.yml @@ -0,0 +1,9 @@ +server: + port: 8080 + address: 0.0.0.0 +springdoc: + swagger-ui: + path: / +logging: + level: + org.springframework.web: DEBUG \ No newline at end of file diff --git a/target/classes/org/backendDevTest/BackendDevTestApplication.class b/target/classes/org/backendDevTest/BackendDevTestApplication.class new file mode 100644 index 00000000..59d5934f Binary files /dev/null and b/target/classes/org/backendDevTest/BackendDevTestApplication.class differ diff --git a/target/classes/org/backendDevTest/infra/api/ApiUtil.class b/target/classes/org/backendDevTest/infra/api/ApiUtil.class new file mode 100644 index 00000000..d5750e84 Binary files /dev/null and b/target/classes/org/backendDevTest/infra/api/ApiUtil.class differ diff --git a/target/classes/org/backendDevTest/infra/api/ProductApi.class b/target/classes/org/backendDevTest/infra/api/ProductApi.class new file mode 100644 index 00000000..91f4c6f1 Binary files /dev/null and b/target/classes/org/backendDevTest/infra/api/ProductApi.class differ diff --git a/target/classes/org/backendDevTest/infra/api/ProductApiControllerImpl.class b/target/classes/org/backendDevTest/infra/api/ProductApiControllerImpl.class new file mode 100644 index 00000000..736bc929 Binary files /dev/null and b/target/classes/org/backendDevTest/infra/api/ProductApiControllerImpl.class differ diff --git a/target/classes/org/backendDevTest/infra/model/ProductDetail.class b/target/classes/org/backendDevTest/infra/model/ProductDetail.class new file mode 100644 index 00000000..790bd9db Binary files /dev/null and b/target/classes/org/backendDevTest/infra/model/ProductDetail.class differ diff --git a/target/classes/org/backendDevTest/infra/model/ProductDetailBasic.class b/target/classes/org/backendDevTest/infra/model/ProductDetailBasic.class new file mode 100644 index 00000000..8739505d Binary files /dev/null and b/target/classes/org/backendDevTest/infra/model/ProductDetailBasic.class differ diff --git a/target/classes/org/backendDevTest/infra/model/ProductFullDetail.class b/target/classes/org/backendDevTest/infra/model/ProductFullDetail.class new file mode 100644 index 00000000..d254b20d Binary files /dev/null and b/target/classes/org/backendDevTest/infra/model/ProductFullDetail.class differ diff --git a/target/generated-sources/.openapi-generator-ignore b/target/generated-sources/.openapi-generator-ignore new file mode 100644 index 00000000..7484ee59 --- /dev/null +++ b/target/generated-sources/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/target/generated-sources/.openapi-generator/FILES b/target/generated-sources/.openapi-generator/FILES new file mode 100644 index 00000000..1674b11d --- /dev/null +++ b/target/generated-sources/.openapi-generator/FILES @@ -0,0 +1,8 @@ +.openapi-generator-ignore +README.md +pom.xml +src/main/java/org/backendDevTest/infra/api/ApiUtil.java +src/main/java/org/backendDevTest/infra/api/ProductApi.java +src/main/java/org/backendDevTest/infra/model/ProductDetail.java +src/main/java/org/backendDevTest/infra/model/ProductDetailBasic.java +src/main/java/org/backendDevTest/infra/model/ProductFullDetail.java diff --git a/target/generated-sources/.openapi-generator/VERSION b/target/generated-sources/.openapi-generator/VERSION new file mode 100644 index 00000000..41225218 --- /dev/null +++ b/target/generated-sources/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.0.0 \ No newline at end of file diff --git a/target/generated-sources/.openapi-generator/api-root.yaml-generate-api.sha256 b/target/generated-sources/.openapi-generator/api-root.yaml-generate-api.sha256 new file mode 100644 index 00000000..1c7c24f5 --- /dev/null +++ b/target/generated-sources/.openapi-generator/api-root.yaml-generate-api.sha256 @@ -0,0 +1 @@ +f8e24043579f567fe9994361529e8544e8bee5a0db7c242ab674515ca2f18ca3 \ No newline at end of file diff --git a/target/generated-sources/README.md b/target/generated-sources/README.md new file mode 100644 index 00000000..d43a1de3 --- /dev/null +++ b/target/generated-sources/README.md @@ -0,0 +1,27 @@ + +# OpenAPI generated API stub + +Spring Framework stub + + +## Overview +This code was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. +By using the [OpenAPI-Spec](https://openapis.org), you can easily generate an API stub. +This is an example of building API stub interfaces in Java using the Spring framework. + +The stubs generated can be used in your existing Spring-MVC or Spring-Boot application to create controller endpoints +by adding ```@Controller``` classes that implement the interface. Eg: +```java +@Controller +public class PetController implements PetApi { +// implement all PetApi methods +} +``` + +You can also use the interface to create [Spring-Cloud Feign clients](http://projects.spring.io/spring-cloud/spring-cloud.html#spring-cloud-feign-inheritance).Eg: +```java +@FeignClient(name="pet", url="http://petstore.swagger.io/v2") +public interface PetClient extends PetApi { + +} +``` diff --git a/target/generated-sources/pom.xml b/target/generated-sources/pom.xml new file mode 100644 index 00000000..9452f3bd --- /dev/null +++ b/target/generated-sources/pom.xml @@ -0,0 +1,89 @@ + + 4.0.0 + org.openapitools + openapi-spring + jar + openapi-spring + 1.0 + + 17 + ${java.version} + ${java.version} + UTF-8 + 2.0.2 + 4.15.5 + + + org.springframework.boot + spring-boot-starter-parent + 3.0.0 + + + + + + repository.spring.milestone + Spring Milestone Repository + https://repo.spring.io/milestone + + + + + spring-milestones + https://repo.spring.io/milestone + + + + + src/main/java + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.data + spring-data-commons + + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + ${springdoc.version} + + + + com.google.code.findbugs + jsr305 + 3.0.2 + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + org.openapitools + jackson-databind-nullable + 0.2.6 + + + + org.springframework.boot + spring-boot-starter-validation + + + com.fasterxml.jackson.core + jackson-databind + + + org.springframework.boot + spring-boot-starter-test + test + + + diff --git a/target/generated-sources/src/main/java/org/backendDevTest/infra/api/ApiUtil.java b/target/generated-sources/src/main/java/org/backendDevTest/infra/api/ApiUtil.java new file mode 100644 index 00000000..da96aa03 --- /dev/null +++ b/target/generated-sources/src/main/java/org/backendDevTest/infra/api/ApiUtil.java @@ -0,0 +1,19 @@ +package org.backendDevTest.infra.api; + +import org.springframework.web.context.request.NativeWebRequest; + +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; + +public class ApiUtil { + public static void setExampleResponse(NativeWebRequest req, String contentType, String example) { + try { + HttpServletResponse res = req.getNativeResponse(HttpServletResponse.class); + res.setCharacterEncoding("UTF-8"); + res.addHeader("Content-Type", contentType); + res.getWriter().print(example); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/target/generated-sources/src/main/java/org/backendDevTest/infra/api/ProductApi.java b/target/generated-sources/src/main/java/org/backendDevTest/infra/api/ProductApi.java new file mode 100644 index 00000000..d226a798 --- /dev/null +++ b/target/generated-sources/src/main/java/org/backendDevTest/infra/api/ProductApi.java @@ -0,0 +1,159 @@ +/** + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) (7.0.0). + * https://openapi-generator.tech + * Do not edit the class manually. + */ +package org.backendDevTest.infra.api; + +import org.backendDevTest.infra.model.ProductDetail; +import org.backendDevTest.infra.model.ProductDetailBasic; +import io.swagger.v3.oas.annotations.ExternalDocumentation; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.multipart.MultipartFile; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import jakarta.annotation.Generated; + +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2025-08-29T11:21:00.996791+02:00[Europe/Madrid]") +@Validated +@Tag(name = "product", description = "the product API") +public interface ProductApi { + + default Optional getRequest() { + return Optional.empty(); + } + + /** + * GET /product/{productId} : Gets a product detail + * + * @param productId (required) + * @return OK (status code 200) + * or Product Not found (status code 404) + */ + @Operation( + operationId = "getProductId", + summary = "Gets a product detail", + responses = { + @ApiResponse(responseCode = "200", description = "OK", content = { + @Content(mediaType = "application/json", schema = @Schema(implementation = ProductDetailBasic.class)) + }), + @ApiResponse(responseCode = "404", description = "Product Not found") + } + ) + @RequestMapping( + method = RequestMethod.GET, + value = "/product/{productId}", + produces = { "application/json" } + ) + default ResponseEntity getProductId( + @Parameter(name = "productId", description = "", required = true, in = ParameterIn.PATH) @PathVariable("productId") String productId + ) { + getRequest().ifPresent(request -> { + for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) { + if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) { + String exampleString = "{ \"price\" : 0.8008281904610115, \"name\" : \"name\", \"id\" : \"id\", \"availability\" : true }"; + ApiUtil.setExampleResponse(request, "application/json", exampleString); + break; + } + } + }); + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + + } + + + /** + * GET /product/{productId}/similar : Similar products + * + * @param productId (required) + * @return OK (status code 200) + * or Product Not found (status code 404) + */ + @Operation( + operationId = "getSimilarProductDetails", + summary = "Similar products", + responses = { + @ApiResponse(responseCode = "200", description = "OK", content = { + @Content(mediaType = "application/json", array = @ArraySchema(schema = @Schema(implementation = ProductDetail.class))) + }), + @ApiResponse(responseCode = "404", description = "Product Not found") + } + ) + @RequestMapping( + method = RequestMethod.GET, + value = "/product/{productId}/similar", + produces = { "application/json" } + ) + default ResponseEntity> getSimilarProductDetails( + @Parameter(name = "productId", description = "", required = true, in = ParameterIn.PATH) @PathVariable("productId") String productId + ) { + getRequest().ifPresent(request -> { + for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) { + if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) { + String exampleString = "[ { \"price\" : 0.8008281904610115, \"name\" : \"name\", \"id\" : \"id\", \"availability\" : true }, { \"price\" : 0.8008281904610115, \"name\" : \"name\", \"id\" : \"id\", \"availability\" : true } ]"; + ApiUtil.setExampleResponse(request, "application/json", exampleString); + break; + } + } + }); + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + + } + + + /** + * GET /product/{productId}/similarids : Gets the ids of similar products + * + * @param productId (required) + * @return OK (status code 200) + */ + @Operation( + operationId = "getSimilarProductsIds", + summary = "Gets the ids of similar products", + responses = { + @ApiResponse(responseCode = "200", description = "OK", content = { + @Content(mediaType = "application/json", array = @ArraySchema(schema = @Schema(implementation = String.class))) + }) + } + ) + @RequestMapping( + method = RequestMethod.GET, + value = "/product/{productId}/similarids", + produces = { "application/json" } + ) + default ResponseEntity> getSimilarProductsIds( + @Parameter(name = "productId", description = "", required = true, in = ParameterIn.PATH) @PathVariable("productId") String productId + ) { + getRequest().ifPresent(request -> { + for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) { + if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) { + String exampleString = "[ \"\", \"\" ]"; + ApiUtil.setExampleResponse(request, "application/json", exampleString); + break; + } + } + }); + return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED); + + } + +} diff --git a/target/generated-sources/src/main/java/org/backendDevTest/infra/model/ProductDetail.java b/target/generated-sources/src/main/java/org/backendDevTest/infra/model/ProductDetail.java new file mode 100644 index 00000000..2cdecfa3 --- /dev/null +++ b/target/generated-sources/src/main/java/org/backendDevTest/infra/model/ProductDetail.java @@ -0,0 +1,171 @@ +package org.backendDevTest.infra.model; + +import java.net.URI; +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import java.math.BigDecimal; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * Product detail + */ + +@Schema(name = "ProductDetail", description = "Product detail") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2025-09-02T23:05:51.910583500+02:00[Europe/Madrid]") +public class ProductDetail { + + private String id; + + private String name; + + private BigDecimal price; + + private Boolean availability; + + public ProductDetail() { + super(); + } + + /** + * Constructor with only required parameters + */ + public ProductDetail(String id, String name, BigDecimal price, Boolean availability) { + this.id = id; + this.name = name; + this.price = price; + this.availability = availability; + } + + public ProductDetail id(String id) { + this.id = id; + return this; + } + + /** + * Get id + * @return id + */ + @NotNull @Size(min = 1) + @Schema(name = "id", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("id") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public ProductDetail name(String name) { + this.name = name; + return this; + } + + /** + * Get name + * @return name + */ + @NotNull @Size(min = 1) + @Schema(name = "name", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("name") + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public ProductDetail price(BigDecimal price) { + this.price = price; + return this; + } + + /** + * Get price + * @return price + */ + @NotNull @Valid + @Schema(name = "price", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("price") + public BigDecimal getPrice() { + return price; + } + + public void setPrice(BigDecimal price) { + this.price = price; + } + + public ProductDetail availability(Boolean availability) { + this.availability = availability; + return this; + } + + /** + * Get availability + * @return availability + */ + @NotNull + @Schema(name = "availability", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("availability") + public Boolean getAvailability() { + return availability; + } + + public void setAvailability(Boolean availability) { + this.availability = availability; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ProductDetail productDetail = (ProductDetail) o; + return Objects.equals(this.id, productDetail.id) && + Objects.equals(this.name, productDetail.name) && + Objects.equals(this.price, productDetail.price) && + Objects.equals(this.availability, productDetail.availability); + } + + @Override + public int hashCode() { + return Objects.hash(id, name, price, availability); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class ProductDetail {\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" price: ").append(toIndentedString(price)).append("\n"); + sb.append(" availability: ").append(toIndentedString(availability)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} + diff --git a/target/generated-sources/src/main/java/org/backendDevTest/infra/model/ProductDetailBasic.java b/target/generated-sources/src/main/java/org/backendDevTest/infra/model/ProductDetailBasic.java new file mode 100644 index 00000000..e2d10fb8 --- /dev/null +++ b/target/generated-sources/src/main/java/org/backendDevTest/infra/model/ProductDetailBasic.java @@ -0,0 +1,171 @@ +package org.backendDevTest.infra.model; + +import java.net.URI; +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import java.math.BigDecimal; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * Product detail + */ + +@Schema(name = "ProductDetailBasic", description = "Product detail") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2025-08-29T11:21:00.996791+02:00[Europe/Madrid]") +public class ProductDetailBasic { + + private String id; + + private String name; + + private BigDecimal price; + + private Boolean availability; + + public ProductDetailBasic() { + super(); + } + + /** + * Constructor with only required parameters + */ + public ProductDetailBasic(String id, String name, BigDecimal price, Boolean availability) { + this.id = id; + this.name = name; + this.price = price; + this.availability = availability; + } + + public ProductDetailBasic id(String id) { + this.id = id; + return this; + } + + /** + * Get id + * @return id + */ + @NotNull @Size(min = 1) + @Schema(name = "id", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("id") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public ProductDetailBasic name(String name) { + this.name = name; + return this; + } + + /** + * Get name + * @return name + */ + @NotNull @Size(min = 1) + @Schema(name = "name", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("name") + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public ProductDetailBasic price(BigDecimal price) { + this.price = price; + return this; + } + + /** + * Get price + * @return price + */ + @NotNull @Valid + @Schema(name = "price", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("price") + public BigDecimal getPrice() { + return price; + } + + public void setPrice(BigDecimal price) { + this.price = price; + } + + public ProductDetailBasic availability(Boolean availability) { + this.availability = availability; + return this; + } + + /** + * Get availability + * @return availability + */ + @NotNull + @Schema(name = "availability", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("availability") + public Boolean getAvailability() { + return availability; + } + + public void setAvailability(Boolean availability) { + this.availability = availability; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ProductDetailBasic productDetailBasic = (ProductDetailBasic) o; + return Objects.equals(this.id, productDetailBasic.id) && + Objects.equals(this.name, productDetailBasic.name) && + Objects.equals(this.price, productDetailBasic.price) && + Objects.equals(this.availability, productDetailBasic.availability); + } + + @Override + public int hashCode() { + return Objects.hash(id, name, price, availability); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class ProductDetailBasic {\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" price: ").append(toIndentedString(price)).append("\n"); + sb.append(" availability: ").append(toIndentedString(availability)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} + diff --git a/target/generated-sources/src/main/java/org/backendDevTest/infra/model/ProductFullDetail.java b/target/generated-sources/src/main/java/org/backendDevTest/infra/model/ProductFullDetail.java new file mode 100644 index 00000000..005bf3c4 --- /dev/null +++ b/target/generated-sources/src/main/java/org/backendDevTest/infra/model/ProductFullDetail.java @@ -0,0 +1,171 @@ +package org.backendDevTest.infra.model; + +import java.net.URI; +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import java.math.BigDecimal; +import org.openapitools.jackson.nullable.JsonNullable; +import java.time.OffsetDateTime; +import jakarta.validation.Valid; +import jakarta.validation.constraints.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import java.util.*; +import jakarta.annotation.Generated; + +/** + * Product detail + */ + +@Schema(name = "ProductFullDetail", description = "Product detail") +@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2025-08-29T11:21:00.996791+02:00[Europe/Madrid]") +public class ProductFullDetail { + + private String id; + + private String name; + + private BigDecimal price; + + private Boolean availability; + + public ProductFullDetail() { + super(); + } + + /** + * Constructor with only required parameters + */ + public ProductFullDetail(String id, String name, BigDecimal price, Boolean availability) { + this.id = id; + this.name = name; + this.price = price; + this.availability = availability; + } + + public ProductFullDetail id(String id) { + this.id = id; + return this; + } + + /** + * Get id + * @return id + */ + @NotNull @Size(min = 1) + @Schema(name = "id", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("id") + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public ProductFullDetail name(String name) { + this.name = name; + return this; + } + + /** + * Get name + * @return name + */ + @NotNull @Size(min = 1) + @Schema(name = "name", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("name") + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public ProductFullDetail price(BigDecimal price) { + this.price = price; + return this; + } + + /** + * Get price + * @return price + */ + @NotNull @Valid + @Schema(name = "price", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("price") + public BigDecimal getPrice() { + return price; + } + + public void setPrice(BigDecimal price) { + this.price = price; + } + + public ProductFullDetail availability(Boolean availability) { + this.availability = availability; + return this; + } + + /** + * Get availability + * @return availability + */ + @NotNull + @Schema(name = "availability", requiredMode = Schema.RequiredMode.REQUIRED) + @JsonProperty("availability") + public Boolean getAvailability() { + return availability; + } + + public void setAvailability(Boolean availability) { + this.availability = availability; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ProductFullDetail productFullDetail = (ProductFullDetail) o; + return Objects.equals(this.id, productFullDetail.id) && + Objects.equals(this.name, productFullDetail.name) && + Objects.equals(this.price, productFullDetail.price) && + Objects.equals(this.availability, productFullDetail.availability); + } + + @Override + public int hashCode() { + return Objects.hash(id, name, price, availability); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class ProductFullDetail {\n"); + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" price: ").append(toIndentedString(price)).append("\n"); + sb.append(" availability: ").append(toIndentedString(availability)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } +} + diff --git a/target/maven-archiver/pom.properties b/target/maven-archiver/pom.properties new file mode 100644 index 00000000..191328d6 --- /dev/null +++ b/target/maven-archiver/pom.properties @@ -0,0 +1,3 @@ +artifactId=backendDevTest +groupId=org.backendDevTest +version=1.0-SNAPSHOT diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 00000000..4d71e436 --- /dev/null +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,7 @@ +org/backendDevTest/infra/api/ProductApi.class +org/backendDevTest/infra/model/ProductDetailBasic.class +org/backendDevTest/infra/api/ProductApiControllerImpl.class +org/backendDevTest/infra/model/ProductFullDetail.class +org/backendDevTest/infra/api/ApiUtil.class +org/backendDevTest/BackendDevTestApplication.class +org/backendDevTest/infra/model/ProductDetail.class diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 00000000..4ef3ac86 --- /dev/null +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,7 @@ +/Users/victoriagarciaarmario/Documents/repos/backendDevTest/src/main/java/org/backendDevTest/infra/api/ProductApiControllerImpl.java +/Users/victoriagarciaarmario/Documents/repos/backendDevTest/target/generated-sources/src/main/java/org/backendDevTest/infra/model/ProductFullDetail.java +/Users/victoriagarciaarmario/Documents/repos/backendDevTest/target/generated-sources/src/main/java/org/backendDevTest/infra/api/ProductApi.java +/Users/victoriagarciaarmario/Documents/repos/backendDevTest/target/generated-sources/src/main/java/org/backendDevTest/infra/model/ProductDetail.java +/Users/victoriagarciaarmario/Documents/repos/backendDevTest/target/generated-sources/src/main/java/org/backendDevTest/infra/model/ProductDetailBasic.java +/Users/victoriagarciaarmario/Documents/repos/backendDevTest/src/main/java/org/backendDevTest/BackendDevTestApplication.java +/Users/victoriagarciaarmario/Documents/repos/backendDevTest/target/generated-sources/src/main/java/org/backendDevTest/infra/api/ApiUtil.java