Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,34 @@ on: [pull_request]
jobs:
build:
runs-on: ubuntu-latest
name: Test ${{ matrix.package.name }} (Node ${{ matrix.node-version }})

strategy:
fail-fast: false
matrix:
node-version: ["16", "22", "24"]
node-version: ["22", "24", "25"]
package:
- name: omnipartners
workspace: omnipartners
- name: graphql-schema
workspace: "@igloo-be-omnipartners/graphql-schema"
- name: hooks
workspace: "@igloo-be-omnipartners/hooks"
# - name: graphql-demo
# workspace: "@igloo-be-omnipartners/graphql-demo"

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- run: env
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
- name: yarn config
run: yarn config set ignore-engines true
- name: Install dependencies
run: yarn install
- run: yarn test
- name: Test ${{ matrix.package.name }}
run: yarn workspace ${{ matrix.package.workspace }} test
env:
CI: true
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"prepare": "lerna run --ignore \"*/graphql-demo\" build"
},
"engines": {
"node": ">=10"
"node": ">=22"
},
"workspaces": {
"packages": [
Expand Down
4 changes: 2 additions & 2 deletions packages/graphql-schema/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"@types/fs-extra": "^9.0.13",
"@types/graphql-type-json": "^0.3.2",
"@types/jest": "^27.0.3",
"@types/jsonwebtoken": "^8.5.6",
"@types/jsonwebtoken": "^9",
"@types/node-fetch": "^3.0.2",
"@types/validator": "^13.7.0",
"apollo-server": "^3.5.0",
Expand All @@ -55,7 +55,7 @@
"dataloader": "^2.0.0",
"date-fns": "^2.27.0",
"graphql-type-json": "^0.3",
"jsonwebtoken": "^8.5.1",
"jsonwebtoken": "^9",
"lodash": "^4.17.21",
"p-map": "^4",
"typescript-memoize": "^1.1.0",
Expand Down
3 changes: 2 additions & 1 deletion packages/omnipartners/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ module.exports = {
'^.+\\.tsx?$': 'ts-jest'
},
testRegex: '(/(__tests__|test)/.*|(\\.|/)(test|spec))\\.tsx?$',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node']
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
setupFiles: ['./jest.setup.js']
}
6 changes: 6 additions & 0 deletions packages/omnipartners/jest.setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const nodeFetch = require("node-fetch");

global.fetch = nodeFetch.default || nodeFetch;
global.Headers = nodeFetch.Headers;
global.Request = nodeFetch.Request;
global.Response = nodeFetch.Response;
10 changes: 3 additions & 7 deletions packages/omnipartners/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,15 @@
"@types/depd": "^1.1.32",
"@types/jest": "^27.0.3",
"@types/lodash": "^4.14.177",
"@types/node": "^16.11.11",
"@types/node-fetch": "^3.0.2",
"@types/node": "16.11.11",
"@types/node-fetch": "2",
"@types/qs": "^6.9.7",
"@types/request": "^2.48.7",
"@types/request-promise-native": "^1.0.18",
"@types/sinon": "^10.0.6",
"@types/url-join": "^4.0.1",
"@types/uuid": "^8.3.3",
"jest": "^27.4.3",
"nock": "^13.2.1",
"node-fetch": "2",
"sinon": "^12.0.1",
"ts-jest": "^27.0.7",
"typescript": "^4.5.2"
Expand All @@ -38,10 +37,7 @@
"depd": "^2.0.0",
"fetch-retry": "^5.0.1",
"lodash": "^4.17.21",
"node-fetch": "2",
"qs": "^6.10.1",
"request": "^2.88.2",
"request-promise-native": "^1.0.9",
"url-join": "^4.0.1",
"uuid": "^8.3.2",
"winston": "^3.3.3"
Expand Down
130 changes: 70 additions & 60 deletions packages/omnipartners/src/lib/Request.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { EventEmitter } from "events";
import reduce from "lodash/reduce";
import fetch, { FetchError } from "node-fetch"; // TODO: switch to fetch-retry
import querystring from "qs";
import { Response as RequestResponse } from "request";
import request from "request-promise-native";
import { Readable } from "stream";
import { v4 as uuid } from "uuid";
import { RequestError, RequestTimeoutError } from "./errors";
import Response, { IFetchResponse } from "./Response";
Expand Down Expand Up @@ -107,68 +105,80 @@ export default class Request extends EventEmitter {
let fetchRes: IFetchResponse;

try {
if (this.multipart) {
const requestRes = (await request(uri, {
headers: this.headers,
formData: this.body,
method: this.method,
timeout: this.timeout,
resolveWithFullResponse: true,
})) as RequestResponse;

fetchRes = {
status: requestRes.statusCode,
text: async () => requestRes.body,
headers: requestRes.headers,
ok: true,
size: 0,
statusText: requestRes.statusMessage,
timeout: this.timeout || 0,
};
const headers = { ...this.headers };
let body: BodyInit | undefined;

if (this.multipart && this.body) {
const formData = new FormData();
for (const [key, value] of Object.entries(
this.body as Record<string, unknown>,
)) {
if (value === undefined || value === null) continue;
if (typeof value === "string") {
formData.append(key, value);
} else if (value instanceof Readable) {
const chunks: Buffer[] = [];
for await (const chunk of value) {
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
}
formData.append(key, new Blob([Buffer.concat(chunks)]));
} else if (
value &&
typeof value === "object" &&
"value" in value &&
"options" in value
) {
const { value: buf, options } = value as {
value: Buffer;
options: { filename: string };
};
formData.append(key, new Blob([buf]), options.filename);
} else {
formData.append(key, String(value));
}
}
// Remove Content-Type so fetch sets it automatically with the correct boundary
delete headers["Content-Type"];
body = formData;
} else {
const tempRes = await fetch(uri, {
body: this.json ? JSON.stringify(this.body) : this.body,
headers: this.headers,
method: this.method,
timeout: this.timeout,
// TODO:
// retries: this.retries,
// retryDelay: this.retryDelay
});

const rawHeaders =
typeof tempRes.headers.values === "function"
? tempRes.headers.values()
: typeof tempRes.headers.raw === "function"
? tempRes.headers.raw()
: {};
const flatHeaders = reduce(
rawHeaders,
(res, value, name) => ({
...res,
[name]:
Array.isArray(value) && value.length === 1
? value.join("")
: value,
}),
{},
);

fetchRes = {
status: tempRes.status,
text: () => tempRes.text(),
headers: flatHeaders,
ok: tempRes.ok,
size: tempRes.size,
statusText: tempRes.statusText,
timeout: tempRes.timeout,
};
body = this.json ? JSON.stringify(this.body) : this.body;
}

const controller = new AbortController();
const timeoutId = this.timeout
? setTimeout(() => controller.abort(), this.timeout)
: undefined;

const rawRes = await fetch(uri, {
body,
headers,
method: this.method,
signal: controller.signal,
}).finally(() => {
if (timeoutId !== undefined) clearTimeout(timeoutId);
});

const flatHeaders: { [key: string]: string } = {};
rawRes.headers.forEach((value: string, name: string) => {
flatHeaders[name] = value;
});

fetchRes = {
status: rawRes.status,
text: () => rawRes.text(),
headers: flatHeaders,
ok: rawRes.ok,
statusText: rawRes.statusText,
};
} catch (e) {
this.emit("fetchError", e);
if ((e as FetchError).type === "request-timeout") {
const err = e as { name?: string; code?: string; cause?: { code?: string } };
if (err.name === "TimeoutError" || err.name === "AbortError") {
throw new RequestTimeoutError({ request: this });
} else if ((e as FetchError).code === "ECONNRESET") {
} else if (
err.code === "ECONNRESET" ||
err.cause?.code === "ECONNRESET"
) {
throw new RequestError(this);
} else {
throw e;
Expand Down
4 changes: 2 additions & 2 deletions packages/omnipartners/src/lib/Response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ export interface IFetchResponse {
text: () => Promise<string>;
headers: IncomingHttpHeaders;
ok: boolean;
size: number;
size?: number;
statusText: string;
timeout: number;
timeout?: number;
}

export default class Response {
Expand Down
3 changes: 2 additions & 1 deletion packages/omnipartners/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"module": "commonjs",
"types": ["@types/jest"],
"experimentalDecorators": true,
"resolveJsonModule": true
"resolveJsonModule": true,
"skipLibCheck": true
}
}
Loading