diff --git a/packages/ts/generator-plugin-signals/src/SignalProcessor.ts b/packages/ts/generator-plugin-signals/src/SignalProcessor.ts index c483d8cbde..e86f84ffd6 100644 --- a/packages/ts/generator-plugin-signals/src/SignalProcessor.ts +++ b/packages/ts/generator-plugin-signals/src/SignalProcessor.ts @@ -18,6 +18,15 @@ const signals = ['NumberSignal', 'ValueSignal', 'ListSignal']; const genericSignals = ['ValueSignal', 'ListSignal']; const collectionSignals = ['ListSignal']; +const primitiveModels = Object.freeze( + new Map([ + [ts.SyntaxKind.StringKeyword, 'StringModel'], + [ts.SyntaxKind.NumberKeyword, 'NumberModel'], + [ts.SyntaxKind.BooleanKeyword, 'BooleanModel'], + [ts.SyntaxKind.ArrayType, 'ArrayModel'], + ]), +); + export default class SignalProcessor { readonly #dependencyManager: DependencyManager; readonly #owner: Plugin; @@ -48,27 +57,21 @@ export default class SignalProcessor { transform((tsNode) => { if (ts.isFunctionDeclaration(tsNode) && tsNode.name && this.#methods.has(tsNode.name.text)) { const signalId = this.#replaceSignalImport(tsNode); - let initialValue: ts.Expression = signalId.text.startsWith('NumberSignal') - ? ts.factory.createNumericLiteral('0') - : ts.factory.createIdentifier('undefined'); const filteredParams = tsNode.parameters.filter( (p) => !p.type || !ts.isTypeReferenceNode(p.type) || p.type.typeName !== initTypeId, ); // `filteredParams` can be altered after, need to store the param names now const paramNames = filteredParams.map((p) => (p.name as ts.Identifier).text).join(', '); const isCollectionSignal = collectionSignals.includes(signalId.text); - let genericReturnType; - if (genericSignals.includes(signalId.text)) { - genericReturnType = (tsNode.type as ts.TypeReferenceNode).typeArguments![0]; - if (!isCollectionSignal) { - const defaultValueType = SignalProcessor.#getDefaultValueType(genericReturnType); - if (defaultValueType) { - const { alias, param } = SignalProcessor.#createDefaultValueParameter(defaultValueType); - initialValue = alias; - filteredParams.push(param); - } - } + + const { defaultValueExpression, defaultValueParam, genericReturnType } = this.#createDefaultValue( + signalId, + tsNode, + ); + if (defaultValueParam) { + filteredParams.push(defaultValueParam); } + const returnType = genericReturnType ?? signalId; if (filteredParams.length > 0) { functionParams.set(tsNode.name.text, filteredParams); @@ -83,7 +86,9 @@ export default class SignalProcessor { transform((node) => (ts.isIdentifier(node) && node.text === SIGNAL ? signalId : node)), transform((node) => (ts.isIdentifier(node) && node.text === RETURN_TYPE ? returnType : node)), transform((node) => (ts.isIdentifier(node) && node.text === CONNECT_CLIENT ? connectClientId : node)), - transform((node) => (ts.isIdentifier(node) && node.text === INITIAL_VALUE ? initialValue : node)), + transform((node) => + ts.isIdentifier(node) && node.text === INITIAL_VALUE ? defaultValueExpression : node, + ), ], ); } @@ -135,6 +140,50 @@ export default class SignalProcessor { ); } + #createDefaultValue(signalId: ts.Identifier, functionDeclaration: FunctionDeclaration) { + const defaultValue: { + defaultValueExpression: ts.Expression | ts.Identifier; + defaultValueParam: ts.ParameterDeclaration | undefined; + genericReturnType: ts.TypeNode | undefined; + } = { + defaultValueExpression: signalId.text.startsWith('NumberSignal') + ? ts.factory.createNumericLiteral('0') + : ts.factory.createIdentifier('undefined'), + defaultValueParam: undefined, + genericReturnType: undefined, + }; + + if (!genericSignals.includes(signalId.text)) { + return defaultValue; + } + + defaultValue.genericReturnType = (functionDeclaration.type as ts.TypeReferenceNode).typeArguments![0]; + + if (collectionSignals.includes(signalId.text)) { + return defaultValue; + } + + const defaultValueType = SignalProcessor.#getDefaultValueType(defaultValue.genericReturnType); + if (!defaultValueType) { + return defaultValue; + } + + defaultValue.defaultValueParam = SignalProcessor.#createDefaultValueParameter(defaultValueType); + const emptyValueExpression = this.#createEmptyValueExpression(defaultValueType); + + defaultValue.defaultValueExpression = ts.factory.createBinaryExpression( + ts.factory.createPropertyAccessChain( + ts.factory.createIdentifier('options'), + ts.factory.createToken(ts.SyntaxKind.QuestionDotToken), + ts.factory.createIdentifier('defaultValue'), + ), + ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), + emptyValueExpression, + ); + + return defaultValue; + } + static #getDefaultValueType(node: ts.Node) { if ( ts.isUnionTypeNode(node) && @@ -145,23 +194,115 @@ export default class SignalProcessor { ) { return node.types[0].typeArguments[0]; } - return undefined; } - static #createDefaultValueParameter(returnType: ts.TypeNode) { - const alias = createFullyUniqueIdentifier('defaultValue'); - const bindingPattern = ts.factory.createObjectBindingPattern([ - ts.factory.createBindingElement(undefined, ts.factory.createIdentifier('defaultValue'), alias, undefined), - ]); + static #createDefaultValueParameter(defaultValueType: ts.TypeNode) { const paramType = ts.factory.createTypeLiteralNode([ - ts.factory.createPropertySignature(undefined, ts.factory.createIdentifier('defaultValue'), undefined, returnType), + ts.factory.createPropertySignature( + undefined, + ts.factory.createIdentifier('defaultValue'), + undefined, + defaultValueType, + ), ]); - // Return both the alias and the full parameter - return { - alias, - param: ts.factory.createParameterDeclaration(undefined, undefined, bindingPattern, undefined, paramType), - }; + + return ts.factory.createParameterDeclaration( + undefined, + undefined, + 'options', + ts.factory.createToken(ts.SyntaxKind.QuestionToken), + paramType, + ); + } + + static #isDefaultValueTypeNullable(defaultValueType: ts.TypeNode) { + return ( + ts.isUnionTypeNode(defaultValueType) && + defaultValueType.types.length && + defaultValueType.types.length > 1 && + defaultValueType.types.map((t) => t.kind).includes(ts.SyntaxKind.UndefinedKeyword) + ); + } + + #createEmptyValueExpression(defaultValueType: ts.UnionTypeNode) { + if (SignalProcessor.#isDefaultValueTypeNullable(defaultValueType)) { + return ts.factory.createIdentifier('undefined'); + } + const importedModelUniqueId = this.#determineModelImportUniqueIdentifier(defaultValueType); + return ts.factory.createCallExpression( + ts.factory.createPropertyAccessExpression(importedModelUniqueId, 'createEmptyValue'), + undefined, + [], + ); + } + + #determineModelImportUniqueIdentifier(returnTypeNode: ts.UnionTypeNode) { + let modelName = primitiveModels.get(returnTypeNode.types[0].kind); + let entityName; + if (modelName === undefined) { + const { entityName: e, modelName: m } = SignalProcessor.#extractModelNameFromTypeNode(returnTypeNode); + modelName = m; + entityName = e; + } + const modelImportUniqueId = + this.#getExistingEntityModelUniqueIdentifier(modelName) ?? createFullyUniqueIdentifier(modelName); + + this.#addModelImport(entityName, modelName, modelImportUniqueId); + return modelImportUniqueId; + } + + static #extractModelNameFromTypeNode(returnTypeNode: ts.UnionTypeNode) { + if (ts.isTypeReferenceNode(returnTypeNode.types[0])) { + const typeIdentifier = returnTypeNode.types[0].typeName; + if (ts.isIdentifier(typeIdentifier)) { + const entityName = typeIdentifier.text; + const modelName = `${entityName}Model`; + return { entityName, modelName }; + } + } + throw new Error('Unsupported type reference node'); + } + + #getExistingEntityModelUniqueIdentifier(modelName: string) { + const { imports } = this.#dependencyManager; + return ( + imports.named.getIdentifier('@vaadin/hilla-lit-form', modelName) ?? + imports.default.iter().find(([path]) => path.endsWith(`/${modelName}.js`))?.[1] + ); + } + + #addModelImport( + entityName: string | undefined, + modelName: string | undefined, + modelNameUniqueId: ts.Identifier | undefined, + ) { + if (modelName) { + if (primitiveModels.values().find((primitiveModel) => primitiveModel === modelName)) { + const { imports } = this.#dependencyManager; + const importedModel = imports.named.getIdentifier('@vaadin/hilla-lit-form', modelName); + if (importedModel === undefined) { + imports.named.add('@vaadin/hilla-lit-form', modelName, false, modelNameUniqueId); + } + } else { + this.#addObjectModelImport(entityName!, modelName, modelNameUniqueId!); + } + } + } + + #addObjectModelImport(entityName: string, modelName: string, modelNameUniqueId: ts.Identifier) { + const { imports } = this.#dependencyManager; + const entityImport = imports.default + .iter() + .map(([path]) => path) + .find((path) => path.startsWith('./') && path.endsWith(`/${entityName}.js`)); + if (entityImport) { + const entityModelImportPath = entityImport.replace(`/${entityName}.js`, `/${modelName}.js`); + const importedModel = imports.default.paths().find((path) => path === entityModelImportPath); + if (importedModel === undefined) { + imports.default.add(entityModelImportPath, modelName, false, modelNameUniqueId); + } + } } #replaceSignalImport(method: FunctionDeclaration): Identifier { diff --git a/packages/ts/generator-plugin-signals/test/SignalsEndpoints.spec.ts b/packages/ts/generator-plugin-signals/test/SignalsEndpoints.spec.ts index b550d7f76b..266f512792 100644 --- a/packages/ts/generator-plugin-signals/test/SignalsEndpoints.spec.ts +++ b/packages/ts/generator-plugin-signals/test/SignalsEndpoints.spec.ts @@ -86,5 +86,19 @@ describe('SignalsPlugin', () => { import.meta.url, ); }); + + it('correctly generates service with automatic default values for value signals of primitive types', async () => { + const generator = new Generator([BackbonePlugin, SignalsPlugin], { + logger: new LoggerFactory({ name: 'signals-plugin-test', verbose: true }), + }); + const input = await readFile(new URL('./hilla-openapi-default-value.json', import.meta.url), 'utf8'); + const files = await generator.process(input); + + const generatedValueSignalService = files.find((f) => f.name === 'PrimitiveTypeValueSignalService.ts')!; + await expect(await generatedValueSignalService.text()).toMatchSnapshot( + `PrimitiveTypeValueSignalService.snap.ts`, + import.meta.url, + ); + }); }); }); diff --git a/packages/ts/generator-plugin-signals/test/fixtures/PrimitiveTypeValueSignalService.snap.ts b/packages/ts/generator-plugin-signals/test/fixtures/PrimitiveTypeValueSignalService.snap.ts new file mode 100644 index 0000000000..be55b6e061 --- /dev/null +++ b/packages/ts/generator-plugin-signals/test/fixtures/PrimitiveTypeValueSignalService.snap.ts @@ -0,0 +1,49 @@ +import { ArrayModel as ArrayModel_1, BooleanModel as BooleanModel_1, NumberModel as NumberModel_1, StringModel as StringModel_1 } from "@vaadin/hilla-lit-form"; +import { ValueSignal as ValueSignal_1 } from "@vaadin/hilla-react-signals"; +import client_1 from "./connect-client.default.js"; +function anotherStringValueSignal_1(options?: { + defaultValue: string; +}): ValueSignal_1 { + return new ValueSignal_1(options?.defaultValue ?? StringModel_1.createEmptyValue(), { client: client_1, endpoint: "PrimitiveTypeValueSignalService", method: "anotherStringValueSignal" }); +} +function booleanValueSignal_1(options?: { + defaultValue: boolean; +}): ValueSignal_1 { + return new ValueSignal_1(options?.defaultValue ?? BooleanModel_1.createEmptyValue(), { client: client_1, endpoint: "PrimitiveTypeValueSignalService", method: "booleanValueSignal" }); +} +function booleanValueSignalNullable_1(options?: { + defaultValue: boolean | undefined; +}): ValueSignal_1 { + return new ValueSignal_1(options?.defaultValue ?? undefined, { client: client_1, endpoint: "PrimitiveTypeValueSignalService", method: "booleanValueSignalNullable" }); +} +function doubleValueSignal_1(options?: { + defaultValue: number; +}): ValueSignal_1 { + return new ValueSignal_1(options?.defaultValue ?? NumberModel_1.createEmptyValue(), { client: client_1, endpoint: "PrimitiveTypeValueSignalService", method: "doubleValueSignal" }); +} +function doubleValueSignalNullable_1(options?: { + defaultValue: number | undefined; +}): ValueSignal_1 { + return new ValueSignal_1(options?.defaultValue ?? undefined, { client: client_1, endpoint: "PrimitiveTypeValueSignalService", method: "doubleValueSignalNullable" }); +} +function stringArrayValueSignal_1(options?: { + defaultValue: Array; +}): ValueSignal_1> { + return new ValueSignal_1(options?.defaultValue ?? ArrayModel_1.createEmptyValue(), { client: client_1, endpoint: "PrimitiveTypeValueSignalService", method: "stringArrayValueSignal" }); +} +function stringArrayValueSignalNullable_1(options?: { + defaultValue: Array; +}): ValueSignal_1> { + return new ValueSignal_1(options?.defaultValue ?? ArrayModel_1.createEmptyValue(), { client: client_1, endpoint: "PrimitiveTypeValueSignalService", method: "stringArrayValueSignalNullable" }); +} +function stringValueSignal_1(options?: { + defaultValue: string; +}): ValueSignal_1 { + return new ValueSignal_1(options?.defaultValue ?? StringModel_1.createEmptyValue(), { client: client_1, endpoint: "PrimitiveTypeValueSignalService", method: "stringValueSignal" }); +} +function stringValueSignalNullable_1(options?: { + defaultValue: string | undefined; +}): ValueSignal_1 { + return new ValueSignal_1(options?.defaultValue ?? undefined, { client: client_1, endpoint: "PrimitiveTypeValueSignalService", method: "stringValueSignalNullable" }); +} +export { anotherStringValueSignal_1 as anotherStringValueSignal, booleanValueSignal_1 as booleanValueSignal, booleanValueSignalNullable_1 as booleanValueSignalNullable, doubleValueSignal_1 as doubleValueSignal, doubleValueSignalNullable_1 as doubleValueSignalNullable, stringArrayValueSignal_1 as stringArrayValueSignal, stringArrayValueSignalNullable_1 as stringArrayValueSignalNullable, stringValueSignal_1 as stringValueSignal, stringValueSignalNullable_1 as stringValueSignalNullable }; diff --git a/packages/ts/generator-plugin-signals/test/fixtures/SignalServiceMix.snap.ts b/packages/ts/generator-plugin-signals/test/fixtures/SignalServiceMix.snap.ts index 00586ef275..64650b71af 100644 --- a/packages/ts/generator-plugin-signals/test/fixtures/SignalServiceMix.snap.ts +++ b/packages/ts/generator-plugin-signals/test/fixtures/SignalServiceMix.snap.ts @@ -1,29 +1,36 @@ import { EndpointRequestInit as EndpointRequestInit_1 } from "@vaadin/hilla-frontend"; +import { ArrayModel as ArrayModel_1 } from "@vaadin/hilla-lit-form"; import { ListSignal as ListSignal_1, ValueSignal as ValueSignal_1 } from "@vaadin/hilla-react-signals"; import type Person_1 from "./com/github/taefi/data/Person.js"; +import PersonModel_1 from "./com/github/taefi/data/PersonModel.js"; import client_1 from "./connect-client.default.js"; async function getPerson_1(init?: EndpointRequestInit_1): Promise { return client_1.call("PersonService", "getPerson", {}, init); } +function personArraySignal_1(options?: { + defaultValue: Array; +}): ValueSignal_1> { + return new ValueSignal_1(options?.defaultValue ?? ArrayModel_1.createEmptyValue(), { client: client_1, endpoint: "PersonService", method: "personArraySignal" }); +} function personListSignal_1(): ListSignal_1 { return new ListSignal_1({ client: client_1, endpoint: "PersonService", method: "personListSignal" }); } -function personSignal_1({ defaultValue: defaultValue_1 }: { +function personSignalNotNull_1(options?: { + defaultValue: Person_1; +}): ValueSignal_1 { + return new ValueSignal_1(options?.defaultValue ?? PersonModel_1.createEmptyValue(), { client: client_1, endpoint: "PersonService", method: "personSignalNotNull" }); +} +function personSignalNullable_1(isAdult: boolean, options?: { defaultValue: Person_1 | undefined; }): ValueSignal_1 { - return new ValueSignal_1(defaultValue_1, { client: client_1, endpoint: "PersonService", method: "personSignal" }); + return new ValueSignal_1(options?.defaultValue ?? undefined, { client: client_1, endpoint: "PersonService", method: "personSignalNullable", params: { isAdult } }); } -function personSignalWithParams_1(dummyBoolean: boolean, dummyString: string | undefined, { defaultValue: defaultValue_2 }: { +function personSignalWithParams_1(dummyBoolean: boolean, dummyString: string | undefined, options?: { defaultValue: Person_1 | undefined; }): ValueSignal_1 { - return new ValueSignal_1(defaultValue_2, { client: client_1, endpoint: "PersonService", method: "personSignalWithParams", params: { dummyBoolean, dummyString } }); -} -function personSignalNonNull_1({ defaultValue: defaultValue_3 }: { - defaultValue: Person_1; -}): ValueSignal_1 { - return new ValueSignal_1(defaultValue_3, { client: client_1, endpoint: "PersonService", method: "personSignalNonNull" }); + return new ValueSignal_1(options?.defaultValue ?? undefined, { client: client_1, endpoint: "PersonService", method: "personSignalWithParams", params: { dummyBoolean, dummyString } }); } -function personSignalNonNullWithParams_1(dummyBoolean: boolean, dummyString: string | undefined, { defaultValue: defaultValue_4 }: { +function personSignalNonNullWithParams_1(dummyBoolean: boolean, dummyString: string | undefined, options?: { defaultValue: Person_1; }): ValueSignal_1 { - return new ValueSignal_1(defaultValue_4, { client: client_1, endpoint: "PersonService", method: "personSignalNonNullWithParams", params: { dummyBoolean, dummyString } }); + return new ValueSignal_1(options?.defaultValue ?? PersonModel_1.createEmptyValue(), { client: client_1, endpoint: "PersonService", method: "personSignalNonNullWithParams", params: { dummyBoolean, dummyString } }); } -export { getPerson_1 as getPerson, personListSignal_1 as personListSignal, personSignal_1 as personSignal, personSignalNonNull_1 as personSignalNonNull, personSignalNonNullWithParams_1 as personSignalNonNullWithParams, personSignalWithParams_1 as personSignalWithParams }; +export { getPerson_1 as getPerson, personArraySignal_1 as personArraySignal, personListSignal_1 as personListSignal, personSignalNonNullWithParams_1 as personSignalNonNullWithParams, personSignalNotNull_1 as personSignalNotNull, personSignalNullable_1 as personSignalNullable, personSignalWithParams_1 as personSignalWithParams }; diff --git a/packages/ts/generator-plugin-signals/test/hilla-openapi-default-value.json b/packages/ts/generator-plugin-signals/test/hilla-openapi-default-value.json new file mode 100644 index 0000000000..18f8fb0542 --- /dev/null +++ b/packages/ts/generator-plugin-signals/test/hilla-openapi-default-value.json @@ -0,0 +1,378 @@ +{ + "openapi" : "3.0.1", + "info" : { + "title" : "Hilla Application", + "version" : "1.0.0" + }, + "servers" : [ + { + "url" : "http://localhost:8080/connect", + "description" : "Hilla Backend" + } + ], + "tags" : [ + { + "name" : "PrimitiveTypeValueSignalService", + "x-class-name" : "com.github.taefi.services.PrimitiveTypeValueSignalService" + } + ], + "paths" : { + "/PrimitiveTypeValueSignalService/anotherStringValueSignal" : { + "post" : { + "tags" : [ + "PrimitiveTypeValueSignalService" + ], + "operationId" : "PrimitiveTypeValueSignalService_anotherStringValueSignal_POST", + "responses" : { + "200" : { + "description" : "", + "content" : { + "application/json" : { + "schema" : { + "anyOf" : [ + { + "$ref" : "#/components/schemas/com.vaadin.hilla.signals.ValueSignal" + } + ], + "x-type-arguments" : { + "allOf" : [ + { + "type" : "string", + "x-java-type" : "java.lang.String" + } + ] + } + } + } + } + } + } + } + }, + "/PrimitiveTypeValueSignalService/booleanValueSignal" : { + "post" : { + "tags" : [ + "PrimitiveTypeValueSignalService" + ], + "operationId" : "PrimitiveTypeValueSignalService_booleanValueSignal_POST", + "responses" : { + "200" : { + "description" : "", + "content" : { + "application/json" : { + "schema" : { + "anyOf" : [ + { + "$ref" : "#/components/schemas/com.vaadin.hilla.signals.ValueSignal" + } + ], + "x-type-arguments" : { + "allOf" : [ + { + "type" : "boolean", + "x-java-type" : "java.lang.Boolean" + } + ] + } + } + } + } + } + } + } + }, + "/PrimitiveTypeValueSignalService/booleanValueSignalNullable" : { + "post" : { + "tags" : [ + "PrimitiveTypeValueSignalService" + ], + "operationId" : "PrimitiveTypeValueSignalService_booleanValueSignalNullable_POST", + "responses" : { + "200" : { + "description" : "", + "content" : { + "application/json" : { + "schema" : { + "anyOf" : [ + { + "$ref" : "#/components/schemas/com.vaadin.hilla.signals.ValueSignal" + } + ], + "x-type-arguments" : { + "allOf" : [ + { + "type" : "boolean", + "nullable" : true, + "x-java-type" : "java.lang.Boolean" + } + ] + } + } + } + } + } + } + } + }, + "/PrimitiveTypeValueSignalService/doubleValueSignal" : { + "post" : { + "tags" : [ + "PrimitiveTypeValueSignalService" + ], + "operationId" : "PrimitiveTypeValueSignalService_doubleValueSignal_POST", + "responses" : { + "200" : { + "description" : "", + "content" : { + "application/json" : { + "schema" : { + "anyOf" : [ + { + "$ref" : "#/components/schemas/com.vaadin.hilla.signals.ValueSignal" + } + ], + "x-type-arguments" : { + "allOf" : [ + { + "type" : "number", + "format" : "double", + "x-java-type" : "java.lang.Double" + } + ] + } + } + } + } + } + } + } + }, + "/PrimitiveTypeValueSignalService/doubleValueSignalNullable" : { + "post" : { + "tags" : [ + "PrimitiveTypeValueSignalService" + ], + "operationId" : "PrimitiveTypeValueSignalService_doubleValueSignalNullable_POST", + "responses" : { + "200" : { + "description" : "", + "content" : { + "application/json" : { + "schema" : { + "anyOf" : [ + { + "$ref" : "#/components/schemas/com.vaadin.hilla.signals.ValueSignal" + } + ], + "x-type-arguments" : { + "allOf" : [ + { + "type" : "number", + "format" : "double", + "nullable" : true, + "x-java-type" : "java.lang.Double" + } + ] + } + } + } + } + } + } + } + }, + "/PrimitiveTypeValueSignalService/stringArrayValueSignal" : { + "post" : { + "tags" : [ + "PrimitiveTypeValueSignalService" + ], + "operationId" : "PrimitiveTypeValueSignalService_stringArrayValueSignal_POST", + "responses" : { + "200" : { + "description" : "", + "content" : { + "application/json" : { + "schema" : { + "anyOf" : [ + { + "$ref" : "#/components/schemas/com.vaadin.hilla.signals.ValueSignal" + } + ], + "x-type-arguments" : { + "allOf" : [ + { + "type" : "array", + "items" : { + "type" : "string", + "x-java-type" : "java.lang.String" + }, + "x-java-type" : "java.lang.String[]" + } + ] + } + } + } + } + } + } + } + }, + "/PrimitiveTypeValueSignalService/stringArrayValueSignalNullable" : { + "post" : { + "tags" : [ + "PrimitiveTypeValueSignalService" + ], + "operationId" : "PrimitiveTypeValueSignalService_stringArrayValueSignalNullable_POST", + "responses" : { + "200" : { + "description" : "", + "content" : { + "application/json" : { + "schema" : { + "anyOf" : [ + { + "$ref" : "#/components/schemas/com.vaadin.hilla.signals.ValueSignal" + } + ], + "x-type-arguments" : { + "allOf" : [ + { + "type" : "array", + "items" : { + "type" : "string", + "nullable" : true, + "x-java-type" : "java.lang.String" + } + } + ] + } + } + } + } + } + } + } + }, + "/PrimitiveTypeValueSignalService/stringValueSignal" : { + "post" : { + "tags" : [ + "PrimitiveTypeValueSignalService" + ], + "operationId" : "PrimitiveTypeValueSignalService_stringValueSignal_POST", + "responses" : { + "200" : { + "description" : "", + "content" : { + "application/json" : { + "schema" : { + "anyOf" : [ + { + "$ref" : "#/components/schemas/com.vaadin.hilla.signals.ValueSignal" + } + ], + "x-type-arguments" : { + "allOf" : [ + { + "type" : "string", + "x-java-type" : "java.lang.String" + } + ] + } + } + } + } + } + } + } + }, + "/PrimitiveTypeValueSignalService/stringValueSignalNullable" : { + "post" : { + "tags" : [ + "PrimitiveTypeValueSignalService" + ], + "operationId" : "PrimitiveTypeValueSignalService_stringValueSignalNullable_POST", + "responses" : { + "200" : { + "description" : "", + "content" : { + "application/json" : { + "schema" : { + "anyOf" : [ + { + "$ref" : "#/components/schemas/com.vaadin.hilla.signals.ValueSignal" + } + ], + "x-type-arguments" : { + "allOf" : [ + { + "type" : "string", + "nullable" : true, + "x-java-type" : "java.lang.String" + } + ] + } + } + } + } + } + } + } + } + }, + "components" : { + "schemas" : { + "com.vaadin.hilla.signals.Signal" : { + "type" : "object", + "properties" : { + "id" : { + "type" : "string", + "x-java-type" : "java.lang.String" + }, + "valueType" : { + "type" : "object", + "x-type-arguments" : { + "allOf" : [ + { + "type" : "object", + "x-type-variable" : "T" + } + ] + }, + "x-java-type" : "java.lang.Class" + } + }, + "x-type-parameters" : [ + "T" + ] + }, + "com.vaadin.hilla.signals.ValueSignal" : { + "anyOf" : [ + { + "$ref" : "#/components/schemas/com.vaadin.hilla.signals.Signal" + }, + { + "type" : "object", + "properties" : { + "value" : { + "type" : "object", + "nullable" : true, + "x-type-variable" : "T" + } + }, + "x-type-parameters" : [ + "T" + ] + } + ], + "x-type-arguments" : { + "allOf" : [ + { + "type" : "object", + "x-type-variable" : "T" + } + ] + } + } + } + } +} diff --git a/packages/ts/generator-plugin-signals/test/hilla-openapi-mix.json b/packages/ts/generator-plugin-signals/test/hilla-openapi-mix.json index 8e4346ba4b..ee00a86eed 100644 --- a/packages/ts/generator-plugin-signals/test/hilla-openapi-mix.json +++ b/packages/ts/generator-plugin-signals/test/hilla-openapi-mix.json @@ -275,6 +275,44 @@ } } }, + "/PersonService/personArraySignal" : { + "post" : { + "tags" : [ + "PersonService" + ], + "operationId" : "PersonService_personArraySignal_POST", + "responses" : { + "200" : { + "description" : "", + "content" : { + "application/json" : { + "schema" : { + "anyOf" : [ + { + "$ref" : "#/components/schemas/com.vaadin.hilla.signals.ValueSignal" + } + ], + "x-type-arguments" : { + "allOf" : [ + { + "type" : "array", + "items" : { + "anyOf" : [ + { + "$ref" : "#/components/schemas/com.github.taefi.data.Person" + } + ] + } + } + ] + } + } + } + } + } + } + } + }, "/PersonService/personListSignal" : { "post" : { "tags" : [ @@ -310,12 +348,12 @@ } } }, - "/PersonService/personSignal" : { + "/PersonService/personSignalNotNull" : { "post" : { "tags" : [ "PersonService" ], - "operationId" : "PersonService_personSignal_POST", + "operationId" : "PersonService_personSignalNotNull_POST", "responses" : { "200" : { "description" : "", @@ -327,13 +365,12 @@ "$ref" : "#/components/schemas/com.vaadin.hilla.signals.ValueSignal" } ], - "x-type-arguments": { - "allOf": [ + "x-type-arguments" : { + "allOf" : [ { - "nullable": true, - "anyOf": [ + "anyOf" : [ { - "$ref": "#/components/schemas/com.github.taefi.data.Person" + "$ref" : "#/components/schemas/com.github.taefi.data.Person" } ] } @@ -346,26 +383,21 @@ } } }, - "/PersonService/personSignalWithParams" : { + "/PersonService/personSignalNullable" : { "post" : { "tags" : [ "PersonService" ], - "operationId" : "PersonService_personSignalWithParams_POST", + "operationId" : "PersonService_personSignalNullable_POST", "requestBody" : { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "dummyBoolean": { - "type": "boolean", - "x-java-type": "java.lang.Boolean" - }, - "dummyString": { - "type": "string", - "x-java-type": "java.lang.String", - "nullable": true + "content" : { + "application/json" : { + "schema" : { + "type" : "object", + "properties" : { + "isAdult" : { + "type" : "boolean", + "x-java-type" : "boolean" } } } @@ -383,13 +415,13 @@ "$ref" : "#/components/schemas/com.vaadin.hilla.signals.ValueSignal" } ], - "x-type-arguments": { - "allOf": [ + "x-type-arguments" : { + "allOf" : [ { - "nullable": true, - "anyOf": [ + "nullable" : true, + "anyOf" : [ { - "$ref": "#/components/schemas/com.github.taefi.data.Person" + "$ref" : "#/components/schemas/com.github.taefi.data.Person" } ] } @@ -402,12 +434,32 @@ } } }, - "/PersonService/personSignalNonNull" : { + "/PersonService/personSignalWithParams" : { "post" : { "tags" : [ "PersonService" ], - "operationId" : "PersonService_personSignalNonNull_POST", + "operationId" : "PersonService_personSignalWithParams_POST", + "requestBody" : { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "dummyBoolean": { + "type": "boolean", + "x-java-type": "java.lang.Boolean" + }, + "dummyString": { + "type": "string", + "x-java-type": "java.lang.String", + "nullable": true + } + } + } + } + } + }, "responses" : { "200" : { "description" : "", @@ -422,7 +474,7 @@ "x-type-arguments": { "allOf": [ { - "nullable": false, + "nullable": true, "anyOf": [ { "$ref": "#/components/schemas/com.github.taefi.data.Person"