How do I inject environment variables to Lambda running under sam local? - node.js

I am developing an aws lambda locally using AWS SAM.
I am trying to inject environment variables, with no success.
The Lambda code is:
exports.lambdaHandler = async (event, context) => {
console.log('INJECTED ENVIRONMENT:',JSON.stringify(process.env));
response = {
'statusCode': 200,
'body': "booo"
}
return response;
};
The environment file test.json is:
{
"parameters": {
"PPPPPPPPPPPPPPPPPPPPP1": "PPPPPPPPPPPPPPPPPPPPP1",
"PPPPPPPPPPPPPPPPPPPPP2": "PPPPPPPPPPPPPPPPPPPPP2",
"PPPPPPPPPPPPPPPPPPPPP3": "PPPPPPPPPPPPPPPPPPPPP3"
}
}
I am running the lambda. It runs ok, shows me the expected output, Except the env variables I tried defining.
sam local invoke -e events/event.json -n test.json
What am I missing?

The environment variables file has a specific format it needs to adhere to that depends on the logical id of the lambda function you want to test:
Test.json:
{
"LogicalIdOfLambdaHere":{
"ENV_VAR_KEY": "value"
}
}
Make sure the logical id matches the one defined in your sam template. Alternatively you can use Parameters instead of parameters.
Note you also need to define the same variables in the actual SAM template. This method is for overriding the values specified in the template and not for adding new ones not defined in the template. Variables are also case sensitive:)

Related

How to create a custom blueprint?

