Skip to content

Latest commit

 

History

History
424 lines (345 loc) · 18.1 KB

File metadata and controls

424 lines (345 loc) · 18.1 KB

Workshop & Webinar showcasing a collaborative API Delivery Scenario

A repo storing assets related to webinar demonstrating a collaborative API design and delivery journey using SwaggerHub, ReadyAPI, and PactFlow

Tooling integration - SmartBear API Platform

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.

Benefits

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:

  1. It unifies teams around a single source of truth - the OAS - and enables standardisation across your services
  2. Allows teams to work independently
  3. Unlocks automation such as code-generation and mock services

PactFlow brings increased visibility into how consumers use your API, enabling:

  1. API consumer and API producer development teams to work in independently and safely
  2. Prevent breaking changes to your API and releasing an incompatible API consumer
  3. 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.

Pre-requisites:

To use this feature, you will need:

The FinTech Scenario

SmartBearCoin

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 😕

Overview

  • 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​

Deliverables

  • 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

End-Deliverable

Integration Guide

shub_webinar_phase1

Requirements & Design

Design phase

  1. Create your API definition in SwaggerHub
    1. https://support.smartbear.com/swaggerhub/docs/apis/creating-api.html
  2. Enable AutoMocking to ensure you have a VirtServer available for users to explore, prior to development work beginning.
    1. https://support.smartbear.com/swaggerhub/docs/integrations/api-auto-mocking.html
  3. Utilise Domains to utilise libraries of common components used across your business
    1. https://support.smartbear.com/swaggerhub/docs/domains/index.html
  4. Invite your team members to collaborate on your API
    1. https://support.smartbear.com/swaggerhub/docs/organizations/working-with/add-delete-users.html#add
  5. Collaborate via comments
    1. https://support.smartbear.com/swaggerhub/docs/collaboration/comments.html

Testing & Virtualisation - Provider

Testing Provider

  1. Import OpenAPI from SwaggerHub to ReadyAPI
    1. https://support.smartbear.com/readyapi/docs/integrations/swagger.html
  2. Create ReadyAPI Functional Tests in the newly created Project
    1. https://support.smartbear.com/readyapi/docs/functional/creating.html#from-project
  3. Test ReadyAPI tests against VirtServer locally in ReadyAPI
    1. https://support.smartbear.com/readyapi/docs/functional/running/case.html
  4. Test ReadyAPI tests against VirtServer locally with test runner GUI
    1. https://support.smartbear.com/readyapi/docs/functional/running/automating/gui.html
  5. Test ReadyAPI tests against VirtServer locally with test runner docker image
    1. https://support.smartbear.com/readyapi/docs/integrations/docker/soapui.html
  6. Test ReadyAPI tests against VirtServer in CI
    1. - name: Test
      run: chmod ugo+rwx ReadyAPI_Tests && ./startServerAndTest.sh
      env:
      PROJECT_FOLDER: ReadyAPI_Tests
      PROJECT_FILE: 'SmartBearCoin-Payee-Provider-readyapi-project.xml'
      SLM_API_KEY: ${{ secrets.SLM_API_KEY }}
      SERVER_DIR: provider_azure_function
      SERVER_COMMAND: 'func start --csharp'
      WAIT_FOR: 'http://localhost:7071'
      ENDPOINT: http://localhost:7071/api
  7. Upload to Provider Contract to PactFlow
    1. https://docs.pactflow.io/docs/bi-directional-contract-testing/contracts/oas#publishing-the-provider-contract--results-to-pactflow

Coding & Deployment - Provider

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.

Provider Code

  1. In SwaggerHub export a server stub from the OpenAPI definition (Export > Server Stub > aspnetcore)
  2. Extract the models into the Models > OpenAPI folder of the provider_azure_function solution
  3. Ensure all paths defined in the OpenAPI definition are implemented by an Azure Function controller
  4. Build out the appropriate validation services as required
  5. Ensure all required infrastructure is contained in the Azure Resource Manager template (azuredeploy.json)
  6. Build and commit the project

Provider Flow

  1. 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:

Prerequisites

  • 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

Running locally

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": "*"
  }
}

 Coding & Deployment - Consumer

Here we will be creating a website which we will communicate with our provider and present required information to the user on screen.

consumer workflow

The scope of our testing with Pact, will be our API collaborator code.

consumer test scope

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

  1. Create our react app npx create-react-app smartbearcoin-payments-ui --template typescript
  2. In SwaggerHub export a Client SDK from the OpenAPI definition (Export > Client SDK > Typescript Fetch)
  3. Extract the code from typescript-fetch folder of the smartbearcoin-payments-ui/src solution
  4. cd smartbearcoin-payments-ui
  5. npm install cross-fetch
  6. replace isomorphicFetch with crossfetch in imports
    1. import crossfetch from 'cross-fetch';
  7. Update these tests to use new Date as just plain unquoted strings
    1. const transactionFrom: Date = new Date('2013-10-20T19:20:30+01:00');
    2. const transactionUntil: Date = new Date('2013-10-20T19:20:30+01:00');
  8. Update test assertions to have assertions, can use expected responses
  9. Install pact npm install @pact-foundation/pact
  10. Copy the test code below into file smartbearcoin-payments-ui/src/typescript-fetch/api_test.spec.ts
  11. Run npm run test
    1. pact files will be generated in swaggerhub-consumer/pacts and can now be uploaded to Pactflow
  12. 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: 'test@smartbear.com'
    };
    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: 'test@smartbear.com'
        }
      ],
      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;
    });
  });
});

Prerequisites

Running locally

  1. Clone this repo
  2. cd smartbearcoin-payments-ui
  3. npm run test - Run the Pact tests locally
  4. npm run start - Run the website locally
    1. You can update the endpoint used for the provider, by setting the environment variable REACT_APP_API_BASE_URL
      1. https://sbdevrel-fua-smartbearcoin-prd.azurewebsites.net/api - Production
      2. https://sbdevrel-fua-smartbearcoin-acc.azurewebsites.net/api - Acceptance
      3. https://virtserver.swaggerhub.com/mhiggins-sa/payee-api/1.0.0 - Mock
      4. http://localhost:7071/api/ - Locally running provider (see provider section for details on how to run locally)

 Exploration

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.

Explore a live API

  1. Visit https://explore.swaggerhub.com/
  2. Copy the following URL and paste it into the address bar
    1. https://sbdevrel-fua-smartbearcoin-prd.azurewebsites.net/api/payees?country_of_registration=IE&jurisdiction_identifier=06488522&jurisdiction_identifier_type=chamber-of-commerce-number
  3. Press Send
  4. Congrats 🎉 you just send your first request in Explore.

Explore a local API

  1. Visit https://explore.swaggerhub.com/
  2. Click on the network connection icon
  3. Select Local connection
  4. Download the relevant explore local execution agent for your platform (Windows/Linux/MacOS)
  5. Run the local execution agent
  6. Run the provider code as described above in the Coding & Deployment - Provider so that your API is locally running
  7. Use cURL and check you can call the API correctly
    1. curl http://localhost:7071/api/payees?country_of_registration=IE&jurisdiction_identifier=06488522&jurisdiction_identifier_type=chamber-of-commerce-number
  8. Copy the following URL and paste it into the address bar
    1. http://localhost:7071/api/payees?country_of_registration=IE&jurisdiction_identifier=06488522&jurisdiction_identifier_type=chamber-of-commerce-number
  9. Press Send
  10. Congrats 🎉 you just send your first request to a locally running service with Explore.