From e0b12d7d78f5230c72d9318ae08909345931c701 Mon Sep 17 00:00:00 2001
From: itachi sasuke <8012032+yurisasuke@users.noreply.github.com>
Date: Fri, 7 Feb 2025 20:42:58 +0300
Subject: [PATCH] [DX-1780]Generate test for tyk gateway swagger (#6827)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
# OpenAPI Specification Contract Testing with Portman
To ensure that the schema in our gateway's OpenAPI specification matches
our API, we have implemented contract tests using Portman. Portman
leverages Postman collections to automatically generate contract tests
from our OpenAPI specifications.
### What the Contract Tests Are Testing:
1. **Request Body Validation**: Verifying that the request body defined
in our Swagger schema matches the request body expected by our gateway
API.
2. **Response Body Validation**: Ensuring that the response returned by
our gateway API matches the response body defined in our Swagger schema.
3. **Content-Type Validation**: Confirming that the content types
defined in the request and response schemas in our Swagger documentation
match those expected and returned by our gateway API.
4. **Header Validation**: Validating that the headers sent to or
returned by the gateway API are as described in our Swagger
documentation.
Full documentation [can be found
here](https://github.com/TykTechnologies/tyk/blob/5540baeb205a9466d661b49ecf19792fa6491f99/ci/tests/schema/specs/README.md)
DX-1780
Summary |
Create contract tests for the tyk gateway |
Type |
Story
|
Status |
In Progress |
Points |
N/A |
Labels |
- |
---
### **User description**
[DX-1780]
## Description
## Related Issue
## Motivation and Context
## How This Has Been Tested
## Screenshots (if appropriate)
## Types of changes
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Refactoring or add test (improvements in base code or adds test
coverage to functionality)
## Checklist
- [ ] I ensured that the documentation is up to date
- [ ] I explained why this PR updates go.mod in detail with reasoning
why it's required
- [ ] I would like a code coverage CI quality gate exception and have
explained why
___
### **PR Type**
Tests, Enhancement
___
### **Description**
- Added a pre-request script for Tyk Gateway reload.
- Configured Portman CLI for OpenAPI specification testing.
- Introduced test cases for API and policy management.
- Updated Swagger schema to allow empty string values.
___
### **Changes walkthrough** 📝
| Relevant
files |
---|
Enhancement |
prescript.jsAdd pre-request script
for Tyk Gateway reload
ci/tests/schema/specs/config/prescript.js
Added a script to reload Tyk Gateway using API key.
Included error handling for missing API key. Logged responses
from the Tyk Gateway reload endpoint.
|
+27/-0
|
swagger.ymlUpdate Swagger schema to
allow empty strings
swagger.yml
Allowed empty string values for execution mode. Updated
version enum to include empty string.
|
+2/-1
|
|
Configuration
changes |
Taskfile.ymlConfigure tasks for
OpenAPI specification tests
ci/tests/schema/specs/Taskfile.yml
Added tasks for running OpenAPI specification tests.
Configured Venom and npm commands for testing.
|
+9/-0
|
portman-cli-options.jsonAdd Portman
CLI configuration for testing
ci/tests/schema/specs/config/portman-cli-options.json
Configured Portman CLI options for OpenAPI testing. Enabled
test inclusion and Newman execution. Set up environment file
and output paths.
|
+12/-0
|
portmanconfig.jsonDefine Portman
configuration for OpenAPI tests
ci/tests/schema/specs/config/portmanconfig.json
Defined contract tests for OpenAPI operations. Added
variable assignments for API operations. Configured pre-request
scripts and request overwrites. Included global settings for
test execution order.
|
+144/-0
|
|
Dependencies |
package.jsonAdd Portman dependency
and npm script
ci/tests/schema/specs/package.json
Added Portman dependency for OpenAPI testing. Configured
npm start script for Portman execution.
|
+16/-0
|
|
Tests |
populate_gateway_test_data.yamlAdd
test cases for API and Gateway management
ci/tests/schema/specs/testdata/populate_gateway_test_data.yaml
Added test cases for API creation and deletion. Included a
test case for Tyk Gateway reload. Configured assertions for
HTTP responses.
|
+61/-0
|
|
___
> 💡 **PR-Agent usage**: Comment `/help "your question"` on any pull
request to receive relevant information
[DX-1780]:
https://tyktech.atlassian.net/browse/DX-1780?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
---------
Co-authored-by: itachi sasuke <8012032+Keithwachira@users.noreply.github.com>
Co-authored-by: Tit Petric
---
.github/workflows/release-tests.yml | 7 +
.gitignore | 7 +
ci/tests/specs/.env.example | 2 +
ci/tests/specs/README.md | 55 +++
ci/tests/specs/Taskfile.yml | 37 ++
ci/tests/specs/config/certs.js | 32 ++
.../specs/config/portman-cli-options.json | 12 +
ci/tests/specs/config/portmanconfig.json | 361 ++++++++++++++++++
ci/tests/specs/config/prescript.js | 27 ++
ci/tests/specs/config/tyk.standalone.conf | 29 ++
ci/tests/specs/docker-compose.yml | 26 ++
ci/tests/specs/package.json | 16 +
ci/tests/specs/test.sh | 21 +
.../testdata/populate_gateway_test_data.yaml | 174 +++++++++
swagger.yml | 6 +-
15 files changed, 810 insertions(+), 2 deletions(-)
create mode 100644 ci/tests/specs/.env.example
create mode 100644 ci/tests/specs/README.md
create mode 100644 ci/tests/specs/Taskfile.yml
create mode 100644 ci/tests/specs/config/certs.js
create mode 100644 ci/tests/specs/config/portman-cli-options.json
create mode 100644 ci/tests/specs/config/portmanconfig.json
create mode 100644 ci/tests/specs/config/prescript.js
create mode 100644 ci/tests/specs/config/tyk.standalone.conf
create mode 100644 ci/tests/specs/docker-compose.yml
create mode 100644 ci/tests/specs/package.json
create mode 100755 ci/tests/specs/test.sh
create mode 100644 ci/tests/specs/testdata/populate_gateway_test_data.yaml
diff --git a/.github/workflows/release-tests.yml b/.github/workflows/release-tests.yml
index cd5715afaa6..d470cdc9748 100644
--- a/.github/workflows/release-tests.yml
+++ b/.github/workflows/release-tests.yml
@@ -39,6 +39,13 @@ jobs:
- id: ecr-login
uses: ./.github/actions/ecr-login
+ - name: Setup CI Tooling
+ uses: shrink/actions-docker-extract@v3
+ with:
+ image: tykio/ci-tools:latest
+ path: /usr/local/bin/.
+ destination: /usr/local/bin
+
- name: Run /ci/tests
shell: bash
env:
diff --git a/.gitignore b/.gitignore
index 9dc5cecaf00..7efd08ab9df 100644
--- a/.gitignore
+++ b/.gitignore
@@ -65,3 +65,10 @@ tyk_linux_*
main
/coprocess/*.pb.go-e
+ci/tests/specs/tmp
+ci/tests/specs/node_modules
+ci/tests/specs/package-lock.json
+ci/tests/specs/gateway.collection.postman.json
+ci/tests/specs/.env
+ci/tests/specs/apps
+ci/tests/specs/policies
diff --git a/ci/tests/specs/.env.example b/ci/tests/specs/.env.example
new file mode 100644
index 00000000000..e02d41e3917
--- /dev/null
+++ b/ci/tests/specs/.env.example
@@ -0,0 +1,2 @@
+PORTMAN_API_Key=example_gateway_secret
+GATEWAY_IMAGE=internal/tyk-gateway
diff --git a/ci/tests/specs/README.md b/ci/tests/specs/README.md
new file mode 100644
index 00000000000..cc9efd39061
--- /dev/null
+++ b/ci/tests/specs/README.md
@@ -0,0 +1,55 @@
+# OpenAPI Specification Contract Testing with Portman
+
+To ensure that the schema in our gateway's OpenAPI specification matches our API, we have implemented contract tests using Portman. Portman leverages Postman collections to automatically generate contract tests from our OpenAPI specifications.
+
+## What the Contract Tests Are Testing:
+
+1. **Request Body Validation**: Verifying that the request body defined in our Swagger schema matches the request body expected by our gateway API.
+
+2. **Response Body Validation**: Ensuring that the response returned by our gateway API matches the response body defined in our Swagger schema.
+
+3. **Content-Type Validation**: Confirming that the content types defined in the request and response schemas in our Swagger documentation match those expected and returned by our gateway API.
+
+4. **Header Validation**: Validating that the headers sent to or returned by the gateway API are as described in our Swagger documentation.
+
+## Overview of the Included Files
+
+1. **config/portman-cli-options.json**: This file sets the configuration for running the Portman CLI (similar to `tyk.conf`). It also specifies the location of your `swagger.yml` file. Full configuration options can be found [here](https://github.com/apideck-libraries/portman#cli-usage).
+
+2. **config/portmanconfig.json**: [This file controls](https://github.com/apideck-libraries/portman#portman-settings) the content and how Portman generates the Postman tests. It specifies the types of tests to generate and any values to override in the generated tests. For example, to override headers or body content in the generated Postman tests, declare the overriding values in this file.
+
+3. **package.json**: Since Portman uses Node.js, this file specifies the Portman version to install and the command to run Portman.
+
+4. **testdata/populate_gateway_test_data.yaml**: A Venom test file that populates the gateway with data needed for the tests.
+
+**The following file is generated after running the tests:**
+
+- **gateway.collection.postman.json**: Contains the tests generated by Portman that Newman will execute.
+
+## How to Run It Locally
+
+To run the tests locally, install the following:
+
+1. [Venom](https://github.com/ovh/venom): Used to populate data needed for the Portman tests.
+
+2. [Portman](https://github.com/apideck-libraries/portman): Generates the contract tests from the Swagger file and converts them into Postman tests.
+
+3. [Newman](https://github.com/postmanlabs/newman): A Postman CLI tool needed to run the contract tests.
+
+Once these dependencies are installed, navigate to the `ci/tests/specs` directory and create a `.env` file using .env.example as template. Inside the `.env` file, add:
+
+```bash
+PORTMAN_API_Key=
+```
+
+After adding the `PORTMAN_API_Key`, run the `task` command from ci/tests/specs directory to run a gateway instance and execute portman tests.
+
+You can then stop the gateway instance by running `task down` command from the ci/tests/specs directory.
+
+## How It Is Run on the CI
+
+The GitHub Action used to run these tests is `swagger-contract-tests.yml`.
+
+- In the CI environment, we launch a live gateway using the using th image created by release.yml GitHub action on every pull request.
+
+- After that, we run the task `task tests`, which uses Portman to generate and execute all the Swagger contract tests.
diff --git a/ci/tests/specs/Taskfile.yml b/ci/tests/specs/Taskfile.yml
new file mode 100644
index 00000000000..89773dbcaa4
--- /dev/null
+++ b/ci/tests/specs/Taskfile.yml
@@ -0,0 +1,37 @@
+version: "3"
+
+dotenv: ['.env']
+
+tasks:
+ default:
+ desc: "Run the portman tests"
+ cmds:
+ - task: up
+ - task: tests
+
+ tests:
+ desc: "Run the OpenAPI specification tests"
+ cmds:
+ - venom run testdata/populate_gateway_test_data.yaml --var bearerToken=$PORTMAN_API_Key --stop-on-failure && rm venom*.log
+ - npm install
+ - npm start
+
+ build:
+ desc: "Build docker image"
+ cmds:
+ - docker compose build
+
+ up:
+ desc: "Bring up env"
+ cmds:
+ - docker compose up -d --wait --force-recreate || { docker compose logs gw; exit 1; }
+
+ down:
+ desc: "Shut down env"
+ cmds:
+ - docker compose down --remove-orphans
+
+ logs:
+ desc: "Tail container logs (live)"
+ cmds:
+ - docker compose logs --tail=10 -f
diff --git a/ci/tests/specs/config/certs.js b/ci/tests/specs/config/certs.js
new file mode 100644
index 00000000000..cbafa4c5db2
--- /dev/null
+++ b/ci/tests/specs/config/certs.js
@@ -0,0 +1,32 @@
+// certificate content
+const certContent = `
+-----BEGIN CERTIFICATE-----
+MIIDxDCCAqygAwIBAgIgEzBjeD6zmb42Re6D1cn9z2iewlkMr7nWwZkxFYphDZ8w
+DQYJKoZIhvcNAQEFBQAwcjETMBEGA1UEBhMKUGVhY2h0cmVlIDEMMAoGA1UECgwD
+dHlrMQwwCgYDVQQLDAN0eWsxDzANBgNVBAMMBnR5ay5pbzEdMBsGCSqGSIb3DQEJ
+ARYOc3VwcG9ydEB0eWsuaW8xDzANBgNVBAMMBnR5ay5pbzAeFw0yNDAzMjUwODQ2
+MzdaFw0zNDAzMjYwODQ2MzdaMGExEzARBgNVBAYTClBlYWNodHJlZSAxDDAKBgNV
+BAoMA3R5azEMMAoGA1UECwwDdHlrMQ8wDQYDVQQDDAZ0eWsuaW8xHTAbBgkqhkiG
+9w0BCQEWDnN1cHBvcnRAdHlrLmlvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAjXjBHRJvcrzO8MIY2impRNRMhWoYlkKM5KIzQiyNxXJSkgXd9CoROCMr
+rQdRPusmFKONvorLRw1JEVhfMiEBG+Vmpmi07zK/STBJXdPNPCBjvaHTbDIx6git
+ezM+aKIDdnYoMDIx9T7zOukzW7AxzZ566oJETNgsz4lcleAwempqRndT91Nf5A97
+QAEL+LnDBpInE2rr36pAKxDRsMAaaBFpgM+tF2+6jd0Sh2gQCA904ZqeOOum7IqU
+KGOfYEQVjicTr+TF6V5N5EVw71jwOZt9WPO7lOh7wRmdgNp0MRFVUp1rITiIYstv
+AwdWEc7nbIj/VhMbAvX1za5hzlSNpwIDAQABo1cwVTAdBgNVHQ4EFgQUpNkYJfJr
+H5b97SmpTzdPjTGDYbMwHwYDVR0jBBgwFoAUpNkYJfJrH5b97SmpTzdPjTGDYbMw
+EwYDVR0RBAwwCoIILip0eWsuaW8wDQYJKoZIhvcNAQEFBQADggEBAIocI/ZqtXyS
+j/oI+Vlh8v/4RPzFHHCCBo8JWrSavYnWdkLl3nFF1pFaXvemlTGgUGL3c8GlujO9
+qiCKtza9XRdPPlZhbUOSTRZ0ck/KEQBZDar6juf2Ye2OgJhzeSsLyXQHD8Co6nGf
+4HE41MmhKSAUm+QIZMFRS98EkARkmtqhqehYhTvaKpvzEaRbMW+y5Dh1f6y0bYos
+Vx4NZLOF1C3/eEZywHJaPAtkPxjlhRVmgjJLDqEaj+DzwFYK2RMbRHCh8LKK8GIS
+CWeLM/p2ROlKFup25txezId1cmrhSP4m4djAX6IkgZSy1tDv3UmQQpPIrc2zdsmb
+4KKpTyHqfYo=
+-----END CERTIFICATE-----
+`;
+
+// Set the request body to the certificate content
+pm.request.body.update({
+ mode: 'raw',
+ raw: certContent
+});
diff --git a/ci/tests/specs/config/portman-cli-options.json b/ci/tests/specs/config/portman-cli-options.json
new file mode 100644
index 00000000000..14dbd1108ad
--- /dev/null
+++ b/ci/tests/specs/config/portman-cli-options.json
@@ -0,0 +1,12 @@
+{
+ "local": "../../../swagger.yml",
+ "baseUrl": "http://localhost:8080",
+ "output": "gateway.collection.postman.json",
+ "oaOutput": "filtered.gateway.swagger.yml",
+ "includeTests": true,
+ "syncPostman": false,
+ "runNewman": true,
+ "envFile": ".env",
+ "ignoreCircularRefs": true,
+ "portmanConfigFile":"config/portmanconfig.json"
+}
diff --git a/ci/tests/specs/config/portmanconfig.json b/ci/tests/specs/config/portmanconfig.json
new file mode 100644
index 00000000000..8e578c35451
--- /dev/null
+++ b/ci/tests/specs/config/portmanconfig.json
@@ -0,0 +1,361 @@
+{
+ "version": 1.0,
+ "$schema": "https://raw.githubusercontent.com/apideck-libraries/portman/main/src/utils/portman-config-schema.json",
+ "tests": {
+ "contractTests": [
+ {
+ "openApiOperation": "*::/*",
+ "excludeForOperations": [
+ "batch"
+ ],
+ "statusSuccess": {
+ "enabled": true
+ }
+ },
+ {
+ "openApiOperation": "*::/*",
+ "excludeForOperations": [
+ "batch"
+ ],
+ "responseTime": {
+ "enabled": false,
+ "maxMs": 300
+ }
+ },
+ {
+ "openApiOperation": "*::/*",
+ "excludeForOperations": [
+ "batch"
+ ],
+ "contentType": {
+ "enabled": true
+ }
+ },
+ {
+ "openApiOperation": "*::/*",
+ "excludeForOperations": [
+ "batch"
+ ],
+ "jsonBody": {
+ "enabled": true
+ }
+ },
+ {
+ "openApiOperation": "*::/*",
+ "excludeForOperations": [
+ "validateAKeyDefinition",
+ "listCerts",
+ "batch"
+ ],
+ "schemaValidation": {
+ "enabled": true
+ }
+ },
+ {
+ "openApiOperation": "*::/*",
+ "headersPresent": {
+ "enabled": true
+ }
+ }
+ ],
+ "contentTests": [],
+ "extendTests": [
+ ],
+ "variationTests": []
+ },
+ "assignVariables": [
+ {
+ "openApiOperationIds": [
+ "addPolicy"
+ ],
+ "collectionVariables": [
+ {
+ "responseBodyProp": "key",
+ "name": "addPolicy.ID"
+ }
+ ]
+ },
+ {
+ "openApiOperationIds": [
+ "addCert"
+ ],
+ "collectionVariables": [
+ {
+ "responseBodyProp": "id",
+ "name": "addCert.id"
+ }
+ ]
+ },
+ {
+ "openApiOperationIds": [
+ "addKey"
+ ],
+ "collectionVariables": [
+ {
+ "responseBodyProp": "key_hash",
+ "name": "addKey.key_hash"
+ },
+ {
+ "responseBodyProp": "key",
+ "name": "addKey.key"
+ }
+ ]
+ },
+ {
+ "openApiOperationIds": [
+ "createApiOAS"
+ ],
+ "collectionVariables": [
+ {
+ "responseBodyProp": "key",
+ "name": "createApiOAS.apiID"
+ }
+ ]
+ },
+ {
+ "openApiOperationIds": [
+ "createApi"
+ ],
+ "collectionVariables": [
+ {
+ "responseBodyProp": "key",
+ "name": "createApi.apiID"
+ }
+ ]
+ }
+ ],
+ "operationPreRequestScripts": [
+ {
+ "openApiOperationIds": [
+ "addCert"
+ ],
+ "scripts": [
+ "file:config/certs.js"
+ ]
+ },
+ {
+ "openApiOperationIds": [
+ "updateApi",
+ "deleteApi",
+ "getApi",
+ "updateApi",
+ "listApiVersions",
+ "deleteOASApi",
+ "getOASApi",
+ "patchApiOAS",
+ "updateApiOAS",
+ "downloadApiOASPublic",
+ "getPolicy"
+ ],
+ "scripts": [
+ "file:config/prescript.js"
+ ]
+ }
+ ],
+ "overwrites": [
+ {
+ "openApiOperationIds": [
+ "getPolicy",
+ "deletePolicy",
+ "updatePolicy"
+ ],
+ "overwriteRequestPathVariables": [
+ {
+ "value": "{{addPolicy.ID}}",
+ "key": "polID",
+ "overwrite": true
+ }
+ ]
+ },
+ {
+ "openApiOperationIds": [
+ "listOASApiVersions",
+ "deleteOASApi",
+ "getOASApi",
+ "patchApiOAS",
+ "updateApiOAS",
+ "downloadApiOASPublic"
+ ],
+ "overwriteRequestPathVariables": [
+ {
+ "value": "{{createApiOAS.apiID}}",
+ "overwrite": true,
+ "key": "apiID"
+ }
+ ]
+ },
+ {
+ "openApiOperationIds": [
+ "batch"
+ ],
+ "overwriteRequestPathVariables": [
+ {
+ "key": "listen_path",
+ "value": "tyk-keyless-api",
+ "overwrite": true
+ }
+ ]
+ },
+ {
+ "openApiOperationIds": [
+ "deleteCerts",
+ "listCertsWithIDs"
+ ],
+ "overwriteRequestPathVariables": [
+ {
+ "key": "certID",
+ "value": "{{addCert.id}}",
+ "overwrite": true
+ }
+ ]
+ },
+ {
+ "openApiOperationIds": [
+ "getKey",
+ "updateKey",
+ "deleteKey"
+ ],
+ "overwriteRequestPathVariables": [
+ {
+ "key": "keyID",
+ "value": "{{addKey.key}}",
+ "overwrite": true
+ }
+ ]
+ },
+ {
+ "openApiOperationIds": [
+ "invalidateOAuthRefresh"
+ ],
+ "overwriteRequestQueryParams": [
+ {
+ "key": "api_id",
+ "value": "f84ve1a04e5648c2797170567971565n"
+ }
+ ]
+ },
+ {
+ "openApiOperationIds": [
+ "getApisForOauthApp"
+ ],
+ "overwriteRequestQueryParams": [
+ {
+ "key": "orgID",
+ "value": ""
+ }
+ ]
+ },
+ {
+ "openApiOperationIds": [
+ "listOAuthClients",
+ "getOAuthClient",
+ "getOAuthClientTokens",
+ "updateOAuthClient",
+ "rotateOauthClient",
+ "deleteOAuthClient"
+ ],
+ "overwriteRequestPathVariables": [
+ {
+ "key": "apiID",
+ "value": "f84ve1a04e5648c2797170567971565n",
+ "overwrite": true
+ }
+ ]
+ },
+ {
+ "openApiOperationIds": [
+ "createOAuthClient"
+ ],
+ "overwriteRequestBody": [
+ {
+ "key": "api_id",
+ "value": "f84ve1a04e5648c2797170567971565n",
+ "overwrite": true
+ }
+ ]
+ },
+ {
+ "openApiOperationIds": [
+ "addKey",
+ "createCustomKey",
+ "createKey",
+ "updateKey"
+ ],
+ "overwriteRequestBody": [
+ {
+ "key": "apply_policies",
+ "overwrite": true,
+ "value": [
+ "46ad120575961080181867e"
+ ]
+ }
+ ]
+ },
+ {
+ "openApiOperationIds": [
+ "updateApiOAS",
+ "patchApiOAS"
+ ],
+ "overwriteRequestBody": [
+ {
+ "overwrite": true,
+ "key": "x-tyk-api-gateway.info.id",
+ "value": "{{createApiOAS.apiID}}"
+ }
+ ]
+ },
+ {
+ "openApiOperationIds": [
+ "setPoliciesToHashedKey"
+ ],
+ "overwriteRequestPathVariables": [
+ {
+ "key": "keyID",
+ "value": "{{addKey.key_hash}}",
+ "overwrite": true
+ }
+ ]
+ },
+ {
+ "openApiOperationIds": [
+ "deleteApi",
+ "getApi",
+ "updateApi",
+ "listApiVersions"
+ ],
+ "overwriteRequestPathVariables": [
+ {
+ "key": "apiID",
+ "value": "{{createApi.apiID}}",
+ "overwrite": true
+ }
+ ]
+ },
+ {
+ "openApiOperationIds": [
+ "updateApi"
+ ],
+ "overwriteRequestBody": [
+ {
+ "key": "api_id",
+ "value": "{{createApi.apiID}}",
+ "overwrite": true
+ }
+ ]
+ }
+ ],
+ "globals": {
+ "collectionPreRequestScripts": [
+ ],
+ "keyValueReplacements": {},
+ "valueReplacements": {},
+ "rawReplacements": [],
+ "orderOfOperations": [
+ "POST::/*",
+ "GET::/*",
+ "PUT::/*",
+ "PATCH::/*",
+ "DELETE::/*"
+ ]
+ }
+}
diff --git a/ci/tests/specs/config/prescript.js b/ci/tests/specs/config/prescript.js
new file mode 100644
index 00000000000..e08b8c56319
--- /dev/null
+++ b/ci/tests/specs/config/prescript.js
@@ -0,0 +1,27 @@
+// Retrieve the API key from the environment variable
+const apiKey = pm.environment.get('apiKey');
+
+// Check if the API key is available
+if (!apiKey) {
+ console.error('API_Key environment variable is not set.');
+ // Optionally, you can abort the request if the API key is missing
+ postman.setNextRequest(null);
+} else {
+ // Define the Tyk Gateway reload endpoint using the baseUrl
+ const tykGatewayReloadUrl = `${pm.variables.get('baseUrl')}/tyk/reload?block=true`;
+
+ // Send a GET request to the reload endpoint with the API key in the header
+ pm.sendRequest({
+ url: tykGatewayReloadUrl,
+ method: 'GET',
+ header: {
+ 'x-tyk-authorization': apiKey
+ }
+ }, function (err, res) {
+ if (err) {
+ console.error('Error reloading Tyk Gateway:', err);
+ } else {
+ console.log('Tyk Gateway reload response:', res.status);
+ }
+ });
+}
diff --git a/ci/tests/specs/config/tyk.standalone.conf b/ci/tests/specs/config/tyk.standalone.conf
new file mode 100644
index 00000000000..f0893777322
--- /dev/null
+++ b/ci/tests/specs/config/tyk.standalone.conf
@@ -0,0 +1,29 @@
+{
+ "log_level": "info" ,
+ "listen_port": 8080,
+ "secret": "example_gateway_secret",
+ "template_path": "/opt/tyk-gateway/templates",
+ "app_path": "/opt/tyk-gateway/apps/",
+ "storage": {
+ "type": "redis",
+ "host": "redis",
+ "port": 6379,
+ "username": "",
+ "password": "",
+ "database": 0
+ },
+ "enable_analytics": false,
+ "health_check": {
+ "enable_health_checks": false,
+ "health_check_value_timeouts": 60
+ },
+ "policies": {
+ "policy_source": "file",
+ "policy_path": "/opt/tyk-gateway/policies"
+ },
+ "hash_keys": true,
+ "enable_hashed_keys_listing": true,
+ "close_connections": false,
+ "allow_insecure_configs": true,
+ "enable_batch_request_support": true
+}
diff --git a/ci/tests/specs/docker-compose.yml b/ci/tests/specs/docker-compose.yml
new file mode 100644
index 00000000000..482bd3334b2
--- /dev/null
+++ b/ci/tests/specs/docker-compose.yml
@@ -0,0 +1,26 @@
+name: open-api-specification-contract-tests
+
+include:
+ - ../../../docker/services/redis.yml
+ - ../../../docker/services/httpbin.yml
+
+services:
+ tyk:
+ image: ${GATEWAY_IMAGE}
+ networks: [ proxy ]
+ build: ../../../.
+ depends_on:
+ - redis
+ volumes:
+ - ./policies:/opt/tyk-gateway/policies
+ - ./apps:/opt/tyk-gateway/apps
+ - ./config/tyk.standalone.conf:/opt/tyk-gateway/tyk.conf
+ ports:
+ - "8080:8080"
+ environment:
+ - TYK_LOGLEVEL=debug
+
+networks:
+ proxy:
+ name: proxy
+ driver: bridge
diff --git a/ci/tests/specs/package.json b/ci/tests/specs/package.json
new file mode 100644
index 00000000000..54615adbf67
--- /dev/null
+++ b/ci/tests/specs/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "cli-filtering",
+ "version": "1.0.0",
+ "description": "ns",
+ "directories": {
+ "example": "examples"
+ },
+ "scripts": {
+ "start": "portman --cliOptionsFile=config/portman-cli-options.json"
+ },
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "@apideck/portman": "1.30.7"
+ }
+}
diff --git a/ci/tests/specs/test.sh b/ci/tests/specs/test.sh
new file mode 100755
index 00000000000..1269cb7c23d
--- /dev/null
+++ b/ci/tests/specs/test.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+set -eo pipefail
+
+function setup {
+ local tag=${1:-"v0.0.0"}
+ # Setup required env vars for docker compose
+ export GATEWAY_IMAGE=${GATEWAY_IMAGE:-"tykio/tyk-gateway:${tag}"}
+
+ docker pull -q $GATEWAY_IMAGE
+}
+
+setup $1
+
+trap "task down" EXIT
+
+echo "Creating .env file..."
+echo "PORTMAN_API_Key=example_gateway_secret" > ".env"
+
+task up
+
+task tests
diff --git a/ci/tests/specs/testdata/populate_gateway_test_data.yaml b/ci/tests/specs/testdata/populate_gateway_test_data.yaml
new file mode 100644
index 00000000000..b37f6e862a4
--- /dev/null
+++ b/ci/tests/specs/testdata/populate_gateway_test_data.yaml
@@ -0,0 +1,174 @@
+name: Manage APIs and Policies
+testcases:
+ - name: Delete API
+ steps:
+ - type: http
+ method: DELETE
+ url: http://localhost:8080/tyk/apis/f84ve1a04e5648c2797170567971565n
+ headers:
+ X-Tyk-Authorization: {{ .bearerToken }}
+ assertions:
+ - result.statuscode ShouldBeIn 200 404
+
+ - name: Delete Keyless API
+ steps:
+ - type: http
+ method: DELETE
+ url: http://localhost:8080/tyk/apis/Nwupjt6uqbohnSxgdrTfm7p7b50a5kgU
+ headers:
+ X-Tyk-Authorization: {{ .bearerToken }}
+ assertions:
+ - result.statuscode ShouldBeIn 200 404
+
+ - name: Delete Policy
+ steps:
+ - type: http
+ method: DELETE
+ url: http://localhost:8080/tyk/policies/46ad120575961080181867e
+ headers:
+ X-Tyk-Authorization: {{ .bearerToken }}
+ assertions:
+ - result.statuscode ShouldBeIn 200 500
+
+ - name: Create API
+ steps:
+ - type: http
+ method: POST
+ url: http://localhost:8080/tyk/apis
+ headers:
+ Content-Type: application/json
+ X-Tyk-Authorization: {{ .bearerToken }}
+ body: |
+ {
+ "api_id": "f84ve1a04e5648c2797170567971565n",
+ "auth": {
+ "auth_header_name": "authorization"
+ },
+ "definition": {
+ "key": "version",
+ "location": "header"
+ },
+ "name": "Tyk Test API",
+ "org_id": "664a14650619d40001f1f00f",
+ "proxy": {
+ "listen_path": "/tyk-api-test/",
+ "strip_listen_path": true,
+ "target_url": "https://httpbin.org"
+ },
+ "use_oauth2": true,
+ "version_data": {
+ "not_versioned": true,
+ "versions": {
+ "Default": {
+ "name": "Default"
+ }
+ }
+ }
+ }
+ assertions:
+ - result.statuscode ShouldEqual 200
+
+ - name: Create Keyless API
+ steps:
+ - type: http
+ method: POST
+ url: http://localhost:8080/tyk/apis
+ headers:
+ Content-Type: application/json
+ X-Tyk-Authorization: {{ .bearerToken }}
+ body: |
+ {
+ "api_id": "Nwupjt6uqbohnSxgdrTfm7p7b50a5kgU",
+ "use_keyless": true,
+ "definition": {
+ "key": "version",
+ "location": "header"
+ },
+ "name": "Tyk Keyless API",
+ "org_id": "664a14650619d40001f1f00f",
+ "proxy": {
+ "listen_path": "/tyk-keyless-api/",
+ "strip_listen_path": true,
+ "target_url": "https://httpbin.org"
+ },
+ "use_oauth2": false,
+ "version_data": {
+ "not_versioned": true,
+ "versions": {
+ "Default": {
+ "name": "Default"
+ }
+ }
+ }
+ }
+ assertions:
+ - result.statuscode ShouldEqual 200
+
+ - name: Create Policy
+ steps:
+ - type: http
+ method: POST
+ url: http://localhost:8080/tyk/policies
+ headers:
+ Content-Type: application/json
+ X-Tyk-Authorization: {{ .bearerToken }}
+ body: |
+ {
+ "access_rights": {
+ "itachi-api": {
+ "allowed_urls": [
+ {
+ "methods": [
+ "GET"
+ ],
+ "url": "/users"
+ }
+ ],
+ "api_id": "f84ve1a04e5648c2797170567971565n",
+ "api_name": "Itachi api",
+ "disable_introspection": false,
+ "versions": [
+ "Default"
+ ]
+ }
+ },
+ "active": true,
+ "hmac_enabled": false,
+ "id": "46ad120575961080181867e",
+ "is_inactive": false,
+ "key_expires_in": 2592000,
+ "max_query_depth": -1,
+ "meta_data": {
+ "update": "sample policy test",
+ "user_type": "mobile_user"
+ },
+ "name": "Sample policy",
+ "partitions": {
+ "acl": true,
+ "complexity": false,
+ "per_api": false,
+ "quota": true,
+ "rate_limit": true
+ },
+ "per": 60,
+ "quota_max": 10000,
+ "quota_renewal_rate": 3600,
+ "rate": 1000,
+ "tags": [
+ "security"
+ ],
+ "throttle_interval": 10,
+ "throttle_retry_limit": 10
+ }
+ assertions:
+ - result.statuscode ShouldEqual 200
+
+ - name: Reload gateway
+ steps:
+ - type: http
+ method: GET
+ url: http://localhost:8080/tyk/reload
+ headers:
+ X-Tyk-Authorization: {{ .bearerToken }}
+ assertions:
+ - result.statuscode ShouldBeIn 200
diff --git a/swagger.yml b/swagger.yml
index 3b05e26306d..ecb7719ecbc 100644
--- a/swagger.yml
+++ b/swagger.yml
@@ -1787,7 +1787,7 @@ paths:
summary: Return one certificate or list multiple certificates in the Tyk Gateway
given a comma separated list of cert IDs.
tags:
- - CertsTag
+ - Certs
/tyk/debug:
post:
description: Used to test API definition by sending sample request and analysing
@@ -6319,6 +6319,7 @@ components:
- executionEngine
- subgraph
- supergraph
+ - "" # Allow empty string
type: string
introspection:
$ref: '#/components/schemas/GraphQLIntrospectionConfig'
@@ -6343,9 +6344,9 @@ components:
type: array
version:
enum:
- - ''''
- "1"
- "2"
+ - "" # Allow empty string
type: string
type: object
GraphQLEngineConfig:
@@ -7307,6 +7308,7 @@ components:
trigger:
type: number
type: object
+ nullable: true
RateLimitType2:
properties:
per: