Framework and build tools

For this project I chose to use Spring Boot alongside Gradle.

Purpose

Small assignment to assess your capacity to execute a specific request

It is taking into account some of the software engineering aspects beyond programming, like (non-exhaustive list):

  • Packaging

  • Layering

  • Error management

  • Documentation

  • Deployment aspects

  • Testing

This is a contrived example, especially designed to make the candidates exercise a deeper knowledge of the framework, going beyond simple tutorials that can be found online.

Credits (Purpose section) : B2Boost

Api documentation

In the following document you will find a simple documentation of the API. Most of the following sections have been generated with Spring Rest Docs.

The partner resource

Field Type Semantics Example

id

long

The database id of the partner

1

name

string

"The name of the partner"

"B2Boost"

reference

string

The unique reference of the partner

"xxxxx"

locale

string

A valid Locale of the partner

"en_BE"

expirationTime

string

The ISO-8601 UTC date time when the partner is going to expire

"2022-11-24 17:46:00+01:00"

Accessing all the partners GET endpoint

A GET request is used to access all the partners read.

Request structure

GET /partners HTTP/1.1
Host: localhost:8080

Example response

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 1359

{
    "content": [
        {
            "id": 1,
            "name": "B2boost",
            "reference": "FYI1",
            "locale": "en_BE",
            "expirationTime": "2022-11-24 17:46:00+01"
        },
        {
            "id": 2,
            "name": "Proximus",
            "reference": "FYI2",
            "locale": "en_BE",
            "expirationTime": "2022-11-24 17:46:00+01"
        },

        // more json elements...

        {
            "id": 10,
            "name": "Cisco",
            "reference": "FYI10",
            "locale": "en_US",
            "expirationTime": "2022-11-24 17:46:00-04"
        }
    ],
    "pageable": {
        "sort": {
            "empty": true,
            "sorted": false,
            "unsorted": true
        },
        "offset": 0,
        "pageNumber": 0,
        "pageSize": 10,
        "paged": true,
        "unpaged": false
    },
    "totalPages": 2,
    "totalElements": 12,
    "last": false,
    "size": 10,
    "number": 0,
    "sort": {
        "empty": true,
        "sorted": false,
        "unsorted": true
    },
    "first": true,
    "numberOfElements": 10,
    "empty": false
}
Note
response body within content json array and pagination infos within pageable json object

CURL request

$ curl 'http://localhost:8080/partners' -i -X GET

Pagination parameters

Table 1. /partners
Parameter Description

from

offset in the resultset to paginate to (default value is 0)

size

Window pagination size (default value is 10)

Exemple CURL request with pagination parameters

$ curl --location --request GET 'http://localhost:8080/partners?from=0&size=4'

Accessing the partner GET endpoint

A GET request is used to access the partner read.

Request structure

GET /partner/1 HTTP/1.1
Host: localhost:8080

Path Parameters

Table 2. /partner/{id}
Parameter Description

id

id of partner to be searched

Example response

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 129

{
  "id" : 1,
  "name" : "B2boost",
  "reference" : "FYI1",
  "locale" : "en_BE",
  "expirationTime" : "2022-11-24 17:46:00+01"
}

CURL request

$ curl 'http://localhost:8080/partner/1' -i -X GET

Example GET request with errors

Example with invalid value for id
$ curl --location --request GET 'http://localhost:8080/partner/-1'

Response

{
    "code": 500,
    "message": "getPartnerById.Id: must be greater than or equal to 1"
}
Note
The Min value for id is 1
Example partner not found
$ curl --location --request GET 'http://localhost:8080/partner/3000'

Response

{
    "code": 404,
    "message": "Partner with id 3000 not found!"
}

Accessing the partner POST endpoint

A POST request is used to add a partner resource.

Request structure

POST /partner HTTP/1.1
Content-Type: application/json
Content-Length: 117
Host: localhost:8080

{
  "reference" : "FYI25",
  "expirationTime" : "2013-10-03T12:18:46+01:00",
  "name" : "UPS",
  "locale" : "en_BE"
}

Example response

HTTP/1.1 201 Created
Content-Type: application/json
Content-Length: 130

{
  "id" : 13,
  "name" : "UPS",
  "reference" : "FYI25",
  "locale" : "en_BE",
  "expirationTime" : "2013-10-03T12:18:46+01:00"
}

CURL request

$ curl 'http://localhost:8080/partner' -i -X POST \
    -H 'Content-Type: application/json' \
    -d '{
  "reference" : "FYI25",
  "expirationTime" : "2013-10-03T12:18:46+01:00",
  "name" : "UPS",
  "locale" : "en_BE"
}'

