diff --git a/src/core/plugins/json-schema-2020-12/fn/index.js b/src/core/plugins/json-schema-2020-12/fn.js similarity index 99% rename from src/core/plugins/json-schema-2020-12/fn/index.js rename to src/core/plugins/json-schema-2020-12/fn.js index 41f31912..10aa6472 100644 --- a/src/core/plugins/json-schema-2020-12/fn/index.js +++ b/src/core/plugins/json-schema-2020-12/fn.js @@ -1,7 +1,7 @@ /** * @prettier */ -import { useFn } from "../hooks" +import { useFn } from "./hooks" export const upperFirst = (value) => { if (typeof value === "string") { diff --git a/src/core/plugins/json-schema-2020-12/hoc.jsx b/src/core/plugins/json-schema-2020-12/hoc.jsx index 1003fcef..32825070 100644 --- a/src/core/plugins/json-schema-2020-12/hoc.jsx +++ b/src/core/plugins/json-schema-2020-12/hoc.jsx @@ -56,7 +56,7 @@ import { stringify, stringifyConstraints, getDependentRequired, -} from "./fn/index" +} from "./fn" export const withJSONSchemaContext = (Component, overrides = {}) => { const value = { diff --git a/src/core/plugins/json-schema-2020-12/index.js b/src/core/plugins/json-schema-2020-12/index.js index 07d88872..aefd146c 100644 --- a/src/core/plugins/json-schema-2020-12/index.js +++ b/src/core/plugins/json-schema-2020-12/index.js @@ -43,15 +43,14 @@ import KeywordWriteOnly from "./components/keywords/WriteOnly" import Accordion from "./components/Accordion/Accordion" import ExpandDeepButton from "./components/ExpandDeepButton/ExpandDeepButton" import ChevronRightIcon from "./components/icons/ChevronRight" -import { upperFirst, hasKeyword, isExpandable } from "./fn/index" +import { upperFirst, hasKeyword, isExpandable } from "./fn" import { - inferSchema, sampleFromSchema, sampleFromSchemaGeneric, createXMLExample, memoizedSampleFromSchema, memoizedCreateXMLExample, -} from "./fn/samples" +} from "./samples-extensions/fn" import { JSONSchemaDeepExpansionContext } from "./context" import { useFn, useConfig, useComponent, useIsExpandedDeeply } from "./hooks" import { withJSONSchemaContext } from "./hoc" @@ -113,7 +112,6 @@ const JSONSchema202012Plugin = () => ({ useConfig, useComponent, useIsExpandedDeeply, - inferSchema, sampleFromSchema, sampleFromSchemaGeneric, createXMLExample, diff --git a/src/core/plugins/json-schema-2020-12/fn/samples.js b/src/core/plugins/json-schema-2020-12/samples-extensions/fn.js similarity index 98% rename from src/core/plugins/json-schema-2020-12/fn/samples.js rename to src/core/plugins/json-schema-2020-12/samples-extensions/fn.js index 559bdd2f..1902dccf 100644 --- a/src/core/plugins/json-schema-2020-12/fn/samples.js +++ b/src/core/plugins/json-schema-2020-12/samples-extensions/fn.js @@ -709,16 +709,6 @@ export const sampleFromSchemaGeneric = ( return value } -export const inferSchema = (thing) => { - if (thing.schema) thing = thing.schema - - if (thing.properties) { - thing.type = "object" - } - - return thing // Hopefully this will have something schema like in it... `type` for example -} - export const createXMLExample = (schema, config, o) => { const json = sampleFromSchemaGeneric(schema, config, o, true) if (!json) { diff --git a/src/core/plugins/oas31/fn.js b/src/core/plugins/oas31/fn.js index 2bf85e89..dc7b0b29 100644 --- a/src/core/plugins/oas31/fn.js +++ b/src/core/plugins/oas31/fn.js @@ -98,7 +98,7 @@ export const createOnlyOAS31ComponentWrapper = /* eslint-enable react/jsx-filename-extension */ /** Utilize JSON Schema 2020-12 samples **/ -export const wrapSampleFn = +const wrapSampleFn = (fnName) => (getSystem) => (...args) => { @@ -111,7 +111,6 @@ export const wrapSampleFn = return fn[fnName](...args) } -export const wrapInferSchema = wrapSampleFn("inferSchema") export const wrapSampleFromSchema = wrapSampleFn("sampleFromSchema") export const wrapSampleFromSchemaGeneric = wrapSampleFn( "sampleFromSchemaGeneric" diff --git a/src/core/plugins/oas31/index.js b/src/core/plugins/oas31/index.js index 4628c0dc..6fb9db15 100644 --- a/src/core/plugins/oas31/index.js +++ b/src/core/plugins/oas31/index.js @@ -20,7 +20,6 @@ import { isOAS31 as isOAS31Fn, createOnlyOAS31Selector as createOnlyOAS31SelectorFn, createSystemSelector as createSystemSelectorFn, - wrapInferSchema, wrapSampleFromSchema, wrapSampleFromSchemaGeneric, wrapCreateXMLExample, @@ -90,7 +89,6 @@ const OAS31Plugin = ({ getSystem }) => { } // wraps schema generators and make them specific to OpenAPI version if (typeof system.fn.inferSchema === "function") { - pluginFn.inferSchema = wrapInferSchema(getSystem) pluginFn.sampleFromSchema = wrapSampleFromSchema(getSystem) pluginFn.sampleFromSchemaGeneric = wrapSampleFromSchemaGeneric(getSystem) pluginFn.createXMLExample = wrapCreateXMLExample(getSystem) diff --git a/src/core/plugins/samples/fn/get-json-sample-schema.js b/src/core/plugins/samples/fn/get-json-sample-schema.js new file mode 100644 index 00000000..0fd18e84 --- /dev/null +++ b/src/core/plugins/samples/fn/get-json-sample-schema.js @@ -0,0 +1,32 @@ +/** + * @prettier + */ +import some from "lodash/some" + +const shouldStringifyTypesConfig = [ + { + when: /json/, + shouldStringifyTypes: ["string"], + }, +] +const defaultStringifyTypes = ["object"] +const makeGetJsonSampleSchema = + (getSystem) => (schema, config, contentType, exampleOverride) => { + const { fn } = getSystem() + const res = fn.memoizedSampleFromSchema(schema, config, exampleOverride) + const resType = typeof res + + const typesToStringify = shouldStringifyTypesConfig.reduce( + (types, nextConfig) => + nextConfig.when.test(contentType) + ? [...types, ...nextConfig.shouldStringifyTypes] + : types, + defaultStringifyTypes + ) + + return some(typesToStringify, (x) => x === resType) + ? JSON.stringify(res, null, 2) + : res + } + +export default makeGetJsonSampleSchema diff --git a/src/core/plugins/samples/fn/get-sample-schema.js b/src/core/plugins/samples/fn/get-sample-schema.js new file mode 100644 index 00000000..adec115f --- /dev/null +++ b/src/core/plugins/samples/fn/get-sample-schema.js @@ -0,0 +1,30 @@ +/** + * @prettier + */ +const makeGetSampleSchema = + (getSystem) => + (schema, contentType = "", config = {}, exampleOverride = undefined) => { + const { fn } = getSystem() + + if (typeof schema?.toJS === "function") { + schema = schema.toJS() + } + if (typeof exampleOverride?.toJS === "function") { + exampleOverride = exampleOverride.toJS() + } + + if (/xml/.test(contentType)) { + return fn.getXmlSampleSchema(schema, config, exampleOverride) + } + if (/(yaml|yml)/.test(contentType)) { + return fn.getYamlSampleSchema( + schema, + config, + contentType, + exampleOverride + ) + } + return fn.getJsonSampleSchema(schema, config, contentType, exampleOverride) + } + +export default makeGetSampleSchema diff --git a/src/core/plugins/samples/fn/get-xml-sample-schema.js b/src/core/plugins/samples/fn/get-xml-sample-schema.js new file mode 100644 index 00000000..5c148375 --- /dev/null +++ b/src/core/plugins/samples/fn/get-xml-sample-schema.js @@ -0,0 +1,31 @@ +/** + * @prettier + */ +const makeGetXmlSampleSchema = + (getSystem) => (schema, config, exampleOverride) => { + const { fn } = getSystem() + + if (schema && !schema.xml) { + schema.xml = {} + } + if (schema && !schema.xml.name) { + if ( + !schema.$$ref && + (schema.type || + schema.items || + schema.properties || + schema.additionalProperties) + ) { + // eslint-disable-next-line quotes + return '\n' + } + if (schema.$$ref) { + let match = schema.$$ref.match(/\S*\/(\S+)$/) + schema.xml.name = match[1] + } + } + + return fn.memoizedCreateXMLExample(schema, config, exampleOverride) + } + +export default makeGetXmlSampleSchema diff --git a/src/core/plugins/samples/fn/get-yaml-sample-schema.js b/src/core/plugins/samples/fn/get-yaml-sample-schema.js new file mode 100644 index 00000000..abef8de9 --- /dev/null +++ b/src/core/plugins/samples/fn/get-yaml-sample-schema.js @@ -0,0 +1,34 @@ +/** + * @prettier + */ +import YAML, { JSON_SCHEMA } from "js-yaml" + +const makeGetYamlSampleSchema = + (getSystem) => (schema, config, contentType, exampleOverride) => { + const { fn } = getSystem() + const jsonExample = fn.getJsonSampleSchema( + schema, + config, + contentType, + exampleOverride + ) + let yamlString + try { + yamlString = YAML.dump( + YAML.load(jsonExample), + { + lineWidth: -1, // don't generate line folds + }, + { schema: JSON_SCHEMA } + ) + if (yamlString[yamlString.length - 1] === "\n") { + yamlString = yamlString.slice(0, yamlString.length - 1) + } + } catch (e) { + console.error(e) + return "error: could not generate yaml example" + } + return yamlString.replace(/\t/g, " ") + } + +export default makeGetYamlSampleSchema diff --git a/src/core/plugins/samples/fn.js b/src/core/plugins/samples/fn/index.js similarity index 99% rename from src/core/plugins/samples/fn.js rename to src/core/plugins/samples/fn/index.js index a140dc4e..340867bc 100644 --- a/src/core/plugins/samples/fn.js +++ b/src/core/plugins/samples/fn/index.js @@ -3,7 +3,7 @@ import RandExp from "randexp" import isEmpty from "lodash/isEmpty" import { objectify, isFunc, normalizeArray, deeplyStripKey } from "core/utils" -import memoizeN from "../../../helpers/memoizeN" +import memoizeN from "../../../../helpers/memoizeN" const generateStringFromRegex = (pattern) => { try { diff --git a/src/core/plugins/samples/index.js b/src/core/plugins/samples/index.js index 96c12234..cb56c2db 100644 --- a/src/core/plugins/samples/index.js +++ b/src/core/plugins/samples/index.js @@ -1,5 +1,32 @@ -import * as fn from "./fn" +/** + * @prettier + */ +import { + sampleFromSchema, + inferSchema, + sampleFromSchemaGeneric, + createXMLExample, + memoizedCreateXMLExample, + memoizedSampleFromSchema, +} from "./fn/index" +import makeGetJsonSampleSchema from "./fn/get-json-sample-schema" +import makeGetYamlSampleSchema from "./fn/get-yaml-sample-schema" +import makeGetXmlSampleSchema from "./fn/get-xml-sample-schema" +import makeGetSampleSchema from "./fn/get-sample-schema" -export default function () { - return { fn } -} +const SamplesPlugin = ({ getSystem }) => ({ + fn: { + inferSchema, + sampleFromSchema, + sampleFromSchemaGeneric, + createXMLExample, + memoizedSampleFromSchema, + memoizedCreateXMLExample, + getJsonSampleSchema: makeGetJsonSampleSchema(getSystem), + getYamlSampleSchema: makeGetYamlSampleSchema(getSystem), + getXmlSampleSchema: makeGetXmlSampleSchema(getSystem), + getSampleSchema: makeGetSampleSchema(getSystem), + }, +}) + +export default SamplesPlugin diff --git a/test/unit/core/plugins/samples/fn/get-sample-schema.js b/test/unit/core/plugins/samples/fn/get-sample-schema.js new file mode 100644 index 00000000..581dba0d --- /dev/null +++ b/test/unit/core/plugins/samples/fn/get-sample-schema.js @@ -0,0 +1,195 @@ +/** + * @prettier + */ +import { + memoizedSampleFromSchema, + memoizedCreateXMLExample, +} from "core/plugins/samples/fn/index" +import makeGetSampleSchema from "core/plugins/samples/fn/get-sample-schema" +import makeGetJsonSampleSchema from "core/plugins/samples/fn/get-json-sample-schema" +import makeGetYamlSampleSchema from "core/plugins/samples/fn/get-yaml-sample-schema" +import makeGetXmlSampleSchema from "core/plugins/samples/fn/get-xml-sample-schema" + +describe("getSampleSchema", () => { + const oriDate = Date + const getSystem = () => ({ + fn: { + memoizedSampleFromSchema, + memoizedCreateXMLExample, + getJsonSampleSchema: makeGetJsonSampleSchema(getSystem), + getYamlSampleSchema: makeGetYamlSampleSchema(getSystem), + getXmlSampleSchema: makeGetXmlSampleSchema(getSystem), + }, + }) + const getSampleSchema = makeGetSampleSchema(getSystem) + + beforeEach(() => { + Date = function () { + this.toISOString = function () { + return "2018-07-07T07:07:05.189Z" + } + } + }) + + afterEach(() => { + Date = oriDate + }) + + it("should stringify string values if json content-type", () => { + // Given + const res = getSampleSchema( + { + type: "string", + format: "date-time", + }, + "text/json" + ) + + // Then + expect(res).toEqual(JSON.stringify(new Date().toISOString())) + }) + + it("should not unnecessarily stringify string values for other content-types", () => { + // Given + const res = getSampleSchema({ + type: "string", + format: "date-time", + }) + + // Then + expect(res).toEqual(new Date().toISOString()) + }) + + it("should not unnecessarily stringify non-object values", () => { + // Given + const res = getSampleSchema({ + type: "number", + }) + + // Then + expect(res).toEqual(0) + }) + + it("should not unnecessarily stringify non-object values if content-type is json", () => { + // Given + const res = getSampleSchema( + { + type: "number", + }, + "application/json" + ) + + // Then + expect(res).toEqual(0) + }) + + it("should stringify object when literal string example is provided if json content-type", () => { + // Given + const expected = "" + const res = getSampleSchema( + { + type: "object", + }, + "text/json", + {}, + expected + ) + + // Then + expect(res).toEqual(JSON.stringify(expected)) + }) + + it("should parse valid json literal example if json content-type", () => { + // Given + const expected = { test: 123 } + const res = getSampleSchema( + { + type: "object", + }, + "text/json", + {}, + JSON.stringify(expected) + ) + + // Then + const actual = JSON.parse(res) + expect(actual.test).toEqual(123) + }) + + it("should handle number example with string schema as string", () => { + // Given + const expected = 123 + const res = getSampleSchema( + { + type: "string", + }, + "text/json", + {}, + expected + ) + + // Then + const actual = JSON.parse(res) + expect(actual).toEqual("123") + }) + + it("should handle number literal example with string schema as string", () => { + // Given + const expected = "123" + const res = getSampleSchema( + { + type: "string", + }, + "text/json", + {}, + expected + ) + + // Then + const actual = JSON.parse(res) + expect(actual).toEqual("123") + }) + + it("should handle number literal example with number schema as number", () => { + // Given + const expected = "123" + const res = getSampleSchema( + { + type: "number", + }, + "text/json", + {}, + expected + ) + + // Then + const actual = JSON.parse(res) + expect(actual).toEqual(123) + }) + + it("should return yaml example if yaml is contained in the content-type", () => { + const res = getSampleSchema( + { + type: "object", + }, + "text/yaml", + {}, + { test: 123 } + ) + + expect(res).toEqual("test: 123") + }) + + it("should return yaml example if yml is contained in the content-type", () => { + const res = getSampleSchema( + { + type: "object", + }, + "text/yml", + {}, + { test: 123 } + ) + + expect(res).toEqual("test: 123") + }) +}) diff --git a/test/unit/core/plugins/samples/fn.js b/test/unit/core/plugins/samples/fn/index.js similarity index 99% rename from test/unit/core/plugins/samples/fn.js rename to test/unit/core/plugins/samples/fn/index.js index b87d59c2..11815f95 100644 --- a/test/unit/core/plugins/samples/fn.js +++ b/test/unit/core/plugins/samples/fn/index.js @@ -1,5 +1,5 @@ import { fromJS } from "immutable" -import { createXMLExample, sampleFromSchema, memoizedCreateXMLExample, memoizedSampleFromSchema } from "corePlugins/samples/fn" +import { createXMLExample, sampleFromSchema, memoizedCreateXMLExample, memoizedSampleFromSchema } from "corePlugins/samples/fn/index" describe("sampleFromSchema", () => { it("handles Immutable.js objects for nested schemas", function () { diff --git a/test/unit/core/utils.js b/test/unit/core/utils.js index 42ab9b55..2d6e1ae9 100644 --- a/test/unit/core/utils.js +++ b/test/unit/core/utils.js @@ -24,7 +24,6 @@ import { requiresValidationURL, extractFileNameFromContentDispositionHeader, deeplyStripKey, - getSampleSchema, paramToIdentifier, paramToValue, generateCodeVerifier, @@ -1615,139 +1614,6 @@ describe("utils", () => { }) - describe("getSampleSchema", () => { - const oriDate = Date - - beforeEach(() => { - Date = function () { - this.toISOString = function () { - return "2018-07-07T07:07:05.189Z" - } - } - }) - - afterEach(() => { - Date = oriDate - }) - - it("should stringify string values if json content-type", () => { - // Given - const res = getSampleSchema({ - type: "string", - format: "date-time" - }, "text/json") - - // Then - expect(res).toEqual(JSON.stringify(new Date().toISOString())) - }) - - it("should not unnecessarily stringify string values for other content-types", () => { - // Given - const res = getSampleSchema({ - type: "string", - format: "date-time" - }) - - // Then - expect(res).toEqual(new Date().toISOString()) - }) - - it("should not unnecessarily stringify non-object values", () => { - // Given - const res = getSampleSchema({ - type: "number" - }) - - // Then - expect(res).toEqual(0) - }) - - it("should not unnecessarily stringify non-object values if content-type is json", () => { - // Given - const res = getSampleSchema({ - type: "number" - }, "application/json") - - // Then - expect(res).toEqual(0) - }) - - it("should stringify object when literal string example is provided if json content-type", () => { - // Given - const expected = "" - const res = getSampleSchema({ - type: "object", - }, "text/json", {}, expected) - - // Then - expect(res).toEqual(JSON.stringify(expected)) - }) - - it("should parse valid json literal example if json content-type", () => { - // Given - const expected = {test: 123} - const res = getSampleSchema({ - type: "object", - }, "text/json", {}, JSON.stringify(expected)) - - // Then - const actual = JSON.parse(res) - expect(actual.test).toEqual(123) - }) - - it("should handle number example with string schema as string", () => { - // Given - const expected = 123 - const res = getSampleSchema({ - type: "string", - }, "text/json", {}, expected) - - // Then - const actual = JSON.parse(res) - expect(actual).toEqual("123") - }) - - it("should handle number literal example with string schema as string", () => { - // Given - const expected = "123" - const res = getSampleSchema({ - type: "string", - }, "text/json", {}, expected) - - // Then - const actual = JSON.parse(res) - expect(actual).toEqual("123") - }) - - it("should handle number literal example with number schema as number", () => { - // Given - const expected = "123" - const res = getSampleSchema({ - type: "number", - }, "text/json", {}, expected) - - // Then - const actual = JSON.parse(res) - expect(actual).toEqual(123) - }) - - it("should return yaml example if yaml is contained in the content-type", () => { - const res = getSampleSchema({ - type: "object", - }, "text/yaml", {}, {test: 123}) - - expect(res).toEqual("test: 123") - }) - - it("should return yaml example if yml is contained in the content-type", () => { - const res = getSampleSchema({ - type: "object", - }, "text/yml", {}, {test: 123}) - - expect(res).toEqual("test: 123") - }) - }) - describe("paramToIdentifier", () => { it("should convert an Immutable parameter map to an identifier", () => { const param = fromJS({