API-First with OpenAPI

ZenWave SDK API-First tooling for OpenAPI

Whether you write your OpenAPI definition manually or generate it using ZenWave Domain Model as an Interface Definition Language (IDL), ZenWave SDK provides several tools to build different aspects of your application:

  • Use OpenAPI Generator Maven plugin to generate SpringMVC Controller Interfaces and DTOs from OpenAPI.
  • Use OpenAPIControllersPlugin to generate SpringMVC Controllers (skeletons) implementing OpenAPI Controller Interfaces.
  • Use SpringWebTestClientPlugin to generate Spring WebTestClient Tests for your SpringMVC Controllers.
  • Use OpenAPIKaratePlugin to generate KarateDSL API Tests for your REST APIs from your OpenAPI specifications.

You can also:

Using OpenAPI Generator Maven Plugin

The following examples are based on Customer Service JPA Example Project and its OpenAPI definition:

OpenAPI definition for Customer Service JPA Example (πŸ‘‡ view source)
openapi: 3.0.1
info:
title: "ZenWave Customer JPA Example"
version: 0.0.1
description: "ZenWave Customer JPA Example"
contact:
email: email@domain.com
servers:
- description: localhost
url: http://localhost:8080/api
- description: custom
url: "{protocol}://{server}/{path}"
variables:
protocol:
enum: ['http', 'https']
default: 'http'
server:
default: 'localhost:8080'
path:
default: 'api'
tags:
- name: "Default"
- name: "Customer"
paths:
/customers:
post:
operationId: createCustomer
description: "createCustomer"
tags: [Customer]
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/Customer"
responses:
"201":
description: "OK"
content:
application/json:
schema:
$ref: "#/components/schemas/Customer"
/customers/{id}:
get:
operationId: getCustomer
description: "getCustomer"
tags: [Customer]
parameters:
- name: "id"
in: path
required: true
schema:
type: integer
format: int64
responses:
"200":
description: "OK"
content:
application/json:
schema:
$ref: "#/components/schemas/Customer"
put:
operationId: updateCustomer
description: "updateCustomer"
tags: [Customer]
parameters:
- name: "id"
in: path
required: true
schema:
type: integer
format: int64
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/Customer"
responses:
"200":
description: "OK"
content:
application/json:
schema:
$ref: "#/components/schemas/Customer"
delete:
operationId: deleteCustomer
description: "deleteCustomer"
tags: [Customer]
parameters:
- name: "id"
in: path
required: true
schema:
type: integer
format: int64
responses:
"204":
description: "OK"
/customers/search:
post:
operationId: searchCustomers
description: "searchCustomers"
tags: [Customer]
parameters:
- $ref: "#/components/parameters/page"
- $ref: "#/components/parameters/limit"
- $ref: "#/components/parameters/sort"
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CustomerSearchCriteria"
responses:
"200":
description: "OK"
content:
application/json:
schema:
$ref: "#/components/schemas/CustomerPaginated"
components:
schemas:
Customer:
type: "object"
x-business-entity: "Customer"
required:
- "name"
- "email"
properties:
id:
type: "integer"
format: "int64"
version:
type: "integer"
name:
type: "string"
maxLength: 254
description: "Customer name"
email:
type: "string"
maxLength: 254
pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}"
description: ""
addresses:
type: "array"
items:
$ref: "#/components/schemas/Address"
minLength: 1
maxLength: 5
paymentMethods:
type: "array"
items:
$ref: "#/components/schemas/PaymentMethod"
CustomerPaginated:
allOf:
- $ref: "#/components/schemas/Page"
- x-business-entity-paginated: "Customer"
- properties:
content:
type: "array"
items:
$ref: "#/components/schemas/Customer"
CustomerSearchCriteria:
type: "object"
x-business-entity: "CustomerSearchCriteria"
properties:
name:
type: "string"
email:
type: "string"
city:
type: "string"
state:
type: "string"
PaymentMethodType:
type: "string"
x-business-entity: "PaymentMethodType"
enum:
- "VISA"
- "MASTERCARD"
Address:
type: "object"
x-business-entity: "Address"
required:
- "street"
- "city"
properties:
street:
type: "string"
maxLength: 254
city:
type: "string"
maxLength: 254
PaymentMethod:
type: "object"
x-business-entity: "PaymentMethod"
required:
- "type"
- "cardNumber"
properties:
id:
type: "integer"
format: "int64"
version:
type: "integer"
type:
$ref: "#/components/schemas/PaymentMethodType"
cardNumber:
type: "string"
Page:
type: object
required:
- "content"
- "totalElements"
- "totalPages"
- "size"
- "number"
properties:
number:
type: integer
minimum: 0
numberOfElements:
type: integer
minimum: 0
size:
type: integer
minimum: 0
maximum: 200
multipleOf: 25
totalElements:
type: integer
totalPages:
type: integer
parameters:
page:
name: page
in: query
description: The number of results page
schema:
type: integer
format: int32
default: 0
limit:
name: limit
in: query
description: The number of results in a single page
schema:
type: integer
format: int32
default: 20
sort:
name: sort
in: query
description: The number of results page
schema:
type: array
items:
type: string
securitySchemes:
basicAuth: # <-- arbitrary name for the security scheme
type: http
scheme: basic
bearerAuth: # <-- arbitrary name for the security scheme
type: http
scheme: bearer
bearerFormat: JWT # optional, arbitrary value for documentation purposes
security:
- basicAuth: [] # <-- use the same name here
- bearerAuth: [] # <-- use the same name here
πŸ”—Navigate to source

