Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@forge/events

Package Overview
Dependencies
Maintainers
8
Versions
289
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@forge/events - npm Package Compare versions

Comparing version 0.2.0 to 0.3.0-next.0

6

CHANGELOG.md
# @forge/events
## 0.3.0-next.0
### Minor Changes
- 767d40d: Explicitly add uuid dependency and whitelist WHP getStats endpoint
## 0.2.0

@@ -4,0 +10,0 @@

@@ -61,2 +61,27 @@ "use strict";

});
it('should throw InternalServerError when WHP returns 422 response', async () => {
const apiClientMock = utils_1.getApiClientMock({
errors: ['jobId must not be null', 'queueName must not be null']
}, 422);
const jobProgress = getJobProgress(apiClientMock, 'test-queue-name#test-job-id');
await expect(jobProgress.getStats()).rejects.toThrow(new errors_1.InternalServerError(`422 Status Text: jobId must not be null, queueName must not be null`));
utils_1.verifyApiClientCalledWith(apiClientMock, '/webhook/queue/stats/{cloudId}/{environmentId}/{appId}/{appVersion}', {
queueName: 'test-queue-name',
jobId: 'test-job-id'
});
});
it('should throw errors when queueName or jobId is empty', async () => {
const apiClientMock = utils_1.getApiClientMock({
success: 100,
inProgress: 50,
failed: 1
}, 200);
const jobProgress1 = getJobProgress(apiClientMock, 'test-queue-name#');
await expect(jobProgress1.getStats()).rejects.toThrow(new errors_1.JobDoesNotExistError(`jobId must not be empty`));
const jobProgress2 = getJobProgress(apiClientMock, '#test-job-id');
await expect(jobProgress2.getStats()).rejects.toThrow(new errors_1.InvalidQueueNameError('Queue names can only contain alphanumeric characters and the dash and underscore characters.'));
const jobProgress3 = getJobProgress(apiClientMock, '');
await expect(jobProgress3.getStats()).rejects.toThrow(new errors_1.JobDoesNotExistError(`jobId must not be empty`));
expect(apiClientMock).toHaveBeenCalledTimes(0);
});
});

@@ -156,2 +156,9 @@ "use strict";

});
it('should throw InternalServerError for error response without response body', async () => {
const apiClientMock = utils_1.getApiClientMockWithoutResponseBody(504, 'Gateway Timeout');
const queue = getQueue(apiClientMock, 'name');
const payload = [1];
await expect(queue.push(1)).rejects.toThrow(new errors_1.InternalServerError(`504 Gateway Timeout`, 504));
utils_1.verifyApiClientCalledPushPathWith(apiClientMock, payload, 'name');
});
});

