Skip to content

Commit

Permalink
Added a new template for invoking a lambda with a payload, included V…
Browse files Browse the repository at this point in the history
…ue.js for clientside parts of project, add an ExtensionUtilities class, and refactored some of the client portions.
  • Loading branch information
allenmichael authored and stevejroberts committed Jul 20, 2018
1 parent 0f436f4 commit 4325484
Show file tree
Hide file tree
Showing 14 changed files with 399 additions and 24 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
out
node_modules
.DS_Store
.vscode-test/
*.vsix
32 changes: 32 additions & 0 deletions build-scripts/bundleDeps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
'use strict';
/*
This script is called from npm run compile.
It adds clientside libraries to ./media/libs.
*/
const fs = require('fs-extra');
const path = require('path');
const _ = require('lodash');

const DEPS = [{
name: 'vue.min.js',
path: 'vue/dist/vue.min.js'
}
];
const workingDir = path.join(__dirname, '..');
const nodeModulesDir = path.join(workingDir, 'node_modules');
const libraryDir = path.join(workingDir, 'media', 'libs');
(async () => {
const work = [];
_.forEach(DEPS, (dep) => {
const depPath = path.join(nodeModulesDir, dep.path);
console.log(`Copying ${depPath} to ${libraryDir}`);
work.push(fs.copy(depPath, path.join(libraryDir, dep.name)));
});
try {
await Promise.all(work);
console.log('Successfully copied all clientside dependencies.');
} catch (e) {
console.error('Error when copying clientside dependencies.');
console.error(e);
}
})();
44 changes: 44 additions & 0 deletions media/js/sampleRequests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
console.log('Loaded!');
(function () {
const vscode = acquireVsCodeApi();
const app = new Vue({
el: '#app',
data: {
selectedSampleRequest: {},
sampleText: ""
},
mounted() {
this.$nextTick(function () {
window.addEventListener('message', this.handleMessageReceived);
})
},
methods: {
newSelection: function () {
vscode.postMessage({
command: 'sampleRequestSelected',
value: this.selectedSampleRequest
})
},
handleMessageReceived: function (e) {
const message = event.data;
console.log(message.command);
console.log(message.sample);
switch (message.command) {
case 'loadedSample':
this.loadSampleText(message.sample);
break;
}
},
loadSampleText: function (txt) {
this.sampleText = txt;
},
sendInput: function() {
console.log(this.sampleText);
vscode.postMessage({
command: 'invokeLambda',
value: this.sampleText
})
}
}
});
})();
6 changes: 6 additions & 0 deletions media/libs/vue.min.js

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@
},
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "tsc -p ./",
"bundleDeps": "node ./build-scripts/bundleDeps.js",
"compile": "tsc -p ./ && npm run bundleDeps",
"watch": "tsc -watch -p ./",
"postinstall": "node ./node_modules/vscode/bin/install",
"test": "npm run compile && node ./node_modules/vscode/bin/test"
Expand All @@ -156,6 +157,7 @@
"aws-sdk": "^2.227.1",
"fs-extra": "^6.0.1",
"lodash": "^4.17.10",
"npm": "^6.1.0"
"npm": "^6.1.0",
"vue": "^2.5.16"
}
}
107 changes: 107 additions & 0 deletions resources/vs-lambda-sample-request-manifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?xml version="1.0" encoding="utf-8"?>
<requests>
<request category="AWS">
<name>API Gateway AWS Proxy</name>
<filename>APIGatewayAWSProxy.json</filename>
</request>
<request category="AWS">
<name>API Gateway Authorizer</name>
<filename>APIGatewayAuthorizer.json</filename>
</request>
<request category="AWS">
<name>Rekognition S3 Request</name>
<filename>RekognitionS3Request.json</filename>
</request>
<request category="AWS">
<name>CodeCommit</name>
<filename>CodeCommit.json</filename>
</request>
<request category="AWS">
<name>CloudFormation Create Request</name>
<filename>CloudFormationCreateRequest.json</filename>
</request>
<request category="AWS">
<name>SES Email Receiving</name>
<filename>SESEmailReceiving.json</filename>
</request>
<request category="AWS">
<name>Scheduled Event</name>
<filename>ScheduledEvent.json</filename>
</request>
<request category="AWS">
<name>CloudWatch Logs</name>
<filename>CloudWatchLogs.json</filename>
</request>
<request category="AWS">
<name>SNS</name>
<filename>SNS.json</filename>
</request>
<request category="AWS">
<name>DynamoDB Update</name>
<filename>DynamoDBUpdate.json</filename>
</request>
<request category="AWS">
<name>Cognito Sync Trigger</name>
<filename>CognitoSyncTrigger.json</filename>
</request>
<request category="AWS">
<name>Kinesis</name>
<filename>Kinesis.json</filename>
</request>
<request category="AWS">
<name>Kinesis Firehose</name>
<filename>KinesisFirehose.json</filename>
</request>
<request category="AWS">
<name>S3 Put</name>
<filename>S3Put.json</filename>
</request>
<request category="AWS">
<name>S3 Delete</name>
<filename>S3Delete.json</filename>
</request>
<request category="AWS">
<name>AWS Config Periodic Rule</name>
<filename>AWSConfigPeriodicRule.json</filename>
</request>
<request category="AWS">
<name>AWS Config Change Trigger Rule</name>
<filename>AWSConfigChangeTriggerRule.json</filename>
</request>
<request category="Lex">
<name>Lex Book Car</name>
<filename>LexBookCar.json</filename>
</request>
<request category="Lex">
<name>Lex Book Hotel</name>
<filename>LexBookHotel.json</filename>
</request>
<request category="Lex">
<name>Order Flowers</name>
<filename>OrderFlowers.json</filename>
</request>
<request category="Alexa">
<name>Alexa Start Session</name>
<filename>AlexaStartSession.json</filename>
</request>
<request category="Alexa">
<name>Alexa End Session</name>
<filename>AlexaEndSession.json</filename>
</request>
<request category="Alexa">
<name>Alexa Smart Home - Control</name>
<filename>AlexaSmartHomeControl.json</filename>
</request>
<request category="Alexa">
<name>Alexa Intent - MyColorIs</name>
<filename>AlexaIntentMyColorIs.json</filename>
</request>
<request category="Common">
<name>Hello World</name>
<filename>HelloWorld.json</filename>
</request>
<request category="Common">
<name>Mobile Backend</name>
<filename>MobileBackend.json</filename>
</request>
</requests>
117 changes: 102 additions & 15 deletions src/lambda/commands/invokeLambda.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use strict';

