Skip to content

Commit 561b212

Browse files
committed
client: add getPageNumberFromLinkHeader and parseLinkHeader to common
1 parent 8c4124c commit 561b212

File tree

8 files changed

+151
-66
lines changed

8 files changed

+151
-66
lines changed

generators/angular/__snapshots__/generator.spec.ts.snap

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,9 @@ exports[`generator - angular gateway-jwt-skipUserManagement(true)-withAdminUi(fa
812812
"clientRoot/src/app/shared/jhipster/headers.ts": {
813813
"stateCleared": "modified",
814814
},
815+
"clientRoot/src/app/shared/jhipster/link-header.ts": {
816+
"stateCleared": "modified",
817+
},
815818
"clientRoot/src/app/shared/jhipster/problem-details.ts": {
816819
"stateCleared": "modified",
817820
},
@@ -2045,6 +2048,9 @@ exports[`generator - angular gateway-oauth2-withAdminUi(true)-skipJhipsterDepend
20452048
"clientRoot/src/app/shared/jhipster/headers.ts": {
20462049
"stateCleared": "modified",
20472050
},
2051+
"clientRoot/src/app/shared/jhipster/link-header.ts": {
2052+
"stateCleared": "modified",
2053+
},
20482054
"clientRoot/src/app/shared/jhipster/problem-details.ts": {
20492055
"stateCleared": "modified",
20502056
},
@@ -3056,6 +3062,9 @@ exports[`generator - angular microservice-jwt-skipUserManagement(false)-withAdmi
30563062
"clientRoot/src/app/shared/jhipster/headers.ts": {
30573063
"stateCleared": "modified",
30583064
},
3065+
"clientRoot/src/app/shared/jhipster/link-header.ts": {
3066+
"stateCleared": "modified",
3067+
},
30593068
"clientRoot/src/app/shared/jhipster/problem-details.ts": {
30603069
"stateCleared": "modified",
30613070
},
@@ -4073,6 +4082,9 @@ exports[`generator - angular microservice-oauth2-withAdminUi(true)-skipJhipsterD
40734082
"src/main/webapp/app/shared/jhipster/headers.ts": {
40744083
"stateCleared": "modified",
40754084
},
4085+
"src/main/webapp/app/shared/jhipster/link-header.ts": {
4086+
"stateCleared": "modified",
4087+
},
40764088
"src/main/webapp/app/shared/jhipster/problem-details.ts": {
40774089
"stateCleared": "modified",
40784090
},
@@ -5525,6 +5537,9 @@ exports[`generator - angular monolith-jwt-skipUserManagement(false)-withAdminUi(
55255537
"src/main/webapp/app/shared/jhipster/headers.ts": {
55265538
"stateCleared": "modified",
55275539
},
5540+
"src/main/webapp/app/shared/jhipster/link-header.ts": {
5541+
"stateCleared": "modified",
5542+
},
55285543
"src/main/webapp/app/shared/jhipster/problem-details.ts": {
55295544
"stateCleared": "modified",
55305545
},
@@ -6536,6 +6551,9 @@ exports[`generator - angular monolith-oauth2-withAdminUi(false)-skipJhipsterDepe
65366551
"src/main/webapp/app/shared/jhipster/headers.ts": {
65376552
"stateCleared": "modified",
65386553
},
6554+
"src/main/webapp/app/shared/jhipster/link-header.ts": {
6555+
"stateCleared": "modified",
6556+
},
65396557
"src/main/webapp/app/shared/jhipster/problem-details.ts": {
65406558
"stateCleared": "modified",
65416559
},
@@ -7534,6 +7552,9 @@ exports[`generator - angular monolith-session-skipUserManagement(true)-withAdmin
75347552
"src/main/webapp/app/shared/jhipster/headers.ts": {
75357553
"stateCleared": "modified",
75367554
},
7555+
"src/main/webapp/app/shared/jhipster/link-header.ts": {
7556+
"stateCleared": "modified",
7557+
},
75377558
"src/main/webapp/app/shared/jhipster/problem-details.ts": {
75387559
"stateCleared": "modified",
75397560
},

generators/angular/templates/src/main/webapp/app/core/util/parse-links.service.ts.ejs

Lines changed: 3 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
limitations under the License.
1818
-%>
1919
import { Injectable } from '@angular/core';
20+
import { getPageNumberFromLinkHeader, parseLinkHeader } from 'app/shared/jhipster/parse-link-header';
2021

2122
/**
2223
* A utility service for link parsing.
@@ -29,49 +30,13 @@ export class ParseLinks {
2930
* Method to parse the links
3031
*/
3132
parseAll(header: string): Record<string, Record<string, string | undefined> | undefined> {
32-
if (header.length === 0) {
33-
throw new Error('input must not be of zero length');
34-
}
35-
36-
// Split parts by comma
37-
const parts: string[] = header.split(',');
38-
39-
// Parse each part into a named link
40-
return Object.fromEntries(
41-
parts.map(p => {
42-
const section: string[] = p.split(';');
43-
44-
if (section.length !== 2) {
45-
throw new Error('section could not be split on ";"');
46-
}
47-
48-
const url: string = section[0].replace(/<(.*)>/, '$1').trim(); // NOSONAR
49-
const queryString: Record<string, string> = {};
50-
51-
url.replaceAll(/([^?=&]+)(=([^&]*))?/g, (_$0: string, $1: string | undefined, _$2: string | undefined, $3: string | undefined) => {
52-
if ($1 !== undefined && $3 !== undefined) {
53-
queryString[$1] = decodeURIComponent($3);
54-
}
55-
return $3 ?? '';
56-
});
57-
58-
const name: string = section[1].replace(/rel="(.*)"/, '$1').trim();
59-
return [name, queryString];
60-
}),
61-
);
33+
return parseLinkHeader(header);
6234
}
6335

6436
/**
6537
* Method to parse the links
6638
*/
6739
parse(header: string): Record<string, number> {
68-
const sections = this.parseAll(header);
69-
const links: Record<string, number> = {};
70-
for (const [name, queryParams] of Object.entries(sections)) {
71-
if (queryParams?.page !== undefined) {
72-
links[name] = Number.parseInt(queryParams.page, 10);
73-
}
74-
}
75-
return links;
40+
return getPageNumberFromLinkHeader(header);
7641
}
7742
}

generators/client/generators/common/__snapshots__/generator.spec.ts.snap

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ exports[`generator - client:common with defaults options should match files snap
1313
"src/main/webapp/app/shared/jhipster/headers.ts": {
1414
"stateCleared": "modified",
1515
},
16+
"src/main/webapp/app/shared/jhipster/link-header.ts": {
17+
"stateCleared": "modified",
18+
},
1619
"src/main/webapp/app/shared/jhipster/problem-details.ts": {
1720
"stateCleared": "modified",
1821
},

generators/client/generators/common/generator.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,12 @@ export default class CommonGenerator extends BaseApplicationGenerator {
3636
await this.writeFiles({
3737
blocks: [
3838
clientApplicationTemplatesBlock({
39-
templates: ['shared/jhipster/constants.ts', 'shared/jhipster/problem-details.ts', 'shared/jhipster/headers.ts'],
39+
templates: [
40+
'shared/jhipster/constants.ts',
41+
'shared/jhipster/link-header.ts',
42+
'shared/jhipster/problem-details.ts',
43+
'shared/jhipster/headers.ts',
44+
],
4045
}),
4146
],
4247
context: application,
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<%#
2+
Copyright 2013-2026 the original author or authors from the JHipster project.
3+
4+
This file is part of the JHipster project, see https://www.jhipster.tech/
5+
for more information.
6+
7+
Licensed under the Apache License, Version 2.0 (the "License");
8+
you may not use this file except in compliance with the License.
9+
You may obtain a copy of the License at
10+
11+
https://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing, software
14+
distributed under the License is distributed on an "AS IS" BASIS,
15+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
See the License for the specific language governing permissions and
17+
limitations under the License.
18+
-%>
19+
/**
20+
* Method to parse the link header into an object
21+
* @example
22+
* link: </api/audits?page=0&size=20>; rel="last",</api/audits?page=0&size=20>; rel="first"
23+
* will return { last: { page: '0', size: '20' }, first: { page: '0', size: '20' } }
24+
*/
25+
export function parseLinkHeader(header: string): Record<string, Record<string, string | undefined> | undefined> {
26+
if (header.length === 0) {
27+
throw new Error('input must not be of zero length');
28+
}
29+
30+
// Split parts by comma
31+
const parts: string[] = header.split(',');
32+
33+
// Parse each part into a named link
34+
return Object.fromEntries(
35+
parts.map(p => {
36+
const section: string[] = p.split(';');
37+
38+
if (section.length !== 2) {
39+
throw new Error('section could not be split on ";"');
40+
}
41+
42+
const url: string = section[0].replace(/<(.*)>/, '$1').trim(); // NOSONAR
43+
const queryString: Record<string, string> = {};
44+
45+
url.replaceAll(/([^?=&]+)(=([^&]*))?/g, (_$0: string, $1: string | undefined, _$2: string | undefined, $3: string | undefined) => {
46+
if ($1 !== undefined && $3 !== undefined) {
47+
queryString[$1] = decodeURIComponent($3);
48+
}
49+
return $3 ?? '';
50+
});
51+
52+
const name: string = section[1].replace(/rel="(.*)"/, '$1').trim();
53+
return [name, queryString];
54+
}),
55+
);
56+
}
57+
58+
/**
59+
* Method to parse the link header
60+
* @example
61+
* link: </api/audits?page=0&size=20>; rel="last",</api/audits?page=0&size=20>; rel="first"
62+
* will return { last: 0, first: 0 }
63+
*/
64+
export function getPageNumberFromLinkHeader(header: string): Record<string, number> {
65+
const sections = parseLinkHeader(header);
66+
const pages: Record<string, number> = {};
67+
for (const [name, queryParams] of Object.entries(sections)) {
68+
if (queryParams?.page !== undefined) {
69+
pages[name] = Number.parseInt(queryParams.page, 10);
70+
}
71+
}
72+
return pages;
73+
}

generators/react/__snapshots__/generator.spec.ts.snap

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,9 @@ exports[`generator - react gateway-jwt-skipUserManagement(true)-withAdminUi(fals
508508
"clientRoot/src/app/shared/jhipster/headers.ts": {
509509
"stateCleared": "modified",
510510
},
511+
"clientRoot/src/app/shared/jhipster/link-header.ts": {
512+
"stateCleared": "modified",
513+
},
511514
"clientRoot/src/app/shared/jhipster/problem-details.ts": {
512515
"stateCleared": "modified",
513516
},
@@ -1286,6 +1289,9 @@ exports[`generator - react gateway-oauth2-withAdminUi(true)-skipJhipsterDependen
12861289
"clientRoot/src/app/shared/jhipster/headers.ts": {
12871290
"stateCleared": "modified",
12881291
},
1292+
"clientRoot/src/app/shared/jhipster/link-header.ts": {
1293+
"stateCleared": "modified",
1294+
},
12891295
"clientRoot/src/app/shared/jhipster/problem-details.ts": {
12901296
"stateCleared": "modified",
12911297
},
@@ -2066,6 +2072,9 @@ exports[`generator - react microservice-jwt-skipUserManagement(false)-withAdminU
20662072
"clientRoot/src/app/shared/jhipster/headers.ts": {
20672073
"stateCleared": "modified",
20682074
},
2075+
"clientRoot/src/app/shared/jhipster/link-header.ts": {
2076+
"stateCleared": "modified",
2077+
},
20692078
"clientRoot/src/app/shared/jhipster/problem-details.ts": {
20702079
"stateCleared": "modified",
20712080
},
@@ -2844,6 +2853,9 @@ exports[`generator - react microservice-oauth2-withAdminUi(true)-skipJhipsterDep
28442853
"src/main/webapp/app/shared/jhipster/headers.ts": {
28452854
"stateCleared": "modified",
28462855
},
2856+
"src/main/webapp/app/shared/jhipster/link-header.ts": {
2857+
"stateCleared": "modified",
2858+
},
28472859
"src/main/webapp/app/shared/jhipster/problem-details.ts": {
28482860
"stateCleared": "modified",
28492861
},
@@ -3720,6 +3732,9 @@ exports[`generator - react monolith-jwt-skipUserManagement(false)-withAdminUi(tr
37203732
"src/main/webapp/app/shared/jhipster/headers.ts": {
37213733
"stateCleared": "modified",
37223734
},
3735+
"src/main/webapp/app/shared/jhipster/link-header.ts": {
3736+
"stateCleared": "modified",
3737+
},
37233738
"src/main/webapp/app/shared/jhipster/problem-details.ts": {
37243739
"stateCleared": "modified",
37253740
},
@@ -4479,6 +4494,9 @@ exports[`generator - react monolith-oauth2-withAdminUi(false)-skipJhipsterDepend
44794494
"src/main/webapp/app/shared/jhipster/headers.ts": {
44804495
"stateCleared": "modified",
44814496
},
4497+
"src/main/webapp/app/shared/jhipster/link-header.ts": {
4498+
"stateCleared": "modified",
4499+
},
44824500
"src/main/webapp/app/shared/jhipster/problem-details.ts": {
44834501
"stateCleared": "modified",
44844502
},
@@ -5241,6 +5259,9 @@ exports[`generator - react monolith-session-skipUserManagement(true)-withAdminUi
52415259
"src/main/webapp/app/shared/jhipster/headers.ts": {
52425260
"stateCleared": "modified",
52435261
},
5262+
"src/main/webapp/app/shared/jhipster/link-header.ts": {
5263+
"stateCleared": "modified",
5264+
},
52445265
"src/main/webapp/app/shared/jhipster/problem-details.ts": {
52455266
"stateCleared": "modified",
52465267
},

generators/vue/__snapshots__/generator.spec.ts.snap

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,9 @@ exports[`generator - vue gateway-jwt-skipUserManagement(true)-withAdminUi(false)
595595
"clientRoot/src/app/shared/jhipster/headers.ts": {
596596
"stateCleared": "modified",
597597
},
598+
"clientRoot/src/app/shared/jhipster/link-header.ts": {
599+
"stateCleared": "modified",
600+
},
598601
"clientRoot/src/app/shared/jhipster/problem-details.ts": {
599602
"stateCleared": "modified",
600603
},
@@ -1478,6 +1481,9 @@ exports[`generator - vue gateway-oauth2-withAdminUi(true)-skipJhipsterDependenci
14781481
"clientRoot/src/app/shared/jhipster/headers.ts": {
14791482
"stateCleared": "modified",
14801483
},
1484+
"clientRoot/src/app/shared/jhipster/link-header.ts": {
1485+
"stateCleared": "modified",
1486+
},
14811487
"clientRoot/src/app/shared/jhipster/problem-details.ts": {
14821488
"stateCleared": "modified",
14831489
},
@@ -2285,6 +2291,9 @@ exports[`generator - vue microservice-jwt-skipUserManagement(false)-withAdminUi(
22852291
"clientRoot/src/app/shared/jhipster/headers.ts": {
22862292
"stateCleared": "modified",
22872293
},
2294+
"clientRoot/src/app/shared/jhipster/link-header.ts": {
2295+
"stateCleared": "modified",
2296+
},
22882297
"clientRoot/src/app/shared/jhipster/problem-details.ts": {
22892298
"stateCleared": "modified",
22902299
},
@@ -3133,6 +3142,9 @@ exports[`generator - vue microservice-oauth2-withAdminUi(true)-skipJhipsterDepen
31333142
"src/main/webapp/app/shared/jhipster/headers.ts": {
31343143
"stateCleared": "modified",
31353144
},
3145+
"src/main/webapp/app/shared/jhipster/link-header.ts": {
3146+
"stateCleared": "modified",
3147+
},
31363148
"src/main/webapp/app/shared/jhipster/problem-details.ts": {
31373149
"stateCleared": "modified",
31383150
},
@@ -4141,6 +4153,9 @@ exports[`generator - vue monolith-jwt-skipUserManagement(false)-withAdminUi(true
41414153
"src/main/webapp/app/shared/jhipster/headers.ts": {
41424154
"stateCleared": "modified",
41434155
},
4156+
"src/main/webapp/app/shared/jhipster/link-header.ts": {
4157+
"stateCleared": "modified",
4158+
},
41444159
"src/main/webapp/app/shared/jhipster/problem-details.ts": {
41454160
"stateCleared": "modified",
41464161
},
@@ -4924,6 +4939,9 @@ exports[`generator - vue monolith-oauth2-withAdminUi(false)-skipJhipsterDependen
49244939
"src/main/webapp/app/shared/jhipster/headers.ts": {
49254940
"stateCleared": "modified",
49264941
},
4942+
"src/main/webapp/app/shared/jhipster/link-header.ts": {
4943+
"stateCleared": "modified",
4944+
},
49274945
"src/main/webapp/app/shared/jhipster/problem-details.ts": {
49284946
"stateCleared": "modified",
49294947
},
@@ -5722,6 +5740,9 @@ exports[`generator - vue monolith-session-skipUserManagement(true)-withAdminUi(f
57225740
"src/main/webapp/app/shared/jhipster/headers.ts": {
57235741
"stateCleared": "modified",
57245742
},
5743+
"src/main/webapp/app/shared/jhipster/link-header.ts": {
5744+
"stateCleared": "modified",
5745+
},
57255746
"src/main/webapp/app/shared/jhipster/problem-details.ts": {
57265747
"stateCleared": "modified",
57275748
},

0 commit comments

Comments
 (0)