Skip to content

Commit

Permalink
Merge pull request #799 from christophercr/feature/fix-aot-session-mo…
Browse files Browse the repository at this point in the history
…dule

refactor(stark-core): refactor logic to validate StarkSessionConfig to prevent Angular AOT error "Function calls are not supported in decorators"
  • Loading branch information
christophercr authored Oct 25, 2018
2 parents 3812ed1 + ea8c18c commit c061045
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 38 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*tslint:disable:completed-docs no-undefined-argument*/
/*tslint:disable:completed-docs no-big-function*/
import { HttpHeaders, HttpRequest } from "@angular/common/http";
import { EventEmitter, Injector } from "@angular/core";
import { DEFAULT_INTERRUPTSOURCES, Idle, InterruptSource } from "@ng-idle/core";
Expand Down Expand Up @@ -36,9 +36,8 @@ import { MockStarkRoutingService } from "../../routing/testing";
import { StarkCoreApplicationState } from "../../../common/store";
import Spy = jasmine.Spy;
import SpyObj = jasmine.SpyObj;
import { starkSessionExpiredStateName } from "../routes";
import { starkAppExitStateName, starkAppInitStateName, starkSessionExpiredStateName } from "../routes";

// tslint:disable-next-line:no-big-function
describe("Service: StarkSessionService", () => {
let mockStore: SpyObj<Store<StarkCoreApplicationState>>;
let appConfig: StarkApplicationConfig;
Expand All @@ -64,7 +63,7 @@ describe("Service: StarkSessionService", () => {
roles: ["a role", "another role", "yet another role"]
};
const mockSessionConfig: StarkSessionConfig = {
sessionExpiredStateName: "mock-session-expired-state-name"
sessionExpiredStateName: starkAppExitStateName + ".mock-session-expired-state-name"
};

// Inject module dependencies
Expand Down Expand Up @@ -142,6 +141,67 @@ describe("Service: StarkSessionService", () => {
}
});

it("should throw an error in case the session config object contains invalid initial states", () => {
const invalidSessionConfigValues: StarkSessionConfig[] = [
{ loginStateName: "someLoginState" },
{ loginStateName: "" },
{ loginStateName: starkAppInitStateName },
{ loginStateName: starkAppInitStateName + "." },
{ loginStateName: starkAppExitStateName + ".someLoginState" },
{ preloadingStateName: "somePreloadingState" },
{ preloadingStateName: "" },
{ preloadingStateName: starkAppInitStateName },
{ preloadingStateName: starkAppInitStateName + "." },
{ preloadingStateName: starkAppExitStateName + ".somePreloadingState" }
];

for (const invalidSessionConfig of invalidSessionConfigValues) {
expect(() => {
return new SessionServiceHelper(
mockStore,
mockLogger,
mockRoutingService,
appConfig,
mockIdleService,
mockInjectorService,
mockTranslateService,
invalidSessionConfig
);
}).toThrowError(/invalid StarkSessionConfig(.*)initial state/);
}
});

it("should throw an error in case the session config object contains invalid exit states", () => {
const invalidSessionConfigValues: StarkSessionConfig[] = [
{ sessionExpiredStateName: "someSessionExpiredState" },
{ sessionExpiredStateName: "" },
{ sessionExpiredStateName: starkAppExitStateName },
{ sessionExpiredStateName: starkAppExitStateName + "." },
{ sessionExpiredStateName: starkAppInitStateName + ".someSessionExpiredState" },
{ sessionLogoutStateName: "someSessionExpiredState" },
{ sessionLogoutStateName: "" },
{ sessionLogoutStateName: starkAppExitStateName },
{ sessionLogoutStateName: starkAppExitStateName + "." },
{ sessionLogoutStateName: starkAppInitStateName + ".someSessionExpiredState" }
];

for (const invalidSessionConfig of invalidSessionConfigValues) {
expect(
() =>
new SessionServiceHelper(
mockStore,
mockLogger,
mockRoutingService,
appConfig,
mockIdleService,
mockInjectorService,
mockTranslateService,
invalidSessionConfig
)
).toThrowError(/invalid StarkSessionConfig(.*)exit state/);
}
});

it("should throw an error in case the warning period in the app config is invalid", () => {
const invalidSessionTimeoutWarningPeriodValues: number[] = [<any>undefined, -1];

Expand Down Expand Up @@ -374,8 +434,6 @@ describe("Service: StarkSessionService", () => {
});
});

// FIXME rewrite those tests to reduce function
/* tslint:disable-next-line:no-big-function */
describe("configureIdleService", () => {
it("should set the necessary options of the idle service", () => {
const interruptsToBeSet: InterruptSource[] = DEFAULT_INTERRUPTSOURCES;
Expand Down Expand Up @@ -865,6 +923,7 @@ describe("Service: StarkSessionService", () => {
});

it("should return an empty map if the pre-authentication headers were not constructed", () => {
/* tslint:disable-next-line:no-undefined-argument */
sessionService.setInternalDevAuthenticationHeaders(undefined);

const devAuthenticationHeaders: Map<string, string> = sessionService.devAuthenticationHeaders;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ export class StarkSessionServiceImpl implements StarkSessionService {
// ensuring that the app config is valid before doing anything
StarkConfigurationUtil.validateConfig(this.appConfig, ["session"], starkSessionServiceName);

// ensuring that the session config is valid before doing anything
if (this.sessionConfig) {
this.validateSessionConfig(this.sessionConfig);
}

if (this.idle.getKeepaliveEnabled() && !this.appConfig.keepAliveDisabled) {
this.keepalive = injector.get<Keepalive>(Keepalive);
}
Expand Down Expand Up @@ -102,6 +107,49 @@ export class StarkSessionServiceImpl implements StarkSessionService {
this.logger.debug(starkSessionServiceName + " loaded");
}

/**
* Validates the StarkSessionConfig provided.
* @param customConfig - Custom configuration object passed via the StarkSessionModule.forRoot() method
* @throws In case the configuration object passed via the StarkSessionModule.forRoot() method is not valid
*/
protected validateSessionConfig(customConfig: StarkSessionConfig): void {
const invalidConfigErrorPrefix: string = starkSessionServiceName + ": invalid StarkSessionConfig object. ";
const invalidConfigErrorAppInitSuffix: string =
" should have the prefix '" + starkAppInitStateName + ".' in order to be configured correctly as an application initial state";
const invalidConfigErrorAppExitSuffix: string =
" should have the prefix '" + starkAppExitStateName + ".' in order to be configured correctly as an application exit state";

// validate config to ensure that the init/exit states have the correct StarkAppInit or StarkAppExit parent
if (
typeof customConfig.loginStateName !== "undefined" &&
(!customConfig.loginStateName.startsWith(starkAppInitStateName) ||
customConfig.loginStateName.replace(starkAppInitStateName, "").length < 2)
) {
throw new Error(invalidConfigErrorPrefix + "'loginStateName' value" + invalidConfigErrorAppInitSuffix);
}
if (
typeof customConfig.preloadingStateName !== "undefined" &&
(!customConfig.preloadingStateName.startsWith(starkAppInitStateName) ||
customConfig.preloadingStateName.replace(starkAppInitStateName, "").length < 2)
) {
throw new Error(invalidConfigErrorPrefix + "'preloadingStateName' value" + invalidConfigErrorAppInitSuffix);
}
if (
typeof customConfig.sessionExpiredStateName !== "undefined" &&
(!customConfig.sessionExpiredStateName.startsWith(starkAppExitStateName) ||
customConfig.sessionExpiredStateName.replace(starkAppExitStateName, "").length < 2)
) {
throw new Error(invalidConfigErrorPrefix + "'sessionExpiredStateName' value" + invalidConfigErrorAppExitSuffix);
}
if (
typeof customConfig.sessionLogoutStateName !== "undefined" &&
(!customConfig.sessionLogoutStateName.startsWith(starkAppExitStateName) ||
customConfig.sessionLogoutStateName.replace(starkAppExitStateName, "").length < 2)
) {
throw new Error(invalidConfigErrorPrefix + "'sessionLogoutStateName' value" + invalidConfigErrorAppExitSuffix);
}
}

protected registerTransitionHook(): void {
this.routingService.addKnownNavigationRejectionCause(starkUnauthenticatedUserError);

Expand Down
33 changes: 1 addition & 32 deletions packages/stark-core/src/modules/session/session.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,6 @@ import { starkSessionReducers } from "./reducers";
import { StarkSessionConfig, STARK_SESSION_CONFIG } from "./entities";
import { STARK_SESSION_SERVICE, StarkSessionServiceImpl } from "./services";
import { StarkUserModule } from "../user/user.module";
import { starkAppExitStateName, starkAppInitStateName } from "./routes";

/**
* Validates and creates the StarkSessionConfig to be provided for the Stark Session Service
* @param customConfig - Custom configuration object passed via the StarkSessionModule.forRoot() method
* @returns The StarkSessionConfig to be provided for the Stark Session Service.
* @throws In case the configuration object passed via the StarkSessionModule.forRoot() method is not valid
*/
export function starkSessionConfigFactory(customConfig: StarkSessionConfig): StarkSessionConfig {
const invalidConfigErrorPrefix: string = "StarkSessionModule: invalid StarkSessionConfig object. ";
const invalidConfigErrorAppInitSuffix: string =
" should have the prefix '" + starkAppInitStateName + ".' in order to be configured correctly as an application initial state";
const invalidConfigErrorAppExitSuffix: string =
" should have the prefix '" + starkAppExitStateName + ".' in order to be configured correctly as an application exit state";

// validate config to ensure that the init/exit states have the correct StarkAppInit or StarkAppExit parent
if (customConfig.loginStateName && !customConfig.loginStateName.startsWith(starkAppInitStateName)) {
throw new Error(invalidConfigErrorPrefix + "'loginStateName' value" + invalidConfigErrorAppInitSuffix);
}
if (customConfig.preloadingStateName && !customConfig.preloadingStateName.startsWith(starkAppInitStateName)) {
throw new Error(invalidConfigErrorPrefix + "'preloadingStateName' value" + invalidConfigErrorAppInitSuffix);
}
if (customConfig.sessionExpiredStateName && !customConfig.sessionExpiredStateName.startsWith(starkAppExitStateName)) {
throw new Error(invalidConfigErrorPrefix + "'sessionExpiredStateName' value" + invalidConfigErrorAppExitSuffix);
}
if (customConfig.sessionLogoutStateName && !customConfig.sessionLogoutStateName.startsWith(starkAppExitStateName)) {
throw new Error(invalidConfigErrorPrefix + "'sessionLogoutStateName' value" + invalidConfigErrorAppExitSuffix);
}

return customConfig;
}

@NgModule({
imports: [StoreModule.forFeature("StarkSession", starkSessionReducers), StarkUserModule]
Expand All @@ -52,7 +21,7 @@ export class StarkSessionModule {
ngModule: StarkSessionModule,
providers: [
{ provide: STARK_SESSION_SERVICE, useClass: StarkSessionServiceImpl },
sessionConfig ? { provide: STARK_SESSION_CONFIG, useValue: starkSessionConfigFactory(sessionConfig) } : []
sessionConfig ? { provide: STARK_SESSION_CONFIG, useValue: sessionConfig } : []
]
};
}
Expand Down

0 comments on commit c061045

Please sign in to comment.