Skip to content

Commit

Permalink
feat(worker): only support POST, PUT and PATCH verbs
Browse files Browse the repository at this point in the history
  • Loading branch information
manast committed Aug 10, 2024
1 parent c809436 commit 8975545
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 44 deletions.
21 changes: 13 additions & 8 deletions src/validators/workers.validators.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,17 @@ describe('validateWorkerEndpoint', () => {
});

it('validates with correct fields', () => {
const endpoint = { url: 'https://example.com', method: 'GET' };
const endpoint = { url: 'https://example.com', method: 'POST' };
expect(() => validateWorkerEndpoint(endpoint)).not.toThrow();
});

it('throws with invalid method', () => {
const endpoint = { url: 'https://example.com', method: 'GET' };
expect(() => validateWorkerEndpoint(endpoint)).toThrow();
});

it('throws an error for missing url', () => {
expect(() => validateWorkerEndpoint(<any>{ method: 'GET' })).toThrow('url is required');
expect(() => validateWorkerEndpoint(<any>{ method: 'PATCH' })).toThrow('url is required');
});

it('throws an error for missing method', () => {
Expand All @@ -110,11 +115,11 @@ describe('validateWorkerEndpoint', () => {
});

it('throws an error for invalid url', () => {
expect(() => validateWorkerEndpoint({ url: 'htp://example.com', method: 'GET' })).toThrow('Invalid URL');
expect(() => validateWorkerEndpoint({ url: 'htp://example.com', method: 'PUT' })).toThrow('Invalid URL');
});

it('throws an error for invalid url', () => {
expect(() => validateWorkerEndpoint({ url: 'not a url', method: 'GET' })).toThrow('Invalid URL');
expect(() => validateWorkerEndpoint({ url: 'not a url', method: 'POST' })).toThrow('Invalid URL');
});
});

Expand All @@ -124,21 +129,21 @@ describe('validateWorkerMetadata', () => {
});

it('validates with correct fields', () => {
const metadata = { queue: 'myQueue', endpoint: { url: 'https://example.com', method: 'GET' } };
const metadata = { queue: 'myQueue', endpoint: { url: 'https://example.com', method: 'POST' } };
expect(() => validateWorkerMetadata(metadata)).not.toThrow();
});

// Additional tests for opts validation
it('throws an error for invalid opts field', () => {
expect(() => validateWorkerMetadata({ queue: 'myQueue', endpoint: { url: 'https://example.com', method: 'GET' }, opts: <any>{ invalidField: true } })).toThrow('Invalid field');
expect(() => validateWorkerMetadata({ queue: 'myQueue', endpoint: { url: 'https://example.com', method: 'PUT' }, opts: <any>{ invalidField: true } })).toThrow('Invalid field');
});

it('throws an error for invalid queue name', () => {
expect(() => validateWorkerMetadata({ queue: 'ab', endpoint: { url: 'https://example.com', method: 'GET' } })).toThrow('queue name must be at least');
expect(() => validateWorkerMetadata({ queue: 'ab', endpoint: { url: 'https://example.com', method: 'PATCH' } })).toThrow('queue name must be at least');
});

it('throws an error for invalid queue name', () => {
expect(() => validateWorkerMetadata({ queue: 'a'.repeat(101), endpoint: { url: 'https://example.com', method: 'GET' } })).toThrow('queue name must be at most');
expect(() => validateWorkerMetadata({ queue: 'a'.repeat(101), endpoint: { url: 'https://example.com', method: 'POST' } })).toThrow('queue name must be at most');
});

it('throws an error for invalid endpoint', () => {
Expand Down
104 changes: 68 additions & 36 deletions src/validators/workers.validators.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { WorkerMetadata, WorkerEndpoint, WorkerSimpleOptions } from "../interfaces";
import {
WorkerMetadata,
WorkerEndpoint,
WorkerSimpleOptions,
} from "../interfaces";
import { validateQueueName } from "./queue.validators";

const validProtocols = new Set(['http:', 'https:']);
const validProtocols = new Set(["http:", "https:"]);
export function isValidUrl(s: string) {
try {
const url = new URL(s);
Expand All @@ -11,32 +15,41 @@ export function isValidUrl(s: string) {
}
}

const allowedOnFinishFields = new Set(['count', 'age']);
export const validateRemoveOnFinish = (removeOnFinish: {
count?: number;
age?: number;
}, field: "removeOnComplete" | "removeOnFail") => {
const allowedOnFinishFields = new Set(["count", "age"]);
export const validateRemoveOnFinish = (
removeOnFinish: {
count?: number;
age?: number;
},
field: "removeOnComplete" | "removeOnFail"
) => {
for (const allowedField in removeOnFinish) {
if (!allowedOnFinishFields.has(allowedField)) {
throw new Error(`Invalid field: ${field}.${allowedField}`);
}
}

if (removeOnFinish.count && (!Number.isInteger(removeOnFinish.count) || removeOnFinish.count <= 0)) {
if (
removeOnFinish.count &&
(!Number.isInteger(removeOnFinish.count) || removeOnFinish.count <= 0)
) {
throw new Error(`Invalid ${field}.count`);
}

if (removeOnFinish.age && (!Number.isInteger(removeOnFinish.age) || removeOnFinish.age <= 0)) {
if (
removeOnFinish.age &&
(!Number.isInteger(removeOnFinish.age) || removeOnFinish.age <= 0)
) {
throw new Error(`Invalid ${field}.age`);
}
}
};

const allowedWorkerOptionsFields = new Set([
'concurrency',
'removeOnComplete',
'removeOnFail',
'limiter',
'maxStalledCount'
"concurrency",
"removeOnComplete",
"removeOnFail",
"limiter",
"maxStalledCount",
]);
export const validateWorkerOptions = (workerOptions: WorkerSimpleOptions) => {
for (const field in workerOptions) {
Expand All @@ -45,44 +58,60 @@ export const validateWorkerOptions = (workerOptions: WorkerSimpleOptions) => {
}
}

if (workerOptions.limiter && (!Number.isInteger(workerOptions.limiter.max) || workerOptions.limiter.max <= 0)) {
throw new Error('Invalid limiter.max');
if (
workerOptions.limiter &&
(!Number.isInteger(workerOptions.limiter.max) ||
workerOptions.limiter.max <= 0)
) {
throw new Error("Invalid limiter.max");
}

if (workerOptions.limiter && (!Number.isInteger(workerOptions.limiter.duration) || workerOptions.limiter.duration <= 0)) {
throw new Error('Invalid limiter.duration');
if (
workerOptions.limiter &&
(!Number.isInteger(workerOptions.limiter.duration) ||
workerOptions.limiter.duration <= 0)
) {
throw new Error("Invalid limiter.duration");
}

if (workerOptions.maxStalledCount && (!Number.isInteger(workerOptions.maxStalledCount) || workerOptions.maxStalledCount <= 0)) {
throw new Error('Invalid maxStalledCount');
if (
workerOptions.maxStalledCount &&
(!Number.isInteger(workerOptions.maxStalledCount) ||
workerOptions.maxStalledCount <= 0)
) {
throw new Error("Invalid maxStalledCount");
}

if (workerOptions.concurrency && (!Number.isInteger(workerOptions.concurrency) || workerOptions.concurrency <= 0)) {
throw new Error('Invalid concurrency');
if (
workerOptions.concurrency &&
(!Number.isInteger(workerOptions.concurrency) ||
workerOptions.concurrency <= 0)
) {
throw new Error("Invalid concurrency");
}

if (workerOptions.removeOnComplete) {
validateRemoveOnFinish(workerOptions.removeOnComplete, 'removeOnComplete');
validateRemoveOnFinish(workerOptions.removeOnComplete, "removeOnComplete");
}

if (workerOptions.removeOnFail) {
validateRemoveOnFinish(workerOptions.removeOnFail, 'removeOnFail');
validateRemoveOnFinish(workerOptions.removeOnFail, "removeOnFail");
}
}
};

const validHttpMethods = new Set(['GET', 'POST', 'PUT', 'DELETE', 'PATCH']);
const allowedEndpointFields = new Set(['url', 'method', 'headers', 'timeout']);
const validHttpMethods = new Set(["POST", "PUT", "PATCH"]);
const allowedEndpointFields = new Set(["url", "method", "headers", "timeout"]);

export const validateWorkerEndpoint = (workerEndpoint: WorkerEndpoint) => {
const requiredFields: (keyof WorkerEndpoint)[] = ['url', 'method'];
const requiredFields: (keyof WorkerEndpoint)[] = ["url", "method"];
for (const field of requiredFields) {
if (!workerEndpoint[field]) {
throw new Error(`${field} is required`);
}
}

if (!isValidUrl(workerEndpoint.url)) {
throw new Error('Invalid URL');
throw new Error("Invalid URL");
}

if (!validHttpMethods.has(workerEndpoint.method.toUpperCase())) {
Expand All @@ -95,14 +124,17 @@ export const validateWorkerEndpoint = (workerEndpoint: WorkerEndpoint) => {
}
}

if (workerEndpoint.timeout && (!Number.isInteger(workerEndpoint.timeout) || workerEndpoint.timeout <= 0)) {
throw new Error('Invalid timeout');
if (
workerEndpoint.timeout &&
(!Number.isInteger(workerEndpoint.timeout) || workerEndpoint.timeout <= 0)
) {
throw new Error("Invalid timeout");
}
}
};

const allowedWorkerMetaddaFields = new Set(['queue', 'endpoint', 'opts']);
const allowedWorkerMetaddaFields = new Set(["queue", "endpoint", "opts"]);
export const validateWorkerMetadata = (workerMetadata: WorkerMetadata) => {
const requiredFields: (keyof WorkerMetadata)[] = ['queue', 'endpoint'];
const requiredFields: (keyof WorkerMetadata)[] = ["queue", "endpoint"];
for (const field of requiredFields) {
if (!workerMetadata[field]) {
throw new Error(`${field} is required`);
Expand All @@ -123,4 +155,4 @@ export const validateWorkerMetadata = (workerMetadata: WorkerMetadata) => {
if (workerMetadata.opts) {
validateWorkerOptions(workerMetadata.opts);
}
}
};

0 comments on commit 8975545

Please sign in to comment.