@arkyn/server 1.9.1 → 1.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +24 -0
- package/README.md +116 -0
- package/dist/api/__test__/deleteRequest.spec.d.ts +2 -0
- package/dist/api/__test__/deleteRequest.spec.d.ts.map +1 -0
- package/dist/api/__test__/deleteRequest.spec.js +84 -0
- package/dist/api/__test__/getRequest.spec.d.ts +2 -0
- package/dist/api/__test__/getRequest.spec.d.ts.map +1 -0
- package/dist/api/__test__/getRequest.spec.js +62 -0
- package/dist/api/__test__/inboxFlowRequest.spec.d.ts +2 -0
- package/dist/api/__test__/inboxFlowRequest.spec.d.ts.map +1 -0
- package/dist/api/__test__/inboxFlowRequest.spec.js +96 -0
- package/dist/api/__test__/makeRequest.spec.d.ts +2 -0
- package/dist/api/__test__/makeRequest.spec.d.ts.map +1 -0
- package/dist/api/__test__/makeRequest.spec.js +80 -0
- package/dist/api/__test__/patchRequest.spec.d.ts +2 -0
- package/dist/api/__test__/patchRequest.spec.d.ts.map +1 -0
- package/dist/api/__test__/patchRequest.spec.js +84 -0
- package/dist/api/__test__/postRequest.spec.d.ts +2 -0
- package/dist/api/__test__/postRequest.spec.d.ts.map +1 -0
- package/dist/api/__test__/postRequest.spec.js +84 -0
- package/dist/api/__test__/putRequest.spec.d.ts +2 -0
- package/dist/api/__test__/putRequest.spec.d.ts.map +1 -0
- package/dist/api/__test__/putRequest.spec.js +84 -0
- package/dist/api/deleteRequest.d.ts +13 -0
- package/dist/api/deleteRequest.d.ts.map +1 -0
- package/dist/api/deleteRequest.js +14 -0
- package/dist/api/getRequest.d.ts +12 -0
- package/dist/api/getRequest.d.ts.map +1 -0
- package/dist/api/getRequest.js +13 -0
- package/dist/api/inboxFlowRequest.d.ts +40 -0
- package/dist/api/inboxFlowRequest.d.ts.map +1 -0
- package/dist/api/inboxFlowRequest.js +63 -0
- package/dist/api/makeRequest.d.ts +38 -0
- package/dist/api/makeRequest.d.ts.map +1 -0
- package/dist/api/makeRequest.js +103 -0
- package/dist/api/patchRequest.d.ts +13 -0
- package/dist/api/patchRequest.d.ts.map +1 -0
- package/dist/api/patchRequest.js +14 -0
- package/dist/api/postRequest.d.ts +13 -0
- package/dist/api/postRequest.d.ts.map +1 -0
- package/dist/api/postRequest.js +14 -0
- package/dist/api/putRequest.d.ts +13 -0
- package/dist/api/putRequest.d.ts.map +1 -0
- package/dist/api/putRequest.js +14 -0
- package/dist/config/__test__/apiInstance.spec.d.ts +2 -0
- package/dist/config/__test__/apiInstance.spec.d.ts.map +1 -0
- package/dist/config/__test__/apiInstance.spec.js +86 -0
- package/dist/config/__test__/inboxFlowInstance.spec.d.ts +2 -0
- package/dist/config/__test__/inboxFlowInstance.spec.d.ts.map +1 -0
- package/dist/config/__test__/inboxFlowInstance.spec.js +48 -0
- package/dist/config/apiInstance.d.ts +80 -0
- package/dist/config/apiInstance.d.ts.map +1 -0
- package/dist/config/apiInstance.js +111 -0
- package/dist/config/inboxFlowInstance.d.ts +44 -0
- package/dist/config/inboxFlowInstance.d.ts.map +1 -0
- package/dist/config/inboxFlowInstance.js +46 -0
- package/dist/http/badResponses/__test__/badGateway.spec.d.ts +2 -0
- package/dist/http/badResponses/__test__/badGateway.spec.d.ts.map +1 -0
- package/dist/http/badResponses/__test__/badGateway.spec.js +42 -0
- package/dist/http/badResponses/__test__/badRequest.spec.d.ts +2 -0
- package/dist/http/badResponses/__test__/badRequest.spec.d.ts.map +1 -0
- package/dist/http/badResponses/__test__/badRequest.spec.js +40 -0
- package/dist/http/badResponses/__test__/conflict.spec.d.ts +2 -0
- package/dist/http/badResponses/__test__/conflict.spec.d.ts.map +1 -0
- package/dist/http/badResponses/__test__/conflict.spec.js +40 -0
- package/dist/http/badResponses/__test__/forbidden.spec.d.ts +2 -0
- package/dist/http/badResponses/__test__/forbidden.spec.d.ts.map +1 -0
- package/dist/http/badResponses/__test__/forbidden.spec.js +40 -0
- package/dist/http/badResponses/__test__/notFound.spec.d.ts +2 -0
- package/dist/http/badResponses/__test__/notFound.spec.d.ts.map +1 -0
- package/dist/http/badResponses/__test__/notFound.spec.js +40 -0
- package/dist/http/badResponses/__test__/notImplemented.spec.d.ts +2 -0
- package/dist/http/badResponses/__test__/notImplemented.spec.d.ts.map +1 -0
- package/dist/http/badResponses/__test__/notImplemented.spec.js +40 -0
- package/dist/http/badResponses/__test__/serverError.spec.d.ts +2 -0
- package/dist/http/badResponses/__test__/serverError.spec.d.ts.map +1 -0
- package/dist/http/badResponses/__test__/serverError.spec.js +40 -0
- package/dist/http/badResponses/__test__/unauthorized.spec.d.ts +2 -0
- package/dist/http/badResponses/__test__/unauthorized.spec.d.ts.map +1 -0
- package/dist/http/badResponses/__test__/unauthorized.spec.js +40 -0
- package/dist/http/badResponses/__test__/unprocessableEntity.spec.d.ts +2 -0
- package/dist/http/badResponses/__test__/unprocessableEntity.spec.d.ts.map +1 -0
- package/dist/http/badResponses/__test__/unprocessableEntity.spec.js +66 -0
- package/dist/http/badResponses/badGateway.d.ts +34 -0
- package/dist/http/badResponses/badGateway.d.ts.map +1 -0
- package/dist/http/badResponses/badGateway.js +52 -0
- package/dist/http/badResponses/badRequest.d.ts +34 -0
- package/dist/http/badResponses/badRequest.d.ts.map +1 -0
- package/dist/http/badResponses/badRequest.js +52 -0
- package/dist/http/badResponses/conflict.d.ts +34 -0
- package/dist/http/badResponses/conflict.d.ts.map +1 -0
- package/dist/http/badResponses/conflict.js +52 -0
- package/dist/http/badResponses/forbidden.d.ts +34 -0
- package/dist/http/badResponses/forbidden.d.ts.map +1 -0
- package/dist/http/badResponses/forbidden.js +52 -0
- package/dist/http/badResponses/notFound.d.ts +34 -0
- package/dist/http/badResponses/notFound.d.ts.map +1 -0
- package/dist/http/badResponses/notFound.js +52 -0
- package/dist/http/badResponses/notImplemented.d.ts +34 -0
- package/dist/http/badResponses/notImplemented.d.ts.map +1 -0
- package/dist/http/badResponses/notImplemented.js +52 -0
- package/dist/http/badResponses/serverError.d.ts +34 -0
- package/dist/http/badResponses/serverError.d.ts.map +1 -0
- package/dist/http/badResponses/serverError.js +52 -0
- package/dist/http/badResponses/unauthorized.d.ts +34 -0
- package/dist/http/badResponses/unauthorized.d.ts.map +1 -0
- package/dist/http/badResponses/unauthorized.js +52 -0
- package/dist/http/badResponses/unprocessableEntity.d.ts +43 -0
- package/dist/http/badResponses/unprocessableEntity.d.ts.map +1 -0
- package/dist/http/badResponses/unprocessableEntity.js +60 -0
- package/dist/http/successResponses/__test__/created.spec.d.ts +2 -0
- package/dist/http/successResponses/__test__/created.spec.d.ts.map +1 -0
- package/dist/http/successResponses/__test__/created.spec.js +57 -0
- package/dist/http/successResponses/__test__/found.spec.d.ts +2 -0
- package/dist/http/successResponses/__test__/found.spec.d.ts.map +1 -0
- package/dist/http/successResponses/__test__/found.spec.js +57 -0
- package/dist/http/successResponses/__test__/noContent.spec.d.ts +2 -0
- package/dist/http/successResponses/__test__/noContent.spec.d.ts.map +1 -0
- package/dist/http/successResponses/__test__/noContent.spec.js +40 -0
- package/dist/http/successResponses/__test__/success.spec.d.ts +2 -0
- package/dist/http/successResponses/__test__/success.spec.d.ts.map +1 -0
- package/dist/http/successResponses/__test__/success.spec.js +60 -0
- package/dist/http/successResponses/__test__/updated.spec.d.ts +2 -0
- package/dist/http/successResponses/__test__/updated.spec.d.ts.map +1 -0
- package/dist/http/successResponses/__test__/updated.spec.js +60 -0
- package/dist/http/successResponses/created.d.ts +36 -0
- package/dist/http/successResponses/created.d.ts.map +1 -0
- package/dist/http/successResponses/created.js +54 -0
- package/dist/http/successResponses/found.d.ts +39 -0
- package/dist/http/successResponses/found.d.ts.map +1 -0
- package/dist/http/successResponses/found.js +57 -0
- package/dist/http/successResponses/noContent.d.ts +25 -0
- package/dist/http/successResponses/noContent.d.ts.map +1 -0
- package/dist/http/successResponses/noContent.js +35 -0
- package/dist/http/successResponses/success.d.ts +36 -0
- package/dist/http/successResponses/success.d.ts.map +1 -0
- package/dist/http/successResponses/success.js +54 -0
- package/dist/http/successResponses/updated.d.ts +36 -0
- package/dist/http/successResponses/updated.d.ts.map +1 -0
- package/dist/http/successResponses/updated.js +54 -0
- package/dist/index.d.ts +22 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -15
- package/dist/services/__test__/decodeRequestBody.spec.d.ts +2 -0
- package/dist/services/__test__/decodeRequestBody.spec.d.ts.map +1 -0
- package/dist/services/__test__/decodeRequestBody.spec.js +39 -0
- package/dist/services/__test__/errorHandler.spec.d.ts +2 -0
- package/dist/services/__test__/errorHandler.spec.d.ts.map +1 -0
- package/dist/services/__test__/errorHandler.spec.js +115 -0
- package/dist/services/__test__/formParse.spec.d.ts +2 -0
- package/dist/services/__test__/formParse.spec.d.ts.map +1 -0
- package/dist/services/__test__/formParse.spec.js +78 -0
- package/dist/services/__test__/getCaller.spec.d.ts +2 -0
- package/dist/services/__test__/getCaller.spec.d.ts.map +1 -0
- package/dist/services/__test__/getCaller.spec.js +60 -0
- package/dist/services/__test__/getScopedParams.spec.d.ts +2 -0
- package/dist/services/__test__/getScopedParams.spec.d.ts.map +1 -0
- package/dist/services/__test__/getScopedParams.spec.js +51 -0
- package/dist/services/__test__/schemaValidator.spec.d.ts +2 -0
- package/dist/services/__test__/schemaValidator.spec.d.ts.map +1 -0
- package/dist/services/__test__/schemaValidator.spec.js +89 -0
- package/dist/services/decodeErrorMessageFromRequest.d.ts +17 -0
- package/dist/services/decodeErrorMessageFromRequest.d.ts.map +1 -0
- package/dist/services/decodeErrorMessageFromRequest.js +30 -0
- package/dist/services/decodeRequestBody.d.ts +17 -0
- package/dist/services/decodeRequestBody.d.ts.map +1 -0
- package/dist/services/decodeRequestBody.js +38 -0
- package/dist/services/errorHandler.d.ts +44 -0
- package/dist/services/errorHandler.d.ts.map +1 -0
- package/dist/services/errorHandler.js +93 -0
- package/dist/services/formParse.d.ts +40 -1
- package/dist/services/formParse.d.ts.map +1 -1
- package/dist/services/formParse.js +44 -3
- package/dist/services/getCaller.d.ts +17 -0
- package/dist/services/getCaller.d.ts.map +1 -0
- package/dist/services/getCaller.js +45 -0
- package/dist/services/getScopedParams.d.ts +26 -1
- package/dist/services/getScopedParams.d.ts.map +1 -1
- package/dist/services/getScopedParams.js +26 -2
- package/dist/services/httpDebug.d.ts +35 -0
- package/dist/services/httpDebug.d.ts.map +1 -0
- package/dist/services/httpDebug.js +56 -0
- package/dist/services/schemaValidator.d.ts +13 -0
- package/dist/services/schemaValidator.d.ts.map +1 -0
- package/dist/services/schemaValidator.js +51 -0
- package/dist/services/sendFileToS3.d.ts +50 -18
- package/dist/services/sendFileToS3.d.ts.map +1 -1
- package/dist/services/sendFileToS3.js +73 -31
- package/package.json +14 -6
- package/src/api/__test__/deleteRequest.spec.ts +107 -0
- package/src/api/__test__/getRequest.spec.ts +76 -0
- package/src/api/__test__/inboxFlowRequest.spec.ts +125 -0
- package/src/api/__test__/makeRequest.spec.ts +121 -0
- package/src/api/__test__/patchRequest.spec.ts +125 -0
- package/src/api/__test__/postRequest.spec.ts +125 -0
- package/src/api/__test__/putRequest.spec.ts +125 -0
- package/src/api/deleteRequest.ts +22 -0
- package/src/api/getRequest.ts +20 -0
- package/src/api/inboxFlowRequest.ts +76 -0
- package/src/api/makeRequest.ts +117 -0
- package/src/api/patchRequest.ts +22 -0
- package/src/api/postRequest.ts +22 -0
- package/src/api/putRequest.ts +22 -0
- package/src/config/__test__/apiInstance.spec.ts +129 -0
- package/src/config/__test__/inboxFlowInstance.spec.ts +61 -0
- package/src/config/apiInstance.ts +148 -0
- package/src/config/inboxFlowInstance.ts +65 -0
- package/src/http/badResponses/__test__/badGateway.spec.ts +50 -0
- package/src/http/badResponses/__test__/badRequest.spec.ts +50 -0
- package/src/http/badResponses/__test__/conflict.spec.ts +50 -0
- package/src/http/badResponses/__test__/forbidden.spec.ts +50 -0
- package/src/http/badResponses/__test__/notFound.spec.ts +50 -0
- package/src/http/badResponses/__test__/notImplemented.spec.ts +50 -0
- package/src/http/badResponses/__test__/serverError.spec.ts +50 -0
- package/src/http/badResponses/__test__/unauthorized.spec.ts +50 -0
- package/src/http/badResponses/__test__/unprocessableEntity.spec.ts +76 -0
- package/src/http/badResponses/badGateway.ts +63 -0
- package/src/http/badResponses/badRequest.ts +63 -0
- package/src/http/badResponses/conflict.ts +63 -0
- package/src/http/badResponses/forbidden.ts +63 -0
- package/src/http/badResponses/notFound.ts +63 -0
- package/src/http/badResponses/notImplemented.ts +63 -0
- package/src/http/badResponses/serverError.ts +63 -0
- package/src/http/badResponses/unauthorized.ts +63 -0
- package/src/http/badResponses/unprocessableEntity.ts +79 -0
- package/src/http/successResponses/__test__/created.spec.ts +65 -0
- package/src/http/successResponses/__test__/found.spec.ts +65 -0
- package/src/http/successResponses/__test__/noContent.spec.ts +49 -0
- package/src/http/successResponses/__test__/success.spec.ts +68 -0
- package/src/http/successResponses/__test__/updated.spec.ts +68 -0
- package/src/http/successResponses/created.ts +64 -0
- package/src/http/successResponses/found.ts +67 -0
- package/src/http/successResponses/noContent.ts +42 -0
- package/src/http/successResponses/success.ts +64 -0
- package/src/http/successResponses/updated.ts +64 -0
- package/src/index.ts +25 -16
- package/src/services/__test__/decodeRequestBody.spec.ts +51 -0
- package/src/services/__test__/errorHandler.spec.ts +133 -0
- package/src/services/__test__/formParse.spec.ts +96 -0
- package/src/services/__test__/getCaller.spec.ts +78 -0
- package/src/services/__test__/getScopedParams.spec.ts +68 -0
- package/src/services/__test__/schemaValidator.spec.ts +106 -0
- package/src/services/decodeErrorMessageFromRequest.ts +36 -0
- package/src/services/decodeRequestBody.ts +42 -0
- package/src/services/errorHandler.ts +99 -0
- package/src/services/formParse.ts +46 -6
- package/src/services/getCaller.ts +58 -0
- package/src/services/getScopedParams.ts +29 -2
- package/src/services/httpDebug.ts +64 -0
- package/src/services/schemaValidator.ts +66 -0
- package/src/services/sendFileToS3.ts +81 -56
- package/vitest.config.ts +5 -0
- package/dist/helpers/globalErrorHandler.d.ts +0 -3
- package/dist/helpers/globalErrorHandler.d.ts.map +0 -1
- package/dist/helpers/globalErrorHandler.js +0 -53
- package/dist/httpBadResponses/badRequest.d.ts +0 -9
- package/dist/httpBadResponses/badRequest.d.ts.map +0 -1
- package/dist/httpBadResponses/badRequest.js +0 -23
- package/dist/httpBadResponses/conflict.d.ts +0 -9
- package/dist/httpBadResponses/conflict.d.ts.map +0 -1
- package/dist/httpBadResponses/conflict.js +0 -23
- package/dist/httpBadResponses/forbidden.d.ts +0 -9
- package/dist/httpBadResponses/forbidden.d.ts.map +0 -1
- package/dist/httpBadResponses/forbidden.js +0 -23
- package/dist/httpBadResponses/notFound.d.ts +0 -9
- package/dist/httpBadResponses/notFound.d.ts.map +0 -1
- package/dist/httpBadResponses/notFound.js +0 -23
- package/dist/httpBadResponses/serverError.d.ts +0 -9
- package/dist/httpBadResponses/serverError.d.ts.map +0 -1
- package/dist/httpBadResponses/serverError.js +0 -23
- package/dist/httpBadResponses/unauthorized.d.ts +0 -9
- package/dist/httpBadResponses/unauthorized.d.ts.map +0 -1
- package/dist/httpBadResponses/unauthorized.js +0 -23
- package/dist/httpBadResponses/unprocessableEntity.d.ts +0 -17
- package/dist/httpBadResponses/unprocessableEntity.d.ts.map +0 -1
- package/dist/httpBadResponses/unprocessableEntity.js +0 -30
- package/dist/httpResponses/created.d.ts +0 -10
- package/dist/httpResponses/created.d.ts.map +0 -1
- package/dist/httpResponses/created.js +0 -30
- package/dist/httpResponses/noContent.d.ts +0 -9
- package/dist/httpResponses/noContent.d.ts.map +0 -1
- package/dist/httpResponses/noContent.js +0 -28
- package/dist/httpResponses/success.d.ts +0 -10
- package/dist/httpResponses/success.d.ts.map +0 -1
- package/dist/httpResponses/success.js +0 -30
- package/dist/httpResponses/updated.d.ts +0 -10
- package/dist/httpResponses/updated.d.ts.map +0 -1
- package/dist/httpResponses/updated.js +0 -30
- package/dist/services/extractJsonFromRequest.d.ts +0 -3
- package/dist/services/extractJsonFromRequest.d.ts.map +0 -1
- package/dist/services/extractJsonFromRequest.js +0 -29
- package/src/helpers/globalErrorHandler.ts +0 -64
- package/src/httpBadResponses/badRequest.ts +0 -29
- package/src/httpBadResponses/conflict.ts +0 -29
- package/src/httpBadResponses/forbidden.ts +0 -29
- package/src/httpBadResponses/notFound.ts +0 -29
- package/src/httpBadResponses/serverError.ts +0 -29
- package/src/httpBadResponses/unauthorized.ts +0 -29
- package/src/httpBadResponses/unprocessableEntity.ts +0 -43
- package/src/httpResponses/created.ts +0 -35
- package/src/httpResponses/noContent.ts +0 -33
- package/src/httpResponses/success.ts +0 -35
- package/src/httpResponses/updated.ts +0 -35
- package/src/services/extractJsonFromRequest.ts +0 -30
|
@@ -1,31 +1,22 @@
|
|
|
1
|
+
import { generateId } from "@arkyn/shared";
|
|
1
2
|
import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
|
|
2
3
|
import { unstable_composeUploadHandlers as composeUploadHandlers, unstable_createFileUploadHandler as createFileUploadHandler, unstable_parseMultipartFormData as parseMultipartFormData, } from "@remix-run/node";
|
|
3
|
-
import { randomUUID } from "crypto";
|
|
4
4
|
import fs from "fs";
|
|
5
5
|
import sharp from "sharp";
|
|
6
|
-
import {
|
|
6
|
+
import { BadRequest } from "../http/badResponses/badRequest";
|
|
7
7
|
import { getScopedParams } from "./getScopedParams";
|
|
8
|
-
async function
|
|
9
|
-
const { AWS_ACCESS_KEY_ID, AWS_REGION, AWS_S3_BUCKET, AWS_SECRET_ACCESS_KEY, } = awsS3Config;
|
|
10
|
-
const filePath = fileStream.path;
|
|
11
|
-
let fileName = "";
|
|
12
|
-
if (typeof filePath === "string") {
|
|
13
|
-
fileName = filePath.split("/").pop() || "";
|
|
14
|
-
}
|
|
15
|
-
else {
|
|
16
|
-
fileName = randomUUID();
|
|
17
|
-
}
|
|
8
|
+
async function s3Upload(fileStream, contentType, awsConfig) {
|
|
18
9
|
const uploadParams = {
|
|
19
|
-
Bucket: AWS_S3_BUCKET,
|
|
20
|
-
Key: `uploads/${
|
|
10
|
+
Bucket: awsConfig.AWS_S3_BUCKET,
|
|
11
|
+
Key: `uploads/${generateId("text", "v4")}`,
|
|
21
12
|
Body: fileStream,
|
|
22
13
|
ContentType: contentType,
|
|
23
14
|
};
|
|
24
15
|
const s3Client = new S3Client({
|
|
25
|
-
region: AWS_REGION,
|
|
16
|
+
region: awsConfig.AWS_REGION,
|
|
26
17
|
credentials: {
|
|
27
|
-
accessKeyId: AWS_ACCESS_KEY_ID,
|
|
28
|
-
secretAccessKey: AWS_SECRET_ACCESS_KEY,
|
|
18
|
+
accessKeyId: awsConfig.AWS_ACCESS_KEY_ID,
|
|
19
|
+
secretAccessKey: awsConfig.AWS_SECRET_ACCESS_KEY,
|
|
29
20
|
},
|
|
30
21
|
});
|
|
31
22
|
const command = new PutObjectCommand(uploadParams);
|
|
@@ -36,15 +27,64 @@ async function s3_upload(fileStream, contentType, awsS3Config) {
|
|
|
36
27
|
console.error(error);
|
|
37
28
|
}
|
|
38
29
|
return {
|
|
39
|
-
location: `https://${AWS_S3_BUCKET}.s3.amazonaws.com/${uploadParams.Key}`,
|
|
30
|
+
location: `https://${awsConfig.AWS_S3_BUCKET}.s3.amazonaws.com/${uploadParams.Key}`,
|
|
40
31
|
};
|
|
41
32
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Handles file uploads to an AWS S3 bucket. This function processes a file
|
|
35
|
+
* from a multipart form request, validates and optionally compresses the file,
|
|
36
|
+
* and uploads it to S3. It supports image-specific operations such as resizing
|
|
37
|
+
* validation and quality reduction.
|
|
38
|
+
*
|
|
39
|
+
* @param request - The HTTP request containing the multipart form data.
|
|
40
|
+
* @param awsS3Config - Configuration object for AWS S3, including bucket name,
|
|
41
|
+
* region, and credentials.
|
|
42
|
+
* @param config - Optional configuration object for file handling.
|
|
43
|
+
*
|
|
44
|
+
* @param config.fileName - The name of the form field containing the file. Defaults to `"file"`.
|
|
45
|
+
* @param config.maxPartSize - The maximum size (in bytes) for each part of the file. Defaults to `5_000_000`.
|
|
46
|
+
* @param config.reduceImageQuality - The quality percentage for image compression. Defaults to `100`.
|
|
47
|
+
* @param config.validateImageSize - Whether to validate the image dimensions. Defaults to `false`.
|
|
48
|
+
* @param config.validateImageMessage - The error message template for invalid image dimensions.
|
|
49
|
+
* Defaults to `"Invalid dimensions {{width}}px x {{height}}px"`.
|
|
50
|
+
*
|
|
51
|
+
* @returns A promise that resolves to an object containing the uploaded file's URL
|
|
52
|
+
* or an error message if validation fails.
|
|
53
|
+
*
|
|
54
|
+
* @throws {BadRequest} If no file is uploaded.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```typescript
|
|
58
|
+
* const awsS3Config = {
|
|
59
|
+
* AWS_S3_BUCKET: "my-bucket",
|
|
60
|
+
* AWS_REGION: "us-east-1",
|
|
61
|
+
* AWS_ACCESS_KEY_ID: "my-access-key",
|
|
62
|
+
* AWS_SECRET_ACCESS_KEY: "my-secret-key",
|
|
63
|
+
* };
|
|
64
|
+
*
|
|
65
|
+
* const config = {
|
|
66
|
+
* fileName: "upload",
|
|
67
|
+
* maxPartSize: 10_000_000,
|
|
68
|
+
* reduceImageQuality: 80,
|
|
69
|
+
* validateImageSize: true,
|
|
70
|
+
* validateImageMessage: "Invalid dimensions {{width}}px x {{height}}px",
|
|
71
|
+
* };
|
|
72
|
+
*
|
|
73
|
+
* const response = await sendFileToS3(request, awsS3Config, config);
|
|
74
|
+
* if (response.error) {
|
|
75
|
+
* console.error(response.error);
|
|
76
|
+
* } else {
|
|
77
|
+
* console.log("File uploaded to:", response.url);
|
|
78
|
+
* }
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
const sendFileToS3 = async (request, awsS3Config, config) => {
|
|
82
|
+
const fileName = config?.fileName || "file";
|
|
83
|
+
const maxPartSize = config?.maxPartSize || 5_000_000;
|
|
84
|
+
const reduceImageQuality = config?.reduceImageQuality || 100;
|
|
85
|
+
const validateImageSize = config?.validateImageSize || false;
|
|
86
|
+
const validateImageMessage = config?.validateImageMessage ||
|
|
87
|
+
"Invalid dimensions {{width}}px x {{height}}px";
|
|
48
88
|
const uploadHandler = composeUploadHandlers(createFileUploadHandler({
|
|
49
89
|
maxPartSize,
|
|
50
90
|
file: ({ filename }) => filename,
|
|
@@ -52,14 +92,14 @@ async function sendFileToS3(props, awsS3Config, config = {
|
|
|
52
92
|
const formData = await parseMultipartFormData(request, uploadHandler);
|
|
53
93
|
const file = formData.get(fileName);
|
|
54
94
|
if (!file)
|
|
55
|
-
throw new
|
|
95
|
+
throw new BadRequest("No file uploaded");
|
|
56
96
|
const filterParams = getScopedParams(request);
|
|
57
97
|
const width = filterParams.get("w");
|
|
58
98
|
const height = filterParams.get("h");
|
|
59
99
|
const reduceQuality = filterParams.get("reduceQuality");
|
|
60
|
-
const quality = reduceQuality ? +reduceQuality :
|
|
100
|
+
const quality = reduceQuality ? +reduceQuality : reduceImageQuality;
|
|
61
101
|
const isImage = file.type.startsWith("image");
|
|
62
|
-
if (isImage && width && height) {
|
|
102
|
+
if (isImage && width && height && validateImageSize) {
|
|
63
103
|
const image = sharp(file.getFilePath());
|
|
64
104
|
const metadata = await image.metadata();
|
|
65
105
|
if (metadata.width && metadata.height) {
|
|
@@ -67,7 +107,9 @@ async function sendFileToS3(props, awsS3Config, config = {
|
|
|
67
107
|
const heightDiff = Math.abs(metadata.height - +height);
|
|
68
108
|
if (widthDiff > 10 || heightDiff > 10) {
|
|
69
109
|
return {
|
|
70
|
-
error:
|
|
110
|
+
error: validateImageMessage
|
|
111
|
+
.replace("{{width}}", width)
|
|
112
|
+
.replace("{{height}}", height),
|
|
71
113
|
};
|
|
72
114
|
}
|
|
73
115
|
}
|
|
@@ -87,7 +129,7 @@ async function sendFileToS3(props, awsS3Config, config = {
|
|
|
87
129
|
await image.toFile(compressedFilePath);
|
|
88
130
|
file.getFilePath = () => compressedFilePath;
|
|
89
131
|
const streamFile = fs.createReadStream(file.getFilePath());
|
|
90
|
-
const apiResponse = await
|
|
132
|
+
const apiResponse = await s3Upload(streamFile, file.type, awsS3Config);
|
|
91
133
|
fs.unlink(compressedFilePath, (err) => {
|
|
92
134
|
if (err)
|
|
93
135
|
console.error(`Delete image error: ${err}`);
|
|
@@ -95,7 +137,7 @@ async function sendFileToS3(props, awsS3Config, config = {
|
|
|
95
137
|
return { url: apiResponse.location };
|
|
96
138
|
}
|
|
97
139
|
const streamFile = fs.createReadStream(file.getFilePath());
|
|
98
|
-
const apiResponse = await
|
|
140
|
+
const apiResponse = await s3Upload(streamFile, file.type, awsS3Config);
|
|
99
141
|
return { url: apiResponse.location };
|
|
100
|
-
}
|
|
142
|
+
};
|
|
101
143
|
export { sendFileToS3 };
|
package/package.json
CHANGED
|
@@ -1,23 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arkyn/server",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.5",
|
|
4
4
|
"main": "./dist/bundle.js",
|
|
5
5
|
"module": "./src/index.ts",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
|
+
"license": "Apache-2.0",
|
|
9
|
+
"author": "Arkyn | Lucas Gonçalves",
|
|
10
|
+
"description": "Server-side utilities for projects.",
|
|
8
11
|
"scripts": {
|
|
9
12
|
"clean": "rm -rf dist",
|
|
10
13
|
"build": "bun run clean && bunx tsc --project tsconfig.json",
|
|
14
|
+
"test": "vitest --config vitest.config.ts",
|
|
11
15
|
"typecheck": "bunx tsc --project tsconfig.json --noEmit"
|
|
12
16
|
},
|
|
13
17
|
"dependencies": {
|
|
14
|
-
"@arkyn/
|
|
15
|
-
"@aws-sdk/client-s3": "^3.
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
+
"@arkyn/shared": "*",
|
|
19
|
+
"@aws-sdk/client-s3": "^3.782.0",
|
|
20
|
+
"sharp": "^0.33.5",
|
|
21
|
+
"zod": "^3.24.2"
|
|
22
|
+
},
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"@remix-run/node": "^2.16.4"
|
|
18
25
|
},
|
|
19
26
|
"devDependencies": {
|
|
20
27
|
"bun-types": "latest",
|
|
21
|
-
"
|
|
28
|
+
"vitest": "^3.1.1",
|
|
29
|
+
"typescript": "^5.8.3"
|
|
22
30
|
}
|
|
23
31
|
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import { deleteRequest } from "../deleteRequest";
|
|
3
|
+
|
|
4
|
+
global.fetch = vi.fn() as any;
|
|
5
|
+
|
|
6
|
+
describe("deleteRequest", () => {
|
|
7
|
+
it("should return success response for a successfully delete request", async () => {
|
|
8
|
+
const mockResponse = { message: "Resource deleted successfully" };
|
|
9
|
+
(fetch as any).mockResolvedValueOnce({
|
|
10
|
+
ok: true,
|
|
11
|
+
status: 200,
|
|
12
|
+
json: async () => mockResponse,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const response = await deleteRequest("https://api.example.com/resource");
|
|
16
|
+
|
|
17
|
+
expect(response).toEqual({
|
|
18
|
+
success: true,
|
|
19
|
+
status: 200,
|
|
20
|
+
message: "Resource deleted successfully",
|
|
21
|
+
response: mockResponse,
|
|
22
|
+
cause: null,
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("should return failed response for a failed delete request", async () => {
|
|
27
|
+
const mockErrorResponse = { error: "Resource not found" };
|
|
28
|
+
|
|
29
|
+
(fetch as any).mockResolvedValueOnce({
|
|
30
|
+
ok: false,
|
|
31
|
+
status: 404,
|
|
32
|
+
json: async () => mockErrorResponse,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const response = await deleteRequest("https://api.example.com/resource");
|
|
36
|
+
|
|
37
|
+
expect(response.success).toBe(false);
|
|
38
|
+
expect(response.status).toBe(404);
|
|
39
|
+
expect(response.response).toEqual(mockErrorResponse);
|
|
40
|
+
expect(response.message).toBeDefined();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("should handle network errors gracefully", async () => {
|
|
44
|
+
(fetch as any).mockRejectedValueOnce(new Error("Network Error"));
|
|
45
|
+
|
|
46
|
+
const response = await deleteRequest("https://api.example.com/resource");
|
|
47
|
+
|
|
48
|
+
expect(response).toEqual({
|
|
49
|
+
success: false,
|
|
50
|
+
status: 0,
|
|
51
|
+
message: "Network error or request failed",
|
|
52
|
+
response: null,
|
|
53
|
+
cause: "Network Error",
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("should handle invalid JSON response gracefully", async () => {
|
|
58
|
+
(fetch as any).mockResolvedValueOnce({
|
|
59
|
+
ok: true,
|
|
60
|
+
status: 200,
|
|
61
|
+
json: async () => {
|
|
62
|
+
throw new Error("Invalid JSON");
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const response = await deleteRequest("https://api.example.com/resource");
|
|
67
|
+
|
|
68
|
+
expect(response).toEqual({
|
|
69
|
+
success: true,
|
|
70
|
+
status: 200,
|
|
71
|
+
message: "Resource deleted successfully",
|
|
72
|
+
response: null,
|
|
73
|
+
cause: null,
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("should send a body with the delete request if provided", async () => {
|
|
78
|
+
const mockResponse = { message: "Resource deleted successfully" };
|
|
79
|
+
const mockBody = { reason: "No longer needed" };
|
|
80
|
+
|
|
81
|
+
(fetch as any).mockResolvedValueOnce({
|
|
82
|
+
ok: true,
|
|
83
|
+
status: 200,
|
|
84
|
+
json: async () => mockResponse,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const response = await deleteRequest(
|
|
88
|
+
"https://api.example.com/resource",
|
|
89
|
+
{ "Content-Type": "application/json" },
|
|
90
|
+
mockBody
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
expect(fetch).toHaveBeenCalledWith("https://api.example.com/resource", {
|
|
94
|
+
method: "DELETE",
|
|
95
|
+
headers: { "Content-Type": "application/json" },
|
|
96
|
+
body: JSON.stringify(mockBody),
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
expect(response).toEqual({
|
|
100
|
+
success: true,
|
|
101
|
+
status: 200,
|
|
102
|
+
message: "Resource deleted successfully",
|
|
103
|
+
response: mockResponse,
|
|
104
|
+
cause: null,
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import { getRequest } from "../getRequest";
|
|
3
|
+
|
|
4
|
+
global.fetch = vi.fn() as any;
|
|
5
|
+
|
|
6
|
+
describe("getRequest", () => {
|
|
7
|
+
it("should return success response for a successful request", async () => {
|
|
8
|
+
const mockResponse = { message: "Request successful", data: { id: 1 } };
|
|
9
|
+
(fetch as any).mockResolvedValueOnce({
|
|
10
|
+
ok: true,
|
|
11
|
+
status: 200,
|
|
12
|
+
json: async () => mockResponse,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const response = await getRequest("https://api.example.com/resource");
|
|
16
|
+
|
|
17
|
+
expect(response).toEqual({
|
|
18
|
+
success: true,
|
|
19
|
+
status: 200,
|
|
20
|
+
message: "Request successful",
|
|
21
|
+
response: mockResponse,
|
|
22
|
+
cause: null,
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("should return failed response for a failed request", async () => {
|
|
27
|
+
const mockErrorResponse = { error: "Not Found" };
|
|
28
|
+
|
|
29
|
+
(fetch as any).mockResolvedValueOnce({
|
|
30
|
+
ok: false,
|
|
31
|
+
status: 404,
|
|
32
|
+
json: async () => mockErrorResponse,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const response = await getRequest("https://api.example.com/resource");
|
|
36
|
+
|
|
37
|
+
expect(response.success).toBe(false);
|
|
38
|
+
expect(response.status).toBe(404);
|
|
39
|
+
expect(response.response).toEqual(mockErrorResponse);
|
|
40
|
+
expect(response.message).toBeDefined();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("should handle network errors gracefully", async () => {
|
|
44
|
+
(fetch as any).mockRejectedValueOnce(new Error("Network Error"));
|
|
45
|
+
|
|
46
|
+
const response = await getRequest("https://api.example.com/resource");
|
|
47
|
+
|
|
48
|
+
expect(response).toEqual({
|
|
49
|
+
success: false,
|
|
50
|
+
status: 0,
|
|
51
|
+
message: "Network error or request failed",
|
|
52
|
+
response: null,
|
|
53
|
+
cause: "Network Error",
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("should handle invalid JSON response gracefully", async () => {
|
|
58
|
+
(fetch as any).mockResolvedValueOnce({
|
|
59
|
+
ok: true,
|
|
60
|
+
status: 200,
|
|
61
|
+
json: async () => {
|
|
62
|
+
throw new Error("Invalid JSON");
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const response = await getRequest("https://api.example.com/resource");
|
|
67
|
+
|
|
68
|
+
expect(response).toEqual({
|
|
69
|
+
success: true,
|
|
70
|
+
status: 200,
|
|
71
|
+
message: "Request successful",
|
|
72
|
+
response: null,
|
|
73
|
+
cause: null,
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
import { inboxFlowRequest } from "../inboxFlowRequest";
|
|
3
|
+
import { InboxFlowInstance } from "../../config/inboxFlowInstance";
|
|
4
|
+
import { httpDebug } from "../../services/httpDebug";
|
|
5
|
+
|
|
6
|
+
vi.mock("../../config/inboxFlowInstance");
|
|
7
|
+
vi.mock("../../services/httpDebug");
|
|
8
|
+
|
|
9
|
+
global.fetch = vi.fn() as any;
|
|
10
|
+
|
|
11
|
+
describe("inboxFlowRequest", () => {
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
vi.clearAllMocks();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("should send a POST request with correct body and headers", async () => {
|
|
17
|
+
const mockConfig = {
|
|
18
|
+
status: 200,
|
|
19
|
+
method: "POST" as any,
|
|
20
|
+
request: "Request data",
|
|
21
|
+
response: "Response data",
|
|
22
|
+
token: "test-token",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const mockInboxConfig = {
|
|
26
|
+
inboxChannelId: "channel-id",
|
|
27
|
+
inboxUserToken: "user-token",
|
|
28
|
+
inboxApiUrl: "https://api.example.com/inbox",
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
(InboxFlowInstance.getInboxConfig as any).mockReturnValue(mockInboxConfig);
|
|
32
|
+
|
|
33
|
+
(fetch as any).mockResolvedValueOnce({
|
|
34
|
+
ok: true,
|
|
35
|
+
status: 200,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
await inboxFlowRequest(mockConfig);
|
|
39
|
+
|
|
40
|
+
expect(fetch).toHaveBeenCalledWith(mockInboxConfig.inboxApiUrl, {
|
|
41
|
+
method: "POST",
|
|
42
|
+
body: JSON.stringify({
|
|
43
|
+
status: mockConfig.status,
|
|
44
|
+
channelId: mockInboxConfig.inboxChannelId,
|
|
45
|
+
method: mockConfig.method,
|
|
46
|
+
token: mockConfig.token,
|
|
47
|
+
request: mockConfig.request,
|
|
48
|
+
response: mockConfig.response,
|
|
49
|
+
}),
|
|
50
|
+
headers: {
|
|
51
|
+
"Content-Type": "application/json",
|
|
52
|
+
Authorization: `Bearer ${mockInboxConfig.inboxUserToken}`,
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("should not send a request if inbox configuration is not set", async () => {
|
|
58
|
+
(InboxFlowInstance.getInboxConfig as any).mockReturnValue(undefined);
|
|
59
|
+
|
|
60
|
+
await inboxFlowRequest({
|
|
61
|
+
status: 200,
|
|
62
|
+
method: "POST",
|
|
63
|
+
request: "Request data",
|
|
64
|
+
response: "Response data",
|
|
65
|
+
token: "test-token",
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
expect(fetch).not.toHaveBeenCalled();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("should handle fetch errors gracefully", async () => {
|
|
72
|
+
const mockConfig = {
|
|
73
|
+
status: 500,
|
|
74
|
+
method: "ERROR" as any,
|
|
75
|
+
request: "Request data",
|
|
76
|
+
response: "Error response",
|
|
77
|
+
token: "test-token",
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const mockInboxConfig = {
|
|
81
|
+
inboxChannelId: "channel-id",
|
|
82
|
+
inboxUserToken: "user-token",
|
|
83
|
+
inboxApiUrl: "https://api.example.com/inbox",
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
(InboxFlowInstance.getInboxConfig as any).mockReturnValue(mockInboxConfig);
|
|
87
|
+
|
|
88
|
+
(fetch as any).mockRejectedValueOnce(new Error("Network Error"));
|
|
89
|
+
|
|
90
|
+
await inboxFlowRequest(mockConfig);
|
|
91
|
+
|
|
92
|
+
expect(httpDebug).toHaveBeenCalledWith(
|
|
93
|
+
"inboxFlowRequest",
|
|
94
|
+
"Error sending inbox flow request",
|
|
95
|
+
expect.any(Error)
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("should not send a request in development environment", async () => {
|
|
100
|
+
const originalEnv = process.env.NODE_ENV;
|
|
101
|
+
process.env.NODE_ENV = "development";
|
|
102
|
+
|
|
103
|
+
const mockConfig = {
|
|
104
|
+
status: 200,
|
|
105
|
+
method: "POST" as any,
|
|
106
|
+
request: "Request data",
|
|
107
|
+
response: "Response data",
|
|
108
|
+
token: "test-token",
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const mockInboxConfig = {
|
|
112
|
+
inboxChannelId: "channel-id",
|
|
113
|
+
inboxUserToken: "user-token",
|
|
114
|
+
inboxApiUrl: "https://api.example.com/inbox",
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
(InboxFlowInstance.getInboxConfig as any).mockReturnValue(mockInboxConfig);
|
|
118
|
+
|
|
119
|
+
await inboxFlowRequest(mockConfig);
|
|
120
|
+
|
|
121
|
+
expect(fetch).not.toHaveBeenCalled();
|
|
122
|
+
|
|
123
|
+
process.env.NODE_ENV = originalEnv;
|
|
124
|
+
});
|
|
125
|
+
});
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import { makeRequest } from "../makeRequest";
|
|
3
|
+
|
|
4
|
+
global.fetch = vi.fn() as any;
|
|
5
|
+
|
|
6
|
+
describe("makeRequest", () => {
|
|
7
|
+
it("should return success response for a successful POST request", async () => {
|
|
8
|
+
const mockResponse = { message: "Resource created successfully" };
|
|
9
|
+
const mockBody = { name: "New Resource" };
|
|
10
|
+
|
|
11
|
+
(fetch as any).mockResolvedValueOnce({
|
|
12
|
+
ok: true,
|
|
13
|
+
status: 201,
|
|
14
|
+
json: async () => mockResponse,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const response = await makeRequest(
|
|
18
|
+
"POST",
|
|
19
|
+
"https://api.example.com/resource",
|
|
20
|
+
{ "Content-Type": "application/json" },
|
|
21
|
+
mockBody
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
expect(response).toEqual({
|
|
25
|
+
success: true,
|
|
26
|
+
status: 201,
|
|
27
|
+
message: "Resource created successfully",
|
|
28
|
+
response: mockResponse,
|
|
29
|
+
cause: null,
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should return failed response for a failed PUT request", async () => {
|
|
34
|
+
const mockErrorResponse = { error: "Invalid data" };
|
|
35
|
+
const mockBody = { name: "" };
|
|
36
|
+
|
|
37
|
+
(fetch as any).mockResolvedValueOnce({
|
|
38
|
+
ok: false,
|
|
39
|
+
status: 400,
|
|
40
|
+
json: async () => mockErrorResponse,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const response = await makeRequest(
|
|
44
|
+
"PUT",
|
|
45
|
+
"https://api.example.com/resource",
|
|
46
|
+
{ "Content-Type": "application/json" },
|
|
47
|
+
mockBody
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
expect(response.success).toBe(false);
|
|
51
|
+
expect(response.status).toBe(400);
|
|
52
|
+
expect(response.response).toEqual(mockErrorResponse);
|
|
53
|
+
expect(response.message).toBeDefined();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("should handle network errors gracefully", async () => {
|
|
57
|
+
(fetch as any).mockRejectedValueOnce(new Error("Network Error"));
|
|
58
|
+
|
|
59
|
+
const response = await makeRequest(
|
|
60
|
+
"GET",
|
|
61
|
+
"https://api.example.com/resource",
|
|
62
|
+
{ "Content-Type": "application/json" }
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
expect(response).toEqual({
|
|
66
|
+
success: false,
|
|
67
|
+
status: 0,
|
|
68
|
+
message: "Network error or request failed",
|
|
69
|
+
response: null,
|
|
70
|
+
cause: "Network Error",
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("should handle invalid JSON response gracefully", async () => {
|
|
75
|
+
(fetch as any).mockResolvedValueOnce({
|
|
76
|
+
ok: true,
|
|
77
|
+
status: 200,
|
|
78
|
+
json: async () => {
|
|
79
|
+
throw new Error("Invalid JSON");
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const response = await makeRequest(
|
|
84
|
+
"GET",
|
|
85
|
+
"https://api.example.com/resource",
|
|
86
|
+
{ "Content-Type": "application/json" }
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
expect(response).toEqual({
|
|
90
|
+
success: true,
|
|
91
|
+
status: 200,
|
|
92
|
+
message: "Request successful",
|
|
93
|
+
response: null,
|
|
94
|
+
cause: null,
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("should send headers and body correctly for a DELETE request", async () => {
|
|
99
|
+
const mockResponse = { message: "Resource deleted successfully" };
|
|
100
|
+
|
|
101
|
+
(fetch as any).mockResolvedValueOnce({
|
|
102
|
+
ok: true,
|
|
103
|
+
status: 204,
|
|
104
|
+
json: async () => mockResponse,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const response = await makeRequest(
|
|
108
|
+
"DELETE",
|
|
109
|
+
"https://api.example.com/resource",
|
|
110
|
+
{ Authorization: "Bearer token" }
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
expect(response).toEqual({
|
|
114
|
+
success: true,
|
|
115
|
+
status: 204,
|
|
116
|
+
message: "Resource deleted successfully",
|
|
117
|
+
response: mockResponse,
|
|
118
|
+
cause: null,
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
});
|