AWS SAM Lambda typescript project doesnt attach to debugger in VSCode - visual-studio-code

I have used AWS SAM to initialize a typescript lambda project which I am editing in VSCode on Windows 10. I have also setup debug points in the typescript code and this is my launch.json
{
"configurations": [
{
"type": "aws-sam",
"request": "direct-invoke",
"invokeTarget": {
"target": "template",
"logicalId": "HelloWorldFunction",
"templatePath": "${workspaceFolder}/lambda-app/template.yaml"
},
"lambda": {
"runtime": "nodejs14.x"
},
"sam": {
"containerBuild": false,
"skipNewImageCheck": false
},
"api": {
"httpMethod": "get"
},
"name": "templatestyle"
},
{
"type": "aws-sam",
"request": "direct-invoke",
"invokeTarget": {
"target": "code",
"lambdaHandler": "app.lambdaHandler",
"projectRoot": "${workspaceFolder}/lambda-app/hello-world"
},
"lambda": {
"runtime": "nodejs14.x",
"payload": {
"json": {
"key1": "value1",
"key2": "value2",
"key3": "value3"
}
}
},
"sam": {
"containerBuild": false,
"skipNewImageCheck": false
},
"api": {
"httpMethod": "get"
},
"name": "basic"
}
]
}
This is my tsconfig.json
{
"compilerOptions": {
"target": "es2020",
"strict": true,
"preserveConstEnums": true,
"noEmit": true,
"sourceMap": true,
"module":"es2015",
"moduleResolution":"node",
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
},
"include": ["*"],
"exclude": ["node_modules", "**/*.test.ts"]
}
This is the code in app.ts
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
/**
*
* Event doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format
* #param {Object} event - API Gateway Lambda Proxy Input Format
*
* Return doc: https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html
* #returns {Object} object - API Gateway Lambda Proxy Output Format
*
*/
export const lambdaHandler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
let response: APIGatewayProxyResult;
try {
response = {
statusCode: 200,
body: JSON.stringify({
message: 'hello world',
}),
};
} catch (err) {
console.log(err);
response = {
statusCode: 500,
body: JSON.stringify({
message: 'some error happened',
}),
};
}
return response;
};
When I select either of the debug configurations above, the break point only attaches in a file that is not part of what I created. It stops without a breakpoint on the first line below, which is seems to be in the path \var\runtime\index.js
Where did this file come from? Docker perhaps?. Also, why is it stopping here in this file and not in the breakpoint I put on my app.ts?
This is the result I get in the debug console, and the breakpoints I have set are not hit. How do I fix this?

By default the debugger will break on the first line of code. There is a switch to disable this. I don't see the command you are using for local invoke. I was able to get it to break on my source code by using the following:
sam local invoke <function logical id> --event events/event.json -d <port>
Function logical id is the name of your function in template.yml and port is the debug port to use. I used 9999. The command I used was
sam local invoke HelloWorldFunction --event events/event.json -d 9999

Related

Implement I18n localization in Strapi local plugins

I generated a local plugin and created an article model using:
"pluginOptions": {
"i18n": {
"localized": true
}
},
inside his article.settings.json file, in order to make some specific fields translatables using the Internationalization(I18N) plugin
Problem is, while running the command:
strapi develop --watch-admin
I end up having the following errors:
error Something went wrong in the model "Article" with the attribute "localizations"
error TypeError: Cannot read property "uid" of undefined
Removing the "pluginOptions" instead, gives my local plugin running without any translatable field or articles__translations pivot that should be generated into my mysql database
"pluginOptions" is the very same parameter that gets generated into the model settings creating a collection type using the Content-Types Builder, but I can't have it to work while using it for a local plugin.
Here is my article.settings.json:
plugins/blog/models/article.settings.json
{
"kind": "collectionType",
"collectionName": "articles",
"info": {
"name": "article"
},
"options": {
"draftAndPublish": false,
"timestamps": true,
"populateCreatorFields": true,
"increments": true,
"comment": ""
},
"pluginOptions": {
"i18n": {
"localized": true
}
},
"attributes": {
"title": {
"pluginOptions": {
"i18n": {
"localized": true
}
},
"type": "string",
"required": true,
"maxLength": 255,
"minLength": 3
},
"slug": {
"pluginOptions": {
"i18n": {
"localized": true
}
},
"type": "uid",
"targetField": "title",
"required": true
},
"featured": {
"pluginOptions": {
"i18n": {
"localized": false
}
},
"type": "boolean",
"default": false
},
"published_date": {
"pluginOptions": {
"i18n": {
"localized": false
}
},
"type": "datetime"
},
}
}
You can use the content-type-builder plugin as a workaround. You would not create the content type under the content-types folder but create it programmatically.
As an example of a very simple tag content type:
{
"singularName": "tag",
"pluralName": "tags",
"displayName": "tag",
"description": "",
"draftAndPublish": false,
"pluginOptions": {
"i18n": {
"localized": true
}
},
"attributes": {
"label": {
"type": "string",
"pluginOptions": {
"i18n": {
"localized": true
}
},
"unique": true
}
}
}
Note, this schema of the json is a bit different from the ones in plugin/server/content-types.
Then you can create the content type programmatically like this:
import { Strapi } from "#strapi/strapi";
import tag from "../content-types/tag.json";
import page from "../content-types/page.json";
export default ({ strapi }: { strapi: Strapi }) => ({
async createContentComponent() {
if (!tag) return null;
try {
const components: any = [];
const contentType = await strapi
.plugin("content-type-builder")
.services["content-types"].createContentType({
contentType: tag,
components,
});
return contentType;
} catch (e) {
console.log("error", e);
return null;
}
},
});
This is exactly how the admin creates content types using the content builder UI.
And it works using the pluginOptions.i18n.localized: true.
One approach would be to do this, e.g., on the bootstrap phase of the plugin. Here you could also check whether or not the contents are created or not.
As a bonus, you can also create components that otherwise would not work.
Hope that helps.
Links:
Create components programmatically in a plugin: https://github.com/strapi/strapi-plugin-seo/blob/main/server/services/seo.js
Create content types:
https://github.com/strapi/strapi/blob/88caa92f878a068926255dd482180202f53fcdcc/packages/core/content-type-builder/server/controllers/content-types.js#L48
EDIT:
You could also keep the original schema and use this fn to transform it - at least for now as long as the other approach is not working:
https://github.com/strapi/strapi/blob/1eab2fb08c7a4d3d40a5a7ff3b2f137ce0afcf8a/packages/core/content-type-builder/server/services/content-types.js#L37

Getting started with KrakenD

I need some beginner help to KrakenD. I am running it on Ubuntu. The config is provided below.
I am able to reach the /healthz API without problem.
My challenge is that the /hello path returns error 500. I want this path to redirect to a Quarkus app that runs at http://getting-started36-getting-going.apps.bamboutos.hostname.us/.
Why is this not working? If I modify the /hello backend and use a fake host, I get the exacts ame result. This suggests that KrakendD is not even trying to connect to the backend.
In logs, KrakendD is saying:
Error #01: invalid character 'H' looking for beginning of value
kraken.json:
{
"version": 2,
"port": 9080,
"extra_config": {
"github_com/devopsfaith/krakend-gologging": {
"level": "DEBUG",
"prefix": "[KRAKEND]",
"syslog": false,
"stdout": true,
"format": "default"
}
},
"timeout": "3000ms",
"cache_ttl": "300s",
"output_encoding": "json",
"name": "KrakenD API Gateway Service",
"endpoints": [
{
"endpoint": "/healthz",
"extra_config": {
"github.com/devopsfaith/krakend/proxy": {
"static": {
"data": { "status": "OK"},
"strategy": "always"
}
}
},
"backend": [
{
"url_pattern": "/",
"host": ["http://fake-backend"]
}
]
},
{
"endpoint": "/hello",
"extra_config": {},
"backend": [
{
"url_pattern": "/hello",
"method": "GET",
"host": [
"http://getting-started36-getting-going.apps.bamboutos.hostname.us/"
]
}
]
}
]
}
What am I missing?
add "encoding": "string" to the backend section.
"backend": [
{
"url_pattern": "/hello",
"method": "GET",
"encoding": "string" ,
"host": [
"http://getting-started36-getting-going.apps.bamboutos.hostname.us/"
]
}
]

stitch mongoClient auth AnonymousCredential dont continue inside the continueWith

i am logging in to make a search on mongodb stitch client.
but after the AnonymousCredential authentication it does nothing.
kt code:
val mongoClient = client!!.getServiceClient(RemoteMongoClient.factory, "mongodb-atlas")
client!!.auth
.loginWithCredential(AnonymousCredential())
.continueWith{ task -> { ...some code... } }
but it never gets to the "some code" part.
and i dont know why cause in stitch UI logs i get OK status:
{
"arguments": [
{
"database": "test",
"collection": "users",
"query": {
"id": ""
},
"limit": {
"$numberInt": "1"
},
"project": null,
"sort": null
}
],
"name": "find",
"service": "mongodb-atlas"
}
Function Call Location: US-VA
Compute Used: 624980924 bytes•ms
Remote IP Address: 201.124.215.137
SDK: android v0.0
Platform Version: 8.1.0
Rule Performance Metrics:
{
"test.users": {
"roles": {
"default": {
"matching_documents": 1,
"evaluated_fields": 0,
"discarded_fields": 0
}
},
"no_matching_role": 0
}
}

My Visual Code Debug Extension, doesn't execute my debugger

