Repository: serverless/dashboard (language: python)

Issue:
SDK: Resolve parametrized paths with nested routers
Resolve properly paramterized paths as passed to `app.use()` routing method.

It was observed that with nested working constructed as follows, the first part of the path is assigned as resolved to 
`aws.lambda.http_router.path` tag

```javascript
 const paramRouter = new express.Router();
 paramRouter.get('/ipsum/:cat', (req, res) => { res.send('"ok"');  });

app.use('/lorem/:dog', paramRouter);
```


Discussion / hints:

Fix patch:
diff --git a/node/packages/aws-lambda-sdk/package.json b/node/packages/aws-lambda-sdk/package.json
index f99c8cfc2..92604b139 100644
--- a/node/packages/aws-lambda-sdk/package.json
+++ b/node/packages/aws-lambda-sdk/package.json
@@ -4,7 +4,7 @@
   "version": "0.14.20",
   "author": "Serverless, Inc.",
   "dependencies": {
-    "@serverless/sdk": "^0.5.14",
+    "@serverless/sdk": "^0.5.15",
     "@serverless/sdk-schema": "^0.15.3",
     "d": "^1.0.1",
     "ext": "^1.7.0",


Test patch:
diff --git a/node/packages/aws-lambda-sdk/test/fixtures/lambdas/express.js b/node/packages/aws-lambda-sdk/test/fixtures/lambdas/express.js
index 9d5d18144..fd6e1c317 100644
--- a/node/packages/aws-lambda-sdk/test/fixtures/lambdas/express.js
+++ b/node/packages/aws-lambda-sdk/test/fixtures/lambdas/express.js
@@ -29,6 +29,13 @@ app.post('/users/:userId/books/:bookId', (req, res) => {
   res.send('"ok"');
 });
 
+const paramRouter = new express.Router();
+paramRouter.post('/ipsum/:cat', (req, res) => {
+  res.send('"ok"');
+});
+
+app.use('/lorem/:dog', paramRouter);
+
 app.use((req, res) => res.status(404).json({ error: 'Not Found' }));
 
 module.exports.handler = serverless(app);
diff --git a/node/packages/aws-lambda-sdk/test/integration/index.test.js b/node/packages/aws-lambda-sdk/test/integration/index.test.js
index 80c40d698..6321ca35b 100644
--- a/node/packages/aws-lambda-sdk/test/integration/index.test.js
+++ b/node/packages/aws-lambda-sdk/test/integration/index.test.js
@@ -81,6 +81,12 @@ describe('integration', function () {
       Target: `integrations/${integrationId}`,
     });
 
+    await awsRequest(ApiGatewayV2, 'createRoute', {
+      ApiId: apiId,
+      RouteKey: 'POST /lorem/{dog}/ipsum/{cat}',
+      Target: `integrations/${integrationId}`,
+    });
+
     await awsRequest(ApiGatewayV2, 'createStage', {
       ApiId: apiId,
       StageName: '$default',
@@ -349,7 +355,7 @@ describe('integration', function () {
     }),
   };
 
-  const resolveExpressInvoke = ({ pathname }) =>
+  const resolveExpressInvoke = ({ pathname }, retryCount = 0) =>
     async function self(testConfig) {
       const startTime = process.hrtime.bigint();
       const response = await fetch(
@@ -363,10 +369,10 @@ describe('integration', function () {
         }
       );
       if (response.status !== 200) {
-        if (response.status === 404) {
+        if (retryCount < 10 && response.status === 404) {
           log.warn(`API Gateway at POST ${pathname} not ready yet, retrying in 1s`);
           await wait(1000);
-          return self(testConfig);
+          return self(testConfig, ++retryCount);
         }
         throw new Error(`Unexpected response status: ${response.status}`);
       }
@@ -1780,6 +1786,64 @@ describe('integration', function () {
               },
             },
           ],
+          [
+            'nestedParametrized',
+            {
+              invoke: resolveExpressInvoke({ pathname: '/lorem/hau/ipsum/miau' }),
+              test: ({ invocationsData, testConfig }) => {
+                for (const [
+                  index,
+                  {
+                    trace: { spans },
+                  },
+                ] of invocationsData.entries()) {
+                  const lambdaSpan = spans.shift();
+                  if (!index) spans.shift();
+                  const { tags: lambdaTags } = lambdaSpan;
+
+                  expect(lambdaTags.aws.lambda.eventSource).to.equal('aws.apigateway');
+                  expect(lambdaTags.aws.lambda.eventType).to.equal('aws.apigatewayv2.http.v2');
+
+                  expect(lambdaTags.aws.lambda.apiGateway).to.have.property('accountId');
+                  expect(lambdaTags.aws.lambda.apiGateway.apiId).to.equal(testConfig.apiId);
+                  expect(lambdaTags.aws.lambda.apiGateway.apiStage).to.equal('$default');
+                  expect(lambdaTags.aws.lambda.apiGateway.request).to.have.property('id');
+                  expect(lambdaTags.aws.lambda.apiGateway.request).to.have.property('timeEpoch');
+                  expect(lambdaTags.aws.lambda.http).to.have.property('host');
+                  expect(lambdaTags.aws.lambda.http).to.have.property('requestHeaderNames');
+                  expect(lambdaTags.aws.lambda.http.method).to.equal('POST');
+                  expect(lambdaTags.aws.lambda.http.path).to.equal('/lorem/hau/ipsum/miau');
+
+                  expect(lambdaTags.aws.lambda.http.statusCode.toString()).to.equal('200');
+
+                  expect(lambdaTags.aws.lambda.httpRouter.path.toString()).to.equal(
+                    '/lorem/:dog/ipsum/:cat'
+                  );
+
+                  const [invocationSpan, expressSpan, ...middlewareSpans] = spans;
+                  const routeSpan = middlewareSpans.pop();
+                  const routerSpan = middlewareSpans.pop();
+                  const topRouterSpan = middlewareSpans[middlewareSpans.length - 1];
+
+                  expect(expressSpan.parentSpanId).to.deep.equal(invocationSpan.id);
+
+                  expect(middlewareSpans.map(({ name }) => name)).to.deep.equal([
+                    'express.middleware.query',
+                    'express.middleware.expressinit',
+                    'express.middleware.jsonparser',
+                    'express.middleware.router.loremdog',
+                  ]);
+                  for (const middlewareSpan of middlewareSpans) {
+                    expect(String(middlewareSpan.parentSpanId)).to.equal(String(expressSpan.id));
+                  }
+                  expect(routerSpan.name).to.equal('express.middleware.router');
+                  expect(String(routerSpan.parentSpanId)).to.equal(String(topRouterSpan.id));
+                  expect(routeSpan.name).to.equal('express.middleware.route.post.anonymous');
+                  expect(String(routeSpan.parentSpanId)).to.equal(String(routerSpan.id));
+                }
+              },
+            },
+          ],
         ]),
         config: {
           hooks: {
diff --git a/node/test/lib/get-process-function.js b/node/test/lib/get-process-function.js
index 8d768dbf4..3d234f823 100644
--- a/node/test/lib/get-process-function.js
+++ b/node/test/lib/get-process-function.js
@@ -82,7 +82,7 @@ module.exports = async (basename, coreConfig, options) => {
   };
 
   const ensureIsActive = async (testConfig) => {
-    await wait(100);
+    await wait(1000);
     let state;
     try {
       state = (