Skip to content

Commit

Permalink
feat(samples): add support for custom formats, encoders and media typ…
Browse files Browse the repository at this point in the history
…es (#8905)

This change is specific to JSON Schema 2020-12
and OpenAPI 3.1.0.

Refs #8577
  • Loading branch information
char0n authored Jun 9, 2023
1 parent 1925622 commit d72b72c
Show file tree
Hide file tree
Showing 55 changed files with 877 additions and 371 deletions.
8 changes: 7 additions & 1 deletion src/core/plugins/json-schema-2020-12/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ import {
createXMLExample,
memoizedSampleFromSchema,
memoizedCreateXMLExample,
} from "./samples-extensions/fn"
encoderAPI,
mediaTypeAPI,
formatAPI,
} from "./samples-extensions/fn/index"
import { JSONSchemaDeepExpansionContext } from "./context"
import { useFn, useConfig, useComponent, useIsExpandedDeeply } from "./hooks"
import { withJSONSchemaContext } from "./hoc"
Expand Down Expand Up @@ -113,6 +116,9 @@ const JSONSchema202012Plugin = () => ({
useIsExpandedDeeply,
sampleFromSchema,
sampleFromSchemaGeneric,
sampleEncoderAPI: encoderAPI,
sampleFormatAPI: formatAPI,
sampleMediaTypeAPI: mediaTypeAPI,
createXMLExample,
memoizedSampleFromSchema,
memoizedCreateXMLExample,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* @prettier
*/

import EncoderRegistry from "core/plugins/json-schema-2020-12/samples-extensions/fn/class/EncoderRegistry"

const registry = new EncoderRegistry()

const encoderAPI = (encodingName, encoder) => {
if (typeof encoder === "function") {
return registry.register(encodingName, encoder)
} else if (encoder === null) {
return registry.unregister(encodingName)
}

return registry.get(encodingName)
}
encoderAPI.getDefaults = () => registry.defaults

export default encoderAPI
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* @prettier
*/

import Registry from "../class/Registry"

const registry = new Registry()

const formatAPI = (format, generator) => {
if (typeof generator === "function") {
return registry.register(format, generator)
} else if (generator === null) {
return registry.unregister(format)
}

return registry.get(format)
}

export default formatAPI
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* @prettier
*/

import MediaTypeRegistry from "../class/MediaTypeRegistry"

const registry = new MediaTypeRegistry()

const mediaTypeAPI = (mediaType, generator) => {
if (typeof generator === "function") {
return registry.register(mediaType, generator)
} else if (generator === null) {
return registry.unregister(mediaType)
}

const mediaTypeNoParams = mediaType.split(";").at(0)
const topLevelMediaType = `${mediaTypeNoParams.split("/").at(0)}/*`

return (
registry.get(mediaType) ||
registry.get(mediaTypeNoParams) ||
registry.get(topLevelMediaType)
)
}
mediaTypeAPI.getDefaults = () => registry.defaults

export default mediaTypeAPI
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* @prettier
*/
import Registry from "./Registry"
import encode7bit from "../encoders/7bit"
import encode8bit from "../encoders/8bit"
import encodeBinary from "../encoders/binary"
import encodeQuotedPrintable from "../encoders/quoted-printable"
import encodeBase16 from "../encoders/base16"
import encodeBase32 from "../encoders/base32"
import encodeBase64 from "../encoders/base64"

class EncoderRegistry extends Registry {
#defaults = {
"7bit": encode7bit,
"8bit": encode8bit,
binary: encodeBinary,
"quoted-printable": encodeQuotedPrintable,
base16: encodeBase16,
base32: encodeBase32,
base64: encodeBase64,
}

data = { ...this.#defaults }

get defaults() {
return { ...this.#defaults }
}
}

export default EncoderRegistry
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* @prettier
*/
import Registry from "./Registry"
import textMediaTypesGenerators from "../generators/media-types/text"
import imageMediaTypesGenerators from "../generators/media-types/image"
import audioMediaTypesGenerators from "../generators/media-types/audio"
import videoMediaTypesGenerators from "../generators/media-types/video"
import applicationMediaTypesGenerators from "../generators/media-types/application"

class MediaTypeRegistry extends Registry {
#defaults = {
...textMediaTypesGenerators,
...imageMediaTypesGenerators,
...audioMediaTypesGenerators,
...videoMediaTypesGenerators,
...applicationMediaTypesGenerators,
}

data = { ...this.#defaults }

get defaults() {
return { ...this.#defaults }
}
}

export default MediaTypeRegistry
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* @prettier
*/
class Registry {
data = {}

register(name, value) {
this.data[name] = value
}

unregister(name) {
if (typeof name === "undefined") {
this.data = {}
} else {
delete this.data[name]
}
}

get(name) {
return this.data[name]
}
}

export default Registry
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* @prettier
*/
import randomBytes from "randombytes"
import RandExp from "randexp"
export const bytes = (length) => randomBytes(length)

export const randexp = (pattern) => {
try {
const randexpInstance = new RandExp(pattern)
return randexpInstance.gen()
} catch {
// invalid regex should not cause a crash (regex syntax varies across languages)
return "string"
}
}

export const string = () => "string"

export const number = () => 0

export const integer = () => 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* @prettier
*/
export const isURI = (uri) => {
try {
return new URL(uri) && true
} catch {
return false
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* @prettier
*/
const encode7bit = (content) => Buffer.from(content).toString("ascii")

export default encode7bit
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* @prettier
*/
const encode8bit = (content) => Buffer.from(content).toString("utf8")

export default encode8bit
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* @prettier
*/
const encodeBase16 = (content) => Buffer.from(content).toString("hex")

export default encodeBase16
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* @prettier
*/
const encodeBase32 = (content) => {
const utf8Value = Buffer.from(content).toString("utf8")
const base32Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
let paddingCount = 0
let base32Str = ""
let buffer = 0
let bufferLength = 0

for (let i = 0; i < utf8Value.length; i++) {
buffer = (buffer << 8) | utf8Value.charCodeAt(i)
bufferLength += 8

while (bufferLength >= 5) {
base32Str += base32Alphabet.charAt((buffer >>> (bufferLength - 5)) & 31)
bufferLength -= 5
}
}

if (bufferLength > 0) {
base32Str += base32Alphabet.charAt((buffer << (5 - bufferLength)) & 31)
paddingCount = (8 - ((utf8Value.length * 8) % 5)) % 5
}

for (let i = 0; i < paddingCount; i++) {
base32Str += "="
}

return base32Str
}

export default encodeBase32
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* @prettier
*/
const encodeBase64 = (content) => Buffer.from(content).toString("base64")

export default encodeBase64
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* @prettier
*/
const encodeBinary = (content) => Buffer.from(content).toString("binary")

export default encodeBinary
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* @prettier
*/
const encodeQuotedPrintable = (content) => {
let quotedPrintable = ""

for (let i = 0; i < content.length; i++) {
const charCode = content.charCodeAt(i)

if (charCode === 61) {
// ASCII content of "="
quotedPrintable += "=3D"
} else if (
(charCode >= 33 && charCode <= 60) ||
(charCode >= 62 && charCode <= 126) ||
charCode === 9 ||
charCode === 32
) {
quotedPrintable += content.charAt(i)
} else if (charCode === 13 || charCode === 10) {
quotedPrintable += "\r\n"
} else if (charCode > 126) {
// convert non-ASCII characters to UTF-8 and encode each byte
const utf8 = unescape(encodeURIComponent(content.charAt(i)))
for (let j = 0; j < utf8.length; j++) {
quotedPrintable +=
"=" + ("0" + utf8.charCodeAt(j).toString(16)).slice(-2).toUpperCase()
}
} else {
quotedPrintable +=
"=" + ("0" + charCode.toString(16)).slice(-2).toUpperCase()
}
}

return quotedPrintable
}

export default encodeQuotedPrintable
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* @prettier
*/
const dateTimeGenerator = () => new Date().toISOString()

export default dateTimeGenerator
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* @prettier
*/
const dateGenerator = () => new Date().toISOString().substring(0, 10)

export default dateGenerator
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* @prettier
*/
const doubleGenerator = () => 0.1

export default doubleGenerator
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* @prettier
*/
const durationGenerator = () => "P3D" // expresses a duration of 3 days

export default durationGenerator
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* @prettier
*/
const emailGenerator = () => "[email protected]"

export default emailGenerator
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* @prettier
*/
const floatGenerator = () => 0.1

export default floatGenerator
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* @prettier
*/
const hostnameGenerator = () => "example.com"

export default hostnameGenerator
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* @prettier
*/
const idnEmailGenerator = () => "실례@example.com"

export default idnEmailGenerator
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* @prettier
*/
const idnHostnameGenerator = () => "실례.com"

export default idnHostnameGenerator
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* @prettier
*/
const int32Generator = () => (2 ** 30) >>> 0

export default int32Generator
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* @prettier
*/
const int64Generator = () => 2 ** 53 - 1

export default int64Generator
Loading

0 comments on commit d72b72c

Please sign in to comment.