@studyportals/fawkes 8.7.2-9 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/organisations/policies/our-picks/AreaAttendance.js +16 -14
- package/dist/src/organisations/policies/our-picks/CountryAttendance.js +15 -13
- package/dist/src/organisations/policies/ranked/RankedAreaDiscipline.d.ts +1 -0
- package/dist/src/organisations/policies/ranked/RankedAreaDiscipline.js +1 -0
- package/dist/src/organisations/policies/ranked/RankedAttendanceDiscipline.js +1 -0
- package/dist/src/organisations/policies/ranked/RankedContinentAttendance.js +15 -13
- package/dist/src/organisations/policies/ranked/RankedCountryAttendance.js +1 -0
- package/dist/src/organisations/policies/ranked/RankedCountryDiscipline.js +30 -14
- package/dist/src/organisations/policies/ranked/RankedDiscipline.js +14 -10
- package/dist/src/organisations/policies/ranked/RankedUnfiltered.js +10 -4
- package/dist/src/organisations/rules/MinimumAmountOfRankedResultsRule.js +4 -0
- package/package.json +3 -2
|
@@ -23,21 +23,23 @@ export class AreaAttendance extends OrganisationsSeoIndexabilityPolicy {
|
|
|
23
23
|
async generateUrls() {
|
|
24
24
|
const areaFragments = AreaPresenter.getInstance().getFragments();
|
|
25
25
|
const attendanceFragments = AttendancePresenter.getInstance().getFragments();
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
paths.push(this.getPathWithSortingOption(`${attendance.path}/${area.path}`));
|
|
37
|
-
}
|
|
26
|
+
// Create array of promises for all combinations to parallelize API calls
|
|
27
|
+
const promises = areaFragments.flatMap(area => attendanceFragments.map(async (attendance) => {
|
|
28
|
+
const filterKeyValues = new Map([
|
|
29
|
+
[FilterKey.AREA, [area.id]],
|
|
30
|
+
[FilterKey.COUNTRY, [area.countryId]],
|
|
31
|
+
[FilterKey.DELIVERY_METHOD, [attendance.id]]
|
|
32
|
+
]);
|
|
33
|
+
const result = await this.checkRulesForSitemap(filterKeyValues);
|
|
34
|
+
if (result) {
|
|
35
|
+
return this.getPathWithSortingOption(`${attendance.path}/${area.path}`);
|
|
38
36
|
}
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
return null;
|
|
38
|
+
}));
|
|
39
|
+
// Wait for all promises to resolve in parallel
|
|
40
|
+
const results = await Promise.all(promises);
|
|
41
|
+
// Filter out null values and return paths
|
|
42
|
+
return results.filter((path) => path !== null);
|
|
41
43
|
}
|
|
42
44
|
get filterCombination() {
|
|
43
45
|
return FilterCombinations.AREA_ATTENDANCE;
|
|
@@ -25,19 +25,21 @@ export class CountryAttendance extends OrganisationsSeoIndexabilityPolicy {
|
|
|
25
25
|
async generateUrls() {
|
|
26
26
|
const countryFragments = CountryPresenter.getInstance().getFragments();
|
|
27
27
|
const attendanceFragments = AttendancePresenter.getInstance().getFragments();
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
paths.push(this.getPathWithSortingOption(`${attendance.path}/${country.path}`));
|
|
38
|
-
}
|
|
28
|
+
// Create array of promises for all combinations to parallelize API calls
|
|
29
|
+
const promises = countryFragments.flatMap(country => attendanceFragments.map(async (attendance) => {
|
|
30
|
+
const filterKeyValues = new Map([
|
|
31
|
+
[FilterKey.COUNTRY, [country.id]],
|
|
32
|
+
[FilterKey.DELIVERY_METHOD, [attendance.id]]
|
|
33
|
+
]);
|
|
34
|
+
const result = await this.checkRulesForSitemap(filterKeyValues);
|
|
35
|
+
if (result) {
|
|
36
|
+
return this.getPathWithSortingOption(`${attendance.path}/${country.path}`);
|
|
39
37
|
}
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
return null;
|
|
39
|
+
}));
|
|
40
|
+
// Wait for all promises to resolve in parallel
|
|
41
|
+
const results = await Promise.all(promises);
|
|
42
|
+
// Filter out null values and return paths
|
|
43
|
+
return results.filter((path) => path !== null);
|
|
42
44
|
}
|
|
43
45
|
}
|
|
@@ -4,6 +4,7 @@ import { SingleValueSelectedForFilterRule } from '../../../common/rules/SingleVa
|
|
|
4
4
|
import { OnlyFiltersSelectedRule } from '../../../common/rules/OnlyFiltersSelectedRule';
|
|
5
5
|
import { RankedOrganisationsSeoIndexabilityPolicy } from '../RankedOrganisationsSeoIndexabilityPolicy';
|
|
6
6
|
import { IOrganisationsSeoDependencies } from '../../../organisations/types/IOrganisationsSeoDependencies';
|
|
7
|
+
/** @deprecated Don't use this policy as it's been removed from indexability manager. */
|
|
7
8
|
export declare class RankedAreaDiscipline extends RankedOrganisationsSeoIndexabilityPolicy {
|
|
8
9
|
readonly name: string;
|
|
9
10
|
readonly description: string;
|
|
@@ -6,6 +6,7 @@ import { OnlyFiltersSelectedRule } from '../../../common/rules/OnlyFiltersSelect
|
|
|
6
6
|
import { AreaPresenter } from '../../../presenters/AreaPresenter';
|
|
7
7
|
import { DisciplinePresenter } from '../../../presenters/DisciplinePresenter';
|
|
8
8
|
import { RankedOrganisationsSeoIndexabilityPolicy } from '../RankedOrganisationsSeoIndexabilityPolicy';
|
|
9
|
+
/** @deprecated Don't use this policy as it's been removed from indexability manager. */
|
|
9
10
|
export class RankedAreaDiscipline extends RankedOrganisationsSeoIndexabilityPolicy {
|
|
10
11
|
name = 'Ranked Area Discipline Policy';
|
|
11
12
|
description = 'Controls indexing for ranking-sorted pages filtered by geographic areas, optimizing for region-specific content with quality-based result ordering.';
|
|
@@ -25,6 +25,7 @@ export class RankedAttendanceDiscipline extends RankedOrganisationsSeoIndexabili
|
|
|
25
25
|
const attendanceFragments = AttendancePresenter.getInstance().getFragments();
|
|
26
26
|
const disciplineFragments = DisciplinePresenter.getInstance().getFragments();
|
|
27
27
|
const paths = [];
|
|
28
|
+
// Process sequentially to respect API rate limits
|
|
28
29
|
for (const attendance of attendanceFragments) {
|
|
29
30
|
for (const discipline of disciplineFragments) {
|
|
30
31
|
const filterKeyValues = new Map([
|
|
@@ -23,20 +23,22 @@ export class RankedContinentAttendance extends RankedOrganisationsSeoIndexabilit
|
|
|
23
23
|
async generateUrls() {
|
|
24
24
|
const continentFragments = ContinentPresenter.getInstance().getFragments();
|
|
25
25
|
const attendanceFragments = AttendancePresenter.getInstance().getFragments();
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
paths.push(this.getPathWithSortingOption(`${attendance.path}/${continent.path}`));
|
|
36
|
-
}
|
|
26
|
+
// Create array of promises for all combinations to parallelize API calls
|
|
27
|
+
const promises = continentFragments.flatMap(continent => attendanceFragments.map(async (attendance) => {
|
|
28
|
+
const filterKeyValues = new Map([
|
|
29
|
+
[FilterKey.CONTINENT, [continent.id]],
|
|
30
|
+
[FilterKey.DELIVERY_METHOD, [attendance.id]],
|
|
31
|
+
]);
|
|
32
|
+
const result = await this.checkRulesForSitemap(filterKeyValues);
|
|
33
|
+
if (result) {
|
|
34
|
+
return this.getPathWithSortingOption(`${attendance.path}/${continent.path}`);
|
|
37
35
|
}
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
return null;
|
|
37
|
+
}));
|
|
38
|
+
// Wait for all promises to resolve in parallel
|
|
39
|
+
const results = await Promise.all(promises);
|
|
40
|
+
// Filter out null values and return paths
|
|
41
|
+
return results.filter((path) => path !== null);
|
|
40
42
|
}
|
|
41
43
|
get filterCombination() {
|
|
42
44
|
return FilterCombinations.RANKED_CONTINENT_ATTENDANCE;
|
|
@@ -22,6 +22,7 @@ export class RankedCountryAttendance extends RankedOrganisationsSeoIndexabilityP
|
|
|
22
22
|
const countryFragments = CountryPresenter.getInstance().getFragments();
|
|
23
23
|
const attendanceFragments = AttendancePresenter.getInstance().getFragments();
|
|
24
24
|
const paths = [];
|
|
25
|
+
// Process sequentially to respect API rate limits
|
|
25
26
|
for (const country of countryFragments) {
|
|
26
27
|
for (const attendance of attendanceFragments) {
|
|
27
28
|
const filterKeyValues = new Map([
|
|
@@ -5,6 +5,7 @@ import { OnlyFiltersSelectedRule } from '../../../common/rules/OnlyFiltersSelect
|
|
|
5
5
|
import { CountryPresenter } from '../../../presenters/CountryPresenter';
|
|
6
6
|
import { DisciplinePresenter } from '../../../presenters/DisciplinePresenter';
|
|
7
7
|
import { RankedOrganisationsSeoIndexabilityPolicy } from '../RankedOrganisationsSeoIndexabilityPolicy';
|
|
8
|
+
const DEFAULT_CONCURRENT_REQUESTS = 20; // Maximum number of concurrent requests to avoid rate limiting
|
|
8
9
|
export class RankedCountryDiscipline extends RankedOrganisationsSeoIndexabilityPolicy {
|
|
9
10
|
name = 'Ranked Country Discipline Policy';
|
|
10
11
|
description = 'Determines which ranking-sorted pages at the intersection of country and discipline filters qualify for indexing based on search value.';
|
|
@@ -17,22 +18,37 @@ export class RankedCountryDiscipline extends RankedOrganisationsSeoIndexabilityP
|
|
|
17
18
|
super(dependencies);
|
|
18
19
|
}
|
|
19
20
|
async generateUrls() {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
21
|
+
try {
|
|
22
|
+
const countryFragments = CountryPresenter.getInstance().getFragments();
|
|
23
|
+
const disciplineFragments = DisciplinePresenter.getInstance().getFragments();
|
|
24
|
+
const paths = [];
|
|
25
|
+
const configuredConcurrency = Number.parseInt(process.env.SITEMAP_COMBINATION_CONCURRENCY
|
|
26
|
+
|| DEFAULT_CONCURRENT_REQUESTS.toString(), 10);
|
|
27
|
+
const concurrencyLimit = Math.max(1, configuredConcurrency); // Ensure at least 1
|
|
28
|
+
// Create all combinations
|
|
29
|
+
const combinations = countryFragments.flatMap(country => disciplineFragments.map(discipline => ({ country, discipline })));
|
|
30
|
+
// Process in batches to respect API rate limits
|
|
31
|
+
for (let i = 0; i < combinations.length; i += concurrencyLimit) {
|
|
32
|
+
const batch = combinations.slice(i, i + concurrencyLimit);
|
|
33
|
+
const results = await Promise.all(batch.map(async ({ country, discipline }) => {
|
|
34
|
+
const filterKeyValues = new Map([
|
|
35
|
+
[FilterKey.COUNTRY, [country.id]],
|
|
36
|
+
[FilterKey.DISCIPLINES, [discipline.id]]
|
|
37
|
+
]);
|
|
38
|
+
const result = await this.checkRulesForSitemap(filterKeyValues);
|
|
39
|
+
if (result) {
|
|
40
|
+
return this.getPathWithSortingOption(`${country.path}/${discipline.path}`);
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}));
|
|
44
|
+
paths.push(...results.filter((path) => path !== null));
|
|
33
45
|
}
|
|
46
|
+
return paths;
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
console.error('Error generating URLs for RankedCountryDiscipline:', error);
|
|
50
|
+
return [];
|
|
34
51
|
}
|
|
35
|
-
return paths;
|
|
36
52
|
}
|
|
37
53
|
get filterCombination() {
|
|
38
54
|
return FilterCombinations.RANKED_DISCIPLINE_COUNTRY;
|
|
@@ -15,18 +15,22 @@ export class RankedDiscipline extends RankedOrganisationsSeoIndexabilityPolicy {
|
|
|
15
15
|
super(dependencies);
|
|
16
16
|
}
|
|
17
17
|
async generateUrls() {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
[FilterKey.DISCIPLINES, [discipline.id]]
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
try {
|
|
19
|
+
const disciplineFragments = DisciplinePresenter.getInstance().getFragments();
|
|
20
|
+
const paths = [];
|
|
21
|
+
for (const discipline of disciplineFragments) {
|
|
22
|
+
const filterKeyValues = new Map([[FilterKey.DISCIPLINES, [discipline.id]]]);
|
|
23
|
+
const result = await this.checkRulesForSitemap(filterKeyValues);
|
|
24
|
+
if (result) {
|
|
25
|
+
paths.push(this.getPathWithSortingOption(discipline.path));
|
|
26
|
+
}
|
|
27
27
|
}
|
|
28
|
+
return paths;
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
console.error('Error generating URLs for RankedDiscipline:', error);
|
|
32
|
+
return [];
|
|
28
33
|
}
|
|
29
|
-
return paths;
|
|
30
34
|
}
|
|
31
35
|
get filterCombination() {
|
|
32
36
|
return FilterCombinations.RANKED_DISCIPLINE;
|
|
@@ -9,11 +9,17 @@ export class RankedUnfiltered extends RankedOrganisationsSeoIndexabilityPolicy {
|
|
|
9
9
|
super(dependencies);
|
|
10
10
|
}
|
|
11
11
|
async generateUrls() {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
try {
|
|
13
|
+
const filterKeyValues = new Map([]);
|
|
14
|
+
if (await this.checkRulesForSitemap(filterKeyValues)) {
|
|
15
|
+
return [this.getPathWithSortingOption('')];
|
|
16
|
+
}
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
console.error('Error generating URLs for RankedUnfiltered:', error);
|
|
21
|
+
return [];
|
|
15
22
|
}
|
|
16
|
-
return [];
|
|
17
23
|
}
|
|
18
24
|
get filterCombination() {
|
|
19
25
|
return FilterCombinations.RANKED_UNFILTERED;
|
|
@@ -14,6 +14,10 @@ export class MinimumAmountOfRankedResultsRule {
|
|
|
14
14
|
if (!this.searchApiClient) {
|
|
15
15
|
throw new DependencyMissingError('SearchApiClient');
|
|
16
16
|
}
|
|
17
|
+
if (!this.searchApiClient.getCountWithRankings) {
|
|
18
|
+
const filterKeys = [...filterKeyValues.keys()];
|
|
19
|
+
throw new DependencyMissingError(`CountwithRankings ${filterKeys.join(',')}`);
|
|
20
|
+
}
|
|
17
21
|
const rankedResultsCount = await this.searchApiClient.getCountWithRankings(filterKeyValues);
|
|
18
22
|
return rankedResultsCount >= this.minimumRankedResultsCount;
|
|
19
23
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@studyportals/fawkes",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.0.0",
|
|
4
4
|
"description": "A package to centralize SEO related logic for SBLP and Sitemap Generator.",
|
|
5
5
|
"files": [
|
|
6
6
|
"./dist"
|
|
@@ -91,7 +91,8 @@
|
|
|
91
91
|
},
|
|
92
92
|
"dependencies": {
|
|
93
93
|
"@studyportals/domain-client": "^8.1.0",
|
|
94
|
-
"@studyportals/
|
|
94
|
+
"@studyportals/ranking-api-interface": "^2.4.0",
|
|
95
|
+
"@studyportals/search-api-interface": "5.9.3-0",
|
|
95
96
|
"@studyportals/search-filters": "^6.3.1",
|
|
96
97
|
"@studyportals/static-domain-data": "^6.1.0"
|
|
97
98
|
},
|