This is how you can configure openapi-generator-maven-plugin in your pom.xml to make it compatible with OpenAPIControllersPlugin:

openapi-generator-maven-plugin in pom.xml (πŸ‘‡ view source)
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>7.10.0</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<phase>generate-sources</phase>
<configuration>
<inputSpec>${project.basedir}/src/main/resources/public/apis/openapi.yml</inputSpec>
<skipIfSpecIsUnchanged>true</skipIfSpecIsUnchanged>
<generatorName>spring</generatorName>
<apiPackage>${openApiApiPackage}</apiPackage>
<modelPackage>${openApiModelPackage}</modelPackage>
<modelNameSuffix>DTO</modelNameSuffix>
<addCompileSourceRoot>true</addCompileSourceRoot>
<generateSupportingFiles>false</generateSupportingFiles>
<typeMappings>
<typeMapping>Double=java.math.BigDecimal</typeMapping>
</typeMappings>
<configOptions>
<useSpringBoot3>true</useSpringBoot3>
<documentationProvider>none</documentationProvider>
<openApiNullable>false</openApiNullable>
<useOptional>false</useOptional>
<useTags>true</useTags>
<interfaceOnly>true</interfaceOnly>
<skipDefaultInterface>true</skipDefaultInterface>
<delegatePattern>false</delegatePattern>
<sortParamsByRequiredFlag>false</sortParamsByRequiredFlag>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>
πŸ”—Navigate to source

Important configuration properties for compatibility with OpenAPIControllersPlugin:

  • <useSpringBoot3>true</useSpringBoot3> use SpringBoot 3 and jakarta annotations
  • <openApiNullable>false</openApiNullable> we use java.util.Optional instead
  • <useOptional>false</useOptional> do not use java.util.Optional for parameters
  • <useTags>true</useTags> required for grouping the operations in services by tag
  • <sortParamsByRequiredFlag>false</sortParamsByRequiredFlag> required for matching path params in generated code

Generating SpringMVC Controller Interfaces and DTOs from OpenAPI yaml

Previous openapi-generator-maven-plugin will generate as part of the maven build the following java code:

  • target/generated-sources/openapi/src/main/java/io/zenwave360/example/adapters/web/CustomerApi.java
  • target/generated-sources/openapi/src/main/java/io/zenwave360/example/adapters/web/model/CustomerDTO.java
  • target/generated-sources/openapi/src/main/java/io/zenwave360/example/adapters/web/model/AddressDTO.java
  • target/generated-sources/openapi/src/main/java/io/zenwave360/example/adapters/web/model/PaymentMethodDTO.java
  • target/generated-sources/openapi/src/main/java/io/zenwave360/example/adapters/web/model/PaymentMethodTypeDTO.java
  • target/generated-sources/openapi/src/main/java/io/zenwave360/example/adapters/web/model/CustomerSearchCriteriaDTO.java
  • target/generated-sources/openapi/src/main/java/io/zenwave360/example/adapters/web/model/CustomerPaginatedDTO.java
  • target/generated-sources/openapi/src/main/java/io/zenwave360/example/adapters/web/model/PageDTO.java

