Skip to content

Commit befe2dc

Browse files
authored
Bug fix/config service (#411)
* fix(test): fixed missed refactors and tests * refactor(bad-merge): there was a bad merge and mistake naming
1 parent 95c8df9 commit befe2dc

28 files changed

Lines changed: 143 additions & 158 deletions

src/app/app.component.spec.ts

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
88
import { By } from '@angular/platform-browser';
99
import { NavigationEnd, Router } from '@angular/router';
1010

11-
import { OSFConfigService } from '@core/services/osf-config.service';
11+
import { ENVIRONMENT } from '@core/provider/environment.provider';
1212
import { GetCurrentUser, UserState } from '@core/store/user';
1313
import { UserEmailsState } from '@core/store/user-emails';
1414

@@ -22,7 +22,6 @@ import { GoogleTagManagerService } from 'angular-google-tag-manager';
2222
describe('Component: App', () => {
2323
let routerEvents$: Subject<any>;
2424
let gtmServiceMock: jest.Mocked<GoogleTagManagerService>;
25-
let osfConfigServiceMock: OSFConfigService;
2625
let fixture: ComponentFixture<AppComponent>;
2726

2827
beforeEach(async () => {
@@ -48,16 +47,9 @@ describe('Component: App', () => {
4847
events: routerEvents$.asObservable(),
4948
},
5049
},
51-
{
52-
provide: OSFConfigService,
53-
useValue: {
54-
has: jest.fn(),
55-
},
56-
},
5750
],
5851
}).compileComponents();
5952

60-
osfConfigServiceMock = TestBed.inject(OSFConfigService);
6153
fixture = TestBed.createComponent(AppComponent);
6254
});
6355

