Skip to content

Commit

Permalink
schemaValidation - Improved OpenAPI to JSON schema conversion (#644)
Browse files Browse the repository at this point in the history
* schemaValidation - Improved OpenAPI to JSON schema conversion
  • Loading branch information
thim81 authored Aug 27, 2024
1 parent cac1911 commit 1dc6a68
Show file tree
Hide file tree
Showing 7 changed files with 272 additions and 124 deletions.
82 changes: 41 additions & 41 deletions src/__snapshots__/Portman.test.ts.snap

Large diffs are not rendered by default.

30 changes: 15 additions & 15 deletions src/application/__snapshots__/TestSuite.test.ts.snap

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -38633,7 +38633,7 @@ pm.test(\\"[POST]::/crm/leads - Response has JSON Body\\", function () {
});
",
"// Response Validation
const schema = {\\"properties\\":{\\"status_code\\":{\\"type\\":\\"number\\",\\"description\\":\\"HTTP status code\\",\\"example\\":402},\\"error\\":{\\"type\\":\\"string\\",\\"description\\":\\"Contains an explanation of the status_code as defined in HTTP/1.1 standard (RFC 7231)\\",\\"example\\":\\"Payment Required\\"},\\"typeName\\":{\\"type\\":\\"string\\",\\"description\\":\\"The type of error returned\\",\\"example\\":\\"RequestLimitError\\"},\\"message\\":{\\"type\\":\\"string\\",\\"description\\":\\"A human-readable message providing more details about the error.\\",\\"example\\":\\"Request Limit Reached\\"},\\"detail\\":{\\"type\\":\\"string\\",\\"description\\":\\"Contains parameter or domain specific information related to the error and why it occured.\\",\\"example\\":\\"You have reached your limit of 2000\\"},\\"ref\\":{\\"type\\":\\"string\\",\\"description\\":\\"Link to documentation of error type\\",\\"example\\":\\"https://developers.apideck.com/errors#requestlimiterror\\"}}}
const schema = {\\"properties\\":{\\"status_code\\":{\\"type\\":\\"number\\",\\"description\\":\\"HTTP status code\\"},\\"error\\":{\\"type\\":\\"string\\",\\"description\\":\\"Contains an explanation of the status_code as defined in HTTP/1.1 standard (RFC 7231)\\"},\\"typeName\\":{\\"type\\":\\"string\\",\\"description\\":\\"The type of error returned\\"},\\"message\\":{\\"type\\":\\"string\\",\\"description\\":\\"A human-readable message providing more details about the error.\\"},\\"detail\\":{\\"type\\":\\"string\\",\\"description\\":\\"Contains parameter or domain specific information related to the error and why it occured.\\"},\\"ref\\":{\\"type\\":\\"string\\",\\"description\\":\\"Link to documentation of error type\\"}}}

// Validate if response matches JSON schema
pm.test(\\"[POST]::/crm/leads - Schema is valid\\", function() {
Expand Down Expand Up @@ -40169,7 +40169,7 @@ pm.test(\\"[POST]::/crm/leads - Response has JSON Body\\", function () {
});
",
"// Response Validation
const schema = {\\"properties\\":{\\"status_code\\":{\\"type\\":\\"number\\",\\"description\\":\\"HTTP status code\\",\\"example\\":400},\\"error\\":{\\"type\\":\\"string\\",\\"description\\":\\"Contains an explanation of the status_code as defined in HTTP/1.1 standard (RFC 7231)\\",\\"example\\":\\"Bad Request\\"},\\"typeName\\":{\\"type\\":\\"string\\",\\"description\\":\\"The type of error returned\\",\\"example\\":\\"RequestHeadersValidationError\\"},\\"message\\":{\\"type\\":\\"string\\",\\"description\\":\\"A human-readable message providing more details about the error.\\",\\"example\\":\\"Invalid Params\\"},\\"detail\\":{\\"anyOf\\":[{\\"type\\":\\"string\\",\\"example\\":\\"Missing property foobar\\"},{\\"type\\":\\"object\\",\\"example\\":{\\"missing\\":[[{\\"foobar\\":\\"required\\"}]]}}],\\"description\\":\\"Contains parameter or domain specific information related to the error and why it occured.\\"},\\"ref\\":{\\"type\\":\\"string\\",\\"description\\":\\"Link to documentation of error type\\",\\"example\\":\\"https://developers.apideck.com/errors#requestbodyvalidationerror\\"}}}
const schema = {\\"properties\\":{\\"status_code\\":{\\"type\\":\\"number\\",\\"description\\":\\"HTTP status code\\"},\\"error\\":{\\"type\\":\\"string\\",\\"description\\":\\"Contains an explanation of the status_code as defined in HTTP/1.1 standard (RFC 7231)\\"},\\"typeName\\":{\\"type\\":\\"string\\",\\"description\\":\\"The type of error returned\\"},\\"message\\":{\\"type\\":\\"string\\",\\"description\\":\\"A human-readable message providing more details about the error.\\"},\\"detail\\":{\\"anyOf\\":[{\\"type\\":\\"string\\"},{\\"type\\":\\"object\\"}],\\"description\\":\\"Contains parameter or domain specific information related to the error and why it occured.\\"},\\"ref\\":{\\"type\\":\\"string\\",\\"description\\":\\"Link to documentation of error type\\"}}}

// Validate if response matches JSON schema
pm.test(\\"[POST]::/crm/leads - Schema is valid\\", function() {
Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

138 changes: 127 additions & 11 deletions src/application/tests/testResponseJsonSchema.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { OpenAPIV3 } from 'openapi-types'
import { getOasMappedOperation } from '../../../__tests__/testUtils/getOasMappedOperation'
import { getPostmanMappedOperation } from '../../../__tests__/testUtils/getPostmanMappedOperation'
import { testResponseJsonSchema } from '../../application'
import { convertUnsupportedJsonSchemaProperties, testResponseJsonSchema } from '../../application'
import { OasMappedOperation } from '../../oas'
import { PostmanMappedOperation } from '../../postman'
import { ContractTestConfig, GlobalConfig } from '../../types'
Expand All @@ -16,7 +16,8 @@ describe('testResponseJsonSchema', () => {
})

it('should add test for valid json schema', async () => {
const schema = (oasOperation.schema?.responses?.['200'] as OpenAPIV3.ResponseObject)?.content
const response = (oasOperation.schema?.responses?.['200'] as OpenAPIV3.ResponseObject)?.content
const schema = response?.['application/json'].schema
pmOperation = testResponseJsonSchema(
{ enabled: true } as ContractTestConfig,
schema,
Expand All @@ -42,7 +43,7 @@ describe('testResponseJsonSchema', () => {
expect(pmTest.script.exec).toMatchSnapshot()
})

it('should skip test for invalid json schema', async () => {
it('should skip test for json schema with $ref', async () => {
const schema = {
type: 'object',
properties: {
Expand All @@ -62,6 +63,32 @@ describe('testResponseJsonSchema', () => {
expect(pmTest.script.exec).toMatchSnapshot()
})

it('should skip test for invalid json schema', async () => {
const schema = {
type: 'array',
maxItems: 100,
items: {
typess: ['object', 'null'],
required: ['id', 'name'],
properties: {
id: { type: 'integer', format: 'int64' },
name: { type: 'string' },
tag: { type: 'string' }
},
type: [null, 'null']
}
}

pmOperation = testResponseJsonSchema(
{} as ContractTestConfig,
schema,
pmOperation,
oasOperation
)
const pmTest = pmOperation.getTests()
expect(pmTest.script.exec).toMatchSnapshot()
})

it('should add extra unknown formats', async () => {
const schema = (oasOperation.schema?.responses?.['200'] as OpenAPIV3.ResponseObject)?.content
pmOperation = testResponseJsonSchema(
Expand All @@ -75,7 +102,7 @@ describe('testResponseJsonSchema', () => {
expect(pmTest.script.exec).toMatchSnapshot()
})

it('should remove minItems on items levels, for valid json schema', async () => {
it.skip('should remove minItems on items levels, for valid json schema', async () => {
const schema = {
type: 'array',
items: {
Expand Down Expand Up @@ -109,7 +136,7 @@ describe('testResponseJsonSchema', () => {
expect(pmTest.script.exec).toMatchSnapshot()
})

it('should remove minItems on items levels, for types array/null, for valid json schema', async () => {
it.skip('should remove minItems on items levels, for types array/null, for valid json schema', async () => {
const schema = {
type: ['array', 'null'],
items: {
Expand Down Expand Up @@ -143,7 +170,7 @@ describe('testResponseJsonSchema', () => {
expect(pmTest.script.exec).toMatchSnapshot()
})

it('should remove maxItems on items levels, for valid json schema', async () => {
it.skip('should remove maxItems on items levels, for valid json schema', async () => {
const schema = {
type: 'array',
items: {
Expand Down Expand Up @@ -177,7 +204,7 @@ describe('testResponseJsonSchema', () => {
expect(pmTest.script.exec).toMatchSnapshot()
})

it('should remove maxItems on items levels, for types array/null, for valid json schema', async () => {
it.skip('should remove maxItems on items levels, for types array/null, for valid json schema', async () => {
const schema = {
type: ['array', 'null'],
items: {
Expand Down Expand Up @@ -211,7 +238,7 @@ describe('testResponseJsonSchema', () => {
expect(pmTest.script.exec).toMatchSnapshot()
})

it('should remove minItems on nested levels, for type array, for valid json schema', async () => {
it.skip('should remove minItems on nested levels, for type array, for valid json schema', async () => {
const schema = {
type: 'object',
properties: {
Expand Down Expand Up @@ -263,7 +290,7 @@ describe('testResponseJsonSchema', () => {
expect(pmTest.script.exec).toMatchSnapshot()
})

it('should remove minItems on nested levels, for types array/null, for valid json schema', async () => {
it.skip('should remove minItems on nested levels, for types array/null, for valid json schema', async () => {
const schema = {
type: 'object',
properties: {
Expand Down Expand Up @@ -315,7 +342,7 @@ describe('testResponseJsonSchema', () => {
expect(pmTest.script.exec).toMatchSnapshot()
})

it('should remove maxItems on nested levels, for type array, for valid json schema', async () => {
it.skip('should remove maxItems on nested levels, for type array, for valid json schema', async () => {
const schema = {
type: 'object',
properties: {
Expand Down Expand Up @@ -367,7 +394,7 @@ describe('testResponseJsonSchema', () => {
expect(pmTest.script.exec).toMatchSnapshot()
})

it('should remove maxItems on nested levels, for types array/null, for valid json schema', async () => {
it.skip('should remove maxItems on nested levels, for types array/null, for valid json schema', async () => {
const schema = {
type: 'object',
properties: {
Expand Down Expand Up @@ -522,4 +549,93 @@ describe('testResponseJsonSchema', () => {
const pmTest = pmOperation.getTests()
expect(pmTest.script.exec).toMatchSnapshot()
})

it('should convert OpenAPI schema to valid json schema', async () => {
const schema = {
type: 'array',
maxItems: 100,
minItems: 2,
items: {
types: ['object', 'null'],
nullable: true,
required: ['id', 'name'],
properties: {
id: {
type: 'integer',
format: 'int64',
nullable: true
},
name: {
type: 'string'
},
tag: {
type: 'string',
example: 'sample-tag'
},
description: {
type: 'string',
deprecated: true
},
metadata: {
type: 'object',
properties: {
createdBy: {
type: 'string'
},
createdAt: {
type: 'string',
format: 'date-time'
}
},
discriminator: 'type'
}
},
maxItems: 2,
minItems: 1
}
}

const result = convertUnsupportedJsonSchemaProperties(schema)
expect(result).toEqual({
type: 'array',
maxItems: 100, // Root level maxItems should be kept
minItems: 2,
items: {
type: ['object', 'null'], // 'types' converted to 'type' and 'nullable' handled correctly
required: ['id', 'name'],
properties: {
id: {
type: ['integer', 'null'], // Nullable 'id' should have 'null' added to type
format: 'int64'
},
name: {
type: 'string'
},
tag: {
type: 'string'
// 'example' should be removed as it's OpenAPI-specific
},
description: {
type: 'string'
// 'deprecated' should be removed as it's OpenAPI-specific
},
metadata: {
type: 'object',
properties: {
createdBy: {
type: 'string'
},
createdAt: {
type: 'string',
format: 'date-time'
}
}
// 'discriminator' should be removed as it's OpenAPI-specific
}
},
maxItems: 2,
minItems: 1
}
})
})
})
Loading

0 comments on commit 1dc6a68

Please sign in to comment.