Skip to content

Commit

Permalink
feat: support mapping ObjectId message as mongodb.ObjectId (#467)
Browse files Browse the repository at this point in the history
  • Loading branch information
davearata-snorack authored Jan 9, 2022
1 parent a5bb22c commit 8b23897
Show file tree
Hide file tree
Showing 16 changed files with 601 additions and 12 deletions.
7 changes: 6 additions & 1 deletion README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@ creating a class and calling the right getters/setters.

- `fromJSON`/`toJSON` use the [proto3 canonical JSON encoding format](https://developers.google.com/protocol-buffers/docs/proto3#json) (e.g. timestamps are ISO strings), unlike [`protobufjs`](https://github.com/protobufjs/protobuf.js/issues/1304).

- ObjectIds can be mapped as `mongodb.ObjectId`

(Configurable with the `useObjectId` parameter.)

# Auto-Batching / N+1 Prevention

(Note: this is currently only supported by the Twirp clients.)
Expand Down Expand Up @@ -347,6 +351,8 @@ Generated code will be placed in the Gradle build directory.

- With `--ts_proto_opt=useDate=false`, fields of type `google.protobuf.Timestamp` will not be mapped to type `Date` in the generated types. See [Timestamp](#timestamp) for more details.

- With `--ts_proto_opt=useObjectId=true`, fields of a type called ObjectId where the message is constructed to have on field called value that is a string will be mapped to type `mongodb.ObjectId` in the generated types. This will require your project to install the mongodb npm package. See [ObjectId](#objectid) for more details.

- With `--ts_proto_opt=outputSchema=true`, meta typings will be generated that can later be used in other code generators.

- With `--ts_proto_opt=outputTypeRegistry=true`, the type registry will be generated that can be used to resolve message types by fully-qualified name. Also, each message will get extra `$type` field containing fully-qualified name.
Expand Down Expand Up @@ -685,7 +691,6 @@ The representation of `google.protobuf.Timestamp` is configurable by the `useDat
| --------------------------- | ---------------------- | ------------------------------------ | ---------------- |
| `google.protobuf.Timestamp` | `Date` | `{ seconds: number, nanos: number }` | `string` |


# Number Types

Numbers are by default assumed to be plain JavaScript `number`s.
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
syntax = "proto3";

package foo.objectid;

message ObjectId {
string value = 1;
}
86 changes: 86 additions & 0 deletions integration/use-objectid-true-external-import/objectid/objectid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/* eslint-disable */
import { util, configure, Writer, Reader } from 'protobufjs/minimal';
import * as Long from 'long';

export const protobufPackage = 'foo.objectid';

export interface ObjectId {
value: string;
}

function createBaseObjectId(): ObjectId {
return { value: '' };
}

export const ObjectId = {
encode(message: ObjectId, writer: Writer = Writer.create()): Writer {
if (message.value !== '') {
writer.uint32(10).string(message.value);
}
return writer;
},

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

fromJSON(object: any): ObjectId {
return {
value: isSet(object.value) ? String(object.value) : '',
};
},

toJSON(message: ObjectId): unknown {
const obj: any = {};
message.value !== undefined && (obj.value = message.value);
return obj;
},

fromPartial<I extends Exact<DeepPartial<ObjectId>, I>>(object: I): ObjectId {
const message = createBaseObjectId();
message.value = object.value ?? '';
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 (util.Long !== Long) {
util.Long = Long as any;
configure();
}

function isSet(value: any): boolean {
return value !== null && value !== undefined;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
useMongoObjectId=true
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Todo } from './use-objectid-true';

import * as mongodb from 'mongodb';

const id1 = new mongodb.ObjectId();
const id2 = new mongodb.ObjectId();

describe('useMongoObjectId=true External Import', () => {
it('generates types that compile and encode', () => {
const output = Todo.encode({
id: '6883ed6e-bd0d-4817-ba58-c2a53c73edc2',
oid: id1,
repeatedOid: [id1, id2],
mapOfOids: {
id1,
id2,
},
}).finish();

expect(Todo.decode(output)).toMatchInlineSnapshot(`
Object {
"id": "6883ed6e-bd0d-4817-ba58-c2a53c73edc2",
"mapOfOids": Object {
"id1": "${id1.toString()}",
"id2": "${id2.toString()}",
},
"oid": "${id1.toString()}",
"optionalOid": undefined,
"repeatedOid": Array [
"${id1.toString()}",
"${id2.toString()}",
],
}
`);

expect(Todo.toJSON(Todo.decode(output))).toMatchInlineSnapshot(`
Object {
"id": "6883ed6e-bd0d-4817-ba58-c2a53c73edc2",
"mapOfOids": Object {
"id1": "${id1.toString()}",
"id2": "${id2.toString()}",
},
"oid": "${id1.toString()}",
"repeatedOid": Array [
"${id1.toString()}",
"${id2.toString()}",
],
}
`);
});
});
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
syntax = "proto3";

package foo;

import "objectid/objectid.proto";

message Todo {
string id = 1;
foo.objectid.ObjectId oid = 2;
repeated foo.objectid.ObjectId repeated_oid = 3;
optional foo.objectid.ObjectId optional_oid = 4;
map<string, foo.objectid.ObjectId> map_of_oids = 5;
}
Loading

0 comments on commit 8b23897

Please sign in to comment.