I have created a Visual Code Debugger Extension, and in my extension activate routine, I can tell it was activated when I debug a sample *.xyz file, as it writes out XYZ Debuger Activated to the console. The problem is, my debugger executable named debugger.exe (which is a console application written in C#) does not get executed.
When I close the *.xyz file, my breakpoint in the deactivate extension function gets hit. this leaves me to believe that for the most part, the extension is working somewhat, but not totally.
What am I doing wrong?
Here is my extension.ts file
'use strict';
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';
import { WorkspaceFolder, DebugConfiguration, ProviderResult, CancellationToken } from 'vscode';
import * as Net from 'net';
// this method is called when your extension is activated
// your extension is activated the very first time the command is executed
export function activate(context: vscode.ExtensionContext) {
// Use the console to output diagnostic information (console.log) and errors (console.error)
// This line of code will only be executed once when your extension is activated
console.log('XYZ-Debugger Activated');
}
// this method is called when your extension is deactivated
export function deactivate() {
console.log('XYZ-Debugger Deactivated');
}
And here is my package.json file:
{
"name": "xyz-debugger",
"displayName": "XYZ Debugger",
"description": "Test Debugger.",
"version": "0.0.1",
"publisher": "Ed_Starkey",
"engines": {
"vscode": "^1.24.0",
"node": "^6.3.0"
},
"categories": [
"Debuggers"
],
"dependencies": {
"vscode-debugprotocol": "^1.20.0",
"vscode-nls": "^2.0.2"
},
"activationEvents": [
"onDebug"
],
"main": "./out/extension",
"contributes": {
"breakpoints": [
{
"language": "xyz"
}
],
"debuggers": [{
"type": "XYZDebug",
"label": "XYZ Debug",
"windows": {
"program": "program": "./out/debugger.exe"
},
"languages": [
{
"id": "xyz",
"extensions": [
".xyz"
],
"aliases": [
"XYZ"
]
}],
"configurationAttributes": {
"launch": {
"required": [
"program"
],
"properties": {
"program": {
"type": "string",
"description": "The program to debug."
}
}
}
},
"initialConfigurations": [
{
"type": "xyz",
"request": "launch",
"name": "Launch Program",
"windows": {
"program": "./out/debugger.exe"
}
}
],
}]
},
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./",
"postinstall": "node ./node_modules/vscode/bin/install",
"test": "npm run compile && node ./node_modules/vscode/bin/test"
},
"devDependencies": {
"typescript": "^2.5.3",
"vscode": "^1.1.5",
"#types/node": "^7.0.43",
"vscode-debugadapter-testsupport":"^1.29.0"
}
}
And here is my launch.json:
// A launch configuration that compiles the extension and then opens it inside a new window
{
"version": "0.1.0",
"configurations": [
{
"name": "Launch XYZ Debug",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": ["--extensionDevelopmentPath=${workspaceRoot}" ],
"stopOnEntry": false,
"sourceMaps": true,
"outFiles": [ "${workspaceRoot}/out/**/*.js" ],
"preLaunchTask": "npm: watch"
},
{
"name": "Launch Tests",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test" ],
"stopOnEntry": false,
"sourceMaps": true,
"outFiles": [ "${workspaceRoot}/out/test/**/*.js" ],
"preLaunchTask": "npm: watch"
}
]
}
You're not registering what you want VSCode to do in activate.
Something like this will do what you want:
let factory: XYZDebugAdapterDescriptorFactory
export function activate(context: vscode.ExtensionContext) {
const debugProvider = new XYZDebugConfigProvider();
factory = new XYZDebugAdapterDescriptorFactory();
context.subscriptions.push(
vscode.debug.registerDebugConfigurationProvider(
'xyz', debugProvider
)
);
context.subscriptions.push(
vscode.debug.onDidReceiveDebugSessionCustomEvent(
handleCustomEvent
)
);
context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory('xyz', factory));
context.subscriptions.push(factory);
}
You'll then need to provide an adaptor for the config. This one will connect to a DAP server on the supplied port or launch xyz.exe
class XYZDebugConfigProvider implements vscode.DebugConfigurationProvider {
resolveDebugConfiguration(folder: WorkspaceFolder | undefined, config: DebugConfiguration, token?: CancellationToken): ProviderResult<DebugConfiguration> {
const editor = vscode.window.activeTextEditor;
const defaultConfig = vscode.extensions
.getExtension("YOUR EXTENSION NAME HERE")
.packageJSON
.contributes
.debuggers[0]
.initialConfigurations[0];
if (!config.request && editor.document.languageId === 'xyz') {
return defaultConfig;
} else if (!config.request) {
// Not trying to debug xyz?
return undefined;
}
config = {
...defaultConfig,
...config
}
config.execArgs = (config.args || [])
.concat(`-l${config.port}`);
return config;
}
}
class ElpsDebugAdapterDescriptorFactory implements vscode.DebugAdapterDescriptorFactory {
private outputchannel: vscode.OutputChannel
constructor() {
this.outputchannel = vscode.window.createOutputChannel(
'XYZ Debug Log'
);
}
createDebugAdapterDescriptor(session: DebugSession, executable: DebugAdapterExecutable | undefined): ProviderResult<DebugAdapterDescriptor> {
if (session.configuration.type !== "xyz") {
return undefined
}
if (session.configuration.request == "launch") {
const args = [...session.configuration.execArgs, session.configuration.program]
this.outputchannel.appendLine(`Starting XYZ with "${session.configuration.executable}" and args "${args.join(" ")}"`)
const debugee = spawn(session.configuration.executable, args)
debugee.stderr.on('data', (data) => {
this.outputchannel.appendLine(data.toString())
})
debugee.stdout.on('data', (data) => {
this.outputchannel.appendLine(data.toString())
})
debugee.on("close", (data) => {
this.outputchannel.appendLine(`Process closed with status ${data}`)
})
debugee.on("error", (data) => {
this.outputchannel.appendLine(`Error from XYZ: ${data.message}:\n${data.stack}`)
})
}
return new vscode.DebugAdapterServer(session.configuration.port)
}
dispose() {}
}
Lastly you'll need to add the rest of the config that you might want to use to your package.json and into launch.json in the debuggee - that part is very well covered in the docs for the example debugger https://code.visualstudio.com/api/extension-guides/debugger-extension
The above is adapted from a ELISP debugger I was writing and is in turn inspired by the excellent Perl 6 support at https://marketplace.visualstudio.com/items?itemName=mortenhenriksen.perl-debug

