Skip to content

Commit 878df5b

Browse files
authored
Add ability to specify capabilities in setup (#255)
and clarify that setup options are merged instead of replaced.
1 parent a2e27a2 commit 878df5b

File tree

7 files changed

+86
-18
lines changed

7 files changed

+86
-18
lines changed

src/InvocationModel.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import { AzFuncSystemError } from './errors';
2424
import { waitForProxyRequest } from './http/httpProxy';
2525
import { createStreamRequest } from './http/HttpRequest';
2626
import { InvocationContext } from './InvocationContext';
27-
import { isHttpStreamEnabled } from './setup';
27+
import { enableHttpStream } from './setup';
2828
import { isHttpTrigger, isTimerTrigger, isTrigger } from './utils/isTrigger';
2929
import { isDefined, nonNullProp, nonNullValue } from './utils/nonNull';
3030

@@ -76,7 +76,7 @@ export class InvocationModel implements coreTypes.InvocationModel {
7676
const bindingType = rpcBinding.type;
7777

7878
let input: unknown;
79-
if (isHttpTrigger(bindingType) && isHttpStreamEnabled()) {
79+
if (isHttpTrigger(bindingType) && enableHttpStream) {
8080
const proxyRequest = await waitForProxyRequest(this.#coreCtx.invocationId);
8181
input = createStreamRequest(proxyRequest, nonNullProp(req, 'triggerMetadata'));
8282
} else {

src/ProgrammingModel.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { CoreInvocationContext, WorkerCapabilities } from '@azure/functions-core
66
import { version } from './constants';
77
import { setupHttpProxy } from './http/httpProxy';
88
import { InvocationModel } from './InvocationModel';
9-
import { isHttpStreamEnabled, lockSetup } from './setup';
9+
import { capabilities as libraryCapabilities, enableHttpStream, lockSetup } from './setup';
1010

1111
export class ProgrammingModel implements coreTypes.ProgrammingModel {
1212
name = '@azure/functions';
@@ -16,14 +16,16 @@ export class ProgrammingModel implements coreTypes.ProgrammingModel {
1616
return new InvocationModel(coreCtx);
1717
}
1818

19-
async getCapabilities(capabilities: WorkerCapabilities): Promise<WorkerCapabilities> {
19+
async getCapabilities(workerCapabilities: WorkerCapabilities): Promise<WorkerCapabilities> {
2020
lockSetup();
2121

22-
if (isHttpStreamEnabled()) {
22+
if (enableHttpStream) {
2323
const httpUri = await setupHttpProxy();
24-
capabilities.HttpUri = httpUri;
24+
workerCapabilities.HttpUri = httpUri;
2525
}
2626

27-
return capabilities;
27+
Object.assign(workerCapabilities, libraryCapabilities);
28+
29+
return workerCapabilities;
2830
}
2931
}

src/converters/toRpcHttp.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { RpcHttpData, RpcTypedData } from '@azure/functions-core';
55
import { AzFuncSystemError } from '../errors';
66
import { sendProxyResponse } from '../http/httpProxy';
77
import { HttpResponse } from '../http/HttpResponse';
8-
import { isHttpStreamEnabled } from '../setup';
8+
import { enableHttpStream } from '../setup';
99
import { toRpcHttpCookie } from './toRpcHttpCookie';
1010
import { toRpcTypedData } from './toRpcTypedData';
1111

@@ -19,7 +19,7 @@ export async function toRpcHttp(invocationId: string, data: unknown): Promise<Rp
1919
}
2020

2121
const response = data instanceof HttpResponse ? data : new HttpResponse(data);
22-
if (isHttpStreamEnabled()) {
22+
if (enableHttpStream) {
2323
// send http data over http proxy instead of rpc
2424
await sendProxyResponse(invocationId, response);
2525
return;

src/setup.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,18 @@
33

44
import { SetupOptions } from '../types';
55
import { AzFuncSystemError } from './errors';
6+
import { isDefined } from './utils/nonNull';
67
import { tryGetCoreApiLazy } from './utils/tryGetCoreApiLazy';
78
import { workerSystemLog } from './utils/workerSystemLog';
89

9-
let options: SetupOptions = {};
1010
let setupLocked = false;
11-
1211
export function lockSetup(): void {
1312
setupLocked = true;
1413
}
1514

15+
export let enableHttpStream = false;
16+
export const capabilities: Record<string, string> = {};
17+
1618
export function setup(opts: SetupOptions): void {
1719
if (setupLocked) {
1820
throw new AzFuncSystemError("Setup options can't be changed after app startup has finished.");
@@ -27,10 +29,21 @@ export function setup(opts: SetupOptions): void {
2729
}
2830
}
2931

30-
options = opts;
31-
workerSystemLog('information', `Setup options: ${JSON.stringify(options)}`);
32-
}
32+
if (isDefined(opts.enableHttpStream)) {
33+
enableHttpStream = opts.enableHttpStream;
34+
}
3335

34-
export function isHttpStreamEnabled(): boolean {
35-
return !!options.enableHttpStream;
36+
if (opts.capabilities) {
37+
for (let [key, val] of Object.entries(opts.capabilities)) {
38+
if (isDefined(val)) {
39+
val = String(val);
40+
workerSystemLog('debug', `Capability ${key} set to ${val}.`);
41+
capabilities[key] = val;
42+
}
43+
}
44+
}
45+
46+
if (enableHttpStream) {
47+
workerSystemLog('debug', `HTTP streaming enabled.`);
48+
}
3649
}

test/setup.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
import 'mocha';
5+
import { expect } from 'chai';
6+
import { capabilities, enableHttpStream, setup } from '../src/setup';
7+
8+
describe('setup', () => {
9+
it('enableHttpStream', () => {
10+
// default
11+
expect(enableHttpStream).to.equal(false);
12+
13+
// set to true
14+
setup({ enableHttpStream: true });
15+
expect(enableHttpStream).to.equal(true);
16+
17+
// don't change if not explicitly set
18+
setup({});
19+
expect(enableHttpStream).to.equal(true);
20+
setup({ capabilities: {} });
21+
expect(enableHttpStream).to.equal(true);
22+
23+
// set to false
24+
setup({ enableHttpStream: false });
25+
expect(enableHttpStream).to.equal(false);
26+
});
27+
28+
it('capabilities', () => {
29+
// default
30+
expect(capabilities).to.deep.equal({});
31+
32+
// various setting & merging without replacing
33+
setup({ capabilities: { a: '1' } });
34+
expect(capabilities).to.deep.equal({ a: '1' });
35+
setup({});
36+
expect(capabilities).to.deep.equal({ a: '1' });
37+
setup({ capabilities: { b: '2' } });
38+
expect(capabilities).to.deep.equal({ a: '1', b: '2' });
39+
setup({ capabilities: { a: '3' } });
40+
expect(capabilities).to.deep.equal({ a: '3', b: '2' });
41+
42+
// boolean converted to string
43+
setup({ capabilities: { c: true } });
44+
expect(capabilities).to.deep.equal({ a: '3', b: '2', c: 'true' });
45+
});
46+
});

types/app.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ import { WarmupFunctionOptions } from './warmup';
1515

1616
/**
1717
* Optional method to configure the behavior of your app.
18-
* This can only be done during app startup, before invocations occur
18+
* This can only be done during app startup, before invocations occur.
19+
* If called multiple times, options will be merged with the previous options specified.
1920
*/
2021
export declare function setup(options: SetupOptions): void;
2122

types/setup.d.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,14 @@
33

44
export interface SetupOptions {
55
/**
6-
* PREVIEW: Stream http requests and responses instead of loading entire body in memory.
6+
* Stream http requests and responses instead of loading entire body in memory.
77
* [Learn more here](https://aka.ms/AzFuncNodeHttpStreams)
88
*/
99
enableHttpStream?: boolean;
10+
11+
/**
12+
* Dictionary of Node.js worker capabilities.
13+
* This will be merged with existing capabilities specified by the Node.js worker and library.
14+
*/
15+
capabilities?: Record<string, string | boolean>;
1016
}

0 commit comments

Comments
 (0)