To bootstrap our application we have a configuration stored in a JSON file. This config holds different settings, like definition of endpoints (e. g. an API or SMS gateway etc.) or some default values for login.
endpoint.ts
export const isEndpoint = (candidate: any): candidate is Endpoint => {
return (
candidate && typeof candidate === 'object' &&
candidate.hasOwnProperty('protocol') && (candidate.protocol === null || ['http', 'https'].includes(candidate.protocol)) &&
candidate.hasOwnProperty('address') && (candidate.address === null || typeof candidate.address === 'string') &&
candidate.hasOwnProperty('port') && (candidate.port === null || typeof candidate.port === 'number') &&
candidate.hasOwnProperty('path') && (candidate.path === null || typeof candidate.path === 'string')
);
}
export interface Endpoint {
protocol: 'http'|'https'|null,
address: string|null,
port: number|null,
path: string|null,
}
configutation.ts
import { Endpoint, isEndpoint } from './endpoint';
export const isConfiguration = (candidate: any): candidate is Configuration => {
return (
candidate && typeof candidate === 'object' &&
candidate.hasOwnProperty('endpoints') &&
candidate.endpoints.hasOwnProperty('api') && isEndpoint(candidate.endpoints.api) &&
candidate.endpoints.hasOwnProperty('smsGateway') && isEndpoint(candidate.endpoints.smsGateway) &&
candidate.hasOwnProperty('auth') &&
candidate.auth.hasOwnProperty('company') && (candidate.auth.company === null || typeof candidate.auth.company === 'string')
);
}
export interface Configuration {
endpoints: {
api: Endpoint,
smsGateway: Endpoint
},
auth: {
company: string|null
}
}
In the end the code is used in an Angular service like this:
get configuration$(): Observable<Configuration> {
if (this.configutation$) {
return this.configutation$;
}
const path = environment.configuration;
this.configutation$ = this.http.get<any>(path).pipe(
map(configutation => {
if (isConfigutation(configutation)) {
return configutation;
}
throw new Error('Configutation is broken.');
}),
shareReplay(1),
catchError(error => {
console.log(error);
return of(null);
})
);
return this.configutation$;
}
Now the isEndpoint
and isConfiguration
methos seems very cumbersome, especially when the configuration grows. I was thinking of using JSON schema and validate the input against the schema file. But if somebody replaces that file for example during build, then it still could go wrong.
Can this be improved?
Is this the TypeScrpt way or how could this be improved?