configurable-operation.ts 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. // prettier-ignore
  2. import { ConfigArg, ConfigurableOperation } from '../../../shared/generated-types';
  3. import { InternalServerError } from './error/errors';
  4. /**
  5. * Certain entities allow arbitrary configuration arguments to be specified which can then
  6. * be set in the admin-ui and used in the business logic of the app. These are the valid
  7. * data types of such arguments. The data type influences:
  8. * 1. How the argument form field is rendered in the admin-ui
  9. * 2. The JavaScript type into which the value is coerced before being passed to the business logic.
  10. */
  11. export type ConfigArgType =
  12. | 'percentage'
  13. | 'money'
  14. | 'int'
  15. | 'string'
  16. | 'datetime'
  17. | 'boolean'
  18. | 'facetValueIds';
  19. export type ConfigArgs<T extends ConfigArgType> = {
  20. [name: string]: T;
  21. };
  22. /**
  23. * Represents the ConfigArgs once they have been coerced into JavaScript values for use
  24. * in business logic.
  25. */
  26. export type ConfigArgValues<T extends ConfigArgs<any>> = {
  27. [K in keyof T]: T[K] extends 'int' | 'money' | 'percentage'
  28. ? number
  29. : T[K] extends 'datetime'
  30. ? Date
  31. : T[K] extends 'boolean'
  32. ? boolean
  33. : T[K] extends 'facetValueIds'
  34. ? string[]
  35. : string
  36. };
  37. /**
  38. * Defines a ConfigurableOperation, which is a method which can be configured
  39. * by the Administrator via the Admin API.
  40. */
  41. export interface ConfigurableOperationDef {
  42. code: string;
  43. args: ConfigArgs<any>;
  44. description: string;
  45. }
  46. /**
  47. * Convert a ConfigurableOperationDef into a ConfigurableOperation object, typically
  48. * so that it can be sent via the API.
  49. */
  50. export function configurableDefToOperation(def: ConfigurableOperationDef): ConfigurableOperation {
  51. return {
  52. code: def.code,
  53. description: def.description,
  54. args: Object.entries(def.args).map(([name, type]) => ({ name, type })),
  55. };
  56. }
  57. /**
  58. * Coverts an array of ConfigArgs into a hash object:
  59. *
  60. * from:
  61. * [{ name: 'foo', type: 'string', value: 'bar'}]
  62. *
  63. * to:
  64. * { foo: 'bar' }
  65. **/
  66. export function argsArrayToHash<T>(args: ConfigArg[]): ConfigArgValues<T> {
  67. const output: ConfigArgValues<T> = {} as any;
  68. for (const arg of args) {
  69. if (arg.value != null) {
  70. output[arg.name] = coerceValueToType<T>(arg);
  71. }
  72. }
  73. return output;
  74. }
  75. function coerceValueToType<T>(arg: ConfigArg): ConfigArgValues<T>[keyof T] {
  76. switch (arg.type as ConfigArgType) {
  77. case 'int':
  78. case 'money':
  79. return Number.parseInt(arg.value || '', 10) as any;
  80. case 'datetime':
  81. return Date.parse(arg.value || '') as any;
  82. case 'boolean':
  83. return !!arg.value as any;
  84. case 'facetValueIds':
  85. try {
  86. return JSON.parse(arg.value as any);
  87. } catch (err) {
  88. throw new InternalServerError(err.message);
  89. }
  90. default:
  91. return (arg.value as string) as any;
  92. }
  93. }