@byu-oit/cartographer
v0.4.2
Published
The art or technique of making maps
Downloads
841
Maintainers
Keywords
Readme
Introduction
This utility provides a helpful interface for converting Javascript objects into new forms.
Install
npm i @byu-oit/cartographer
Usage
Basic Usage
import { Cartographer } from '@byu-oit/cartographer'
// Configure the Cartographer how you want
const cartographer = new Cartographer().cpy('y', 'x')
// Get data that you want to be mapped
const sourceData1 = { x: '1' }
const sourceData2 = { x: '2' }
// use your configured Cartographer to map any number of sources
const result1 = cartographer.run(sourceData1)
// expected result: { y: '1' }
const result2 = cartographer.run(sourceData2)
// expected result: { y: '2' }
Documentation
Basic Functionality
set
: sets the first parameter in the result object to the second parameter (This is static)
const cartographer = new Cartographer().set('To', 'Value')
// Output : { To: 'Value' }
cpy
: sets the first parameter in the result object to the REFERENCE of the second parameter
const cartographer = new Cartographer().cpy('To', 'reference')
// Input : { reference: 'Value' }
// Output : { To: 'Value' }
Additional Functionality
Because a lot of data needs functionality more complex than just setting and copying we have added these additional features:
- Conditions
- Default Value
- Transformers
- Lookups / Dictionaries
- Other Predefined Transformers (Pick & Select)
- Using Cartographer to create Configuration Files
All of these features can be added to the basic set and cpy methods using options
import { Cartographer, Transformers } from '@byu-oit/cartographer'
const condtion = new Expression({ operands: [{ value: 1 }, { value: 1 }], operator: Operators.EQ })
const lookup = Transformers.Lookup({ dictionaryName: 'dictionaryName'})
const cartographer = new Cartographer().cpy('To', 'Value', {condtion: condtion, transformers: [lookup], defaultValue : "NO VALUE FOUND"})
// Lookup is a predefined transformer that uses dictionaries
Conditions
We use the BYU poliblob package for all Condition Logic Visit Poliblob Repo
- It should be noted that the Reference in the poliblob expressions can reference both the source and the result in the cartographer.
- Source Reference Example:
const condition = new Condition().reference('path.in.source').equals(1).build()
- Result Reference Example ($result):
const condtion = new Condition().reference('$result.path.in.result').equals(1).build()
- $result must be used to access result
- Source Reference Example:
- If the expression evaluates to true then the value is set otherwise it will not be set at all
Default Value
When the default value should be set:
- If the reference is not found then it will set it to the default value
- If the transformers return a null value
Transformers
Transformers are functions that you can register with the cartographer to call them in your configuration using an id. Your resolved reference or value will then be ran through the function updating the value that will be set.
- If an array is given then the transformer will be run on each element in the array.
- Your transformers must be registered with the Cartographer system.
- The transformers are ran sequentially (order matters)
- Transformers can access the Cartographer Object, Result, and Source if configured correctly.
export interface CartographerRunContext {
source: unknown
result: unknown
}
(this: Cartographer, value: unknown, options: Options, context?: CartographerRunContext): unknown
If you do not need to access the Cartographer Instance or the context then you do not need to specify those parameters in your function.
Here is a basic transformer that does not need access to the cartographer or the context
import { Static, Type } from '@sinclair/typebox'
import { TransformerRegistry } from '../../src/registry/index.js'
import * as System from '../../src/system/system.js'
export const Schema = Type.Object({
incrementBy: Type.Number({ description: 'The amount numbers will be incremented by' })
}, {
description: 'The increment transformer options'
})
export type Model = Static<typeof Schema>
export const Name = 'increment'
export const Func: TransformerRegistry.Transformer<Model> = function increment (value, { incrementBy }) {
if (typeof value === 'number') {
return value + incrementBy
} else {
return value
}
}
Here is a contrived example of a transformer using context
export const Schema = Type.Object({
path: Type.String({ description: 'The path in the result that you are looking for' })
}, {
description: 'The resultSearch transformer options'
})
export type Model = Static<typeof Schema>
export const Name = 'result'
export const Func: TransformerRegistry.Transformer<Model> = function result (value, options, context) {
if (context != null) {
return get(context.result, options.path)
}
}
const Config = { transformer: Func, args: Schema }
Transformers must be registered to the Cartographer using this system call of the Cartographer
import { Transformer } from '@byu-oit/cartographer/system'
Transformer<typeof Schema>(Name, { transformer: Func, args: Schema })
Lookups / Dictionaries
Mapping values to corresponding values is a very common task and because of this we have add functionality to have lookups and dictionaries in the cartographer
- Dictionaries are registered similar to how transformers are registered
- You define the id of the dictionary in the system call and then use that value in your configuration to have values looked up
const numberDictionary: DictionaryRegistry.Dictionary<string, number> = new Map<string, number>([
['one', 1],
['two', 2]
])
System.Dictionary('stringToNumber', numberDictionary)
const source = {
y: 'one'
}
const lookup = Transformers.Lookup({ dictionaryName: 'stringToNumber'})
const cartographer = new Cartographer().cpy('x', 'y', { transformers: [lookup] })
result = cartographer.run(source)
// result = { x: 1 }
Parameters:
- dictionaryName : The name of the dictionary that has been registered with the cartographer
- throwErr : If true this transformer will throw and error if it does not find a value in the mapping
- defaultValue : The value that will be set if it is not found in the map.
- If this value is not set then the transformer will return the original value
- reference : The path of an object to return from the lookup
- If your dictionary is a dictionary of objects then you can use the reference to return a property from that object
Other Predefined Transformers (Pick & Select)
Pick
// source = {x: { a: 'one', b: 'two' } }
const cartographer = new Cartographer().trf('y', 'x', Transformers.Pick({ properties: ['a'] }))
// { y : { a : 'one' } }
Given an object this transformer will return the specified properties from that object
Parameters:
- properties : an array of strings of all the properties that you want to be returned
Select
// source = {x: { a: { b: 'two' } } }
const cartographer = new Cartographer().trf('y', 'x', Transformers.Select({ path: 'a.b' }))
// { y : 'two' }
Given a path this transformer will return the value at the given path. If it does not find a value then it will return the default value.
Parameters:
- path : a string of the path of the value that you want returned
- defaultValue : the value that you want returned if the path value resolves to undefined
Using Cartographer to create Configuration Files
One of the main features of the Cartographer is that it can be converted completely to and from JSON. The toJson
method
serializes the configuration to be stored or transferred statically (e.g. exposing the cartographer via API or storing
configurations in a database). Additionally, the from
constructor validates the configuration, or throws and error if
the configuration could not be parsed.
- Once you configure the cartographer instance how you want then you can call
toJson
and it will convert that cartographer instance to a json object - Using that json object you can create a new instance of your cartographer with the same configurations using the
from
method
const cartographer = new Cartographer()
const configuration = cartographer.toJson()
const newCartographer = Cartographer.from(JSON.parse(configuration))
Global Settings
const cartographer = new Cartographer({
transformers: [
Transformers.Lookup({ dictionaryName: 'stringToNumber', reference: 'value' })
]
})
// Every operation that is ran with this cartographer object will now add this transformer
you can set global transformations and conditions that are run on everything.
- Condition:
- If both a global condition and a local condition are set then both must be true for the value to be set
- Transformer:
- Global transformers are ran in order after the local transformers have finished
Helper Methods
The methods below are used as shortcuts
trf
trl
toObject
toJson
from
trl
trl
: shorthand for writing a cpy that has a lookup
trl (to: string, from: string, dictionary: string, options?: SetOptions & Omit<LookupOptions, 'dictionaryName'>) => this
const cartographer = new Cartographer()
.trl('to', 'from', 'spanishToEnglish', { throwErr: true, defaultValue: null })
trf
trf
: shorthand for writing a cpy with transformers attached to it.
trf (to: string, from: string, ...transformers: TransformerModel[]): => this
const cartographer = new Cartographer().trf('to', 'from', Transformers.Pick({ properties: ['value'] }))
toObject
toObject
: Returns the cartographer as an object
toObject (): => ConfigurationModel
const cartographer = new Cartographer().toObject()
toJson
toJson
: Returns the cartographer as a string
toJson (): => string
const cartographer = new Cartographer().toJson()
from
from
: Returns the cartographer as a string
from (config : unknown): => Cartographer
const cartographer = new Cartographer().from(configuration)