This repository was archived by the owner on Dec 9, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 160
Feat: Invoke Plugin #188
Merged
Merged
Feat: Invoke Plugin #188
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or 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
This file contains hidden or 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
This file contains hidden or 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| import { MockFactory } from "../../test/mockFactory"; | ||
| import { invokeHook } from "../../test/utils"; | ||
| import mockFs from "mock-fs"; | ||
| import { AzureInvoke } from "./azureInvoke"; | ||
| jest.mock("../../services/functionAppService"); | ||
| jest.mock("../../services/resourceService"); | ||
| jest.mock("../../services/invokeService"); | ||
| import { InvokeService } from "../../services/invokeService"; | ||
|
|
||
| describe("Azure Invoke Plugin", () => { | ||
| const fileContent = JSON.stringify({ | ||
| name: "Azure-Test", | ||
| }); | ||
| afterEach(() => { | ||
| jest.resetAllMocks(); | ||
| }) | ||
|
|
||
| beforeAll(() => { | ||
| mockFs({ | ||
| "testFile.json": fileContent, | ||
| }, { createCwd: true, createTmp: true }); | ||
| }); | ||
| afterAll(() => { | ||
| mockFs.restore(); | ||
| }); | ||
|
|
||
| it("calls invoke hook", async () => { | ||
| const expectedResult = { data: "test" }; | ||
| const invoke = jest.fn(() => expectedResult); | ||
| InvokeService.prototype.invoke = invoke as any; | ||
|
|
||
| const sls = MockFactory.createTestServerless(); | ||
| const options = MockFactory.createTestServerlessOptions(); | ||
| options["function"] = "testApp"; | ||
| options["data"] = "{\"name\": \"AzureTest\"}"; | ||
| options["method"] = "GET"; | ||
|
|
||
| const plugin = new AzureInvoke(sls, options); | ||
| await invokeHook(plugin, "invoke:invoke"); | ||
| expect(invoke).toBeCalledWith(options["method"], options["function"], options["data"]); | ||
| expect(sls.cli.log).toBeCalledWith(JSON.stringify(expectedResult.data)); | ||
| }); | ||
|
|
||
| it("calls the invoke hook with file path", async () => { | ||
| const expectedResult = { data: "test" }; | ||
| const invoke = jest.fn(() => expectedResult); | ||
| InvokeService.prototype.invoke = invoke as any; | ||
| const sls = MockFactory.createTestServerless(); | ||
| const options = MockFactory.createTestServerlessOptions(); | ||
| options["function"] = "testApp"; | ||
| options["path"] = "testFile.json"; | ||
| options["method"] = "GET"; | ||
| const plugin = new AzureInvoke(sls, options); | ||
| await invokeHook(plugin, "invoke:invoke"); | ||
| expect(invoke).toBeCalledWith(options["method"], options["function"], fileContent); | ||
| expect(sls.cli.log).toBeCalledWith(JSON.stringify(expectedResult.data)); | ||
|
|
||
| }); | ||
|
|
||
| it("calls the invoke hook with file path", async () => { | ||
| const invoke = jest.fn(); | ||
| InvokeService.prototype.invoke = invoke; | ||
| const sls = MockFactory.createTestServerless(); | ||
| const options = MockFactory.createTestServerlessOptions(); | ||
| options["function"] = "testApp"; | ||
| options["path"] = "notExist.json"; | ||
| options["method"] = "GET"; | ||
| expect(() => new AzureInvoke(sls, options)).toThrow(); | ||
| }); | ||
|
|
||
| it("Function invoked with no data", async () => { | ||
| const expectedResult = { data: "test" }; | ||
| const invoke = jest.fn(() => expectedResult); | ||
| InvokeService.prototype.invoke = invoke as any; | ||
| const sls = MockFactory.createTestServerless(); | ||
| const options = MockFactory.createTestServerlessOptions(); | ||
| options["function"] = "testApp"; | ||
| options["method"] = "GET"; | ||
| const plugin = new AzureInvoke(sls, options); | ||
| await invokeHook(plugin, "invoke:invoke"); | ||
| expect(invoke).toBeCalledWith(options["method"], options["function"], undefined); | ||
| expect(sls.cli.log).toBeCalledWith(JSON.stringify(expectedResult.data)); | ||
| }); | ||
|
|
||
| it("The invoke function fails when no function name is passed", async () => { | ||
| const invoke = jest.fn(); | ||
| InvokeService.prototype.invoke = invoke; | ||
| const sls = MockFactory.createTestServerless(); | ||
| const options = MockFactory.createTestServerlessOptions(); | ||
| options["function"] = null; | ||
| options["data"] = "{\"name\": \"AzureTest\"}"; | ||
| options["method"] = "GET"; | ||
| const plugin = new AzureInvoke(sls, options); | ||
| await invokeHook(plugin, "invoke:invoke"); | ||
| expect(invoke).not.toBeCalled(); | ||
| }); | ||
| }); |
This file contains hidden or 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,41 +1,72 @@ | ||
| import { isAbsolute, join } from "path"; | ||
| import Serverless from "serverless"; | ||
| import { join, isAbsolute } from "path"; | ||
| import AzureProvider from "../../provider/azureProvider"; | ||
| import { InvokeService } from "../../services/invokeService"; | ||
| import fs from "fs"; | ||
| import { ServerlessCommandMap } from "../../models/serverless"; | ||
|
|
||
| export class AzureInvoke { | ||
| public hooks: { [eventName: string]: Promise<any> }; | ||
| private provider: AzureProvider; | ||
|
|
||
| private commands: ServerlessCommandMap; | ||
| private invokeService: InvokeService; | ||
| public constructor(private serverless: Serverless, private options: Serverless.Options) { | ||
| this.provider = (this.serverless.getProvider("azure") as any) as AzureProvider; | ||
| const path = this.options["path"]; | ||
|
|
||
| if (path) { | ||
| const absolutePath = isAbsolute(path) | ||
| ? path | ||
| : join(this.serverless.config.servicePath, path); | ||
| this.serverless.cli.log(this.serverless.config.servicePath); | ||
| this.serverless.cli.log(path); | ||
|
|
||
| if (!this.serverless.utils.fileExistsSync(absolutePath)) { | ||
| if (!fs.existsSync(absolutePath)) { | ||
| throw new Error("The file you provided does not exist."); | ||
| } | ||
| this.options["data"] = this.serverless.utils.readFileSync(absolutePath); | ||
| this.options["data"] = fs.readFileSync(absolutePath).toString(); | ||
neerajmandal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| this.commands = { | ||
| invoke: { | ||
| usage: "Invoke command", | ||
| lifecycleEvents: ["invoke"], | ||
| options: { | ||
| function: { | ||
| usage: "Function to call", | ||
| shortcut: "f", | ||
| }, | ||
| path: { | ||
neerajmandal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| usage: "Path to file to put in body", | ||
| shortcut: "p" | ||
| }, | ||
| data: { | ||
| usage: "Data string for body of request", | ||
| shortcut: "d" | ||
| }, | ||
| method: { | ||
| usage: "HTTP method (Default is GET)", | ||
| shortcut: "m" | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| this.hooks = { | ||
| "before:invoke:invoke": this.provider.getAdminKey.bind(this), | ||
| "invoke:invoke": this.invoke.bind(this) | ||
| }; | ||
| } | ||
|
|
||
| private async invoke() { | ||
| const func = this.options.function; | ||
| const functionObject = this.serverless.service.getFunction(func); | ||
| const eventType = Object.keys(functionObject["events"][0])[0]; | ||
|
|
||
| if (!this.options["data"]) { | ||
| this.options["data"] = {}; | ||
| const functionName = this.options["function"]; | ||
| const data = this.options["data"]; | ||
| const method = this.options["method"] || "GET"; | ||
| if (!functionName) { | ||
| this.serverless.cli.log("Need to provide a name of function to invoke"); | ||
| return; | ||
| } | ||
|
|
||
| return this.provider.invoke(func, eventType, this.options["data"]); | ||
| this.invokeService = new InvokeService(this.serverless, this.options); | ||
| const response = await this.invokeService.invoke(method, functionName, data); | ||
| if(response){ | ||
| this.serverless.cli.log(JSON.stringify(response.data)); | ||
| } | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or 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
This file contains hidden or 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
This file contains hidden or 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
This file contains hidden or 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| import axios from "axios"; | ||
| import MockAdapter from "axios-mock-adapter"; | ||
| import { MockFactory } from "../test/mockFactory"; | ||
| import { InvokeService } from "./invokeService"; | ||
| jest.mock("@azure/arm-appservice") | ||
| jest.mock("@azure/arm-resources") | ||
| jest.mock("./functionAppService") | ||
| import { FunctionAppService } from "./functionAppService"; | ||
|
|
||
| describe("Invoke Service ", () => { | ||
| const app = MockFactory.createTestSite(); | ||
| const expectedSite = MockFactory.createTestSite(); | ||
| const testData = "test-data"; | ||
| const testResult = "test-data"; | ||
| const authKey = "authKey"; | ||
| const baseUrl = "https://management.azure.com" | ||
| const masterKeyUrl = `https://${app.defaultHostName}/admin/host/systemkeys/_master`; | ||
| const authKeyUrl = `${baseUrl}${app.id}/functions/admin/token?api-version=2016-08-01`; | ||
| let urlPOST = `http://${app.defaultHostName}/api/hello`; | ||
| let urlGET = `http://${app.defaultHostName}/api/hello?name%3D${testData}`; | ||
| let masterKey: string; | ||
|
|
||
| beforeAll(() => { | ||
| const axiosMock = new MockAdapter(axios); | ||
| // Master Key | ||
| axiosMock.onGet(masterKeyUrl).reply(200, { value: masterKey }); | ||
| // Auth Key | ||
| axiosMock.onGet(authKeyUrl).reply(200, authKey); | ||
| //Mock url for GET | ||
| axiosMock.onGet(urlGET).reply(200, testResult); | ||
| //Mock url for POST | ||
| axiosMock.onPost(urlPOST).reply(200, testResult); | ||
| }); | ||
|
|
||
| beforeEach(() => { | ||
| FunctionAppService.prototype.getMasterKey = jest.fn(); | ||
| FunctionAppService.prototype.get = jest.fn(() => Promise.resolve(expectedSite)); | ||
| }); | ||
|
|
||
neerajmandal marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| it("Invokes a function with GET request", async () => { | ||
| const sls = MockFactory.createTestServerless(); | ||
| const options = MockFactory.createTestServerlessOptions(); | ||
| const expectedResult = {url: `${app.defaultHostName}/api/hello`}; | ||
| const httpConfig = jest.fn(() => expectedResult); | ||
|
|
||
| FunctionAppService.prototype.getFunctionHttpTriggerConfig = httpConfig as any; | ||
|
|
||
| options["function"] = "hello"; | ||
| options["data"] = `{"name": "${testData}"}`; | ||
| options["method"] = "GET"; | ||
|
|
||
| const service = new InvokeService(sls, options); | ||
| const response = await service.invoke(options["method"], options["function"], options["data"]); | ||
| expect(JSON.stringify(response.data)).toEqual(JSON.stringify(testResult)); | ||
| }); | ||
|
|
||
| it("Invokes a function with POST request", async () => { | ||
| const sls = MockFactory.createTestServerless(); | ||
| const options = MockFactory.createTestServerlessOptions(); | ||
| const expectedResult = {url: `${app.defaultHostName}/api/hello`}; | ||
| const httpConfig = jest.fn(() => expectedResult); | ||
| FunctionAppService.prototype.getFunctionHttpTriggerConfig = httpConfig as any; | ||
|
|
||
| options["function"] = "hello"; | ||
| options["data"] = `{"name": "${testData}"}`; | ||
| options["method"] = "POST"; | ||
|
|
||
| const service = new InvokeService(sls, options); | ||
| const response = await service.invoke(options["method"], options["function"], options["data"]); | ||
| expect(JSON.stringify(response.data)).toEqual(JSON.stringify(testResult)); | ||
| }); | ||
|
|
||
| }); | ||
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.