import { validateArgs, validateString, validateObject, validateDate, } from '../utils/validating'; // Anlix user database source names that will be acceptable. // map to use when checking strings to be a valid user source name. const userSources = { 'anlix': true, 'flashman': true, 'control': true, } as Record; export type UserSource = 'anlix' | 'flashman' | 'control'; // UserMessage structure to hold user data to be synchronized from a product, in a client, to FlashAudit. export interface UserMessage { _id: { client: string, source: UserSource, id: string, }, name: string, }; // User structure to be saved in FlashAudit user collection. export interface User extends UserMessage { last_update: Date, } // Returns the user database source identification string for a given product. // Because each product has only one user database scheme, each product can be mapped // to one string identifying the user database a user may have come from. export const sourceFromProduct = function (product: string): UserSource { switch (product.toLowerCase()) { case 'flashman': return 'flashman'; case 'controle': case 'control': return 'control'; case 'anlixid': case 'anlix': default: return 'anlix'; } } // returns a 'UserMessage' object. Expects all arguments to be already validated. const newUserMessageStruct = function ( client: string, source: UserSource, id: string, name: string, ): UserMessage { return {_id: {client, source, id}, name}; } // returns a 'User' object. Expects all arguments to be already validated. const newUserStruct = function ( client: string, source: UserSource, id: string, name: string, date: Date, ): User { // because 'User' is an extension of 'UserMessage', we build the a 'UserMessage' and then // extend it to make a 'User'. const u: any = newUserMessageStruct(client, source, id, name); u.last_update = date; return u as User; } // Meant to be called in TypeSript projects. // Returns a data structure representing a 'UserMessage'. // Instantiates a new Object in 'UserMessage' format. Returns a tuple where first element is // the created 'UserMessage' object and the second element is the message of any found error. export const buildMessage = function ( client: string, product: string, id: string, name: string, ): [UserMessage | undefined, string | undefined] { // maybe we should validate the product values. const source = sourceFromProduct(product); // this always returns a valid source. const u = newUserMessageStruct(client, source, id, name); return [u, undefined]; }; // Meant to be called in JavaScript projects. // Returns a data structure representing a 'UserMessage'. // Validates arguments and instantiates a new object in 'UserMessage' format. Returns a tuple // where first element is the created 'UserMessage' object and the second element is the // message of any found error. export const buildMessageForJS = function ( client: any, product: any, id: any, name: any, ): [UserMessage | undefined, string | undefined] { let errmsg = validateBuildArguments(client, product, id, name); if (errmsg) return [undefined, `Argument '`+errmsg]; return buildMessage(client, product, id, name); }; // Validates a 'UserMessage' returning undefined if valid, otherwise returns a string with // the error message. export const validateMessage = function (u: any): string | undefined { let errmsg = validateUserCommon(u); if (errmsg) return `UserMessage '`+errmsg; } // Meant to be called in TypeSript projects. // Returns a data structure representing a 'User'. // Instantiates a new Object in 'User' format. Returns a tuple where first element is // the created 'User' object and the second element is the message of any found error. export const build = function ( client: string, product: string, id: string, name: string, date: Date, ): [User | undefined, string | undefined] { // maybe we should validate the product values. const source = sourceFromProduct(product); // this always returns a valid source. let errmsg = validateDate(date); if (errmsg) return [undefined, `Argument '`+errmsg]; const u = newUserStruct(client, source, id, name, date); return [u, undefined]; }; // Meant to be called in JavaScript projects. // Returns a data structure representing a 'User'. // Validates arguments and instantiates a new object in 'User' format. Returns a tuple // where first element is the created 'User' object and the second element is the // message of any found error. export const buildForJS = function ( client: any, product: any, id: any, name: any, date: any, ): [User | undefined, string | undefined] { let errmsg = validateBuildArguments(client, product, id, name); if (errmsg) return [undefined, `Argument '`+errmsg]; return build(client, product, id, name, date); }; // Validates a 'User' returning undefined if valid, otherwise returns a string with // the error message. export const validate = function (u: any): string | undefined { let errmsg = validateUserCommon(u); if (errmsg) return `User '`+errmsg; errmsg = validateDate(u.last_update); if (errmsg) return `User '`+errmsg; } // validates the type of the arguments for 'buildForJS()' function. const validateBuildArguments = function ( client: any, product: any, id: any, name: any, ): string | undefined { // maybe we should validate the product values. return validateArgs([ {value: client, name: 'client', func: validateString}, {value: product, name: 'product', func: validateString}, {value: id, name: 'id', func: validateString}, {value: name, name: 'name', func: validateString}, ]); } // returns undefined if given 'source' string is valid, otherwise returns error string. const checkSource = function(source: UserSource): string | undefined { if (!userSources[source]) { return `source' has unrecognized value '${source}'. `+ `Allowed are: ["${Object.keys(userSources).join('" | "')}"].`; } } // validates data types for each field common to User and UserMessage. Returns string with error // if any, or returns undefined when no errors found. export const validateUserCommon = function (u: any): string | undefined { let errmsg = validateArgs([ {value: u, name: '', func: validateObject}, {value: u?._id, name: 'u._id', func: validateObject}, {value: u?._id?.client, name: '_id.client', func: validateString}, {value: u?._id?.source, name: '_id.source', func: validateString}, {value: u?._id?.id, name: '_id.id', func: validateString}, {value: u?.name, name: 'name', func: validateString}, ]); if (errmsg) return errmsg; errmsg = checkSource(u._id.source); if (errmsg) return errmsg; } // creating a mock UserMessage to get the keys out of it. const mockUserMessage = buildMessage('a', 'b', 'c', 'd')[0]; // getting amount of keys in a well formatted UserMessage. const amountOfKeys = Object.keys(mockUserMessage as any).length; // Receives an 'UserMessage' and returns a 'User' from it, stripped from any dangling attributes. // Expects 'UserMessage' to be already validated. export const convertMessageToUser = function(syncedUser: UserMessage, date: Date): User { let obj: any = syncedUser; if (Object.keys(obj).length > amountOfKeys) { // if there are dangling keys. const tmp: any = {}; // building a new object. // using a mocked UserMessage as a reference of a good UserMessage. for (const key in mockUserMessage) tmp[key] = obj[key]; // filling object with all known keys. obj = tmp; } let errmsg = validateDate(date); // checking date value. obj.last_update = errmsg ? new Date() : date; // if date has a problem, use a new date instead. return obj as User; };