This repository has been archived by the owner on Aug 21, 2023. It is now read-only.
forked from apache/thrift
-
Notifications
You must be signed in to change notification settings - Fork 0
[FOUND-1522 / THRIFT-1976] Allow JS thrift client to use ES6 maps #2
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
alaynarichmond
changed the title
Allow structs to be map keys in NodeJS
[FOUND-1522] Allow structs to be map keys in NodeJS
Dec 15, 2021
alaynarichmond
force-pushed
the
ar/structs-as-keys
branch
3 times, most recently
from
January 6, 2022 19:59
c962206
to
48d0e78
Compare
alaynarichmond
changed the title
[FOUND-1522] Allow structs to be map keys in NodeJS
[FOUND-1522 / THRIFT-1976] Allow JS thrift client to use ES6 maps
Jan 6, 2022
cc @strava/graphql |
@@ -219,6 +222,21 @@ copyMap = function(obj, types){ | |||
} | |||
var Type = type; | |||
|
|||
if (obj instanceof Map) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is happening on line 226? and 231?
i understand that you're following the example on lines 240-256; am wondering if want to use clearer JS syntax (and possibly es6?)
nice explanation. my only suggestion would be to use consistent examples b/w apple/artichoke and bonk, esp if this is what we'll use to make the PR back to apache |
The JavaScript thrift client currently uses JS objects to represent Thrift maps: when making requests, the client serializes objects into thrift maps, and when receiving responses, thrift maps are deserialized back into JS objects. There is an issue because the keys of JS objects are always strings. If you try to use an object or other non-primitive value as a key, the `.toString()` function gets called and the resulting value is used as the key. This means that serialization from JS to thrift does not work as expected when there are complex map keys. For example: ``` let key = { "a": "apple" } let obj = {} obj[key] = "artichoke" console.log(obj) ``` The above code prints out: ``` { '[object Object]': 'artichoke' } ``` Also, if a server sends the client a map with complex keys, deserialization will fail for the same reason. The fact that the JS client uses objects to represent maps means that the keys of a map must be primitive values, such as strings or numbers. However, the thrift protocol allows any type to be used for the keys of a map, including structs or containers. Thrift implementations in other languages, especially strongly typed languages, support this feature. This problem has been documented in the Apache Thrift JIRA under [THRIFT-1976](https://issues.apache.org/jira/browse/THRIFT-1976). One of the suggestions on that issue was to use the ES6 [Map type](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map), which allows any JavaScript type to be used for the keys, including objects. I took this suggestion and changed the thrift client so that it can now send and receive maps with structs, containers, etc. as keys. I did not implement the capability to support ES6 maps in the JS thrift server. For example, the JS client is now able to make requests against the following interface: ``` struct Bonk { 1: string message, 2: i32 type } Service ThriftTest { map<Bonk,i32> testMapWithStructKey(1: map<Bonk,i32> thing) map<list<i32>,i32> testMapWithListKey(1: map<list<i32>,i32> thing) } ``` Client example: ``` const testClient = // JS thrift client generated against the interface above const Bonk = // imported from the generated thrift types const mapWithStructKey = new Map(); mapWithStructKey.set(new Bonk({ message: “fruit”, type: 1 }), 10); mapWithStructKey.set(new Bonk({ message: “veggie”, type: 2}), 20); client.testMapWithStructKey(mapWithStructKey).then( (response) => { console.log(response); } ); const mapWithListKey = new Map(); mapWithListKey.set([1, 2, 3], 10); mapWithListKey.set([1, 2, 3, 4], 15); client.testMapWithListKey(mapWithListKey).then( (response) => { console.log(response); } ); ``` Console output: ``` Map { { message: 'veggie', type: 2 } => 20, { message: 'fruit', type: 1 } => 10 } Map { [ 1, 2, 3, 4 ] => 15, [ 1, 2, 3 ] => 10 } ``` The changes in this PR are only enabled when the `es6` flag is enabled during compilation. Note that the changes are breaking because all thrift maps will be deserialized into ES6 maps instead of objects, which is what they were deserialized into before. When working to get this merged into the official Thrift library, if people prefer a non-breaking change I will work on that. Making this change backwards compatible would involve using objects when the keys are simple values and using ES6 maps when the keys are complex values. However, I think this half and half solution may be more confusing than switching entirely to ES6 maps, which is what I’ve done here. I have not yet added tests because I’m having trouble getting the test suite to run. I will add those afterwards but would like to get this merged now so we can unblock our other work!
alaynarichmond
force-pushed
the
ar/structs-as-keys
branch
from
March 15, 2022 18:40
cd3390f
to
f726eb2
Compare
Merging this to unblock current work. I still need to add tests and will address this feedback at that point! I don't think we have to use this exact PR to contribute back to the Apache repo. |
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
The JavaScript thrift client currently uses JS objects to represent Thrift maps: when making requests, the client serializes objects into thrift maps, and when receiving responses, thrift maps are deserialized back into JS objects. There is an issue because the keys of JS objects are always strings. If you try to use an object or other non-primitive value as a key, the
.toString()
function gets called and the resulting value is used as the key. This means that serialization from JS to thrift does not work as expected when there are complex map keys.For example:
The above code prints out:
Also, if a server sends the client a map with complex keys, deserialization will fail for the same reason.
The fact that the JS client uses objects to represent maps means that the keys of a map must be primitive values, such as strings or numbers. However, the thrift protocol allows any type to be used for the keys of a map, including structs or containers. Thrift implementations in other languages, especially strongly typed languages, support this feature. This problem has been documented in the Apache Thrift JIRA under THRIFT-1976.
One of the suggestions on that issue was to use the ES6 Map type, which allows any JavaScript type to be used for the keys, including objects. I took this suggestion and changed the thrift client so that it can now send and receive maps with structs, containers, etc. as keys. I did not implement the capability to support ES6 maps in the JS thrift server.
For example, the JS client is now able to make requests against the following interface:
Client example:
Console output:
The changes in this PR are only enabled when the
es6
flag is enabled during compilation. Note that the changes are breaking because all thrift maps will be deserialized into ES6 maps instead of objects, which is what they were deserialized into before. When working to get this merged into the official Thrift library, if people prefer a non-breaking change I will work on that. Making this change backwards compatible would involve using objects when the keys are simple values and using ES6 maps when the keys are complex values. However, I think this half and half solution may be more confusing than switching entirely to ES6 maps, which is what I’ve done here.I have not yet added tests because I’m having trouble getting the test suite to run. I will add those afterwards but would like to get this merged now so we can unblock our other work!