Skip to content

Commit

Permalink
Merge pull request #294 from blockfrost/chore/make-fastify-happy
Browse files Browse the repository at this point in the history
fix(getSchemaForEndpoint): fastify serializer compatible schema
  • Loading branch information
vladimirvolek authored Mar 15, 2023
2 parents 26f9fe4 + fa30a83 commit 2efa517
Show file tree
Hide file tree
Showing 8 changed files with 288 additions and 42 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ Unreleased changes are in the `master` branch.

## [Unreleased]

### Fixed

- `getSchemaForEndpoint` compatibility with fast-json-stringify (array `type` not supported except for `["<type>", "null"]`)

## [0.1.56] - 2023-03-15

### Fix
Expand Down
4 changes: 2 additions & 2 deletions openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5628,6 +5628,7 @@ components:
epoch_stake_content:
type: array
items:
type: object
properties:
stake_address:
type: string
Expand Down Expand Up @@ -8082,8 +8083,7 @@ components:
type: object
properties:
json_value:
type:
- object
type: object
additionalProperties: true
description: JSON content of the datum
required:
Expand Down
68 changes: 62 additions & 6 deletions src/functions/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,57 @@ const file = fs.readFileSync(
);
const spec = YAML.parse(file);

export const convertType = (schema: any) => {
// To generate response schema supported by fast-json-stringify
// We need to convert array type (["null", "<other type>"]) to type: "<other type>" with nullable set to true.
// Note: Alternative approach for values with multiple types is to use anyOf/oneOf.
// https://github.com/fastify/fast-json-stringify#anyof-and-oneof

if (schema.type === 'object' && schema.properties) {
// convert type in object properties
for (const property of Object.keys(schema.properties)) {
schema.properties[property] = convertType(schema.properties[property]);
}
return schema;
} else if (schema.type === 'array' && schema.items) {
// convert type in array items
schema.items = convertType(schema.items);
return schema;
} else if (Array.isArray(schema.type)) {
const isNullable = schema.type.includes('null');
if (isNullable) {
if (schema.type.length > 2) {
throw Error(
`Error in ${JSON.stringify(
schema,
)}. Type doesn't support an array with multiple values. Use anyOf/oneOf.`,
);
}
return {
...schema,
type: schema.type.filter((a: string) => a !== 'null')[0],
nullable: true,
};
} else {
// edge case where type is an array with only 1 element
if (schema.type.length === 1) {
return {
...schema,
type: schema.type[0],
};
}
throw Error(
`Error in ${JSON.stringify(
schema,
)}. Type doesn't support an array with multiple values. Use anyOf/oneOf.`,
);
}
} else {
// do nothing
return schema;
}
};

export const getSchemaForEndpoint = (endpointName: string) => {
if (!spec.paths[endpointName]) {
throw Error(
Expand Down Expand Up @@ -56,20 +107,24 @@ export const getSchemaForEndpoint = (endpointName: string) => {
);

if (schemaReferenceOrValue.type) {
responses.response[200] = {
responses.response[200] = convertType({
...schemaReferenceOrValue,
items: spec.components.schemas[nestedSchemaName],
};
});
} else {
responses.response[200] = spec.components.schemas[nestedSchemaName];
responses.response[200] = convertType(
spec.components.schemas[nestedSchemaName],
);
}
} else {
// is not nested reference
responses.response[200] = spec.components.schemas[schemaName];
responses.response[200] = convertType(
spec.components.schemas[schemaName],
);
}
} else {
// is not reference
responses.response[200] = referenceOrValue;
responses.response[200] = convertType(referenceOrValue);
}

// anyOf case
Expand All @@ -82,7 +137,8 @@ export const getSchemaForEndpoint = (endpointName: string) => {
'',
);

anyOfResult['anyOf'].push(spec.components.schemas[schemaName]);
const item = convertType(spec.components.schemas[schemaName]);
anyOfResult['anyOf'].push(item);
}

responses.response[200] = anyOfResult;
Expand Down
1 change: 1 addition & 0 deletions src/schemas/epochs/epoch_stake_content.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
type: array
items:
type: object
properties:
stake_address:
type: string
Expand Down
3 changes: 1 addition & 2 deletions src/schemas/scripts/script_datum.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
type: object
properties:
json_value:
type:
- object
type: object
additionalProperties: true
description: JSON content of the datum
required:
Expand Down
143 changes: 143 additions & 0 deletions test/fixtures/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
export const convertType = [
{
description: 'perfectly fine object that does not need transformation',
data: {
type: 'object',
properties: {
key: {
type: 'object',
properties: {
nestedkey: 'string',
},
},
},
},
result: {
properties: {
key: {
properties: {
nestedkey: 'string',
},
type: 'object',
},
},
type: 'object',
},
},
{
description: 'nullable object',
data: {
type: 'object',
properties: {
key: {
type: ['object', 'null'],
properties: {
nestedkey: 'string',
},
},
},
},
result: {
properties: {
key: {
nullable: true,
properties: {
nestedkey: 'string',
},
type: 'object',
},
},
type: 'object',
},
},
{
description: 'array with nullable object',
data: {
type: 'array',
items: {
type: 'object',
properties: {
key: {
type: ['object', 'null'],
properties: {
nestedkey: 'string',
},
},
},
},
},
result: {
type: 'array',
items: {
properties: {
key: {
nullable: true,
properties: {
nestedkey: 'string',
},
type: 'object',
},
},
type: 'object',
},
},
},
{
description: 'object with array type of length 1',
data: {
type: 'object',
properties: {
key: {
type: ['object'],
properties: {
nestedkey: 'string',
},
},
},
},
result: {
properties: {
key: {
properties: {
nestedkey: 'string',
},
type: 'object',
},
},
type: 'object',
},
},
];

export const convertTypeError = [
{
description: 'array type with 2 types should throw',
data: {
type: ['object', 'string'],
properties: {
key: {
type: 'object',
properties: {
nestedkey: 'string',
},
},
},
},
result: `Error in {"type":["object","string"],"properties":{"key":{"type":"object","properties":{"nestedkey":"string"}}}}. Type doesn't support an array with multiple values. Use anyOf/oneOf.`,
},
{
description: 'array type with multiple types should throw',
data: {
type: ['object', 'string', 'integer'],
properties: {
key: {
type: 'object',
properties: {
nestedkey: 'string',
},
},
},
},
result: `Error in {"type":["object","string","integer"],"properties":{"key":{"type":"object","properties":{"nestedkey":"string"}}}}. Type doesn't support an array with multiple values. Use anyOf/oneOf.`,
},
];
Loading

0 comments on commit 2efa517

Please sign in to comment.