I'm trying to create a customized JHipster blueprint for my organization.
I've started my journey:
Installed Yeoman v4.3.0
Installed Jhipster v7.9.3
Created a directory for my future blueprint mkdir mygenerator && cd mygenerator
Executed the command to create a new blueprint: jhipster generate-blueprint
selected only the sub-generator server
add a cli: Y
Is server generator a side-by-side blueprint: Y
Is server generator a cli command: N
selected the tasks: initializing, prompting and configuring
From this point, I've opened the generated blueprint project with VS Code and noticed a first problem, some jhipster packages can't be resolved:
Unable to resolve path to module 'generator-jhipster/esm/generators/server'
Unable to resolve path to module 'generator-jhipster/esm/priorities'
I also noticed that the generator created for me has a small difference from the existing generators in the JHipster Github, such as jhipster-dotnetcore, generator-jhipster-quarkus, generator-jhipster-nodejs: the returned functions are async while in the cited repos they are regular functions (sync):
get [INITIALIZING_PRIORITY]() {
return {
async initializingTemplateTask() {},
};
}
Does it make any difference in this Jhipster version or there is no problem if I return the same way as jhipster-dotnetcore:
get initializing() {
return {
...super._initializing(),
setupServerConsts() {
this.packagejs = packagejs;
...
I've assumed that this detail is not important and followed with async function and write my prompting function to get some input from the user/developer in order to replace values in the template files :
get [PROMPTING_PRIORITY]() {
return {
...super._prompting(),
async promptingTemplateTask() {
const choices = [
{
name: 'OAuth 2.0 Protocol',
value: 'oauth2',
},
{
name: 'CAS Protocol',
value: 'cas',
},
];
const PROMPTS = {
type: 'list',
name: 'authenticationProtocol',
message: 'Which authentication protocol do you want to use?',
choices,
default: 'oauth2',
};
const done = this.async();
if (choices.length > 0) {
this.prompt(PROMPTS).then(prompt => {
this.authenticationProtocol = this.jhipsterConfig.authenticationProtocol = prompt.authenticationProtocol;
done();
});
} else {
done();
}
},
};
}
<%_ if (authenticationProtocol == 'oauth2') { _%>
security:
enable-csrf: true
oauth2:
client:
clientId: ${this.baseName}
clientSecret: Z3ByZXBmdGVy
accessTokenUri: http://localhost:8443/oauth2.0/accessToken
userAuthorizationUri: http://localhost:8443/oauth2.0/authorize
tokenName: oauth_token
authenticationScheme: query
clientAuthenticationScheme: form
logoutUri: http://localhost:8443/logout
clientSuccessUri: http://localhost:4200/#/login-success
resource:
userInfoUri: http://localhost:8443/oauth2.0/profile
<%_ } _%>
thymeleaf:
mode: HTML
templates/src/test/java/resources/config/application.yml.ejs
All this done, I've followed the next steps:
Run npm link inside the blueprint directory.
Created a new directory for a app example: mkdir appmygenerator && cd appmygenerator
Started a new example app with my blueprint: jhipster --blueprint mygenerator --skip-git --skip-install --skip-user-management --skip-client answering all question.
Here I've got some surprises:
After answering What is the base name of your application? I've got this warning: [DEP0148] DeprecationWarning: Use of deprecated folder mapping "./lib/util/" in the "exports" field module resolution of the package at /...<my-generator-path>/node_modules/yeoman-environment/package.json. Update this package.json to use a subpath pattern like "./lib/util/*"
My prompting function somehow made some questions be repeated, from question Do you want to make it reactive with Spring WebFlux? until Which other technologies would you like to use?.
When my prompt was finally shown, there was a message in front of the last option: CAS Protocol Run-async wrapped function (sync) returned a promise but async() callback must be executed to resolve
I've made some changes to my prompt function: removed the calling of super._prompting() with the hope to solve the item 2, and removed the async in the hope to solve the item 3.
Well ... apparently it was solved. But I get a new error when JHipster (or Yeoman) try process the template:
An error occured while running jhipster:server#writeFiles
ERROR! /home/fabianorodrigo/Downloads/my-blueprint/generators/server/templates/src/test/resources/config/application.yml.ejs:47
45| favicon:
46| enabled: false
>> 47| <%_ if (authenticationProtocol == 'oauth2') { _%>
48| security:
49| enable-csrf: true
50| oauth2:
authenticationProtocol is not defined
How come authenticationProtocol is not defined? I'm stuck here. What I could noticed is that, in all the Jhipster's generators I've cited above, the prompt function sets the properties like "this.[property] = [value]" and the "this.jhipsterConfig.[property] = [value]" and in the templates they are referenced (just the property's name) and it works.
What am I missing? Why even if I set the property "this.authenticationProtocol" in the function prompting it is not seem at the template?
Yeoman (yo/yeoman-generator/yeoman-environment) are not required and should no be a dependency to avoid duplication in the dependency tree, unless you know what you are doing. JHipster customizes them, yeoman-test is required by tests.
Unable to resolve path to module is a bug at eslint-plugin-import
I also noticed that the generator created for me has a small difference from the existing generators in the JHipster Github, such as jhipster-dotnetcore, generator-jhipster-quarkus, generator-jhipster-nodejs. Those blueprints are quite old (blueprint support is changing very fast for v8/esm) and are full server/backend replacements, seems you are trying to add cas support. The use case is quite different.
Does it make any difference in this Jhipster version or there is no problem if I return the same way as jhipster-dotnetcore? Yes, get [INITIALIZING_PRIORITY]() is the new notation, and INITIALIZING_PRIORITY may be >initializing instead of initializing. The explanation is here. JHipster v8 will not support the old notation.
...super._prompting(), is used to ask original prompts, since this is a side-by-side blueprint, prompts will be duplicated.
[DEP0148] DeprecationWarning: Use of deprecated folder mapping "./lib/util/" is a bug in yeoman-environment, and should be fixed in next version.
CAS Protocol Run-async wrapped function (sync) returned a promise but async() callback must be executed to resolve is shown because you are using async function with const done = this.async(); done(); together.
this.async() is a to support async through callbacks before Promises were a js default.
There are a few blueprints that uses new notation and can be used as inspiration: native, ionic, jooq and entity-audit.
I didn't see anything about the writing priority, so it looks like you are overriding an existing template and the original generator will write it. For this reason you should inject you configuration into the original generator.
The end result should be something like:
get [INITIALIZING_PRIORITY]() {
return {
async initializingTemplateTask() {
this.info('this blueprint adds support to cas authentication protocol');
},
};
}
get [PROMPTING_PRIORITY]() {
return {
async promptingTemplateTask() {
await this.prompt({
type: 'list',
name: 'authenticationProtocol',
message: 'Which authentication protocol do you want to use?',
choices: [
{
name: 'OAuth 2.0 Protocol',
value: 'oauth2',
},
{
name: 'CAS Protocol',
value: 'cas',
},
],
default: 'oauth2',
}, this.blueprintStorage); // <- `this.blueprintStorage` tells the prompt function to store the configuration inside `.yo-rc.json` at the blueprint namespace.
},
};
}
get [CONFIGURING_PRIORITY]() {
return {
configuringTemplateTask() {
// Store the default configuration
this.blueprintConfig.authenticationProtocol = this.blueprintConfig.authenticationProtocol || 'oauth2';
},
};
}
get [LOADING_PRIORITY]() {
return {
loadingTemplateTask() {
// Load the stored configuration, the prompt can be skipped so this needs to be in another priority.
this.authenticationProtocol = this.blueprintConfig.authenticationProtocol;
// Inject the configuration into the original generator. If you are writing the template by yourself, this may be not necessary.
this.options.jhipsterContext.authenticationProtocol = this.blueprintConfig.authenticationProtocol;
},
};
}

How do you find the filename and path of a running test in Jest

I am using Jest for testing. How do I access the filename or file path of the current test being run?
I need a conditional statement that runs different lines of code based on whether it is a unit test file or integration test file.
Here is an example of what I am trying to achieve:
beforeAll(() => {
if (integration_test_file){
// run this this code
} else if (unit_test_file){
// run this code instead
}
})
This information is available in Jest environment. This is the case for custom environment:
const Environment = require('jest-environment-node'); // or jest-environment-jsdom
module.exports = class MyEnvironment extends Environment {
constructor(config, context) {
super(config, context);
this.testPath = context.testPath;
}
async setup() {
await super.setup();
this.global.IS_INTEGRATION = /match integration/.test(this.testPath);
}
}
The environment is instantiated for each test suite, testPath contains full path to current test file.
IS_INTEGRATION global variable will be available in setupFilesAfterEnv and tests themselves. In case the code needs to be evaluated for all tests, it may belong to environment setup and teardown methods.
You can access the name of the file path of the test being ran in jest via its global variables.
the file path can be found under the global variable
jasmine.testPath
or
global.jasmine.testPath
this answer only applies if you're using jest with its default test runner "jasmine" or "jasmine2". results will differ based on the test runner you use. see
https://jestjs.io/docs/en/configuration#testrunner-string
__dirname works a treat (ref: https://www.geeksforgeeks.org/how-to-get-the-path-of-current-script-using-node-js/)

Using aws-sdk.js with require.js in a browser

I'm trying to figure out how to use the browser-based aws-sdk.js with require.js when building a web app.
If I try to build a class and include aws-sdk in the require.js define, once I try to create an instance and reference the AWS class, it says it is undefined.
For example, I have a function that checks to see if the credentials are an instance of AWS.Credentials and if not, it tries to initialize it with an STS token it retrieves via Rest.
The problem is, the code dies on the if(this.credentials instanceof AWS.Credentials) saying AWS is undefined. It even dies on my simple check of the needsRefresh.
This is what my define looks like - I'll include the 'needsRefresh' wrapper for an example of the sort of thing that is throwing the Undefined error:
define(['require','aws-sdk','jquery'],
function (require, AWS, $) {
// Aws helper class
function AwsHelper() { };
AwsHelper.prototype = {
credentials: null,
tokenNeedsRefresh: function () {
//////////////////////////////////////////////////////////////////
// errors out on the following line with: //
// TypeError: Cannot read property 'Credentials' of undefined //
//////////////////////////////////////////////////////////////////
if(this.credentials instanceof AWS.Credentials) {
return this.credentials.needsRefresh();
} else return true;
}
};
return AwsHelper;
}
);
I also tried the following format at the top of the file:
define(function (require) {
var AWS = require("aws-sdk"),
$ = require("jquery");
/* .. */
If I remove all onLoad references to the refresh code running, it will load and I can create an instance. But as soon as I call any function that references the AWS class, it dies.
How do I make sure that AWS class definition is still in global space once an instance is spawned?
Dunno what difference the paths will make (it's finding and loading the code just fine - I can see AWS Class in namespace in the debugger as it's loading but it's not in the namespace on the function call), but added on request:
requirejs.config({
baseUrl: '/js',
paths: {
lib: 'lib',
ImageUploader: 'ImageUploader'
}
});
I decided to try to play with this again and seemed to have figured out how I was going about it incorrectly. I have yet to integrate it back into my existing code setup, but I think I have a working solution.
The thing I was doing wrong was I was trying to set up the AWS class so I could load it as a module (thus why I tried wrapping it as the require.js suggested).
Playing with it this time around, I noticed something I hadn't before. I had an AWS that was undefined in the local scope and an other AWS that held the class definition in the global scope. So it was trying to specify AWS in my define that was creating the local 'null' version:
define(
["jquery","aws-sdk","dropzone","app/MyUtilities"],
function ($, AWS, Dropzone, MyUtilities) {
"use strict";
function MyClass() {};
return MyClass;
}
);
jquery and dropzone have whatever is needed to make sure they load that way, but aws-sdk seems to do some of it's own asynchronous loading. Thus the scope variable in the function is undefined.
Technically, it seems the only thing I needed defined as a module in the function wrapper is my own utilities module. So by switching it to:
define(
["app/MyUtilities","jquery","aws-sdk","dropzone"],
function (MyUtilities) {
"use strict";
function MyClass() {};
return MyClass;
}
);
... it seems $ and jquery are defined as needed, Dropzone is defined as needed and AWS is defined as needed. (and I don't get my errors when accessing the static AWS.config or AWS.Credentials definitions as I did before)

In Jest: In setupTestFrameworkScriptFile, can I access the name/filename of the current executing test file

In many of my tests, I create a new slqite database and want to use the name of the test file as the sqlite DBname. This way, all my DB tests are isolated from each other.
As a related question: I would like my setupTestFrameworkScriptFile to be able to read config items (an Object) from the test. Is there a standard way to pass these configs from test to TestFramework?
Here is an example:
In myclass.test.js:
// "tell" the setup file I'm named 'myclass'
global.testId = function () {
return "myclass"
}
In setupTestFrameworkScriptFile:
beforeAll(async () => {
// if the test supports this method
if (typeof testId === 'undefined') {
return
}
// create an sqlite DB with that name
_createAndSyncDb(testId)
Instead I would like the code in setupTestFrameworkScriptFile to read the filename from the Jest environment, removing the need for the test and setupTestFrameworkScriptFile to both know about a previously agreed upon method named testId.

Error:"Failed to get the current sub/segment from the context" when use AWS X-ray in Lambda with node.js

I am trying to use implement the AWS X-ray into my current project (using Node.js and Serverless framework). I am trying to wire the X-ray to one of my lambda function, I got the problem of
Error: Failed to get the current sub/segment from the context.
at Object.contextMissingRuntimeError [as contextMissing] (/.../node_modules/aws-xray-sdk-core/lib/context_utils.js:21:15)
at Object.getSegment (/.../node_modules/aws-xray-sdk-core/lib/context_utils.js:92:45)
at Object.resolveSegment (/.../node_modules/aws-xray-sdk-core/lib/context_utils.js:73:19).....
code below:
import { DynamoDB } from "aws-sdk";
import AWSXRay from 'aws-xray-sdk';
export const handler = async (event, context, callback) => {
const dynamo = new DynamoDB.DocumentClient({
service: new DynamoDB({ region })
});
AWSXRay.captureAWSClient(dynamo.service);
try {
// call dynamoDB function
} catch(err) {
//...
}
}
for this problem, I use the solution from
https://forums.aws.amazon.com/thread.jspa?messageID=821510&#821510
the other solution I tried is from https://forums.aws.amazon.com/thread.jspa?messageID=829923&#829923
code is like
import AWSXRay from 'aws-xray-sdk';
const AWS = AWSXRay.captureAWS(require('aws-sdk'));
export const handler = async (event, context, callback) => {
const dynamo = new AWS.DynamoDB.DocumentClient({region});
//....
}
Still not working...
Appreciated to the help of any kind.
As you mention, that happened because you're running locally (using serverless-offline plugin) and the serverless-offline plugin doesn't provide a valid XRAY context.
One possible way to pass this error and still be able to call your function locally is setting AWS_XRAY_CONTEXT_MISSING environment variable to LOG_ERROR instead of RUNTIME_ERROR (default).
Something like:
serverless invoke local -f functionName -e AWS_XRAY_CONTEXT_MISSING=LOG_ERROR
I didn't test this using serverless framework but it worked when the same error occurred calling an amplify function locally:
amplify function invoke <function-name>
I encountered this error also. To fix it, I disabled XRay when running locally. XRay isn't needed when running locally because I can just set up debug log statements at that time.
This is what the code would look like
let AWS = require('aws-sdk');
if (!process.env.IS_OFFLINE) {
const AWSXRay = require('aws-xray-sdk');
AWS = AWSXRay.captureAWS(require('aws-sdk'));
}
If you don't like this approach, you can set up a contextStrategy to not error out when the context is missing.
Link here
AWSXRay.setContextMissingStrategy("LOG_ERROR");
If you don't want the error clogging up your output you can add a helper that ignores only that error.
// Removes noisy Error: Failed to get the current sub/segment from the context due to Xray
export async function disableXrayError() {
console.error = jest.fn((err) => {
if (err.message.includes("Failed to get the current sub/segment from the context")) {
return;
} else {
console.error(err);
}
});
}

Resources