Skip to content

Commit

Permalink
fix: JOIN-47136 fix single required field and required enum
Browse files Browse the repository at this point in the history
  • Loading branch information
eugene-taran authored Oct 8, 2024
2 parents 9b92372 + d820152 commit e4f00bb
Show file tree
Hide file tree
Showing 32 changed files with 324 additions and 1,731 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ commands:
jobs:
build_and_test:
docker:
- image: circleci/golang:1.17-node
- image: cimg/go:1.23-node
<<: *docker-auth
steps:
- install_protoc
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
dist/
node_modules/
.DS_Store
.idea
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
module github.com/join-com/protoc-gen-ts

go 1.17
go 1.23

require (
github.com/iancoleman/strcase v0.2.0
google.golang.org/protobuf v1.27.1
google.golang.org/protobuf v1.33.0
)
12 changes: 2 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/iancoleman/strcase v0.1.3 h1:dJBk1m2/qjL1twPLf68JND55vvivMupZ4wIzE8CTdBw=
github.com/iancoleman/strcase v0.1.3/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0=
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
31 changes: 18 additions & 13 deletions internal/generator/runner_generate_class.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,11 @@ func (r *Runner) generateAsInterfaceMethod(generatedFileStream *protogen.Generat
r.indentLevel += 2

hasRepeated := false
onlyRequired := true
for _, fieldSpec := range messageSpec.GetField() {
hasRepeated = hasRepeated || (fieldSpec.GetLabel() == descriptorpb.FieldDescriptorProto_LABEL_REPEATED)
requiredField, foundRequired := join_proto.GetBooleanCustomFieldOption("typescript_required", fieldSpec.GetOptions(), r.extensionTypes)
onlyRequired = onlyRequired && requiredField && foundRequired
}

r.P(generatedFileStream, "const message = {")
Expand All @@ -211,22 +214,24 @@ func (r *Runner) generateAsInterfaceMethod(generatedFileStream *protogen.Generat
var loopInnerIfClause string
if hasRepeated {
fieldAssignment = " const field = message[fieldName as keyof I" + className + "]"
loopInnerIfClause = "field == null || Array.isArray(field) && field.length === 0"
loopInnerIfClause = "field == null || Array.isArray(field) && (field as unknown[]).length === 0"
} else {
fieldAssignment = ""
loopInnerIfClause = "message[fieldName as keyof I" + className + "] == null"
}

r.P(
generatedFileStream,
"for (const fieldName of Object.keys(message)) {",
fieldAssignment,
" if ("+loopInnerIfClause+") {",
" // We remove the key to avoid problems with code making too many assumptions",
" delete message[fieldName as keyof I"+className+"]",
" }",
"}",
)
if !onlyRequired {
r.P(
generatedFileStream,
"for (const fieldName of Object.keys(message)) {",
fieldAssignment,
" if ("+loopInnerIfClause+") {",
" // We remove the key to avoid problems with code making too many assumptions",
" delete message[fieldName as keyof I"+className+"]",
" }",
"}",
)
}

r.P(generatedFileStream, "return message")

Expand Down Expand Up @@ -271,7 +276,7 @@ func (r *Runner) generateDecodePatchedMethod(generatedFileStream *protogen.Gener
var loopInerIfClause string
if hasRepeated {
fieldAssignment = " const field = message[fieldName]"
loopInerIfClause = "field == null || Array.isArray(field) && field.length === 0"
loopInerIfClause = "field == null || Array.isArray(field) && (field as unknown[]).length === 0"
} else {
fieldAssignment = ""
loopInerIfClause = "message[fieldName] == null"
Expand Down Expand Up @@ -330,7 +335,7 @@ func (r *Runner) generateEncodePatchedMethod(generatedFileStream *protogen.Gener
var loopInerIfClause string
if hasRepeated {
fieldAssignment = " const field = message[fieldName]"
loopInerIfClause = "field == null || Array.isArray(field) && field.length === 0"
loopInerIfClause = "field == null || Array.isArray(field) && (field as unknown[]).length === 0"
} else {
fieldAssignment = ""
loopInerIfClause = "message[fieldName] == null"
Expand Down
2 changes: 1 addition & 1 deletion tests/.node-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
14.17.0
20.17.0
2 changes: 1 addition & 1 deletion tests/__tests__/generated/FieldMasks.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// GENERATED CODE -- DO NOT EDIT!
// GENERATOR VERSION: 2.3.0.efa59c2.1691588972
// GENERATOR VERSION: 2.3.1.cbb8b0b.1728390468
/* eslint-disable @typescript-eslint/no-non-null-assertion */

import * as joinGRPC from '@join-com/grpc'
Expand Down
8 changes: 4 additions & 4 deletions tests/__tests__/generated/Flavors.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// GENERATED CODE -- DO NOT EDIT!
// GENERATOR VERSION: 2.3.0.efa59c2.1691588972
// GENERATOR VERSION: 2.3.1.cbb8b0b.1728390468
/* eslint-disable @typescript-eslint/no-non-null-assertion */

import * as joinGRPC from '@join-com/grpc'
Expand Down Expand Up @@ -50,7 +50,7 @@ export namespace Flavors {
}
for (const fieldName of Object.keys(message)) {
const field = message[fieldName as keyof IUserProfile]
if (field == null || (Array.isArray(field) && field.length === 0)) {
if (field == null || (Array.isArray(field) && (field as any[]).length === 0)) {
// We remove the key to avoid problems with code making too many assumptions
delete message[fieldName as keyof IUserProfile]
}
Expand All @@ -66,7 +66,7 @@ export namespace Flavors {
const message = UserProfile.decode(reader).asInterface()
for (const fieldName of ['id', 'username', 'emails'] as (keyof IUserProfile)[]) {
const field = message[fieldName]
if (field == null || (Array.isArray(field) && field.length === 0)) {
if (field == null || (Array.isArray(field) && (field as any[]).length === 0)) {
throw new Error(`Required field ${fieldName} in UserProfile is null or undefined`)
}
}
Expand All @@ -76,7 +76,7 @@ export namespace Flavors {
public static encodePatched(this: void, message: IUserProfile, writer?: protobufjs.Writer): protobufjs.Writer {
for (const fieldName of ['id', 'username', 'emails'] as (keyof IUserProfile)[]) {
const field = message[fieldName]
if (field == null || (Array.isArray(field) && field.length === 0)) {
if (field == null || (Array.isArray(field) && (field as any[]).length === 0)) {
throw new Error(`Required field ${fieldName} in UserProfile is null or undefined`)
}
}
Expand Down
2 changes: 1 addition & 1 deletion tests/__tests__/generated/Regressions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// GENERATED CODE -- DO NOT EDIT!
// GENERATOR VERSION: 2.3.0.efa59c2.1691588972
// GENERATOR VERSION: 2.3.1.cbb8b0b.1728390468
/* eslint-disable @typescript-eslint/no-non-null-assertion */

import * as protobufjs from 'protobufjs/light'
Expand Down
93 changes: 83 additions & 10 deletions tests/__tests__/generated/Test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// GENERATED CODE -- DO NOT EDIT!
// GENERATOR VERSION: 2.3.0.efa59c2.1691588972
// GENERATOR VERSION: 2.3.1.cbb8b0b.1728390468
/* eslint-disable @typescript-eslint/no-non-null-assertion */

import * as joinGRPC from '@join-com/grpc'
Expand Down Expand Up @@ -102,6 +102,12 @@ export namespace Foo {
requiredField: number
customRequiredField: number
optionalField?: number
repeatedOptionalField?: number[]
requiredEnumField: EnumType
}

export interface IOneRequiredFieldTest {
singleRequiredField: number
}

@protobufjs.Type.d('foo_CustomOptionsTest')
Expand Down Expand Up @@ -200,6 +206,49 @@ export namespace Foo {
}
}

@protobufjs.Type.d('foo_OneRequiredFieldTest')
export class OneRequiredFieldTest
extends protobufjs.Message<OneRequiredFieldTest>
implements ConvertibleTo<IOneRequiredFieldTest>, IOneRequiredFieldTest
{
@protobufjs.Field.d(1, 'int32')
public singleRequiredField!: number

public asInterface(): IOneRequiredFieldTest {
const message = {
...this,
}
return message
}

public static fromInterface(this: void, value: IOneRequiredFieldTest): OneRequiredFieldTest {
return OneRequiredFieldTest.fromObject(value)
}

public static decodePatched(this: void, reader: protobufjs.Reader | Uint8Array): IOneRequiredFieldTest {
const message = OneRequiredFieldTest.decode(reader).asInterface()
for (const fieldName of ['singleRequiredField'] as (keyof IOneRequiredFieldTest)[]) {
if (message[fieldName] == null) {
throw new Error(`Required field ${fieldName} in OneRequiredFieldTest is null or undefined`)
}
}
return message
}

public static encodePatched(
this: void,
message: IOneRequiredFieldTest,
writer?: protobufjs.Writer,
): protobufjs.Writer {
for (const fieldName of ['singleRequiredField'] as (keyof IOneRequiredFieldTest)[]) {
if (message[fieldName] == null) {
throw new Error(`Required field ${fieldName} in OneRequiredFieldTest is null or undefined`)
}
}
return OneRequiredFieldTest.encode(message, writer)
}
}

@protobufjs.Type.d('foo_Request')
export class Request extends protobufjs.Message<Request> implements ConvertibleTo<IRequest>, IRequest {
@protobufjs.Field.d(1, 'int32', 'optional')
Expand Down Expand Up @@ -234,7 +283,7 @@ export namespace Foo {
@protobufjs.Type.d('foo_RequiredPropertiesTest')
export class RequiredPropertiesTest
extends protobufjs.Message<RequiredPropertiesTest>
implements ConvertibleTo<IRequiredPropertiesTest>, IRequiredPropertiesTest
implements ConvertibleTo<IRequiredPropertiesTest>
{
@protobufjs.Field.d(1, 'int32')
public requiredField!: number
Expand All @@ -245,12 +294,20 @@ export namespace Foo {
@protobufjs.Field.d(3, 'int32', 'optional')
public optionalField?: number

@protobufjs.Field.d(4, 'int32', 'repeated')
public repeatedOptionalField?: number[]

@protobufjs.Field.d(5, EnumType_Enum)
public requiredEnumField!: EnumType_Enum

public asInterface(): IRequiredPropertiesTest {
const message = {
...this,
requiredEnumField: EnumType_Enum[this.requiredEnumField]! as EnumType,
}
for (const fieldName of Object.keys(message)) {
if (message[fieldName as keyof IRequiredPropertiesTest] == null) {
const field = message[fieldName as keyof IRequiredPropertiesTest]
if (field == null || (Array.isArray(field) && (field as any[]).length === 0)) {
// We remove the key to avoid problems with code making too many assumptions
delete message[fieldName as keyof IRequiredPropertiesTest]
}
Expand All @@ -259,13 +316,23 @@ export namespace Foo {
}

public static fromInterface(this: void, value: IRequiredPropertiesTest): RequiredPropertiesTest {
return RequiredPropertiesTest.fromObject(value)
const patchedValue = {
...value,
requiredEnumField: EnumType_Enum[value.requiredEnumField]!,
}

return RequiredPropertiesTest.fromObject(patchedValue)
}

public static decodePatched(this: void, reader: protobufjs.Reader | Uint8Array): IRequiredPropertiesTest {
const message = RequiredPropertiesTest.decode(reader).asInterface()
for (const fieldName of ['requiredField', 'customRequiredField'] as (keyof IRequiredPropertiesTest)[]) {
if (message[fieldName] == null) {
for (const fieldName of [
'requiredField',
'customRequiredField',
'requiredEnumField',
] as (keyof IRequiredPropertiesTest)[]) {
const field = message[fieldName]
if (field == null || (Array.isArray(field) && (field as any[]).length === 0)) {
throw new Error(`Required field ${fieldName} in RequiredPropertiesTest is null or undefined`)
}
}
Expand All @@ -277,12 +344,18 @@ export namespace Foo {
message: IRequiredPropertiesTest,
writer?: protobufjs.Writer,
): protobufjs.Writer {
for (const fieldName of ['requiredField', 'customRequiredField'] as (keyof IRequiredPropertiesTest)[]) {
if (message[fieldName] == null) {
for (const fieldName of [
'requiredField',
'customRequiredField',
'requiredEnumField',
] as (keyof IRequiredPropertiesTest)[]) {
const field = message[fieldName]
if (field == null || (Array.isArray(field) && (field as any[]).length === 0)) {
throw new Error(`Required field ${fieldName} in RequiredPropertiesTest is null or undefined`)
}
}
return RequiredPropertiesTest.encode(message, writer)
const transformedMessage = RequiredPropertiesTest.fromInterface(message)
return RequiredPropertiesTest.encode(transformedMessage, writer)
}
}

Expand Down Expand Up @@ -424,7 +497,7 @@ export namespace Foo {
}
for (const fieldName of Object.keys(message)) {
const field = message[fieldName as keyof ITest]
if (field == null || (Array.isArray(field) && field.length === 0)) {
if (field == null || (Array.isArray(field) && (field as any[]).length === 0)) {
// We remove the key to avoid problems with code making too many assumptions
delete message[fieldName as keyof ITest]
}
Expand Down
2 changes: 1 addition & 1 deletion tests/__tests__/generated/common/Common.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// GENERATED CODE -- DO NOT EDIT!
// GENERATOR VERSION: 2.3.0.efa59c2.1691588972
// GENERATOR VERSION: 2.3.1.cbb8b0b.1728390468
/* eslint-disable @typescript-eslint/no-non-null-assertion */

import * as protobufjs from 'protobufjs/light'
Expand Down
2 changes: 1 addition & 1 deletion tests/__tests__/generated/common/Extra.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// GENERATED CODE -- DO NOT EDIT!
// GENERATOR VERSION: 2.3.0.efa59c2.1691588972
// GENERATOR VERSION: 2.3.1.cbb8b0b.1728390468
/* eslint-disable @typescript-eslint/no-non-null-assertion */

import * as protobufjs from 'protobufjs/light'
Expand Down
8 changes: 1 addition & 7 deletions tests/__tests__/generated/google/protobuf/Empty.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// GENERATED CODE -- DO NOT EDIT!
// GENERATOR VERSION: 2.3.0.efa59c2.1691588972
// GENERATOR VERSION: 2.3.1.cbb8b0b.1728390468
/* eslint-disable @typescript-eslint/no-non-null-assertion */

import * as protobufjs from 'protobufjs/light'
Expand All @@ -22,12 +22,6 @@ export namespace GoogleProtobuf {
const message = {
...this,
}
for (const fieldName of Object.keys(message)) {
if (message[fieldName as keyof IEmpty] == null) {
// We remove the key to avoid problems with code making too many assumptions
delete message[fieldName as keyof IEmpty]
}
}
return message
}

Expand Down
4 changes: 2 additions & 2 deletions tests/__tests__/generated/google/protobuf/FieldMask.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// GENERATED CODE -- DO NOT EDIT!
// GENERATOR VERSION: 2.3.0.efa59c2.1691588972
// GENERATOR VERSION: 2.3.1.cbb8b0b.1728390468
/* eslint-disable @typescript-eslint/no-non-null-assertion */

import * as protobufjs from 'protobufjs/light'
Expand Down Expand Up @@ -29,7 +29,7 @@ export namespace GoogleProtobuf {
}
for (const fieldName of Object.keys(message)) {
const field = message[fieldName as keyof IFieldMask]
if (field == null || (Array.isArray(field) && field.length === 0)) {
if (field == null || (Array.isArray(field) && (field as any[]).length === 0)) {
// We remove the key to avoid problems with code making too many assumptions
delete message[fieldName as keyof IFieldMask]
}
Expand Down
2 changes: 1 addition & 1 deletion tests/__tests__/generated/google/protobuf/Timestamp.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// GENERATED CODE -- DO NOT EDIT!
// GENERATOR VERSION: 2.3.0.efa59c2.1691588972
// GENERATOR VERSION: 2.3.1.cbb8b0b.1728390468
/* eslint-disable @typescript-eslint/no-non-null-assertion */

import * as protobufjs from 'protobufjs/light'
Expand Down
Loading

0 comments on commit e4f00bb

Please sign in to comment.