SaFi Bank Space : Tyk API import(route) & code promotion

Open API Specification

For all our micro-services, we are using Version 3.0.3 -

The OpenAPI Specification (OAS) defines a standard, language-agnostic interface to RESTful APIs which allows both humans and computers to discover and understand the capabilities of the service without access to source code, documentation, or through network traffic inspection. When properly defined, a consumer can understand and interact with the remote micro-service with a minimal amount of implementation logic

Example OAS specs for our micro-services:

card-manager micro-service: https://card-manager.apps.dev.safibank.online/swagger/card-manager.yml

customer-manger micro-service: https://customer-manager.apps.dev.safibank.online/swagger/customer-manager.yml

advanceai-gateway micro-service: https://advanceai-gateway.apps.dev.safibank.online/swagger/advanceai-gateway.yml

We are converting all the micro-services OAS -Open API Specification yml files to json files since the tyk-import API needs json as OAS swagger importer with the below code..and also handling all the special characters encoding with resp.encoding = resp.apparent_encoding and appending ListenPath on the fly with yaml_output['basePath'] = str(f"/{self.name}/")

the below code can be found here: https://github.com/SafiBank/SaFiMono/blob/main/devops/scripts/tyk/api-import/tyk_api_importer.py

    def swagger_json(self):
        try:
            headers = {'Accept': 'text/yaml'}
            resp = requests.get(self.url, headers)
            resp.encoding = resp.apparent_encoding

            if resp.status_code == 200:
                yaml_output = yaml.safe_load(resp.text)
                if 'basePath' not in yaml_output:
                    yaml_output['basePath'] = str(f"/{self.name}/")
                print("[DEVOPS-INFO] - yaml safe load successful for " + self.url)
                self.swagger = str(json.dumps(yaml_output))
                self.version_name = str(yaml_output["info"]["version"])
                print("[DEVOPS-INFO] - json dumps successful for {url}".format(url=self.url))
                return True

            else:
                print("[DEVOPS-ERROR] - URL {url} returned resp status_code code {status_code}".format(url=self.url,
                                                                                                       status_code=resp.status_code))
                return False

        except Exception as e:
            print("[DEVOPS-ERROR] - Exception during swagger_json for URL {url}".format(url=self.url))
            print("[DEVOPS-ERROR] - Exception error message{e}".format(e=e))
            return False

Once we have OAS json with the above code - we need to pass this importer file to the tyk-dashboard import API https://tyk.io/docs/getting-started/import-apis/#import-apis-via-the-dashboard-api as mentioned below.

If it’s a new API we check the insert_into_api: False and if it’s an existing API we check the insert_into_api: True and run the Dashboard import API to import the API’s to the Tyk API Gateway.

    def swagger_import(self):
        if self.swagger_json():
            tyk_dash_url = str(environ.get('TYK_DASH_URL'))
            tyk_swagger_import_url = "{tyk_dash_url}/api/import/swagger/".format(tyk_dash_url=tyk_dash_url)
            tyk_apis_url = "{tyk_dash_url}/api/apis".format(tyk_dash_url=tyk_dash_url)
            headers = {
                'authorization': str(environ.get('TYK_DASH_SECRET')),
                'Content-Type': 'application/json'
            }
            print("[DEVOPS-INFO] - TYK_DASH_URL " + str(environ.get('TYK_DASH_URL')))
            print("[DEVOPS-INFO] - TYK_DASH_SECRET " + str(environ.get('TYK_DASH_SECRET')))

            tyk_apis = requests.get(tyk_apis_url, headers=headers)
            if tyk_apis.status_code == 200:
                tyk_api = [api for api in tyk_apis.json()["apis"] if api["api_definition"]["name"] == self.name]
            else:
                print("[DEVOPS-ERROR] - TYK Dashboard {tyk_apis_url} failed with http status code: {status_code}\n\n".
                      format(tyk_apis_url=tyk_apis_url, status_code=tyk_apis.status_code))
                exit(1)

            if tyk_api:
                tyk_api_meta = tyk_api[0]
                print("[DEVOPS-INFO] - updating api with insert_into_api true")
                payload = json.dumps({
                    "swagger": self.swagger,
                    "insert_into_api": True,
                    "api_id": tyk_api_meta["api_definition"]["id"],
                    "upstream_url": self.upstream_url
                })
            else:
                print("[DEVOPS-INFO] - creating new api with insert_into_api false")
                payload = json.dumps({
                    "swagger": self.swagger,
                    "insert_into_api": False,
                    "upstream_url": self.upstream_url
                })

