From 38adce3e87c0588501c1e912a3a315077a8a8010 Mon Sep 17 00:00:00 2001 From: eladcon Date: Thu, 11 Apr 2024 14:26:51 +0300 Subject: [PATCH] feat(tsoa)!: new lift API (#194) - Changes API from (`liftClient`, `getClient`) to (`lift`, `lifted`) - Lift API is now `service.lift(resource, id: "name", allow: ["action"])`. didn't use `as` because it is highlighted as a special keyword in the IDE - Uses [asynclocalstorage](https://nodejs.org/api/async_context.html#class-asynclocalstorage) instead of req for storing and passing lifted clients context --- tsoa/README.md | 8 ++++---- tsoa/app-aws.js | 4 ++-- tsoa/app.js | 4 ++-- tsoa/clients.d.ts | 6 ++---- tsoa/clients.js | 20 +++++++++++--------- tsoa/lib.w | 4 ++-- tsoa/package-lock.json | 4 ++-- tsoa/package.json | 2 +- tsoa/sim.w | 6 +++--- tsoa/test-assets/usersController.ts | 4 ++-- tsoa/test/lib.test.w | 2 +- tsoa/tfaws.w | 6 +++--- tsoa/types.w | 17 ++++++++++++++++- 13 files changed, 51 insertions(+), 36 deletions(-) diff --git a/tsoa/README.md b/tsoa/README.md index 5dbc8726..550bd6f5 100644 --- a/tsoa/README.md +++ b/tsoa/README.md @@ -29,12 +29,12 @@ It is also possible to use Wing resources from the TS code ```js let bucket = new cloud.Bucket(); -service.liftClient("bucket", bucket, ["put"]); +service.lift(bucket, id: "bucket", allow: ["put"]); ``` ```ts // someController.ts ... -import { getClient } from "@winglibs/tsoa/clients.js"; +import { lifted } from "@winglibs/tsoa/clients.js"; @Get("{userId}") public async getUser( @@ -42,8 +42,8 @@ public async getUser( @Request() request: Req, @Query() name?: string, ): Promise { - let bucket = getClient(request, "bucket"); - bucket.put(userId.toString(), name ?? "not-a-name"); + let bucket = lifted("bucket"); + await bucket.put(userId.toString(), name ?? "not-a-name"); return { id :userId, diff --git a/tsoa/app-aws.js b/tsoa/app-aws.js index 5b700621..6fdc5ad2 100644 --- a/tsoa/app-aws.js +++ b/tsoa/app-aws.js @@ -1,12 +1,12 @@ const express = require("express"); -const { setClients } = require("./clients"); +const { setLifted } = require("./clients"); const serverlessHttp = require("serverless-http"); var _clients = undefined; const app = express(); app.use((req, res, next) => { - setClients(req, _clients); + setLifted(_clients); next(); }); app.use( diff --git a/tsoa/app.js b/tsoa/app.js index 89953cf5..60f25486 100644 --- a/tsoa/app.js +++ b/tsoa/app.js @@ -1,11 +1,11 @@ const express = require("express"); -const { setClients } = require("./clients"); +const { setLifted } = require("./clients"); exports.runServer = async (routes, clients) => { const app = express(); app.use((req, res, next) => { - setClients(req, clients); + setLifted(clients); next(); }); diff --git a/tsoa/clients.d.ts b/tsoa/clients.d.ts index bca17134..22779c60 100644 --- a/tsoa/clients.d.ts +++ b/tsoa/clients.d.ts @@ -1,5 +1,3 @@ -import { Request } from 'express'; +declare const lifted: (id: string) => any; -declare const getClient: (req: Request, id: string) => any; - -export { getClient }; +export { lifted }; diff --git a/tsoa/clients.js b/tsoa/clients.js index b3e8df7c..b7ffe4dd 100644 --- a/tsoa/clients.js +++ b/tsoa/clients.js @@ -1,20 +1,22 @@ -module.exports.getClients = (req) => { - return req.__wing_clients; -}; +const { AsyncLocalStorage } = require("node:async_hooks"); + +// this might run in a different context in sim +const asyncLocalStorage = new AsyncLocalStorage(); +global.asyncLocalStorage = asyncLocalStorage; /** * Get a Wing client from the request object. - * @param {Object} req - The request object. * @param {string} id - The client Id. * @returns {Object} - The requested client. */ -module.exports.getClient = (req, id) => { - if (!req.__wing_clients || !req.__wing_clients[id]) { +module.exports.lifted = (id) => { + const clients = global.asyncLocalStorage.getStore(); + if (!clients || !clients[id]) { throw new Error(`Wing client ${id} not found`); } - return req.__wing_clients[id]; + return clients[id]; }; -module.exports.setClients = (req, clients) => { - req.__wing_clients = clients; +module.exports.setLifted = (clients) => { + global.asyncLocalStorage.enterWith(clients); }; diff --git a/tsoa/lib.w b/tsoa/lib.w index 041ca4e4..a4f686f7 100644 --- a/tsoa/lib.w +++ b/tsoa/lib.w @@ -29,7 +29,7 @@ pub class Service impl types.IService { new cloud.Endpoint(this.url); } - pub liftClient(id: str, client: std.Resource, ops: Array) { - this.inner.liftClient(id, client, ops); + pub lift(client: std.Resource, ops: types.LiftOptions) { + this.inner.lift(client, ops); } } diff --git a/tsoa/package-lock.json b/tsoa/package-lock.json index 09f5a908..20ca2b91 100644 --- a/tsoa/package-lock.json +++ b/tsoa/package-lock.json @@ -1,12 +1,12 @@ { "name": "@winglibs/tsoa", - "version": "0.0.2", + "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@winglibs/tsoa", - "version": "0.0.2", + "version": "0.1.0", "license": "MIT", "peerDependencies": { "@cdktf/provider-aws": "^19.13.0", diff --git a/tsoa/package.json b/tsoa/package.json index 6416df2d..45d0245a 100644 --- a/tsoa/package.json +++ b/tsoa/package.json @@ -1,7 +1,7 @@ { "name": "@winglibs/tsoa", "description": "TSOA library for Wing", - "version": "0.0.2", + "version": "0.1.0", "author": { "email": "eyalk@wing.cloud", "name": "Eyal Keren" diff --git a/tsoa/sim.w b/tsoa/sim.w index 48d10a85..dd84a096 100644 --- a/tsoa/sim.w +++ b/tsoa/sim.w @@ -62,9 +62,9 @@ pub class Service_sim impl types.IService { }, link: true); } - pub liftClient(id: str, client: std.Resource, ops: Array) { - client.onLift(this.service, ops); - this.clients.set(id, client); + pub lift(client: std.Resource, ops: types.LiftOptions) { + client.onLift(this.service, ops.allow); + this.clients.set(ops.id, client); } extern "./lib.js" inflight static startService(options: types.StartServiceOptions): StartResponse; diff --git a/tsoa/test-assets/usersController.ts b/tsoa/test-assets/usersController.ts index 123a4f01..ad226e4d 100644 --- a/tsoa/test-assets/usersController.ts +++ b/tsoa/test-assets/usersController.ts @@ -10,7 +10,7 @@ import { Request, } from "tsoa"; -import { getClient } from "../clients.js"; +import { lifted } from "../clients.js"; import { Request as Req } from "express"; interface User { @@ -36,7 +36,7 @@ export class UsersController extends Controller { @Request() request: Req, @Query() name?: string, ): Promise { - let bucket = getClient(request, "bucket"); + let bucket = lifted("bucket"); await bucket.put(userId.toString(), name ?? "not-a-name"); return { diff --git a/tsoa/test/lib.test.w b/tsoa/test/lib.test.w index ed1260ee..ebe8b9c9 100644 --- a/tsoa/test/lib.test.w +++ b/tsoa/test/lib.test.w @@ -15,7 +15,7 @@ let service = new tsoa.Service( routesDir: "../test-assets/build", ); -service.liftClient("bucket", bucket, ["put"]); +service.lift(bucket, id: "bucket", allow: ["put"]); test "will start tsoa service" { let res = http.get("{service.url}/users/123?name=stam"); diff --git a/tsoa/tfaws.w b/tsoa/tfaws.w index a0404401..5f5b1677 100644 --- a/tsoa/tfaws.w +++ b/tsoa/tfaws.w @@ -147,9 +147,9 @@ pub class Service_tfaws impl types.IService { this.url = deploy.invokeUrl; } - pub liftClient(id: str, client: std.Resource, ops: Array) { - client.onLift(this.func.fn, ops); - this.clients.set(id, client); + pub lift(client: std.Resource, ops: types.LiftOptions) { + client.onLift(this.func.fn, ops.allow); + this.clients.set(ops.id, client); } extern "./lib.js" static build(options: types.StartServiceOptions): str; diff --git a/tsoa/types.w b/tsoa/types.w index b9c1c9cb..583d4016 100644 --- a/tsoa/types.w +++ b/tsoa/types.w @@ -39,9 +39,24 @@ pub struct ServiceProps { routesDir: str; } +/** + * Options for the lift method. + */ +pub struct LiftOptions { + /** + * Id of the client + */ + id: str; + + /** + * List of operations to allow for this client + */ + allow: Array; +} + /** * Starts a new TSOA service. */ pub interface IService { - liftClient(id: str, client: std.Resource, ops: Array): void; + lift(client: std.Resource, ops: LiftOptions): void; }