@studyportals/fawkes 8.7.2-2 → 8.7.2-21

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.
@@ -11,3 +11,4 @@ export { ISitemapUrlGenerator } from '../src/common/ISitemapUrlGenerator';
11
11
  export { FilterKeyValuesMap } from '../src/common/FilterKeyValuesMap';
12
12
  export { IPresenter } from '../src/common';
13
13
  export { IProgrammeSitemapUrlGenerator } from '../src/programmes/types/IProgrammeSitemapUrlGenerator';
14
+ export { IRankingApiClient } from '../src/sitemap-generator/IRankingApiClient';
@@ -1,3 +1,2 @@
1
1
  import { FilterKey } from '@studyportals/search-filters/server-side';
2
- import { FilterType } from '@studyportals/omnisearch-interfaces';
3
2
  export declare const FILTER_KEY_TO_FILTER_TYPE_MAP: Map<FilterKey, FilterType>;
@@ -3,4 +3,3 @@ export * from './ISeoFilterState';
3
3
  export * from './ISearchApplicationState';
4
4
  export * from './ISearchIndexabilityManager';
5
5
  export * from './IPresenter';
6
- export * from './FilterTypeMap';
@@ -3,4 +3,3 @@ export * from './ISeoFilterState';
3
3
  export * from './ISearchApplicationState';
4
4
  export * from './ISearchIndexabilityManager';
5
5
  export * from './IPresenter';
