@scalar/oas-utils
Version:
Open API spec and Yaml handling utilities
231 lines (230 loc) • 9.74 kB
JavaScript
const MAX_LEVELS_DEEP = 5;
const MAX_PROPERTIES = 10;
const DEFAULT_ADDITIONAL_PROPERTIES_NAME = "propertyName*";
const genericExampleValues = {
// 'date-time': '1970-01-01T00:00:00Z',
"date-time": (/* @__PURE__ */ new Date()).toISOString(),
// 'date': '1970-01-01',
"date": (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
"email": "hello@example.com",
"hostname": "example.com",
// https://tools.ietf.org/html/rfc6531#section-3.3
"idn-email": "jane.doe@example.com",
// https://tools.ietf.org/html/rfc5890#section-2.3.2.3
"idn-hostname": "example.com",
"ipv4": "127.0.0.1",
"ipv6": "51d4:7fab:bfbf:b7d7:b2cb:d4b4:3dad:d998",
"iri-reference": "/entitiy/1",
// https://tools.ietf.org/html/rfc3987
"iri": "https://example.com/entity/123",
"json-pointer": "/nested/objects",
"password": "super-secret",
"regex": "/[a-z]/",
// https://tools.ietf.org/html/draft-handrews-relative-json-pointer-01
"relative-json-pointer": "1/nested/objects",
// full-time in https://tools.ietf.org/html/rfc3339#section-5.6
// 'time': '00:00:00Z',
"time": (/* @__PURE__ */ new Date()).toISOString().split("T")[1].split(".")[0],
// either a URI or relative-reference https://tools.ietf.org/html/rfc3986#section-4.1
"uri-reference": "../folder",
"uri-template": "https://example.com/{id}",
"uri": "https://example.com",
"uuid": "123e4567-e89b-12d3-a456-426614174000",
"object-id": "6592008029c8c3e4dc76256c"
};
function guessFromFormat(schema, makeUpRandomData = false, fallback = "") {
if (schema.format === "binary") {
return new File([""], "filename");
}
return makeUpRandomData ? genericExampleValues[schema.format] ?? fallback : "";
}
const resultCache = /* @__PURE__ */ new WeakMap();
function cache(schema, result) {
if (typeof result !== "object" || result === null) {
return result;
}
resultCache.set(schema, result);
return result;
}
const getExampleFromSchema = (schema, options, level = 0, parentSchema, name) => {
if (resultCache.has(schema)) {
return resultCache.get(schema);
}
if (level === MAX_LEVELS_DEEP + 1) {
try {
JSON.stringify(schema);
} catch {
return "[Circular Reference]";
}
}
const makeUpRandomData = !!options?.emptyString;
if (schema.deprecated) {
return void 0;
}
if (options?.mode === "write" && schema.readOnly || options?.mode === "read" && schema.writeOnly) {
return void 0;
}
if (schema["x-variable"]) {
const value = options?.variables?.[schema["x-variable"]];
if (value !== void 0) {
if (schema.type === "number" || schema.type === "integer") {
return Number.parseInt(value, 10);
}
return cache(schema, value);
}
}
if (Array.isArray(schema.examples) && schema.examples.length > 0) {
return cache(schema, schema.examples[0]);
}
if (schema.example !== void 0) {
return cache(schema, schema.example);
}
if (schema.default !== void 0) {
return cache(schema, schema.default);
}
if (Array.isArray(schema.enum) && schema.enum.length > 0) {
return cache(schema, schema.enum[0]);
}
const isObjectOrArray = schema.type === "object" || schema.type === "array" || !!schema.allOf?.at?.(0) || !!schema.anyOf?.at?.(0) || !!schema.oneOf?.at?.(0);
if (!isObjectOrArray && options?.omitEmptyAndOptionalProperties === true) {
const isRequired = schema.required === true || parentSchema?.required === true || parentSchema?.required?.includes(name ?? schema.name);
if (!isRequired) {
return void 0;
}
}
if (schema.type === "object" || schema.properties !== void 0) {
const response = {};
let propertyCount = 0;
if (schema.properties !== void 0) {
for (const propertyName in schema.properties) {
if (Object.prototype.hasOwnProperty.call(schema.properties, propertyName)) {
if (level > 3 && propertyCount >= MAX_PROPERTIES) {
response["..."] = "[Additional Properties Truncated]";
break;
}
const property = schema.properties[propertyName];
const propertyXmlTagName = options?.xml ? property.xml?.name : void 0;
const value = getExampleFromSchema(property, options, level + 1, schema, propertyName);
if (typeof value !== "undefined") {
response[propertyXmlTagName ?? propertyName] = value;
propertyCount++;
}
}
}
}
if (schema.patternProperties !== void 0) {
for (const pattern in schema.patternProperties) {
if (Object.prototype.hasOwnProperty.call(schema.patternProperties, pattern)) {
const property = schema.patternProperties[pattern];
const exampleKey = pattern;
response[exampleKey] = getExampleFromSchema(property, options, level + 1, schema, exampleKey);
}
}
}
if (schema.additionalProperties !== void 0) {
const anyTypeIsValid = (
// true
schema.additionalProperties === true || // or an empty object {}
typeof schema.additionalProperties === "object" && !Object.keys(schema.additionalProperties).length
);
const additionalPropertiesName = typeof schema.additionalProperties === "object" && schema.additionalProperties["x-additionalPropertiesName"] && typeof schema.additionalProperties["x-additionalPropertiesName"] === "string" && schema.additionalProperties["x-additionalPropertiesName"].trim().length > 0 ? `${schema.additionalProperties["x-additionalPropertiesName"].trim()}*` : DEFAULT_ADDITIONAL_PROPERTIES_NAME;
if (anyTypeIsValid) {
response[additionalPropertiesName] = "anything";
} else if (schema.additionalProperties !== false) {
response[additionalPropertiesName] = getExampleFromSchema(schema.additionalProperties, options, level + 1);
}
}
if (schema.anyOf !== void 0) {
Object.assign(response, getExampleFromSchema(schema.anyOf[0], options, level + 1));
} else if (schema.oneOf !== void 0) {
Object.assign(response, getExampleFromSchema(schema.oneOf[0], options, level + 1));
} else if (schema.allOf !== void 0) {
Object.assign(
response,
...schema.allOf.map((item) => getExampleFromSchema(item, options, level + 1, schema)).filter((item) => item !== void 0)
);
}
return cache(schema, response);
}
if (schema.type === "array" || schema.items !== void 0) {
const itemsXmlTagName = schema?.items?.xml?.name;
const wrapItems = !!(options?.xml && schema.xml?.wrapped && itemsXmlTagName);
if (schema.example !== void 0) {
return cache(schema, wrapItems ? { [itemsXmlTagName]: schema.example } : schema.example);
}
if (schema.items) {
if (schema.items.allOf) {
if (schema.items.allOf[0].type === "object") {
const mergedExample = getExampleFromSchema(
{ type: "object", allOf: schema.items.allOf },
options,
level + 1,
schema
);
return cache(schema, wrapItems ? [{ [itemsXmlTagName]: mergedExample }] : [mergedExample]);
}
const examples = schema.items.allOf.map((item) => getExampleFromSchema(item, options, level + 1, schema)).filter((item) => item !== void 0);
return cache(schema, wrapItems ? examples.map((example) => ({ [itemsXmlTagName]: example })) : examples);
}
const rules = ["anyOf", "oneOf"];
for (const rule of rules) {
if (!schema.items[rule]) {
continue;
}
const schemas = schema.items[rule].slice(0, 1);
const exampleFromRule = schemas.map((item) => getExampleFromSchema(item, options, level + 1, schema)).filter((item) => item !== void 0);
return cache(schema, wrapItems ? [{ [itemsXmlTagName]: exampleFromRule }] : exampleFromRule);
}
}
const isObject = schema.items?.type === "object" || schema.items?.properties !== void 0;
const isArray = schema.items?.type === "array" || schema.items?.items !== void 0;
if (schema.items?.type || isObject || isArray) {
const exampleFromSchema = getExampleFromSchema(schema.items, options, level + 1);
return wrapItems ? [{ [itemsXmlTagName]: exampleFromSchema }] : [exampleFromSchema];
}
return [];
}
const exampleValues = {
string: guessFromFormat(schema, makeUpRandomData, options?.emptyString),
boolean: true,
integer: schema.min ?? 1,
number: schema.min ?? 1,
array: []
};
if (schema.type !== void 0 && exampleValues[schema.type] !== void 0) {
return cache(schema, exampleValues[schema.type]);
}
const discriminateSchema = schema.oneOf || schema.anyOf;
if (Array.isArray(discriminateSchema) && discriminateSchema.length > 0) {
const firstNonNullItem = discriminateSchema.find((item) => item.type !== "null");
if (firstNonNullItem) {
return getExampleFromSchema(firstNonNullItem, options, level + 1);
}
return null;
}
if (Array.isArray(schema.allOf)) {
let example = null;
schema.allOf.forEach((allOfItem) => {
const newExample = getExampleFromSchema(allOfItem, options, level + 1);
example = typeof newExample === "object" && typeof example === "object" ? {
...example ?? {},
...newExample
} : Array.isArray(newExample) && Array.isArray(example) ? [...example ?? {}, ...newExample] : newExample;
});
return cache(schema, example);
}
if (Array.isArray(schema.type)) {
if (schema.type.includes("null")) {
return null;
}
const exampleValue = exampleValues[schema.type[0]];
if (exampleValue !== void 0) {
return cache(schema, exampleValue);
}
}
return null;
};
export {
getExampleFromSchema
};
//# sourceMappingURL=get-example-from-schema.js.map