@spytecgps/lambda-utils 3.0.5 → 3.0.6-rc.0
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/dist/config/index.js +31 -0
- package/dist/errors/BadRequestError.js +13 -0
- package/dist/errors/BaseError.js +10 -0
- package/dist/errors/ConflictError.js +13 -0
- package/dist/errors/ForbiddenError.js +13 -0
- package/dist/errors/GoneError.js +13 -0
- package/dist/errors/HttpError.js +8 -0
- package/dist/errors/NotFoundError.js +13 -0
- package/dist/errors/UnauthorizedError.js +13 -0
- package/dist/index.js +66 -653
- package/dist/middleware/contextualLogger.js +42 -0
- package/dist/middleware/http/response.js +27 -0
- package/dist/middleware/index.js +54 -0
- package/dist/middleware/ioLogger.js +42 -0
- package/dist/middleware/normalizer.js +13 -0
- package/dist/middleware/offlineAuthorizer.js +50 -0
- package/dist/middleware/responseWrapper.js +25 -0
- package/dist/middleware/validation.js +27 -0
- package/dist/middleware/warmup.js +19 -0
- package/dist/utils/cache.js +59 -0
- package/dist/utils/cacheWrapper.js +12 -0
- package/dist/utils/timeOut.js +15 -0
- package/dist/validation/custom.js +165 -0
- package/dist/validation/index.js +20 -0
- package/dist/validation/requestContext.js +73 -0
- package/dist/validation/validateEvent.js +31 -0
- package/package.json +1 -1
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const AMAZON_TRACE_ID = '_X_AMZN_TRACE_ID';
|
|
4
|
+
const CORRELATION_HEADER = 'x-correlation-';
|
|
5
|
+
const CORRELATION_ID = `${CORRELATION_HEADER}id`;
|
|
6
|
+
const CORRELATION_TRACE_ID = `${CORRELATION_HEADER}trace-id`;
|
|
7
|
+
const contextualLogger = ({ logger }) => {
|
|
8
|
+
const before = async ({ event, context }) => {
|
|
9
|
+
const ctx = {
|
|
10
|
+
awsRequestId: context?.awsRequestId,
|
|
11
|
+
};
|
|
12
|
+
// capture api gateway request ID
|
|
13
|
+
const apiRequestId = event?.requestContext?.requestId;
|
|
14
|
+
if (apiRequestId) {
|
|
15
|
+
ctx.apiRequestId = apiRequestId;
|
|
16
|
+
}
|
|
17
|
+
// capture any correlation headers sent from upstream callers
|
|
18
|
+
if (event.headers) {
|
|
19
|
+
Object.keys(event.headers).forEach((header) => {
|
|
20
|
+
if (header.toLowerCase().startsWith(CORRELATION_HEADER)) {
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
22
|
+
ctx[header] = event.headers[header];
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
// capture the xray trace id if its enabled
|
|
27
|
+
if (process.env[AMAZON_TRACE_ID]) {
|
|
28
|
+
ctx[CORRELATION_TRACE_ID] = process.env[AMAZON_TRACE_ID];
|
|
29
|
+
}
|
|
30
|
+
// set the correlation id if not already set by upstream callers
|
|
31
|
+
/* istanbul ignore next */
|
|
32
|
+
if (!ctx[CORRELATION_ID]) {
|
|
33
|
+
ctx[CORRELATION_ID] = context.awsRequestId;
|
|
34
|
+
}
|
|
35
|
+
logger.setHapnContext(ctx);
|
|
36
|
+
};
|
|
37
|
+
return {
|
|
38
|
+
before,
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
exports.contextualLogger = contextualLogger;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const baseHeaders = {
|
|
4
|
+
'Content-Type': 'application/json',
|
|
5
|
+
'Access-Control-Allow-Origin': '*',
|
|
6
|
+
'Access-Control-Allow-Credentials': true,
|
|
7
|
+
};
|
|
8
|
+
const buildResponseBody = (statusCode, message, data) => {
|
|
9
|
+
return {
|
|
10
|
+
success: statusCode < 400,
|
|
11
|
+
message,
|
|
12
|
+
result: typeof data !== 'undefined' ? data : undefined,
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
const buildProxyResult = ({ statusCode = 200, message = 'ok', data, headers = {}, multiValueHeaders = {}, rawResult = false, stringifyBody = true, }) => {
|
|
16
|
+
const resp = rawResult ? data : buildResponseBody(statusCode, message, data);
|
|
17
|
+
const body = stringifyBody ? resp && JSON.stringify(resp) : data;
|
|
18
|
+
return {
|
|
19
|
+
headers: { ...baseHeaders, ...headers },
|
|
20
|
+
multiValueHeaders,
|
|
21
|
+
statusCode,
|
|
22
|
+
body,
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
exports.buildProxyResult = buildProxyResult;
|
|
27
|
+
exports.buildResponseBody = buildResponseBody;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var core = require('@middy/core');
|
|
4
|
+
var httpErrorHandler = require('@middy/http-error-handler');
|
|
5
|
+
var httpResponseSerializer = require('@middy/http-response-serializer');
|
|
6
|
+
var sqsJsonBodyParser = require('@middy/sqs-json-body-parser');
|
|
7
|
+
var contextualLogger = require('./contextualLogger.js');
|
|
8
|
+
var ioLogger = require('./ioLogger.js');
|
|
9
|
+
var normalizer = require('./normalizer.js');
|
|
10
|
+
var offlineAuthorizer = require('./offlineAuthorizer.js');
|
|
11
|
+
var responseWrapper = require('./responseWrapper.js');
|
|
12
|
+
var warmup = require('./warmup.js');
|
|
13
|
+
|
|
14
|
+
// Function that returns base middlewares with required logger
|
|
15
|
+
const getBaseMiddlewares = ({ logger }) => [
|
|
16
|
+
warmup.warmupMiddleware(),
|
|
17
|
+
contextualLogger.contextualLogger({ logger }),
|
|
18
|
+
ioLogger.ioLogger({ logger }),
|
|
19
|
+
];
|
|
20
|
+
// Function that returns API Gateway middlewares with required logger
|
|
21
|
+
const getApiGatewayMiddlewares = ({ logger, offlineAuthEnabled }) => [
|
|
22
|
+
httpResponseSerializer({
|
|
23
|
+
serializers: [
|
|
24
|
+
{
|
|
25
|
+
regex: /^application\/xml$/,
|
|
26
|
+
serializer: ({ body }) => `<message>${body}</message>`,
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
regex: /^application\/json$/,
|
|
30
|
+
serializer: ({ body }) => JSON.stringify(body),
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
regex: /^text\/plain$/,
|
|
34
|
+
serializer: ({ body }) => body,
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
default: 'application/json',
|
|
38
|
+
}),
|
|
39
|
+
responseWrapper.responseWrapper({ logger }),
|
|
40
|
+
offlineAuthorizer.offlineAuth({ logger, enabled: offlineAuthEnabled }),
|
|
41
|
+
normalizer.normalizerMiddleware(),
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
exports.middy = core;
|
|
45
|
+
exports.httpErrorHandler = httpErrorHandler;
|
|
46
|
+
exports.httpResponseSerializer = httpResponseSerializer;
|
|
47
|
+
exports.sqsJsonBodyParser = sqsJsonBodyParser;
|
|
48
|
+
exports.contextualLogger = contextualLogger.contextualLogger;
|
|
49
|
+
exports.ioLogger = ioLogger.ioLogger;
|
|
50
|
+
exports.offlineAuth = offlineAuthorizer.offlineAuth;
|
|
51
|
+
exports.responseWrapper = responseWrapper.responseWrapper;
|
|
52
|
+
exports.warmupMiddleware = warmup.warmupMiddleware;
|
|
53
|
+
exports.getApiGatewayMiddlewares = getApiGatewayMiddlewares;
|
|
54
|
+
exports.getBaseMiddlewares = getBaseMiddlewares;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var inputOutputLogger = require('@middy/input-output-logger');
|
|
4
|
+
var index = require('../config/index.js');
|
|
5
|
+
|
|
6
|
+
const ioLogger = ({ logger }) => inputOutputLogger({
|
|
7
|
+
omitPaths: [
|
|
8
|
+
'event.multiValueHeaders',
|
|
9
|
+
'event.multiValueQueryStringParameters',
|
|
10
|
+
'event.resource',
|
|
11
|
+
'event.httpMethod',
|
|
12
|
+
'event.headers',
|
|
13
|
+
'event.stageVariables',
|
|
14
|
+
'event.requestContext.resourceId',
|
|
15
|
+
'event.requestContext.resourcePath',
|
|
16
|
+
'event.requestContext.httpMethod',
|
|
17
|
+
'event.requestContext.extendedRequestId',
|
|
18
|
+
'event.requestContext.requestTime',
|
|
19
|
+
'event.requestContext.path',
|
|
20
|
+
'event.requestContext.accountId',
|
|
21
|
+
'event.requestContext.protocol',
|
|
22
|
+
'event.requestContext.stage',
|
|
23
|
+
'event.requestContext.domainPrefix',
|
|
24
|
+
'event.requestContext.requestTimeEpoch',
|
|
25
|
+
'event.requestContext.apiId',
|
|
26
|
+
'event.requestContext.domainName',
|
|
27
|
+
'event.requestContext.identity',
|
|
28
|
+
'event.isBase64Encoded',
|
|
29
|
+
'event.body',
|
|
30
|
+
'response.body',
|
|
31
|
+
'response.headers',
|
|
32
|
+
],
|
|
33
|
+
logger: (req) => {
|
|
34
|
+
if (index.isLocal() || index.isTest()) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const message = req?.event ? 'event' : 'response';
|
|
38
|
+
logger.info(req.event ?? req.response, message);
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
exports.ioLogger = ioLogger;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const normalizerMiddleware = () => {
|
|
4
|
+
return {
|
|
5
|
+
before: async (request) => {
|
|
6
|
+
const event = request.event;
|
|
7
|
+
event.queryStringParameters = event.queryStringParameters || {};
|
|
8
|
+
event.pathParameters = event.pathParameters || {};
|
|
9
|
+
},
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
exports.normalizerMiddleware = normalizerMiddleware;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var clientLambda = require('@aws-sdk/client-lambda');
|
|
4
|
+
var index = require('../config/index.js');
|
|
5
|
+
|
|
6
|
+
const defaultAuthFunctionName = process.env.STAGE === 'dev'
|
|
7
|
+
? 'spytec-web-api-auth-dev-AuthorizerFunctionV4'
|
|
8
|
+
: 'spytec-web-api-auth-prod-AuthorizerFunctionV4';
|
|
9
|
+
const offlineAuth = ({ authFunctionName = defaultAuthFunctionName, enabled = index.isOffline(), logger, }) => {
|
|
10
|
+
const lambdaClient = new clientLambda.LambdaClient({ region: process.env.AWS_REGION });
|
|
11
|
+
return {
|
|
12
|
+
before: async (request) => {
|
|
13
|
+
if (!enabled)
|
|
14
|
+
return;
|
|
15
|
+
const { event } = request;
|
|
16
|
+
// Extract Bearer token from the Authorization header
|
|
17
|
+
const authHeader = event.headers?.Authorization || event.headers?.authorization;
|
|
18
|
+
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
19
|
+
// throw new Error('Authorization header is missing or invalid')
|
|
20
|
+
// if we can't extract the token, this is is a public route, ignore it
|
|
21
|
+
logger.warn('Authorization header is missing or invalid, skipping fake offline authorization');
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const token = authHeader.slice(7); // Remove 'Bearer ' prefix
|
|
25
|
+
// Prepare payload for the auth function
|
|
26
|
+
const payload = {
|
|
27
|
+
authorizationToken: token,
|
|
28
|
+
};
|
|
29
|
+
// Invoke the auth function manually
|
|
30
|
+
const command = new clientLambda.InvokeCommand({
|
|
31
|
+
FunctionName: authFunctionName,
|
|
32
|
+
Payload: Buffer.from(JSON.stringify(payload)),
|
|
33
|
+
});
|
|
34
|
+
try {
|
|
35
|
+
const response = await lambdaClient.send(command);
|
|
36
|
+
const responsePayload = JSON.parse(Buffer.from(response.Payload ?? '').toString());
|
|
37
|
+
if (responsePayload.errorMessage) {
|
|
38
|
+
throw new Error(responsePayload.errorMessage);
|
|
39
|
+
}
|
|
40
|
+
event.requestContext.authorizer = responsePayload.context;
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
logger.error(error, 'Error invoking auth function');
|
|
44
|
+
throw new Error('Authorization failed');
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
exports.offlineAuth = offlineAuth;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var response = require('./http/response.js');
|
|
4
|
+
|
|
5
|
+
const responseWrapper = ({ logger }) => {
|
|
6
|
+
const responseWrapperMiddlewareAfter = (req) => {
|
|
7
|
+
req.response = response.buildProxyResult(req.response);
|
|
8
|
+
};
|
|
9
|
+
const responseWrapperMiddlewareError = (req) => {
|
|
10
|
+
const statusCode = req.error?.code ?? 500;
|
|
11
|
+
const errorMessage = req.error?.message || 'Error';
|
|
12
|
+
const loggerMethod = statusCode >= 500 ? 'error' : 'info';
|
|
13
|
+
logger[loggerMethod](req.error, 'Request failed');
|
|
14
|
+
req.response = response.buildProxyResult({
|
|
15
|
+
statusCode,
|
|
16
|
+
message: errorMessage,
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
return {
|
|
20
|
+
after: responseWrapperMiddlewareAfter,
|
|
21
|
+
onError: responseWrapperMiddlewareError,
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
exports.responseWrapper = responseWrapper;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var BadRequestError = require('../errors/BadRequestError.js');
|
|
4
|
+
|
|
5
|
+
const validator = ({ schema, allowUnknown = true, logger }) => {
|
|
6
|
+
const validatorMiddlewareBefore = (request) => {
|
|
7
|
+
const { error, value } = schema.validate(request.event, {
|
|
8
|
+
allowUnknown,
|
|
9
|
+
errors: {
|
|
10
|
+
label: 'key',
|
|
11
|
+
wrap: {
|
|
12
|
+
label: false,
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
if (error) {
|
|
17
|
+
logger.warn(error, 'Validation error');
|
|
18
|
+
throw error.isJoi ? new BadRequestError.default(error.message) : error;
|
|
19
|
+
}
|
|
20
|
+
request.event = value;
|
|
21
|
+
};
|
|
22
|
+
return {
|
|
23
|
+
before: validatorMiddlewareBefore,
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
exports.validator = validator;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
4
|
+
const defaults = {
|
|
5
|
+
isWarmingUp: (event) => event.source === 'serverless-plugin-warmup',
|
|
6
|
+
};
|
|
7
|
+
const warmupMiddleware = (opt = {}) => {
|
|
8
|
+
const options = { ...defaults, ...opt };
|
|
9
|
+
const warmupMiddlewareBefore = (request) => {
|
|
10
|
+
if (options.isWarmingUp(request.event)) {
|
|
11
|
+
return 'warmup';
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
return {
|
|
15
|
+
before: warmupMiddlewareBefore,
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
exports.warmupMiddleware = warmupMiddleware;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
class LambdaCache {
|
|
4
|
+
collectionName;
|
|
5
|
+
/**
|
|
6
|
+
* @param {String} collectionName (not required) - The collection key used to store the cache values.
|
|
7
|
+
* If not provide default collection name uses :
|
|
8
|
+
* ${process.env.AWS_LAMBDA_FUNCTION_NAME}-${process.env.AWS_LAMBDA_FUNCTION_VERSION}
|
|
9
|
+
* */
|
|
10
|
+
constructor(collectionName) {
|
|
11
|
+
this.collectionName =
|
|
12
|
+
collectionName ?? `${process.env.AWS_LAMBDA_FUNCTION_NAME}-${process.env.AWS_LAMBDA_FUNCTION_VERSION}`;
|
|
13
|
+
if (!global['CACHE_STORAGE']) {
|
|
14
|
+
global['CACHE_STORAGE'] = {};
|
|
15
|
+
}
|
|
16
|
+
if (!global['CACHE_STORAGE'][this.collectionName]) {
|
|
17
|
+
global['CACHE_STORAGE'][this.collectionName] = new Map();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* @param {String} key (required) - cache key
|
|
22
|
+
* @param {Object} value (required) - cache value
|
|
23
|
+
* @param {Number} expire (required) - cache expiration time (seconds)
|
|
24
|
+
* */
|
|
25
|
+
set(key, value, ttl) {
|
|
26
|
+
const expire = 1000 * ttl + Date.now();
|
|
27
|
+
global['CACHE_STORAGE'][this.collectionName].set(key, { value, expire });
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* @param {String} key (required) - cache key to get
|
|
31
|
+
* */
|
|
32
|
+
get(key) {
|
|
33
|
+
if (!key) {
|
|
34
|
+
throw new Error('key is required!');
|
|
35
|
+
}
|
|
36
|
+
const record = global['CACHE_STORAGE'][this.collectionName].get(key);
|
|
37
|
+
if (!record) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
if (!record.expire || record.expire > Date.now()) {
|
|
41
|
+
return record.value;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
return this.remove(key);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* @param {String} key (required) - cache key to remove
|
|
49
|
+
* */
|
|
50
|
+
remove(key) {
|
|
51
|
+
const record = global['CACHE_STORAGE'][this.collectionName].get(key);
|
|
52
|
+
if (!record) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
global['CACHE_STORAGE'][this.collectionName].delete(key);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
exports.LambdaCache = LambdaCache;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const promiseWithCache = async (promise, cacheInstance, cacheKey, ttl) => {
|
|
4
|
+
let instance = cacheInstance.get(cacheKey);
|
|
5
|
+
if (!instance) {
|
|
6
|
+
instance = await promise();
|
|
7
|
+
cacheInstance.set(cacheKey, instance, ttl);
|
|
8
|
+
}
|
|
9
|
+
return instance;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
exports.promiseWithCache = promiseWithCache;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const promiseWithTimeout = (promise, ms, timeoutError = new Error('Promise timed out')) => {
|
|
4
|
+
// create a promise that rejects in milliseconds
|
|
5
|
+
const timeout = new Promise((_, reject) => {
|
|
6
|
+
setTimeout(() => {
|
|
7
|
+
reject(timeoutError);
|
|
8
|
+
}, ms);
|
|
9
|
+
});
|
|
10
|
+
// returns a race between timeout and the passed promise
|
|
11
|
+
return Promise.race([promise, timeout]);
|
|
12
|
+
//Adding comment to test
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
exports.promiseWithTimeout = promiseWithTimeout;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var dayjs = require('dayjs');
|
|
4
|
+
var timezone = require('dayjs/plugin/timezone');
|
|
5
|
+
var utc = require('dayjs/plugin/utc');
|
|
6
|
+
var Joi = require('joi');
|
|
7
|
+
var qs = require('qs');
|
|
8
|
+
|
|
9
|
+
function _interopNamespaceDefault(e) {
|
|
10
|
+
var n = Object.create(null);
|
|
11
|
+
if (e) {
|
|
12
|
+
Object.keys(e).forEach(function (k) {
|
|
13
|
+
if (k !== 'default') {
|
|
14
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
15
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
get: function () { return e[k]; }
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
n.default = e;
|
|
23
|
+
return Object.freeze(n);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
var Joi__namespace = /*#__PURE__*/_interopNamespaceDefault(Joi);
|
|
27
|
+
var qs__namespace = /*#__PURE__*/_interopNamespaceDefault(qs);
|
|
28
|
+
|
|
29
|
+
dayjs.extend(utc);
|
|
30
|
+
dayjs.extend(timezone);
|
|
31
|
+
const json = Joi__namespace.extend((joi) => {
|
|
32
|
+
return {
|
|
33
|
+
type: 'object',
|
|
34
|
+
base: joi.object(),
|
|
35
|
+
messages: {
|
|
36
|
+
'json.valid': 'must be valid JSON',
|
|
37
|
+
},
|
|
38
|
+
coerce(value) {
|
|
39
|
+
try {
|
|
40
|
+
return { value: JSON.parse(value) };
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
validate(value, helpers) {
|
|
47
|
+
if (!value) {
|
|
48
|
+
return { value, errors: helpers.error('json.valid') };
|
|
49
|
+
}
|
|
50
|
+
return { value };
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
});
|
|
54
|
+
const urlEncoded = Joi__namespace.extend((joi) => {
|
|
55
|
+
return {
|
|
56
|
+
type: 'object',
|
|
57
|
+
base: joi.object(),
|
|
58
|
+
coerce(value) {
|
|
59
|
+
return { value: qs__namespace.parse(value) };
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
const imeiSchema = Joi__namespace.string()
|
|
64
|
+
.regex(/^\d{15,16}$/)
|
|
65
|
+
.message('Invalid IMEI');
|
|
66
|
+
const iccidSchema = Joi__namespace.string()
|
|
67
|
+
.regex(/^[0-9A-Za-z]{18,22}$/)
|
|
68
|
+
.message('Invalid ICCID');
|
|
69
|
+
const SpytecJoi = Joi__namespace.extend((joi) => ({
|
|
70
|
+
type: 'imei',
|
|
71
|
+
messages: 'Invalid IMEI',
|
|
72
|
+
base: joi.string().regex(/^\d{15,16}$/),
|
|
73
|
+
}), (joi) => ({
|
|
74
|
+
type: 'iccid',
|
|
75
|
+
messages: 'Invalid ICCID',
|
|
76
|
+
base: joi.string().regex(/^[0-9A-Za-z]{18,22}$/),
|
|
77
|
+
}), (joi) => ({
|
|
78
|
+
type: 'urlEncodedObject',
|
|
79
|
+
base: joi.object(),
|
|
80
|
+
coerce(value) {
|
|
81
|
+
return { value: qs__namespace.parse(value) };
|
|
82
|
+
},
|
|
83
|
+
}), (joi) => ({
|
|
84
|
+
type: 'jsonObject',
|
|
85
|
+
base: joi.object(),
|
|
86
|
+
coerce(value) {
|
|
87
|
+
try {
|
|
88
|
+
return { value: JSON.parse(value) };
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
validate(value, helpers) {
|
|
95
|
+
if (!value) {
|
|
96
|
+
return { value, errors: helpers.error('json.valid') };
|
|
97
|
+
}
|
|
98
|
+
return { value };
|
|
99
|
+
},
|
|
100
|
+
}), (joi) => ({
|
|
101
|
+
type: 'delimitedArray',
|
|
102
|
+
base: joi.array().default([]),
|
|
103
|
+
coerce: (value) => ({
|
|
104
|
+
value: value.split ? value.split(',') : value,
|
|
105
|
+
}),
|
|
106
|
+
}), (joi) => ({
|
|
107
|
+
type: 'queryStringParameters',
|
|
108
|
+
messages: 'Missing query parameters',
|
|
109
|
+
base: joi.object().required(),
|
|
110
|
+
}), (joi) => ({
|
|
111
|
+
type: 'date',
|
|
112
|
+
base: joi.date(),
|
|
113
|
+
prepare(value, helpers) {
|
|
114
|
+
try {
|
|
115
|
+
const dayjsDate = dayjs.tz(value, 'UTC');
|
|
116
|
+
if (dayjsDate.isValid()) {
|
|
117
|
+
return { value: dayjsDate.toDate() };
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
return helpers.error('any.invalid');
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
}), (joi) => ({
|
|
125
|
+
type: 'jsonArray',
|
|
126
|
+
base: joi.array(),
|
|
127
|
+
coerce(value) {
|
|
128
|
+
try {
|
|
129
|
+
return { value: JSON.parse(value) };
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
return { value: null };
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
validate(value, helpers) {
|
|
136
|
+
if (!Array.isArray(value)) {
|
|
137
|
+
return { value, errors: helpers.error('jsonArray.schema') };
|
|
138
|
+
}
|
|
139
|
+
return { value };
|
|
140
|
+
},
|
|
141
|
+
}), (joi) => ({
|
|
142
|
+
type: 'base64ThenUriEncodedObject',
|
|
143
|
+
base: joi.object(),
|
|
144
|
+
coerce(value) {
|
|
145
|
+
try {
|
|
146
|
+
const decodedValue = decodeURIComponent(Buffer.from(value, 'base64').toString());
|
|
147
|
+
return { value: JSON.parse(decodedValue) };
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
validate(value, helpers) {
|
|
154
|
+
if (!value) {
|
|
155
|
+
return { value, errors: helpers.error('json.valid') };
|
|
156
|
+
}
|
|
157
|
+
return { value };
|
|
158
|
+
},
|
|
159
|
+
}));
|
|
160
|
+
|
|
161
|
+
exports.SpytecJoi = SpytecJoi;
|
|
162
|
+
exports.iccidSchema = iccidSchema;
|
|
163
|
+
exports.imeiSchema = imeiSchema;
|
|
164
|
+
exports.json = json;
|
|
165
|
+
exports.urlEncoded = urlEncoded;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var custom = require('./custom.js');
|
|
4
|
+
var requestContext = require('./requestContext.js');
|
|
5
|
+
|
|
6
|
+
const defaultApiSchema = custom.SpytecJoi.object({
|
|
7
|
+
requestContext: requestContext.getRequestContextValidator(),
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
exports.SpytecJoi = custom.SpytecJoi;
|
|
11
|
+
exports.iccidSchema = custom.iccidSchema;
|
|
12
|
+
exports.imeiSchema = custom.imeiSchema;
|
|
13
|
+
exports.json = custom.json;
|
|
14
|
+
exports.urlEncoded = custom.urlEncoded;
|
|
15
|
+
exports.getAuthorizerValidator = requestContext.getAuthorizerValidator;
|
|
16
|
+
exports.getAuthorizerValidatorV4 = requestContext.getAuthorizerValidatorV4;
|
|
17
|
+
exports.getRequestContextValidator = requestContext.getRequestContextValidator;
|
|
18
|
+
exports.getRequestContextValidatorV4 = requestContext.getRequestContextValidatorV4;
|
|
19
|
+
exports.requestContextValidator = requestContext.requestContextValidator;
|
|
20
|
+
exports.defaultApiSchema = defaultApiSchema;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var Joi = require('joi');
|
|
4
|
+
var custom = require('./custom.js');
|
|
5
|
+
|
|
6
|
+
function _interopNamespaceDefault(e) {
|
|
7
|
+
var n = Object.create(null);
|
|
8
|
+
if (e) {
|
|
9
|
+
Object.keys(e).forEach(function (k) {
|
|
10
|
+
if (k !== 'default') {
|
|
11
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
12
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: function () { return e[k]; }
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
n.default = e;
|
|
20
|
+
return Object.freeze(n);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
var Joi__namespace = /*#__PURE__*/_interopNamespaceDefault(Joi);
|
|
24
|
+
|
|
25
|
+
const getAuthorizerValidator = (params = {}) => {
|
|
26
|
+
return Joi__namespace.object({
|
|
27
|
+
clientId: Joi__namespace.number().greater(0).required(),
|
|
28
|
+
userId: Joi__namespace.string().guid( /*{ version: 'uuidv4' }*/).required(),
|
|
29
|
+
resources: custom.json.object({}),
|
|
30
|
+
scope: Joi__namespace.string().optional(),
|
|
31
|
+
// .error(() => new UnauthorizedError(`missing scope ${scope}`))
|
|
32
|
+
type: Joi__namespace.string().optional(),
|
|
33
|
+
// .error(() => new UnauthorizedError(`missing user type ${type}`))
|
|
34
|
+
enterprise: Joi__namespace.boolean().default(false),
|
|
35
|
+
maintenanceModule: Joi__namespace.boolean().default(false),
|
|
36
|
+
billingMethod: Joi__namespace.string().optional(),
|
|
37
|
+
customerSegment: Joi__namespace.string().optional(),
|
|
38
|
+
securityGroupTagId: Joi__namespace.number().optional().allow(null),
|
|
39
|
+
securityRole: Joi__namespace.string().optional().allow(null),
|
|
40
|
+
...params,
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
const getAuthorizerValidatorV4 = (params = {}) => {
|
|
44
|
+
return Joi__namespace.object({
|
|
45
|
+
clientId: Joi__namespace.number().greater(0).required(),
|
|
46
|
+
userId: Joi__namespace.string().guid( /*{ version: 'uuidv4' }*/).required(),
|
|
47
|
+
scope: Joi__namespace.string().optional(),
|
|
48
|
+
type: Joi__namespace.string().optional(),
|
|
49
|
+
...params,
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* @deprecated
|
|
54
|
+
*/
|
|
55
|
+
const requestContextValidator = Joi__namespace.object({
|
|
56
|
+
authorizer: getAuthorizerValidator(),
|
|
57
|
+
});
|
|
58
|
+
const getRequestContextValidator = (params = {}) => {
|
|
59
|
+
return Joi__namespace.object({
|
|
60
|
+
authorizer: getAuthorizerValidator(params),
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
const getRequestContextValidatorV4 = (params = {}) => {
|
|
64
|
+
return Joi__namespace.object({
|
|
65
|
+
authorizer: getAuthorizerValidatorV4(params),
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
exports.getAuthorizerValidator = getAuthorizerValidator;
|
|
70
|
+
exports.getAuthorizerValidatorV4 = getAuthorizerValidatorV4;
|
|
71
|
+
exports.getRequestContextValidator = getRequestContextValidator;
|
|
72
|
+
exports.getRequestContextValidatorV4 = getRequestContextValidatorV4;
|
|
73
|
+
exports.requestContextValidator = requestContextValidator;
|