diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 58fd220..29ba173 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -32,15 +32,18 @@ jobs: run: yarn flow - name: Publish Test Report if: ${{ always() && matrix.node-version == '14' }} - uses: mikepenz/action-junit-report@v2 + uses: mikepenz/action-junit-report@127c778ac944abc0f48a5103964304bab7eb208b with: check_name: JUnit Annotations for Node ${{ matrix.node-version }} - report_paths: "**/coverage/junit/**/*.xml" + report_paths: '**/coverage/junit/**/*.xml' publish: if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/alpha' || github.ref == 'refs/heads/beta' needs: [tests] runs-on: ubuntu-latest + permissions: + contents: write + packages: write steps: - uses: actions/checkout@v3 - name: Use Node.js 14 @@ -55,4 +58,5 @@ jobs: run: yarn semantic-release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + NPM_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/package.json b/package.json index c983c1e..f01ac9c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { - "name": "react-relay-network-modern", + "name": "@tensor-hq/react-relay-network-modern", "version": "0.0.0-semantically-released", + "private": false, "description": "Network Layer for React Relay and Express (Batch Queries, AuthToken, Logging, Retry)", "engines": { "node": ">=14.21.3" @@ -11,9 +12,9 @@ ], "main": "lib/index.js", "types": "lib/index.d.ts", - "repository": { - "type": "git", - "url": "https://github.com/relay-tools/react-relay-network-modern.git" + "repository": "git@github.com:tensor-hq/react-relay-network-modern.git", + "publishConfig": { + "registry": "https://npm.pkg.github.com" }, "keywords": [ "relay", diff --git a/src/index.d.ts b/src/index.d.ts index c1c6826..51d62fc 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -146,6 +146,7 @@ export type BatchMiddlewareOpts = { batchUrl?: string | Promise | ((requestMap: BatchRequestMap) => string | Promise); batchTimeout?: number; maxBatchSize?: number; + maxRequestsPerBatch?: number; allowMutations?: boolean; method?: 'POST' | 'GET'; headers?: Headers | Promise | ((req: RelayRequestBatch) => Headers | Promise); diff --git a/src/middlewares/__tests__/batch-test.js b/src/middlewares/__tests__/batch-test.js index 2872e85..a4a8800 100644 --- a/src/middlewares/__tests__/batch-test.js +++ b/src/middlewares/__tests__/batch-test.js @@ -554,4 +554,56 @@ describe('middlewares/batch', () => { ); }); }); + + describe('option `maxRequestsPerBatch`', () => { + beforeEach(() => { + fetchMock.restore(); + }); + + it('should split batch requests based on max requests limit', async () => { + // Set up mocks for single and batch requests + fetchMock.mock({ + matcher: '/graphql', + response: { + status: 200, + body: { data: {} }, + }, + method: 'POST', + }); + + fetchMock.mock({ + matcher: '/graphql/batch', + response: { + status: 200, + body: [{ data: {} }, { data: {} }], + }, + method: 'POST', + }); + + // Create a network layer with maxRequestsPerBatch set to 2 + const rnl = new RelayNetworkLayer([batchMiddleware({ maxRequestsPerBatch: 2 })]); + + // Create 5 mock requests + const req1 = mockReq(1); + const req2 = mockReq(2); + const req3 = mockReq(3); + const req4 = mockReq(4); + const req5 = mockReq(5); + + // Execute all requests simultaneously + await Promise.all([ + req1.execute(rnl), + req2.execute(rnl), + req3.execute(rnl), + req4.execute(rnl), + req5.execute(rnl), + ]); + + // Check if the requests were properly split into batches + const batchReqs = fetchMock.calls('/graphql/batch'); + const singleReqs = fetchMock.calls('/graphql'); + expect(batchReqs).toHaveLength(2); + expect(singleReqs).toHaveLength(1); + }); + }); }); diff --git a/src/middlewares/batch.js b/src/middlewares/batch.js index 1367ef5..19a66d3 100644 --- a/src/middlewares/batch.js +++ b/src/middlewares/batch.js @@ -20,6 +20,7 @@ export type BatchMiddlewareOpts = {| | ((requestList: RequestWrapper[]) => string | Promise), batchTimeout?: number, maxBatchSize?: number, + maxRequestsPerBatch?: number, allowMutations?: boolean, method?: 'POST' | 'GET', headers?: Headers | Promise | ((req: RelayRequestBatch) => Headers | Promise), @@ -57,6 +58,7 @@ export default function batchMiddleware(options?: BatchMiddlewareOpts): Middlewa const allowMutations = opts.allowMutations || false; const batchUrl = opts.batchUrl || '/graphql/batch'; const maxBatchSize = opts.maxBatchSize || DEFAULT_BATCH_SIZE; + const maxRequestsPerBatch = opts.maxRequestsPerBatch || 0; // 0 is the same as no limit const singleton = {}; const fetchOpts = {}; @@ -94,6 +96,7 @@ export default function batchMiddleware(options?: BatchMiddlewareOpts): Middlewa batchUrl, singleton, maxBatchSize, + maxRequestsPerBatch, fetchOpts, }); }; @@ -111,6 +114,13 @@ function passThroughBatch(req: RelayRequest, next, opts) { singleton.batcher = prepareNewBatcher(next, opts); } + if ( + opts.maxRequestsPerBatch && + singleton.batcher.requestList.length + 1 > opts.maxRequestsPerBatch + ) { + singleton.batcher = prepareNewBatcher(next, opts); + } + if (singleton.batcher.bodySize + bodyLength + 1 > opts.maxBatchSize) { singleton.batcher = prepareNewBatcher(next, opts); }