These classes are part of your project compilee sources and will be compiled and packaged as part of your application. In the next section we will be implementing CustomerApi.java generated interface.

Do not edit this code by hand, it will be overwritten by the maven build. You will need to edit openapi.yml definition instead and then run maven build again, for instance:

mvn clean test-compile

Generating SpringMVC Controller Implementations from OpenAPI yaml

While openapi-generator-maven-plugin can generate SpringMVC annotated Controller Interfaces and their DTOs, you will need to implement the interface methods yourself.

Thankfully, ZenWave SDK provides a plugin to generate SpringMVC Controller Implementations from OpenAPI yaml.

With OpenAPIControllersPlugin you can generate SpringMVC Controller Implementations from OpenAPI yaml.

βš™οΈ

When using the OpenAPIControllersPlugin, the following

this zdl script: (πŸ‘‡ view source)
OpenAPIControllersPlugin {
openapiFile "src/main/resources/public/apis/openapi.yml"
}
πŸ”—Navigate to source

would produce this:

CustomerApiController.java (πŸ‘‡ view source)
/**
* REST controller for CustomerApi.
*/
@RestController
@RequestMapping("/api")
public class CustomerApiController implements CustomerApi {
private final Logger log = LoggerFactory.getLogger(getClass());
@Autowired
private NativeWebRequest request;
private CustomerService customerService;
@Autowired
public CustomerApiController setCustomerService(CustomerService customerService) {
this.customerService = customerService;
return this;
}
private CustomerDTOsMapper mapper = CustomerDTOsMapper.INSTANCE;
public CustomerApiController(CustomerService customerService) {
this.customerService = customerService;
}
public Optional<NativeWebRequest> getRequest() {
return Optional.ofNullable(request);
}
@Override
public ResponseEntity<CustomerDTO> createCustomer(CustomerDTO reqBody) {
log.debug("REST request to createCustomer: {}", reqBody);
var input = mapper.asCustomer(reqBody);
var customer = customerService.createCustomer(input);
CustomerDTO responseDTO = mapper.asCustomerDTO(customer);
return ResponseEntity.status(201).body(responseDTO);
}
@Override
public ResponseEntity<CustomerDTO> getCustomer(Long id) {
log.debug("REST request to getCustomer: {}", id);
var customer = customerService.getCustomer(id);
if (customer.isPresent()) {
CustomerDTO responseDTO = mapper.asCustomerDTO(customer.get());
return ResponseEntity.status(200).body(responseDTO);
} else {
return ResponseEntity.notFound().build();
}
}
@Override
public ResponseEntity<CustomerDTO> updateCustomer(Long id, CustomerDTO reqBody) {
log.debug("REST request to updateCustomer: {}, {}", id, reqBody);
var input = mapper.asCustomer(reqBody);
var customer = customerService.updateCustomer(id, input);
if (customer.isPresent()) {
CustomerDTO responseDTO = mapper.asCustomerDTO(customer.get());
return ResponseEntity.status(200).body(responseDTO);
} else {
return ResponseEntity.notFound().build();
}
}
@Override
public ResponseEntity<Void> deleteCustomer(Long id) {
log.debug("REST request to deleteCustomer: {}", id);
customerService.deleteCustomer(id);
return ResponseEntity.status(204).build();
}
@Override
public ResponseEntity<CustomerPaginatedDTO> searchCustomers(Integer page, Integer limit, List<String> sort, CustomerSearchCriteriaDTO reqBody) {
log.debug("REST request to searchCustomers: {}, {}, {}, {}", page, limit, sort, reqBody);
var input = mapper.asCustomerSearchCriteria(reqBody);
var customerPage = customerService.searchCustomers(input, pageOf(page, limit, sort));
var responseDTO = mapper.asCustomerPaginatedDTO(customerPage);
return ResponseEntity.status(200).body(responseDTO);
}
protected Pageable pageOf(Integer page, Integer limit, List<String> sort) {
Sort sortOrder = sort != null ? Sort.by(sort.stream().map(sortParam -> {
String[] parts = sortParam.split(":");
String property = parts[0];
Sort.Direction direction = parts.length > 1 ? Sort.Direction.fromString(parts[1]) : Sort.Direction.ASC;
return new Sort.Order(direction, property);
}).toList()) : Sort.unsorted();
return PageRequest.of(page != null ? page : 0, limit != null ? limit : 10, sortOrder);
}
}
πŸ”—Navigate to source