6
- export * from './FilterTypeMap';
@@ -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
- const paths = [];
27
- for (const area of areaFragments) {
28
- for (const attendance of attendanceFragments) {
29
- const filterKeyValues = new Map([
30
- [FilterKey.AREA, [area.id]],
31
- [FilterKey.COUNTRY, [area.countryId]],
32
- [FilterKey.DELIVERY_METHOD, [attendance.id]]
33
- ]);
34
- const result = await this.checkRulesForSitemap(filterKeyValues);
35
- if (result) {
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
- return paths;
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
- const paths = [];
29
- for (const country of countryFragments) {
30
- for (const attendance of attendanceFragments) {
31
- const filterKeyValues = new Map([
32
- [FilterKey.COUNTRY, [country.id]],
33
- [FilterKey.DELIVERY_METHOD, [attendance.id]]
34
- ]);
35
- const result = await this.checkRulesForSitemap(filterKeyValues);
36
- if (result) {
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
- return paths;
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
  }
@@ -24,19 +24,21 @@ export class RankedAttendanceDiscipline extends RankedOrganisationsSeoIndexabili
24
24
  async generateUrls() {
25
25
  const attendanceFragments = AttendancePresenter.getInstance().getFragments();
26
26
  const disciplineFragments = DisciplinePresenter.getInstance().getFragments();
27
- const paths = [];
28
- for (const attendance of attendanceFragments) {
29
- for (const discipline of disciplineFragments) {
30
- const filterKeyValues = new Map([
31
- [FilterKey.DELIVERY_METHOD, [attendance.id]],
32
- [FilterKey.DISCIPLINES, [discipline.id]],
33
- ]);
34
- const result = await this.checkRulesForSitemap(filterKeyValues);
35
- if (result) {
36
- paths.push(this.getPathWithSortingOption(`${attendance.path}/${discipline.path}`));
37
- }
27
+ // Create array of promises for all combinations to parallelize API calls
28
+ const promises = attendanceFragments.flatMap(attendance => disciplineFragments.map(async (discipline) => {
29
+ const filterKeyValues = new Map([
30
+ [FilterKey.DELIVERY_METHOD, [attendance.id]],
31
+ [FilterKey.DISCIPLINES, [discipline.id]],
32
+ ]);
33
+ const result = await this.checkRulesForSitemap(filterKeyValues);
34
+ if (result) {
35
+ return this.getPathWithSortingOption(`${attendance.path}/${discipline.path}`);
38
36
  }
39
- }
40
- return paths;
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
  }
@@ -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
- const paths = [];
27
- for (const continent of continentFragments) {
28
- for (const attendance of attendanceFragments) {
29
- const filterKeyValues = new Map([
30
- [FilterKey.CONTINENT, [continent.id]],
31
- [FilterKey.DELIVERY_METHOD, [attendance.id]],
32
- ]);
33
- const result = await this.checkRulesForSitemap(filterKeyValues);
34
- if (result) {
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
- return paths;
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;
@@ -21,20 +21,22 @@ export class RankedCountryAttendance extends RankedOrganisationsSeoIndexabilityP
21
21
  async generateUrls() {
22
22
  const countryFragments = CountryPresenter.getInstance().getFragments();
23
23
  const attendanceFragments = AttendancePresenter.getInstance().getFragments();
24
- const paths = [];
25
- for (const country of countryFragments) {
26
- for (const attendance of attendanceFragments) {
27
- const filterKeyValues = new Map([
28
- [FilterKey.COUNTRY, [country.id]],
29
- [FilterKey.DELIVERY_METHOD, [attendance.id]]
30
- ]);
31
- const result = await this.checkRulesForSitemap(filterKeyValues);
32
- if (result) {
33
- paths.push(this.getPathWithSortingOption(`${attendance.path}/${country.path}`));
34
- }
24
+ // Create array of promises for all combinations to parallelize API calls
25
+ const promises = countryFragments.flatMap(country => attendanceFragments.map(async (attendance) => {
26
+ const filterKeyValues = new Map([
27
+ [FilterKey.COUNTRY, [country.id]],
28
+ [FilterKey.DELIVERY_METHOD, [attendance.id]]
29
+ ]);
30
+ const result = await this.checkRulesForSitemap(filterKeyValues);
31
+ if (result) {
32
+ return this.getPathWithSortingOption(`${attendance.path}/${country.path}`);
35
33
  }
36
- }
37
- return paths;
34
+ return null;
35
+ }));
36
+ // Wait for all promises to resolve in parallel
37
+ const results = await Promise.all(promises);
38
+ // Filter out null values and return paths
39
+ return results.filter((path) => path !== null);
38
40
  }
39
41
  get filterCombination() {
40
42
  return FilterCombinations.RANKED_COUNTRY_ATTENDANCE;
@@ -17,22 +17,30 @@ export class RankedCountryDiscipline extends RankedOrganisationsSeoIndexabilityP
17
17
  super(dependencies);
18
18
  }
19
19
  async generateUrls() {
20
- const countryFragments = CountryPresenter.getInstance().getFragments();
21
- const disciplineFragments = DisciplinePresenter.getInstance().getFragments();
22
- const paths = [];
23
- for (const country of countryFragments) {
24
- for (const discipline of disciplineFragments) {
20
+ try {
21
+ const countryFragments = CountryPresenter.getInstance().getFragments();
22
+ const disciplineFragments = DisciplinePresenter.getInstance().getFragments();
23
+ // Create array of promises for all combinations to parallelize API calls
24
+ const promises = countryFragments.flatMap(country => disciplineFragments.map(async (discipline) => {
25
25
  const filterKeyValues = new Map([
26
26
  [FilterKey.COUNTRY, [country.id]],
27
- [FilterKey.DISCIPLINES, [discipline.id]],
27
+ [FilterKey.DISCIPLINES, [discipline.id]]
28
28
  ]);
29
29
  const result = await this.checkRulesForSitemap(filterKeyValues);
30
30
  if (result) {
31
- paths.push(this.getPathWithSortingOption(`${country.path}/${discipline.path}`));
31
+ return this.getPathWithSortingOption(`${country.path}/${discipline.path}`);
32
32
  }
33
- }
33
+ return null;
34
+ }));
35
+ // Wait for all promises to resolve in parallel
36
+ const results = await Promise.all(promises);
37
+ // Filter out null values and return paths
38
+ return results.filter((path) => path !== null);
39
+ }
40
+ catch (error) {
41
+ console.error('Error generating URLs for RankedCountryDiscipline:', error);
42
+ return [];
34
43
  }
35
- return paths;
36
44
  }
37
45
  get filterCombination() {
38
46
  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
- const disciplineFragments = DisciplinePresenter.getInstance().getFragments();
19
- const paths = [];
20
- for (const discipline of disciplineFragments) {
21
- const filterKeyValues = new Map([
22
- [FilterKey.DISCIPLINES, [discipline.id]]
23
- ]);
24
- const result = await this.checkRulesForSitemap(filterKeyValues);
25
- if (result) {
26
- paths.push(this.getPathWithSortingOption(discipline.path));
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
- const filterKeyValues = new Map([]);
13
- if (await this.checkRulesForSitemap(filterKeyValues)) {
14
- return [this.getPathWithSortingOption('')];
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;
@@ -7,7 +7,6 @@ export declare class MinimumAmountOfRankedResultsRule implements IRule {
7
7
  constructor(searchApiClient?: ISearchApiClient);
8
8
  forSearch(dependencies: IOrganisationSearchDependencies): Promise<boolean>;
9
9
  forSitemapGenerator(filterKeyValues: FilterKeyValuesMap): Promise<boolean>;
10
- private mapResults;
11
10
  getName(): string;
12
11
  getDescription(): string;
13
12
  }
@@ -1,7 +1,4 @@
1
1
  import { DependencyMissingError } from '../../errors/DependencyMissingError';
2
- import { FILTER_KEY_TO_FILTER_TYPE_MAP } from '../../common/FilterKeyToFilterTypeMap';
3
- import { FilterType } from '@studyportals/omnisearch-interfaces';
4
- import { OrganisationSortType } from '@studyportals/search-api-interface';
5
2
  export class MinimumAmountOfRankedResultsRule {
6
3
  minimumRankedResultsCount = 4;
7
4
  searchApiClient;
@@ -17,21 +14,23 @@ export class MinimumAmountOfRankedResultsRule {
17
14
  if (!this.searchApiClient) {
18
15
  throw new DependencyMissingError('SearchApiClient');
19
16
  }
20
- const mappedResults = this.mapResults(filterKeyValues);
21
- const rankedResultsCount = await this.searchApiClient.getCount(mappedResults);
22
- return rankedResultsCount >= this.minimumRankedResultsCount;
23
- }
24
- mapResults(filterKeyValues) {
25
- const filterTypeMap = new Map([
26
- [FilterType.SORTING, [OrganisationSortType.RANKINGS]]
27
- ]);
28
- for (const [filterKey, values] of filterKeyValues.entries()) {
29
- const filterType = FILTER_KEY_TO_FILTER_TYPE_MAP.get(filterKey);
30
- if (filterType) {
31
- filterTypeMap.set(filterType, values);
32
- }
17
+ if (!this.searchApiClient.getCountWithRankings) {
18
+ const filterKeys = [...filterKeyValues.keys()];
19
+ throw new DependencyMissingError(`CountwithRankings ${filterKeys.join(',')}`);
20
+ }
21
+ // eslint-disable-next-line no-console
22
+ console.debug('MinimumAmountOfRankedResultsRule: Checking ranked results count for filterKeyValues:', filterKeyValues);
23
+ try {
24
+ const rankedResultsCount = await this.searchApiClient.getCountWithRankings(filterKeyValues);
25
+ // eslint-disable-next-line no-console
26
+ console.debug('MinimumAmountOfRankedResultsRule: Ranked results count:', rankedResultsCount);
27
+ return rankedResultsCount >= this.minimumRankedResultsCount;
28
+ }
29
+ catch (error) {
30
+ // eslint-disable-next-line no-console
31
+ console.error('MinimumAmountOfRankedResultsRule: API call failed:', error);
32
+ throw error;
33
33
  }
34
- return filterTypeMap;
35
34
  }
36
35
  getName() {
37
36
  return 'AtLeastFourRankedResultsRule';
@@ -9,7 +9,6 @@ export declare class MinimumAmountOfResultsRule implements IRule {
9
9
  constructor(searchApiClient?: ISearchApiClient);
10
10
  forSearch(dependencies: ISearchDependencies): Promise<boolean>;
11
11
  forSitemapGeneratorWithPageNumber(filterKeyValues: FilterKeyValuesMap, pageNumber: number): Promise<boolean>;
12
- private mapResults;
13
12
  forSitemapGenerator(filterKeyValues: FilterKeyValuesMap): Promise<boolean>;
14
13
  getName(): string;
15
14
  getDescription(): string;
@@ -1,7 +1,4 @@
1
1
  import { DependencyMissingError } from '../../errors/DependencyMissingError';
2
- import { FilterType } from '@studyportals/omnisearch-interfaces';
3
- import { OrganisationSortType } from '@studyportals/search-api-interface';
4
- import { FILTER_KEY_TO_FILTER_TYPE_MAP } from '../../common/FilterKeyToFilterTypeMap';
5
2
  export class MinimumAmountOfResultsRule {
6
3
  minimumResultsCount = 7;
7
4
  maximumPageSize = 20;
@@ -18,22 +15,9 @@ export class MinimumAmountOfResultsRule {
18
15
  if (!this.searchApiClient) {
19
16
  throw new DependencyMissingError('SearchApiClient');
20
17
  }
21
- const mappedResults = this.mapResults(filterKeyValues);
22
- const count = await this.searchApiClient.getCount(mappedResults);
18
+ const count = await this.searchApiClient.getCount(filterKeyValues);
23
19
  return count >= (pageNumber - 1) * this.maximumPageSize + this.minimumResultsCount;
24
20
  }
25
- mapResults(filterKeyValues) {
26
- const filterTypeMap = new Map([
27
- [FilterType.SORTING, [OrganisationSortType.RANKINGS]]
28
- ]);
29
- for (const [filterKey, values] of filterKeyValues.entries()) {
30
- const filterType = FILTER_KEY_TO_FILTER_TYPE_MAP.get(filterKey);
31
- if (filterType) {
32
- filterTypeMap.set(filterType, values);
33
- }
34
- }
35
- return filterTypeMap;
36
- }
37
21
  async forSitemapGenerator(filterKeyValues) {
38
22
  if (!this.searchApiClient) {
39
23
  throw new DependencyMissingError('SearchApiClient');
@@ -9,7 +9,6 @@ export declare class MinimumAmountOfResultsRule implements IProgrammeRule {
9
9
  constructor(searchApiClient?: ISearchApiClient);
10
10
  forSearch(dependencies: ISearchDependencies): Promise<boolean>;
11
11
  forSitemapGeneratorWithPageNumber(filterKeyValues: FilterKeyValuesMap, pageNumber: number): Promise<boolean>;
12
- private mapResults;
13
12
  forSitemapGenerator(): Promise<boolean>;
14
13
  getName(): string;
15
14
  getDescription(): string;
@@ -1,7 +1,4 @@
1
1
  import { DependencyMissingError } from '../../errors/DependencyMissingError';
2
- import { FilterType } from '@studyportals/omnisearch-interfaces';
3
- import { OrganisationSortType } from '@studyportals/search-api-interface';
4
- import { FILTER_KEY_TO_FILTER_TYPE_MAP } from '../../common/FilterKeyToFilterTypeMap';
5
2
  export class MinimumAmountOfResultsRule {
6
3
  minimumResultsCount = 7;
7
4
  maximumPageSize = 20;
@@ -18,22 +15,9 @@ export class MinimumAmountOfResultsRule {
18
15
  if (!this.searchApiClient) {
19
16
  throw new DependencyMissingError('SearchApiClient');
20
17
  }
21
- const mappedResults = this.mapResults(filterKeyValues);
22
- const count = await this.searchApiClient.getCount(mappedResults);
18
+ const count = await this.searchApiClient.getCount(filterKeyValues);
23
19
  return count >= (pageNumber - 1) * this.maximumPageSize + this.minimumResultsCount;
24
20
  }
25
- mapResults(filterKeyValues) {
26
- const filterTypeMap = new Map([
27
- [FilterType.SORTING, [OrganisationSortType.RANKINGS]]
28
- ]);
29
- for (const [filterKey, values] of filterKeyValues.entries()) {
30
- const filterType = FILTER_KEY_TO_FILTER_TYPE_MAP.get(filterKey);
31
- if (filterType) {
32
- filterTypeMap.set(filterType, values);
33
- }
34
- }
35
- return filterTypeMap;
36
- }
37
21
  forSitemapGenerator() {
38
22
  throw new Error('Method not implemented.');
39
23
  }
@@ -17,6 +17,7 @@ export class BaseSitemapUrlGeneratorManager {
17
17
  for (const presenter of this.presenters.values()) {
18
18
  const count = presenter.getFragments().length;
19
19
  this.filterKeyValueCounts.set(presenter.filterKey, count);
20
+ console.debug(`Set filter key value count for ${presenter.filterKey}: ${count}`); // eslint-disable-line no-console
20
21
  }
21
22
  }
22
23
  getPolicies() {
@@ -1,5 +1,4 @@
1
1
  import { FilterKeyValuesMap } from '../common/FilterKeyValuesMap';
2
- import { FilterTypeMap } from '../common/FilterTypeMap';
3
2
  export interface ISearchApiClient {
4
3
  getOrganisationIds(): Promise<number[]>;
5
4
  getCityIds?(): Promise<number[]>;
@@ -11,5 +10,6 @@ export interface ISearchApiClient {
11
10
  countryIsoCode: string;
12
11
  }[]>;
13
12
  getProgrammeCount?(filterKeyValues: FilterKeyValuesMap): Promise<number>;
14
- getCount(filterKeyValues: FilterKeyValuesMap | FilterTypeMap): Promise<number>;
13
+ getCount(filterKeyValues: FilterKeyValuesMap): Promise<number>;
14
+ getCountWithRankings?(filterKeyValues: FilterKeyValuesMap): Promise<number>;
15
15
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@studyportals/fawkes",
3
- "version": "8.7.2-2",
3
+ "version": "8.7.2-21",
4
4
  "description": "A package to centralize SEO related logic for SBLP and Sitemap Generator.",
5
5
  "files": [
6
6
  "./dist"
@@ -91,9 +91,8 @@
91
91
  },
92
92
  "dependencies": {
93
93
  "@studyportals/domain-client": "^8.1.0",
94
- "@studyportals/omnisearch-interfaces": "^0.2.1",
95
- "@studyportals/ranking-api-interface": "^1.3.12",
96
- "@studyportals/search-api-interface": "^5.9.2",
94
+ "@studyportals/ranking-api-interface": "^2.4.0",
95
+ "@studyportals/search-api-interface": "5.9.3-0",
97
96
  "@studyportals/search-filters": "^6.3.1",
98
97
  "@studyportals/static-domain-data": "^6.1.0"
99
98
  },