@beacon/utils
v3.0.8
Published

Readme
Utils
Generic utility functions for Beacon. Can be used browser and client side.
Local dev
Want to try a new util or a new version of a util in another repo that depends on @beacon/utils?
# in this repo:
yarn && yarn build
cd .build && yarn link
# in the target repo, where you want to consume @beacon/utils:
yarn link @beacon/utilsAnd to go back to the version from npm:
# in this repo:
yarn unlink
# in the target:
yarn unlink @beacon/utilsInstallation
yarn add @beacon/utilsUsage
Each util should be imported specifically, like so:
Node.js
const firstName = require("@beacon/utils/firstName");
console.log(firstName("Frodo Baggins"));
// FrodoBrowser
import firstName from "@beacon/utils/firstName";
console.log(firstName("Frodo Baggins"));
// FrodoLocal development
If you're working on the utils locally, you can use the link command to link the package to your local environment.
# utils
yarn build
cd .build && yarn link
# target repo
yarn link @beacon/utilsDocs
Each exciting method documnented in full below!
Want to see the code? Or the unit tests? You'll find them in the /lib and /test directories.
auStateCodes
An array of all Australian states.
[{ code: 'ACT', name: 'Australian Capital Territory' }, ...]billingFeatures
A list of all of the billing feature flags we support, and the plans/elements they correspond to.
billingPlans
An object containing starter, standard, and premium keys. Each object provides a core definition for each plan. Used by the other billing utils.
canRunEntityExport
Returns true if a user can run an entity export, false otherwise. User must have at least read access to all fields.
canRunEntityExport({
entityType,
entityExportTemplate, // optional. If provided, only needs to be able to read the fields in the template.
allEntityTypes,
permissions,
isAdmin, // boolean
}); // truecleanFilterConditions
Given an array of entity filter conditions, remove the conditions where ignore: true is set on them, and also omit the id (legacy) and ignore keys from each condition object.
Works at all levels of nesting. Added because it's possible for a filter condition to be "blank" when being configured in the UI, but we want to ignore blank conditions when running queries and saving filters to things like charts, workflows, etc.
const filterConditions = [{
field: 'name.first',
operator: 'contains',
value: '',
ignore: true,
}, {
field: 'date_of_birth',
operator: '==',
value: 'this_year',
ignore: false,
}];
const result = cleanFilterConditions(filterConditions);
// [{
// field: 'date_of_birth',
// operator: '==',
// value: 'this_year',
//}]closePopup
Close a popup window/tab that was previously opened. Browser-only.
closePopup(popup);countDecimalPlaces
Count the number of decimal places in a number.
countDecimalPlaces(5.233); // 3countryCodeThreeLetter
A mapping of all two letter country codes to their corresponding three letter iso codes
countryCodes
An array of all of the countries in the world. Useful for lists in forms.
console.log(countryCodes); // [{ code: 'GB', name: 'United Kingdom' }, ...]Currently, the French locale is also supported:
console.log(countryCodes); // [{ code: 'GB', name: 'Royaume-Uni' }, ...]countryDialCodes
A list of all of the country dialling codes in the world.
const countryDialCodes = require("@beacon/utils/countryDialCodes");
console.log(countryDialCodes); // [{ country_code: 'GB', dial_code: '+44' }, ...]countryEmojiFlags
An object mapping of every country two letter iso code, to their corresponding flag in emoji format.
currencyCodes
A list of all of the currency codes in the world. Used under the hood in getCurrencySymbol;, currencySymbol;, and currencyFormat;
console.log(currencyCodes); // [{ isoCode: 'GBP', unicode: 'a3', locale: 'en', name: 'United Kingdom Pound' }]decodeCharRefs
Decode hex unicode character references to their core... thing. Honestly I have no idea how this works but it's used internally in getCurrencySymbol.
detectDateFormat
Detect a date format from a date string. Defaults to detection in UK formats, but a second optional isUSDate property can be passed.
Uses moment under the hood.
detectDateFormat("04/05/2018"); // DD/MM/YYYY
detectDateFormat("01-03-20 13:30", "DD-MM-YY HH:mm");
detectDateFormat("04/05/2018", true); // MM/DD/YYYYThe format returned can be used in moment to parse a date string according to that format:
const format = detectDateFormat("04/05/2018"); // DD/MM/YYYY;
const m = moment("04/05/2018", format);Optionally, a third preferredDateFormat parameter can be passed. If passed, this format will be checked first, and if it matches, then no other formats will be checked. (Useful if you're detecting across very large CSV files like the import validator does!)
doesEntityMatchSmartFieldRule
Util for rules-based smart fields. Return true if an entity matches a given smart field "rule", false otherwise.
rule objects must follow the standard smart field rule structure.
// Usage
doesEntityMatchSmartFieldRule({
rule,
entity,
references,
allEntityTypes,
});
// Example:
doesEntityMatchSmartFieldRule({
rule: {
conditions: [
{
field_id: 62,
operator: "contains",
value: "Donation",
},
],
},
entity,
references,
allEntityTypes,
}); // truedynamicDateValues
An array of dynamic date values (e.g. this_year) that can be used in entity filter conditions.
elements
An object containing element definitions for all elements.
expandZapierPayloadReferences
Function expects an entity and associated references array alongside all entity
types for the account. Function will find each reference type field on the given
entity and expand it from a pointer like [ 123 ] to a corresponding set of
objects like [{ id: 123, name: 'Person Name', email: '[email protected]' }].
If the reference field can point to more than one entity types, function will
handle all in the metadata.entity_types array and work with them.
If the reference field pointer is one-to-many (like [123, 456, 789]), function
will handle all corresponding items in the references array to pull data from.
If there is no item in the references array corresponding to current reference
field entity type, function will set a null value template expansion for utility
like [{ id: null, name: null, email: null }].
The null value template is provided for use in the Zapier UI, where a payload may be used either as a source of data OR as a configuration template for future 'zap' executions (e.g. user can view recent invocations and use items in the payload to configure actions, paths etc.).
The template expansion will comprise the folowing properties:
- always
id,entity_type_id,entity_type_keyand[<entity_type_key>] - under
[<entity_type_key>], in case of a person entity type we providename(full name) andemail(primary); in all other cases[<primary_field>]
const entity = {
id: 101,
customer: [100],
fundraiser: [],
};
const references = [
{
entity: {
id: 100,
name: {
full: "Haydn Appleby",
last: "Appleby",
first: "Haydn",
middle: null,
prefix: null,
},
emails: [
{
email: "[email protected]",
is_primary: true,
},
],
},
},
];
const result = expandZapierPayloadReferences({
entity,
references,
allEntityTypes,
});
/**
{
id: 101,
customer: [{
id: 100,
entity_type_id: 987,
entity_type_key: 'person',
person: {
name: 'Haydn Appleby',
email: '[email protected]',
}
}],
fundraiser: [{
id: null,
entity_type_id: 987,
entity_type_key: 'person',
person: {
name: null,
email: null,
}
},{
id: 101,
entity_type_id: 654,
entity_type_key: 'organization',
organization: {
name: 'Beacon CRM',
}
}]
}
*/findIndexes
Finds all indexes for a given pattern in a string, and return them as an array.
findIndexes("my name is chris", " "); // [2, 7, 10]
findIndexes("my name is chris, my name is big dave", "my"); // [0, 18]firstName
Return the first word within an string that isn't a "name prefix" (e.g. Mr).
firstName("Chris Houghton"); // Chris
firstName("Mr Chris Houghton"); // Chris
firstName("Mr. Chris Houghton"); // Chris
firstName("Prof Dr Chris Houghton"); // ChrisgetContactPointIdByValue
Returns the contactPointId for a specific contact type (emails, phone numbers, or address) on a person entity.
const contactPointId = getContactPointIdByValue({
personEntity,
contactPointFieldType: 'emails',
value: '[email protected]',
});
// => '019b2d14-9700-75e1-8a0a-89883f5abac6'getContactPointsForNewEntity
Returns an object with contact points of the new object.
Useful when you need to get contactPointIds for a person entity that has just been upserted. Eg draftEntity is the mapped entity before upserting, publishedEntity is the returned entity which has been populated with contactPointIds.
draftEntity is optional; if you omit it, the contact point fields of a published entity will be returned.
// Simplified example usage within apps
const draftEntity = {
name: { first: 'John', last: 'Tester' },
emails: [{ email: '[email protected]' }],
// and other fields
}
const { ids: personIds } = await api.bulkUpsertEntities({
type: 'person',
primary_field_key: ['emails', 'name'],
entities: [draftEntity],
update_policy: 'merge_and_skip',
});
const { entity: publishedEntity } = await api.readEntity({
type: 'person',
id: personIds[0]
});
const contactPoints = getContactPointsForNewEntity({
draftEntity,
publishedEntity
});
// Returns:
// {
// emails: [{
// email: '[email protected]',
// contact_point_id: '019b2d14-9700-75e1-8a0a-89883f5abac6'
// }],
// phone_numbers: [],
// address: [],
// }formatEntityForEmailTemplate
Given an entity object, and it's references, format it into a format that can be added directly into an email template.
Information like accountUsers and allEntityTypes are passed as they are relevant to the data that gets added.
The locale affects date formats (powered by moment.js).
formatEntityForEmailTemplate({
entity,
references = [],
accountUsers,
allEntityTypes,
locale = 'en_GB',
});formatPhoneNumber
formatUKPostcode
Format a UK postcode into the standard format. It automatically does the following:
- trims whitespace
- uppercases
- adds a space in the middle
formatUKPostcode("sw112ae"); // SW11 2AE
formatUKPostcode("SW112AE"); // SW11 2AE
formatUKPostcode("sw11 2Ae "); // SW11 2AEgetBillingPlans
Returns an object containing starter, standard, and premium keys. Each object provides a core definition for each plan. Used by the other billing utils.
Requires a version number to return the billing plans for a specific pricing model version.
getBillingPlans(2);getBillingPrices
For a given plan, number of contacts, and billing frequency, return the prices as shown on the pricing page. This automatically applies an annual discount to all prices.
Optional parameters:
discountPercent- discount everything by this percentage (in addition to any annual discount)annualDiscountPercent- defaults to10freeCustomFields- override the number of allowed free custom fields on the planbilling- override the standard prices (only ifplanmatchesbilling.plan). Overrides annual discount regardless of plan.
getBillingPrices({
plan: "starter",
contacts: 2000,
frequency: "annual",
// annualDiscountPercent: 20, // optional. default 10
// discountPercent: 15, // optional. in addition to annual discount
});
// => {
// base: 91,
// elements: [
// { key: 'fundraising', price: 24.5 },
// { key: 'finance', price: 12.5 },
// { key: 'ticketing', price: 12.5 },
// { key: 'email_integrations', price: 3.75 },
// { key: 'memberships', price: 17 },
// { key: 'volunteering', price: 10 },
// { key: 'case_management', price: 10 }
// ],
// limit_increases: [
// {
// key: 'custom_fields',
// number_free: 25,
// unit_cost: 25,
// step: 25,
// maximum: 100
// },
// {
// key: 'users',
// number_free: 3,
// unit_cost: 20,
// step: 1,
// maximum: 10
// }
// ]
// }getBillingEstimate
Calculate how much Beacon should cost a customer, based on their contacts, plan, elements, limit increases, billing frequency, and custom discount percent.
Optional parameters:
annualDiscountPercent- override the default10annual discount percent. Will be overridden ifbillingis passed.discountPercent- discount everything by this percentage (in addition to any annual discount)billing- override the standard prices (ifplanmatchesbilling.plan, andbilling.versionmatchesversion). Annual discount overrides regardless of plan.
getBillingEstimate({
contacts: 50000,
plan: "premium",
version: 2,
elements: ["fundraising", "finance", "workflows", "case_management"],
limitIncreases: [
{
key: "custom_fields",
quantity: 2,
},
],
frequency: "monthly",
// annualDiscountPercent: 20, // optional
// discountPercent: 15, // optional
// billing: {...} // optional
});
// => {
// base: 495,
// elements: [
// { key: 'fundraising', price: 136 },
// { key: 'finance', price: 68 },
// { key: 'workflows', price: 136 },
// { key: 'case_management', price: 40 }
// ],
// limit_increases: [ { key: 'custom_fields', quantity: 2, price: 100 } ],
// total: 975
// }getBillingFeatures
Get a list of all the billing feature flags, and the plans/elements they correspond to. Takes a version argument to return the billing features for a specific pricing model version.
const features = getBillingFeatures(2);
// => [
// { key: 'developer_api', title: 'Developer API', elements: ['core'], plans: ['standard', 'premium', 'ultimate'] }
// etc...
// ]hasCustomBillingPrices
Given a billing object, return true if billing prices have been customised (compared to the standard billing plans), false otherwise.
const isCustom = hasCustomBillingPrices(billing);
// => truegetCountryCode
For a given string containing either an ISO country code, an official country name, or an unofficial country name, return the two-letter country code for it.
Case and whitespace insensitive. Really useful for saying "given a string, get the country for it". (Like in CSV imports)
getCountryCode("GB"); // GB
getCountryCode(" gb"); // GB
getCountryCode("United Kingdom"); // GB
getCountryCode(" united STates "); // US
getCountryCode("United States of America"); // `US` (unofficial name)
getCountryCode("Hong Kong"); // `HK` (unofficial name)getCountryNameFromCode
Given a two letter ISO country code (e.g. GB, US), return the name of that country.
getCountryNameFromCode("GB"); // United Kingdom
getCountryNameFromCode("US"); // United States
getCountryNameFromCode("CHRIS"); // undefinedgetCurrencySymbol
Given a 3 letter ISO currency code (e.g. GBP, USD), return a symbol that can be used for it. If you're using this util in the frontend, be sure to pass the encode: false option.
I don't think we have a single place using the encoded version, but it's often a requirement for server side usage.
getCurrencySymbol("GBP"); // £
getCurrencySymbol("GBP", { encode: false }); // £
getCurrencySymbol("USD", { encode: false }); // $currencyFormat
Given a number value and a 3 letter ISO currency code (e.g. GBP, USD), default of 'GBP', return formatted number with the appropiate currency symbol at the correct position (start or end).
It uses the locales so it will also use the correct spacing for that country (see below Poland Zloty uses spaces (' ') instead of commas (',').
currencyFormat({ value: 100000 }); // £100,000
currencyFormat({ value: 100000, nDecimalDigits: 2 }); // £100,000.00
currencyFormat({ value: 100000, currency: "PLN" }); // 100 000 złcurrencySymbol
Given a 3 letter ISO currency code (e.g. GBP, USD), return a symbol that can be used for it (this function never returns an encoded version). This function is different to get currency symbol as it uses toLocaleString and gets the currency symbol based on a locale. It seems to be more accurate for certain currencies
e.g. Poland Zolty
currencySymbol = zł (this is correct)
getCurrencySymbol = z (this is wrong)currencySymbol("GBP"); // £
currencySymbol("USD"); // $
currencySymbol("PLN"); // złenforcePrecision
ported from moutjs
Enforce a specific amount of decimal digits and also fix floating point rounding issues.
Example:
enforcePrecision(0.615, 2); // 0.62
enforcePrecision(0.625, 2); // 0.63
//floating point rounding "error" (rounds to odd number)
+(0.615).toFixed(2); // 0.61
+(0.625).toFixed(2); // 0.63getDefaultStaticData
Get default static data. Used by Beacon apps.
getDefaultStaticData({
idealStaticData,
entityType,
});getDependentSmartFields
For a given smart field, return an array of all of the fields that depend on it. This method is recursive, so it will also return fields that depend on fields that depend on this field.
getDependentSmartFields({
field, // smart field
entityType,
});
// => [{ ... }]getDirectories
Get a list of directories at a given path. Node.js only.
getDirectories(`${__dirname}/sampledirectory`); // ['anothersub', 'sub']getDomainFromEmail
Given an email address, get the domain from it.
getDomainFromEmail("[email protected]"); // beaconcrm.orggetDomainFromUrl
Given a URL or website address, return the top level domain for it.
getDomainFromUrl("https://www.facebook.com/test"); // facebook.com
getDomainFromUrl("www.beaconcrm.org"); // beaconcrm.org
getDomainFromUrl("beaconcrm.org"); // beaconcrm.orggetElementName
Return a human friendly name for an element, given an element "key" (e.g. fundraising).
getElementName("roles_and_permissions");
// => "Roles & permissions"getElementFeatures
Return an array of element feature flags for a given element. Optionally, a plan can be passed to only return the features available as standard on a given plan.
getElementFeatures({
element: "roles_and_permissions",
});
// => ['feature_permissions', 'entity_type_permissions', 'field_permissions']
getElementFeatures({
element: "roles_and_permissions",
plan: "standard",
});
// => ['feature_permissions', 'entity_type_permissions']getLimitIncreaseName
For a given limit increase key (e.g. "custom_fields"), return a human friendly name for that limit increase ("Custom fields").
getLimitIncreaseName("custom_fields");
// => "Custom fields"getEntityFieldPermission
For a given fieldId, return the permission that the user has for that field. Possible return values are:
write- user can edit this fieldread- user can read this field (also applies to core platform fields, e.g.created_at)forbidden- user cannot read or edit this field
getEntityFieldPermission({
fieldId, // integer
entityTypeId, // integer
isAdmin, // boolean
permissions,
});getEntityTitle
Return the "title" for a given entity, in a basic format. Work in progress.
getEntityTitle({
entity, // entity to get the title for
references, // array of references. Defaults to []
allEntityTypes, // list of all entity types
fallback, // fallback if no title is available. Defaults to "Unknown"
});getEntityTypeFieldIds
Return an array of field ids for a given entity type. Optionally, a second options object can be passed, to exclude specific field ids or keys.
getEntityTypeFieldIds(entityType); // [1, 2, 3, ...]
getEntityTypeFieldIds(entityType, {
exclude: [1, 2],
}); // [3, ...]
getEntityTypeFieldIds(entityType, {
excludeByParam: {
is_read_only: true, // Removes all read only fields
}
})getEntityTypeInboundReferenceFields
Return an array of fields that point at a given entity type.
getEntityTypeInboundReferenceFields({
entityTypeId, // the entity type being referenced
allEntityTypes,
fromEntityTypeId, // (optional) only return fields _from_ a particular type
includeRollupFields = false, // include first/last reference rollup fields? (don't by default)
});getFeatureElements
For a given billing feature (e.g. xero_app), return the element keys (e.g. ['finance']) that the feature belongs to. All features are owned by one or more elements.
For core features, core is returned.
getFeatureElement("xero_app");
// => ['finance']
getFeatureElement("custom_entity_types");
// => ['core']getFeatureLimit
Return the limit that should be applied for a given feature (e.g. workflows).
getFeatureLimit("workflows", billing);
getFeatureLimit("users", billing); // 10getFieldIdsInFilterConditions
Returns an array of all field ids found in an array of filter conditions. Useful for knowing field dependencies.
getFieldIdsInFilterConditions({
entityTypeId, // id of entity type being filtered
filterConditions, // array of the filter conditions
allEntityTypes, // all entity types in the db
});getEntityTypeIdsInFilterConditions
Returns an array of all entity type ids found in an array of filter conditions. Useful for knowing entity type dependencies.
getEntityTypeIdsInFilterConditions({
entityTypeId, // id of entity type being filtered
filterConditions, // array of the filter conditions
allEntityTypes, // all entity types in the db
});getFieldsInSmartFieldRules
Returns an array of all of the fields used in the rules of a given smart field.
IMPORTANT: this only returns the fields on the entity type that the smart field belongs to - not the ones on the records pointed at.
getFieldsInSmartFieldRules({
field, // smart field
entityType,
});
// => [{ ... }]getFieldsInAllSmartFieldRules
Returns an array of all of the fields used in any of rules-based smart fields for a given entity type.
IMPORTANT: this only returns the fields on the entity type that the smart field belongs to - not the ones on the records pointed at.
getFieldsInAllSmartFieldRules({
entityType,
});
// => [{ ... }]getFieldsInSmartFieldTemplate
Returns an array of all of the fields used in the template of a template-based smart field.
getFieldsInSmartFieldTemplate({
field, // smart field
entityType,
});
// => [{ ... }]getFieldsInAllSmartFieldTemplates
Returns an array of all of the fields used in the templates of all template-based smart fields for a given entity type.
getFieldsInAllSmartFieldTemplates({
entityType,
});
// => [{ ... }]getRemoteFieldsInSmartFieldTemplate
Returns an array of all of the remote fields used in a smart field template, for a given outbound referenceField.
You'll want to run this if a reference field is returned in getFieldsInSmartFieldTemplate - to see if we need to go deeper at all.
getRemoteFieldsInSmartFieldTemplate({
field, // smart field
referenceField, // that exists in the smart field
allEntityTypes,
});
// => [{ ... }]getFilesInDirectory
Node.js only. Get a list of all of the files in a given directory. Optionally, an second options parameter can be passed, with the following parameters:
recursive: (boolean) set totrueif you'd like all files in sub directories too
getFilesInDirectory(`${__dirname}/src`); // ['img.png', ...']
getFilesInDirectory(`${__dirname}/src`, {
recursive: true,
});getFriendlyIndex
Given an index (starting at zero), return a human readable string to describe the index. Powered by Mout's nth function.
getFriendlyIndex(0); // 1st
getFriendlyIndex(1); // 2nd
getFriendlyIndex(2); // 3rd
// etcgetFullName
Given a name object, return a "full name" by concatenating the different parts of the name together.
getFullName({ prefix: "Mr", first: "John", middle: "Harry", last: "Smith" }); // Mr John Harry Smith
getFullName({ first: "John", last: "Smith" }); // John Smith
getFullName({ first: "John" }); // John
getFullName({}); // ''getInboundEntityTypes
Get a list of the inbound entity types pointing at a given entityTypeId. That is - the entity types that can point at a given entity type via one or more "Point to another record" fields.
getInboundEntityTypes({
entityTypeId, // the entity type being referenced
allEntityTypes,
includeRollupFields = false, // include first/last reference rollup fields? (don't by default)
});getMimeTypeCategory
For a given mime type, return a "normalised" category for it. The following categories are currently supported:
documentpowerpointspreadsheetpdfimagevideo
getMimeTypeCategory("application/pdf"); // pdf
getMimeTypeCategory("image/png"); // image
getMimeTypeCategory(
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
); // spreadsheetgetMonthlySubscriptionAmount
For a given billing object, return the monthly amount rate that they are paying based on the number of
licences they have across all account user types.
Returns the monthly cost, tax exclusive. The annual discount is applied to this cost if they
are paying annually. Pass false as the second argument to disable this.
Returns 0 if billing is not active yet.
getMonthlySubscriptionAmount({
"id": 23,
"name": "Professional"
"stripe_technology_subscription_id": "sub_123"
// ...
}) // 50getPortalFormUrl
Get the URL of a form, given a portalForm, account, and enviroment.
getPortalFormUrl({
portalForm,
account,
enviroment: "production",
});
// => https://saveewoks.beaconforms.com/form/abc123getPostcodeArea
Returns the UK postcode "area" from a postcode string. Only supports UK postcodes.
getPostcodeArea(`SY3 8AY`); // SY
getPostcodeArea(`e148dw`); // E
getPostcodeArea("Chris"); // nullgetPrimaryField
For a given entity type (standard format with fields populated within it), return the primary field object for it.
getPrimaryField(personEntityType); // { id: 123, key: 'name', type: 'person_name', ... }getQuotePairs
For a string, return an array of objects reflecting all of the double quote pairs in the string.
If there an odd number of quotes, the final closing will be null.
getQuotePairs('Hello "my" name is Chris');
// => [{ opening: 6, closing: 9 }]getReferencedEntityTypes
Return an array of the entity types that a particular entity type references. For example, if you were looking at payments, you would get an array containing entity types like people, campaigns, funds, etc.
getReferencedEntityTypes({
entityTypeId, // entity type doing the referencing
allEntityTypes, // list of all entity types
includeRollupFields, // should first/last rollup fields be included? (default `false`)
}); // [{ id: 123, key: 'person', ... }]getRelatedFieldOptions
Return an array of options that reflect fields on a particular entity type, along with fields on record types that are pointed at. Each option includes:
id: the field ID of the optionvia_entity_field_id: the reference field ID that we need to go "via" to get toid. This is only set for "remote" options.label: nice human-friendly labelkey: a unique string key for the option. This is just aJSON.stringifywith sorted keys ofidandvia_entity_field_id. Nothing clever going on here.
getRelatedFieldOptions({
entityTypeId,
allEntityTypes,
allowedFieldTypes: ["string", "select"], // defaults to all field types
includeRollupFields: false, // defaults to true
includeSmartFields: true, // defaults to true
includeReferenceRollupFields: false, // defaults to whatever includeRollupFields is set to
includeReferenceSmartFields: true, // defaults to whatever includeSmartFields is set to
});getRelatedFieldValue
Given an entity and references, along with fieldId and viaFieldId, return the value of the fieldId for this entity.
This method always returns an array of field values (unless one of the field IDs is invalid). When viaFieldId is not specified, the array is always of length 1 (value is pulled from entity). When viaFieldId is specified, we're pulling from the values of each of the references.
A very useful counterpart to getRelatedFieldOptions.
// on the current entity
getRelatedFieldValue({
entity,
references,
fieldId: 69,
viaFieldId: null,
allEntityTypes,
}); // => [{ amount: 10, currency: 'GBP' }]
// on a referenced record (returns an array of values)
getRelatedFieldValue({
entity,
references,
fieldId: 69,
viaFieldId: 52,
allEntityTypes,
}); // => [['Donor'], ['Fundraiser']]getSmartFieldTemplateParameters
For a given smart field template, return the parameters included in it.
getSmartFieldTemplateParameters("{{{amount}}} from {{{customer}}}");
// => ['amount', 'customer']getStartOfUKTaxYear
For a given date, return the date at the start of the tax year in the UK.
getStartOfUKTaxYear(new Date(2016, 5, 1)); // new Date(2016, 3, 6)
getStartOfUKTaxYear(new Date(2013, 2, 1)); // new Date(2012, 3, 6)
getStartOfUKTaxYear(new Date(2012, 5, 2)); // new Date(2012, 3, 6)getSubscriptionAmount
For a given billing object, return the amount that the customer is paying us on each invoice, tax exclusive.
Accounts for custom frequencies, and applies an annual discount if the technology_plan_frequency is 12.
Returns 0 if billing is not active yet.
getSubscriptionAmount({
"id": 23,
"name": "Professional"
"stripe_technology_subscription_id": "sub_123"
// ...
}) // 540getSQSGroupId(identifier, numberOfGroups)
Given an integer identifier, return a string value that can be used as an SQS group ID to limit the number of concurrent messages being processed in a FIFO queue.
For example, we might only want to create max 3 indexes in the database at once. In this case, you can pass 3 as the second parameter, and the account ID as the first.
This function will return a string integer 1-3, depending on the number of the identifier.
You can also pass strings as the identifier! Only the numberic characters in the identifier are used to generate the group ID though.
// integer id usage
getSQSGroupId(20600, 5); // '1'
getSQSGroupId(20601, 5); // '2'
getSQSGroupId(20602, 5); // '3'
getSQSGroupId(20603, 5); // '4'
getSQSGroupId(20604, 5); // '5'
getSQSGroupId(20605, 5); // '1'
getSQSGroupId(20606, 5); // '2'
// string usage:
getSQSGroupId("acct_1JuxkJE1b0hni1ao", 2); // 2
getSQSGroupId("OR0000ES93S5XN", 7); // 5
getSQSGroupId("95520cb7-9b45-4fdf-8ed4-b9ecd2cd2f21", 3); // 2getTaxExempt
Return the Stripe tax exempt status for a given country code.
none(taxable) for UKreverse(reverse charge) for EU countriesexemptfor everyone else (US, channel islands etc)
getTaxExempt('GB'); // none
getTaxExempt('DE'); // reverse
getTaxExempt('US'); // exemptgetUKTaxYear
For a given date, return the UK tax year that that date falls into, as a 4 digit integer. This returns the LAST year (e.g. 2018 from 2017/2018).
getUKTaxYear(new Date(2017, 2, 1)); // 2017
getUKTaxYear(new Date(2017, 3, 5)); // 2017
getUKTaxYear(new Date(2017, 11, 2)); // 2018getPaymentProviderFromExternalId
For a given Stripe/GoCardless external ID stored on an entity, return the payment provider that corresponds to it.
Both subscription and payment IDs are supported.
getPaymentProviderFromExternalId("sub_123"); // stripe
getPaymentProviderFromExternalId("ch_123"); // stripe
getPaymentProviderFromExternalId("SB123"); // gocardless
getPaymentProviderFromExternalId("PM123"); // gocardless
getPaymentProviderFromExternalId(null); // undefined
getPaymentProviderFromExternalId(123); // undefinedhasDeletedFieldInFilterConditions
Given an array of filter conditions, return true if any of the fields (or entity types) used within the filter conditions at ANY DEPTH are deleted.
hasDeletedFieldInFilterConditions({
filterConditions, // [{ field: 'name.first', operator: '==', ... }]
entityTypeId, // ID of entity type being filtered
allEntityTypes,
includeAggregateConditions, // default `true`. Traverse through nested aggregate conditions?
});hasDynamicDateFieldInFilterConditions
Given an array of filter conditions, return true if any of the fields are date fields using a "dynamic value" (e.g. this_month, today) in them.
hasDynamicDateFieldInFilterConditions({
filterConditions, // [{ field: .... }]
entityTypeId, // id of the entity type we're filtering
allEntityTypes,
});hasDynamicDateFieldInFilterConditionsChangedSinceYesterday
Given an array of filter conditions, return true if any of the fields are date fields using a "dynamic value" (e.g. this_month, today) in them, and the date bounds that they represent have changed since yesterday.
For example, if you were using this_week in a filter condition, this would only return true on Mondays.
hasDynamicDateFieldInFilterConditionsChangedSinceYesterday({
filterConditions, // [{ field: .... }]
entityTypeId, // id of the entity type we're filtering
allEntityTypes,
});hasDynamicDateValueChangedSinceYesterday
Given a dynamic date value (e.g. this_year, today), return true if the date bounds that it represents have changed since yesterday, false otherwise.
hasDynamicDateValueChangedSinceYesterday("today"); // true
hasDynamicDateValueChangedSinceYesterday("this_week"); // false (`true` on mondays)hasEntityFieldPermission
Counterpart to getEntityFieldPermission. Returns true if a user has a required permission on a particular field, false otherwise.
hasEntityFieldPermission({
fieldId, // field id to check
entityTypeId, // entity type it belongs to
isAdmin, // boolean
permissions, // full user permissions object
requiredPermission, // 'read' or 'write'
}); // `true`hasFieldOfTypeInFilterConditions
Given an array of filter conditions, return true if
any of the conditions are based on fields of a particular type, false
otherwise.
Works at any level of nesting in reference and aggregate filters.
hasFieldOfTypeInFilterConditions({
type, // e.g. 'date'
filterConditions, // [{ field: .... }]
entityTypeId, // id of the entity type we're filtering
allEntityTypes,
isAllowedCondition, // optional function
});Optionally, an isAllowedCondition function can be passed, which is invoked with each condition. You can use this to evaluate whether a condition should be allowed, based on condition.operator, condition.value, etc.
hasSettingPermission
Return true if a user has permission to read or write on a particular setting (e.g. portal_forms), false otherwise.
hasSettingPermission({
type, // e.g. 'apps'
requiredPermission, // 'read' or 'write'
isAdmin, // boolean
permissions,
}); // truehasStrongEntityTypePermission
Returns true if the user has a given permission on all fields in a given entity type, false otherwise.
If isAdmin is true, then this will always return true.
hasStrongEntityTypePermission({
entityType,
isAdmin, // true or false
permissions, // permissions object
requiredPermission, // 'read' or 'write'
}); // truehasWeakEntityTypePermission
Returns true if the user has a given permission on any fields in a given entity type, false otherwise.
If isAdmin is true, then this will always return true.
hasWeakEntityTypePermission({
entityType,
isAdmin, // true or false
permissions, // permissions object
requiredPermission, // 'read' or 'write'
}); // trueisArrayFieldType
Return true if fieldType is an "array" field type (has values stored as an array), false otherwise.
isArrayFieldType("email"); // true
isArrayFieldType("select"); // true
isArrayFieldType("string"); // false
isArrayFieldType("number"); // falseisBeaconEmail
Returns true if an email address string is a Beacon email address, false otherwise. Checks for:
@beaconcrm.org@beaconcrm.co.uk@beaconproducts.co.uk
isBeaconEmail("[email protected]"); // true
isBeaconEmail("[email protected]"); // falseisBillingConfigured
Given a billing argument, return true if it reflects a configured billing setup, false otherwise.
isBillingConfigured({
id: 23,
plan: "Professional",
// ...
}); // true
isBillingConfigured({
id: null,
plan: null,
// ...
}); // falseisBillingActive
Given a billing argument, return true if it is "active" (there is a subscription), false otherwise.
isBillingConfigured({
"id": 23,
"name": "Professional"
"stripe_technology_subscription_id": "sub_123"
// ...
}) // true
isBillingConfigured({
"key": "billing_not_configured"
// ...
}) // falseisDisposableEmail
Returns true if an email address is disposable, false otherwise. Uses disposable-email-domains.
isDisposableEmail("[email protected]"); // `true`
isDisposableEmail("[email protected] "); // true
isDisposableEmail("[email protected]"); // falsedynamicDateValues
An array of all of the possible dynamic date values, for example:
this_yearnext_weektoday
Used internally by isDynamicDateValue.
isDynamicDateValue
Returns true if the value is a dynamic date value (e.g. this_year), false otherwise.
isDynamicDateValue("this_year"); // true
isDynamicDateValue("2021-04-02"); // false
isDynamicDateValue("chris"); // falsedynamicDateOperators
An array of all of the possible dynamic date operators, for example:
date_after_relativedate_exact_relativedate_before_relative
Used internally by isDynamicDateOperator.
isDynamicDateOperator
Returns true if the operator is a dynamic date value (e.g. date_exact_relative), false otherwise.
isDynamicDateValue("date_exact_relative"); // true
isDynamicDateValue("date_on"); // falseisEntityFieldValueMatch
A synchronous JavaScript-only function to check to see if an entity field value matches a given condition.
This implements a sub-set of our standard filtering system (which is implemented at the database level), and is designed for immediate, more primitive value checking. Quite useful when you have all of the data you need in variables. (e.g. when checking an entity + its references)
Currently used in:
- Card visibility
- Smart field rules
If you need more rules that aren't yet implemented, please add them!
// Syntax:
isEntityFieldValueMatch({
fieldValue, // the saved entity field value
fieldType, // field type
conditionOperator, // condition operator
conditionValue, // condition value
});
// Example 1: "is"
isEntityFieldValueMatch({
fieldValue: ["Donor"],
fieldType: "select", // type of field
conditionOperator: "contains", // condition operator
conditionValue: "Donor", // condition value
}); // true
// Example 2: "is not checked"
isEntityFieldValueMatch({
fieldValue: true,
fieldType: "boolean", // type of field
conditionOperator: "==",
conditionValue: false,
}); // false
// Example 3: "starts with"
isEntityFieldValueMatch({
fieldValue: "ch_123",
fieldType: "string", // type of field
conditionOperator: "starts_with",
conditionValue: "ch_",
}); // trueisEntityTypeDeleted
Returns true if a entityTypeId does not exist within allEntityTypes, false otherwise.
If null or undefined is passed, also returns false. (Handy for drop-down fields when the value hasn't been selected yet)
isEntityTypeDeleted(12, allEntityTypes); // true
isEntityTypeDeleted(23, allEntityTypes); // false
isEntityTypeDeleted(null, allEntityTypes); // false
isEntityTypeDeleted(undefined, allEntityTypes); // falseisEntityTypeEnabled
Returns true if an entity type is enabled based on elements configuration, false otherwise.
isEntityTypeEnabled({
id: 1
enabledElements: [], // e.g. ['fundraising', 'workflows']
allEntityTypes,
})
// => trueA key parameter can also be passed instead of the id.
isEntityTypeEnabled({
key: "ticket",
enabledElements: ["fundraising"],
allEntityTypes,
});
// => falseisFieldEnabled
Returns true if a field is enabled based on elements configuration, false otherwise.
isFieldEnabled({
id: 11,
enabledElements: ["fundraising"],
allEntityTypes,
});
// => trueisFeatureEnabled
Returns true if a feature (e.g. roles_and_permissions) is enabled on an account based on their billing settings, false otherwise. This method handles the basic feature flags, and add-ons as well.
isFeatureEnabled("workflows", billing); // true
isFeatureEnabled("roles_and_permissions", billing); // false
isFeatureEnabled("memberships", billing); // true
isFeatureEnabled("ticketing", billing); // true
isFeatureEnabled("zapier", billing); // true
isFeatureEnabled("api", billing); // trueisFieldDeleted
Returns true if a fieldId does not exist within allEntityTypes, false otherwise.
If null or undefined is passed, also returns false. (Handy for drop-down fields when the value hasn't been selected yet)
isFieldDeleted(123, allEntityTypes); // true
isFieldDeleted(456, allEntityTypes); // false
isFieldDeleted(null, allEntityTypes); // false
isFieldDeleted(undefined, allEntityTypes); // falseisLocationEqual
Returns true if two "location" objects are the same. Doesn't require strict equality, but does a lot of handy checks under the hood:
- If address line 1 and postcode match, then will return
true - (UK only) If only postcodes match, will return
true - (International) If only postcodes match, will return
false - One of the postcodes is blank, will return
false - Auto-formats UK postcodes when comparing
const first = {
address_line_one: "Crowmeole Farm",
address_line_two: null,
address_line_three: null,
city: "Shrewsbury",
country: "United Kingdom",
country_code: "GB",
is_primary: true,
latitude: 52.700747,
longitude: -2.785249,
notes: null,
postal_code: "SY3 8AY",
region: null,
};
const second = {
address_line_one: "crowmeole farm ", // whitespace and case ignored
address_line_two: null,
address_line_three: null,
city: "Shrewsbury",
country: "United Kingdom",
country_code: "GB",
is_primary: true,
latitude: 52.700747,
longitude: -2.785249,
notes: null,
postal_code: "sy38ay ",
region: null,
};
isLocationEqual(first, second); // trueisNamePrefix
Returns true if a string is a "name prefix", false otherwise.
isNamePrefix("Mr"); // true
isNamePrefix("Mr."); // true
isNamePrefix("mr"); // true
isNamePrefix(" mr. "); // true
isNamePrefix("chris"); // falseisNumeric
Returns true if a value is numeric, false otherwise.
isNumeric(1); // true
isNumeric("1"); // true
isNumeric("-51"); // true
isNumeric("1.23"); // true
isNumeric("chris"); // falseisPersonalEmail
Returns true if a string is a personal email address, false otherwise.
Auto-checks about 3.5k personal email domains (gmail.com, algeria.com, etc).
isPersonalEmail("[email protected]"); // true
isPersonalEmail("[email protected]"); // falseisRestrictedActivityEmailField
Activities can set restricted fields.
Check to see if a field is restricted or not.
isRestrictedActivityEmailField({
field: {
key: "content",
},
actorType: "user",
actorId: 2,
createdByUserId: 1,
entityTypeKey: "activity",
isPrivate: true,
});
// => true
isRestrictedActivityEmailField({
field: {
key: "content",
},
actorType: "user",
actorId: 2,
createdByUserId: 1,
entityTypeKey: "person",
isPrivate: true,
});
// => falseisSmartFieldCircularDependency
Return true if a smart field depends on itself via a circular dependency, false otherwise. (We should not allow users to create fields that get into this situation)
isSmartFieldCircularDependency({
field, // smart field
entityType,
});
// => falseisSnakeCase
Returns true if a string is snake cased, false otherwise.
isSnakeCase("chris_test"); // true
isSnakeCase("greatstuff"); // true
isSnakeCase("FrodoBaggins"); // false
isSnakeCase("Frodo Baggins"); // falseisValidCountryCode
Returns true if a string is a valid ISO 2 letter country code, false otherwise.
isValidCountryCode("GB"); // true
isValidCountryCode("CA"); // true
isValidCountryCode("FRODO"); // falseisValidCurrencyCode
Returns true if a string is a valid ISO 3 letter currency code, false otherwise.
isValidCurrencyCode("GBP"); // true
isValidCurrencyCode("Luke Skywalker"); // false
isValidCurrencyCode(null); // falseisValidLocale
Returns true if a string is a valid locale, false otherwise. Works for both long format locales (e.g. en_GB) and short formats (en).
isValidLocale("en_GB"); // true
isValidLocale("en"); // true
isValidLocale("orcish_MO"); // falseisValidPhoneNumber
Returns true the phone number is valid, false otherwise.
isValidPhoneNumber("07234567324", "GB"); // trueisValidUKPostcode
Returns true if a string is a valid UK postcode, false otherwise.
isValidUKPostcode("SW11 2AE"); // true
isValidUKPostcode("sw112ae"); // true
isValidUKPostcode("chris"); // falseisWrappedInQuotes
Returns true is a string is wrapped in double or single quotes, false otherwise.
isWrappedInQuotes('"Boris"'); // true
isWrappedInQuotes("Is the best prime minister ever"); // falsejsonStringifySorted
Runs JSON.stringify on an array/object, and sorts the keys within it. For all the times you need to have sorted JSON strings.
jsonStringifySorted({
last_name: "Houghton",
first_name: "Chris",
age: 30,
}); // {"age":30,"first_name":"Chris","last_name":"Houghton"}lastName
Get the last name for a given string. Treats the first non-prefix word as the first name, and all other names after it as the last name.
lastName("Frodo Baggins"); // Baggins
lastName("Mr Frodo Baggins"); // Baggins
lastName("Mr. Frodo David Baggins"); // David Bagginslocales
An array of all supported locales, including their code and name. Used in isValidLocale.
console.log(locales); // [{ code: 'af', name: 'Afrikaans' }, ...]mapFormSections
For default behaviour, no need to pass a predicate:
Given a beacon portal form 'sections' array and a callback, applies callback to any generic fields type field under a section, whether deeply nested or not. The rest of the data is returned unmodified.
mapFormSections({
sections: [
{
id: 2,
prop: true,
fields: [{}, {}, {}],
},
],
callback: (fields) => map(fields, (field) => ({ ...field, new_prop: true })),
});
// [ { id: 2, prop: true, fields: [ { new_prop: true }, { new_prop: true }, { new_prop: true } ] } ]For non-default behaviour, pass a special predicate for callback application:
mapFormSections({
sections: [
{
id: 2,
prop: true,
something_else: [{}, {}, {}],
},
],
callback: (data) => map(data, (x) => ({ ...x, new_prop: true })),
predicate: ([key]) => key === "something_else",
});
// [ { id: 2, prop: true, something_else: [ { new_prop: true }, { new_prop: true }, { new_prop: true } ] } ]mapRelativeDate
Given a string (e.g. tomorrow) return a date string in the format YYYY-MM-DD that corresponds to that date. By default, this date is relative to the current time, but a second date parameter can be passed to set a custom time.
Also supports an n_days format, where n is the number of days after today.
All supported relative dates are provided in the example below:
mapRelativeDate("today"); // 2020-10-25
mapRelativeDate("tomorrow"); // 2020-10-26
mapRelativeDate("monday"); // 2020-10-26
mapRelativeDate("one_week"); // 2020-11-01
mapRelativeDate("one_month"); // 2020-11-25
mapRelativeDate("6_days"); // 2020-10-31matchesSchema
Validates a value against a given JSON schema. If invalid, an error will be thrown. If valid, then nothing will happen.
Powered by revalidator. (Go there for more docs)
const value = {
first: "Chris",
last: null,
};
matchesSchema(value, {
type: "object",
properties: {
first: {
type: "string",
required: true,
},
last: {
type: "string",
required: true,
},
},
});
// throws ValidationError(`last is required`);Optionally, a third prefix argument can be provided, which prefixes the error message.
A fourth options argument can be provided, which directly corresponds to the options parameter in revalidator. It defaults to:
{
validateFormats: true,
validateFormatsStrict: false,
additionalProperties: false,
}namePrefix
For a given string, returns the string for the prefix(es) at the start of it.
namePrefix("Mr Frodo Baggins"); // Mr
namePrefix("Mr Prof Frodo Baggins"); // Mr Prof
namePrefix("Frodo Baggins"); // undefinednamePrefixes
An array of 250+ name "prefixes", including Mr, Mrs, Seaman, and Rabbi.
console.log(namePrefixes); // ['Mr', 'Mrs', ...]openPopup
Client side only. Opens a popup window. Should be executed synchronously immediately after clicking on a button to prevent popup blockers.
Useful for OAuth flows, among other things.
openPopup(url, title, width, height);parallelLimit
Runs an array of promise-returning functions in batches (default batch size 10), waiting for each batch to finish before starting the next. Results are returned in the same order as the input functions.
const parallelLimit = require('@beacon/utils/parallelLimit');
// change this to an array of promises
const tasks = [async () => 'a', async () => 'b', async () => 'c'];
const results = await parallelLimit(tasks, 2); // ['a', 'b', 'c']personNameToString
For a Beacon "person name" object (format of the person name field), return a string that can be rendered into emails, or otherwise.
const name = {
prefix: null,
first: "Frodo",
middle: "David",
last: "Baggins",
};
personNameToString(name); // Frodo David BagginsOptionally, a second includePrefix argument can be provided, indicating whether to prepend the string with the prefix.
platformEntityFields
An array of the "platform" entity fields, including id, avatar, created_by_id, etc.
console.log(platformEntityFields); // ['id', 'created_at', ....]sanitizeEntityPayload
For a given payload that will be sent to the create/update entity API endpoint, santise the data, ensuring that:
- Only one value is provided if
allow_multipleisfalse(applies toselect,email,location,phone,user, andreferencefield types) - Only valid values in
selectfields - Remove invalid reference IDs from
referencefields
safeParseJson
Safely parses a JSON string, returning the parsed object or a default value if parsing fails.
safeParseJSON('{"name": "Fazza"}'); // {name: "Fazza"}
safeParseJSON('invalid json'); // undefined
safeParseJSON('invalid json', {}); // {}
safeParseJSON('invalid json', null); // nullsentenceCase
Convert a string into "Sentence case" (first letter of each sentence capitalized, rest lowercase).
sentenceCase('lorem Ipsum doLOr. sit amet dolor.');
// output: Lorem ipsum dolor. Sit amet dolor.snakecase
Given a string it will convert it to snakecase
snakecase('my wildString with áccénts')
// my_wild_string_with_accentsspliceString
Inserts a string into another string at a certain index. Returns a new string value, does not mutate the original one.
spliceString("Chris Houghton", 6, "John "); // Chris John HoughtonstartsWithVowel
Returns true if a string starts with a vowel, false otherwise.
startsWithVowel("chris"); // false
startsWithVowel("alice"); // truestateCodes
An array of all US states.
[{ code: 'AL', name: 'Alabama' }, ...]timeout
Promise wrapper around the native setTimeout.
await timeout(1000);titleCase
Convert a string into "Title Case" (each word capitalised);
titleCase('this string is ODD');
// output: This String Is OddgetGraphQLEntityField
Given a particular field type, return a value that will allow the selection of all of the data in that field.
This is the JSON expression that will get passed to jsonToGraphQLQuery that's used to generate the actual graphql query.
This works for primitive field types (e.g. short text just returns a boolean of true) and complex fields.
getGraphQLEntityField(fieldType);hasNotificationPermission
Given an 'account notification setting' validates whether a user has the relevant permissions to receive a notification.
hasNotificationPermission({
accountNotificationSetting,
accountUser,
permissions,
entityType,
});isFieldValueBlank
Determines if the value of a given field is blank. Checks things like whether each key in a person field is blank.
isFieldValueBlank({
value,
field,
});getMappingFieldDepth
Mapping fields are used to map fields to columns when running imports. Mappings which are at least two steps removed will have a depth of 1 upwards. The mapping depth is used to determine offsets when evaluting a key.
getMappingFieldDepth("123.456.789");findRanges
Find all of the "ranges" where a particular regex pattern matches.
findRanges("my name is chris and his name is david", /(name)/g);
// => [{ string: 'name', start: 3, end: 7, length: 4 }, ...]);replaceRange
Replace a range (between two indexes) of text within a string with a new bit of text.
Note: the second index is exclusive, i.e. before that index.
replaceRange(
"My name is Chris",
3, // start (inclusive)
7, // end (exclusive)
"first name"
);
// => My first name is ChrisrequireIndex
Copy of https://github.com/stephenhandley/requireindex/blob/master/index.js with 'ts' added to the list of exts.
requireIndex(`${__dirname}/../../controllers`);redactData
For a given object or array, redact the data found at the specific keys with a replacement (default is [REDACTED]).
const data = {
id: 1,
name: "Chris Houghton",
email: {
provider: "gmail",
address: "[email protected]",
},
};
redactData(data, {
keys: ["name", "email.address"],
});
// =>
// {
// id: 1,
// name: '[REDACTED]',
// email: {
// provider: 'gmail',
// address: '[REDACTED]',
// },
// };substituteSmartParameters
Given formula (starting with =), return a string that's ready to be passed through our formula parser. Substitute parameters, and auto-fix/quote them as needed.
For templates (not starting with =), this util just substitutes in parameters.
substituteSmartParameters("=IF({{{Widget count}}} > 4, TRUE, FALSE)", {
"Widget count": 5,
});
// => =IF(5 > 4, TRUE, FALSE)
substituteSmartParameters('=IF({{{Type}}} = "Donor", TRUE, FALSE)', {
Type: "Donor",
});
// => =IF("Donor" = "Donor", TRUE, FALSE)
substituteSmartParameters("{{{Title}}} is good", {
Title: "Fundraising",
});
// => Fundraising is goodgetFieldFromMappingKey
Get the field from a mapping key.
getFieldFromMappingKey("123.456.789", entityTypes);findMatViewCondition
Get the first (if any) 'in_materialized_view' condition block from a set of filter conditions. Can handle any degree of nested-ness...
NOTE! - at this point, does not yet take account of related X-filters or aggregate conditions...
findMatViewCondition({
filter_conditions: [
{
field: "id",
operator: "s987df",
value: 395,
},
{
field: "id",
operator: "rahh",
value: 765,
},
{
field: "id",
operator: "in_materialized_view",
value: 146,
},
],
filter_strictness: "all",
});cleanFileName
Clean file names to ensure they can be read from S3.
cleanFileName("thefilename$$"); // returns thefilenamehasAuditLogInFilterConditions
Returns if the filter conditions contain an audit_log filter
hasAuditLogInFilterConditions({ filterConditions }); // returns true/falsevalidateSchema
Validates a value against a given Zod schema. If invalid, an error will be
throw. If valid, the validated object will be returned with the correct type.
This is useful for validating input that may be unknown, e.g. from a request, invocation, a file, etc.
Powered by zod. (Go there for more docs)
// imagine we've loaded some data from a CSV file
const value = parsedCsvFile[0];
// we can validate against the schema to ensure correctness
const result = validateSchema(value, z.object({
first: z.string(),
last: z.string(),
}));
// result is of type { first: string; last: string; }hasEntityAccessRuleChanged
Initialise on a current state of access rules for a given role: util returns a
callback function cb to be passed as a callback to an iterator on a new state
of the same rules; cb returns boolean whether the matching rule state has
changed...
Where rule contains via_field_ids or filter_conditions util will ignore
changes of item order in change determination - this only is feasible in case of
filter_conditions due to access rules forbidding deeply nested conditions.
Note that if core rule data is invalid (e.g. core properties have changed) a validation error is thrown, in all other cases we return boolean.
Note that because we are testing the new state against the old, if items are being removed from the old state and therefore are not contained in the new, util will be none the wiser - ensuring that deleted items are removed is a related but separate activity.
Note that currently util provides its own types and schema definitions - these duplicate definitions buyild in the entities service but maybe we can find a way to centralise these definitions at some time (in @beacon/types etc.).
const state: AccessRule[] = [
{
id: 10,
account_id: 11,
entity_type_id: 12,
role_id: 13,
type: 'all',
conditions: null,
},
];
const rule: AccessRule = {
id: 10,
account_id: 11,
entity_type_id: 12,
role_id: 13,
type: 'forbidden',
conditions: null,
};
hasRuleChanged(state)(rule); // true
[rule].filter(hasRuleChanged(state)) // [rule]
namecase
Given a name string, convert it into a propercased format, allowing for some of the exceptions that exist in the world.
A direct port of the PHP library https://github.com/tamtamchik/namecase
Usage
nameCase('chris houghton')
// => Chris HoughtonExamples
keith = Keith
yuri's = Yuri's
leigh-williams = Leigh-Williams
mccarthy = McCarthy
machin = Machin
machlin = Machlin
machar = Machar
mackle = Mackle
macklin = Macklin
mackie = Mackie
macquarie = Macquarie
machado = Machado
macevicius = Macevicius
maciulis = Maciulis
macias = Macias
macmurdo = MacMurdo
o'callaghan = O'Callaghan
st. john = St. John
von streit = von Streit
van dyke = van Dyke
van = Van
ap llwyd dafydd = ap Llwyd Dafydd
al fahd = al Fahd
al = Al
el grecco = el Grecco
ben gurion = ben Gurion
ben = Ben
da vinci = da Vinci
di caprio = di Caprio
du pont = du Pont
de legate = de Legate
del crond = del Crond
der sind = der Sind
van der post = van der Post
van den thillart = van den Thillart
della vinci = della Vinci
von trapp = von Trapp
la poisson = la Poisson
le figaro = le Figaro
mack knife = Mack Knife
dougal macdonald = Dougal MacDonald
ruiz y picasso = Ruiz y Picasso
dato e iradier = Dato e Iradier
mas i gavarró = Mas i Gavarró
henry viii = Henry VIII
louis iii = Louis III
louis xiv = Louis XIV
charles ii = Charles II
fred xlix = Fred XLIX
yusof bin ishak = Yusof bin IshaktimeLogger
Structured logging utility for measuring how long a block of code takes.
Example:
import { setContext, timeLogger } from '@beacon/utils/timeLogger';
setContext({
serviceName: 'hello-world-service',
accountIds: [123, 345],
});
const logger = createTimeLogger({ type: 'my-func', accountId: 123 });
const span = logger.startSpan('foo', { bar: 999 });
// do some work
await new Promise((resolve) => setTimeout(resolve, 100));
span.end();
// do more work
await new Promise((resolve) => setTimeout(resolve, 100));
logger.end();{"service":"hello-world-service","account_id":123,"type":"my-func","metric_event":"foo","duration_ms":101,"bar":999}
{"service":"hello-world-service","account_id":123,"type":"my-func","metric_event":"my-func","duration_ms":202}logging
A logger is available in the @beacon/utils/logging module. It will set the expected log level based on the deployment stage (development, staging, production - via NODE_ENV), or the LOGGING_LEVEL environment variable if set.
import logger from '@beacon/utils/logging';
logger.info('hello world');_types
This is a private package to enforce type strictness internally. It should not be used outside of code in beacon/utils
Deployment
Merge into the master branch with a new package.json version and Github actions will deploy to NPM.
License
Apache2
