From 6bf9c7fb1f7fa3957be34d95a6820e1ee07ce792 Mon Sep 17 00:00:00 2001 From: ZaidMaslouhi Date: Fri, 6 Dec 2024 18:23:17 +0100 Subject: [PATCH 01/15] build: :green_heart: uncomment env docker compose file --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b49f9409..c65b7b46 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,8 +20,8 @@ jobs: # The type of runner that the job will run on runs-on: ubuntu-latest container: node:20 - # env: - # COMPOSE_FILE: ./docker-compose.yml + env: + COMPOSE_FILE: ./docker-compose.yml # Steps represent a sequence of tasks that will be executed as part of the job steps: From 4f9b078c53b141c0b34e8eebcd84d8de2fa0e629 Mon Sep 17 00:00:00 2001 From: ZaidMaslouhi Date: Fri, 6 Dec 2024 18:24:22 +0100 Subject: [PATCH 02/15] fix: :pencil2: change joinCondition delimiter to '&' symbol --- packages/crud-request/src/request-query.builder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/crud-request/src/request-query.builder.ts b/packages/crud-request/src/request-query.builder.ts index f53b0bf9..a2f8e007 100644 --- a/packages/crud-request/src/request-query.builder.ts +++ b/packages/crud-request/src/request-query.builder.ts @@ -60,7 +60,7 @@ export class RequestQueryBuilder { } = {}; private joinConditionString: IStringifyOptions = { encode: false, - delimiter: this.options.delimStr, + delimiter: '&', arrayFormat: 'indices', }; public queryObject: { [key: string]: any } = {}; From 42c583b91e879fd63f06974e7ad7bbb19336c150 Mon Sep 17 00:00:00 2001 From: ZaidMaslouhi Date: Fri, 6 Dec 2024 18:25:27 +0100 Subject: [PATCH 03/15] test: :test_tube: add tests for join condition value contains array of values --- .../test/request-query.builder.spec.ts | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/packages/crud-request/test/request-query.builder.spec.ts b/packages/crud-request/test/request-query.builder.spec.ts index c619df96..fe6dcd94 100644 --- a/packages/crud-request/test/request-query.builder.spec.ts +++ b/packages/crud-request/test/request-query.builder.spec.ts @@ -223,7 +223,24 @@ describe('#request-query', () => { ], }, ]); - const expected = ['baz||a,b,c||on[0]=bar||eq||100,on[1]=foo||isnull']; + const expected = ['baz||a,b,c||on[0]=bar||eq||100&on[1]=foo||isnull']; + expect(qb.queryObject.join).toIncludeSameMembers(expected); + }); + it('should set join, 9', () => { + qb.setJoin([ + { + field: 'qux', + select: ['a', 'b', 'c'], + on: [ + { field: 'bar', operator: 'eq', value: 100 }, + { field: 'foo', operator: 'isnull' }, + { field: 'date', operator: 'between', value: ['2023-12-06', '2023-12-12'] }, + ], + }, + ]); + const expected = [ + 'qux||a,b,c||on[0]=bar||eq||100&on[1]=foo||isnull&on[2]=date||between||2023-12-06,2023-12-12', + ]; expect(qb.queryObject.join).toIncludeSameMembers(expected); }); }); @@ -384,6 +401,7 @@ describe('#request-query', () => { on: [ { field: 'foo', operator: 'eq', value: 'baz' }, { field: 'bar', operator: 'isnull' }, + { field: 'qux', operator: 'between', value: [1000, 8000] }, ], }) .setLimit(1) @@ -394,7 +412,7 @@ describe('#request-query', () => { .setIncludeDeleted(1) .query(false); const expected = - 'fields=foo,bar&filter[0]=is||notnull&or[0]=ok||ne||false&join[0]=voo||h,data||on[0]=foo||eq||baz,on[1]=bar||isnull&limit=1&offset=2&page=3&sort[0]=foo,DESC&cache=0&include_deleted=1'; + 'fields=foo,bar&filter[0]=is||notnull&or[0]=ok||ne||false&join[0]=voo||h,data||on[0]=foo||eq||baz&on[1]=bar||isnull&on[2]=qux||between||1000,8000&limit=1&offset=2&page=3&sort[0]=foo,DESC&cache=0&include_deleted=1'; expect(test).toBe(expected); }); }); @@ -437,6 +455,7 @@ describe('#request-query', () => { on: [ { field: 'foo', operator: 'eq', value: 'baz' }, { field: 'bar', operator: 'isnull' }, + { field: 'qux', operator: 'between', value: [1000, 8000] }, ], }, limit: 1, @@ -446,7 +465,7 @@ describe('#request-query', () => { resetCache: true, }).query(false); const expected = - 'fields=foo,bar&filter[0]=is||notnull&or[0]=ok||ne||false&join[0]=voo||h,data||on[0]=foo||eq||baz,on[1]=bar||isnull&limit=1&offset=2&page=3&sort[0]=foo,DESC&cache=0'; + 'fields=foo,bar&filter[0]=is||notnull&or[0]=ok||ne||false&join[0]=voo||h,data||on[0]=foo||eq||baz&on[1]=bar||isnull&on[2]=qux||between||1000,8000&limit=1&offset=2&page=3&sort[0]=foo,DESC&cache=0'; expect(test).toBe(expected); }); it('should return a valid query string, 2', () => { From d7b003de149003138fd838045b28aa677aa2623f Mon Sep 17 00:00:00 2001 From: ZaidMaslouhi Date: Fri, 6 Dec 2024 18:28:38 +0100 Subject: [PATCH 04/15] fix: :bug: remove query string parser options --- packages/crud-request/src/request-query.parser.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/crud-request/src/request-query.parser.ts b/packages/crud-request/src/request-query.parser.ts index 20f474eb..3b242f14 100644 --- a/packages/crud-request/src/request-query.parser.ts +++ b/packages/crud-request/src/request-query.parser.ts @@ -356,14 +356,14 @@ export class RequestQueryParser implements ParsedRequestParams { } private parseJoinConditions(conditionsString: string): QueryFilter[] { - const conditions: string[] = parse(conditionsString, this._joinConditionParseOptions)[ - 'on' - ]; + const conditions: string[] = parse(conditionsString)['on']; + return conditions.map((cond: string) => this.conditionParser('filter', {}, cond)); } private joinParser(data: string): QueryJoin { const param = data.split(this._options.delim); + const field = param[0]; const selectString = param[1]; const conditions = param.slice(2).join(this._options.delim); From 85c7ab1590ef7046cf6a3c3524b15945bfc857f8 Mon Sep 17 00:00:00 2001 From: ZaidMaslouhi Date: Fri, 6 Dec 2024 18:29:27 +0100 Subject: [PATCH 05/15] test: :test_tube: add tests for join condition value contains array of values --- .../test/request-query.parser.spec.ts | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/packages/crud-request/test/request-query.parser.spec.ts b/packages/crud-request/test/request-query.parser.spec.ts index 8f20f649..0df02b3d 100644 --- a/packages/crud-request/test/request-query.parser.spec.ts +++ b/packages/crud-request/test/request-query.parser.spec.ts @@ -264,7 +264,7 @@ describe('#request-query', () => { join: [ 'foo', 'bar||baz,boo', - 'bar||baz,boo||on[0]=name||eq||jhon,on[1]=foo||isnull', + 'bar||baz,boo||on[0]=name||eq||jhon&on[1]=foo||isnull', ], }; const expected: QueryJoin[] = [ @@ -285,6 +285,45 @@ describe('#request-query', () => { expect(test.join[1]).toMatchObject(expected[1]); expect(test.join[2]).toMatchObject(expected[2]); }); + it('should set array, 4', () => { + const query = { + join: [ + 'foo', + 'bar||baz,boo', + 'bar||baz,boo||on[0]=name||eq||jhon&on[1]=foo||isnull', + 'qux||qubaz,quboo||on[0]=quux||between||06-12-2023,12-12-2023', + ], + }; + const expected: QueryJoin[] = [ + { field: 'foo' }, + { field: 'bar', select: ['baz', 'boo'] }, + { + field: 'bar', + select: ['baz', 'boo'], + on: [ + { field: 'name', operator: 'eq', value: 'jhon' }, + { field: 'foo', operator: 'isnull', value: '' }, + ], + }, + { + field: 'qux', + select: ['qubaz', 'quboo'], + on: [ + { + field: 'quux', + operator: 'between', + value: ['06-12-2023', '12-12-2023'], + }, + ], + }, + ]; + const test = qp.parseQuery(query); + + expect(test.join[0]).toMatchObject(expected[0]); + expect(test.join[1]).toMatchObject(expected[1]); + expect(test.join[2]).toMatchObject(expected[2]); + expect(test.join[3]).toMatchObject(expected[3]); + }); }); describe('#parse sort', () => { From 536a2221d1bf5443e688142756fa07316a6bd3dc Mon Sep 17 00:00:00 2001 From: ZaidMaslouhi Date: Fri, 6 Dec 2024 18:32:04 +0100 Subject: [PATCH 06/15] docs: :memo: update doc to new delimiter for join condition 'on clause' --- docs/requests.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requests.md b/docs/requests.md index 23038092..3e33cbe9 100644 --- a/docs/requests.md +++ b/docs/requests.md @@ -222,7 +222,7 @@ _Examples:_ The join parameter now supports specifying a WHERE condition within the ON clause of the join using the on property. This allows for more granular control over the joined data. -> ?join[]=**relation**||**field1**,**field2**,...||on[0]=**field**||**\$condition**||**value**,on[1]=**field**||**\$condition**...&join[]=... +> ?join[]=**relation**||**field1**,**field2**,...||on[0]=**field**||**\$condition**||**value**&on[1]=**field**||**\$condition**...&join[]=... _Examples:_ From baea0cfd89f43f0d6eb28116d69e4bc6abe818df Mon Sep 17 00:00:00 2001 From: ZaidMaslouhi Date: Fri, 6 Dec 2024 18:32:59 +0100 Subject: [PATCH 07/15] docs: :memo: add example for join condition contains array of values --- docs/requests.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/requests.md b/docs/requests.md index 3e33cbe9..37b26f58 100644 --- a/docs/requests.md +++ b/docs/requests.md @@ -356,6 +356,7 @@ qb.select(['foo', 'bar']) on: [ { field: 'bar', operator: 'eq', value: 100 }, { field: 'baz', operator: 'isnull' }, + { field: 'date', operator: 'between', value: ['2023-01-01', '2023-12-31'] }, ], }) .sortBy({ field: 'bar', order: 'DESС' }) From f6b3c553e6df808c02c977ec11ecaf81d41c41fd Mon Sep 17 00:00:00 2001 From: ZaidMaslouhi Date: Wed, 11 Dec 2024 17:09:29 +0100 Subject: [PATCH 08/15] ci: :green_heart: fix CI docker build error --- .github/workflows/main.yml | 50 +++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c65b7b46..7dadd4b0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,36 +15,48 @@ on: # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: - # This workflow contains a single job called "build" test: # The type of runner that the job will run on - runs-on: ubuntu-latest - container: node:20 - env: - COMPOSE_FILE: ./docker-compose.yml - - # Steps represent a sequence of tasks that will be executed as part of the job + runs-on: + ubuntu-latest + # Steps represent a sequence of tasks that will be executed as part of the job steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 + # Checkout the repository + - uses: actions/checkout@v4 - # Runs a single command using the runners shell - - name: build docker db - run: docker compose up -d + # Install Docker + - name: Set up Docker + uses: docker/setup-docker@v3 + + # Install Node.js + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' - - name: install + # Install Docker Compose + - name: Install Docker Compose + run: | + sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + sudo chmod +x /usr/local/bin/docker-compose + + # Build and start Docker containers + - name: Build docker db + run: docker-compose up -d + + # Install dependencies + - name: Install dependencies run: yarn install - - name: build + # Build the project + - name: Build run: yarn build - - name: check docker - run: docker compose up -d - - # Runs a set of commands using the runners shell - - name: tests + # Run tests + - name: Run tests run: yarn test:coverage + # Publish coverage - name: Coveralls Parallel uses: coverallsapp/github-action@master with: From f4a4710cb932c6daaa4697c03becfc7c4ba511e6 Mon Sep 17 00:00:00 2001 From: ZaidMaslouhi Date: Wed, 11 Dec 2024 17:20:25 +0100 Subject: [PATCH 09/15] ci: :rewind: revert changes --- .github/workflows/main.yml | 58 +++++++++++--------------------------- 1 file changed, 17 insertions(+), 41 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7dadd4b0..e477bbdd 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,7 +1,5 @@ # This is a basic workflow to help you get started with Actions - name: Tests - # Controls when the action will run. on: # Triggers the workflow on push or pull request events but only for the master branch @@ -9,61 +7,39 @@ on: branches: [master] pull_request: branches: [master] - # Allows you to run this workflow manually from the Actions tab workflow_dispatch: - # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: + # This workflow contains a single job called "build" test: # The type of runner that the job will run on - runs-on: - ubuntu-latest - # Steps represent a sequence of tasks that will be executed as part of the job + runs-on: ubuntu-latest + container: node:20 + # env: + # COMPOSE_FILE: ./docker-compose.yml + # Steps represent a sequence of tasks that will be executed as part of the job steps: - # Checkout the repository - - uses: actions/checkout@v4 - - # Install Docker - - name: Set up Docker - uses: docker/setup-docker@v3 - - # Install Node.js - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' - - # Install Docker Compose - - name: Install Docker Compose - run: | - sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose - sudo chmod +x /usr/local/bin/docker-compose - - # Build and start Docker containers - - name: Build docker db - run: docker-compose up -d - - # Install dependencies - - name: Install dependencies + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v2 + # Runs a single command using the runners shell + - name: build docker db + run: docker compose up -d + - name: install run: yarn install - - # Build the project - - name: Build + - name: build run: yarn build - - # Run tests - - name: Run tests + - name: check docker + run: docker compose up -d + # Runs a set of commands using the runners shell + - name: tests run: yarn test:coverage - - # Publish coverage - name: Coveralls Parallel uses: coverallsapp/github-action@master with: github-token: ${{ secrets.github_token }} flag-name: run-${{ matrix.test_number }} parallel: true - coverage: needs: test runs-on: ubuntu-latest From 63e6278c1d9e51d246e2d351b14b965590d9de22 Mon Sep 17 00:00:00 2001 From: ZaidMaslouhi Date: Wed, 11 Dec 2024 17:22:56 +0100 Subject: [PATCH 10/15] ci: :construction_worker: add docker compose file --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e477bbdd..eef23ef5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -16,8 +16,8 @@ jobs: # The type of runner that the job will run on runs-on: ubuntu-latest container: node:20 - # env: - # COMPOSE_FILE: ./docker-compose.yml + env: + COMPOSE_FILE: ./docker-compose.yml # Steps represent a sequence of tasks that will be executed as part of the job steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it From f3e4411bfa55bbbe81a8fe24fe368b8ca12867fc Mon Sep 17 00:00:00 2001 From: ZaidMaslouhi Date: Sat, 21 Dec 2024 23:11:51 +0100 Subject: [PATCH 11/15] feat: :label: add new array delimiter --- .../src/interfaces/request-query-builder-options.interface.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/crud-request/src/interfaces/request-query-builder-options.interface.ts b/packages/crud-request/src/interfaces/request-query-builder-options.interface.ts index c122f65e..527b971a 100644 --- a/packages/crud-request/src/interfaces/request-query-builder-options.interface.ts +++ b/packages/crud-request/src/interfaces/request-query-builder-options.interface.ts @@ -1,6 +1,7 @@ export interface RequestQueryBuilderOptions { delim?: string; delimStr?: string; + delimArr?: string; paramNamesMap?: { fields?: string | string[]; search?: string | string[]; From d7d7cba1dfcfa0de842e908527158a16f7ad1b6a Mon Sep 17 00:00:00 2001 From: ZaidMaslouhi Date: Sat, 21 Dec 2024 23:13:08 +0100 Subject: [PATCH 12/15] feat: :sparkles: add array delimiter from query builder options --- packages/crud/src/crud/swagger.helper.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/crud/src/crud/swagger.helper.ts b/packages/crud/src/crud/swagger.helper.ts index f084a629..820e5742 100644 --- a/packages/crud/src/crud/swagger.helper.ts +++ b/packages/crud/src/crud/swagger.helper.ts @@ -289,6 +289,7 @@ export class Swagger { const { delim: d, delimStr: coma, + delimArr: amp, fields, search, filter, @@ -578,6 +579,7 @@ export class Swagger { return { delim: qbOptions.delim, delimStr: qbOptions.delimStr, + delimArr: qbOptions.delimArr, fields: name('fields'), search: name('search'), filter: name('filter'), From b49c34270b796a55933add6b95e592db3b95a6f2 Mon Sep 17 00:00:00 2001 From: ZaidMaslouhi Date: Sat, 21 Dec 2024 23:13:59 +0100 Subject: [PATCH 13/15] docs: :memo: add array delimiter to the request query builder options --- docs/requests.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/requests.md b/docs/requests.md index 37b26f58..25edade0 100644 --- a/docs/requests.md +++ b/docs/requests.md @@ -295,6 +295,7 @@ import { RequestQueryBuilder } from '@dataui/crud-request'; RequestQueryBuilder.setOptions({ delim: '||', delimStr: ',', + delimArr: '&', paramNamesMap: { fields: ['fields', 'select'], search: 's', From 4acba980b74b925c44542c1c3006134d7ca271ff Mon Sep 17 00:00:00 2001 From: ZaidMaslouhi Date: Sat, 21 Dec 2024 23:15:45 +0100 Subject: [PATCH 14/15] feat: :sparkles: get array delimiter from config options to stringify join conditions array --- .../crud-request/src/request-query.builder.ts | 15 ++++++++------- packages/crud-request/src/request-query.parser.ts | 4 ---- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/packages/crud-request/src/request-query.builder.ts b/packages/crud-request/src/request-query.builder.ts index a2f8e007..0b405e61 100644 --- a/packages/crud-request/src/request-query.builder.ts +++ b/packages/crud-request/src/request-query.builder.ts @@ -40,6 +40,7 @@ export class RequestQueryBuilder { private static _options: RequestQueryBuilderOptions = { delim: '||', delimStr: ',', + delimArr: '&', paramNamesMap: { fields: ['fields', 'select'], search: 's', @@ -58,11 +59,6 @@ export class RequestQueryBuilder { private paramNames: { [key in keyof RequestQueryBuilderOptions['paramNamesMap']]: string; } = {}; - private joinConditionString: IStringifyOptions = { - encode: false, - delimiter: '&', - arrayFormat: 'indices', - }; public queryObject: { [key: string]: any } = {}; public queryString: string; @@ -213,7 +209,12 @@ export class RequestQueryBuilder { } private addJoin(join: QueryJoin | QueryJoinArr): string { - const { delim, delimStr } = this.options; + const { delim, delimStr, delimArr } = this.options; + const conditionStringifyOptions: IStringifyOptions = { + encode: false, + delimiter: delimArr, + arrayFormat: 'indices', + }; const normalizedJoin = Array.isArray(join) ? { field: join[0], select: join[1], on: join[2] } @@ -230,7 +231,7 @@ export class RequestQueryBuilder { ? delim + normalizedJoin.select.join(delimStr) : ''; const conditionsPart = conditions - ? delim + stringify(conditions, this.joinConditionString) + ? delim + stringify(conditions, conditionStringifyOptions) : ''; return fieldPart + selectPart + conditionsPart; diff --git a/packages/crud-request/src/request-query.parser.ts b/packages/crud-request/src/request-query.parser.ts index 3b242f14..eab585d3 100644 --- a/packages/crud-request/src/request-query.parser.ts +++ b/packages/crud-request/src/request-query.parser.ts @@ -67,10 +67,6 @@ export class RequestQueryParser implements ParsedRequestParams { private _paramNames: string[]; private _paramsOptions: ParamsOptions; - private _joinConditionParseOptions: IParseOptions = { - delimiter: this._options.delimStr, - }; - private get _options(): RequestQueryBuilderOptions { return RequestQueryBuilder.getOptions(); } From 8e11ab2688138365098719024a1d5f039ff52e32 Mon Sep 17 00:00:00 2001 From: ZaidMaslouhi Date: Sat, 21 Dec 2024 23:16:26 +0100 Subject: [PATCH 15/15] test: :white_check_mark: add tests for the new array delimiter option --- packages/crud-request/test/request-query.builder.spec.ts | 6 ++++++ packages/crud/test/crud-config.service.spec.ts | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/crud-request/test/request-query.builder.spec.ts b/packages/crud-request/test/request-query.builder.spec.ts index fe6dcd94..e9693894 100644 --- a/packages/crud-request/test/request-query.builder.spec.ts +++ b/packages/crud-request/test/request-query.builder.spec.ts @@ -36,6 +36,12 @@ describe('#request-query', () => { const _options = (RequestQueryBuilder as any)._options; expect(_options.delim).toBe(override); }); + it('should merge options, 3', () => { + const override = '#'; + RequestQueryBuilder.setOptions({ delimArr: override }); + const _options = (RequestQueryBuilder as any)._options; + expect(_options.delimArr).toBe(override); + }); }); describe('#select', () => { diff --git a/packages/crud/test/crud-config.service.spec.ts b/packages/crud/test/crud-config.service.spec.ts index d2cd7b48..7254d6e8 100644 --- a/packages/crud/test/crud-config.service.spec.ts +++ b/packages/crud/test/crud-config.service.spec.ts @@ -26,13 +26,14 @@ describe('#crud', () => { const conf: CrudGlobalConfig = { queryParser: { delim: '__', + delimArr: '§', }, }; const expected = { ...CrudConfigService.config }; CrudConfigService.load(conf); expect(CrudConfigService.config).toEqual(expect.objectContaining(expected)); expect(RequestQueryBuilder.getOptions()).toEqual( - expect.objectContaining({ ...requestOptions, delim: '__' }), + expect.objectContaining({ ...requestOptions, delim: '__', delimArr: '§' }), ); }); it('should set query, routes, params', () => {