Example POST request with errors

Example with error on create with wrong date format
$ curl --location --request POST 'http://localhost:8080/partner' \
--header 'Content-Type: application/json' \
--data-raw '{
    "name": "UPS",
    "reference": "FYI15",
    "locale": "en_BE",
    "expirationTime": "2017-10-03T12:18:46"
}'

Response

{
    "code": 400,
    "message": "Date 2017-10-03T12:18:46 is an invalid ISO-8601 UTC date time! Should be something like 2017-10-03T12:18:46+00:00"
}
Note
Here the UTC timezone is missing
Example with error on create with invalid locale
$ curl --location --request POST 'http://localhost:8080/partner' \
--header 'Content-Type: application/json' \
--data-raw '{
    "name": "UPS",
    "reference": "FYI15",
    "locale": "en_BEE",
    "expirationTime": "2017-10-03T12:18:46+08:00"
}'

Response

{
    "code": 400,
    "message": "Locale en_BEE is an invalid Locale!"
}
Note
Here the locale should be en_BE

Accessing the partner PUT endpoint

A PUT request is used to update a partner resource.

Request structure

PUT /partner/3 HTTP/1.1
Content-Type: application/json
Content-Length: 97
Host: localhost:8080

{"reference":"FYI255","expirationTime":"2022-05-23T12:18:46+01:00","name":"DHL","locale":"de_DE"}

Example response

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 104

{"id":3,"name":"DHL","reference":"FYI255","locale":"de_DE","expirationTime":"2022-05-23T12:18:46+01:00"}

CURL request

$ curl 'http://localhost:8080/partner/3' -i -X PUT \
    -H 'Content-Type: application/json' \
    -d '{"reference":"FYI255","expirationTime":"2022-05-23T12:18:46+01:00","name":"DHL","locale":"de_DE"}'

Example PUT request with errors

The errors for validating locale and date are the same between PUT and POST

Example with error on update with not unique reference
$ curl --location --request PUT 'http://localhost:8080/partner/1' \
--header 'Content-Type: application/json' \
--data-raw '{
    "name": "UPS",
    "reference": "FYI5",
    "locale": "en_US",
    "expirationTime": "2022-05-23T12:18:46-05:00"
}'

Response

{
    "code": 500,
    "message": "could not execute statement; SQL [n/a]; constraint [\"PUBLIC.CONSTRAINT_INDEX_7 ON PUBLIC.PARTNERS(REF) VALUES 5\"; SQL statement:\nupdate partners set expires=?, locale=?, company_name=?, ref=? where id=? [23505-200]]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement"
}
Note
The reference attribute is configured to be a unique identifier for the partner
Example with error on update with partner not found
$ curl --location --request PUT 'http://localhost:8080/partner/4000' \
--header 'Content-Type: application/json' \
--data-raw '{
    "name": "UPS",
    "reference": "FYI50",
    "locale": "en_US",
    "expirationTime": "2022-05-23T12:18:46-05:00"
}'

Response

{
    "code": 404,
    "message": "Partner with id 4000 not found!"
}

Accessing the partner DELETE endpoint

A DELETE request is used to delete the partner.

Request structure

DELETE /partner/2 HTTP/1.1
Host: localhost:8080

Path Parameters

Table 3. /partner/{id}
Parameter Description

id

The id of the partner to delete

Example response

HTTP/1.1 200 OK

CURL request

$ curl 'http://localhost:8080/partner/2' -i -X DELETE

Example DELETE request with errors

Example with error on update with with partner not found
$ curl --location --request DELETE 'http://localhost:8080/partner/2000'

Response

{
    "code": 404,
    "message": "Partner with id 2000 not found!"
}

Accessing the health check endpoint

$ curl --location --request GET 'http://localhost:8080/health'

Response

{
    "status": "UP",
    "components": {
        "db": {
            "status": "UP",
            "details": {
                "database": "H2",
                "validationQuery": "isValid()"
            }
        },
        "diskSpace": {
            "status": "UP",
            "details": {
                "total": 250438021120,
                "free": 10172166144,
                "threshold": 10485760,
                "exists": true
            }
        },
        "ping": {
            "status": "UP"
        }
    }
}
Note
For security purpose you can display less by changing management.endpoint.health.show-details=always to never in application.properties

Useful commands

Assuming you are using a bash cli

Run the app in dev env

./gradlew bootRun

Run the test suite with gradle

./gradlew test

Prepare the jar archive with gradle for deployment

./gradlew clean bootJar
Note
Path of the output is build/libs/api-spring-gradle-b2boost-0.0.1-SNAPSHOT.jar