@@ -81,7 +73,6 @@ describe('Component: App', () => {
8173

8274
describe('Google Tag Manager', () => {
8375
it('should push GTM tag on NavigationEnd with google tag id', () => {
84-
jest.spyOn(osfConfigServiceMock, 'has').mockReturnValue(true);
8576
fixture.detectChanges();
8677
const event = new NavigationEnd(1, '/previous', '/current');
8778

@@ -93,8 +84,9 @@ describe('Component: App', () => {
9384
});
9485
});
9586

96-
it('should not push GTM tag on NavigationEnd with google tag id', () => {
97-
jest.spyOn(osfConfigServiceMock, 'has').mockReturnValue(false);
87+
it('should not push GTM tag on NavigationEnd without google tag id', () => {
88+
const environment = TestBed.inject(ENVIRONMENT);
89+
environment.googleTagManagerId = '';
9890
fixture.detectChanges();
9991
const event = new NavigationEnd(1, '/previous', '/current');
10092

src/app/app.component.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { ChangeDetectionStrategy, Component, DestroyRef, effect, inject, OnInit
1010
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
1111
import { NavigationEnd, Router, RouterOutlet } from '@angular/router';
1212

13-
import { OSFConfigService } from '@core/services/osf-config.service';
13+
import { ENVIRONMENT } from '@core/provider/environment.provider';
1414
import { GetCurrentUser } from '@core/store/user';
1515
import { GetEmails, UserEmailsSelectors } from '@core/store/user-emails';
1616
import { ConfirmEmailComponent } from '@shared/components';
@@ -34,7 +34,7 @@ export class AppComponent implements OnInit {
3434
private readonly dialogService = inject(DialogService);
3535
private readonly router = inject(Router);
3636
private readonly translateService = inject(TranslateService);
37-
private readonly osfConfigService = inject(OSFConfigService);
37+
private readonly environment = inject(ENVIRONMENT);
3838

3939
private readonly actions = createDispatchMap({ getCurrentUser: GetCurrentUser, getEmails: GetEmails });
4040

@@ -52,7 +52,7 @@ export class AppComponent implements OnInit {
5252
this.actions.getCurrentUser();
5353
this.actions.getEmails();
5454

55-
if (this.osfConfigService.has('googleTagManagerId')) {
55+
if (this.environment.googleTagManagerId) {
5656
this.router.events
5757
.pipe(
5858
filter((event) => event instanceof NavigationEnd),

src/app/app.config.ts

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,14 @@ import { ConfirmationService, MessageService } from 'primeng/api';
77
import { providePrimeNG } from 'primeng/config';
88

99
import { provideHttpClient, withInterceptors } from '@angular/common/http';
10-
import {
11-
ApplicationConfig,
12-
ErrorHandler,
13-
importProvidersFrom,
14-
PLATFORM_ID,
15-
provideZoneChangeDetection,
16-
} from '@angular/core';
10+
import { ApplicationConfig, ErrorHandler, importProvidersFrom, provideZoneChangeDetection } from '@angular/core';
1711
import { provideAnimations } from '@angular/platform-browser/animations';
1812
import { provideRouter, withInMemoryScrolling } from '@angular/router';
1913

2014
import { STATES } from '@core/constants';
21-
import { APPLICATION_INITIALIZATION_PROVIDER } from '@core/factory/application.initialization.factory';
22-
import { SENTRY_PROVIDER } from '@core/factory/sentry.factory';
23-
import { WINDOW, windowFactory } from '@core/factory/window.factory';
2415
import { provideTranslation } from '@core/helpers';
16+
import { APPLICATION_INITIALIZATION_PROVIDER } from '@core/provider/application.initialization.provider';
17+
import { SENTRY_PROVIDER } from '@core/provider/sentry.provider';
2518

2619
import { authInterceptor, errorInterceptor, viewOnlyInterceptor } from './core/interceptors';
2720
import CustomPreset from './core/theme/custom-preset';
@@ -53,20 +46,6 @@ export const appConfig: ApplicationConfig = {
5346
},
5447
}),
5548
provideHttpClient(withInterceptors([authInterceptor, viewOnlyInterceptor, errorInterceptor])),
56-
importProvidersFrom(TranslateModule.forRoot(provideTranslation())),
57-
ConfirmationService,
58-
MessageService,
59-
60-
APPLICATION_INITIALIZATION_PROVIDER,
61-
{
62-
provide: ErrorHandler,
63-
useFactory: () => Sentry.createErrorHandler({ showDialog: false }),
64-
},
65-
{
66-
provide: WINDOW,
67-
useFactory: windowFactory,
68-
deps: [PLATFORM_ID],
69-
},
7049
provideRouter(routes, withInMemoryScrolling({ scrollPositionRestoration: 'top', anchorScrolling: 'enabled' })),
7150
provideStore(STATES, withNgxsReduxDevtoolsPlugin({ disabled: false })),
7251
provideZoneChangeDetection({ eventCoalescing: true }),

src/app/core/factory/environment.factory.ts

Lines changed: 0 additions & 22 deletions
This file was deleted.

src/app/core/factory/window.factory.spec.ts

Lines changed: 0 additions & 32 deletions
This file was deleted.

src/app/core/factory/window.factory.ts

Lines changed: 0 additions & 26 deletions
This file was deleted.

src/app/core/factory/application.initialization.factory.spec.ts renamed to src/app/core/provider/application.initialization.provider.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import { TestBed } from '@angular/core/testing';
44

55
import { OSFConfigService } from '@core/services/osf-config.service';
66

7-
import { initializeApplication } from './application.initialization.factory';
8-
import { ENVIRONMENT } from './environment.factory';
7+
import { initializeApplication } from './application.initialization.provider';
8+
import { ENVIRONMENT } from './environment.provider';
99

1010
import * as Sentry from '@sentry/angular';
1111
import { OSFTestingModule } from '@testing/osf.testing.module';
@@ -16,7 +16,7 @@ jest.mock('@sentry/angular', () => ({
1616
createErrorHandler: jest.fn(() => 'mockErrorHandler'),
1717
}));
1818

19-
describe('factory: sentry', () => {
19+
describe('Provider: sentry', () => {
2020
let osfConfigServiceMock: OSFConfigService;
2121
let googleTagManagerConfigurationMock: GoogleTagManagerConfiguration;
2222
let httpMock: HttpTestingController;

src/app/core/factory/application.initialization.factory.ts renamed to src/app/core/provider/application.initialization.provider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { inject, provideAppInitializer } from '@angular/core';
22

33
import { OSFConfigService } from '@core/services/osf-config.service';
44

5-
import { ENVIRONMENT } from './environment.factory';
5+
import { ENVIRONMENT } from './environment.provider';
66

77
import * as Sentry from '@sentry/angular';
88
import { GoogleTagManagerConfiguration } from 'angular-google-tag-manager';

src/app/core/factory/environment.factory.spec.ts renamed to src/app/core/provider/environment.provider.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import { TestBed } from '@angular/core/testing';
22

33
import { EnvironmentModel } from '@osf/shared/models/environment.model';
44

5-
import { ENVIRONMENT } from './environment.factory';
5+
import { ENVIRONMENT } from './environment.provider';
66

77
import { OSFTestingModule } from '@testing/osf.testing.module';
88

9-
describe('Factory: Environment', () => {
9+
describe('Provider: Environment', () => {
1010
let environment: EnvironmentModel;
1111

1212
beforeEach(async () => {
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { inject, InjectionToken } from '@angular/core';
2+
3+
import { ENVIRONMENT_DO_NO_USE } from '@core/constants/environment.token';
4+
import { EnvironmentModel } from '@osf/shared/models/environment.model';
5+
6+
/**
7+
* `ENVIRONMENT` is an Angular `InjectionToken` that provides a **runtime-mutable proxy**
8+
* over the application's static environment configuration.
9+
*
10+
* This factory wraps the base environment config (`ENVIRONMENT_DO_NO_USE`) in a `Proxy`
11+
* so values can be accessed and overridden at runtime while preserving type safety
12+
* based on the `EnvironmentModel` interface.
13+
*
14+
* ## Key Features:
15+
* - Provides type-safe access to environment variables (e.g. `apiDomainUrl`, `recaptchaSiteKey`)
16+
* - Supports **runtime modification** of values (e.g. `environment.featureFlag = true`)
17+
* - Works seamlessly with Angular dependency injection
18+
*
19+
* @example
20+
* ```ts
21+
* const env = inject(ENVIRONMENT);
22+
* console.log(env.apiDomainUrl);
23+
* env.apiDomainUrl = 'https://dev.example.com'; // Override at runtime
24+
* ```
25+
*
26+
* @see EnvironmentModel for a complete list of available keys.
27+
* @see ENVIRONMENT_DO_NO_USE for the static base config (not modifiable).
28+
*/
29+
export const ENVIRONMENT = new InjectionToken<EnvironmentModel>('EnvironmentProxy', {
30+
providedIn: 'root',
31+
factory: () => {
32+
const environment = inject(ENVIRONMENT_DO_NO_USE);
33+
34+
return new Proxy<EnvironmentModel>(
35+
{ ...environment },
36+
{
37+
get: (target, prop: keyof EnvironmentModel) => target[prop],
38+
set: <K extends keyof EnvironmentModel>(target: EnvironmentModel, prop: K, value: EnvironmentModel[K]) => {
39+
target[prop] = value;
40+
return true;
41+
},
42+
}
43+
);
44+
},
45+
});

0 commit comments

Comments
 (0)