Skip to content
This repository has been archived by the owner on Dec 9, 2024. It is now read-only.

feat: Path from x-azure-settings is included in function.json #212

Merged
merged 2 commits into from
Jul 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/models/serverless.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,23 @@ export interface ServerlessAzureConfig {
functions: any;
}

export interface ServerlessAzureFunctionConfig {
handler: string;
events: ServerlessAzureFunctionBindingConfig[];
}

export interface ServerlessAzureFunctionBindingConfig {
http?: boolean;
"x-azure-settings": ServerlessExtraAzureSettingsConfig;
}

export interface ServerlessExtraAzureSettingsConfig {
direction?: string;
route?: string;
name?: string;
authLevel?: string;
}

export interface ServerlessCommand {
usage: string;
lifecycleEvents: string[];
Expand All @@ -73,6 +90,7 @@ export interface ServerlessCommand {
export interface ServerlessCommandMap {
[command: string]: ServerlessCommand;
}

export interface ServerlessAzureOptions extends Serverless.Options {
resourceGroup?: string;
}
2 changes: 1 addition & 1 deletion src/plugins/package/azurePackagePlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export class AzurePackagePlugin extends AzureBasePlugin {
"after:package:finalize": this.finalize.bind(this),
};

this.packageService = new PackageService(this.serverless);
this.packageService = new PackageService(this.serverless, this.options);
}

private async setupProviderConfiguration(): Promise<void> {
Expand Down
4 changes: 2 additions & 2 deletions src/services/baseService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import axios from "axios";
import fs from "fs";
import request from "request";
import Serverless from "serverless";
import { ServerlessAzureOptions } from "../models/serverless";
import { ServerlessAzureOptions, ServerlessAzureFunctionConfig } from "../models/serverless";
import { StorageAccountResource } from "../armTemplates/resources/storageAccount";
import { configConstants } from "../config";
import { DeploymentConfig, ServerlessAzureConfig } from "../models/serverless";
Expand Down Expand Up @@ -181,7 +181,7 @@ export abstract class BaseService {
/**
* Get function objects
*/
protected slsFunctions() {
protected slsFunctions(): { [functionName: string]: ServerlessAzureFunctionConfig } {
return this.serverless.service["functions"];
}

Expand Down
6 changes: 3 additions & 3 deletions src/services/offlineService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ export class OfflineService extends BaseService {
}
}),
}

public constructor(serverless: Serverless, options: Serverless.Options) {
super(serverless, options, false);
this.packageService = new PackageService(serverless);
this.packageService = new PackageService(serverless, options);
}

public async build() {
Expand Down Expand Up @@ -55,4 +55,4 @@ export class OfflineService extends BaseService {
this.log("Make sure you have Azure Functions Core Tools installed");
this.log("If not installed run 'npm i azure-functions-core-tools -g")
}
}
}
49 changes: 38 additions & 11 deletions src/services/packageService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ import { FunctionMetadata } from "../shared/utils";
describe("Package Service", () => {
let sls: Serverless;
let packageService: PackageService;
const functionRoute = "myRoute";

beforeEach(() => {
sls = MockFactory.createTestServerless();
sls.config.servicePath = process.cwd();

packageService = new PackageService(sls);
sls.service["functions"] = {
hello: MockFactory.createTestAzureFunctionConfig(functionRoute),
}
packageService = new PackageService(sls, MockFactory.createTestServerlessOptions());
});

afterEach(() => {
Expand Down Expand Up @@ -71,17 +74,34 @@ describe("Package Service", () => {
});

it("createBinding writes function.json files into function folder", async () => {
const functionName = "helloWorld";
const functionName = "hello";
const functionMetadata: FunctionMetadata = {
entryPoint: "handler",
handlerPath: "src/handlers/hello.js",
handlerPath: "src/handlers/hello",
params: {
functionsJson: {},
functionsJson: {
bindings: [
MockFactory.createTestHttpBinding("out"),
MockFactory.createTestHttpBinding("in"),
]
}
},
};

const expectedFolderPath = path.join(sls.config.servicePath, functionName);
const expectedFilePath = path.join(expectedFolderPath, "function.json");
const expectedFunctionJson = {
entryPoint: "handler",
scriptFile: "src/handlers/hello",
bindings: [
MockFactory.createTestHttpBinding("out"),
{
...MockFactory.createTestHttpBinding("in"),
route: functionRoute
}
]

}

mockFs({});

Expand All @@ -91,27 +111,34 @@ describe("Package Service", () => {
await packageService.createBinding(functionName, functionMetadata);

expect(mkdirSpy).toBeCalledWith(expectedFolderPath);
expect(writeFileSpy).toBeCalledWith(expectedFilePath, expect.any(String));
const call = writeFileSpy.mock.calls[0];
expect(call[0]).toEqual(expectedFilePath);
expect(JSON.parse(call[1])).toEqual(expectedFunctionJson);

mkdirSpy.mockRestore();
writeFileSpy.mockRestore();
});

it("createBinding does not need to create directory if function folder already exists", async () => {
const functionName = "helloWorld";
const functionName = "hello";
const functionMetadata: FunctionMetadata = {
entryPoint: "handler",
handlerPath: "src/handlers/hello.js",
handlerPath: "src/handlers/hello",
params: {
functionsJson: {},
functionsJson: {
bindings: [
MockFactory.createTestHttpBinding("out"),
MockFactory.createTestHttpBinding("in"),
]
},
},
};

const expectedFolderPath = path.join(sls.config.servicePath, functionName);
const expectedFilePath = path.join(expectedFolderPath, "function.json");

mockFs({
"helloWorld": {
"hello": {
"index.js": "contents",
},
});
Expand Down Expand Up @@ -162,4 +189,4 @@ describe("Package Service", () => {
mkDirSpy.mockRestore();
copyFileSpy.mockRestore();
});
});
});
18 changes: 15 additions & 3 deletions src/services/packageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import fs from "fs";
import path from "path";
import Serverless from "serverless";
import { FunctionMetadata, Utils } from "../shared/utils";
import { BaseService } from "./baseService";

/**
* Adds service packing support
*/
export class PackageService {
public constructor(private serverless: Serverless) {
export class PackageService extends BaseService {
public constructor(serverless: Serverless, options: Serverless.Options) {
super(serverless, options, false);
}

/**
Expand Down Expand Up @@ -82,13 +84,23 @@ export class PackageService {
const functionJSON = functionMetadata.params.functionsJson;
functionJSON.entryPoint = functionMetadata.entryPoint;
functionJSON.scriptFile = functionMetadata.handlerPath;
const functionObject = this.slsFunctions()[functionName];
const bindingAzureSettings = Utils.getIncomingBindingConfig(functionObject)["x-azure-settings"];
if (bindingAzureSettings.route) {
// Find incoming binding within functionJSON and set the route
const index = (functionJSON.bindings as any[])
.findIndex((binding) => (!binding.direction || binding.direction === "in"));
functionJSON.bindings[index].route = bindingAzureSettings.route;
}

const functionDirPath = path.join(this.serverless.config.servicePath, functionName);
if (!fs.existsSync(functionDirPath)) {
fs.mkdirSync(functionDirPath);
}

fs.writeFileSync(path.join(functionDirPath, "function.json"), JSON.stringify(functionJSON, null, 2));
const functionJsonString = JSON.stringify(functionJSON, null, 2);

fs.writeFileSync(path.join(functionDirPath, "function.json"), functionJsonString);

return Promise.resolve();
}
Expand Down
21 changes: 20 additions & 1 deletion src/shared/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,12 +162,31 @@ describe("utils", () => {
const actual = Utils.getNormalizedRegionName(expected);
expect(actual).toEqual(expected);
});

it("should get a timestamp from a name", () => {
expect(Utils.getTimestampFromName("myDeployment-t12345")).toEqual("12345");
expect(Utils.getTimestampFromName("myDeployment-t678987645")).toEqual("678987645");
expect(Utils.getTimestampFromName("-t12345")).toEqual("12345");

expect(Utils.getTimestampFromName("myDeployment-t")).toEqual(null);
expect(Utils.getTimestampFromName("")).toEqual(null);
})
});

it("should get incoming binding", () => {
expect(Utils.getIncomingBindingConfig(MockFactory.createTestAzureFunctionConfig())).toEqual(
{
http: true,
"x-azure-settings": MockFactory.createTestHttpBinding("in"),
}
);
});

it("should get outgoing binding", () => {
expect(Utils.getOutgoingBinding(MockFactory.createTestAzureFunctionConfig())).toEqual(
{
http: true,
"x-azure-settings": MockFactory.createTestHttpBinding("out"),
}
);
});
});
15 changes: 15 additions & 0 deletions src/shared/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Serverless from "serverless";
import { BindingUtils } from "./bindings";
import { constants } from "./constants";
import { Guard } from "./guard";
import { ServerlessAzureFunctionConfig } from "../models/serverless";

export interface FunctionMetadata {
entryPoint: any;
Expand Down Expand Up @@ -212,4 +213,18 @@ export class Utils {
}
return match[1];
}

public static getIncomingBindingConfig(functionConfig: ServerlessAzureFunctionConfig) {
return functionConfig.events.find((event) => {
const settings = event["x-azure-settings"]
return settings && (!settings.direction || settings.direction === "in");
});
}

public static getOutgoingBinding(functionConfig: ServerlessAzureFunctionConfig) {
return functionConfig.events.find((event) => {
const settings = event["x-azure-settings"]
return settings && settings.direction === "out";
});
}
}
21 changes: 19 additions & 2 deletions src/test/mockFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { ApiCorsPolicy, ApiManagementConfig } from "../models/apiManagement";
import { ArmDeployment, ArmResourceTemplate } from "../models/armTemplates";
import { ServicePrincipalEnvVariables } from "../models/azureProvider";
import { Logger } from "../models/generic";
import { ServerlessAzureConfig, ServerlessAzureProvider } from "../models/serverless";
import { ServerlessAzureConfig, ServerlessAzureProvider, ServerlessAzureFunctionConfig } from "../models/serverless";

function getAttribute(object: any, prop: string, defaultValue: any): any {
if (object && object[prop]) {
Expand Down Expand Up @@ -394,18 +394,35 @@ export class MockFactory {
return bindings;
}

public static createTestAzureFunctionConfig(route?: string): ServerlessAzureFunctionConfig {
return {
events: [
{
http: true,
"x-azure-settings": MockFactory.createTestHttpBinding("in", route),
},
{
http: true,
"x-azure-settings": MockFactory.createTestHttpBinding("out"),
}
],
handler: "handler.js",
}
}

public static createTestBinding() {
// Only supporting HTTP for now, could support others
return MockFactory.createTestHttpBinding();
}

public static createTestHttpBinding(direction: string = "in") {
public static createTestHttpBinding(direction: string = "in", route?: string) {
if (direction === "in") {
return {
authLevel: "anonymous",
type: "httpTrigger",
direction,
name: "req",
route,
}
} else {
return {
Expand Down