Include Loopback relation in POST response

I have a working chat-room loopback project, which also utilises Socket.IO.
Once I create a Message (POST to Loopback REST API), I need the response the include the 'creator' relation.
This works fine when using GET, but I cannot include the relation to the POST response.
I'm sure it's a simple remote hook, but I'm stuck...
Any help is greatly appreciated!
"relations": {
"creator": {
"type": "belongsTo",
"model": "Person",
"foreignKey": "",
"options": {
"nestRemoting": true
}
},
"event": {
"type": "belongsTo",
"model": "Event",
"foreignKey": "",
"options": {
"nestRemoting": true
}
}
},
There are two options how you can do it with Loopback 2.x or 3.x (not sure about the Loopback 4.x).
Let's assume we have the following "Note" model:
{
"name": "Note",
"properties": {
"title": {
"type": "string",
"required": true
},
"content": {
"type": "string"
},
"userId": {
"type": "number"
}
},
"relations": {
"user": {
"type": "belongsTo",
"model": "User",
"foreignKey": "userId"
}
}
}
Now, to include "user" property (which is a belongsTo relation of Note) in the response when you create (POST) a Note you have two options.
Option #1 (Recommended): Create a custom remote method and hide the default method in your model's script file. In this case your note.js file should look something like:
module.exports = function (Note) {
// Hide the default 'create' remote method
Note.disableRemoteMethod('create', true);
// Add a custom 'customCreate' remote method
Note.remoteMethod('customCreate', {
description: 'Create a new instance of the model and persist it into the data source.',
accessType: 'WRITE',
accepts: [
{
arg: 'data',
type: 'object',
model: 'Note',
allowArray: true,
description: 'Model instance data',
http: { source: 'body' },
},
{ arg: 'options', type: 'object', http: 'optionsFromRequest' },
],
returns: { arg: 'data', type: 'Note', root: true },
http: { verb: 'post', path: '/' },
isStatic: true,
});
Note.customCreate = function (data, options, cb) {
Note.create(data, options, function(err, newObj) {
if (err) {
cb(err);
}
else {
// here we try to load the user value
newObj.user(function (err, user) {
if (user) {
// if we found a user we add it to __data, so it appears in the output (a bit hacky way)
newObj.__data.user = user;
}
cb(err, newObj);
});
}
});
};
};
I would recommend using this option because you achieve what you need with minimal changes in the default logic of the loopback model, i.e. all the default methods like create, upsert, etc. continue to have default behavior.
Option 2: Use 'after save' operation hook (be careful with this approach as it changes the way how create, upsert, upsertWithWhere and other default methods work)
In this case your note.js file should look something like:
module.exports = function (Note) {
Note.observe('after save', function (ctx, next) {
ctx.instance.user(function (err, user) {
ctx.instance.__data.user = user;
next();
});
});
};
The second option has less code, but as I mentioned before you should be very careful using it because it will change the behavior of default "create" method of the model. I.e. 'after save' action will be executed each time you call Model.create, Model.upsert, etc. It will also slow down these operation as you add additional select query in the 'after save' hook.