Merge pull request #295 from blockfrost/chore/release0157
fix: fastify schema, release 0.1.57
fix: fastify schema, release 0.1.57
## [Unreleased]
## [0.1.57] - 2023-03-17
### Fixed
- `getSchemaForEndpoint` compatibility with fast-json-stringify (array `type` not supported except for `["<type>", "null"]`)
- `getSchemaForEndpoint` compatibility with fast-json-stringify (array in `type` not supported except for `["<type>", "null"]`, fix for nested arbitrary objects)
- generated ts types and schema for `/ipfs/gateway/{IPFS_path}`
## [0.1.56] - 2023-03-15
openapi: 3.1.0
info:
version: 0.1.56
version: 0.1.57
title: Blockfrost.io ~ API Documentation
x-logo:
url: https://staging.blockfrost.io/images/logo.svg
responses:
'200':
description: Returns the object content
content:
application/octet-stream:
schema:
type: string
format: binary
'400':
$ref: '#/components/responses/400'
'403':
{
"name": "@blockfrost/openapi",
"version": "0.1.56",
"version": "0.1.57",
"description": "OpenAPI specifications for blockfrost.io",
"repository": "[email protected]:blockfrost/openapi.git",
"author": "[email protected]",
"@vitest/coverage-c8": "^0.25.3",
"ajv": "^8.11.2",
"core-js": "3.1.4",
"openapi-typescript": "^6.1.0",
"openapi-typescript": "6.1.0",
"react-is": "16.8.2",
"redoc-cli": "^0.13.20",
"rimraf": "^3.0.2",
"typescript": "^4.8.4",
"typescript": "^5.0.2",
"vite": "^3.2.4",
"vitest": "^0.24.3"
},
openapi: 3.1.0
info:
version: "0.1.56"
version: "0.1.57"
title: Blockfrost.io ~ API Documentation
x-logo:
url: https://staging.blockfrost.io/images/logo.svg
);
const spec = YAML.parse(file);
export const convertType = (schema: any) => {
export const transformSchemaElement = (schema: any): 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]);
for (const propertyKey of Object.keys(schema.properties)) {
const property = schema.properties[propertyKey];
if (
property.type === 'object' &&
property.additionalProperties === true &&
!property.properties
) {
if (!property.anyOf && !property.oneOf) {
// Workaround for fast-json-stringify
// If object's property is arbitrary object,
// convert {type: 'object', additionalProperties: true} to {}
delete schema.properties[propertyKey].type;
delete schema.properties[propertyKey].additionalProperties;
}
}
if (property.anyOf) {
if (
property.anyOf.find(
(p: unknown) =>
typeof p === 'object' &&
p !== null &&
'type' in p &&
p.type === 'null',
)
) {
// if array of anyOf items includes {"type": "null"} then set nullable to true on the parent
property.nullable = true;
}
}
schema.properties[propertyKey] = transformSchemaElement(
schema.properties[propertyKey],
);
}
return schema;
} else if (schema.type === 'array' && schema.items) {
// convert type in array items
schema.items = convertType(schema.items);
schema.items = transformSchemaElement(schema.items);
return schema;
} else if (Array.isArray(schema.type)) {
const isNullable = schema.type.includes('null');
)}. Type doesn't support an array with multiple values. Use anyOf/oneOf.`,
);
}
return {
return transformSchemaElement({
...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) {
for (const response of Object.keys(endpoint.responses)) {
// success 200
if (response === '200') {
const contentType =
'application/octet-stream' in endpoint.responses['200'].content
? 'application/octet-stream'
: 'application/json';
const referenceOrValue =
endpoint.responses['200'].content['application/json'].schema;
endpoint.responses['200'].content[contentType].schema;
// is reference -> resolve references
if (referenceOrValue['$ref']) {
);
if (schemaReferenceOrValue.type) {
responses.response[200] = convertType({
responses.response[200] = transformSchemaElement({
...schemaReferenceOrValue,
items: spec.components.schemas[nestedSchemaName],
});
} else {
responses.response[200] = convertType(
responses.response[200] = transformSchemaElement(
spec.components.schemas[nestedSchemaName],
);
}
} else {
// is not nested reference
responses.response[200] = convertType(
responses.response[200] = transformSchemaElement(
spec.components.schemas[schemaName],
);
}
} else {
// is not reference
responses.response[200] = convertType(referenceOrValue);
responses.response[200] = transformSchemaElement(referenceOrValue);
}
// anyOf case
'',
);
const item = convertType(spec.components.schemas[schemaName]);
const item = transformSchemaElement(
spec.components.schemas[schemaName],
);
anyOfResult['anyOf'].push(item);
}
}
if (endpointName === '/scripts/{script_hash}/json') {
// TODO: no longer necessary
responses.response[200] = scriptsJsonSchema;
}
}
return responses;
};
export const generateSchemas = () => {
// Returns fast-json-stringify compatible schema object indexed by endpoint name
const endpoints = Object.keys(spec.paths);
const schemas: Record<string, unknown> = {};
for (const endpoint of endpoints) {
try {
schemas[endpoint] = getSchemaForEndpoint(endpoint);
} catch (error) {
console.error(`Error while processing endpoint ${endpoint}`);
throw error;
}
}
return schemas;
};
export const getSchema = (schemaName: string) => {
if (!spec.components.schemas[schemaName]) {
throw Error(`Missing Blockfrost OpenAPI schema with name "${schemaName}".`);
};
responses: {
/** @description Returns the object content */
200: never;
200: {
content: {
"application/octet-stream": string;
};
};
400: components["responses"]["400"];
403: components["responses"]["403"];
404: components["responses"]["404"];
getSchemaForEndpoint,
getSchema,
validateSchema,
generateSchemas,
} from './functions/schema';
import {
getOnchainMetadata,
export {
getSchemaForEndpoint,
getSchema,
generateSchemas,
validateSchema,
getOnchainMetadata,
getCIPstandard,
description: Path to the IPFS object
responses:
"200":
# TODO: If no type is specified then generated TS type is never instead of unknown
# https://github.com/drwpow/openapi-typescript/issues/1039
# As a workaround return binary data
description: Returns the object content
content:
application/octet-stream:
schema:
type: string
format: binary
"400":
$ref: ../../../../responses/errors/400.yaml
"403":
export const convertType = [
export const transformSchemaElement = [
{
description: 'perfectly fine object that does not need transformation',
data: {
type: 'object',
},
},
{
description: 'object with nested arbitrary object',
data: {
type: 'object',
properties: {
key: {
type: 'object',
additionalProperties: true,
},
},
},
result: {
type: 'object',
properties: {
key: {},
},
},
},
];
export const convertTypeError = [
export const transformSchemaElementError = [
{
description: 'array type with 2 types should throw',
data: {
import { expect, describe, test } from 'vitest';
import { generateSchemas } from '../../src/functions/schema';
import { getSchemaForEndpoint } from '../../src/index';
import {
error400,
},
});
});
test('/scripts/datum/{datum_hash}', () => {
expect(getSchemaForEndpoint('/scripts/datum/{datum_hash}')).toStrictEqual({
response: {
'200': {
type: 'object',
properties: {
json_value: {
description: 'JSON content of the datum',
},
},
required: ['json_value'],
example: {
json_value: {
int: 42,
},
},
},
'400': {
type: 'object',
properties: {
status_code: {
type: 'integer',
example: 400,
},
error: {
type: 'string',
example: 'Bad Request',
},
message: {
type: 'string',
example: 'Backend did not understand your request.',
},
},
required: ['status_code', 'error', 'message'],
},
'403': {
type: 'object',
properties: {
status_code: {
type: 'integer',
example: 403,
},
error: {
type: 'string',
example: 'Forbidden',
},
message: {
type: 'string',
example: 'Invalid project token.',
},
},
required: ['status_code', 'error', 'message'],
},
'404': {
type: 'object',
properties: {
status_code: {
type: 'integer',
example: 404,
},
error: {
type: 'string',
example: 'Not Found',
},
message: {
type: 'string',
example: 'The requested component has not been found.',
},
},
required: ['status_code', 'error', 'message'],
},
'418': {
type: 'object',
properties: {
status_code: {
type: 'integer',
example: 418,
},
error: {
type: 'string',
example: 'Requested Banned',
},
message: {
type: 'string',
example: 'IP has been auto-banned for flooding.',
},
},
required: ['status_code', 'error', 'message'],
},
'429': {
type: 'object',
properties: {
status_code: {
type: 'integer',
example: 429,
},
error: {
type: 'string',
example: 'Project Over Limit',
},
message: {
type: 'string',
example: 'Usage is over limit.',
},
},
required: ['status_code', 'error', 'message'],
},
'500': {
type: 'object',
properties: {
status_code: {
type: 'integer',
example: 500,
},
error: {
type: 'string',
example: 'Internal Server Error',
},
message: {
type: 'string',
example: 'An unexpected response was received from the backend.',
},
},
required: ['status_code', 'error', 'message'],
},
},
params: {
type: 'object',
properties: {
datum_hash: {
type: 'string',
},
},
},
});
});
test('/scripts/{script_hash}/json', () => {
expect(getSchemaForEndpoint('/scripts/{script_hash}/json'))
.toMatchInlineSnapshot(`
{
"params": {
"properties": {
"script_hash": {
"type": "string",
},
},
"type": "object",
},
"response": {
"200": {
"properties": {
"json": {
"nullable": true,
},
},
"required": [
"json",
],
"type": "object",
},
"400": {
"properties": {
"error": {
"example": "Bad Request",
"type": "string",
},
"message": {
"example": "Backend did not understand your request.",
"type": "string",
},
"status_code": {
"example": 400,
"type": "integer",
},
},
"required": [
"status_code",
"error",
"message",
],
"type": "object",
},
"403": {
"properties": {
"error": {
"example": "Forbidden",
"type": "string",
},
"message": {
import { expect, describe, test } from 'vitest';
import { getSchema } from '../../src/index';
import { convertType } from '../../src/functions/schema';
import { transformSchemaElement } from '../../src/functions/schema';
import * as fixtures from '../fixtures/schema';
describe('getSchema', () => {
});
});
fixtures.convertType.map(fixture => {
test(`getMetadataFromOutputDatum: ${fixture.description}`, async () => {
expect(convertType(fixture.data)).toStrictEqual(fixture.result);
fixtures.transformSchemaElement.map(fixture => {
test(`transformSchemaElement: ${fixture.description}`, async () => {
expect(transformSchemaElement(fixture.data)).toStrictEqual(
fixture.result,
);
});
});
fixtures.convertTypeError.map(fixture => {
test(`getMetadataFromOutputDatum: ${fixture.description}`, async () => {
expect(() => convertType(fixture.data)).toThrowError(fixture.result);
fixtures.transformSchemaElementError.map(fixture => {
test(`transformSchemaElement: ${fixture.description}`, async () => {
expect(() => transformSchemaElement(fixture.data)).toThrowError(
fixture.result,
);
});
test(`transformSchemaElement: ${fixture.description}`, async () => {
expect(() => transformSchemaElement(fixture.data)).toThrowError(
fixture.result,
);
});
});
});
ajv: ^8.11.2
cbor: ^8.1.0
core-js: 3.1.4
openapi-typescript: ^6.1.0
openapi-typescript: 6.1.0
react-is: 16.8.2
redoc-cli: ^0.13.20
rimraf: ^3.0.2
typescript: ^4.8.4
typescript: ^5.0.2
vite: ^3.2.4
vitest: ^0.24.3
yaml: ^2.2.1
languageName: node
linkType: hard
"[email protected]:^6.1.0":
"[email protected]:6.1.0":
version: 6.1.0
resolution: "[email protected]:6.1.0"
dependencies:
languageName: node
linkType: hard
"[email protected]:^4.8.4":
version: 4.8.4
resolution: "[email protected]:4.8.4"
"[email protected]:^5.0.2":
version: 5.0.2
resolution: "[email protected]:5.0.2"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
checksum: 3e4f061658e0c8f36c820802fa809e0fd812b85687a9a2f5430bc3d0368e37d1c9605c3ce9b39df9a05af2ece67b1d844f9f6ea8ff42819f13bcb80f85629af0
checksum: bef1dcd166acfc6934b2ec4d72f93edb8961a5fab36b8dd2aaf6f4f4cd5c0210f2e0850aef4724f3b4913d5aef203a94a28ded731b370880c8bcff7e4ff91fc1
languageName: node
linkType: hard
"[email protected]:typescript@^4.8.4#~builtin<compat/typescript>":
version: 4.8.4
resolution: "[email protected]:[email protected]%3A4.8.4#~builtin<compat/typescript>::version=4.8.4&hash=0102e9"
"[email protected]:typescript@^5.0.2#~builtin<compat/typescript>":
version: 5.0.2
resolution: "[email protected]:[email protected]%3A5.0.2#~builtin<compat/typescript>::version=5.0.2&hash=d73830"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
checksum: 301459fc3eb3b1a38fe91bf96d98eb55da88a9cb17b4ef80b4d105d620f4d547ba776cc27b44cc2ef58b66eda23fe0a74142feb5e79a6fb99f54fc018a696afa
checksum: bdbf3d0aac0d6cf010fbe0536753dc19f278eb4aba88140dcd25487dfe1c56ca8b33abc0dcd42078790a939b08ebc4046f3e9bb961d77d3d2c3cfa9829da4d53
languageName: node
linkType: hard
linkType: hard
"[email protected]:^5.12.0":
version: 5.20.0
resolution: "[email protected]:5.20.0"
version: 5.21.0
resolution: "[email protected]:5.21.0"
dependencies:
busboy: ^1.6.0
checksum: 25412a785b2bd0b12f0bb0ec47ef00aa7a611ca0e570cb7af97cffe6a42e0d78e4b15190363a43771e9002defc3c6647c1b2d52201b3f64e2196819db4d150d3
checksum: 013d5fd503b631d607942c511c2ab3f3fa78ebcab302acab998b43176b4815503ec15ed9752c5a47918b3bff8a0137768001d3eb57625b2bb6f6d30d8a794d6c
languageName: node
linkType: hard
Bumps [log](https://github.com/rust-lang/log) from 0.4.17 to 0.4.18. - [Release notes](https://github.com/rust-lang/log/releases) - [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/log/compare/0.4.17...0.4.18) --- updated-dependencies: - dependency-name: log dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.28.1 to 1.28.2. - [Release notes](https://github.com/tokio-rs/tokio/releases) - [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.28.1...tokio-1.28.2) --- updated-dependencies: - dependency-name: tokio dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
PLT-5901 Implemented checks for valid network addresses.
fix: tuple clause must preserve previous clause properties state
fix: rearrange clauses and fill in gaps now handles nested patterns in a uniform way fix: discards in records was being sorted incorrectly leading to type issues chore: remove some filter maps in cases where None is impossible anyway chore: some refactoring on a couple functions to clean up
fix: tuple clause must preserve previous clause properties state
feat: disable openapi