Skip to content

Instantly share code, notes, and snippets.

@baetheus
Created June 5, 2019 20:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save baetheus/0090a7e7b934c9da7633f9763ede5ae2 to your computer and use it in GitHub Desktop.
Save baetheus/0090a7e7b934c9da7633f9763ede5ae2 to your computer and use it in GitHub Desktop.
io-ts codecs for openapi 3.0.2
import { Option } from 'fp-ts/lib/Option';
import * as t from 'io-ts';
import { createOptionFromNullable } from 'io-ts-types';
/**
* Semver RegExp from https://github.com/sindresorhus/semver-regex/blob/master/index.js
*/
const SEMVER_REGEX = /(?<=^v?|\sv?)(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:[1-9]\d*|[\da-z-]*[a-z-][\da-z-]*)(?:\.(?:[1-9]\d*|[\da-z-]*[a-z-][\da-z-]*))*)?(?:\+[\da-z-]+(?:\.[\da-z-]+)*)?(?=$|\s)/gi;
interface SemverBrand {
readonly Semver: unique symbol; // use `unique symbol` here to ensure uniqueness across modules / packages
}
const Semver = t.brand(
t.string, // a codec representing the type to be refined
(n): n is t.Branded<string, SemverBrand> => SEMVER_REGEX.test(n), // a custom type guard using the build-in helper `Branded`
'Semver' // the name must match the readonly field in the brand
);
type Semver = t.TypeOf<typeof Semver>;
type CodecOf<T> = T extends t.Type<infer A, infer O, infer I>
? [A, O, I]
: never;
/**
* io-ts types for openapi 3.0.2
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md
*/
export const ReferenceObjectIO = t.type({
$ref: t.string,
});
export type ReferenceObject = t.TypeOf<typeof ReferenceObjectIO>;
export const ContactObjectIO = t.type({
name: createOptionFromNullable(t.string),
url: createOptionFromNullable(t.string),
email: createOptionFromNullable(t.string),
});
export type ContactObject = t.TypeOf<typeof ContactObjectIO>;
export const LicenseObjectIO = t.type({
name: t.string,
url: createOptionFromNullable(t.string),
});
export type LicenseObject = t.TypeOf<typeof LicenseObjectIO>;
export const InfoObjectIO = t.type({
title: t.string,
description: createOptionFromNullable(t.string),
termsOfService: createOptionFromNullable(t.string),
contact: createOptionFromNullable(ContactObjectIO),
license: createOptionFromNullable(LicenseObjectIO),
version: t.string,
});
export type InfoObject = t.TypeOf<typeof InfoObjectIO>;
export const ServerVariableObjectIO = t.type({
enum: createOptionFromNullable(
t.union([t.array(t.string), t.array(t.boolean), t.array(t.number)])
),
default: t.union([t.string, t.boolean, t.number]),
description: createOptionFromNullable(t.string),
});
export type ServerVariableObject = t.TypeOf<typeof ServerVariableObjectIO>;
export const ServerObjectIO = t.type({
url: t.string,
description: createOptionFromNullable(t.string),
variables: createOptionFromNullable(
t.record(t.string, ServerVariableObjectIO)
),
});
export type ServerObject = t.TypeOf<typeof ServerObjectIO>;
export const ExternalDocumentationObjectIO = t.type({
url: t.string,
description: createOptionFromNullable(t.string),
});
export type ExternalDocumentationObject = t.TypeOf<
typeof ExternalDocumentationObjectIO
>;
export type ExternalDocumentationObjectO = CodecOf<
typeof ExternalDocumentationObjectIO
>[1];
export const ParameterLocationIO = t.union([
t.literal('query'),
t.literal('header'),
t.literal('path'),
t.literal('cookie'),
]);
export type ParameterLocation = t.TypeOf<typeof ParameterLocationIO>;
export const ParameterStyleIO = t.union([
t.literal('matrix'),
t.literal('label'),
t.literal('form'),
t.literal('simple'),
t.literal('spaceDelimited'),
t.literal('pipeDelimited'),
t.literal('deepObject'),
]);
export type ParameterStyle = t.TypeOf<typeof ParameterStyleIO>;
export const ExampleObjectIO = t.type({
summary: createOptionFromNullable(t.string),
description: createOptionFromNullable(t.string),
value: createOptionFromNullable(t.any),
externalValue: createOptionFromNullable(t.string),
});
export type ExmapleObject = t.TypeOf<typeof ExampleObjectIO>;
export const LinkParametersObjectIO = t.record(t.string, t.any);
export type LinkParametersObject = t.TypeOf<typeof LinkParametersObjectIO>;
export const LinkObjectIO = t.type({
operationRef: createOptionFromNullable(t.string),
operationId: createOptionFromNullable(t.string),
parameters: createOptionFromNullable(LinkParametersObjectIO),
requestBody: createOptionFromNullable(t.any),
description: createOptionFromNullable(t.string),
server: createOptionFromNullable(ServerObjectIO),
});
export type LinkObject = t.TypeOf<typeof LinkObjectIO>;
export const LinksObjectIO = t.record(
t.string,
t.union([LinkObjectIO, ReferenceObjectIO])
);
export type LinksObject = t.TypeOf<typeof LinksObjectIO>;
export const TagObjectIO = t.type({
name: t.string,
description: createOptionFromNullable(t.string),
externalDocs: createOptionFromNullable(ExternalDocumentationObjectIO),
});
export type TagObject = t.TypeOf<typeof TagObjectIO>;
export const ExamplesObjectIO = t.record(
t.string,
t.union([ExampleObjectIO, ReferenceObjectIO])
);
export type ExamplesObject = t.TypeOf<typeof ExamplesObjectIO>;
export type SchemaObject = {
nullable: Option<boolean>;
discriminator: Option<DiscriminatorObject>;
readOnly: Option<boolean>;
writeOnly: Option<boolean>;
xml: Option<XmlObject>;
externalDocs: Option<ExternalDocumentationObject>;
example: Option<any>;
examples: Option<any[]>;
deprecated: Option<boolean>;
type: Option<string>;
allOf: Option<(SchemaObject | ReferenceObject)[]>;
oneOf: Option<(SchemaObject | ReferenceObject)[]>;
anyOf: Option<(SchemaObject | ReferenceObject)[]>;
not: Option<SchemaObject | ReferenceObject>;
items: Option<SchemaObject | ReferenceObject>;
properties: Option<Record<string, SchemaObject | ReferenceObject>>;
additionalProperties: Option<SchemaObject | ReferenceObject | boolean>;
description: Option<string>;
format: Option<string>;
default: Option<any>;
title: Option<string>;
multipleOf: Option<number>;
maximum: Option<number>;
exclusiveMaximum: Option<boolean>;
minimum: Option<number>;
exclusiveMinimum: Option<boolean>;
maxLength: Option<number>;
minLength: Option<number>;
pattern: Option<string>;
maxItems: Option<number>;
minItems: Option<number>;
uniqueItems: Option<boolean>;
maxProperties: Option<number>;
minProperties: Option<number>;
required: Option<string[]>;
enum: Option<any[]>;
};
// This type is necessary to make the types work
export type SchemaObjectO = {
nullable?: boolean;
discriminator?: DiscriminatorObjectO;
readOnly?: boolean;
writeOnly?: boolean;
xml?: XmlObjectO;
externalDocs?: ExternalDocumentationObjectO;
example?: any;
examples?: any[];
deprecated?: boolean;
type?: string;
allOf?: (SchemaObjectO | ReferenceObject)[];
oneOf?: (SchemaObjectO | ReferenceObject)[];
anyOf?: (SchemaObjectO | ReferenceObject)[];
not?: SchemaObjectO | ReferenceObject;
items?: SchemaObjectO | ReferenceObject;
properties?: {
[propertyName: string]: SchemaObjectO | ReferenceObject;
};
additionalProperties?: SchemaObjectO | ReferenceObject | boolean;
description?: string;
format?: string;
default?: any;
title?: string;
multipleOf?: number;
maximum?: number;
exclusiveMaximum?: boolean;
minimum?: number;
exclusiveMinimum?: boolean;
maxLength?: number;
minLength?: number;
pattern?: string;
maxItems?: number;
minItems?: number;
uniqueItems?: boolean;
maxProperties?: number;
minProperties?: number;
required?: string[];
enum?: any[];
};
export const SchemaObjectIO: t.Type<SchemaObject, SchemaObjectO> = t.recursion(
'SchemaObjectIO',
() =>
t.type({
nullable: createOptionFromNullable(t.boolean),
discriminator: createOptionFromNullable(DiscriminatorObjectIO),
readOnly: createOptionFromNullable(t.boolean),
writeOnly: createOptionFromNullable(t.boolean),
xml: createOptionFromNullable(XmlObjectIO),
externalDocs: createOptionFromNullable(ExternalDocumentationObjectIO),
example: createOptionFromNullable(t.any),
examples: createOptionFromNullable(t.array(t.any)),
deprecated: createOptionFromNullable(t.boolean),
type: createOptionFromNullable(t.string),
allOf: createOptionFromNullable(
t.array(t.union([SchemaObjectIO, ReferenceObjectIO]))
),
oneOf: createOptionFromNullable(
t.array(t.union([SchemaObjectIO, ReferenceObjectIO]))
),
anyOf: createOptionFromNullable(
t.array(t.union([SchemaObjectIO, ReferenceObjectIO]))
),
not: createOptionFromNullable(
t.union([SchemaObjectIO, ReferenceObjectIO])
),
items: createOptionFromNullable(
t.union([SchemaObjectIO, ReferenceObjectIO])
),
properties: createOptionFromNullable(
t.record(t.string, t.union([SchemaObjectIO, ReferenceObjectIO]))
),
additionalProperties: createOptionFromNullable(
t.union([SchemaObjectIO, ReferenceObjectIO, t.boolean])
),
description: createOptionFromNullable(t.string),
format: createOptionFromNullable(t.string),
default: createOptionFromNullable(t.any),
title: createOptionFromNullable(t.string),
multipleOf: createOptionFromNullable(t.number),
maximum: createOptionFromNullable(t.number),
exclusiveMaximum: createOptionFromNullable(t.boolean),
minimum: createOptionFromNullable(t.number),
exclusiveMinimum: createOptionFromNullable(t.boolean),
maxLength: createOptionFromNullable(t.number),
minLength: createOptionFromNullable(t.number),
pattern: createOptionFromNullable(t.string),
maxItems: createOptionFromNullable(t.number),
minItems: createOptionFromNullable(t.number),
uniqueItems: createOptionFromNullable(t.boolean),
maxProperties: createOptionFromNullable(t.number),
minProperties: createOptionFromNullable(t.number),
required: createOptionFromNullable(t.array(t.string)),
enum: createOptionFromNullable(t.array(t.any)),
})
);
export const MediaTypeObjectIO = t.type({
schema: createOptionFromNullable(
t.union([SchemaObjectIO, ReferenceObjectIO])
),
examples: createOptionFromNullable(ExamplesObjectIO),
example: createOptionFromNullable(t.any),
// encoding: createOptionFromNullable(EncodingObjectIO), // I'm too lazy to implement mutual recursion right now
});
export type MediaTypeObject = t.TypeOf<typeof MediaTypeObjectIO>;
export const ContentObjectIO = t.record(t.string, MediaTypeObjectIO);
export type ContentObject = t.TypeOf<typeof ContentObjectIO>;
export const BaseParameterObjectIO = t.type({
description: createOptionFromNullable(t.string),
required: createOptionFromNullable(t.boolean),
deprecated: createOptionFromNullable(t.boolean),
allowEmptyValue: createOptionFromNullable(t.boolean),
style: createOptionFromNullable(ParameterStyleIO),
explode: createOptionFromNullable(t.boolean),
allowReserved: createOptionFromNullable(t.boolean),
schema: createOptionFromNullable(
t.union([SchemaObjectIO, ReferenceObjectIO])
),
examples: createOptionFromNullable(
t.record(t.string, t.union([ExampleObjectIO, ReferenceObjectIO]))
),
example: createOptionFromNullable(t.any),
content: createOptionFromNullable(ContentObjectIO),
});
export type BaseParameterObject = t.TypeOf<typeof BaseParameterObjectIO>;
export const HeaderObjectIO = BaseParameterObjectIO;
export type HeaderObject = t.TypeOf<typeof HeaderObjectIO>;
export const HeadersObjectIO = t.record(
t.string,
t.union([HeaderObjectIO, ReferenceObjectIO])
);
export type HeadersObject = t.TypeOf<typeof HeadersObjectIO>;
export const ResponseObjectIO = t.type({
description: t.string,
headers: createOptionFromNullable(HeadersObjectIO),
content: createOptionFromNullable(ContentObjectIO),
links: createOptionFromNullable(LinksObjectIO),
});
export type ResponseObject = t.TypeOf<typeof ResponseObjectIO>;
export const ResponsesObjectIO = t.union([
t.type({
default: createOptionFromNullable(
t.union([ResponseObjectIO, ReferenceObjectIO])
),
}),
t.record(t.string, t.union([ResponseObjectIO, ReferenceObjectIO])),
]);
export type ResponsesObject = t.TypeOf<typeof ResponsesObjectIO>;
export const EncodingPropertyObjectIO = t.type({
contentType: createOptionFromNullable(t.string),
headers: createOptionFromNullable(
t.record(t.string, t.union([HeaderObjectIO, ReferenceObjectIO]))
),
style: createOptionFromNullable(t.string),
explode: createOptionFromNullable(t.boolean),
allowReserved: createOptionFromNullable(t.boolean),
});
export type EncodingPropertyObject = t.TypeOf<typeof EncodingPropertyObjectIO>;
export const EncodingObjectIO = t.record(t.string, EncodingPropertyObjectIO);
export type EncodingObject = t.TypeOf<typeof EncodingObjectIO>;
export const RequestBodyObjectIO = t.type({
description: createOptionFromNullable(t.string),
content: ContentObjectIO,
required: createOptionFromNullable(t.boolean),
});
export type RequestBodyObject = t.TypeOf<typeof RequestBodyObjectIO>;
export const SchemasObjectIO = t.record(t.string, SchemaObjectIO);
export type SchemasObject = t.TypeOf<typeof SchemasObjectIO>;
export const ParameterObjectIO = t.union([
BaseParameterObjectIO,
t.type({
name: t.string,
in: ParameterLocationIO,
}),
]);
export type ParamaterObject = t.TypeOf<typeof ParameterObjectIO>;
export const DiscriminatorObjectIO = t.type({
propertyName: t.string,
mapping: createOptionFromNullable(t.record(t.string, t.string)),
});
export type DiscriminatorObject = t.TypeOf<typeof DiscriminatorObjectIO>;
export type DiscriminatorObjectO = CodecOf<typeof DiscriminatorObjectIO>[1];
export const XmlObjectIO = t.type({
name: createOptionFromNullable(t.string),
namespace: createOptionFromNullable(t.string),
prefix: createOptionFromNullable(t.string),
attribute: createOptionFromNullable(t.boolean),
wrapped: createOptionFromNullable(t.boolean),
});
export type XmlObject = t.TypeOf<typeof XmlObjectIO>;
export type XmlObjectO = CodecOf<typeof XmlObjectIO>[1];
export const SecuritySchemeTypeIO = t.union([
t.literal('apiKey'),
t.literal('http'),
t.literal('oauth2'),
t.literal('openIdConnect'),
]);
export type SecuritySchemeType = t.TypeOf<typeof SecuritySchemeTypeIO>;
export const ScopesObjectIO = t.record(t.string, t.string);
export type ScopesObject = t.TypeOf<typeof ScopesObjectIO>;
export const OAuthFlowObjectIO = t.type({
authorizationUrl: createOptionFromNullable(t.string),
tokenUrl: createOptionFromNullable(t.string),
refreshUrl: createOptionFromNullable(t.string),
scopes: ScopesObjectIO,
});
export type OAuthFlowObject = t.TypeOf<typeof OAuthFlowObjectIO>;
export const OAuthFlowsObjectIO = t.type({
implicit: createOptionFromNullable(OAuthFlowObjectIO),
password: createOptionFromNullable(OAuthFlowObjectIO),
clientCredentials: createOptionFromNullable(OAuthFlowObjectIO),
authorizationCode: createOptionFromNullable(OAuthFlowObjectIO),
});
export type OAuthFlowsObject = t.TypeOf<typeof OAuthFlowsObjectIO>;
export const SecuritySchemeObjectIO = t.type({
type: SecuritySchemeTypeIO,
description: createOptionFromNullable(t.string),
name: createOptionFromNullable(t.string),
in: createOptionFromNullable(t.string),
scheme: createOptionFromNullable(t.string),
bearerFormat: createOptionFromNullable(t.string),
flows: createOptionFromNullable(OAuthFlowsObjectIO),
openIdConnectUrl: createOptionFromNullable(t.string),
});
export type SecuritySchemeObject = t.TypeOf<typeof SecuritySchemeObjectIO>;
export const SecurityRequirementObjectIO = t.record(t.string, t.string);
export type SecurityRequirementObject = t.TypeOf<
typeof SecurityRequirementObjectIO
>;
export const OperationObjectIO = t.type({
tags: createOptionFromNullable(t.array(t.string)),
summary: createOptionFromNullable(t.string),
description: createOptionFromNullable(t.string),
externalDocs: createOptionFromNullable(ExternalDocumentationObjectIO),
operationId: createOptionFromNullable(t.string),
parameters: createOptionFromNullable(
t.array(t.union([ParameterObjectIO, ReferenceObjectIO]))
),
requestBody: createOptionFromNullable(
t.union([RequestBodyObjectIO, ReferenceObjectIO])
),
responses: ResponsesObjectIO,
// callbacks: createOptionFromNullable(CallbacksObjectIO), // I'm too lazy to implement mutual recursion right now
deprecated: createOptionFromNullable(t.boolean),
security: createOptionFromNullable(t.array(SecurityRequirementObjectIO)),
servers: createOptionFromNullable(t.array(ServerObjectIO)),
});
export type OperationObject = t.TypeOf<typeof OperationObjectIO>;
export const PathItemObjectIO = t.type({
$ref: createOptionFromNullable(t.string),
summary: createOptionFromNullable(t.string),
description: createOptionFromNullable(t.string),
get: createOptionFromNullable(OperationObjectIO),
put: createOptionFromNullable(OperationObjectIO),
post: createOptionFromNullable(OperationObjectIO),
delete: createOptionFromNullable(OperationObjectIO),
options: createOptionFromNullable(OperationObjectIO),
head: createOptionFromNullable(OperationObjectIO),
patch: createOptionFromNullable(OperationObjectIO),
trace: createOptionFromNullable(OperationObjectIO),
servers: createOptionFromNullable(t.array(ServerObjectIO)),
parameters: createOptionFromNullable(
t.array(t.union([ParameterObjectIO, ReferenceObjectIO]))
),
});
export type PathItemObject = t.TypeOf<typeof PathItemObjectIO>;
export const PathsObjectIO = t.record(t.string, PathItemObjectIO);
export type PathsObject = t.TypeOf<typeof PathsObjectIO>;
export const CallbackObjectIO = t.record(t.string, PathItemObjectIO);
export type CallbackObject = t.TypeOf<typeof CallbackObjectIO>;
export const CallbacksObjectIO = t.record(
t.string,
t.union([CallbackObjectIO, ReferenceObjectIO])
);
export type CallbacksObject = t.TypeOf<typeof CallbacksObjectIO>;
export const ComponentsObjectIO = t.type({
schemas: createOptionFromNullable(
t.record(t.string, t.union([SchemaObjectIO, ReferenceObjectIO]))
),
responses: createOptionFromNullable(
t.record(t.string, t.union([ResponseObjectIO, ReferenceObjectIO]))
),
parameters: createOptionFromNullable(
t.record(t.string, t.union([ParameterObjectIO, ReferenceObjectIO]))
),
examples: createOptionFromNullable(
t.record(t.string, t.union([ExampleObjectIO, ReferenceObjectIO]))
),
requestBodies: createOptionFromNullable(
t.record(t.string, t.union([RequestBodyObjectIO, ReferenceObjectIO]))
),
headers: createOptionFromNullable(
t.record(t.string, t.union([HeaderObjectIO, ReferenceObjectIO]))
),
securitySchemes: createOptionFromNullable(
t.record(t.string, t.union([SecuritySchemeObjectIO, ReferenceObjectIO]))
),
links: createOptionFromNullable(
t.record(t.string, t.union([LinkObjectIO, ReferenceObjectIO]))
),
callbacks: createOptionFromNullable(
t.record(t.string, t.union([CallbackObjectIO, ReferenceObjectIO]))
),
});
export type ComponentsObject = t.TypeOf<typeof ComponentsObjectIO>;
export const OpenAPIObjectIO = t.type({
openapi: Semver,
info: InfoObjectIO,
servers: createOptionFromNullable(t.array(ServerObjectIO)),
paths: PathsObjectIO,
components: createOptionFromNullable(ComponentsObjectIO),
security: createOptionFromNullable(t.array(SecurityRequirementObjectIO)),
tags: createOptionFromNullable(t.array(TagObjectIO)),
externalDocs: createOptionFromNullable(ExternalDocumentationObjectIO),
});
export type OpenAPIObject = t.TypeOf<typeof OpenAPIObjectIO>;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment