UNPKG

7.42 kBPlain TextView Raw
1// Copyright (c) .NET Foundation. All rights reserved.
2// Licensed under the MIT License.
3
4import {
5 CosmosDBFunctionOptions,
6 EventGridFunctionOptions,
7 EventHubFunctionOptions,
8 ExponentialBackoffRetryOptions,
9 FixedDelayRetryOptions,
10 FunctionOptions,
11 FunctionTrigger,
12 GenericFunctionOptions,
13 HttpFunctionOptions,
14 HttpHandler,
15 HttpMethod,
16 HttpMethodFunctionOptions,
17 ServiceBusQueueFunctionOptions,
18 ServiceBusTopicFunctionOptions,
19 StorageBlobFunctionOptions,
20 StorageQueueFunctionOptions,
21 TimerFunctionOptions,
22 WarmupFunctionOptions,
23} from '@azure/functions';
24import * as coreTypes from '@azure/functions-core';
25import { CoreInvocationContext, FunctionCallback } from '@azure/functions-core';
26import { returnBindingKey, version } from './constants';
27import { toRpcDuration } from './converters/toRpcDuration';
28import { InvocationModel } from './InvocationModel';
29import * as output from './output';
30import * as trigger from './trigger';
31import { isTrigger } from './utils/isTrigger';
32import { tryGetCoreApiLazy } from './utils/tryGetCoreApiLazy';
33
34export * as hook from './hooks/registerHook';
35
36class ProgrammingModel implements coreTypes.ProgrammingModel {
37 name = '@azure/functions';
38 version = version;
39 getInvocationModel(coreCtx: CoreInvocationContext): InvocationModel {
40 return new InvocationModel(coreCtx);
41 }
42}
43
44let hasSetup = false;
45function setup() {
46 const coreApi = tryGetCoreApiLazy();
47 if (!coreApi) {
48 console.warn(
49 'WARNING: Failed to detect the Azure Functions runtime. Switching "@azure/functions" package to test mode - not all features are supported.'
50 );
51 } else {
52 coreApi.setProgrammingModel(new ProgrammingModel());
53 }
54 hasSetup = true;
55}
56
57function convertToHttpOptions(
58 optionsOrHandler: HttpFunctionOptions | HttpHandler,
59 method: HttpMethod
60): HttpFunctionOptions {
61 const options: HttpFunctionOptions =
62 typeof optionsOrHandler === 'function' ? { handler: optionsOrHandler } : optionsOrHandler;
63 options.methods = [method];
64 return options;
65}
66
67function convertToGenericOptions<T extends Omit<FunctionOptions, 'trigger'> & Partial<FunctionOptions>>(
68 options: T,
69 triggerMethod: (o: Omit<T, 'handler' | 'return' | 'trigger' | 'extraInputs' | 'extraOutputs'>) => FunctionTrigger
70): FunctionOptions {
71 const { handler, return: ret, trigger, extraInputs, extraOutputs, ...triggerOptions } = options;
72 return {
73 trigger: trigger ?? triggerMethod(triggerOptions),
74 return: ret,
75 extraInputs,
76 extraOutputs,
77 handler,
78 };
79}
80
81export function get(name: string, optionsOrHandler: HttpMethodFunctionOptions | HttpHandler): void {
82 http(name, convertToHttpOptions(optionsOrHandler, 'GET'));
83}
84
85export function put(name: string, optionsOrHandler: HttpMethodFunctionOptions | HttpHandler): void {
86 http(name, convertToHttpOptions(optionsOrHandler, 'PUT'));
87}
88
89export function post(name: string, optionsOrHandler: HttpMethodFunctionOptions | HttpHandler): void {
90 http(name, convertToHttpOptions(optionsOrHandler, 'POST'));
91}
92
93export function patch(name: string, optionsOrHandler: HttpMethodFunctionOptions | HttpHandler): void {
94 http(name, convertToHttpOptions(optionsOrHandler, 'PATCH'));
95}
96
97export function deleteRequest(name: string, optionsOrHandler: HttpMethodFunctionOptions | HttpHandler): void {
98 http(name, convertToHttpOptions(optionsOrHandler, 'DELETE'));
99}
100
101export function http(name: string, options: HttpFunctionOptions): void {
102 options.return ||= output.http({});
103 generic(name, convertToGenericOptions(options, trigger.http));
104}
105
106export function timer(name: string, options: TimerFunctionOptions): void {
107 generic(name, convertToGenericOptions(options, trigger.timer));
108}
109
110export function storageBlob(name: string, options: StorageBlobFunctionOptions): void {
111 generic(name, convertToGenericOptions(options, trigger.storageBlob));
112}
113
114export function storageQueue(name: string, options: StorageQueueFunctionOptions): void {
115 generic(name, convertToGenericOptions(options, trigger.storageQueue));
116}
117
118export function serviceBusQueue(name: string, options: ServiceBusQueueFunctionOptions): void {
119 generic(name, convertToGenericOptions(options, trigger.serviceBusQueue));
120}
121
122export function serviceBusTopic(name: string, options: ServiceBusTopicFunctionOptions): void {
123 generic(name, convertToGenericOptions(options, trigger.serviceBusTopic));
124}
125
126export function eventHub(name: string, options: EventHubFunctionOptions): void {
127 generic(name, convertToGenericOptions(options, trigger.eventHub));
128}
129
130export function eventGrid(name: string, options: EventGridFunctionOptions): void {
131 generic(name, convertToGenericOptions(options, trigger.eventGrid));
132}
133
134export function cosmosDB(name: string, options: CosmosDBFunctionOptions): void {
135 // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
136 generic(name, convertToGenericOptions(options, <any>trigger.cosmosDB));
137}
138
139export function warmup(name: string, options: WarmupFunctionOptions): void {
140 generic(name, convertToGenericOptions(options, trigger.warmup));
141}
142
143export function generic(name: string, options: GenericFunctionOptions): void {
144 if (!hasSetup) {
145 setup();
146 }
147
148 const bindings: Record<string, coreTypes.RpcBindingInfo> = {};
149
150 const trigger = options.trigger;
151 bindings[trigger.name] = {
152 ...trigger,
153 direction: 'in',
154 type: isTrigger(trigger.type) ? trigger.type : trigger.type + 'Trigger',
155 };
156
157 if (options.extraInputs) {
158 for (const input of options.extraInputs) {
159 bindings[input.name] = {
160 ...input,
161 direction: 'in',
162 };
163 }
164 }
165
166 if (options.return) {
167 bindings[returnBindingKey] = {
168 ...options.return,
169 direction: 'out',
170 };
171 }
172
173 if (options.extraOutputs) {
174 for (const output of options.extraOutputs) {
175 bindings[output.name] = {
176 ...output,
177 direction: 'out',
178 };
179 }
180 }
181
182 let retryOptions: coreTypes.RpcRetryOptions | undefined;
183 if (options.retry) {
184 retryOptions = {
185 ...options.retry,
186 retryStrategy: options.retry.strategy,
187 delayInterval: toRpcDuration((<FixedDelayRetryOptions>options.retry).delayInterval, 'retry.delayInterval'),
188 maximumInterval: toRpcDuration(
189 (<ExponentialBackoffRetryOptions>options.retry).maximumInterval,
190 'retry.maximumInterval'
191 ),
192 minimumInterval: toRpcDuration(
193 (<ExponentialBackoffRetryOptions>options.retry).minimumInterval,
194 'retry.minimumInterval'
195 ),
196 };
197 }
198
199 const coreApi = tryGetCoreApiLazy();
200 if (!coreApi) {
201 console.warn(
202 `WARNING: Skipping call to register function "${name}" because the "@azure/functions" package is in test mode.`
203 );
204 } else {
205 coreApi.registerFunction({ name, bindings, retryOptions }, <FunctionCallback>options.handler);
206 }
207}