import xml2js = require('xml2js');
import path = require('path');
import { FunctionNode } from "../explorer/functionNode";
import { getSelectedLambdaNode } from '../utils';
import { BaseTemplates } from "../../shared/templates/baseTemplates";
Expand All @@ -9,30 +11,115 @@ import { ext } from "../../shared/extensionGlobals";
import { LambdaTemplates } from "../templates/lambdaTemplates";
import { AWSError } from "aws-sdk";
import Lambda = require('aws-sdk/clients/lambda');
import { ResourceFetcher } from "../../shared/resourceFetcher";
import { sampleRequestManifestPath, sampleRequestPath } from "../constants";
import { SampleRequest } from '../models/sampleRequest';
import { ExtensionUtilities } from '../../shared/extensionUtilities';

export async function invokeLambda(element?: FunctionNode) {
try {
const fn: FunctionNode = await getSelectedLambdaNode(element);

const view = vscode.window.createWebviewPanel('html', `Invoked ${fn.functionConfiguration.FunctionName}`, -1);
const view = vscode.window.createWebviewPanel(
'html',
`Invoked ${fn.functionConfiguration.FunctionName}`,
vscode.ViewColumn.One,
{
// Enable scripts in the webview
enableScripts: true
}
);
const baseTemplateFn = _.template(BaseTemplates.SimpleHTML);
view.webview.html = baseTemplateFn({ content: `<h1>Loading...</h1>` });
view.webview.html = baseTemplateFn({
content: `<h1>Loading...</h1>`
});

// ideally need to get the client from the explorer, but the context will do for now
const lambdaClient = await ext.sdkClientBuilder.createAndConfigureSdkClient(Lambda, undefined);
const funcResponse = await lambdaClient.invoke({ FunctionName: fn.functionConfiguration.FunctionArn!, LogType: 'Tail' }).promise();
const logs = funcResponse.LogResult ? Buffer.from(funcResponse.LogResult, 'base64') : "";
const payload = funcResponse.Payload ? funcResponse.Payload : JSON.stringify({});
const invokeTemplateFn = _.template(LambdaTemplates.InvokeTemplate);

view.webview.html = baseTemplateFn({
content: invokeTemplateFn({
FunctionName: fn.functionConfiguration.FunctionName,
LogResult: logs,
StatusCode: funcResponse.StatusCode,
Payload: payload
})
});
console.log('building template...');
const invokeInputTemplateFn = _.template(LambdaTemplates.InvokeInputTemplate);
const resourcePath = path.join(ext.context.extensionPath, 'resources', 'vs-lambda-sample-request-manifest.xml');
console.log(sampleRequestManifestPath);
console.log(resourcePath);
try {
const sampleInput = await ResourceFetcher.fetchHostedResource(sampleRequestManifestPath, resourcePath);
const inputs: SampleRequest[] = [];
console.log('querying manifest url');
xml2js.parseString(sampleInput, { explicitArray: false }, (err, result) => {
console.log(result);
if (err) { return; }
_.forEach(result.requests.request, (r) => {
inputs.push({ name: r.name, filename: r.filename });
});
});
const loadScripts = ExtensionUtilities.getScriptsForHtml(['sampleRequests.js']);
const loadLibs = ExtensionUtilities.getLibrariesForHtml(['vue.min.js']);
console.log(loadLibs);
view.webview.html = baseTemplateFn({
content: invokeInputTemplateFn({
FunctionName: fn.functionConfiguration.FunctionName,
InputSamples: inputs,
Scripts: loadScripts,
Libraries: loadLibs
// LogResult: logs,
// StatusCode: funcResponse.StatusCode,
// Payload: payload
}),
});

view.webview.onDidReceiveMessage(async message => {
switch (message.command) {
case 'sampleRequestSelected':
console.log('selected the following sample:');
console.log(message.value);
const sample = await ResourceFetcher.fetchHostedResource(sampleRequestPath + message.value, resourcePath);
console.log(sample);
view.webview.postMessage({ command: 'loadedSample', sample: sample });
return;
case 'invokeLambda':
console.log('got the following payload:');
console.log(message.value);
const lambdaClient = await ext.sdkClientBuilder.createAndConfigureSdkClient(Lambda, undefined);
let funcRequest = {
FunctionName: fn.functionConfiguration.FunctionArn!,
LogType: 'Tail'
} as AWS.Lambda.InvocationRequest;
if (message.value) {
console.log('found a payload');
funcRequest.Payload = message.value;
}
const invokeTemplateFn = _.template(LambdaTemplates.InvokeTemplate);
try {
const funcResponse = await lambdaClient.invoke(funcRequest).promise();
const logs = funcResponse.LogResult ? Buffer.from(funcResponse.LogResult, 'base64') : "";
const payload = funcResponse.Payload ? funcResponse.Payload : JSON.stringify({});
view.webview.html = baseTemplateFn({
content: invokeTemplateFn({
FunctionName: fn.functionConfiguration.FunctionName,
LogResult: logs,
StatusCode: funcResponse.StatusCode,
Payload: payload
}),
});
} catch (e) {
view.webview.html = baseTemplateFn({
content: invokeTemplateFn({
FunctionName: fn.functionConfiguration.FunctionName,
LogResult: null,
StatusCode: null,
Payload: null,
Error: e
}),
});
}
break;
}
}, undefined, ext.context.subscriptions);
}
catch (err) {
console.log('Error getting manifest data..');
console.log(err);
}
}
catch (err) {
const ex: AWSError = err;
Expand Down
4 changes: 4 additions & 0 deletions src/lambda/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
'use strict';
import { hostedFilesBaseUrl } from "../shared/constants";

export const blueprintsManifestPath: string = 'LambdaSampleFunctions/NETCore/msbuild-v4/vs-lambda-blueprint-manifest.xml';
export const sampleRequestBase: string = 'LambdaSampleFunctions/SampleRequests';
export const sampleRequestPath: string = `${hostedFilesBaseUrl}${sampleRequestBase}/`;
export const sampleRequestManifestPath: string = `${hostedFilesBaseUrl}${sampleRequestBase}/manifest.xml`;
4 changes: 4 additions & 0 deletions src/lambda/models/sampleRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export class SampleRequest {
public name: string | undefined;
public filename: string | undefined;
}
5 changes: 5 additions & 0 deletions src/lambda/models/scriptResource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import * as vscode from 'vscode';
export class ScriptResource {
public Uri: vscode.Uri | undefined;
public Nonce: string | undefined;
}
Loading

0 comments on commit 4325484

Please sign in to comment.