And Unit Tests for your Controller:

CustomerApiControllerTest.java (excerpt) (πŸ‘‡ view source)
/** Test controller for CustomerApiController. */
class CustomerApiControllerTest {
private final Logger log = LoggerFactory.getLogger(getClass());
ServicesInMemoryConfig context = new ServicesInMemoryConfig();
CustomerApiController controller = new CustomerApiController(context.customerService());
@BeforeEach
void setUp() {
context.reloadTestData();
}
@Test
void createCustomerTest() {
CustomerDTO reqBody = new CustomerDTO();
reqBody.setName("John Doe");
reqBody.setEmail("john.doe@example.com");
reqBody.setAddresses(List.of(new AddressDTO("Anytown", "123 Main St")));
reqBody.setPaymentMethods(List.of(new PaymentMethodDTO(PaymentMethodTypeDTO.VISA, "1234567890123456")));
var response = controller.createCustomer(reqBody);
Assertions.assertEquals(201, response.getStatusCode().value());
}
πŸ”—Navigate to source

Testing REST APIs

You can also generate API Tests for your REST APIs from your OpenAPI specifications.

Generating Spring WebTestClient Tests from OpenAPI yaml

WithWebTestClient you can test your REST API via HTTP semantics while in the same JVM process as your application.

ZenWaveSpringWebTestClientPlugin can generate both single endpoint tests as well as business flows spanning multiple endpoints:

βš™οΈ

When using the SpringWebTestClientPlugin, the following

zenwave-scripts.zw (πŸ‘‡ view source)
SpringWebTestClientPlugin {
openapiFile "src/main/resources/public/apis/openapi.yml"
}
πŸ”—Navigate to source

would produce this API test:

CustomerApiIntegrationTest.java (excerpt) (πŸ‘‡ view source)
@Test
void testCreateCustomer_201() {
var requestBody = """
{
"email": "jane.doe@example.com",
"name": "Jane Doe",
"addresses": [
{
"city": "Othertown",
"street": "456 Elm St"
}
],
"paymentMethods": [
{
"type": "VISA",
"cardNumber": "6543210987654321"
}
]
}
""";
webTestClient.method(POST).uri("/api/customers")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(requestBody)
.exchange()
.expectStatus().isEqualTo(201)
.expectHeader().contentType(MediaType.APPLICATION_JSON)
.expectBody()
.jsonPath("$.id").isNotEmpty()
.jsonPath("$.version").isNotEmpty()
.jsonPath("$.name").isNotEmpty()
.jsonPath("$.email").isNotEmpty()
.jsonPath("$.addresses").isNotEmpty()
.jsonPath("$.addresses").isArray()
.jsonPath("$.addresses[0].street").isNotEmpty()
.jsonPath("$.addresses[0].city").isNotEmpty()
.jsonPath("$.paymentMethods").isNotEmpty()
.jsonPath("$.paymentMethods").isArray()
.jsonPath("$.paymentMethods[0].id").isNotEmpty()
.jsonPath("$.paymentMethods[0].version").isNotEmpty()
.jsonPath("$.paymentMethods[0].type").isNotEmpty()
.jsonPath("$.paymentMethods[0].cardNumber").isNotEmpty();
}
πŸ”—Navigate to source

βš™οΈ

When using the SpringWebTestClientPlugin, the following

zenwave-scripts.zw (πŸ‘‡ view source)
SpringWebTestClientPlugin {
openapiFile "src/main/resources/public/apis/openapi.yml"
groupBy businessFlow
businessFlowTestName CreateUpdateDeleteCustomerIntegrationTest
operationIds createCustomer,updateCustomer,deleteCustomer,getCustomer
}
πŸ”—Navigate to source

would produce this Business Flow API test:

CustomerApiIntegrationTest (πŸ‘‡ view source)
package io.zenwave360.example.adapters.web;
import io.zenwave360.example.adapters.web.model.CustomerDTO;
import io.zenwave360.example.adapters.web.model.PaymentMethodTypeDTO;
import org.junit.jupiter.api.Test;
import org.springframework.http.MediaType;
import static org.springframework.http.HttpMethod.*;
/**
* Business Flow Test for: createCustomer, getCustomer, updateCustomer, deleteCustomer.
*/
class CreateUpdateDeleteCustomerIntegrationTest extends BaseWebTestClientTest {
/**
* Business Flow Test for: createCustomer, getCustomer, updateCustomer, deleteCustomer.
*/
@Test
void testCreateUpdateDeleteCustomerIntegrationTest() {
// createCustomer: createCustomer
var customerRequestBody0 = """
{
"email": "jane.doe@example.com",
"name": "Jane Doe",
"addresses": [
{
"city": "Othertown",
"street": "456 Elm St"
}
],
"paymentMethods": [
{
"type": "VISA",
"cardNumber": "6543210987654321"
}
]
}
""";
var createCustomerResponse0 = webTestClient.method(POST).uri("/api/customers")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(customerRequestBody0)
.exchange()
.expectStatus().isEqualTo(201)
.expectHeader().contentType(MediaType.APPLICATION_JSON)
.returnResult(CustomerDTO.class);
// getCustomer: getCustomer
var id = createCustomerResponse0.getResponseBody().blockFirst().getId();
var getCustomerResponse1 = webTestClient.method(GET).uri("/api/customers/{id}", id)
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus().isEqualTo(200)
.expectHeader().contentType(MediaType.APPLICATION_JSON)
.returnResult(CustomerDTO.class);
// updateCustomer: updateCustomer
CustomerDTO customerRequestBody2 = getCustomerResponse1.getResponseBody().blockFirst();
customerRequestBody2.setName("updated");
customerRequestBody2.setEmail("updated@email.com");
customerRequestBody2.getPaymentMethods().get(0).setType(PaymentMethodTypeDTO.VISA);
var updateCustomerResponse2 = webTestClient.method(PUT).uri("/api/customers/{id}", id)
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(customerRequestBody2)
.exchange()
.expectStatus().isEqualTo(200)
.expectHeader().contentType(MediaType.APPLICATION_JSON)
.returnResult(CustomerDTO.class);
// deleteCustomer: deleteCustomer
webTestClient.method(DELETE).uri("/api/customers/{id}", id)
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus().isEqualTo(204);
// getCustomer: getCustomer (not found)
webTestClient.method(GET).uri("/api/customers/{id}", id)
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus().isEqualTo(404);
}
}
πŸ”—Navigate to source

You can control whether these are Unit or Integration Tests with @SpringBootTest in

BaseWebTestClientTest.java (πŸ‘‡ view source)
@ActiveProfiles("test")
@DockerComposeInitializer.EnableDockerCompose
@org.springframework.transaction.annotation.Transactional
public abstract class BaseWebTestClientTest {
@Autowired
protected WebApplicationContext context;
protected WebTestClient webTestClient;
@BeforeEach
void setup() {
this.webTestClient = MockMvcWebTestClient.bindToApplicationContext(this.context).build();
}
}
πŸ”—Navigate to source

As always, it's your responsibility as the developer to provide test data and parameters that match the initial state, whether you're using a containerized database or an in-memory implementation.

Generating KarateDSL Tests from OpenAPI yaml

You can also generate KarateDSL Tests from OpenAPI yaml. Both single endpoint tests as well as business flows spanning multiple endpoints.

βš™οΈ

When using the OpenAPIKaratePlugin, the following

zenwave-scripts.zw (πŸ‘‡ view source)
OpenAPIKaratePlugin {
openapiFile "src/main/resources/public/apis/openapi.yml"
}
πŸ”—Navigate to source

would produce this API test:

CustomerApi.feature KarateDSL API Test (πŸ‘‡ view source)
@openapi-file=src/main/resources/public/apis/openapi.yml
Feature: CustomerApi
Background:
* url baseUrl
# * def auth = { username: '', password: '' }
* def authHeader = call read('classpath:karate-auth.js') auth
* configure headers = authHeader || {}
@operationId=createCustomer
Scenario: createCustomer
Given path '/customers'
And request
"""
{
"name" : "name-iv8wf61cpxe13fa3j0",
"email" : "kraig.hermann@gmail.com",
"addresses" : [ {
"street" : "street-wivpwm357mj4r1",
"city" : "Connellyside"
} ],
"paymentMethods" : [ {
"type" : "VISA",
"cardNumber" : "cardNumber-ydvoeke77t3z"
} ]
}
"""
When method post
Then status 201
* def createCustomerResponse = response
* def customerId = response.id
# TODO: Add response validation
@operationId=getCustomer
Scenario: getCustomer
* def pathParams = { id: 53 }
Given path '/customers/', pathParams.id
When method get
Then status 200
* def getCustomerResponse = response
* def customerId = response.id
# TODO: Add response validation
@operationId=updateCustomer
Scenario: updateCustomer
* def pathParams = { id: 53 }
Given path '/customers/', pathParams.id
And request
"""
{
"name" : "name-t58rqm1n6zb",
"email" : "reta.wiza@gmail.com",
"addresses" : [ {
"street" : "street-e2pn9ih8t7du14h93",
"city" : "East Genesis"
} ],
"paymentMethods" : [ {
"type" : "MASTERCARD",
"cardNumber" : "cardNumber-qx3ag249vxzjgkh"
} ]
}
"""
When method put
Then status 200
* def updateCustomerResponse = response
* def customerId = response.id
# TODO: Add response validation
@operationId=deleteCustomer
Scenario: deleteCustomer
* def pathParams = { id: 89 }
Given path '/customers/', pathParams.id
When method delete
Then status 204
* def deleteCustomerResponse = response
# TODO: Add response validation
@operationId=searchCustomers
Scenario: searchCustomers
Given path '/customers/search'
And def queryParams = { page: 71, limit: 20, sort: ['name:asc'] }
And request
"""
{
"name" : "name-xrx9m9s1",
"email" : "tyron.heidenreich@yahoo.co",
"city" : "Bellamouth",
"state" : "state-y1x18r2h"
}
"""
When method post
Then status 201
* def searchCustomersResponse = response
* def customerPaginatedId = response.id
# TODO: Add response validation
πŸ”—Navigate to source

βš™οΈ

When using the OpenAPIKaratePlugin, the following

zenwave-scripts.zw (πŸ‘‡ view source)
OpenAPIKaratePlugin {
openapiFile "src/main/resources/public/apis/openapi.yml"
groupBy businessFlow
businessFlowTestName CreateUpdateDeleteCustomerKarateTest
operationIds createCustomer,updateCustomer,deleteCustomer,getCustomer
}
πŸ”—Navigate to source

would produce this Business Flow API test:

CreateUpdateDeleteCustomerKarateTest.feature KarateDSL Business Flow Test (πŸ‘‡ view source)
@openapi-file=src/main/resources/public/apis/openapi.yml
Feature: CreateUpdateDeleteCustomerKarateTest
Background:
* url baseUrl
# * def auth = { username: '', password: '' }
* def authHeader = call read('classpath:karate-auth.js') auth
* configure headers = authHeader || {}
@business-flow
@operationId=createCustomer,updateCustomer,deleteCustomer,getCustomer
Scenario: CreateUpdateDeleteCustomerKarateTest
# createCustomer
Given path '/customers'
And request
"""
{
"id" : 21,
"version" : 13,
"name" : "name-5ugq20772n1",
"email" : "rosaria.hyatt@gmail.com",
"addresses" : [ {
"street" : "street-z",
"city" : "West Edmundbury"
} ],
"paymentMethods" : [ {
"id" : 37,
"version" : 10,
"type" : "MASTERCARD",
"cardNumber" : "cardNumber-9c2xzm77ig5"
} ]
}
"""
When method post
Then status 201
* def createCustomerResponse = response
* def customerId = response.id
# TODO: Add response validation
# updateCustomer
* def pathParams = { id: 2 }
Given path '/customers/', pathParams.id
And request
"""
{
"id" : 79,
"version" : 7,
"name" : "name-mu8j1eqcaoy2a7ex9uup1",
"email" : "jerica.emmerich@hotmail.co",
"addresses" : [ {
"street" : "street-7ug0jz4c5ghumuz2yst",
"city" : "Sydneychester"
} ],
"paymentMethods" : [ {
"id" : 30,
"version" : 82,
"type" : "MASTERCARD",
"cardNumber" : "cardNumber-f"
} ]
}
"""
When method put
Then status 200
* def updateCustomerResponse = response
* def customerId = response.id
# TODO: Add response validation
# deleteCustomer
* def pathParams = { id: 42 }
Given path '/customers/', pathParams.id
When method delete
Then status 204
* def deleteCustomerResponse = response
# TODO: Add response validation
# getCustomer
* def pathParams = { id: 71 }
Given path '/customers/', pathParams.id
When method get
Then status 404
* def getCustomerResponse = response
* def customerId = response.id
# TODO: Add response validation
πŸ”—Navigate to source