Skip to content

Conversation

@iamdanfox
Copy link
Contributor

@iamdanfox iamdanfox commented Jul 11, 2018

Before this PR

Conjure POJOs containing primitives (e.g. int, boolean) would be coerced from JSON in which the fields were actually missing. This was not compliant with the wire spec and could lead to nasty bugs, where a field was unintentionally initialized to 0 or false. For example, consider this type:

UpdateSalaryRequest:
  salary: integer
  shares: integer

The following request would result in dfox's salary being set to 0, because the the 'salary' field was missing and is initialized to 0!!

curl -XPOST https://some-api/update-salary/dfox --data '{"shares":20}'

After this PR

Trying to deserialize a type with booleans, integers or doubles from {} will now fail.

Concerns

People may have been relying on this behaviour, so there's a chance this might break people's servers... In theory, we could put this behaviour behind a feature flag, and then phase it in slowly?? EDIT not gonna do this as it's not actually a wire-break and people's servers can be trivially fixed by updating their conjure defs:

 UpdateSalaryRequest:
-  salary: integer
+  salary: optional<integer>
   shares: integer

Future work: unify this validatePrimitivesHaveBeenInitialized method with the other validateFields method

fixes #14

@iamdanfox iamdanfox force-pushed the builders-check-primitives-are-initialized branch 2 times, most recently from f11aec1 to bb42556 Compare July 11, 2018 16:39
@iamdanfox iamdanfox force-pushed the builders-check-primitives-are-initialized branch from bb42556 to f933442 Compare July 11, 2018 16:40
@iamdanfox iamdanfox merged commit 63654bf into develop Jul 11, 2018
@iamdanfox iamdanfox deleted the builders-check-primitives-are-initialized branch July 11, 2018 17:25
@bkrieger
Copy link

It's a little late now, but I think this should have gone into Conjure 2.0.0. While it's technically not a wire break as you say, an unknowing server could accidentally make a wire break if it didn't notice this change.

@iamdanfox
Copy link
Contributor Author

@bkrieger yeah this was definitely a subtle change, but I'm not sure under what criteria we'd class this as a break?

  • old JSON can still be deserialized by new servers
  • JSON serialized by new servers can still be deserialized by old clients

The only case this wouldn't work is if a new client was trying to talk to an old server, but we don't claim to support that workflow anyway.

@bkrieger
Copy link

If you upgrade the server to a version with this change, and you don't notice that you have to change boolean to optional<boolean>, then old JSON cannot be deserialized by new servers.

@iamdanfox
Copy link
Contributor Author

iamdanfox commented Sep 18, 2018

@bkrieger if the JSON came from a remoting client then you'll never actually hit that problem because ObjectMappers.newObjectMapper() will always serialize the value as a boolean:

class Foo {
 boolean hello;
}

System.out.println(ObjectMappers.newClientObjectMapper().writeValueAsString(new Foo()));
// {"hello": false}

@bkrieger
Copy link

bkrieger commented Sep 18, 2018 via email

@iamdanfox
Copy link
Contributor Author

So the requirement now is to add optional<boolean> if you want to add a field to a request object in a backwards compatible way (still tolerating requests from older clients).

@bkrieger
Copy link

bkrieger commented Sep 19, 2018 via email

carterkozak pushed a commit to carterkozak/conjure-java that referenced this pull request Dec 24, 2018
Introduce a new plugin which makes local code-gen easy from a set of published conjure IR files.

Example usage:

`local-api/build.gradle`:
```
apply plugin: 'com.palantir.conjure-local'

dependencies {
    conjure 'com.palantir.conjure:conjure-api:3.2.0'
    conjure 'com.some.api:some-api:1.0.0'
}
```

`settings.gradle`
```
include 'local-api'
include 'local-api:typescript'
include 'local-api:python'
include 'local-api:java'
````

resulting directory structure
```
├── build.gradle
└── local-api/
    ├── build.gradle
    ├── java/
    │   ├── conjure-api/
    │   └── some-api/
    ├── python/
    │   ├── conjure-api/
    │   └── some-api/
    └── typescript/
        ├── conjure-api/
        └── some-api/
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make primitive fields required on builder.build()?

5 participants