Code Promotion using environments

We use GHA environments to deploy the API’s to different environments - https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment

We just need to declare the two environment variables in each environment dev, stage and production like below in the SaFiMono repo settings under Environments (smile)

  1. TYK_DASH_URL="https://tyk-dash.smallog.tech"

  2. TYK_DASH_SECRET="0b5b572c1cad4XXYYYZZZZZ"

Code Promotion to Development Environment

First, declare TYK_DASH_URL and TYK_DASH_SECRET as dev environment secrets in the github environments.

To import the APIs to development environment automatically we have GHA workflow pipeline where we can just tag with pattern like this TYK-API-IMPORT-V*

And GHA workflow file is here: https://github.com/SafiBank/SaFiMono/blob/main/.github/workflows/tyk-api-importer.yml

[main][~/git/SaFiMono]$ git tag -a TYK-API-IMPORT-V1.0.11 -m "import apis in dev"
[main][~/git/SaFiMono]$ git push origin TYK-API-IMPORT-V1.0.11
Enumerating objects: 1, done.
Counting objects: 100% (1/1), done.
Writing objects: 100% (1/1), 194 bytes | 194.00 KiB/s, done.
Total 1 (delta 0), reused 0 (delta 0), pack-reused 0
To github.com:SafiBank/SaFiMono.git
 * [new tag]           TYK-API-IMPORT-V1.0.11 -> TYK-API-IMPORT-V1.0.11
[main][~/git/SaFiMono]$ 

Once we tag like that the below job will trigger and import all the micro-services APIs to the development environment.

jobs:
  tyk-api-importer-tag:
    if: startsWith(github.ref, 'refs/tags/TYK-API-IMPORT-')
    runs-on: ubuntu-latest
    environment: dev
    env:
      TYK_DASH_URL: ${{ secrets.TYK_DASH_URL }}
      TYK_DASH_SECRET: ${{ secrets.TYK_DASH_SECRET }}
      TYK_SCRIPT_PATH: "./devops/scripts/tyk/api-import"
      SERVICE_ENV: "dev"
    steps:
      - name: Check out repository code
        uses: actions/checkout@v3

      - name: Setup python
        uses: actions/setup-python@v2
        with:
          python-version: '3.8'

      - name: install python packages
        run: pip install -r ${TYK_SCRIPT_PATH}/requirements.txt

      - name: Execute py script for github actions trigger mode
        run: |
          service_names=$(yq ".on.push.paths[]" .github/workflows/tyk-api-importer.yml | awk -F'/' '{print $2}')
          python_exit_status=0
          service_fail_list=""
          for SERVICE_NAME in ${service_names}; do
            echo [DEVOPS-INFO] - SERVICE_NAME: ${SERVICE_NAME}
            python ${TYK_SCRIPT_PATH}/tyk_api_importer.py --name=${SERVICE_NAME} --env=${SERVICE_ENV} \
              --url="https://${SERVICE_NAME}.apps.${SERVICE_ENV}.safibank.online/swagger/${SERVICE_NAME}.yml" \
              --upstream_url="http://${SERVICE_NAME}.apps.${SERVICE_ENV}.safibank.internal/swagger/${SERVICE_NAME}.yml" \
              || (python_exit_status=1 && service_fail_list="${service_fail_list} ${SERVICE_NAME}")
          done
          [[ ${python_exit_status} -ne 0 ]] && echo [DEVOPS-ERROR] - Failed service list: ${service_fail_list}  
          exit ${python_exit_status}

Code Promotion to stage and production environment

First, declare TYK_DASH_URL and TYK_DASH_SECRET as stage and production environment secrets in the github environments.

To import the APIs to Stage and Production environment we have a workflow dispatch in GHA workflow pipeline where we can just choose the Environment and the Service Name API to import and run the GHA Workflow pipeline.