Skip to content

Commit 0c36dd6

Browse files
authored
Add support for TypedDataCollections (#263)
* Add support for TypedDataCollection * change package updates for a separate pr * make change only for v3 functions host +
1 parent 5fa0c2e commit 0c36dd6

File tree

5 files changed

+142
-59
lines changed

5 files changed

+142
-59
lines changed

package-lock.json

Lines changed: 67 additions & 47 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/WorkerChannel.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,15 @@ export class WorkerChannel implements IWorkerChannel {
110110
}
111111
}
112112

113-
const workerCapabilities = {
113+
let workerCapabilities = {
114114
RpcHttpTriggerMetadataRemoved: "true",
115115
RpcHttpBodyOnly: "true"
116116
};
117117

118+
if (!this._v1WorkerBehavior) {
119+
workerCapabilities["TypedDataCollection"] = "true";
120+
}
121+
118122
this._eventStream.write({
119123
requestId: requestId,
120124
workerInitResponse: {

src/converters/RpcConverters.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
} from '../../azure-functions-language-worker-protobuf/src/rpc';
88
import { InternalException } from "../utils/InternalException";
99
import { TraceContext } from '../public/Interfaces';
10+
import { isLong } from 'long'
1011

1112
/**
1213
* Converts 'ITypedData' input from the RPC layer to JavaScript types.
@@ -28,6 +29,16 @@ export function fromTypedData(typedData?: rpc.ITypedData, convertStringToJson: b
2829
return str;
2930
} else if (typedData.bytes) {
3031
return Buffer.from(<Buffer>typedData.bytes);
32+
} else if (typedData.collectionBytes && typedData.collectionBytes.bytes) {
33+
let byteCollection = <Uint8Array[]>typedData.collectionBytes.bytes;
34+
return byteCollection.map(element => Buffer.from(<Buffer>element));
35+
} else if (typedData.collectionString && typedData.collectionString.string) {
36+
return <string[]>typedData.collectionString.string;
37+
} else if (typedData.collectionDouble && typedData.collectionDouble.double) {
38+
return <number[]>typedData.collectionDouble.double;
39+
} else if (typedData.collectionSint64 && typedData.collectionSint64.sint64) {
40+
let longCollection = <(Long|number)[]>typedData.collectionSint64.sint64;
41+
return longCollection.map(element => isLong(element) ? element.toString() : <number>element);
3142
}
3243
}
3344

test/BindingConvertersTests.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { getNormalizedBindingData, toRpcHttp, getBindingDefinitions } from '../src/converters';
1+
import { getNormalizedBindingData, toRpcHttp, getBindingDefinitions, fromTypedData } from '../src/converters';
22
import { FunctionInfo } from '../src/FunctionInfo';
33
import { expect } from 'chai';
44
import * as sinon from 'sinon';
55
import { AzureFunctionsRpcMessages as rpc } from '../azure-functions-language-worker-protobuf/src/rpc';
66
import 'mocha';
7+
import { fromString } from 'long';
78

89
describe('Binding Converters', () => {
910
it('normalizes binding trigger metadata for HTTP', () => {
@@ -127,4 +128,46 @@ describe('Binding Converters', () => {
127128
expect(bindingDefinitions[3].direction).to.be.undefined;
128129
expect(bindingDefinitions[3].type).to.equal("queue");
129130
});
131+
132+
it('deserializes string data with fromTypedData', () => {
133+
let data = fromTypedData({ string: "foo" });
134+
expect(data).to.equal("foo");
135+
});
136+
137+
it('deserializes json data with fromTypedData', () => {
138+
let data = fromTypedData({ json: "\{ \"foo\": \"bar\" }" });
139+
expect(data && data["foo"]).to.equal("bar");
140+
});
141+
142+
it('deserializes byte data with fromTypedData', () => {
143+
let buffer = Buffer.from("hello");
144+
let data = fromTypedData({ bytes: buffer });
145+
expect(data && data["buffer"]).to.equal(buffer.buffer);
146+
});
147+
148+
it('deserializes collectionBytes data with fromTypedData', () => {
149+
let fooBuffer = Buffer.from("foo");
150+
let barBuffer = Buffer.from("bar");
151+
let data = fromTypedData({ collectionBytes: { bytes: [fooBuffer, barBuffer] } });
152+
expect(data && data[0] && data[0]["buffer"]).to.equal(fooBuffer.buffer);
153+
expect(data && data[1] && data[1]["buffer"]).to.equal(barBuffer.buffer);
154+
});
155+
156+
it('deserializes collectionString data with fromTypedData', () => {
157+
let data = fromTypedData({ collectionString: { string: ["foo", "bar"] } });
158+
expect(data && data[0]).to.equal("foo");
159+
expect(data && data[1]).to.equal("bar");
160+
});
161+
162+
it('deserializes collectionDouble data with fromTypedData', () => {
163+
let data = fromTypedData({ collectionDouble: { double: [1.1, 2.2] } });
164+
expect(data && data[0]).to.equal(1.1);
165+
expect(data && data[1]).to.equal(2.2);
166+
});
167+
168+
it('deserializes collectionSint64 data with fromTypedData', () => {
169+
let data = fromTypedData({ collectionSint64: { sint64: [123, fromString("9007199254740992")] } });
170+
expect(data && data[0]).to.equal(123);
171+
expect(data && data[1]).to.equal("9007199254740992");
172+
});
130173
})

test/WorkerChannelTests.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -79,24 +79,29 @@ describe('WorkerChannel', () => {
7979
}
8080
};
8181

82-
// Test Functions Host V2 compatability with Node.js 8
83-
if (process.version.startsWith("v8")) {
84-
initMessage.workerInitRequest.capabilities["V2Compatable"] = "true";
85-
}
86-
87-
stream.addTestMessage(initMessage);
88-
sinon.assert.calledWith(stream.written, <rpc.IStreamingMessage>{
82+
let expectedOutput = {
8983
requestId: 'id',
9084
workerInitResponse: {
9185
capabilities: {
92-
'RpcHttpBodyOnly': "true",
93-
'RpcHttpTriggerMetadataRemoved': "true"
86+
'RpcHttpBodyOnly': 'true',
87+
'RpcHttpTriggerMetadataRemoved': 'true'
9488
},
9589
result: {
9690
status: rpc.StatusResult.Status.Success
9791
}
9892
}
99-
});
93+
}
94+
95+
// V1 worker behavior
96+
if (process.version.startsWith('v8')) {
97+
initMessage.workerInitRequest.capabilities['V2Compatable'] = 'true';
98+
// Expect this behavior in V2 worker behavior
99+
} else {
100+
expectedOutput.workerInitResponse.capabilities['TypedDataCollection'] = 'true';
101+
}
102+
103+
stream.addTestMessage(initMessage);
104+
sinon.assert.calledWith(stream.written, );
100105
});
101106

102107
it('responds to function load', () => {

0 commit comments

Comments
 (0)