Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance configuration handling to aid public/midas consolidation #31

Open
wants to merge 7 commits into
base: angular-18-new
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@
"demos/people-service/src/assets"
],
"styles": [
"demos/people-service/src/styles.css"
"demos/people-service/src/styles.css",
"node_modules/primeng/resources/primeng.css",
"node_modules/primeicons/primeicons.css",
"node_modules/primeflex/primeflex.css",
Expand Down
11 changes: 11 additions & 0 deletions libs/oarng/src/lib/config/config.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ describe('ConfigurationService', () => {
// Mock configuration object
const mockConfig: Configuration = {
PDRDMP: 'https://oardev.nist.gov/od/ds/rpa',
daptool: {
editEnabled: true
}
};

beforeEach(() => {
Expand Down Expand Up @@ -68,6 +71,14 @@ describe('ConfigurationService', () => {
expect(actualConfig['PDRDMP']).toEqual(mockConfig['PDRDMP']);
});

it('should return configuration parameter', () => {
service.config = mockConfig;
expect(service.get('PDRDMP', 'unconfigured')).toEqual(mockConfig['PDRDMP']);
expect(service.get('daptool', 'unconfigured')).toEqual(mockConfig['daptool']);
expect(service.get('daptool.editEnabled')).toBe(true);
expect(service.get('daptool.endpoint', 'unconfigured')).toBe('unconfigured');
});

})


Expand Down
23 changes: 22 additions & 1 deletion libs/oarng/src/lib/config/config.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Observable, Subject, throwError } from "rxjs";
import { catchError, tap } from "rxjs/operators";

import { Configuration, ReleaseInfo, RELEASE_INFO, CONFIG_URL } from "./config.model";
import { NamedParameters, AnyObj } from "../data/namedparams";
import { environment } from "../../environments/environment";

/**
Expand Down Expand Up @@ -36,7 +37,7 @@ export class ConfigurationService {
this.config.componentRelease = this.relInfo

let r: ReleaseInfo|undefined = this.config.componentRelease;
let msg = "app configuration loaded for ";
let msg = "app configuration loaded";
if (r) msg += " for "+r.component+", version "+r.version
console.log(msg);
}
Expand Down Expand Up @@ -71,6 +72,26 @@ export class ConfigurationService {
return (this.config ?? { }) as T;
}

/**
* extract a named parameter from the (already loaded) configuration data using its
* hierarchical (dot-delimited) name.
*
* This function accomplishes two things: first, it provides a bit of syntactic
* sugar for getting at deep values in the parameter hierarchy. That is,
* `cfg.get("links.orgHome")` is equivalent to `cfg.getConfig()["links"]["orgHome"]`.
*
* The second bit of functionality is the optional parameter that allows the caller
* to set the default value to return if the value is not set. If the stored value
* is null or undefined, the default value is returned.
*
* @param param the name of the desired parameter
* @param default the default value to return if a parameter with the given name
* is not set or is null.
*/
get<T>(param: string, defval?: T | null): T | null | undefined {
return (new NamedParameters(this.getConfig<AnyObj>())).get(param, defval);
}

/**
* Handle the HTTP errors.
* @param error The error object.
Expand Down
12 changes: 12 additions & 0 deletions libs/oarng/src/lib/data/data.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Utilities for managing various data structures
*/
import { NgModule } from '@angular/core';

import { NamedParameters } from './namedparams';
import { AnyObj } from './types';

@NgModule({})
export class DataModule { }

export { NamedParameters, AnyObj }
45 changes: 45 additions & 0 deletions libs/oarng/src/lib/data/namedparams.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { NamedParameters, AnyObj } from "./namedparams";

describe("NamedParameters", function() {
let data : AnyObj = {
"links": {
orgHome: "https://data.nist.gov/",
rabbitHoles: {
endless: "/endless",
deadend: "/endless/deadend"
}
}
};

it("get()", function() {
let p: NamedParameters = new NamedParameters(data);
debugger;
expect(p.data).toBe(data);
expect(p.get("links", "unconfigured")).toBe(data["links"]);
expect(p.get("apis", "unconfigured")).toBe("unconfigured");
expect(p.get("apis")).toBe(undefined);
expect(p.get("links.orgHome", "unconfigured")).toBe("https://data.nist.gov/");
expect(p.get("links.rabbitHoles.deadend", "unconfigured")).toBe("/endless/deadend");
expect(p.get("links.rabbitHoles.conspiracy", "unconfigured")).toBe("unconfigured");
});

it("construction-time defaults", function() {
let defs = {
links: {
orgHome: "https://google.com",
portalHome: "/sdp",
rabbitHoles: { }
},
apis: false
}
let p: NamedParameters = new NamedParameters(data, defs);
expect(p.data).toBe(data);
expect(p.get("links", "unconfigured")).toBe(data["links"]);
expect(p.get("apis", "unconfigured")).toBe("unconfigured");
expect(p.get("apis")).toBe(false);
expect(p.get("links.orgHome", "unconfigured")).toBe("https://data.nist.gov/");
expect(p.get("links.portalHome")).toBe("/sdp");
expect(p.get("links.rabbitHoles.deadend", "unconfigured")).toBe("/endless/deadend");
expect(p.get("links.rabbitHoles.conspiracy", "unconfigured")).toBe("unconfigured");
});
});
63 changes: 63 additions & 0 deletions libs/oarng/src/lib/data/namedparams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { AnyObj } from './types';

/**
* A wrapper class around data stored in a hierarchical object that eases access. It provides two
* key features:
* * a get() function for accessing items deep in the hierarcy via dot-delimited name
* * the ability to specify a default if the parameter is not set.
*/
export class NamedParameters {

data: AnyObj;
_defs: NamedParameters|null = null;

/**
* wrap a data object
* @param params the data object to wrap
* @param defParams a data object that provides default values to return if a parameter
* is not set in params _and_ a default is not provided to the get()
* function.
*/
constructor(params: AnyObj, defParams?: AnyObj) {
this.data = params;
if (defParams)
this._defs = new NamedParameters(defParams);
}

/**
* get hierarchical values by name with an option to request a default value.
*
* This function accomplishes two things: first, it provides a bit of syntactic
* sugar for getting at deep values in the parameter hierarchy. That is,
* `params.get("links.orgHome")` is equivalent to `params.data["links"]["orgHome"]`.
*
* The second bit of functionality is the optional parameter that allows the caller
* to set the default value to return if the value is not set. If the stored value
* is null or undefined, the default value is returned.
*
* @param param the name of the desired parameter
* @param default the default value to return if a parameter with the given name
* is not set or is null. This overrides any default set at
* construction time. Do not set or set to undefined to explicity
* defer to a construction-time default.
*/
get<T>(param: string, defval?: T | null): T | null | undefined {
let names: string[] = param.split(".");
let val: any = this.data;
for (var i = 0; i < names.length; i++) {
if (typeof val != "object") {
val = null;
break;
}
val = val[names[i]];
}
if (val == null || val == undefined) {
if (defval == undefined && this._defs)
defval = this._defs.get(param);
val = defval;
}
return val;
}
}

export { AnyObj }
9 changes: 9 additions & 0 deletions libs/oarng/src/lib/data/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* exportable type definitions
*/

/**
* an object that can be empty or contain parameters of any names and types
*/
export type AnyObj = { [paramName: string]: any }

1 change: 1 addition & 0 deletions libs/oarng/src/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ export * from './lib/auth/auth';
export * from './lib/config/config.module';
export * from './lib/auth/auth.module';
export * from './lib/staffdir/staffdir.module';
export * from './lib/data/data.module';