diff --git a/package-lock.json b/package-lock.json index ddfad19..532d844 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.1", "license": "BSD-3-Clause", "dependencies": { - "@dylibso/xtp-bindgen": "1.0.0-rc.8", + "@dylibso/xtp-bindgen": "1.0.0-rc.13", "ejs": "^3.1.10" }, "devDependencies": { @@ -21,9 +21,9 @@ } }, "node_modules/@dylibso/xtp-bindgen": { - "version": "1.0.0-rc.8", - "resolved": "https://registry.npmjs.org/@dylibso/xtp-bindgen/-/xtp-bindgen-1.0.0-rc.8.tgz", - "integrity": "sha512-9PVXiNa9xL+LNdn0wTY7cUzYiB44RIO/HNIXBeZZSyaA2Tc0rJl97TPcNPpJvQhNUwQVfjWDxCRXYULpUKf/nw==" + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@dylibso/xtp-bindgen/-/xtp-bindgen-1.0.0-rc.13.tgz", + "integrity": "sha512-aCmYSgC3Xwdlhm8mBKVpkzuHAtQ84ZgIwkdYQ3QFmDRxnBlZUtOZgpRoJTtOnahm0lUCiBkWkLcsikUluMY/qw==" }, "node_modules/@esbuild/aix-ppc64": { "version": "0.19.12", diff --git a/package.json b/package.json index 455db46..1a328ad 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "typescript": "^5.3.2" }, "dependencies": { - "@dylibso/xtp-bindgen": "1.0.0-rc.8", + "@dylibso/xtp-bindgen": "1.0.0-rc.13", "ejs": "^3.1.10" } } diff --git a/src/index.ts b/src/index.ts index 177fab2..cddc6f4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,73 +1,89 @@ -import ejs from "ejs"; -import { getContext, helpers, Property } from "@dylibso/xtp-bindgen"; +import ejs from 'ejs'; +import { ArrayType, EnumType, getContext, helpers, MapType, ObjectType, Property, XtpNormalizedType, XtpTyped } from '@dylibso/xtp-bindgen'; -function toRustType(property: Property): string { - if (property.$ref) return `types::${helpers.capitalize(property.$ref.name)}`; - switch (property.type) { - case "string": - if (property.format === "date-time") { - return "chrono::DateTime"; - } - return "String"; - case "number": - if (property.format === "float") { - return "f32"; - } - if (property.format === "double") { - return "f64"; - } - return "i64"; - case "integer": - return "i32"; - case "boolean": - return "bool"; - case "object": - return "std::collections::HashMap"; - case "array": - if (!property.items) return "Vec"; - return `Vec<${toRustType(property.items as Property)}>`; - case "buffer": - return "Vec"; - default: - throw new Error("Can't convert property to Rust type: " + property.type); +// keywords via https://doc.rust-lang.org/reference/keywords.html +let KEYWORDS: Set = new Set(['as', 'break', 'const', 'continue', 'crate', 'else', 'enum', 'extern', 'false', 'fn', 'for', 'if', 'impl', 'in', 'let', 'loop', 'match', 'mod', 'move', 'mut', 'pub', 'ref', 'return', 'self', 'Self', 'static', 'struct', 'super', 'trait', 'true', 'type', 'unsafe', 'use', 'where', 'while', 'async', 'await', 'dyn', 'abstract', 'become', 'box', 'do', 'final', 'macro', 'override', 'priv', 'typeof', 'unsized', 'virtual', 'yield', 'try', 'macro_rules', 'union', 'dyn']) + +function formatExternIdentifier(ident: string) { + if (KEYWORDS.has(ident)) { + return `r#${ident}` } + return ident } -function jsonWrappedRustType(property: Property): string { - if (property.$ref) return `Json`; - switch (property.type) { - case "string": - if (property.format === "date-time") { - return "Json>"; - } - return "String"; - case "number": - if (property.format === "float") { - return "Json"; - } - if (property.format === "double") { - return "Json"; +function formatIdentifier(ident: string): string { + if (KEYWORDS.has(ident)) { + return `r#${ident}` + } + + return helpers.camelToSnakeCase(ident) +} + +function toRustTypeX(type: XtpNormalizedType): string { + // turn into reference pointer if needed + const optionalize = (t: string) => { + return type.nullable ? `Option<${t}>` : t + } + + switch (type.kind) { + case 'string': + return optionalize('String') + case 'int32': + return optionalize('i32') + case 'float': + return optionalize('f32') + case 'double': + return optionalize('f64') + case 'byte': + return optionalize('byte') + case 'date-time': + return optionalize('chrono::DateTime') + case 'boolean': + return optionalize('bool') + case 'array': + const arrayType = type as ArrayType + return optionalize(`Vec<${toRustTypeX(arrayType.elementType)}>`) + case 'buffer': + return optionalize('Vec') + case 'object': + const oType = (type as ObjectType) + if (oType.properties?.length > 0) { + return optionalize(`types::${helpers.capitalize(oType.name)}`) + } else { + // we're just exposing the serde values directly for backwards compat + return optionalize('serde_json::Map') } - return "Json"; - case "integer": - return "Json"; - case "boolean": - return "Json"; - case "object": - return "Json>"; - case "array": - if (!property.items) return "Json>"; - // TODO this is not quite right to force cast - return `Json>`; - case "buffer": - return "Vec"; + case 'enum': + return optionalize(`types::${helpers.capitalize((type as EnumType).name)}`) + case 'map': + const { keyType, valueType } = type as MapType + return optionalize(`serde_json::Map<${toRustTypeX(keyType)}, ${toRustTypeX(valueType)}>`) default: - throw new Error("Can't convert property to Rust type: " + property.type); + throw new Error(`Can't convert XTP type to Rust type: "${type}"`) } } -function makePublic(s: string) { - return "pub " + s; +function isOptional(type: String): boolean { + return type.startsWith('Option<') +} + +function toRustType(property: XtpTyped, required?: boolean): string { + const t = toRustTypeX(property.xtpType) + + // if required is unset, just return what we get back + if (required === undefined) return t + + // if it's set and true, just return what we get back + if (required) return t + + // otherwise it's false, assuming it's not already, + // wrap it in an Option + if (t.startsWith('Option<')) return t + return `Option<${t}>` +} + +function jsonWrappedRustType(property: Property): string { + return `Json<${toRustType(property)}>` } export function render() { @@ -75,8 +91,10 @@ export function render() { const ctx = { ...helpers, ...getContext(), + formatIdentifier, + formatExternIdentifier, toRustType, - makePublic, + isOptional, jsonWrappedRustType, }; diff --git a/template/src/lib.rs.ejs b/template/src/lib.rs.ejs index 5f2c941..95b1441 100644 --- a/template/src/lib.rs.ejs +++ b/template/src/lib.rs.ejs @@ -7,11 +7,11 @@ use extism_pdk::*; <% if (hasComment(ex)) { -%> // <%- formatCommentBlock(ex.description, "// ") %> <% } -%> -pub(crate) fn <%= camelToSnakeCase(ex.name) %>(<%if (ex.input) { %>_input: <%- toRustType(ex.input) %> <% } %>) -> Result<<%if (ex.output) { %> <%- toRustType(ex.output) %> <% } else { %> () <% } %>, Error> { +pub(crate) fn <%= formatIdentifier(ex.name) %>(<%if (ex.input) { %>_input: <%- toRustType(ex.input) %> <% } %>) -> Result<<%if (ex.output) { %> <%- toRustType(ex.output) %> <% } else { %> () <% } %>, Error> { <% if (featureFlags['stub-with-code-samples'] && codeSamples(ex, 'rust').length > 0) { -%> <%- codeSamples(ex, 'rust')[0].source %> <% } else { -%> - todo!("Implement <%= camelToSnakeCase(ex.name) %>") + todo!("Implement <%= formatIdentifier(ex.name) %>") <% } -%> } diff --git a/template/src/pdk.rs.ejs b/template/src/pdk.rs.ejs index 1c5fc45..821c40c 100644 --- a/template/src/pdk.rs.ejs +++ b/template/src/pdk.rs.ejs @@ -4,6 +4,11 @@ #![allow(unused_macros)] use extism_pdk::*; +#[allow(unused)] +fn panic_if_key_missing() -> ! { + panic!("missing key"); +} + pub(crate) mod internal { pub(crate) fn return_error(e: extism_pdk::Error) -> i32 { let err = format!("{:?}", e); @@ -47,11 +52,11 @@ mod exports { <% schema.exports.forEach(ex => { -%> #[no_mangle] -pub extern "C" fn <%- ex.name %>() -> i32 { - <% if (ex.output && ex.output.contentType === "application/json") { %> - let ret = crate::<%- camelToSnakeCase(ex.name) %>(<% if (ex.input) { %> <% if (ex.input.contentType === "application/json") { %> try_input_json!() <% } else { %> try_input!() <% } %> <% } %>).and_then(|x| extism_pdk::output(extism_pdk::Json(x))); +pub extern "C" fn <%- formatExternIdentifier(ex.name) %>() -> i32 { + <% if (ex.output && isJsonEncoded(ex.output)) { %> + let ret = crate::<%- formatIdentifier(ex.name) %>(<% if (ex.input) { %> <% if (isJsonEncoded(ex.input)) { %> try_input_json!() <% } else { %> try_input!() <% } %> <% } %>).and_then(|x| extism_pdk::output(extism_pdk::Json(x))); <% } else { %> - let ret = crate::<%- camelToSnakeCase(ex.name) %>(<% if (ex.input) { %> <% if (ex.input.contentType === "application/json") { %> try_input_json!() <% } else { %> try_input!() <% } %> <% } %>).and_then(extism_pdk::output); + let ret = crate::<%- formatIdentifier(ex.name) %>(<% if (ex.input) { %> <% if (isJsonEncoded(ex.input)) { %> try_input_json!() <% } else { %> try_input!() <% } %> <% } %>).and_then(extism_pdk::output); <% } %> match ret { Ok(()) => { @@ -68,33 +73,37 @@ pub extern "C" fn <%- ex.name %>() -> i32 { pub mod types { use super::*; <% Object.values(schema.schemas).forEach(schema => { %> - <% if (schema.enum) { %> -#[derive(serde::Serialize, serde::Deserialize, extism_pdk::FromBytes, extism_pdk::ToBytes)] + <% if (isEnum(schema)) { %> +#[derive(Default, serde::Serialize, serde::Deserialize, extism_pdk::FromBytes, extism_pdk::ToBytes)] #[encoding(Json)] pub enum <%- capitalize(schema.name) %> { - <% schema.enum.forEach(variant => { -%> + #[default] + <% schema.xtpType.values.forEach(variant => { -%> #[serde(rename = "<%- variant %>")] <%- capitalize(variant) %>, <% }) %> } <% } else { %> -#[derive(serde::Serialize, serde::Deserialize, extism_pdk::FromBytes, extism_pdk::ToBytes)] +#[derive(Default, serde::Serialize, serde::Deserialize, extism_pdk::FromBytes, extism_pdk::ToBytes)] #[encoding(Json)] pub struct <%- capitalize(schema.name) %> { <% schema.properties.forEach(p => { -%> + <% let propType = toRustType(p, p.required) %> <% if (p.description) { -%> /// <%- formatCommentBlock(p.description, "/// ") %> <% } -%> #[serde(rename = "<%- p.name %>")] - <% if (p.nullable) { %>#[serde(default)]<% } %> - <% if (p.type === "buffer") { %> #[serde(with = "Base64Standard")] <% } %> - <%- makePublic(camelToSnakeCase(p.name)) %>: <%- p.nullable ? `Option<${toRustType(p)}>` : toRustType(p) %>, - <% }) %> - - <% if (schema.additionalProperties) { %> - #[serde(flatten)] - additional_properties: std::collections::HashMap>, + <% if (isOptional(propType)) { %> + <% if (!p.required) { %> + #[serde(skip_serializing_if="Option::is_none")] + #[serde(default)] + <% } else { %> + #[serde(default = "panic_if_key_missing")] + <% } %> <% } %> + <% if (isBuffer(p)) { %> #[serde(with = "Base64Standard")] <% } %> + pub <%- formatIdentifier(p.name) %>: <%- propType %>, + <% }) %> } <% } %> <% }); %> @@ -105,7 +114,7 @@ mod raw_imports { #[host_fn] extern "ExtismHost" { <% schema.imports.forEach(imp => { %> - pub(crate) fn <%- imp.name %>(<% if (imp.input) { -%>input: <% if (imp.input.contentType === "application/json") { %><%- jsonWrappedRustType(imp.input) %><%} else {%> <%- toRustType(imp.input) %> <%}%><%} -%>) <% if (imp.output) { -%> -> <% if (imp.output.contentType === "application/json") {%> <%- jsonWrappedRustType(imp.output) %> <%} else {%> <%-toRustType(imp.output) %> <%}%><% } -%>; + pub(crate) fn <%- formatExternIdentifier(imp.name) %>(<% if (imp.input) { -%>input: <% if (isJsonEncoded(imp.input)) { %><%- jsonWrappedRustType(imp.input) %><%} else {%> <%- toRustType(imp.input) %> <%}%><%} -%>) <% if (imp.output) { -%> -> <% if (isJsonEncoded(imp.output)) {%> <%- jsonWrappedRustType(imp.output) %> <%} else {%> <%-toRustType(imp.output) %> <%}%><% } -%>; <% }) %> } } @@ -120,14 +129,14 @@ mod raw_imports { /// And it returns an output <%- toRustType(imp.output) %> (<%- formatCommentLine(imp.output.description) %>) <% } -%> #[allow(unused)] -pub(crate) fn <%- camelToSnakeCase(imp.name) %>(<% if (imp.input) { -%>input: <%- toRustType(imp.input) %><%} -%>) -> std::result::Result<<% if (imp.output) { -%><%- toRustType(imp.output) %><% } else { -%> () <% } %> , extism_pdk::Error> { +pub(crate) fn <%- formatIdentifier(imp.name) %>(<% if (imp.input) { -%>input: <%- toRustType(imp.input) %><%} -%>) -> std::result::Result<<% if (imp.output) { -%><%- toRustType(imp.output) %><% } else { -%> () <% } %> , extism_pdk::Error> { let res = unsafe { raw_imports::<%- imp.name %>( - <% if (imp.input) { %> <% if (imp.input.contentType === "application/json") { %> extism_pdk::Json(input) <%} else {%> input <%}%> <% } %> + <% if (imp.input) { %> <% if (isJsonEncoded(imp.input)) { %> extism_pdk::Json(input) <%} else {%> input <%}%> <% } %> )? }; - <% if (imp.output && imp.output.contentType === "application/json") { %> + <% if (imp.output && isJsonEncoded(imp.output)) { %> let extism_pdk::Json(res) = res; <% } %> diff --git a/tests/schemas/fruit.yaml b/tests/schemas/fruit.yaml index bdf0d1f..be1d774 100644 --- a/tests/schemas/fruit.yaml +++ b/tests/schemas/fruit.yaml @@ -38,6 +38,19 @@ exports: output: contentType: application/json $ref: "#/components/schemas/ComplexObject" + fn: + description: | + Try to land on a keyword. Just try it. + codeSamples: + - lang: typescript + source: | + return { ghost: GhostGang.inky, aBoolean: true, aString: "okay", anInt: 123 } + input: + contentType: application/json + $ref: "#/components/schemas/impl" + output: + contentType: application/json + $ref: "#/components/schemas/ComplexObject" imports: eatAFruit: input: @@ -69,6 +82,15 @@ imports: version: v1-draft components: schemas: + impl: + properties: + struct: + type: string + description: a keyword + await: + type: string + description: another keyword + description: Really try to land on a keyword please WriteParams: properties: key: @@ -84,6 +106,8 @@ components: - orange - banana - strawberry + - mod + - use description: A set of available fruits you can consume GhostGang: enum: @@ -97,6 +121,15 @@ components: ghost: "$ref": "#/components/schemas/GhostGang" description: I can override the description for the property here + mod: + type: boolean + description: A boolean prop + use: + type: boolean + description: A boolean prop + await: + type: boolean + description: A boolean prop aBoolean: type: boolean description: A boolean prop @@ -118,3 +151,23 @@ components: "$ref": "#/components/schemas/WriteParams" nullable: true description: A complex json object + RequiredVsNullable: + required: + - aRequiredNotNullableBoolean + - aRequiredNullableBoolean + properties: + aRequiredNotNullableBoolean: + type: boolean + description: A not-nullable boolean prop + aRequiredNullableBoolean: + type: boolean + description: A nullable boolean prop + nullable: true + aNotRequiredNotNullableBoolean: + type: boolean + description: A not-nullable boolean prop + aNotRequiredNullableBoolean: + type: boolean + description: A nullable boolean prop + nullable: true + description: A weird little object diff --git a/tests/schemas/random.yaml b/tests/schemas/random.yaml new file mode 100644 index 0000000..6a1d71a --- /dev/null +++ b/tests/schemas/random.yaml @@ -0,0 +1,422 @@ +--- +version: v1-draft +exports: + noInputExport: + description: Export with no input + output: + type: string + contentType: text/plain; charset=utf-8 + description: Simple output + noOutputExport: + description: Export with no output + input: + type: string + contentType: text/plain; charset=utf-8 + description: Simple input + emptyExport: + description: Export with neither input nor output + stringToStringExport: + description: Export with string input and output + input: + type: string + contentType: text/plain; charset=utf-8 + description: Plain text input + output: + type: string + contentType: text/plain; charset=utf-8 + description: Plain text output + stringToJsonExport: + description: Export with string input and JSON output + input: + type: string + contentType: text/plain; charset=utf-8 + description: Plain text input + output: + type: object + contentType: application/json + description: JSON output + jsonToStringExport: + description: Export with JSON input and string output + input: + type: object + contentType: application/json + description: JSON input + output: + type: string + contentType: text/plain; charset=utf-8 + description: Plain text output + bufferToBufferExport: + description: Export with buffer input and output + input: + type: buffer + contentType: application/x-binary + description: Binary input + output: + type: buffer + contentType: application/x-binary + description: Binary output + bufferToJsonExport: + description: Export with buffer input and JSON output + input: + type: buffer + contentType: application/x-binary + description: Binary input + output: + type: object + contentType: application/json + description: JSON output with processing results + jsonToBufferExport: + description: Export with JSON input and buffer output + input: + type: object + contentType: application/json + description: JSON input with configuration + output: + type: buffer + contentType: application/x-binary + description: Generated binary output + simpleJsonExport: + description: Export with simple JSON types + input: + type: object + contentType: application/json + description: Simple JSON input + output: + type: object + contentType: application/json + description: Simple JSON output + complexJsonExport: + description: Export with complex nested JSON + input: + "$ref": "#/components/schemas/ComplexObject" + contentType: application/json + description: Complex JSON input + output: + "$ref": "#/components/schemas/ComplexObject" + contentType: application/json + description: Complex JSON output + arrayJsonExport: + description: Export with JSON arrays + input: + type: array + contentType: application/json + description: Array input + items: + type: object + output: + type: array + contentType: application/json + description: Array output + items: + "$ref": "#/components/schemas/NestedObject" + simpleRefExport: + description: Export with simple schema reference + input: + "$ref": "#/components/schemas/NestedObject" + contentType: application/json + description: Simple reference input + output: + "$ref": "#/components/schemas/NestedObject" + contentType: application/json + description: Simple reference output + arrayRefExport: + description: Export with array of references + input: + type: array + contentType: application/json + description: Array of referenced objects + items: + "$ref": "#/components/schemas/NestedObject" + output: + type: array + contentType: application/json + description: Processed array of objects + items: + "$ref": "#/components/schemas/ComplexObject" + nestedRefExport: + description: Export with nested references + input: + "$ref": "#/components/schemas/ComplexObject" + contentType: application/json + description: Nested reference input + output: + contentType: application/json + description: Nested output structure + "$ref": "#/components/schemas/NestedObject" + mixedTypesExport: + description: Export demonstrating mixed content types and formats + input: + type: object + contentType: application/json + description: Mixed type input object + output: + type: object + contentType: application/json + description: Mixed type response +imports: + noInputImport: + description: Import with no input + output: + type: string + contentType: text/plain; charset=utf-8 + description: Simple output + noOutputImport: + description: Import with no output + input: + type: string + contentType: text/plain; charset=utf-8 + description: Simple input + emptyImport: + description: Import with neither input nor output + stringToStringImport: + description: Import with string input and output + input: + type: string + contentType: text/plain; charset=utf-8 + description: Plain text input + output: + type: string + contentType: text/plain; charset=utf-8 + description: Plain text output + stringToJsonImport: + description: Import with string input and JSON output + input: + type: string + contentType: text/plain; charset=utf-8 + description: Plain text input + output: + type: object + contentType: application/json + description: JSON output + jsonToStringImport: + description: Import with JSON input and string output + input: + type: object + contentType: application/json + description: JSON input + output: + type: string + contentType: text/plain; charset=utf-8 + description: Plain text output + bufferToBufferImport: + description: Import with buffer input and output + input: + type: buffer + contentType: application/x-binary + description: Binary input + output: + type: buffer + contentType: application/x-binary + description: Binary output + bufferToJsonImport: + description: Import with buffer input and JSON output + input: + type: buffer + contentType: application/x-binary + description: Binary input + output: + type: object + contentType: application/json + description: JSON output with processing results + jsonToBufferImport: + description: Import with JSON input and buffer output + input: + type: object + contentType: application/json + description: JSON input with configuration + output: + type: buffer + contentType: application/x-binary + description: Generated binary output + simpleJsonImport: + description: Import with simple JSON types + input: + type: object + contentType: application/json + description: Simple JSON input + output: + type: object + contentType: application/json + description: Simple JSON output + complexJsonImport: + description: Import with complex nested JSON + input: + "$ref": "#/components/schemas/ComplexObject" + contentType: application/json + description: Complex JSON input + output: + "$ref": "#/components/schemas/ComplexObject" + contentType: application/json + description: Complex JSON output + arrayJsonImport: + description: Import with JSON arrays + input: + type: array + contentType: application/json + description: Array input + items: + type: object + output: + type: array + contentType: application/json + description: Array output + items: + "$ref": "#/components/schemas/NestedObject" + simpleRefImport: + description: Import with simple schema reference + input: + "$ref": "#/components/schemas/NestedObject" + contentType: application/json + description: Simple reference input + output: + "$ref": "#/components/schemas/NestedObject" + contentType: application/json + description: Simple reference output + arrayRefImport: + description: Import with array of references + input: + type: array + contentType: application/json + description: Array of referenced objects + items: + "$ref": "#/components/schemas/NestedObject" + output: + type: array + contentType: application/json + description: Processed array of objects + items: + "$ref": "#/components/schemas/ComplexObject" + nestedRefImport: + description: Import with nested references + input: + "$ref": "#/components/schemas/ComplexObject" + contentType: application/json + description: Nested reference input + output: + type: object + contentType: application/json + description: Nested output structure + "$ref": "#/components/schemas/NestedObject" + mixedTypesImport: + description: Import demonstrating mixed content types and formats + input: + type: object + contentType: application/json + description: Mixed type input object + output: + type: object + contentType: application/json + description: Mixed type response + reflectJsonObjectHost: + description: | + This function takes a ComplexObject and returns a ComplexObject. + It should come out the same way it came in. It's the same as the export. + But the export should call this. + input: + contentType: application/json + $ref: "#/components/schemas/ComplexObject" + output: + contentType: application/json + $ref: "#/components/schemas/ComplexObject" + reflectUtf8StringHost: + description: | + This function takes a string and returns it. + Should come out the same way it came in. Same as export. + input: + type: string + description: The input string + contentType: text/plain; charset=utf-8 + output: + type: string + description: The output string + contentType: text/plain; charset=utf-8 + reflectByteBufferHost: + description: | + This function takes a bugger and returns it. + Should come out the same way it came in. Same as export. + input: + contentType: application/x-binary + type: buffer + description: The input byte buffer + output: + contentType: application/x-binary + type: buffer + description: The output byte buffer + + noInputWithOutputHost: + description: a function that takes no input, but returns an output + output: + contentType: text/plain; charset=utf-8 + type: string + + withInputNoOutputHost: + description: a function that takes input, but returns no output + input: + contentType: application/json + type: integer + + noInputNoOutputHost: + description: a function that takes no input, and returns no output +components: + schemas: + ComplexObject: + description: Object with all possible property types + properties: + stringField: + type: string + description: String field + numberField: + type: number + format: double + description: Number field + integerField: + type: integer + format: int64 + description: Integer field + booleanField: + type: boolean + description: Boolean field + arrayField: + type: array + items: + type: string + description: Array field + objectField: + type: object + description: Untyped Object field + nullableField: + type: string + nullable: true + description: Nullable field + referenceField: + "$ref": "#/components/schemas/NestedObject" + description: Reference field + enumField: + "$ref": "#/components/schemas/EnumExample" + description: Enum field + required: + - stringField + - numberField + NestedObject: + description: Object for nested references + properties: + id: + type: string + description: Identifier + value: + type: number + format: double + description: Value + metadata: + type: object + description: Untyped Metadata information + required: + - id + EnumExample: + type: string + description: Example string enum + enum: + - PENDING + - PROCESSING + - COMPLETED + - FAILED diff --git a/tests/test.sh b/tests/test.sh index 6ab79e1..a490a05 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -9,7 +9,7 @@ cd tests for file in ./schemas/*.yaml; do echo "Generating and testing $file..." rm -rf output - xtp plugin init --schema-file $file --template ../bundle --path output -y --name output --feature stub-with-code-samples + xtp plugin init --schema-file "$file" --template ../bundle --path output -y --name output --feature stub-with-code-samples cd output xtp plugin build cd ..