diff --git a/integration/grpc-web/example.ts b/integration/grpc-web/example.ts index 1ddc09bb7..d45cb45b1 100644 --- a/integration/grpc-web/example.ts +++ b/integration/grpc-web/example.ts @@ -827,7 +827,7 @@ export interface DashAPICreds { Create(request: DeepPartial, metadata?: grpc.Metadata): Promise; Update(request: DeepPartial, metadata?: grpc.Metadata): Promise; Delete(request: DeepPartial, metadata?: grpc.Metadata): Promise; - Uppercase(request: DeepPartial, metadata?: grpc.Metadata): Promise; + Uppercase(request: string | undefined, metadata?: grpc.Metadata): Promise; } export class DashAPICredsClientImpl implements DashAPICreds { @@ -853,8 +853,8 @@ export class DashAPICredsClientImpl implements DashAPICreds { return this.rpc.unary(DashAPICredsDeleteDesc, DashAPICredsDeleteReq.fromPartial(request), metadata); } - Uppercase(request: DeepPartial, metadata?: grpc.Metadata): Promise { - return this.rpc.unary(DashAPICredsUppercaseDesc, StringValue.fromPartial(request), metadata); + Uppercase(request: string | undefined, metadata?: grpc.Metadata): Promise { + return this.rpc.unary(DashAPICredsUppercaseDesc, StringValue.fromPartial({ value: request }), metadata); } } diff --git a/integration/wrappers-regression/wrappers-regression-test.ts b/integration/wrappers-regression/wrappers-regression-test.ts index eb187e2c1..f74db8fdb 100644 --- a/integration/wrappers-regression/wrappers-regression-test.ts +++ b/integration/wrappers-regression/wrappers-regression-test.ts @@ -16,7 +16,7 @@ describe('wrappers in service methods', () => { it('generates a services that compiles', () => { let c: Clock = { Now: () => Promise.resolve(Timestamp.fromPartial({ seconds: 0, nanos: 0 })), - NowString: (inp: StringValue) => Promise.resolve(inp), + NowString: (inp: string | undefined) => Promise.resolve(StringValue.fromPartial({ value: inp })), NowStringStream: (inp: Observable) => inp, NowBool: () => Promise.resolve(BoolValue.fromPartial({ value: true })) }; diff --git a/integration/wrappers-regression/wrappers-regression.ts b/integration/wrappers-regression/wrappers-regression.ts index 1b343440a..6e5c9744f 100644 --- a/integration/wrappers-regression/wrappers-regression.ts +++ b/integration/wrappers-regression/wrappers-regression.ts @@ -10,7 +10,7 @@ export const protobufPackage = ""; export interface Clock { Now(request: Empty): Promise; - NowString(request: StringValue): Promise; + NowString(request: string | undefined): Promise; NowStringStream(request: Observable): Observable; NowBool(request: Empty): Promise; } @@ -33,8 +33,8 @@ export class ClockClientImpl implements Clock { return promise.then((data) => Timestamp.decode(_m0.Reader.create(data))); } - NowString(request: StringValue): Promise { - const data = StringValue.encode(request).finish(); + NowString(request: string | undefined): Promise { + const data = StringValue.encode(StringValue.fromPartial({ value: request })).finish(); const promise = this.rpc.request(this.service, "NowString", data); return promise.then((data) => StringValue.decode(_m0.Reader.create(data))); } diff --git a/src/generate-grpc-web.ts b/src/generate-grpc-web.ts index 7328b8025..71d66276d 100644 --- a/src/generate-grpc-web.ts +++ b/src/generate-grpc-web.ts @@ -1,5 +1,5 @@ import { MethodDescriptorProto, FileDescriptorProto, ServiceDescriptorProto } from "ts-proto-descriptors"; -import { rawRequestType, requestType, responsePromiseOrObservable, responseType, observableType } from "./types"; +import { rawRequestType, requestType, responsePromiseOrObservable, responseType, observableType, valueTypeName} from "./types"; import { Code, code, imp, joinCode } from "ts-poet"; import { Context } from "./context"; import { assertInstanceOf, FormattedMethodDescriptor, maybePrefixPackage } from "./utils"; @@ -50,8 +50,10 @@ function generateRpcMethod(ctx: Context, serviceDesc: ServiceDescriptorProto, me assertInstanceOf(methodDesc, FormattedMethodDescriptor); const { options } = ctx; const { useAbortSignal } = options; - const requestMessage = requestType(ctx, methodDesc, false); - const inputType = requestType(ctx, methodDesc, true); + const isValueType = valueTypeName(ctx, methodDesc.inputType) !== undefined; + const inputType = requestType(ctx, methodDesc, true && !isValueType, false); + const inputValue = isValueType ? '{ value: request }' : 'request'; + const requestMessage = requestType(ctx, methodDesc, false, true); const returns = responsePromiseOrObservable(ctx, methodDesc); if (methodDesc.clientStreaming) { @@ -75,7 +77,7 @@ function generateRpcMethod(ctx: Context, serviceDesc: ServiceDescriptorProto, me ): ${returns} { return this.rpc.${method}( ${methodDescName(serviceDesc, methodDesc)}, - ${requestMessage}.fromPartial(request), + ${requestMessage}.fromPartial(${inputValue}), metadata, ${useAbortSignal ? "abortSignal," : ""} ); diff --git a/src/generate-services.ts b/src/generate-services.ts index d81594337..2c520ec54 100644 --- a/src/generate-services.ts +++ b/src/generate-services.ts @@ -8,6 +8,7 @@ import { responsePromiseOrObservable, responseType, observableType, + valueTypeName, } from "./types"; import { assertInstanceOf, @@ -57,8 +58,9 @@ export function generateService( // the grpc-web clients auto-`fromPartial` the input before handing off to grpc-web's // serde runtime, so it's okay to accept partial results from the client + const isValueType = valueTypeName(ctx, methodDesc.inputType) !== undefined; const partialInput = options.outputClientImpl === "grpc-web"; - const inputType = requestType(ctx, methodDesc, partialInput); + const inputType = requestType(ctx, methodDesc, partialInput && !isValueType, !isValueType || methodDesc.clientStreaming); params.push(code`request: ${inputType}`); // Use metadata as last argument for interface only configuration @@ -109,7 +111,8 @@ function generateRegularRpcMethod(ctx: Context, methodDesc: MethodDescriptorProt const { options } = ctx; const Reader = impFile(ctx.options, "Reader@protobufjs/minimal"); const rawInputType = rawRequestType(ctx, methodDesc, { keepValueType: true }); - const inputType = requestType(ctx, methodDesc); + const isValueType = valueTypeName(ctx, methodDesc.inputType) !== undefined; + const inputType = requestType(ctx, methodDesc, false, !isValueType || methodDesc.clientStreaming); const rawOutputType = responseType(ctx, methodDesc, { keepValueType: true }); const params = [ @@ -130,7 +133,9 @@ function generateRegularRpcMethod(ctx: Context, methodDesc: MethodDescriptorProt `; } - let encode = code`${rawInputType}.encode(request).finish()`; + let encode = isValueType && !methodDesc.clientStreaming + ? code`${rawInputType}.encode(${rawInputType}.fromPartial({ value: request })).finish()` + : code`${rawInputType}.encode(request).finish()`; let beforeRequest; if (options.rpcBeforeRequest) { beforeRequest = code` diff --git a/src/types.ts b/src/types.ts index 5ab96c83c..c1c48b87e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -734,8 +734,13 @@ export function observableType(ctx: Context, asType: boolean = false): Code { } } -export function requestType(ctx: Context, methodDesc: MethodDescriptorProto, partial: boolean = false): Code { - let typeName = rawRequestType(ctx, methodDesc, { keepValueType: true }); +export function requestType( + ctx: Context, + methodDesc: MethodDescriptorProto, + partial: boolean = false, + keepValueType: boolean = true +): Code { + let typeName = rawRequestType(ctx, methodDesc, { keepValueType: keepValueType}); if (partial) { typeName = code`${ctx.utils.DeepPartial}<${typeName}>`;