diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 0000000..9eedbc8 --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,36 @@ +# This workflow will build a Java project with Maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: Java CI with Maven + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + repository_dispatch: + types: contracts changed +jobs: + build: + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + java: [17] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + with: + path: main + - name: Set up JDK ${{ matrix.java }} + uses: actions/setup-java@v3 + with: + distribution: 'oracle' + java-version: ${{ matrix.java }} + - name: Build with Maven + working-directory: main + run: mvn test package jacoco:report + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: jacoco coverage report + path: /home/runner/work/specmatic-api-coding-test/specmatic-api-coding-test/main/target/site/jacoco diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6f8f362 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +target/* +build/* +.DS_Store +lib/* +*.iml +*.idea +.specmatic +.vscode/* diff --git a/README.md b/README.md new file mode 100644 index 0000000..1d2f105 --- /dev/null +++ b/README.md @@ -0,0 +1,92 @@ +# Specmatic Coding Test + +### Prerequisites: +JDK 17+ + +### Instructions: + +### 1. Clone this repository to your local machine. +From a terminal, run the following command: +```bash +mvn clean test +``` +You should see two failing tests: +```bash +[ERROR] Tests run: 2, Failures: 2, Errors: 0, Skipped: 0 +[INFO] +[INFO] ------------------------------------------------------------------------ +[INFO] BUILD FAILURE +[INFO] ------------------------------------------------------------------------ +``` +Your objective is to get these two tests to pass by following the rest of the instructions. + +### 2. Implement REST endpoints: +This is a Kotlin based Spring Boot application. +You are expected to implement the following endpoints to the **Products** controller: + +#### A. POST /products +Implement a **POST** route ```/products``` which accepts a json payload. + +##### Request Schema: +```json +{ + "name": "(string)", + "type": "(string enum)", + "inventory": "(integer)" +} +``` +The ```type``` field is an enum which can take the following values: +```yaml + - gadget + - book + - food + - other +``` +On successful creation of a product entity, the ```id``` of the created product should be returned: +##### Response Schema: +```json +{ + "id": "(integer)" +} +``` +   +   +   + +#### B. GET /products +Implement a **GET** route ```/products``` which accepts an **optional** query parameter called ```type```, which is an enum of the following values: +```yaml + - gadget + - book + - food + - other +``` +and returns a list of products with following schema. +##### Response Schema: +```json +[ + { + "id": "(integer)", + "name": "(string)", + "type": "(string enum)", + "inventory": "(integer)" + } +] +``` + +### Note: +- You are **not** expected to use a database. + You are free to use any datastructures like maps, lists, etc to store and retrieve products. + +- Writing any unit tests that you deem fit will be appreciated. + +- Please do not alter the following files: + - ContractTest.kt + - products_api.yaml + - specmatic.json + +### 3. Once you have both the tests passing, commit your changes and push to this repository. + + + + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..1e84086 --- /dev/null +++ b/pom.xml @@ -0,0 +1,203 @@ + + + 4.0.0 + + com.store + specmatic-api-coding-test + 1.0 + + + 1.9.21 + 17 + 1.3.19 + 3.2.5 + + + + + org.springframework.boot + spring-boot-starter + ${spring.boot.version} + + + org.springframework.boot + spring-boot-starter-web + ${spring.boot.version} + + + org.springframework.boot + spring-boot-starter-validation + ${spring.boot.version} + + + org.springframework.boot + spring-boot-starter-test + ${spring.boot.version} + test + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + org.junit.jupiter + junit-jupiter-engine + 5.5.2 + test + + + org.junit.jupiter + junit-jupiter-api + 5.5.2 + test + + + org.junit.platform + junit-platform-runner + 1.6.0 + test + + + org.junit.platform + junit-platform-engine + 1.6.0 + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.2 + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + + in.specmatic + junit5-support + ${specmatic.version} + test + + + org.jetbrains.kotlin + kotlin-reflect + ${kotlin.version} + + + org.jetbrains.kotlin + kotlin-stdlib-jdk8 + ${kotlin.version} + + + org.jetbrains.kotlin + kotlin-test-junit5 + ${kotlin.version} + test + + + + + + + src/test/java + + **/*.java + + + + src/test/resources + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + process-sources + + compile + + + + test-compile + process-test-sources + + test-compile + + + + + 17 + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + UTF-8 + ${maven.compiler} + ${maven.compiler} + -Werror + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.2 + + @{argLine} -Dfile.encoding=UTF-8 + + + + org.springframework.boot + spring-boot-maven-plugin + 2.3.4.RELEASE + + true + com.store.Application + + + + + repackage + + + + + + org.jacoco + jacoco-maven-plugin + 0.8.8 + + + jacoco-initialize + + prepare-agent + + + + report + prepare-package + + report + + + + + + **/DB.* + **/APIKeyAuthFilter.* + + + + + + diff --git a/products_api.yaml b/products_api.yaml new file mode 100644 index 0000000..f69de4a --- /dev/null +++ b/products_api.yaml @@ -0,0 +1,130 @@ +openapi: 3.0.1 +info: + title: Order API + version: '2.0' +servers: + - url: 'http://localhost:3000' +paths: + /products: + get: + summary: GET Products based on type + parameters: + - name: type + in: query + schema: + type: string + enum: + - gadget + - book + - food + - other + examples: + GET_PRODUCTS: + value: 'gadget' + responses: + "200": + description: List of products in the response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Product' + examples: + GET_PRODUCTS: + value: + - id: 1 + name: 'XYZ Phone' + type: 'gadget' + inventory: 2 + post: + summary: POST /products + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ProductDetails' + examples: + CREATE_PRODUCT: + value: + name: iPhone + type: gadget + inventory: 100 + responses: + "200": + description: POST /products + content: + application/json: + schema: + $ref: '#/components/schemas/ProductId' + examples: + CREATE_PRODUCT: + value: + id: 1 + +components: + schemas: + ProductDetails: + title: Product Details + type: object + properties: + name: + type: string + type: + $ref: '#/components/schemas/ProductType' + inventory: + type: integer + required: + - name + - type + - inventory + ProductType: + type: string + title: Product Type + enum: + - book + - food + - gadget + - other + ProductId: + title: Product Id + type: object + properties: + id: + type: integer + required: + - id + Product: + title: Product + allOf: + - $ref: '#/components/schemas/ProductId' + - $ref: '#/components/schemas/ProductDetails' + Products_RequestBody: + required: + - inventory + - name + - type + properties: + name: + type: string + type: + type: string + enum: + - gadget + - book + - food + - other + inventory: + type: number + ErrorResponseBody: + properties: + id: + type: number + timestamp: + type: string + status: + type: number + error: + type: string + message: + type: string diff --git a/specmatic.json b/specmatic.json new file mode 100644 index 0000000..4e3c572 --- /dev/null +++ b/specmatic.json @@ -0,0 +1,10 @@ +{ + "sources": [ + { + "provider": "filesystem", + "test": [ + "products_api.yaml" + ] + } + ] +} diff --git a/src/main/java/com/store/MainApp.kt b/src/main/java/com/store/MainApp.kt new file mode 100644 index 0000000..516676e --- /dev/null +++ b/src/main/java/com/store/MainApp.kt @@ -0,0 +1,11 @@ +package com.store + +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.runApplication + +@SpringBootApplication +open class Application + +fun main(args: Array) { + runApplication(*args) +} \ No newline at end of file diff --git a/src/main/java/com/store/controllers/Products.kt b/src/main/java/com/store/controllers/Products.kt new file mode 100644 index 0000000..caaf695 --- /dev/null +++ b/src/main/java/com/store/controllers/Products.kt @@ -0,0 +1,9 @@ +package com.store.controllers + +import org.springframework.web.bind.annotation.RestController + + +@RestController +open class Products { + +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..64545df --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,2 @@ +management.endpoints.web.exposure.include=* +server.port=8090 \ No newline at end of file diff --git a/src/test/java/com/store/ContractTest.kt b/src/test/java/com/store/ContractTest.kt new file mode 100644 index 0000000..e52f237 --- /dev/null +++ b/src/test/java/com/store/ContractTest.kt @@ -0,0 +1,31 @@ +package com.store; + +import `in`.specmatic.test.SpecmaticContractTest +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.BeforeAll +import org.springframework.boot.SpringApplication +import org.springframework.context.ConfigurableApplicationContext + +//DO NOT ALTER +class ContractTest : SpecmaticContractTest { + companion object { + + private lateinit var context: ConfigurableApplicationContext + + @JvmStatic + @BeforeAll + fun setUp() { + System.setProperty("host", "localhost"); + System.setProperty("port", "8090"); + System.setProperty("endpointsAPI", "http://localhost:8090/actuator/mappings"); + + context = SpringApplication.run(Application::class.java) + } + + @JvmStatic + @AfterAll + fun tearDown() { + context.close(); + } + } +} diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties new file mode 100644 index 0000000..e30144c --- /dev/null +++ b/src/test/resources/application.properties @@ -0,0 +1,3 @@ +server.port=8090 +management.endpoints.web.exposure.include=* +endpointsAPI=http://localhost:8090/actuator/mappings \ No newline at end of file