@studyportals/fawkes 8.2.4-15 → 8.2.4-17

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.
@@ -3,4 +3,5 @@ import { FilterKey } from '@studyportals/search-filters/server-side';
3
3
  export interface IPresenter {
4
4
  readonly filterKey: FilterKey;
5
5
  getFragments(): IFragment[];
6
+ getFragmentsAsync?(): Promise<IFragment[]>;
6
7
  }
@@ -73,6 +73,8 @@ export declare enum FilterCombinations {
73
73
  RANKED_CONTINENT = "rankedContinent",
74
74
  RANKED_CONTINENT_ATTENDANCE = "rankedContinentAttendance",
75
75
  RANKED_COUNTRY = "rankedCountry",
76
+ RANKED_COUNTRY_CITY = "rankedCountryCity",
77
+ RANKED_COUNTRY_AREA_CITY = "rankedCountryAreaCity",
76
78
  RANKED_COUNTRY_ATTENDANCE = "rankedCountryAttendance",
77
79
  RANKED_DISCIPLINE_COUNTRY = "rankedDisciplineCountry",
78
80
  RANKED_DISCIPLINE = "rankedDiscipline",
@@ -74,6 +74,8 @@ export var FilterCombinations;
74
74
  FilterCombinations["RANKED_CONTINENT"] = "rankedContinent";
75
75
  FilterCombinations["RANKED_CONTINENT_ATTENDANCE"] = "rankedContinentAttendance";
76
76
  FilterCombinations["RANKED_COUNTRY"] = "rankedCountry";
77
+ FilterCombinations["RANKED_COUNTRY_CITY"] = "rankedCountryCity";
78
+ FilterCombinations["RANKED_COUNTRY_AREA_CITY"] = "rankedCountryAreaCity";
77
79
  FilterCombinations["RANKED_COUNTRY_ATTENDANCE"] = "rankedCountryAttendance";
78
80
  FilterCombinations["RANKED_DISCIPLINE_COUNTRY"] = "rankedDisciplineCountry";
79
81
  FilterCombinations["RANKED_DISCIPLINE"] = "rankedDiscipline";
@@ -1,5 +1,7 @@
1
1
  import { DependencyTypes } from '../enums/DependencyTypes';
2
2
  import { Area, AreaAttendance, Attendance, Continent, Country, CountryAttendance, Unfiltered, RankedArea, RankedAreaDiscipline, RankedAttendance, RankedAttendanceDiscipline, RankedContinent, RankedContinentAttendance, RankedCountry, RankedCountryAttendance, RankedCountryDiscipline, RankedDiscipline, RankedUnfiltered, CountryCity, CountryAreaCity } from '../organisations/policies';
3
+ import { RankedCountryCity } from './policies/ranked/RankedCountryCity';
4
+ import { RankedCountryAreaCity } from './policies/ranked/RankedCountryAreaCity';
3
5
  export class SearchIndexabilityManager {
4
6
  policies;
5
7
  constructor(portalType, seoInfoBase, filterState, sortingState, applicationState) {
@@ -28,6 +30,8 @@ export class SearchIndexabilityManager {
28
30
  new RankedContinent(dependencies),
29
31
  new RankedContinentAttendance(dependencies),
30
32
  new RankedCountry(dependencies),
33
+ new RankedCountryCity(dependencies),
34
+ new RankedCountryAreaCity(dependencies),
31
35
  new RankedCountryAttendance(dependencies),
32
36
  new RankedCountryDiscipline(dependencies),
33
37
  new RankedDiscipline(dependencies),
@@ -18,13 +18,10 @@ export class CountryAreaCity extends OrganisationsSeoIndexabilityPolicy {
18
18
  async generateUrls() {
19
19
  const cityFragments = await CityPresenter
20
20
  .getInstance(this.dependencies.searchApiClient)
21
- .getFragments();
21
+ .getFragmentsAsync();
22
22
  const paths = [];
23
23
  console.warn(`Total city fragments fetched: ${cityFragments.length}`);
24
- console.warn('Fetched city fragments for CountryAreaCity policy:', JSON.stringify(cityFragments));
25
24
  const filteredFragmentsForCountryAreaCity = cityFragments.filter(city => city.areaId !== null && city.areaId !== undefined);
26
- console.warn(`Filtered down to ${filteredFragmentsForCountryAreaCity.length} city fragments for CountryAreaCity policy.`);
27
- console.warn('Filtered city fragments for CountryAreaCity policy:', JSON.stringify(filteredFragmentsForCountryAreaCity));
28
25
  for (const city of filteredFragmentsForCountryAreaCity) {
29
26
  const areaId = city.areaId?.toString() || '';
30
27
  if (areaId === '') {
@@ -40,8 +37,6 @@ export class CountryAreaCity extends OrganisationsSeoIndexabilityPolicy {
40
37
  paths.push(this.getPathWithSortingOption(city.path));
41
38
  }
42
39
  }
43
- console.warn(`Generated ${paths.length} URLs for CountryAreaCity policy.`);
44
- console.warn('Generated URLs:', JSON.stringify(paths));
45
40
  return paths;
46
41
  }
47
42
  get filterCombination() {
@@ -17,13 +17,9 @@ export class CountryCity extends OrganisationsSeoIndexabilityPolicy {
17
17
  async generateUrls() {
18
18
  const cityFragments = await CityPresenter
19
19
  .getInstance(this.dependencies.searchApiClient)
20
- .getFragments();
20
+ .getFragmentsAsync();
21
21
  const paths = [];
22
- console.warn(`Total city fragments fetched: ${cityFragments.length}`);
23
- console.warn('Fetched city fragments for CountryCity policy:', JSON.stringify(cityFragments));
24
22
  const filteredFragmentsForCountryCity = cityFragments.filter(city => city.areaId === null || city.areaId === undefined);
25
- console.warn(`Filtered down to ${filteredFragmentsForCountryCity.length} city fragments for CountryCity policy.`);
26
- console.warn('Filtered city fragments for CountryCity policy:', JSON.stringify(filteredFragmentsForCountryCity));
27
23
  for (const city of filteredFragmentsForCountryCity) {
28
24
  const filterKeyValues = new Map([
29
25
  [FilterKey.CITY, [city.id]],
@@ -34,8 +30,6 @@ export class CountryCity extends OrganisationsSeoIndexabilityPolicy {
34
30
  paths.push(this.getPathWithSortingOption(city.path));
35
31
  }
36
32
  }
37
- console.warn(`Generated ${paths.length} URLs for CountryCity policy.`);
38
- console.warn('Generated URLs:', JSON.stringify(paths));
39
33
  return paths;
40
34
  }
41
35
  get filterCombination() {
@@ -0,0 +1,11 @@
1
+ import { OnlyFiltersSelectedRule } from "../../../common/rules/OnlyFiltersSelectedRule";
2
+ import { SingleValueSelectedForFilterRule } from "../../../common/rules/SingleValueSelectedForFilterRule";
3
+ import { RankedOrganisationsSeoIndexabilityPolicy } from "../RankedOrganisationsSeoIndexabilityPolicy";
4
+ import { FilterCombinations } from '../../../enums/FilterCombinations';
5
+ export declare class RankedCountryAreaCity extends RankedOrganisationsSeoIndexabilityPolicy {
6
+ readonly name: string;
7
+ readonly description: string;
8
+ protected readonly baseRules: (SingleValueSelectedForFilterRule | OnlyFiltersSelectedRule)[];
9
+ protected generateUrls(): Promise<string[]>;
10
+ get filterCombination(): FilterCombinations;
11
+ }
@@ -0,0 +1,42 @@
1
+ import { OnlyFiltersSelectedRule } from "../../../common/rules/OnlyFiltersSelectedRule";
2
+ import { SingleValueSelectedForFilterRule } from "../../../common/rules/SingleValueSelectedForFilterRule";
3
+ import { FilterKey } from "@studyportals/search-filters/server-side";
4
+ import { RankedOrganisationsSeoIndexabilityPolicy } from "../RankedOrganisationsSeoIndexabilityPolicy";
5
+ import { FilterCombinations } from '../../../enums/FilterCombinations';
6
+ import { CityPresenter } from "../../../presenters/CityPresenter";
7
+ export class RankedCountryAreaCity extends RankedOrganisationsSeoIndexabilityPolicy {
8
+ name = 'Ranked Country Area City Policy';
9
+ description = 'Manages indexability of ranking-sorted country and area-specific city pages, applying rules to prioritize regions where quality-based sorting adds search value.';
10
+ baseRules = [
11
+ new SingleValueSelectedForFilterRule(FilterKey.CITY),
12
+ new SingleValueSelectedForFilterRule(FilterKey.COUNTRY),
13
+ new OnlyFiltersSelectedRule([FilterKey.CITY, FilterKey.COUNTRY])
14
+ ];
15
+ async generateUrls() {
16
+ const cityFragments = await CityPresenter
17
+ .getInstance(this.dependencies.searchApiClient)
18
+ .getFragmentsAsync();
19
+ const paths = [];
20
+ console.warn(`Total city fragments fetched: ${cityFragments.length}`);
21
+ const filteredFragmentsForCountryAreaCity = cityFragments.filter(city => city.areaId !== null && city.areaId !== undefined);
22
+ for (const city of filteredFragmentsForCountryAreaCity) {
23
+ const areaId = city.areaId?.toString() || '';
24
+ if (areaId === '') {
25
+ continue;
26
+ }
27
+ const filterKeyValues = new Map([
28
+ [FilterKey.CITY, [city.id]],
29
+ [FilterKey.AREA, [areaId]],
30
+ [FilterKey.COUNTRY, [city.countryId]]
31
+ ]);
32
+ const result = await this.checkRulesForSitemap(filterKeyValues);
33
+ if (result) {
34
+ paths.push(this.getPathWithSortingOption(city.path));
35
+ }
36
+ }
37
+ return paths;
38
+ }
39
+ get filterCombination() {
40
+ return FilterCombinations.RANKED_COUNTRY_AREA_CITY;
41
+ }
42
+ }
@@ -0,0 +1,11 @@
1
+ import { OnlyFiltersSelectedRule } from "../../../common/rules/OnlyFiltersSelectedRule";
2
+ import { SingleValueSelectedForFilterRule } from "../../../common/rules/SingleValueSelectedForFilterRule";
3
+ import { RankedOrganisationsSeoIndexabilityPolicy } from "../RankedOrganisationsSeoIndexabilityPolicy";
4
+ import { FilterCombinations } from '../../../enums/FilterCombinations';
5
+ export declare class RankedCountryCity extends RankedOrganisationsSeoIndexabilityPolicy {
6
+ readonly name: string;
7
+ readonly description: string;
8
+ protected readonly baseRules: (SingleValueSelectedForFilterRule | OnlyFiltersSelectedRule)[];
9
+ protected generateUrls(): Promise<string[]>;
10
+ get filterCombination(): FilterCombinations;
11
+ }
@@ -0,0 +1,36 @@
1
+ import { OnlyFiltersSelectedRule } from "../../../common/rules/OnlyFiltersSelectedRule";
2
+ import { SingleValueSelectedForFilterRule } from "../../../common/rules/SingleValueSelectedForFilterRule";
3
+ import { FilterKey } from "@studyportals/search-filters/server-side";
4
+ import { RankedOrganisationsSeoIndexabilityPolicy } from "../RankedOrganisationsSeoIndexabilityPolicy";
5
+ import { FilterCombinations } from '../../../enums/FilterCombinations';
6
+ import { CityPresenter } from "../../../presenters/CityPresenter";
7
+ export class RankedCountryCity extends RankedOrganisationsSeoIndexabilityPolicy {
8
+ name = 'Ranked Country City Policy';
9
+ description = 'Manages indexability of ranking-sorted country and city-specific pages, applying rules to prioritize regions where quality-based sorting adds search value.';
10
+ baseRules = [
11
+ new SingleValueSelectedForFilterRule(FilterKey.CITY),
12
+ new SingleValueSelectedForFilterRule(FilterKey.COUNTRY),
13
+ new OnlyFiltersSelectedRule([FilterKey.CITY, FilterKey.COUNTRY])
14
+ ];
15
+ async generateUrls() {
16
+ const cityFragments = await CityPresenter
17
+ .getInstance(this.dependencies.searchApiClient)
18
+ .getFragmentsAsync();
19
+ const paths = [];
20
+ const filteredFragmentsForCountryCity = cityFragments.filter(city => city.areaId === null || city.areaId === undefined);
21
+ for (const city of filteredFragmentsForCountryCity) {
22
+ const filterKeyValues = new Map([
23
+ [FilterKey.CITY, [city.id]],
24
+ [FilterKey.COUNTRY, [city.countryId]]
25
+ ]);
26
+ const result = await this.checkRulesForSitemap(filterKeyValues);
27
+ if (result) {
28
+ paths.push(this.getPathWithSortingOption(city.path));
29
+ }
30
+ }
31
+ return paths;
32
+ }
33
+ get filterCombination() {
34
+ return FilterCombinations.RANKED_COUNTRY_CITY;
35
+ }
36
+ }
@@ -1,14 +1,15 @@
1
1
  import { FilterKey } from '@studyportals/search-filters/server-side';
2
2
  import { ICityFragment } from './fragments/ICityFragment';
3
- import { ISearchApiClient } from '../../sitemap-generator-seo';
4
- export declare class CityPresenter {
3
+ import { IPresenter, ISearchApiClient } from '../../sitemap-generator-seo';
4
+ export declare class CityPresenter implements IPresenter {
5
5
  readonly filterKey: FilterKey;
6
6
  private searchApiClient;
7
7
  private static instance;
8
8
  private constructor();
9
9
  private fragmentsPromise;
10
10
  static getInstance(searchApiClient: ISearchApiClient): CityPresenter;
11
- getFragments(): Promise<ICityFragment[]>;
11
+ getFragments(): ICityFragment[];
12
+ getFragmentsAsync(): Promise<ICityFragment[]>;
12
13
  private fetchCities;
13
14
  private processCity;
14
15
  }
@@ -16,7 +16,10 @@ export class CityPresenter {
16
16
  }
17
17
  return CityPresenter.instance;
18
18
  }
19
- async getFragments() {
19
+ getFragments() {
20
+ return [];
21
+ }
22
+ async getFragmentsAsync() {
20
23
  if (!this.fragmentsPromise) {
21
24
  this.fragmentsPromise = this.fetchCities();
22
25
  }
@@ -8,6 +8,9 @@ import { CountryPresenter } from '../presenters/CountryPresenter';
8
8
  import { DisciplinePresenter } from '../presenters/DisciplinePresenter';
9
9
  import { FilterKey } from '@studyportals/search-filters/server-side';
10
10
  import { CountryAreaCity } from '../organisations/policies/our-picks/CountryAreaCity';
11
+ import { RankedCountryCity } from '../organisations/policies/ranked/RankedCountryCity';
12
+ import { RankedCountryAreaCity } from '../organisations/policies/ranked/RankedCountryAreaCity';
13
+ import { CityPresenter } from '../presenters/CityPresenter';
11
14
  export class OrganisationsSitemapUrlGeneratorManager extends BaseSitemapUrlGeneratorManager {
12
15
  portalType;
13
16
  policies;
@@ -38,6 +41,8 @@ export class OrganisationsSitemapUrlGeneratorManager extends BaseSitemapUrlGener
38
41
  new RankedAttendanceDiscipline(dependencies),
39
42
  new RankedContinent(dependencies),
40
43
  new RankedContinentAttendance(dependencies),
44
+ new RankedCountryCity(dependencies),
45
+ new RankedCountryAreaCity(dependencies),
41
46
  new RankedCountry(dependencies),
42
47
  new RankedCountryAttendance(dependencies),
43
48
  new RankedCountryDiscipline(dependencies),
@@ -49,6 +54,7 @@ export class OrganisationsSitemapUrlGeneratorManager extends BaseSitemapUrlGener
49
54
  this.presenters.set(FilterKey.CONTINENT, ContinentPresenter.getInstance());
50
55
  this.presenters.set(FilterKey.COUNTRY, CountryPresenter.getInstance());
51
56
  this.presenters.set(FilterKey.DISCIPLINES, DisciplinePresenter.getInstance());
57
+ this.presenters.set(FilterKey.CITY, CityPresenter.getInstance(searchApiClient));
52
58
  this.setFilterKeyValueCounts();
53
59
  }
54
60
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@studyportals/fawkes",
3
- "version": "8.2.4-15",
3
+ "version": "8.2.4-17",
4
4
  "description": "A package to centralize SEO related logic for SBLP and Sitemap Generator.",
5
5
  "files": [
6
6
  "./dist"