diff --git a/.github/Architecture.md b/.github/Architecture.md index 423e5d24e..d19920460 100644 --- a/.github/Architecture.md +++ b/.github/Architecture.md @@ -5,185 +5,191 @@ ``` 📦 training | |- 📂 training: -| | |- 📂 core: -| | | |- 📜 __init__.py -| | | |- 📜 dl_model.py : torch model based on user specifications from drag and drop -| | | |- 📜 criterion.py -| | | |- 📜 dataset.py : read in the dataset through URL or file upload -| | | |- 📜 authenticator.py -| | | |- 📜 trainer.py -| | | |- 📜 optimizer.py : what optimizer to use (ie: SGD or Adam for now) | | |- 📂 routes: -| | | |- 📂 image: -| | | | |- 📜 __init__.py -| | | | |- 📜 image.py +| | | |- 📂 tabular: | | | | |- 📜 schemas.py +| | | | |- 📜 __init__.py +| | | | |- 📜 tabular.py | | | |- 📂 datasets: | | | | |- 📂 default: +| | | | | |- 📜 schemas.py | | | | | |- 📜 __init__.py | | | | | |- 📜 columns.py -| | | | | |- 📜 schemas.py | | | | |- 📜 __init__.py -| | | |- 📂 tabular: -| | | | |- 📜 __init__.py -| | | | |- 📜 tabular.py +| | | |- 📂 image: +| | | | |- 📜 image.py | | | | |- 📜 schemas.py -| | | |- 📜 __init__.py +| | | | |- 📜 __init__.py | | | |- 📜 schemas.py +| | | |- 📜 __init__.py +| | |- 📂 core: +| | | |- 📜 criterion.py +| | | |- 📜 dl_model.py : torch model based on user specifications from drag and drop +| | | |- 📜 dataset.py : read in the dataset through URL or file upload +| | | |- 📜 __init__.py +| | | |- 📜 authenticator.py +| | | |- 📜 trainer.py +| | | |- 📜 optimizer.py : what optimizer to use (ie: SGD or Adam for now) | | |- 📂 middleware: -| | | |- 📜 health_check_middleware.py | | | |- 📜 __init__.py -| | |- 📜 __init__.py -| | |- 📜 wsgi.py +| | | |- 📜 health_check_middleware.py | | |- 📜 settings.py | | |- 📜 urls.py +| | |- 📜 __init__.py +| | |- 📜 wsgi.py | | |- 📜 asgi.py -| |- 📜 pytest.ini -| |- 📜 Dockerfile.prod -| |- 📜 Dockerfile +| |- 📜 docker-compose.yml | |- 📜 docker-compose.prod.yml -| |- 📜 manage.py +| |- 📜 pyproject.toml +| |- 📜 README.md | |- 📜 poetry.lock -| |- 📜 environment.yml | |- 📜 cli.py -| |- 📜 README.md -| |- 📜 docker-compose.yml -| |- 📜 pyproject.toml +| |- 📜 environment.yml +| |- 📜 Dockerfile.prod +| |- 📜 Dockerfile +| |- 📜 manage.py +| |- 📜 pytest.ini ``` ## Frontend Architecture ``` 📦 frontend +| |- 📂 layer_docs: +| | |- 📜 Linear.md : Doc for Linear layer +| | |- 📜 Softmax.md : Doc for Softmax layer +| | |- 📜 softmax_equation.png : PNG file of Softmax equation +| | |- 📜 ReLU.md : Doc for ReLU later | |- 📂 public: | | |- 📂 images: -| | | |- 📂 learn_mod_images: -| | | | |- 📜 tanhactivation.png -| | | | |- 📜 sigmoidfunction.png -| | | | |- 📜 lossExample.png -| | | | |- 📜 neuronWithEquation.png -| | | | |- 📜 binarystepactivation.png -| | | | |- 📜 robotImage.jpg -| | | | |- 📜 ReLUactivation.png -| | | | |- 📜 lossExampleTable.png -| | | | |- 📜 sigmoidactivation.png -| | | | |- 📜 LeakyReLUactivation.png -| | | | |- 📜 neuron.png -| | | | |- 📜 neuralnet.png -| | | | |- 📜 lossExampleEquation.png | | | |- 📂 wiki_images: -| | | | |- 📜 avgpool_maxpool.gif -| | | | |- 📜 softmax_equation.png : PNG file of Softmax equation -| | | | |- 📜 dropout_diagram.png -| | | | |- 📜 tanh_equation.png | | | | |- 📜 maxpool2d.gif | | | | |- 📜 conv2d.gif -| | | | |- 📜 tanh_plot.png -| | | | |- 📜 sigmoid_equation.png +| | | | |- 📜 softmax_equation.png : PNG file of Softmax equation +| | | | |- 📜 tanh_equation.png +| | | | |- 📜 dropout_diagram.png | | | | |- 📜 batchnorm_diagram.png +| | | | |- 📜 tanh_plot.png | | | | |- 📜 conv2d2.gif +| | | | |- 📜 sigmoid_equation.png +| | | | |- 📜 avgpool_maxpool.gif +| | | |- 📂 learn_mod_images: +| | | | |- 📜 lossExampleEquation.png +| | | | |- 📜 sigmoidactivation.png +| | | | |- 📜 neuralnet.png +| | | | |- 📜 binarystepactivation.png +| | | | |- 📜 lossExample.png +| | | | |- 📜 LeakyReLUactivation.png +| | | | |- 📜 lossExampleTable.png +| | | | |- 📜 tanhactivation.png +| | | | |- 📜 robotImage.jpg +| | | | |- 📜 ReLUactivation.png +| | | | |- 📜 neuron.png +| | | | |- 📜 neuronWithEquation.png +| | | | |- 📜 sigmoidfunction.png | | | |- 📂 logos: | | | | |- 📂 dlp_branding: -| | | | | |- 📜 dlp-logo.png : DLP Logo, duplicate of files in public, but essential as the frontend can't read public | | | | | |- 📜 dlp-logo.svg : DLP Logo, duplicate of files in public, but essential as the frontend can't read public +| | | | | |- 📜 dlp-logo.png : DLP Logo, duplicate of files in public, but essential as the frontend can't read public +| | | | |- 📜 google.png | | | | |- 📜 pytorch-logo.png +| | | | |- 📜 python-logo.png | | | | |- 📜 dsgt-logo-dark.png -| | | | |- 📜 dsgt-logo-white-back.png +| | | | |- 📜 aws-logo.png | | | | |- 📜 github.png -| | | | |- 📜 python-logo.png | | | | |- 📜 pandas-logo.png -| | | | |- 📜 dsgt-logo-light.png -| | | | |- 📜 flask-logo.png | | | | |- 📜 react-logo.png -| | | | |- 📜 google.png -| | | | |- 📜 aws-logo.png +| | | | |- 📜 dsgt-logo-white-back.png +| | | | |- 📜 flask-logo.png +| | | | |- 📜 dsgt-logo-light.png | | | |- 📜 demo_video.gif : GIF tutorial of a simple classification training session -| | |- 📜 index.html : Base HTML file that will be initially rendered | | |- 📜 robots.txt | | |- 📜 manifest.json : Default React file for choosing icon based on +| | |- 📜 index.html : Base HTML file that will be initially rendered | | |- 📜 dlp-logo.ico : DLP Logo | |- 📂 src: -| | |- 📂 backend_outputs: -| | | |- 📜 model.pkl -| | | |- 📜 my_deep_learning_model.onnx : Last ONNX file output -| | | |- 📜 model.pt : Last model.pt output +| | |- 📂 pages: +| | | |- 📂 train: +| | | | |- 📜 [train_space_id].tsx +| | | | |- 📜 index.tsx +| | | |- 📜 dashboard.tsx +| | | |- 📜 learn.tsx +| | | |- 📜 settings.tsx +| | | |- 📜 about.tsx +| | | |- 📜 feedback.tsx +| | | |- 📜 wiki.tsx +| | | |- 📜 forgot.tsx +| | | |- 📜 _document.tsx +| | | |- 📜 LearnContent.tsx +| | | |- 📜 _app.tsx +| | | |- 📜 login.tsx | | |- 📂 common: +| | | |- 📂 utils: +| | | | |- 📜 dateFormat.ts +| | | | |- 📜 dndHelpers.ts +| | | | |- 📜 firebase.ts | | | |- 📂 components: -| | | | |- 📜 EmailInput.tsx -| | | | |- 📜 TitleText.tsx -| | | | |- 📜 DlpTooltip.tsx | | | | |- 📜 Spacer.tsx -| | | | |- 📜 NavBarMain.tsx +| | | | |- 📜 DlpTooltip.tsx +| | | | |- 📜 TitleText.tsx +| | | | |- 📜 EmailInput.tsx | | | | |- 📜 HtmlTooltip.tsx | | | | |- 📜 ClientOnlyPortal.tsx | | | | |- 📜 Footer.tsx +| | | | |- 📜 NavBarMain.tsx | | | |- 📂 styles: | | | | |- 📜 Home.module.css | | | | |- 📜 globals.css | | | |- 📂 redux: -| | | | |- 📜 userLogin.ts -| | | | |- 📜 backendApi.ts | | | | |- 📜 hooks.ts | | | | |- 📜 store.ts +| | | | |- 📜 backendApi.ts +| | | | |- 📜 userLogin.ts | | | | |- 📜 train.ts -| | | |- 📂 utils: -| | | | |- 📜 dateFormat.ts -| | | | |- 📜 firebase.ts -| | | | |- 📜 dndHelpers.ts | | |- 📂 features: -| | | |- 📂 OpenAi: -| | | | |- 📜 openAiUtils.ts -| | | |- 📂 LearnMod: -| | | | |- 📜 Exercise.tsx -| | | | |- 📜 FRQuestion.tsx -| | | | |- 📜 ClassCard.tsx -| | | | |- 📜 ModulesSideBar.tsx -| | | | |- 📜 MCQuestion.tsx -| | | | |- 📜 LearningModulesContent.tsx -| | | | |- 📜 ImageComponent.tsx | | | |- 📂 Train: +| | | | |- 📂 constants: +| | | | | |- 📜 trainConstants.ts | | | | |- 📂 components: -| | | | | |- 📜 TrainspaceLayout.tsx | | | | | |- 📜 CreateTrainspace.tsx +| | | | | |- 📜 TrainspaceLayout.tsx | | | | | |- 📜 DatasetStepLayout.tsx -| | | | |- 📂 constants: -| | | | | |- 📜 trainConstants.ts -| | | | |- 📂 types: -| | | | | |- 📜 trainTypes.ts | | | | |- 📂 features: -| | | | | |- 📂 Tabular: -| | | | | | |- 📂 components: -| | | | | | | |- 📜 TabularTrainspace.tsx -| | | | | | | |- 📜 TabularDatasetStep.tsx -| | | | | | | |- 📜 TabularFlow.tsx -| | | | | | | |- 📜 TabularReviewStep.tsx -| | | | | | | |- 📜 TabularParametersStep.tsx -| | | | | | |- 📂 constants: -| | | | | | | |- 📜 tabularConstants.ts -| | | | | | |- 📂 types: -| | | | | | | |- 📜 tabularTypes.ts -| | | | | | |- 📂 redux: -| | | | | | | |- 📜 tabularActions.ts -| | | | | | | |- 📜 tabularApi.ts -| | | | | | |- 📜 index.ts | | | | | |- 📂 Image: +| | | | | | |- 📂 constants: +| | | | | | | |- 📜 imageConstants.ts | | | | | | |- 📂 components: -| | | | | | | |- 📜 ImageReviewStep.tsx | | | | | | | |- 📜 ImageTrainspace.tsx +| | | | | | | |- 📜 ImageParametersStep.tsx | | | | | | | |- 📜 ImageFlow.tsx | | | | | | | |- 📜 ImageDatasetStep.tsx -| | | | | | | |- 📜 ImageParametersStep.tsx -| | | | | | |- 📂 constants: -| | | | | | | |- 📜 imageConstants.ts +| | | | | | | |- 📜 ImageReviewStep.tsx +| | | | | | |- 📂 redux: +| | | | | | | |- 📜 imageActions.ts +| | | | | | | |- 📜 imageApi.ts | | | | | | |- 📂 types: | | | | | | | |- 📜 imageTypes.ts +| | | | | | |- 📜 index.ts +| | | | | |- 📂 Tabular: +| | | | | | |- 📂 constants: +| | | | | | | |- 📜 tabularConstants.ts +| | | | | | |- 📂 components: +| | | | | | | |- 📜 TabularDatasetStep.tsx +| | | | | | | |- 📜 TabularReviewStep.tsx +| | | | | | | |- 📜 TabularFlow.tsx +| | | | | | | |- 📜 TabularTrainspace.tsx +| | | | | | | |- 📜 TabularParametersStep.tsx | | | | | | |- 📂 redux: -| | | | | | | |- 📜 imageApi.ts -| | | | | | | |- 📜 imageActions.ts +| | | | | | | |- 📜 tabularApi.ts +| | | | | | | |- 📜 tabularActions.ts +| | | | | | |- 📂 types: +| | | | | | | |- 📜 tabularTypes.ts | | | | | | |- 📜 index.ts | | | | |- 📂 redux: | | | | | |- 📜 trainspaceApi.ts | | | | | |- 📜 trainspaceSlice.ts +| | | | |- 📂 types: +| | | | | |- 📜 trainTypes.ts | | | |- 📂 Feedback: | | | | |- 📂 redux: | | | | | |- 📜 feedbackApi.ts @@ -194,41 +200,35 @@ | | | | | |- 📜 TrainDataGrid.tsx | | | | |- 📂 redux: | | | | | |- 📜 dashboardApi.ts +| | | |- 📂 LearnMod: +| | | | |- 📜 FRQuestion.tsx +| | | | |- 📜 MCQuestion.tsx +| | | | |- 📜 LearningModulesContent.tsx +| | | | |- 📜 Exercise.tsx +| | | | |- 📜 ClassCard.tsx +| | | | |- 📜 ImageComponent.tsx +| | | | |- 📜 ModulesSideBar.tsx +| | | |- 📂 OpenAi: +| | | | |- 📜 openAiUtils.ts +| | |- 📂 backend_outputs: +| | | |- 📜 my_deep_learning_model.onnx : Last ONNX file output +| | | |- 📜 model.pkl +| | | |- 📜 model.pt : Last model.pt output | | |- 📂 __tests__: | | | |- 📂 common: | | | | |- 📂 components: | | | | | |- 📜 TitleText.test.tsx -| | |- 📂 pages: -| | | |- 📂 train: -| | | | |- 📜 [train_space_id].tsx -| | | | |- 📜 index.tsx -| | | |- 📜 settings.tsx -| | | |- 📜 _app.tsx -| | | |- 📜 feedback.tsx -| | | |- 📜 _document.tsx -| | | |- 📜 wiki.tsx -| | | |- 📜 login.tsx -| | | |- 📜 learn.tsx -| | | |- 📜 forgot.tsx -| | | |- 📜 LearnContent.tsx -| | | |- 📜 dashboard.tsx -| | | |- 📜 about.tsx -| | |- 📜 constants.ts | | |- 📜 next-env.d.ts | | |- 📜 iris.csv : Sample CSV data | | |- 📜 GlobalStyle.ts -| |- 📂 layer_docs: -| | |- 📜 Softmax.md : Doc for Softmax layer -| | |- 📜 Linear.md : Doc for Linear layer -| | |- 📜 softmax_equation.png : PNG file of Softmax equation -| | |- 📜 ReLU.md : Doc for ReLU later -| |- 📜 .eslintignore -| |- 📜 next.config.js +| | |- 📜 constants.ts | |- 📜 next-env.d.ts +| |- 📜 .eslintignore +| |- 📜 jest.config.ts | |- 📜 pnpm-lock.yaml -| |- 📜 tsconfig.json | |- 📜 .eslintrc.json +| |- 📜 next.config.js +| |- 📜 tsconfig.json | |- 📜 package.json -| |- 📜 jest.config.ts ``` diff --git a/package.json b/package.json new file mode 100644 index 000000000..7df60b1ee --- /dev/null +++ b/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "mamba": "^0.0.2", + "poetry": "^0.6.28" + } +} diff --git a/serverless/packages/functions/src/trainspace/create_image_trainspace.ts b/serverless/packages/functions/src/trainspace/create_image_trainspace.ts deleted file mode 100644 index fd7cb6aea..000000000 --- a/serverless/packages/functions/src/trainspace/create_image_trainspace.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { APIGatewayProxyHandlerV2 } from "aws-lambda"; -import parseJwt from "@dlp-sst-app/core/parseJwt"; -import { v4 as uuidv4 } from 'uuid'; -import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; -import { DynamoDBDocumentClient, PutCommand, PutCommandInput } from '@aws-sdk/lib-dynamodb'; -import { TrainStatus } from './constants'; - -export const handler: APIGatewayProxyHandlerV2 = async (event) => { - if (event) { - const user_id: string = parseJwt(event.headers.authorization ?? "")["user_id"]; - const eventBody = JSON.parse(event.body? event.body : ""); - - const trainspaceId = uuidv4(); - const putCommandInput: PutCommandInput = { - TableName: "trainspace", - Item: - { - trainspace_id: trainspaceId, - uid: user_id, - created: Date.now().toString(), - data_source: 'IMAGE', - dataset_data: JSON.stringify(eventBody['dataset_data']), - name: eventBody['name'], - parameters_data: JSON.stringify(eventBody['parameters_data']), - review_data: eventBody['review_data'], - status: TrainStatus.QUEUED - } - } - - if (putCommandInput == null) - { - return { - statusCode: 400, - body: JSON.stringify({ message: "Invalid request body" }) - } - } - - const client = new DynamoDBClient({}); - const docClient = DynamoDBDocumentClient.from(client); - - const command = new PutCommand(putCommandInput); - const response = await docClient.send(command); - - if (response.$metadata.httpStatusCode != 200) { - client.destroy(); - - return { - statusCode: 500, - body: JSON.stringify({ message: "Internal server error."}) - }; - } - - client.destroy(); - return { - statusCode: 200, - body: JSON.stringify({ trainspaceId: trainspaceId, message: "Successfully created a new trainspace."}) - }; - } - return { - statusCode: 404, - body: JSON.stringify({ message: "Not Found" }), - }; -}; \ No newline at end of file diff --git a/serverless/packages/functions/src/trainspace/create_tabular_trainspace.ts b/serverless/packages/functions/src/trainspace/create_tabular_trainspace.ts deleted file mode 100644 index 5a2bc6976..000000000 --- a/serverless/packages/functions/src/trainspace/create_tabular_trainspace.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { APIGatewayProxyHandlerV2 } from "aws-lambda"; -import parseJwt from "@dlp-sst-app/core/parseJwt"; -import { v4 as uuidv4 } from 'uuid'; -import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; -import { DynamoDBDocumentClient, PutCommand, PutCommandInput } from '@aws-sdk/lib-dynamodb'; -import { TrainStatus } from './constants'; - -export const handler: APIGatewayProxyHandlerV2 = async (event) => { - if (event) { - const user_id: string = parseJwt(event.headers.authorization ?? "")["user_id"]; - const eventBody = JSON.parse(event.body? event.body : ""); - - const trainspaceId = uuidv4(); - let putCommandInput: PutCommandInput = { - TableName: "trainspace", - Item: - { - trainspace_id: trainspaceId, - uid: user_id, - created: Date.now().toString(), - data_source: 'TABULAR', - dataset_data: JSON.stringify(eventBody['dataset_data']), - name: eventBody['name'], - parameters_data: { - criterion: eventBody['criterion'], - optimizer_name: eventBody['optimizer_name'], - shuffle: eventBody['shuffle'], - epochs: eventBody['epochs'], - batch_size: eventBody['batch_size'], - user_arch: eventBody['user_arch'] - }, - review_data: "", - status: TrainStatus.QUEUED - } - } - - if (putCommandInput == null) - { - return { - statusCode: 400, - body: JSON.stringify({ message: "Invalid request body" }) - } - } - - const client = new DynamoDBClient({}); - const docClient = DynamoDBDocumentClient.from(client); - - const command = new PutCommand(putCommandInput); - const response = await docClient.send(command); - - if (response.$metadata.httpStatusCode != 200) { - return { - statusCode: 500, - body: JSON.stringify({ message: "Internal server error."}) - }; - } - - return { - statusCode: 200, - body: JSON.stringify({ trainspaceId: trainspaceId, message: "Successfully created a new trainspace."}) - }; - } - return { - statusCode: 404, - body: JSON.stringify({ message: "Not Found" }), - }; -}; \ No newline at end of file diff --git a/serverless/packages/functions/src/trainspace/create_trainspace.ts b/serverless/packages/functions/src/trainspace/create_trainspace.ts index 0d5f5ea74..2ab39611e 100644 --- a/serverless/packages/functions/src/trainspace/create_trainspace.ts +++ b/serverless/packages/functions/src/trainspace/create_trainspace.ts @@ -1,66 +1,32 @@ -import { APIGatewayProxyHandlerV2 } from "aws-lambda"; -import parseJwt from "@dlp-sst-app/core/parseJwt"; +import { APIGatewayProxyEventV2 } from "aws-lambda"; +import parseJwt from "@dlp-sst-app/core/src/parseJwt"; import { v4 as uuidv4 } from 'uuid'; -import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; -import { DynamoDBDocumentClient, PutCommand, PutCommandInput } from '@aws-sdk/lib-dynamodb'; +import { DynamoDBClient, PutItemCommand } from '@aws-sdk/client-dynamodb'; import { TrainStatus } from './constants'; -export const handler: APIGatewayProxyHandlerV2 = async (event) => { +export async function handler(event : APIGatewayProxyEventV2) { if (event) { const user_id: string = parseJwt(event.headers.authorization ?? "")["user_id"]; const eventBody = JSON.parse(event.body? event.body : ""); const trainspaceId = uuidv4(); - const datasetArray = eventBody['datasets']; - const modelArray = eventBody['models']; - const blockArray = eventBody['blocks']; - - let putCommandInput: PutCommandInput = { - TableName: "TrainspaceTable", - Item: - { - trainspace_id: trainspaceId, - created: Date.now().toString(), - uid: user_id, + const client = new DynamoDBClient({}); - datasets: datasetArray.map((eventBody: {[x: string] : any;}) => ({ - dataset: removeUndefinedValues(eventBody['dataset_source'] == 's3' ? { - data_source: eventBody['dataset_source']?.trim(), - dataset_id: eventBody['dataset_id']?.trim(), - s3_url: eventBody['s3_url']?.trim(), - } : { - data_source: eventBody['dataset_source']?.trim(), - dataset_id: eventBody['dataset_id']?.trim() - }) - })), - models: modelArray.map((eventBody: { [x: string]: any; }) => ({ - model: removeUndefinedValues({ - model_name: eventBody['model_name']?.trim(), - model_id: eventBody['model_id']?.trim(), - //dataset_id: eventBody['dataset_id']?.trim(), - model_versions: { - model_name: eventBody['model_name']?.trim(), - s3_url: eventBody['model_s3_url']?.trim() - } - }) - })), - blocks: blockArray.map((eventBody: {[x: string] : any;}) => ({ - block: removeUndefinedValues({ - block_id: eventBody['block_id']?.trim(), - block_type: eventBody['block_type']?.trim(), - //not sure what to do with "other info based on the type of block" - run_data: { - s3_url: eventBody['block_s3_url']?.trim(), - string: eventBody['run_string']?.trim(), - number: eventBody['run_number']?.trim() - } - }) - })), - status: TrainStatus.QUEUED + const putCommand : PutItemCommand = new PutItemCommand({ + TableName : "TrainspaceTable", + Item: { + trainspace_id: {"S": trainspaceId}, + name: {"S": eventBody['name']}, + user_id: {"S": user_id}, + data_source: {"S": eventBody['data_source']}, + dataset_data: {"S": eventBody['dataset_data']}, + review_data: {"S": eventBody['review_data']}, + model_id: {"S": eventBody['model_id']}, + results_s3: {"S": eventBody['results_s3']}, + status: {"S": TrainStatus.QUEUED} } - } - - if (putCommandInput == null) + }); + if (putCommand == null) { return { statusCode: 400, @@ -68,11 +34,7 @@ export const handler: APIGatewayProxyHandlerV2 = async (event) => { } } - const client = new DynamoDBClient({}); - const docClient = DynamoDBDocumentClient.from(client); - - const command = new PutCommand(putCommandInput); - const response = await docClient.send(command); + const response = await client.send(putCommand); if (response.$metadata.httpStatusCode != 200) { return { @@ -88,7 +50,7 @@ export const handler: APIGatewayProxyHandlerV2 = async (event) => { } return { statusCode: 404, - body: JSON.stringify({ message: "Not Found" }), + body: JSON.stringify({ message: "Event not Found" }), }; }; function removeUndefinedValues(obj: { [key: string]: any }) { diff --git a/serverless/packages/functions/src/trainspace/delete_all_trainspace.ts b/serverless/packages/functions/src/trainspace/delete_all_trainspace.ts new file mode 100644 index 000000000..0da94bcdb --- /dev/null +++ b/serverless/packages/functions/src/trainspace/delete_all_trainspace.ts @@ -0,0 +1,78 @@ +import { APIGatewayProxyEventV2 } from "aws-lambda"; +import parseJwt from "@dlp-sst-app/core/src/parseJwt"; +import { DynamoDBClient, QueryCommand, BatchExecuteStatementCommand, BatchStatementRequest } from '@aws-sdk/client-dynamodb'; + +export async function handler(event : APIGatewayProxyEventV2) { + if (event) { + const uid: string = parseJwt(event.headers.authorization ?? "")[ + "user_id" + ]; + const client = new DynamoDBClient({}); + + const foundTrainspaceIds: Array = [] + const deletedTrainspaceIds: Array = []; + let lastEvaluatedKey = undefined; + do { + const queryCommand: QueryCommand = new QueryCommand({ + TableName: "TrainspaceTable", + IndexName: "user_id_index", + KeyConditionExpression: "user_id = :uid", + ExpressionAttributeValues: { + ":uid" : {"S": uid} + }, + Limit: 25, + ExclusiveStartKey: lastEvaluatedKey + }); + + const currentTrainspaceIds: Array = [] + const getResults = await client.send(queryCommand); + if (getResults["Count"] !== 0 && getResults['Items']) { + const page: Array = getResults['Items'].map(trainspace => trainspace['trainspace_id'].S); + page.forEach(id => { + if (id) foundTrainspaceIds.push(id); + if (id) currentTrainspaceIds.push(id); + }); + } else { + return { + statusCode: 405, + body: JSON.stringify({ message: "no trainspaces deleted: none found associated with user"}) + } + } + + lastEvaluatedKey = getResults.LastEvaluatedKey; + + const statements: BatchStatementRequest[] = []; + for (const id of currentTrainspaceIds) { + statements.push( { + Statement: "DELETE FROM TrainspaceTable where trainspace_id=?", + Parameters: [{ "S": id.valueOf() }] + }); + deletedTrainspaceIds.push(id.valueOf()); + } + + const command = new BatchExecuteStatementCommand({ + Statements: statements + }); + + const response = await client.send(command); + + if (response.$metadata.httpStatusCode == undefined || response.$metadata.httpStatusCode != 200) + { + return { + statusCode: 404, + body: JSON.stringify({ message : "Delete operation failed" }) + } + } + } while (lastEvaluatedKey !== undefined); + if (deletedTrainspaceIds.length !== 0) { + return { + statusCode: 200, + body: JSON.stringify({ message: "Succesfully deleted trainspaces", trainspace_ids : foundTrainspaceIds}) + }; + } + } + return { + statusCode: 400, + body: JSON.stringify({ message: "Event Not Found" }), + }; +}; \ No newline at end of file diff --git a/serverless/packages/functions/src/trainspace/delete_trainspace.ts b/serverless/packages/functions/src/trainspace/delete_trainspace.ts index 483f39179..d431fc0e0 100644 --- a/serverless/packages/functions/src/trainspace/delete_trainspace.ts +++ b/serverless/packages/functions/src/trainspace/delete_trainspace.ts @@ -1,32 +1,29 @@ -import { APIGatewayProxyHandlerV2 } from "aws-lambda"; +import { APIGatewayProxyEventV2 } from "aws-lambda"; +import { DynamoDBClient, DeleteItemCommand } from '@aws-sdk/client-dynamodb'; -import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; -import { DynamoDBDocumentClient, DeleteCommand } from '@aws-sdk/lib-dynamodb'; - -export const handler: APIGatewayProxyHandlerV2 = async (event) => { +export async function handler(event : APIGatewayProxyEventV2) { let queryParams = null; if (event && (queryParams = event['pathParameters']) != null) { const trainspaceId: string | undefined = queryParams['id']; if (trainspaceId == undefined) { return { - statusCode: 400, + statusCode: 401, body: JSON.stringify({ message : "Malformed request content - trainspace ID missing." }), }; } const client = new DynamoDBClient({}); - const docClient = DynamoDBDocumentClient.from(client); - - const command = new DeleteCommand({ - TableName : "trainspace", + + const command = new DeleteItemCommand({ + TableName : "TrainspaceTable", Key : { - trainspace_id: trainspaceId + trainspace_id: {"S": trainspaceId} } }); - const response = await docClient.send(command); + const response = await client.send(command); if (response.$metadata.httpStatusCode == undefined || response.$metadata.httpStatusCode != 200) { return { diff --git a/serverless/packages/functions/src/trainspace/get_all_trainspace.ts b/serverless/packages/functions/src/trainspace/get_all_trainspace.ts index 4e2cf7733..2a0a7355f 100644 --- a/serverless/packages/functions/src/trainspace/get_all_trainspace.ts +++ b/serverless/packages/functions/src/trainspace/get_all_trainspace.ts @@ -1,45 +1,41 @@ -import { APIGatewayProxyHandlerV2 } from "aws-lambda"; -import parseJwt from "@dlp-sst-app/core/parseJwt"; +import { APIGatewayProxyEventV2 } from "aws-lambda"; +import parseJwt from "@dlp-sst-app/core/src/parseJwt"; import { DynamoDBClient, QueryCommand } from '@aws-sdk/client-dynamodb'; -export const handler: APIGatewayProxyHandlerV2 = async (event) => { +export async function handler(event : APIGatewayProxyEventV2) { if (event) { const uid: string = parseJwt(event.headers.authorization ?? "")[ "user_id" ]; - const client = new DynamoDBClient({}); - const fetchedTrainspaceIds: Array = []; let lastEvaluatedKey = undefined; - do { - const getCommand: QueryCommand = new QueryCommand({ + const queryCommand: QueryCommand = new QueryCommand({ TableName: "TrainspaceTable", - IndexName: "uid-index", - KeyConditionExpression: "uid = :uid", + IndexName: "user_id_index", + KeyConditionExpression: "user_id = :uid", ExpressionAttributeValues: { - ":uid" : - { - "S": uid - } + ":uid" : {"S": uid} }, ExclusiveStartKey: lastEvaluatedKey }); - const results = await client.send(getCommand); - lastEvaluatedKey = results.LastEvaluatedKey; + const results = await client.send(queryCommand); - if (results['Items']) { + if (results.Items && results.Count != 0) { const page: Array = results['Items']?.map(trainspace => trainspace['trainspace_id'].S); page.forEach(id => { if (id) fetchedTrainspaceIds.push(id); }); } else { - console.log("no items fetched"); + return { + statusCode: 404, + body: JSON.stringify({message: "no trainspaces associated with user"}) + } } - - + lastEvaluatedKey = results.LastEvaluatedKey; } while (lastEvaluatedKey !== undefined); + return { statusCode: 200, body: JSON.stringify({ trainspace_ids : fetchedTrainspaceIds}) @@ -47,7 +43,7 @@ export const handler: APIGatewayProxyHandlerV2 = async (event) => { } return { - statusCode: 404, + statusCode: 400, body: JSON.stringify({ message: "Not Found" }), }; }; \ No newline at end of file diff --git a/serverless/packages/functions/src/trainspace/get_trainspace.ts b/serverless/packages/functions/src/trainspace/get_trainspace.ts index 3a3a5def7..d2410776e 100644 --- a/serverless/packages/functions/src/trainspace/get_trainspace.ts +++ b/serverless/packages/functions/src/trainspace/get_trainspace.ts @@ -1,32 +1,29 @@ -import { APIGatewayProxyHandlerV2, APIGatewayProxyEventV2 } from "aws-lambda"; -import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; -import { DynamoDBDocumentClient, GetCommand } from '@aws-sdk/lib-dynamodb'; +import { APIGatewayProxyEventV2 } from "aws-lambda"; +import { DynamoDBClient, GetItemCommand } from '@aws-sdk/client-dynamodb'; -export const handler: APIGatewayProxyHandlerV2 = async (event : APIGatewayProxyEventV2) => { - const queryParams = event['pathParameters']; - if (queryParams != null) - { +export async function handler(event : APIGatewayProxyEventV2) { + let queryParams = null; + if (event && (queryParams = event['pathParameters']) != null) { const trainspaceId: string | undefined = queryParams['id']; if (trainspaceId == undefined) { return { - statusCode: 400, + statusCode: 401, body: JSON.stringify({message: "Malformed request content - trainspace ID missing."}) }; } const client: DynamoDBClient = new DynamoDBClient({}); - const docClient = DynamoDBDocumentClient.from(client); - const command : GetCommand = new GetCommand({ + const command : GetItemCommand = new GetItemCommand({ TableName : "TrainspaceTable", Key : { - trainspace_id : trainspaceId + trainspace_id : {"S": trainspaceId} } }); - const response = await docClient.send(command); + const response = await client.send(command); if (!response.Item) { return { diff --git a/serverless/packages/functions/src/trainspace/tests/create_trainspace.test.ts b/serverless/packages/functions/src/trainspace/tests/create_trainspace.test.ts new file mode 100644 index 000000000..df48805d0 --- /dev/null +++ b/serverless/packages/functions/src/trainspace/tests/create_trainspace.test.ts @@ -0,0 +1,79 @@ +import { APIGatewayProxyEventV2 } from "aws-lambda"; +import { beforeEach, expect, it, vi} from "vitest"; +import { DynamoDBClient, PutItemCommand} from '@aws-sdk/client-dynamodb'; +import { mockClient } from 'aws-sdk-client-mock'; +import { handler } from '../create_trainspace'; + +//mocks parseJwt so that the call just returns whatever the input is +vi.mock('@dlp-sst-app/core/src/parseJwt', async () => { + return { + default: vi.fn().mockImplementation(input => input), + } +}) + +beforeEach(async () => { + ddbMock.reset(); +}) + +const ddbMock = mockClient(DynamoDBClient); + +it("test successful create trainspace call", async () => { + ddbMock.on(PutItemCommand).resolves({ + $metadata: { + httpStatusCode: 200, + } + }) + // @ts-expect-error : error doesn't affect functionality. We don't need the rest of the event, and it's really long for no reason + const event: APIGatewayProxyEventV2 = { + headers: { + authorization: 'abcd', + + }, + body: '{\n' + + ' "name": "SOME NAME",\n' + + ' "data_source": "SOME DATA SOURCE",\n' + + ' "dataset_data": "SOME DATA",\n' + + ' "review_data": "SOME REVIEW DATA",\n' + + ' "model_id": "SOME MODEL ID",\n' + + ' "results_s3": "SOME RESULTS"\n' + + '}', + } + const result = await handler(event); + expect(result.statusCode).toEqual(200); +}); + +it("test internal service error", async () => { + ddbMock.on(PutItemCommand).resolves({ + $metadata: { + httpStatusCode: 456, + } + }) + // @ts-expect-error : error doesn't affect functionality. We don't need the rest of the event, and it's really long for no reason + const event: APIGatewayProxyEventV2 = { + headers: { + authorization: 'abcd', + }, + body: '{\n' + + ' "name": "SOME NAME",\n' + + ' "data_source": "SOME DATA SOURCE",\n' + + ' "dataset_data": "SOME DATA",\n' + + ' "review_data": "SOME REVIEW DATA",\n' + + ' "model_id": "SOME MODEL ID",\n' + + ' "results_s3": "SOME RESULTS"\n' + + '}', + } + + const result = await handler(event); + expect(result.statusCode).toEqual(500); + }); + +it("test undefined event", async () => { + ddbMock.on(PutItemCommand).resolves({ + $metadata: { + httpStatusCode: 400, + } + }) + // @ts-expect-error : we are trying to cause an error + const result = await handler(undefined); + expect(result.statusCode).toEqual(404); + }); \ No newline at end of file diff --git a/serverless/packages/functions/src/trainspace/tests/delete_all_trainspace.test.ts b/serverless/packages/functions/src/trainspace/tests/delete_all_trainspace.test.ts new file mode 100644 index 000000000..d137ee3f6 --- /dev/null +++ b/serverless/packages/functions/src/trainspace/tests/delete_all_trainspace.test.ts @@ -0,0 +1,123 @@ +import { APIGatewayProxyEventV2 } from "aws-lambda"; +import { beforeEach, expect, it, vi} from "vitest"; +import { DynamoDBClient, QueryCommand, BatchExecuteStatementCommand } from '@aws-sdk/client-dynamodb'; +import { mockClient } from 'aws-sdk-client-mock'; +import { handler } from '../delete_all_trainspace'; + + +//mocks parseJwt so that the call just returns whatever the input is +vi.mock('@dlp-sst-app/core/src/parseJwt', async () => { + return { + default: (input: String) => ({ user_id: input }) + } +}); + +beforeEach(async () => { + ddbMock.reset(); +}); + +const ddbMock = mockClient(DynamoDBClient); + +it("test successful delete all trainspace call", async () => { + ddbMock.on(QueryCommand).resolves({ + "Items": [{ + "trainspace_id": { "S": "test id" }, + }], + "Count": 1 + }); + ddbMock.on(BatchExecuteStatementCommand).resolves({ + $metadata: { + httpStatusCode: 200, + } + }) + + // @ts-expect-error : error doesn't affect functionality. We don't need the rest of the event, and it's really long for no reason + const event: APIGatewayProxyEventV2 = { + headers: { + authorization: 'abcd', + }, + } + const result = await handler(event); + expect(result.statusCode).toEqual(200); +}); + + +it("test no batch delete response call", async () => { + ddbMock.on(QueryCommand).resolves({ + "Items": [{ + "user_id":{"S":"abcd"}, + "trainspace_id": { "S": "test id" }, + }], + "Count": 4 + }); + ddbMock.on(BatchExecuteStatementCommand).resolves({ + $metadata: { + httpStatusCode: undefined, + } + }) + + // @ts-expect-error : error doesn't affect functionality. We don't need the rest of the event, and it's really long for no reason + const event: APIGatewayProxyEventV2 = { + headers: { + authorization: 'abcd', + }, + } + + const result = await handler(event); + expect(result.statusCode).toEqual(404); +}); + +it("test incorrect batch delete response failed call", async () => { + ddbMock.on(QueryCommand).resolves({ + "Items": [{ + "user_id":{"S":"abcd"}, + "trainspace_id": { "S": "test id" }, + }], + "Count": 4 + }); + ddbMock.on(BatchExecuteStatementCommand).resolves({ + $metadata: { + httpStatusCode: 267, + } + }) + + // @ts-expect-error : error doesn't affect functionality. We don't need the rest of the event, and it's really long for no reason + const event: APIGatewayProxyEventV2 = { + headers: { + authorization: 'abcd', + }, + } + + const result = await handler(event); + expect(result.statusCode).toEqual(404); +}); + + +it("test delete all on no existing trainspaces call", async () => { + ddbMock.on(QueryCommand).resolves({ + "Items": [], + "Count": 0 + }); + ddbMock.on(BatchExecuteStatementCommand).resolves({ + $metadata: { + httpStatusCode: 200, + } + }) + + // @ts-expect-error : error doesn't affect functionality. We don't need the rest of the event, and it's really long for no reason + const event: APIGatewayProxyEventV2 = { + headers: { + authorization: 'abcd', + }, + } + + const result = await handler(event); + expect(result.statusCode).toEqual(405); +}); + +it("test malformed call", async () => { + + // @ts-expect-error : we are trying to cause an error + const result = await handler(undefined); + expect(result.statusCode).toEqual(400); +}); \ No newline at end of file diff --git a/serverless/packages/functions/src/trainspace/tests/delete_trainspace.test.ts b/serverless/packages/functions/src/trainspace/tests/delete_trainspace.test.ts new file mode 100644 index 000000000..e0d840a7b --- /dev/null +++ b/serverless/packages/functions/src/trainspace/tests/delete_trainspace.test.ts @@ -0,0 +1,120 @@ +import { APIGatewayProxyEventV2 } from "aws-lambda"; +import { beforeEach, expect, it, vi} from "vitest"; +import { DynamoDBClient, DeleteItemCommand } from '@aws-sdk/client-dynamodb'; +import { mockClient } from 'aws-sdk-client-mock'; +import { handler } from '../delete_trainspace'; + +//mocks parseJwt so that the call just returns whatever the input is +vi.mock('@dlp-sst-app/core/src/parseJwt', async () => { + return { + default: vi.fn().mockImplementation(input => input), + } +}) + +beforeEach(async () => { + ddbMock.reset(); +}) + +const ddbMock = mockClient(DynamoDBClient); + +it("test successful delete trainspace call", async () => { + ddbMock.on(DeleteItemCommand).resolves({ + $metadata: { + httpStatusCode: 200, + } + }) + // @ts-expect-error : error doesn't affect functionality. We don't need the rest of the event, and it's really long for no reason + const event: APIGatewayProxyEventV2 = { + headers: { + authorization: 'abcd', + }, + pathParameters: { + id: "trainspace_id" + }, + body: '{\n' + + ' "name": "TEST NAME",\n' + + ' "email": "TESTEMAIL@GMAIL.COM",\n' + + ' "phone": "123-456-7890"\n' + + '}', +} + + const result = await handler(event); + expect(result.statusCode).toEqual(200); +}); + + +it("test no response failed operation call", async () => { + ddbMock.on(DeleteItemCommand).resolves({ + $metadata: { + httpStatusCode: undefined, + } + }) + + // @ts-expect-error : error doesn't affect functionality. We don't need the rest of the event, and it's really long for no reason + const event: APIGatewayProxyEventV2 = { + headers: { + authorization: 'abcd', + }, + pathParameters: { + id: "trainspace_id" + }, + body: '{\n' + + ' "name": "TEST NAME",\n' + + ' "email": "TESTEMAIL@GMAIL.COM",\n' + + ' "phone": "123-456-7890"\n' + + '}', + } + + const result = await handler(event); + expect(result.statusCode).toEqual(404); +}); + + +it("test different status code failed operation call", async () => { + ddbMock.on(DeleteItemCommand).resolves({ + $metadata: { + httpStatusCode: 267, + } + }) + // @ts-expect-error : error doesn't affect functionality. We don't need the rest of the event, and it's really long for no reason + const event: APIGatewayProxyEventV2 = { + headers: { + authorization: 'abcd', + }, + pathParameters: { + id: "trainspace_id" + }, + body: '{\n' + + ' "name": "TEST NAME",\n' + + ' "email": "TESTEMAIL@GMAIL.COM",\n' + + ' "phone": "123-456-7890"\n' + + '}', + } + + const result = await handler(event); + expect(result.statusCode).toEqual(404); +}); + +it("test no trainspace id given", async () => { + ddbMock.on(DeleteItemCommand).resolves({ + $metadata: { + httpStatusCode: 267, + } + }) + + // @ts-expect-error : error doesn't affect functionality. We don't need the rest of the event, and it's really long for no reason + const event: APIGatewayProxyEventV2 = { + pathParameters: { + } + } + + const result = await handler(event); + expect(result.statusCode).toEqual(401); +}); + + +it("test malformed call", async () => { + // @ts-expect-error : we are trying to cause an error + const result = await handler(undefined); + expect(result.statusCode).toEqual(400); +}); \ No newline at end of file diff --git a/serverless/packages/functions/src/trainspace/tests/get_all_trainspace.test.ts b/serverless/packages/functions/src/trainspace/tests/get_all_trainspace.test.ts new file mode 100644 index 000000000..00ba8dfc8 --- /dev/null +++ b/serverless/packages/functions/src/trainspace/tests/get_all_trainspace.test.ts @@ -0,0 +1,60 @@ +import { APIGatewayProxyEventV2 } from "aws-lambda"; +import { beforeEach, expect, it, vi} from "vitest"; +import { mockClient } from 'aws-sdk-client-mock'; +import { DynamoDBClient, QueryCommand } from '@aws-sdk/client-dynamodb'; +import { handler } from '../get_all_trainspace'; + +//mocks parseJwt so that the call just returns whatever the input is +vi.mock('@dlp-sst-app/core/src/parseJwt', async () => { + return { + default: (input: String) => ({ user_id: input }) + } +}) + +beforeEach(async () => { + ddbMock.reset(); +}) + +const ddbMock = mockClient(DynamoDBClient); + +it("test successful get all trainspace call", async () => { + ddbMock.on(QueryCommand).resolves({ + "Items": [{ + "user_id":{"S":"abcd"}, + "trainspace_id": { "S": "test id" }, + }], + "Count": 4 + }); + // @ts-expect-error : error doesn't affect functionality. We don't need the rest of the event, and it's really long for no reason + const event: APIGatewayProxyEventV2 = { + headers: { + authorization: 'abcd', + }, + } + + const result = await handler(event); + + expect(result.statusCode).toEqual(200); +}); + +it("test no existing trainspaces for user id", async () => { + ddbMock.on(QueryCommand).resolves({ + Items: undefined + }) + + // @ts-expect-error : error doesn't affect functionality. We don't need the rest of the event, and it's really long for no reason + const event: APIGatewayProxyEventV2 = { + headers: { + authorization: 'abcd', + }, + } + + const result = await handler(event); + expect(result.statusCode).toEqual(404); +}); + +it("test malformed request", async () => { + // @ts-expect-error : we are trying to cause an error + const result = await handler(undefined); + expect(result.statusCode).toEqual(400); +}); \ No newline at end of file diff --git a/serverless/packages/functions/src/trainspace/tests/get_trainspace.test.ts b/serverless/packages/functions/src/trainspace/tests/get_trainspace.test.ts new file mode 100644 index 000000000..fe8f7e2c1 --- /dev/null +++ b/serverless/packages/functions/src/trainspace/tests/get_trainspace.test.ts @@ -0,0 +1,73 @@ +import { APIGatewayProxyEventV2 } from "aws-lambda"; +import { beforeEach, expect, it, vi} from "vitest"; +import { DynamoDBClient, GetItemCommand } from '@aws-sdk/client-dynamodb'; +import { mockClient } from 'aws-sdk-client-mock'; +import { handler } from '../get_trainspace'; + +//mocks parseJwt so that the call just returns whatever the input is +vi.mock('@dlp-sst-app/core/src/parseJwt', async () => { + return { + default: vi.fn().mockImplementation(input => input), + } +}) + +beforeEach(async () => { + ddbMock.reset(); +}) + +const ddbMock = mockClient(DynamoDBClient); + + +it("test successful get trainspace call", async () => { + ddbMock.on(GetItemCommand).resolves({ + Item: { trainspaceID: { S: 'sample trainspace id' } } + }) + + // @ts-expect-error : error doesn't affect functionality. We don't need the rest of the event, and it's really long for no reason + const event: APIGatewayProxyEventV2 = { + pathParameters: { + id: "some trainspace_id" + }, + } + + const result = await handler(event); + expect(result.statusCode).toEqual(200); +}); + + +it("test no existing trainspace id", async () => { + ddbMock.on(GetItemCommand).resolves({ + Item: undefined + }) + + // @ts-expect-error : error doesn't affect functionality. We don't need the rest of the event, and it's really long for no reason + const event: APIGatewayProxyEventV2 = { + pathParameters: { + id: "some trainspace_id" + }, + } + + const result = await handler(event); + expect(result.statusCode).toEqual(404); +}); + +it("test no trainspace id given", async () => { + ddbMock.on(GetItemCommand).resolves({ + Item: { trainspaceID: { S: 'sample trainspace id' } } + }) + + // @ts-expect-error : error doesn't affect functionality. We don't need the rest of the event, and it's really long for no reason + const event: APIGatewayProxyEventV2 = { + pathParameters: { + } + } + + const result = await handler(event); + expect(result.statusCode).toEqual(401); +}); + +it("test malformed request", async () => { + // @ts-expect-error : we are trying to cause an error + const result = await handler(undefined); + expect(result.statusCode).toEqual(400); +}); \ No newline at end of file diff --git a/serverless/pnpm-lock.yaml b/serverless/pnpm-lock.yaml index 571331b6a..f5a864997 100644 --- a/serverless/pnpm-lock.yaml +++ b/serverless/pnpm-lock.yaml @@ -57,6 +57,9 @@ importers: typescript: specifier: ^5.2.2 version: 5.2.2 + vite-tsconfig-paths: + specifier: 4.3.1 + version: 4.3.1(typescript@5.2.2) vitest: specifier: ^0.34.6 version: 0.34.6 @@ -88,6 +91,9 @@ importers: sst: specifier: ^2.24.3 version: 2.40.1(@aws-sdk/credential-provider-node@3.509.0) + vite-tsconfig-paths: + specifier: 4.3.1 + version: 4.3.1(typescript@5.2.2) vitest: specifier: ^0.34.1 version: 0.34.6 @@ -148,6 +154,9 @@ packages: /@aws-cdk/cloud-assembly-schema@2.124.0: resolution: {integrity: sha512-vVGGXVwqug0/oSRf2meLerqxu11aN/ULKjcVnBw339kiWLBfZYXktqxWYtgXoaAiZBS7eGSuhKrPYDE2Jx2kdA==} engines: {node: '>= 14.15.0'} + dependencies: + jsonschema: 1.4.1 + semver: 7.5.4 dev: true bundledDependencies: - jsonschema @@ -173,6 +182,7 @@ packages: '@aws-cdk/cloud-assembly-schema': 2.124.0 dependencies: '@aws-cdk/cloud-assembly-schema': 2.124.0 + semver: 7.5.4 dev: true bundledDependencies: - semver @@ -5849,6 +5859,10 @@ packages: slash: 3.0.0 dev: true + /globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + dev: true + /gopd@1.0.1: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} dependencies: @@ -6257,6 +6271,10 @@ packages: graceful-fs: 4.2.11 dev: true + /jsonschema@1.4.1: + resolution: {integrity: sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==} + dev: true + /just-extend@6.2.0: resolution: {integrity: sha512-cYofQu2Xpom82S6qD778jBDpwvvy39s1l/hrYij2u9AMdQcGRpaBu6kY4mVhuno5kJVi1DAz4aiphA2WI1/OAw==} dev: true @@ -7603,6 +7621,19 @@ packages: typescript: 5.2.2 dev: true + /tsconfck@3.0.3(typescript@5.2.2): + resolution: {integrity: sha512-4t0noZX9t6GcPTfBAbIbbIU4pfpCwh0ueq3S4O/5qXI1VwK1outmxhe9dOiEWqMz3MW2LKgDTpqWV+37IWuVbA==} + engines: {node: ^18 || >=20} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + dependencies: + typescript: 5.2.2 + dev: true + /tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} @@ -7779,6 +7810,22 @@ packages: - terser dev: true + /vite-tsconfig-paths@4.3.1(typescript@5.2.2): + resolution: {integrity: sha512-cfgJwcGOsIxXOLU/nELPny2/LUD/lcf1IbfyeKTv2bsupVbTH/xpFtdQlBmIP1GEK2CjjLxYhFfB+QODFAx5aw==} + peerDependencies: + vite: '*' + peerDependenciesMeta: + vite: + optional: true + dependencies: + debug: 4.3.4 + globrex: 0.1.2 + tsconfck: 3.0.3(typescript@5.2.2) + transitivePeerDependencies: + - supports-color + - typescript + dev: true + /vite@5.1.0(@types/node@20.11.17): resolution: {integrity: sha512-STmSFzhY4ljuhz14bg9LkMTk3d98IO6DIArnTY6MeBwiD1Za2StcQtz7fzOUnRCqrHSD5+OS2reg4HOz1eoLnw==} engines: {node: ^18.0.0 || >=20.0.0} diff --git a/serverless/stacks/AppStack.test.ts b/serverless/stacks/AppStack.test.ts index 20df452a1..d9a409317 100644 --- a/serverless/stacks/AppStack.test.ts +++ b/serverless/stacks/AppStack.test.ts @@ -14,4 +14,17 @@ it("Check Appstack for User Endpoints", async () => { template.hasOutput("CreateUserFunctionName", Object); template.hasOutput("GetUserFunctionName", Object); template.hasOutput("DeleteUserFunctionName", Object); +}); + +it("Check Appstack for Trainspace Endpoints", async () => { + await initProject({}); + const app = new App({ mode: "deploy" }); + // WHEN + app.stack(AppStack); + // THEN + const template = Template.fromStack(getStack(AppStack)); + template.hasOutput("CreateTrainspaceFunctionName", Object); + template.hasOutput("GetAllTrainspaceIdsFunctionName", Object); + template.hasOutput("DeleteTrainspaceByIdFunctionName", Object); + template.hasOutput("DeleteAllTrainspaceByUIDFunctionName", Object); }); \ No newline at end of file diff --git a/serverless/stacks/AppStack.ts b/serverless/stacks/AppStack.ts index f03251ce0..b910bbde0 100644 --- a/serverless/stacks/AppStack.ts +++ b/serverless/stacks/AppStack.ts @@ -39,19 +39,7 @@ export function AppStack({ stack }: StackContext) { "packages/functions/src/datasets/user/columns.handler", "DELETE /dataset/user/{type}/{filename}" : "packages/functions/src/datasets/user/delete_url.handler", - "POST /trainspace/tabular": { - function: { - handler: "packages/functions/src/trainspace/create_tabular_trainspace.handler", - permissions: ["dynamodb:PutItem"] - } - }, - "POST /trainspace/image": { - function: { - handler: "packages/functions/src/trainspace/create_image_trainspace.handler", - permissions: ["dynamodb:PutItem"] - } - }, - "POST /trainspace/create": { + "POST /trainspace": { function: { handler: "packages/functions/src/trainspace/create_trainspace.handler", permissions: ["dynamodb:PutItem"] @@ -75,6 +63,12 @@ export function AppStack({ stack }: StackContext) { permissions: ["dynamodb:DeleteItem"] } }, + "DELETE /trainspace": { + function: { + handler: "packages/functions/src/trainspace/delete_all_trainspace.handler", + permissions: ["dynamodb:PartiQLDelete", "dynamodb:Query"] + } + }, "POST /user": { function: { handler: "packages/functions/src/user/create_user.handler", @@ -109,17 +103,15 @@ export function AppStack({ stack }: StackContext) { api.getFunction("GET /datasets/user/{type}/{filename}/columns") ?.functionName ?? "", CreateTrainspaceFunctionName: - api.getFunction("POST /trainspace/create")?.functionName ?? "", - PutTabularTrainspaceFunctionName: - api.getFunction("POST /trainspace/tabular")?.functionName ?? "", - PutImageTrainspaceFunctionName: - api.getFunction("POST /trainspace/tabular")?.functionName ?? "", + api.getFunction("POST /trainspace/")?.functionName ?? "", GetAllTrainspaceIdsFunctionName: api.getFunction("GET /trainspace")?.functionName ?? "", GetTrainspaceByIdFunctionName: api.getFunction("GET /trainspace/{id}")?.functionName ?? "", DeleteTrainspaceByIdFunctionName: api.getFunction("DELETE /trainspace/{id}")?.functionName ?? "", + DeleteAllTrainspaceByUIDFunctionName: + api.getFunction("DELETE /trainspace")?.functionName ?? "", CreateUserFunctionName: api.getFunction("POST /user")?.functionName ?? "", GetUserFunctionName: