A repo storing assets related to webinar demonstrating a collaborative API design and delivery journey using SwaggerHub, ReadyAPI, and PactFlow
In this guide, you'll learn how to use the SmartBear API Platform, to add improve confidence and add visibility to your design first API development workflow.
SmartBear's API Platform tools can be combined to increase the quality of your design-first API development workflow, and help navigate the complexity of microservices rollout.
SwaggerHub is foundation of a repeatable process for API Development, providing a secure collaborative environment for your API design process:
- It unifies teams around a single source of truth - the OAS - and enables standardisation across your services
- Allows teams to work independently
- Unlocks automation such as code-generation and mock services
PactFlow brings increased visibility into how consumers use your API, enabling:
- API consumer and API producer development teams to work in independently and safely
- Prevent breaking changes to your API and releasing an incompatible API consumer
- A reduction in the need for API versioning, avoiding the need to create and maintain multiple versions of an API, and communicating the change to consumers.
Together, they allow faster feedback cycles from design through to development, test and release.
To use this feature, you will need:
- A PactFlow account (create a free account).
- A SwaggerHub account (create a free account).
- For the SwaggerHub/PactFlow integration, you will need a team /enterprise account, to create private API's, or you can use the integration for the first 14 day trial of enterprise.
- A ReadyAPI account (create a free 14 day trial account).
Our company SmartBearCoin is expanding to offer services across the wider European region and thus our Payments Domain (business capability) are extending our payment processing sub-capability to cater for cross border payments.
The Business Requirements
The Payments capability has requested the following from Customer Management capability:
The ability to be able to retrieve customer information to help them have up-to-date and accurate payee information at the point to issuing a cross border payment. It was also mentioned was the need to obtain payment transaction summary information for payees but the ask was not very clear 😕
- Establish the business requirements and set the scene
- Enforce API design standards to meet regulatory requirements
- Use SwaggerHub to unlock the advantages of the design-first approach for APIs
- Use ReadyAPI for integrated API testing (functional/security/perf)
- Use ReadyAPI setup rich API virtualization
- Use PactFlow Implement bi-directional API contract testing
- Leverage API definitions and artifacts for DevOps automation
- Run through provider and client deployments with quality-gated CI/CD flows
- Demonstrate how we can safely catch breaking changes
-
FrontEnd
- Team: Financial Payments Team
- Tech: React / TypeScript
- Deployment: GitHub Actions / Netlify
- Tools Used:
- SwaggerHub
- Pact
- PactFlow
-
BackEnd
- Team: Customer Management Team
- Tech: .NET Core 6 / Azure Functions
- Deployment: GitHub Actions / Azure
- Tools Used:
- SwaggerHub
- ReadyAPI
- PactFlow
- Create your API definition in SwaggerHub
- Enable AutoMocking to ensure you have a VirtServer available for users to explore, prior to development work beginning.
- Utilise Domains to utilise libraries of common components used across your business
- Invite your team members to collaborate on your API
- Collaborate via comments
- Import OpenAPI from SwaggerHub to ReadyAPI
- Create ReadyAPI Functional Tests in the newly created Project
- Test ReadyAPI tests against VirtServer locally in ReadyAPI
- Test ReadyAPI tests against VirtServer locally with test runner GUI
- Test ReadyAPI tests against VirtServer locally with test runner docker image
- Test ReadyAPI tests against VirtServer in CI
- Upload to Provider Contract to PactFlow
Here we implement the API (as per design) by manually creating an Azure Function using .NET 6.0. To speed up the creation and to ensure we conform with the schemas, we'll use SwaggerHub code generation capabilities.
- In SwaggerHub export a server stub from the OpenAPI definition (Export > Server Stub > aspnetcore)
- Extract the models into the Models > OpenAPI folder of the
provider_azure_function
solution - Ensure all paths defined in the OpenAPI definition are implemented by an Azure Function controller
- Build out the appropriate validation services as required
- Ensure all required infrastructure is contained in the Azure Resource Manager template (
azuredeploy.json
) - Build and commit the project
- Kick off the
Provider-API-AzureFunction-CI
GitHub action which covers the following:- checks out and builds
- tests using ReadyAPI project created above
- deploys infra to azure
- adds provider contract to PactFlow leveraging the
can-i-deploy
check
If you want to run and edit the provider locally, then take note of the following:
- VSCode, Visual Studio or other IDE capable of running .NET 6 projects
- .NET 6
- ngrok
The project was created using VSCode with the following extensions installed:
Ensure that your development certs are trusted (required for debugging)
dotnet dev-certs https --trust
Clone the repo and ensure the project builds
dotnet build
Setup your local.settings.json
file. Here is a sample settings configuration:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "",
"FUNCTIONS_WORKER_RUNTIME": "dotnet"
},
"Host": {
"CORS": "*"
}
}
Here we will be creating a website which we will communicate with our provider and present required information to the user on screen.
The scope of our testing with Pact, will be our API collaborator code.
If you are new to Pact, we would recommend checking out our interactive 5 minute guide which demonstrate key concepts of Pact and contract testing.
To speed up the creation and to ensure we conform with the schemas, we can use SwaggerHub code generation capabilities for the API collaborator as a Client-SDK, or we can generate our API collaborator directly.
NOTE: If you are generating your own API collaborator code, you can skip steps 2-8 and use the code below as a starter for your Pact test
- Create our react app
npx create-react-app smartbearcoin-payments-ui --template typescript
- In SwaggerHub export a Client SDK from the OpenAPI definition (Export > Client SDK > Typescript Fetch)
- Extract the code from typescript-fetch folder of the
smartbearcoin-payments-ui/src
solution cd smartbearcoin-payments-ui
npm install cross-fetch
- replace
isomorphicFetch
withcrossfetch
in importsimport crossfetch from 'cross-fetch';
- Update these tests to use
new Date
as just plain unquoted stringsconst transactionFrom: Date = new Date('2013-10-20T19:20:30+01:00');
const transactionUntil: Date = new Date('2013-10-20T19:20:30+01:00');
- Update test assertions to have assertions, can use expected responses
- Install pact
npm install @pact-foundation/pact
- Copy the test code below into file
smartbearcoin-payments-ui/src/typescript-fetch/api_test.spec.ts
- Run
npm run test
- pact files will be generated in
swaggerhub-consumer/pacts
and can now be uploaded to Pactflow
- pact files will be generated in
- Once you are generating consumer contracts at the lowest level in your code (the api collaborator), the team can now concentrate on building out the rest of the website, by consuming the data from the client sdk, or api collaborator code that your team has created (see
./smartbearcoin-payments-ui/src/api.pact.spec.ts
). The expecations in the provider response should contain only the required attributes for the consumer code to perform. Additional fields, will bound your provider to ensuring it always provides those fields despite your consumer code not requiring it.
Your test code will look something like
import * as api from './api';
import { Configuration } from './configuration';
import { PactV3, MatchersV3 } from '@pact-foundation/pact';
const provider = new PactV3({
consumer: 'swaggerhub-consumer',
provider: 'swaggerhub-provider'
});
const config: Configuration = {};
const { like } = MatchersV3;
describe('CustomerManagementPayeesApi', () => {
let instance: api.CustomerManagementPayeesApi;
test('getPayeeById', () => {
const payeeId: string = 'payeeId_example';
const xAuthorization: string = 'xAuthorization_example';
const expectedData = {
account_name: 'string',
any_bic: 'AIBKIE2D',
bank_account_currency: 'EUR',
bank_address: { city: 'string', country: 'IE', street: 'string' },
bank_code: 'string',
bank_name: 'AAAA Bank',
iban: 'IE01AIBK935955939393',
id: 'string',
name: 'string',
organization_name: 'string',
payee_address: { city: 'string', country: 'IE', street: 'string' },
payee_type: 'Person',
personal_information: { family_name: 'string', first_name: 'string' },
remittance_email_address: '[email protected]'
};
provider
.given('is authenticated')
.given('a payee exists')
.uponReceiving('a request to get a payee')
.withRequest({
method: 'GET',
path: '/payees/' + payeeId,
headers: {
'x-Authorization': like('Bearer 1234')
}
})
.willRespondWith({
status: 200,
body: expectedData
});
return provider.executeTest(async (mockserver) => {
instance = new api.CustomerManagementPayeesApi({
...config,
basePath: mockserver.url
});
const result = await instance.getPayeeById(payeeId, xAuthorization, {});
expect(result).toEqual(expectedData);
return;
});
});
test('getPayees', () => {
const countryOfRegistration: string = 'IE';
const xAuthorization: string = 'xAuthorization_example';
const jurisdictionIdentifier: string = 'jurisdictionIdentifier_example';
const jurisdictionIdentifierType: string = 'chamber-of-commerce-number';
const name: string = 'name_example';
const page: number = 56;
const sort: Array<string> = [];
const expectedData = {
data: [
{
account_name: 'string',
any_bic: 'AIBKIE2D',
bank_account_currency: 'EUR',
bank_address: { city: 'string', country: 'IE', street: 'string' },
bank_code: 'string',
bank_name: 'AAAA Bank',
iban: 'IE01AIBK935955939393',
id: 'string',
name: 'string',
organization_name: 'string',
payee_address: {
city: 'string',
country: 'IE',
street: 'string'
},
payee_type: 'Person',
personal_information: { family_name: 'string', first_name: 'string' },
remittance_email_address: '[email protected]'
}
],
links: {
links: [
{ last: 'string', next: 'string', previous: 'string', self: 'string' }
],
page: 0,
pageCount: 0,
pageSize: 0,
recordCount: 0
}
};
provider
.given('is authenticated')
.given('payees exist')
.uponReceiving('A request to list all payees')
.withRequest({
method: 'GET',
path: '/payees',
query: {
jurisdiction_identifier: jurisdictionIdentifier,
name,
page: page.toString(),
jurisdiction_identifier_type: jurisdictionIdentifierType,
country_of_registration: countryOfRegistration
},
headers: {
'x-Authorization': like('Bearer 1234')
}
})
.willRespondWith({
status: 200,
body: expectedData
});
return provider.executeTest(async (mockserver) => {
instance = new api.CustomerManagementPayeesApi({
...config,
basePath: mockserver.url
});
const result = await instance.getPayees(
countryOfRegistration,
xAuthorization,
jurisdictionIdentifier,
jurisdictionIdentifierType,
name,
page,
sort,
{}
);
expect(result).toEqual(expectedData);
return;
});
});
});
- Clone this repo
cd smartbearcoin-payments-ui
npm run test
- Run the Pact tests locallynpm run start
- Run the website locally- You can update the endpoint used for the provider, by setting the environment variable
REACT_APP_API_BASE_URL
- https://sbdevrel-fua-smartbearcoin-prd.azurewebsites.net/api - Production
- https://sbdevrel-fua-smartbearcoin-acc.azurewebsites.net/api - Acceptance
- https://virtserver.swaggerhub.com/mhiggins-sa/payee-api/1.0.0 - Mock
- http://localhost:7071/api/ - Locally running provider (see provider section for details on how to run locally)
- You can update the endpoint used for the provider, by setting the environment variable
You can utilise SwaggerHub Explore to see how an API behaves in the real world, whether the API is deployed to an environment, or running locally on your machine. It's free to sign in with your SwaggerHub account.
- Visit https://explore.swaggerhub.com/
- Copy the following URL and paste it into the address bar
https://sbdevrel-fua-smartbearcoin-prd.azurewebsites.net/api/payees?country_of_registration=IE&jurisdiction_identifier=06488522&jurisdiction_identifier_type=chamber-of-commerce-number
- Press
Send
- Congrats 🎉 you just send your first request in Explore.
- Visit https://explore.swaggerhub.com/
- Click on the network connection icon
- Select Local connection
- Download the relevant explore local execution agent for your platform (Windows/Linux/MacOS)
- Run the local execution agent
- Run the provider code as described above in the
Coding & Deployment - Provider
so that your API is locally running - Use
cURL
and check you can call the API correctlycurl http://localhost:7071/api/payees?country_of_registration=IE&jurisdiction_identifier=06488522&jurisdiction_identifier_type=chamber-of-commerce-number
- Copy the following URL and paste it into the address bar
http://localhost:7071/api/payees?country_of_registration=IE&jurisdiction_identifier=06488522&jurisdiction_identifier_type=chamber-of-commerce-number
- Press
Send
- Congrats 🎉 you just send your first request to a locally running service with Explore.