@@ -158,0 +165,0 @@ describe('getJob', () => {

1

out/__test__/utils.d.ts
/// <reference types="jest" />
import { Payload } from '../types';
export declare const getApiClientMock: (response?: any, statusCode?: number) => jest.Mock<any, any>;
export declare const getApiClientMockWithoutResponseBody: (statusCode: number, statusText: string) => jest.Mock<any, any>;
export declare const verifyApiClientCalledWith: (apiClientMock: jest.Mock, path: string, expectedBody: any) => void;
export declare const verifyApiClientCalledPushPathWith: (apiClientMock: jest.Mock, payloads: Payload | Payload[], name: string) => void;
//# sourceMappingURL=utils.d.ts.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.verifyApiClientCalledPushPathWith = exports.verifyApiClientCalledWith = exports.getApiClientMock = void 0;
exports.verifyApiClientCalledPushPathWith = exports.verifyApiClientCalledWith = exports.getApiClientMockWithoutResponseBody = exports.getApiClientMock = void 0;
exports.getApiClientMock = (response, statusCode = 201) => {

@@ -12,2 +12,9 @@ return jest.fn().mockReturnValue({

};
exports.getApiClientMockWithoutResponseBody = (statusCode, statusText) => {
return jest.fn().mockReturnValue({
ok: statusCode === 200,
status: statusCode,
statusText: statusText
});
};
exports.verifyApiClientCalledWith = (apiClientMock, path, expectedBody) => {

@@ -14,0 +21,0 @@ expect(apiClientMock).toHaveBeenCalledWith(path, expect.objectContaining({

2

out/index.d.ts
export { Queue } from './queue';
export { InvalidQueueNameError, TooManyEventsError, PayloadTooBigError, NoEventsToPushError, RateLimitError, PartialSuccessError, InternalServerError, JobDoesNotExistError } from './errors';
export { InvalidQueueNameError, TooManyEventsError, PayloadTooBigError, NoEventsToPushError, RateLimitError, PartialSuccessError, InternalServerError, JobDoesNotExistError, InvalidPushSettingsError } from './errors';
export { JobProgress } from './jobProgress';
//# sourceMappingURL=index.d.ts.map

@@ -14,3 +14,4 @@ "use strict";

Object.defineProperty(exports, "JobDoesNotExistError", { enumerable: true, get: function () { return errors_1.JobDoesNotExistError; } });
Object.defineProperty(exports, "InvalidPushSettingsError", { enumerable: true, get: function () { return errors_1.InvalidPushSettingsError; } });
var jobProgress_1 = require("./jobProgress");
Object.defineProperty(exports, "JobProgress", { enumerable: true, get: function () { return jobProgress_1.JobProgress; } });

@@ -18,2 +18,3 @@ "use strict";

};
validators_1.validateGetStatsPayload(getStatsRequest);
const response = await queries_1.post(queries_1.GET_STATS_PATH, getStatsRequest, this.apiClient);

@@ -20,0 +21,0 @@ await validators_1.validateGetStatsAPIResponse(response, getStatsRequest);

@@ -16,3 +16,3 @@ "use strict";

async push(payloads, pushSettings) {
validators_1.validatePayloads(payloads);
validators_1.validatePushPayloads(payloads);
const queueName = this.queueParams.key;

@@ -19,0 +19,0 @@ const jobId = v4_1.default();

import { APIResponse, GetStatsRequest, Payload, PushRequest, PushSettings } from './types';
export declare const validateQueueKey: (queueName: string) => void;
export declare const validatePushSettings: (settings: PushSettings) => void;
export declare const validatePayloads: (payloads: Payload | Payload[]) => void;
export declare const validatePushPayloads: (payloads: Payload | Payload[]) => void;
export declare const validateGetStatsPayload: (getStatsRequest: GetStatsRequest) => void;
export declare const validateAPIResponse: (response: APIResponse, expectedSuccessStatus: number) => Promise<void>;

@@ -6,0 +7,0 @@ export declare const validatePushAPIResponse: (response: APIResponse, requestBody: PushRequest) => Promise<void>;

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateGetStatsAPIResponse = exports.validatePushAPIResponse = exports.validateAPIResponse = exports.validatePayloads = exports.validatePushSettings = exports.validateQueueKey = void 0;
exports.validateGetStatsAPIResponse = exports.validatePushAPIResponse = exports.validateAPIResponse = exports.validateGetStatsPayload = exports.validatePushPayloads = exports.validatePushSettings = exports.validateQueueKey = void 0;
const errors_1 = require("./errors");

@@ -18,3 +18,3 @@ const VALID_QUEUE_NAME_PATTERN = /^[a-zA-Z0-9-_]+$/;

};
exports.validatePayloads = (payloads) => {
exports.validatePushPayloads = (payloads) => {
if (!payloads || (Array.isArray(payloads) && payloads.length === 0)) {

@@ -31,2 +31,8 @@ throw new errors_1.NoEventsToPushError(`No events pushed`);

};
exports.validateGetStatsPayload = (getStatsRequest) => {
if (!getStatsRequest.jobId) {
throw new errors_1.JobDoesNotExistError(`jobId must not be empty`);
}
exports.validateQueueKey(getStatsRequest.queueName);
};
exports.validateAPIResponse = async (response, expectedSuccessStatus) => {

@@ -37,5 +43,13 @@ if (response.status === 429) {

if (response.status != expectedSuccessStatus && response.status) {
const responseBody = await response.json();
const errorMessage = responseBody.message ? `: ${responseBody.message}` : '';
throw new errors_1.InternalServerError(`${response.status} ${response.statusText}${errorMessage}`, responseBody.code, responseBody.details);
let internalServerError;
try {
const responseBody = await response.json();
const errorMessage = responseBody.message ? `: ${responseBody.message}` : '';
const errors = responseBody.errors ? `: ${responseBody.errors.join(', ')}` : '';
internalServerError = new errors_1.InternalServerError(`${response.status} ${response.statusText}${errorMessage}${errors}`, responseBody.code, responseBody.details);
}
catch (ignore) {
internalServerError = new errors_1.InternalServerError(`${response.status} ${response.statusText}`, response.status);
}
throw internalServerError;
}

@@ -42,0 +56,0 @@ };

{
"name": "@forge/events",
"version": "0.2.0",
"version": "0.3.0-next.0",
"description": "Forge Async Event methods",

@@ -15,4 +15,8 @@ "author": "Atlassian",

"devDependencies": {
"@types/uuid": "^3.4.7",
"@types/node": "^12.12.63"
},
"dependencies": {
"uuid": "^3.4.0"
}
}
import { getApiClientMock, verifyApiClientCalledWith } from './utils';
import { InternalServerError, JobDoesNotExistError, RateLimitError } from '../errors';
import { InternalServerError, InvalidQueueNameError, JobDoesNotExistError, RateLimitError } from '../errors';
import { JobProgress } from '../jobProgress';

@@ -77,2 +77,45 @@

});
it('should throw InternalServerError when WHP returns 422 response', async () => {
const apiClientMock = getApiClientMock(
{
errors: ['jobId must not be null', 'queueName must not be null']
},
422
);
const jobProgress = getJobProgress(apiClientMock, 'test-queue-name#test-job-id');
await expect(jobProgress.getStats()).rejects.toThrow(
new InternalServerError(`422 Status Text: jobId must not be null, queueName must not be null`)
);
verifyApiClientCalledWith(apiClientMock, '/webhook/queue/stats/{cloudId}/{environmentId}/{appId}/{appVersion}', {
queueName: 'test-queue-name',
jobId: 'test-job-id'
});
});
it('should throw errors when queueName or jobId is empty', async () => {
const apiClientMock = getApiClientMock(
{
success: 100,
inProgress: 50,
failed: 1
},
200
);
const jobProgress1 = getJobProgress(apiClientMock, 'test-queue-name#');
await expect(jobProgress1.getStats()).rejects.toThrow(new JobDoesNotExistError(`jobId must not be empty`));
const jobProgress2 = getJobProgress(apiClientMock, '#test-job-id');
await expect(jobProgress2.getStats()).rejects.toThrow(
new InvalidQueueNameError(
'Queue names can only contain alphanumeric characters and the dash and underscore characters.'
)
);
const jobProgress3 = getJobProgress(apiClientMock, '');
await expect(jobProgress3.getStats()).rejects.toThrow(new JobDoesNotExistError(`jobId must not be empty`));
expect(apiClientMock).toHaveBeenCalledTimes(0);
});
});

@@ -11,3 +11,3 @@ import {

} from '../errors';
import { getApiClientMock, verifyApiClientCalledPushPathWith } from './utils';
import { getApiClientMock, getApiClientMockWithoutResponseBody, verifyApiClientCalledPushPathWith } from './utils';
import { Queue } from '../queue';

@@ -210,2 +210,10 @@ import { JobProgress } from '../jobProgress';

});
it('should throw InternalServerError for error response without response body', async () => {
const apiClientMock = getApiClientMockWithoutResponseBody(504, 'Gateway Timeout');
const queue = getQueue(apiClientMock, 'name');
const payload = [1];
await expect(queue.push(1)).rejects.toThrow(new InternalServerError(`504 Gateway Timeout`, 504));
verifyApiClientCalledPushPathWith(apiClientMock, payload, 'name');
});
});

@@ -212,0 +220,0 @@

@@ -12,2 +12,10 @@ import { Payload } from '../types';

export const getApiClientMockWithoutResponseBody = (statusCode: number, statusText: string) => {
return jest.fn().mockReturnValue({
ok: statusCode === 200,
status: statusCode,
statusText: statusText
});
};
export const verifyApiClientCalledWith = (apiClientMock: jest.Mock, path: string, expectedBody: any) => {

@@ -14,0 +22,0 @@ expect(apiClientMock).toHaveBeenCalledWith(

@@ -10,4 +10,5 @@ export { Queue } from './queue';

InternalServerError,
JobDoesNotExistError
JobDoesNotExistError,
InvalidPushSettingsError
} from './errors';
export { JobProgress } from './jobProgress';
import { GET_STATS_PATH, post } from './queries';
import { validateGetStatsAPIResponse } from './validators';
import { validateGetStatsAPIResponse, validateGetStatsPayload } from './validators';
import { APIResponse, FetchMethod, GetStatsRequest } from './types';

@@ -22,2 +22,4 @@

validateGetStatsPayload(getStatsRequest);
const response = await post(GET_STATS_PATH, getStatsRequest, this.apiClient);

@@ -24,0 +26,0 @@ await validateGetStatsAPIResponse(response, getStatsRequest);

import { PUSH_PATH, post } from './queries';
import { validatePushAPIResponse, validatePayloads, validateQueueKey, validatePushSettings } from './validators';
import { validatePushAPIResponse, validatePushPayloads, validateQueueKey, validatePushSettings } from './validators';
import { FetchMethod, Payload, QueueParams, PushSettings, PushRequest } from './types';

@@ -18,3 +18,3 @@ import uuid from 'uuid/v4';

async push(payloads: Payload | Payload[], pushSettings?: PushSettings): Promise<string> {
validatePayloads(payloads);
validatePushPayloads(payloads);
const queueName = this.queueParams.key;

@@ -21,0 +21,0 @@ const jobId = uuid();

@@ -32,3 +32,3 @@ import {

export const validatePayloads = (payloads: Payload | Payload[]) => {
export const validatePushPayloads = (payloads: Payload | Payload[]) => {
if (!payloads || (Array.isArray(payloads) && payloads.length === 0)) {

@@ -48,2 +48,9 @@ throw new NoEventsToPushError(`No events pushed`);

export const validateGetStatsPayload = (getStatsRequest: GetStatsRequest) => {
if (!getStatsRequest.jobId) {
throw new JobDoesNotExistError(`jobId must not be empty`);
}
validateQueueKey(getStatsRequest.queueName);
};
export const validateAPIResponse = async (response: APIResponse, expectedSuccessStatus: number) => {

@@ -56,9 +63,19 @@ if (response.status === 429) {

//Catch all errors from server that we have not handled
const responseBody = await response.json();
const errorMessage = responseBody.message ? `: ${responseBody.message}` : '';
throw new InternalServerError(
`${response.status} ${response.statusText}${errorMessage}`,
responseBody.code,
responseBody.details
);
let internalServerError;
try {
const responseBody = await response.json();
const errorMessage = responseBody.message ? `: ${responseBody.message}` : '';
const errors = responseBody.errors ? `: ${responseBody.errors.join(', ')}` : ''; //Dropwizard returns an array of errors when request body validation failed
internalServerError = new InternalServerError(
`${response.status} ${response.statusText}${errorMessage}${errors}`,
responseBody.code,
responseBody.details
);
} catch (ignore) {
//response body is not a json
internalServerError = new InternalServerError(`${response.status} ${response.statusText}`, response.status);
}
throw internalServerError;
}

@@ -65,0 +82,0 @@ };

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc