diff --git a/.vscode/launch.json b/.vscode/launch.json index 7cbefc2..f02b0a1 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -14,15 +14,6 @@ "request": "launch", "skipFiles": ["/**"], "type": "pwa-node" - }, - { - "name": "Current TS Tests File", - "type": "node", - "request": "launch", - "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha", - "args": ["-r", "ts-node/register", "${relativeFile}"], - "cwd": "${workspaceRoot}", - "protocol": "inspector" } ] } diff --git a/package-lock.json b/package-lock.json index 48a9122..487148b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,17 @@ { "name": "@azure/functions", - "version": "4.7.1-preview", + "version": "4.7.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@azure/functions", - "version": "4.7.1-preview", + "version": "4.7.0", "license": "MIT", "dependencies": { - "@azure/functions-extensions-base": "0.1.0-preview", "cookie": "^0.7.0", "long": "^4.0.0", - "undici": "^5.13.0" + "undici": "^5.29.0" }, "devDependencies": { "@types/chai": "^4.2.22", @@ -24,7 +23,6 @@ "@types/mocha": "^9.1.1", "@types/node": "^18.0.0", "@types/semver": "^7.3.9", - "@types/sinon": "^17.0.4", "@typescript-eslint/eslint-plugin": "^5.12.1", "@typescript-eslint/parser": "^5.12.1", "chai": "^4.2.0", @@ -46,10 +44,9 @@ "mocha-multi-reporters": "^1.5.1", "prettier": "^2.4.1", "semver": "^7.3.5", - "sinon": "^20.0.0", "ts-loader": "^9.3.1", "ts-node": "^3.3.0", - "typescript": "^4.9.5", + "typescript": "^4.5.5", "typescript4": "npm:typescript@~4.0.0", "webpack": "^5.74.0", "webpack-cli": "^4.10.0" @@ -3032,21 +3029,6 @@ "dev": true, "license": "ISC" }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", diff --git a/package.json b/package.json index 93be00c..16b286d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@azure/functions", - "version": "4.7.1-preview", + "version": "4.7.0", "description": "Microsoft Azure Functions NodeJS Framework", "keywords": [ "azure", @@ -41,10 +41,9 @@ "watch": "webpack --watch --mode development" }, "dependencies": { - "@azure/functions-extensions-base": "0.1.0-preview", "cookie": "^0.7.0", "long": "^4.0.0", - "undici": "^5.13.0" + "undici": "^5.29.0" }, "devDependencies": { "@types/chai": "^4.2.22", @@ -56,7 +55,6 @@ "@types/mocha": "^9.1.1", "@types/node": "^18.0.0", "@types/semver": "^7.3.9", - "@types/sinon": "^17.0.4", "@typescript-eslint/eslint-plugin": "^5.12.1", "@typescript-eslint/parser": "^5.12.1", "chai": "^4.2.0", @@ -67,8 +65,8 @@ "eslint-plugin-header": "^3.1.1", "eslint-plugin-import": "^2.29.0", "eslint-plugin-prettier": "^4.0.0", - "eslint-plugin-simple-import-sort": "^10.0.0", "eslint-webpack-plugin": "^3.2.0", + "eslint-plugin-simple-import-sort": "^10.0.0", "fork-ts-checker-webpack-plugin": "^7.2.13", "fs-extra": "^10.0.1", "globby": "^11.0.0", @@ -78,10 +76,9 @@ "mocha-multi-reporters": "^1.5.1", "prettier": "^2.4.1", "semver": "^7.3.5", - "sinon": "^20.0.0", "ts-loader": "^9.3.1", "ts-node": "^3.3.0", - "typescript": "^4.9.5", + "typescript": "^4.5.5", "typescript4": "npm:typescript@~4.0.0", "webpack": "^5.74.0", "webpack-cli": "^4.10.0" diff --git a/src/InvocationModel.ts b/src/InvocationModel.ts index c78900c..d1958ae 100644 --- a/src/InvocationModel.ts +++ b/src/InvocationModel.ts @@ -82,9 +82,11 @@ export class InvocationModel implements coreTypes.InvocationModel { } else { input = fromRpcTypedData(binding.data); } + if (isTimerTrigger(bindingType)) { input = toCamelCaseValue(input); } + if (isTrigger(bindingType)) { inputs.push(input); } else { diff --git a/src/constants.ts b/src/constants.ts index e911704..52f5a6d 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,6 +1,6 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. -export const version = '4.7.1-preview'; +export const version = '4.7.0'; export const returnBindingKey = '$return'; diff --git a/src/converters/fromRpcTypedData.ts b/src/converters/fromRpcTypedData.ts index 54ce42b..a8ed963 100644 --- a/src/converters/fromRpcTypedData.ts +++ b/src/converters/fromRpcTypedData.ts @@ -2,7 +2,6 @@ // Licensed under the MIT License. import { RpcTypedData } from '@azure/functions-core'; -import { ResourceFactoryResolver } from '@azure/functions-extensions-base'; import { HttpRequest } from '../http/HttpRequest'; import { isDefined } from '../utils/nonNull'; @@ -31,18 +30,8 @@ export function fromRpcTypedData(data: RpcTypedData | null | undefined): unknown return data.collectionDouble.double; } else if (data.collectionSint64 && isDefined(data.collectionSint64.sint64)) { return data.collectionSint64.sint64; - } else if (data.modelBindingData && isDefined(data.modelBindingData.content)) { - try { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call - const resourceFactoryResolver: ResourceFactoryResolver = ResourceFactoryResolver.getInstance(); - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call - return resourceFactoryResolver.createClient(data.modelBindingData.source, data.modelBindingData); - } catch (exception) { - throw new Error( - 'Unable to create client. Please register the extensions library with your function app. ' + - `Error: ${exception instanceof Error ? exception.message : String(exception)}` - ); - } + } else { + return undefined; } } diff --git a/src/converters/toCoreFunctionMetadata.ts b/src/converters/toCoreFunctionMetadata.ts index 99afd93..390c9ea 100644 --- a/src/converters/toCoreFunctionMetadata.ts +++ b/src/converters/toCoreFunctionMetadata.ts @@ -6,19 +6,17 @@ import * as coreTypes from '@azure/functions-core'; import { returnBindingKey } from '../constants'; import { AzFuncSystemError } from '../errors'; import { isTrigger } from '../utils/isTrigger'; -import { workerSystemLog } from '../utils/workerSystemLog'; import { toRpcDuration } from './toRpcDuration'; export function toCoreFunctionMetadata(name: string, options: GenericFunctionOptions): coreTypes.FunctionMetadata { const bindings: Record = {}; const bindingNames: string[] = []; - const trigger = options.trigger; + const trigger = options.trigger; bindings[trigger.name] = { ...trigger, direction: 'in', type: isTrigger(trigger.type) ? trigger.type : trigger.type + 'Trigger', - properties: addSdkBindingsFlag(options.trigger?.sdkBinding, name, trigger.type, trigger.name, false), }; bindingNames.push(trigger.name); @@ -27,7 +25,6 @@ export function toCoreFunctionMetadata(name: string, options: GenericFunctionOpt bindings[input.name] = { ...input, direction: 'in', - properties: addSdkBindingsFlag(input?.sdkBinding, name, input.type, input.name, true), }; bindingNames.push(input.name); } @@ -77,45 +74,3 @@ export function toCoreFunctionMetadata(name: string, options: GenericFunctionOpt return { name, bindings, retryOptions }; } - -/** - * Adds the deferred binding flags to function bindings based on the binding configuration - * @param sdkBindingType Boolean indicating if this is an SDK binding - * @param functionName The name of the function for logging purposes - * @param triggerType The type of the trigger or binding - * @param bindingOrTriggerName The name of the trigger or binding - * @param isBinding Boolean indicating if this is a binding (vs a trigger) - * @returns Object with supportsDeferredBinding property set to 'true' or 'false' - */ -export function addSdkBindingsFlag( - sdkBindingType?: boolean | unknown, - functionName?: string, - triggerType?: string, - bindingOrTriggerName?: string, - isBinding?: boolean -): { [key: string]: string } { - // Ensure that trigger type is valid and supported - if (sdkBindingType !== undefined && sdkBindingType === true) { - const entityType = isBinding ? 'binding' : 'trigger'; - - // Create structured JSON log entry - const logData = { - operation: 'EnableDeferredBinding', - properties: { - functionName: functionName || 'unknown', - entityType: entityType, - triggerType: triggerType || 'unknown', - bindingOrTriggerName: bindingOrTriggerName || 'unknown', - supportsDeferredBinding: true, - }, - message: `Enabled Deferred Binding of type '${triggerType || 'unknown'}' for function '${ - functionName || 'unknown' - }'`, - }; - // Log both the structured data - workerSystemLog('information', JSON.stringify(logData)); - return { supportsDeferredBinding: 'true' }; - } - - return { supportsDeferredBinding: 'false' }; -} diff --git a/src/trigger.ts b/src/trigger.ts index ed2f7f6..f709abb 100644 --- a/src/trigger.ts +++ b/src/trigger.ts @@ -13,7 +13,7 @@ import { HttpTrigger, HttpTriggerOptions, MySqlTrigger, - MySqlTriggerOptions, + MySqlTriggerOptions, ServiceBusQueueTrigger, ServiceBusQueueTriggerOptions, ServiceBusTopicTrigger, diff --git a/test/converters/fromRpcTypedData.test.ts b/test/converters/fromRpcTypedData.test.ts index 45d1545..1ffbbcc 100644 --- a/test/converters/fromRpcTypedData.test.ts +++ b/test/converters/fromRpcTypedData.test.ts @@ -7,9 +7,6 @@ import { fromString } from 'long'; import { HttpRequest } from '../../src'; import { fromRpcTypedData } from '../../src/converters/fromRpcTypedData'; import Long = require('long'); -import { RpcTypedData } from '@azure/functions-core'; -import sinon = require('sinon'); -import { ResourceFactoryResolver } from '@azure/functions-extensions-base'; describe('fromRpcTypedData', () => { it('null', () => { @@ -113,160 +110,3 @@ describe('fromRpcTypedData', () => { expect(result[1].toString()).to.equal('9007199254740992'); }); }); - -describe('fromRpcTypedData - modelBindingData path', () => { - // Use SinonSandbox for automatic cleanup of stubs - let sandbox: sinon.SinonSandbox; - - // Store original ResourceFactoryResolver.getInstance to restore after tests - let originalGetInstance: typeof ResourceFactoryResolver.getInstance; - - beforeEach(() => { - sandbox = sinon.createSandbox(); - // Store original method - originalGetInstance = ResourceFactoryResolver.getInstance.bind(ResourceFactoryResolver); - }); - - afterEach(() => { - // Restore all stubs and original methods - sandbox.restore(); - ResourceFactoryResolver.getInstance = originalGetInstance; - }); - - it('should successfully create a client when modelBindingData is valid', () => { - // Arrange - const mockClient = { - name: 'testClient', - download: () => Promise.resolve({ readableStreamBody: Buffer.from('test') }), - }; - - // Create mock ResourceFactoryResolver - const mockResolver = { - createClient: sinon.stub().returns(mockClient), - }; - - // Replace ResourceFactoryResolver.getInstance with our mock - ResourceFactoryResolver.getInstance = sinon.stub().returns(mockResolver); - - // Create test data - const modelBindingData = { - content: Buffer.from('test-content'), - source: 'blob', - contentType: 'application/octet-stream', - }; - - const data: RpcTypedData = { - modelBindingData: modelBindingData, - }; - - // Act - const result = fromRpcTypedData(data); - - // Assert - sinon.assert.calledWith(mockResolver.createClient, 'blob', modelBindingData); - expect(result).to.equal(mockClient); - }); - - it('should handle modelBindingData with undefined source', () => { - // Arrange - const mockClient = { name: 'testClient' }; - - const mockResolver = { - createClient: sinon.stub().returns(mockClient), - }; - - ResourceFactoryResolver.getInstance = sinon.stub().returns(mockResolver); - - const modelBindingData = { - content: Buffer.from('test-content'), - // No source specified - contentType: 'application/octet-stream', - }; - - const data: RpcTypedData = { - modelBindingData: modelBindingData, - }; - - // Act - const result = fromRpcTypedData(data); - - // Assert - expect(mockResolver.createClient.calledWith(undefined, modelBindingData)).to.be.true; - expect(result).to.equal(mockClient); - }); - - it('should throw enhanced error when ResourceFactoryResolver.createClient throws', () => { - // Arrange - const originalError = new Error('Factory not registered'); - - const mockResolver = { - createClient: sinon.stub().throws(originalError), - }; - - ResourceFactoryResolver.getInstance = sinon.stub().returns(mockResolver); - - const modelBindingData = { - content: Buffer.from('test-content'), - source: 'blob', - contentType: 'application/octet-stream', - }; - - const data: RpcTypedData = { - modelBindingData: modelBindingData, - }; - - // Act & Assert - expect(() => fromRpcTypedData(data)).to.throw( - 'Unable to create client. Please register the extensions library with your function app. ' + - 'Error: Factory not registered' - ); - }); - - it('should throw enhanced error when ResourceFactoryResolver.getInstance throws', () => { - // Arrange - const originalError = new Error('Resolver not initialized'); - - ResourceFactoryResolver.getInstance = sinon.stub().throws(originalError); - - const modelBindingData = { - content: Buffer.from('test-content'), - source: 'blob', - contentType: 'application/octet-stream', - }; - - const data: RpcTypedData = { - modelBindingData: modelBindingData, - }; - - // Act & Assert - expect(() => fromRpcTypedData(data)).to.throw( - 'Unable to create client. Please register the extensions library with your function app. ' + - 'Error: Resolver not initialized' - ); - }); - - it('should handle non-Error exceptions by converting to string', () => { - // Arrange - const mockResolver = { - createClient: sinon.stub().throws('String exception'), // Non-Error exception - }; - - ResourceFactoryResolver.getInstance = sinon.stub().returns(mockResolver); - - const modelBindingData = { - content: Buffer.from('test-content'), - source: 'blob', - contentType: 'application/octet-stream', - }; - - const data: RpcTypedData = { - modelBindingData: modelBindingData, - }; - - // Act & Assert - expect(() => fromRpcTypedData(data)).to.throw( - 'Unable to create client. Please register the extensions library with your function app. ' + - 'Error: Sinon-provided String exception' - ); - }); -}); diff --git a/test/converters/toCoreFunctionMetadata.test.ts b/test/converters/toCoreFunctionMetadata.test.ts index c5cfa6f..764da9d 100644 --- a/test/converters/toCoreFunctionMetadata.test.ts +++ b/test/converters/toCoreFunctionMetadata.test.ts @@ -3,24 +3,17 @@ import 'mocha'; import { expect } from 'chai'; -import * as sinon from 'sinon'; import { output, trigger } from '../../src'; -import { addSdkBindingsFlag, toCoreFunctionMetadata } from '../../src/converters/toCoreFunctionMetadata'; -import * as workerLogModule from '../../src/utils/workerSystemLog'; -import { InvocationContext } from '../../types'; +import { toCoreFunctionMetadata } from '../../src/converters/toCoreFunctionMetadata'; describe('toCoreFunctionMetadata', () => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const handler = (blob: Buffer, context: InvocationContext) => {}; + const handler = () => {}; const expectedHttpTrigger = { authLevel: 'anonymous', methods: ['GET', 'POST'], type: 'httpTrigger', name: 'httpTrigger433d175fc9', direction: 'in', - properties: { - supportsDeferredBinding: 'false', - }, }; const expectedHttpOutput = { type: 'http', name: 'httpOutput9a706511b1', direction: 'out' }; const expectedQueueOutput1 = { @@ -162,372 +155,3 @@ describe('toCoreFunctionMetadata', () => { }); }); }); - -describe('toCoreFunctionMetadata sdk binding tests', () => { - const handler = () => {}; // Mock handler function - - it('should set supportsDeferredBinding to true for blob trigger when sdkBinding is true', () => { - const result = toCoreFunctionMetadata('blobFunction', { - handler, - trigger: { - ...trigger.storageBlob({ - path: 'samples-workitems/{name}', - connection: 'AzureWebJobsStorage', - sdkBinding: true, - }), - }, - return: output.http({}), - }); - - expect(result).to.deep.include({ - name: 'blobFunction', - bindings: { - blobTrigger97e7289e53: { - path: 'samples-workitems/{name}', - connection: 'AzureWebJobsStorage', - sdkBinding: true, - type: 'blobTrigger', - name: 'blobTrigger97e7289e53', - direction: 'in', - properties: { - supportsDeferredBinding: 'true', - }, - }, - $return: { type: 'http', name: 'httpOutput9a706511b1', direction: 'out' }, - }, - retryOptions: undefined, - }); - }); - - it('should set supportsDeferredBinding to false for blob trigger when sdkBinding is false', () => { - const result = toCoreFunctionMetadata('blobFunction', { - handler, - trigger: { - ...trigger.storageBlob({ - path: 'samples-workitems/{name}', - connection: 'AzureWebJobsStorage', - sdkBinding: false, - }), - }, - return: output.http({}), - }); - - expect(result).to.deep.include({ - name: 'blobFunction', - bindings: { - blobTrigger81b6e1578f: { - path: 'samples-workitems/{name}', - connection: 'AzureWebJobsStorage', - sdkBinding: false, - type: 'blobTrigger', - name: 'blobTrigger81b6e1578f', - direction: 'in', - properties: { - supportsDeferredBinding: 'false', - }, - }, - $return: { type: 'http', name: 'httpOutput9a706511b1', direction: 'out' }, - }, - retryOptions: undefined, - }); - }); - - it('should set supportsDeferredBinding to false for blob trigger when sdkBinding is undefined', () => { - const result = toCoreFunctionMetadata('blobFunction', { - handler, - trigger: trigger.storageBlob({ - path: 'samples-workitems/{name}', - connection: 'AzureWebJobsStorage', - }), - return: output.http({}), - }); - - expect(result).to.deep.equal({ - name: 'blobFunction', - bindings: { - blobTrigger44ba8238b9: { - path: 'samples-workitems/{name}', - connection: 'AzureWebJobsStorage', - type: 'blobTrigger', - name: 'blobTrigger44ba8238b9', - direction: 'in', - properties: { - supportsDeferredBinding: 'false', - }, - }, - $return: { type: 'http', name: 'httpOutput9a706511b1', direction: 'out' }, - }, - retryOptions: undefined, - }); - }); - - it('should handle sdk binding for extra inputs', () => { - const result = toCoreFunctionMetadata('funcName', { - handler, - trigger: trigger.http({}), - extraInputs: [ - { - ...trigger.storageBlob({ - path: 'samples-workitems/{name}', - connection: 'AzureWebJobsStorage', - sdkBinding: true, - }), - }, - ], - }); - - expect(result).to.deep.equal({ - name: 'funcName', - bindings: { - httpTrigger433d175fc9: { - authLevel: 'anonymous', - methods: ['GET', 'POST'], - type: 'httpTrigger', - name: 'httpTrigger433d175fc9', - direction: 'in', - properties: { supportsDeferredBinding: 'false' }, - }, - blobTrigger97e7289e53: { - path: 'samples-workitems/{name}', - connection: 'AzureWebJobsStorage', - sdkBinding: true, - type: 'blobTrigger', - name: 'blobTrigger97e7289e53', - direction: 'in', - properties: { supportsDeferredBinding: 'true' }, - }, - }, - retryOptions: undefined, - }); - }); -}); - -describe('toCoreFunctionMetadata error handling', () => { - const handler = () => {}; - - it('should throw error for duplicate binding names', () => { - expect(() => { - toCoreFunctionMetadata('funcName', { - handler, - trigger: trigger.http({}), - extraInputs: [ - { - type: 'httpTrigger', // This creates a duplicate with the trigger - name: 'httpTrigger433d175fc9', // Using same name that would be generated by trigger.http({}) - }, - ], - }); - }).to.throw(/duplicate bindings found/i); - }); - - it('should preserve trigger type for non-trigger inputs', () => { - const result = toCoreFunctionMetadata('funcName', { - handler, - trigger: trigger.http({}), - extraInputs: [ - { - type: 'blob', - name: 'blobInput', - path: 'path/to/blob', - }, - ], - }); - - expect(result.bindings['blobInput']?.type).to.equal('blob'); - }); -}); - -describe('addSdkBindingsFlag - logging tests', () => { - let workerSystemLogStub: sinon.SinonStub; - - beforeEach(() => { - // Create a stub for workerSystemLog to capture calls and prevent actual logging - workerSystemLogStub = sinon.stub(workerLogModule, 'workerSystemLog'); - }); - - afterEach(() => { - // Restore the original function after each test - sinon.restore(); - }); - - describe('when sdkBindingType is true', () => { - it('should return supportsDeferredBinding:true and log with complete parameters', () => { - // Arrange - const sdkBindingType = true; - const functionName = 'testFunction'; - const triggerType = 'http'; - const bindingName = 'req'; - const isBinding = false; - - // Act - const result = addSdkBindingsFlag(sdkBindingType, functionName, triggerType, bindingName, isBinding); - - // Assert - // 1. Verify return value - expect(result).to.deep.equal({ supportsDeferredBinding: 'true' }); - - // 2. Verify log was called - expect(workerSystemLogStub.calledOnce).to.be.true; - expect(workerSystemLogStub.firstCall.args[0]).to.equal('information'); - - // 3. Verify log content - const logArg = JSON.parse(workerSystemLogStub.firstCall.args[1]); - expect(logArg).to.deep.include({ - operation: 'EnableDeferredBinding', - properties: { - functionName: 'testFunction', - entityType: 'trigger', - triggerType: 'http', - bindingOrTriggerName: 'req', - supportsDeferredBinding: true, - }, - }); - expect(logArg.message).to.equal("Enabled Deferred Binding of type 'http' for function 'testFunction'"); - }); - - it('should handle binding types correctly', () => { - // Arrange - const sdkBindingType = true; - const functionName = 'testFunction'; - const triggerType = 'blob'; - const bindingName = 'blobInput'; - const isBinding = true; - - // Act - const result = addSdkBindingsFlag(sdkBindingType, functionName, triggerType, bindingName, isBinding); - - // Assert - expect(result).to.deep.equal({ supportsDeferredBinding: 'true' }); - - // Verify log contains binding instead of trigger - const logArg = JSON.parse(workerSystemLogStub.firstCall.args[1]); - expect(logArg.properties.entityType).to.equal('binding'); - expect(logArg.message).to.not.include('trigger'); - }); - - it('should use default values for undefined parameters', () => { - // Arrange - const sdkBindingType = true; - // All other parameters undefined - - // Act - const result = addSdkBindingsFlag(sdkBindingType); - - // Assert - expect(result).to.deep.equal({ supportsDeferredBinding: 'true' }); - - // Verify log contains 'unknown' placeholders - const logArg = JSON.parse(workerSystemLogStub.firstCall.args[1]); - expect(logArg.properties.functionName).to.equal('unknown'); - expect(logArg.properties.triggerType).to.equal('unknown'); - expect(logArg.properties.bindingOrTriggerName).to.equal('unknown'); - expect(logArg.properties.entityType).to.equal('trigger'); // Default is trigger - expect(logArg.message).to.equal("Enabled Deferred Binding of type 'unknown' for function 'unknown'"); - }); - - it('should handle mixed undefined parameters correctly', () => { - // Arrange - const sdkBindingType = true; - const functionName = 'testFunction'; - // Other parameters undefined - - // Act - const result = addSdkBindingsFlag(sdkBindingType, functionName); - - // Assert - expect(result).to.deep.equal({ supportsDeferredBinding: 'true' }); - - // Verify log contains the provided function name but other 'unknown' placeholders - const logArg = JSON.parse(workerSystemLogStub.firstCall.args[1]); - expect(logArg.properties.functionName).to.equal('testFunction'); - expect(logArg.properties.triggerType).to.equal('unknown'); - expect(logArg.message).to.equal("Enabled Deferred Binding of type 'unknown' for function 'testFunction'"); - }); - }); - - describe('when sdkBindingType is not true', () => { - it('should return supportsDeferredBinding:false when sdkBindingType is false', () => { - // Arrange - const sdkBindingType = false; - const functionName = 'testFunction'; - - // Act - const result = addSdkBindingsFlag(sdkBindingType, functionName); - - // Assert - expect(result).to.deep.equal({ supportsDeferredBinding: 'false' }); - - // Verify no logging occurred - expect(workerSystemLogStub.called).to.be.false; - }); - - it('should return supportsDeferredBinding:false when sdkBindingType is undefined', () => { - // Act - const result = addSdkBindingsFlag(); - - // Assert - expect(result).to.deep.equal({ supportsDeferredBinding: 'false' }); - - // Verify no logging occurred - expect(workerSystemLogStub.called).to.be.false; - }); - - it('should return supportsDeferredBinding:false for any non-true sdkBindingType', () => { - // Test with various non-true values - const nonTrueValues = [null, 0, '', 'true', {}, [], NaN]; - - // Test each value - for (const testValue of nonTrueValues) { - // Act - const result = addSdkBindingsFlag(testValue); - - // Assert - expect(result).to.deep.equal({ supportsDeferredBinding: 'false' }); - expect(workerSystemLogStub.called).to.be.false; - - // Reset the stub for the next iteration - workerSystemLogStub.reset(); - } - }); - }); - - describe('log structure validation', () => { - it('should produce correctly formatted JSON in logs', () => { - // Arrange - const sdkBindingType = true; - const functionName = 'testFunction'; - const triggerType = 'http'; - const bindingName = 'req'; - const isBinding = false; - - // Act - addSdkBindingsFlag(sdkBindingType, functionName, triggerType, bindingName, isBinding); - - // Assert - expect(workerSystemLogStub.calledOnce).to.be.true; - - // Get the logged JSON string and parse it to verify it's valid JSON - const logString = workerSystemLogStub.firstCall.args[1]; - - // This should not throw if the JSON is valid - const logObject = JSON.parse(logString); - - // Verify required fields - expect(logObject).to.have.property('operation'); - expect(logObject).to.have.property('properties'); - expect(logObject).to.have.property('message'); - - // Verify properties structure - expect(logObject.properties).to.have.property('functionName'); - expect(logObject.properties).to.have.property('entityType'); - expect(logObject.properties).to.have.property('triggerType'); - expect(logObject.properties).to.have.property('bindingOrTriggerName'); - expect(logObject.properties).to.have.property('supportsDeferredBinding'); - - // Verify data types - expect(typeof logObject.operation).to.equal('string'); - expect(typeof logObject.message).to.equal('string'); - expect(typeof logObject.properties.supportsDeferredBinding).to.equal('boolean'); - }); - }); -}); diff --git a/types-core/index.d.ts b/types-core/index.d.ts index d86c3c4..2fb3506 100644 --- a/types-core/index.d.ts +++ b/types-core/index.d.ts @@ -391,18 +391,12 @@ declare module '@azure/functions-core' { direction?: RpcBindingDirection | null; dataType?: RpcBindingDataType | null; - - properties?: RpcBindingProperties | null; } type RpcBindingDirection = 'in' | 'out' | 'inout'; type RpcBindingDataType = 'undefined' | 'string' | 'binary' | 'stream'; - interface RpcBindingProperties { - supportsDeferredBinding?: 'true' | 'false' | null; - } - interface RpcRetryOptions { maxRetryCount?: number | null; @@ -439,15 +433,6 @@ declare module '@azure/functions-core' { collectionDouble?: RpcCollectionDouble | null; collectionSint64?: RpcCollectionSInt64 | null; - - modelBindingData?: ModelBindingData | null; - } - - export interface ModelBindingData { - content?: Buffer | null; - contentType?: string | null; - source?: string | null; - version?: string | null; } interface RpcCollectionSInt64 { diff --git a/types/InvocationContext.d.ts b/types/InvocationContext.d.ts index c991557..8815bdf 100644 --- a/types/InvocationContext.d.ts +++ b/types/InvocationContext.d.ts @@ -129,7 +129,7 @@ export interface InvocationContextExtraInputs { * @input the configuration object for this SQL input */ get(input: SqlInput): unknown; - + /** * Get a secondary MySql items input for this invocation * @input the configuration object for this MySql input @@ -223,7 +223,7 @@ export interface InvocationContextExtraOutputs { * @message the output event(s) value */ set(output: EventGridOutput, events: EventGridPartialEvent | EventGridPartialEvent[]): void; - + /** * Set a secondary MySql items output for this invocation * @output the configuration object for this MySql output diff --git a/types/input.d.ts b/types/input.d.ts index b264405..52d8c68 100644 --- a/types/input.d.ts +++ b/types/input.d.ts @@ -4,10 +4,10 @@ import { CosmosDBInput, CosmosDBInputOptions } from './cosmosDB'; import { GenericInputOptions } from './generic'; import { FunctionInput } from './index'; -import { MySqlInput, MySqlInputOptions } from './mySql'; import { SqlInput, SqlInputOptions } from './sql'; import { StorageBlobInput, StorageBlobInputOptions } from './storage'; import { TableInput, TableInputOptions } from './table'; +import { MySqlInput, MySqlInputOptions } from './mySql'; import { WebPubSubConnectionInput, WebPubSubConnectionInputOptions, diff --git a/types/output.d.ts b/types/output.d.ts index e9e08d9..b9d9d83 100644 --- a/types/output.d.ts +++ b/types/output.d.ts @@ -7,7 +7,6 @@ import { EventHubOutput, EventHubOutputOptions } from './eventHub'; import { GenericOutputOptions } from './generic'; import { HttpOutput, HttpOutputOptions } from './http'; import { FunctionOutput } from './index'; -import { MySqlOutput, MySqlOutputOptions } from './mySql'; import { ServiceBusQueueOutput, ServiceBusQueueOutputOptions, @@ -17,6 +16,7 @@ import { import { SqlOutput, SqlOutputOptions } from './sql'; import { StorageBlobOutput, StorageBlobOutputOptions, StorageQueueOutput, StorageQueueOutputOptions } from './storage'; import { TableOutput, TableOutputOptions } from './table'; +import { MySqlOutput, MySqlOutputOptions } from './mySql'; import { WebPubSubOutput, WebPubSubOutputOptions } from './webpubsub'; /** diff --git a/types/storage.d.ts b/types/storage.d.ts index e59c258..2ed33ba 100644 --- a/types/storage.d.ts +++ b/types/storage.d.ts @@ -29,11 +29,6 @@ export interface StorageBlobOptions { * An app setting (or environment variable) with the storage connection string to be used by this blob input or output */ connection: string; - - /** - * Whether to use sdk binding for this blob operation. - * */ - sdkBinding?: boolean; } export interface StorageQueueOptions {