Skip to content

Commit

Permalink
fix: regression in being able to return a Date as a GRPC return value (
Browse files Browse the repository at this point in the history
  • Loading branch information
fizx committed Apr 8, 2022
1 parent 8d6f686 commit 22b76ec
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 5 deletions.
81 changes: 81 additions & 0 deletions integration/use-date-true/google/protobuf/empty.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/* eslint-disable */
import * as Long from 'long';
import * as _m0 from 'protobufjs/minimal';

export const protobufPackage = 'google.protobuf';

/**
* A generic empty message that you can re-use to avoid defining duplicated
* empty messages in your APIs. A typical example is to use it as the request
* or the response type of an API method. For instance:
*
* service Foo {
* rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty);
* }
*
* The JSON representation for `Empty` is empty JSON object `{}`.
*/
export interface Empty {}

function createBaseEmpty(): Empty {
return {};
}

export const Empty = {
encode(_: Empty, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
return writer;
},

decode(input: _m0.Reader | Uint8Array, length?: number): Empty {
const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input);
let end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseEmpty();
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
default:
reader.skipType(tag & 7);
break;
}
}
return message;
},

fromJSON(_: any): Empty {
return {};
},

toJSON(_: Empty): unknown {
const obj: any = {};
return obj;
},

fromPartial<I extends Exact<DeepPartial<Empty>, I>>(_: I): Empty {
const message = createBaseEmpty();
return message;
},
};

type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;

export type DeepPartial<T> = T extends Builtin
? T
: T extends Array<infer U>
? Array<DeepPartial<U>>
: T extends ReadonlyArray<infer U>
? ReadonlyArray<DeepPartial<U>>
: T extends {}
? { [K in keyof T]?: DeepPartial<T[K]> }
: Partial<T>;

type KeysOfUnion<T> = T extends T ? keyof T : never;
export type Exact<P, I extends P> = P extends Builtin
? P
: P & { [K in keyof P]: Exact<P[K], I[K]> } & Record<Exclude<keyof I, KeysOfUnion<P>>, never>;

// If you get a compile-error about 'Constructor<Long> and ... have no overlap',
// add '--ts_proto_opt=esModuleInterop=true' as a flag when calling 'protoc'.
if (_m0.util.Long !== Long) {
_m0.util.Long = Long as any;
_m0.configure();
}
2 changes: 1 addition & 1 deletion integration/use-date-true/parameters.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
useDate=true
useDate=true,outputServices=generic-definitions,outputServices=default
7 changes: 6 additions & 1 deletion integration/use-date-true/use-date-true-test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { Todo } from './use-date-true';
import { Todo, Clock } from './use-date-true';

const jan1 = new Date('1970-01-01T00:00:00.000Z');
const feb1 = new Date('1970-02-01T00:00:00.000Z');

describe('useDate=true', () => {
it('generates a services that compiles', () => {
let c: Clock = {
Now: () => Promise.resolve(jan1),
};
});
it('generates types that compile and encode', () => {
const output = Todo.encode({
id: '6883ed6e-bd0d-4817-ba58-c2a53c73edc2',
Expand Down
Binary file modified integration/use-date-true/use-date-true.bin
Binary file not shown.
5 changes: 5 additions & 0 deletions integration/use-date-true/use-date-true.proto
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
syntax = "proto3";
import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";

message Todo {
Expand All @@ -8,3 +9,7 @@ message Todo {
optional google.protobuf.Timestamp optional_timestamp = 4;
map<string, google.protobuf.Timestamp> map_of_timestamps = 5;
}

service Clock {
rpc Now(google.protobuf.Empty) returns (google.protobuf.Timestamp);
}
37 changes: 37 additions & 0 deletions integration/use-date-true/use-date-true.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import * as Long from 'long';
import * as _m0 from 'protobufjs/minimal';
import { Timestamp } from './google/protobuf/timestamp';
import { Empty } from './google/protobuf/empty';

export const protobufPackage = '';

Expand Down Expand Up @@ -188,6 +189,42 @@ export const Todo_MapOfTimestampsEntry = {
},
};

export interface Clock {
Now(request: Empty): Promise<Date>;
}

export class ClockClientImpl implements Clock {
private readonly rpc: Rpc;
constructor(rpc: Rpc) {
this.rpc = rpc;
this.Now = this.Now.bind(this);
}
Now(request: Empty): Promise<Date> {
const data = Empty.encode(request).finish();
const promise = this.rpc.request('Clock', 'Now', data);
return promise.then((data) => fromTimestamp(Timestamp.decode(new _m0.Reader(data))));
}
}

export const ClockDefinition = {
name: 'Clock',
fullName: 'Clock',
methods: {
now: {
name: 'Now',
requestType: Empty,
requestStream: false,
responseType: Timestamp,
responseStream: false,
options: {},
},
},
} as const;

interface Rpc {
request(service: string, method: string, data: Uint8Array): Promise<Uint8Array>;
}

type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;

export type DeepPartial<T> = T extends Builtin
Expand Down
6 changes: 5 additions & 1 deletion src/generate-services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,18 +104,22 @@ function generateRegularRpcMethod(
methodDesc: MethodDescriptorProto
): Code {
assertInstanceOf(methodDesc, FormattedMethodDescriptor);
const { options } = ctx;
const { options, utils } = ctx;
const Reader = imp('Reader@protobufjs/minimal');
const rawInputType = rawRequestType(ctx, methodDesc);
const inputType = requestType(ctx, methodDesc);
const outputType = responseType(ctx, methodDesc);
const rawOutputType = responseType(ctx, methodDesc, { keepValueType: true });

const params = [...(options.context ? [code`ctx: Context`] : []), code`request: ${inputType}`];
const maybeCtx = options.context ? 'ctx,' : '';

let encode = code`${rawInputType}.encode(request).finish()`;
let decode = code`data => ${outputType}.decode(new ${Reader}(data))`;

if (options.useDate && rawOutputType.toString().includes('Timestamp')) {
decode = code`data => ${utils.fromTimestamp}(${rawOutputType}.decode(new ${Reader}(data)))`;
}
if (methodDesc.clientStreaming) {
encode = code`request.pipe(${imp('map@rxjs/operators')}(request => ${encode}))`;
}
Expand Down
8 changes: 6 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -665,8 +665,12 @@ export function requestType(ctx: Context, methodDesc: MethodDescriptorProto, par
return typeName;
}

export function responseType(ctx: Context, methodDesc: MethodDescriptorProto): Code {
return messageToTypeName(ctx, methodDesc.outputType, { keepValueType: true });
export function responseType(
ctx: Context,
methodDesc: MethodDescriptorProto,
typeOptions: { keepValueType?: boolean; repeated?: boolean } = {}
): Code {
return messageToTypeName(ctx, methodDesc.outputType, typeOptions);
}

export function responsePromise(ctx: Context, methodDesc: MethodDescriptorProto): Code {
Expand Down

0 comments on commit 22b76ec

Please sign in to comment.