Reputation: 1200
I am trying to verify that some properties exist on a configuration object and have values (truthy? not necessrily as a few pointed out in the comments) in javascript in the following manner:
const verifyJanrainAppSettings = (options) => {
return options.JanrainAppSettings
&& options.JanrainAppSettings.settings.tokenUrl
&& options.JanrainAppSettings.settings.capture.clientId
&& options.JanrainAppSettings.settings.capture.appId
&& options.JanrainAppSettings.settings.capture.appDomain
&& options.JanrainAppSettings.settings.capture.captureServer
&& options.JanrainAppSettings.settings.httpLoadUrl
&& options.JanrainAppSettings.settings.httpsLoadUrl
}
I feel this is too verbose and I am curious if there is a better pattern. Perhaps something like
if (['my', 'options'] in myObject) ...
Upvotes: 10
Views: 5318
Reputation: 53
I would opt for an utility function:
function contains(object, ...keys) {
return keys.every((key) => key in object)
}
const verifyJanrainAppSettings = (options) => {
return (
contains(options.JanrainAppSettings, 'settings') &&
contains(
options.JanrainAppSettings.settings,
'tokenUrl',
'httpLoadUrl',
'httpsLoadUrl',
'capture'
) &&
contains(
options.JanrainAppSettings.settings.capture,
'clientId',
'appId',
'appDomain',
'captureServer'
)
)
}
Upvotes: 3
Reputation: 31682
I would use a schema-like object that contains all the keys that need to exist in the object and recursively check if an object contains those keys or not using a function like so:
const verifyObject = (object, schema) =>
Object.entries(schema).every(([key, value]) =>
object && object[key] && (typeof value != "object" || verifyObject(object[key], value))
);
The object[key]
test will not only verify that the key exists but it will also verify that the value for that key is truthy which may fail the whole test if a value contains a falsy value. If you only want it to check if the key exists, then replace it with key in object
.
The schema-like object for you JanrainAppSettings
object is simply an object containing all the keys that needs to exists in a valid JanrainAppSettings
, the values could be just 0
s or empty strings as they are not needed:
const janrainAppSettingsSchema = { JanrainAppSettings: { settings: { capture: { clientId: "", appId: "", appDomain: "", captureServer: "" }, tokenUrl: "", httpLoadUrl: "", httpsLoadUrl: "" } } };
So to verify an object, just call verifyObject
and pass the object and the schema-like object to verify against like so:
if(verifyObject(options, janrainAppSettingsSchema)) {
// everything is ok
}
Demo:
const verifyObject = (object, schema) =>
Object.entries(schema).every(([key, value]) =>
object && object[key] && (typeof value != "object" || verifyObject(object[key], value))
);
let thisWillPass = {
JanrainAppSettings: {
settings: {
capture: {
clientId: "Id",
appId: "Id",
appDomain: "Domain",
captureServer: "Server"
},
tokenUrl: "Url",
httpLoadUrl: "Url",
httpsLoadUrl: "Url"
}
}
};
let thisWontPass = {
JanrainAppSettings: {
settings: {
capture: {
clientId: "Id",
appId: "Id",
// appDomain: "Domain",
captureServer: "Server"
},
tokenUrl: "Url",
httpLoadUrl: "Url",
httpsLoadUrl: "Url"
}
}
};
const janrainAppSettingsSchema = { JanrainAppSettings: { settings: { capture: { clientId: "", appId: "", appDomain: "", captureServer: "" }, tokenUrl: "", httpLoadUrl: "", httpsLoadUrl: "" } } };
console.log("Will pass:", verifyObject(thisWillPass, janrainAppSettingsSchema));
console.log("Won't pass:", verifyObject(thisWontPass, janrainAppSettingsSchema));
Upvotes: 0
Reputation: 48590
You could create a helper function that takes a scope and var-arg properties.
const allSet = (scope, ...properties) =>
scope && (!properties || properties.every(key => !key || key in scope))
It would check the scope, along with the presence of all the potential falsy properties.
const sampleOptions = {
JanrainAppSettings: {
settings: {
tokenUrl: '$tokenUrl',
capture: {
clientId: '$clientId',
appId: '$appId',
appDomain: '$appDomain',
captureServer: '$captureServer'
},
httpLoadUrl: '$httpLoadUrl',
httpsLoadUrl: '$httpsLoadUrl'
}
}
}
const allSet = (scope, ...properties) =>
scope && (!properties || properties.every(key => !key || key in scope))
const verifyJanrainAppSettings = (options) =>
allSet(options, 'JanrainAppSettings') &&
allSet(options.JanrainAppSettings, 'settings') &&
allSet(options.JanrainAppSettings.settings, 'tokenUrl', 'httpLoadUrl', 'httpsLoadUrl') &&
allSet(options.JanrainAppSettings.settings.capture, 'clientId', 'appId', 'captureServer')
console.log(verifyJanrainAppSettings(sampleOptions))
Upvotes: 0
Reputation: 83517
Perhaps something like if (['my', 'options'] in myObject) ...
This is the right idea. However, you need to loop over the array of strings and check if each of them is in myObject
. You can do this loop with Array.every()
. The only disadvantage is this only works at the deepest level. Since you have several levels of nesting here it will still get kind of ugly:
const verifyJanrainAppSettings = (options) => {
return options.JanrainAppSettings
&& options.JanrainAppSettings.settings // dont' forget this check
&& ['tokenUrl', 'httpLoadUrl', 'httpsLoadUrl', 'capture'].every(key => // added options.JanrainAppSettings.settings.capture check
key in options.JanrainAppSettings.settings
)
&& ['clientId', 'appId', 'appDomain', 'captureServer'].every(key =>
key in options.JanrainAppSettings.settings.capture
);
}
Upvotes: 2
Reputation: 1073968
You can keep a reference to settings
, which improves things a fair bit:
const verifyJanrainAppSettings = (options) => {
const settings = options.JanrainAppSettings && options.JanrainAppSettings.settings;
return settings &&
settings.tokenUrl &&
settings.capture && // ** This was missing in the question, but I think you need it
settings.capture.clientId &&
settings.capture.appId &&
settings.capture.appDomain &&
settings.capture.captureServer &&
settings.httpLoadUrl &&
settings.httpsLoadUrl;
};
While you could put some of those names in an array and use a loop, I think I'd leave it at that.
With optional chaining (new in ES2020), that first line could be:
const settings = options.JanrainAppSettings?.settings;
The array version would look something like:
const settingsProps = "capture tokenUrl httpLoadUrl httpsLoadUrl".split(" ");
const captureProps = "clientId appId appDomain captureServer".split(" ");
const verifyJanrainAppSettings = (options) => {
const settings = options.JanrainAppSettings && options.JanrainAppSettings.settings;
return settings &&
settingsProps.every(prop => settings[prop]) &&
captureProps.every(prop => settings.capture[prop]);
};
adiga asks a good question. If you want existence rather than truthiness (you have truthiness in your question), you'll want in
(or for an own property check, hasOwnProperty
). For instance:
const settingsProps = "capture tokenUrl httpLoadUrl httpsLoadUrl".split(" ");
const captureProps = "clientId appId appDomain captureServer".split(" ");
const verifyJanrainAppSettings = (options) => {
const settings = options.JanrainAppSettings && options.JanrainAppSettings.settings;
return settings &&
settingsProps.every(prop => prop in settings) &&
captureProps.every(prop => prop in settings.capture);
};
Upvotes: 8