@beseif-solutions/prow-core 0.0.39 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,15 +2,12 @@ import Joi, { DateSchema, NumberSchema, StringSchema } from "joi";
2
2
  import _ from "lodash";
3
3
  import moment from "moment-timezone";
4
4
  import context from "./context";
5
- import { OneOf } from "./types";
6
5
  import { where, Where } from "./where";
7
6
 
8
7
  export type Condition = {
9
8
  condition: Where,
10
- } & OneOf<{
11
9
  overrides: Partial<Field>,
12
- show: boolean,
13
- }>;
10
+ };
14
11
 
15
12
  type Common = {
16
13
  key: string,
@@ -18,6 +15,7 @@ type Common = {
18
15
  disabled?: boolean,
19
16
  required?: boolean,
20
17
  as_password?: boolean,
18
+ show?: boolean,
21
19
  show_label?: boolean,
22
20
  } & ({
23
21
  as_array?: false,
@@ -47,6 +45,7 @@ type Any = string | number | boolean | Any[] | { [key: string]: Any };
47
45
  type AnyInputField<Extend = Record<never, never>> = {
48
46
  type: `any`,
49
47
  default?: Any | Any[],
48
+ allow_nullish?: boolean, // allow null values
50
49
  } & (
51
50
  | SelectField<Any, Extend>
52
51
  | {}
@@ -62,8 +61,8 @@ type StringInputField<Extend = Record<never, never>> = {
62
61
  pattern?: `email` | `uri`,
63
62
  } & (
64
63
  | SelectField<string, Extend>
65
- | TextareaField
66
- | RichTextField
64
+ | CustomDisplayTextField
65
+ | IDETextField
67
66
  | {}
68
67
  );
69
68
 
@@ -74,7 +73,7 @@ type NumberInputField<Extend = Record<never, never>> = {
74
73
  max?: number,
75
74
  } & (
76
75
  | { float: true, decimals?: number }
77
- | { float: false }
76
+ | { float?: false }
78
77
  ) & (
79
78
  | SelectField<number, Extend>
80
79
  | {}
@@ -155,14 +154,14 @@ export type SelectFieldChoice<Values = string | number | boolean | File, Extend
155
154
  value: Values,
156
155
  } & Extend;
157
156
 
158
- /* textarea configuration */
159
- type TextareaField = {
160
- format: `textarea`,
157
+ /* custom display configuration */
158
+ type CustomDisplayTextField = {
159
+ format: `textarea` | `rich-text` | `markdown`,
161
160
  };
162
161
 
163
- /* rich text configuration */
164
- type RichTextField = {
165
- format: `rich-text`,
162
+ type IDETextField = {
163
+ format: `code`,
164
+ language?: string, // language for code highlighting
166
165
  };
167
166
 
168
167
  type ValidationExtended = {
@@ -181,10 +180,9 @@ type ValidationExtended = {
181
180
 
182
181
  const conditionSchema = () =>
183
182
  Joi.object({
184
- condition: Joi.object(),
185
- overrides: Joi.object(),
186
- show: Joi.bool(),
187
- }).xor(`overrides`, `show`);
183
+ condition: Joi.object().required(),
184
+ overrides: Joi.object().required(),
185
+ });
188
186
 
189
187
  const commonFieldSchema = () =>
190
188
  Joi.object({
@@ -193,6 +191,7 @@ const commonFieldSchema = () =>
193
191
  disabled: Joi.bool(),
194
192
  required: Joi.bool(),
195
193
  as_password: Joi.bool(),
194
+ show: Joi.bool(),
196
195
  show_label: Joi.bool().default(true),
197
196
  as_array: Joi.bool(),
198
197
  array_min: Joi.when(`as_array`, {
@@ -275,12 +274,17 @@ const stringInputFieldSchema = () =>
275
274
  })
276
275
  .concat(
277
276
  Joi.object({
278
- format: Joi.string().valid(`select`, `textarea`, `rich-text`),
277
+ format: Joi.string().valid(`select`, `textarea`, `rich-text`, `markdown`, `code`),
279
278
  choices: Joi.when(`format`, {
280
279
  is: `select`,
281
280
  then: choicesSchema(Joi.string()).required(),
282
281
  otherwise: Joi.forbidden(),
283
282
  }),
283
+ language: Joi.when(`format`, {
284
+ is: `code`,
285
+ then: Joi.string(),
286
+ otherwise: Joi.forbidden(),
287
+ }),
284
288
  })
285
289
  );
286
290
 
@@ -385,7 +389,7 @@ const fileReplacer = (field: Common & FileInputField): Field => ({
385
389
  },
386
390
  ],
387
391
  default: field.default,
388
- ..._.pick(field, [`altered_by`, `disabled`, `required`, `as_password`, `show_label`, `as_array`, `array_min`, `array_max`, `array_length`]),
392
+ ..._.pick(field, [`altered_by`, `disabled`, `required`, `as_password`, `show`, `show_label`, `as_array`, `array_min`, `array_max`, `array_length`]),
389
393
  }) as Field;
390
394
 
391
395
  const fileInputFieldSchema = () =>
@@ -457,6 +461,8 @@ const getSimpleFieldSchema = (field: Field) => {
457
461
  switch (field.type) {
458
462
  case `any`:
459
463
  typeSchema = anySchema();
464
+ // allow null values for any type
465
+ if (field.allow_nullish) { typeSchema = typeSchema.allow(null, ``, 0, undefined); }
460
466
  break;
461
467
  case `boolean`:
462
468
  typeSchema = Joi.bool();
@@ -506,11 +512,15 @@ const getSimpleFieldSchema = (field: Field) => {
506
512
  typeSchema = Joi.number();
507
513
  if (field.min) { typeSchema = (typeSchema as NumberSchema).min(field.min); }
508
514
  if (field.max) { typeSchema = (typeSchema as NumberSchema).max(field.max); }
509
- if (field.float) {
510
- typeSchema = (typeSchema as NumberSchema).precision(field.decimals || 2);
511
- } else {
512
- typeSchema = (typeSchema as NumberSchema).precision(0);
515
+ // don't check precision if float is not set
516
+ if (typeof field.float === `boolean`) {
517
+ if (field.float) {
518
+ typeSchema = (typeSchema as NumberSchema).precision(field.decimals || 2);
519
+ } else {
520
+ typeSchema = (typeSchema as NumberSchema).precision(0);
521
+ }
513
522
  }
523
+
514
524
  break;
515
525
  case `dict`:
516
526
  if (field.items) { throw new Error(`No simple field`); }
@@ -563,8 +573,9 @@ const getObjectFieldSchema = (field: Field, items: { key: string, schema: Joi.Sc
563
573
  };
564
574
 
565
575
  const getGeneralSchema = (field: Field, schema: Joi.Schema): Joi.Schema => {
576
+ if (field.required) { schema = schema.required(); }
566
577
  if (field.disabled) { schema = schema.forbidden(); }
567
- return field.required ? schema.required() : schema;
578
+ return schema;
568
579
  };
569
580
 
570
581
  const convertPath = (parts: (string | number)[]): string =>
@@ -593,10 +604,33 @@ const relative_condition = (condition: Where, relative: string): typeof conditio
593
604
  } catch (e) { throw e; }
594
605
  };
595
606
 
596
- const recursive_schema = async (field: Field, data: Record<string, any>, options: { relative?: string, array?: boolean, context: Record<string, any> }): Promise<Joi.Schema> => {
607
+ export const alter = (field: Field, relative: string, data: Record<string, any>) => {
608
+ const cloned = _.cloneDeep(field);
609
+ for (const alteration of cloned.altered_by) {
610
+ const condition = relative_condition(alteration.condition, relative);
611
+
612
+ const matches = where(data, [condition]);
613
+ if (matches) { _.assign(cloned, alteration.overrides); }
614
+ }
615
+ return cloned;
616
+ };
617
+
618
+ const recursive_schema = async (field: Field, data: Record<string, any>, options: { alter: boolean, relative?: string, array?: boolean, context: Record<string, any> }): Promise<{ schema: Joi.Schema, field: Field }> => {
597
619
  try {
620
+ let newField: Field = _.cloneDeep(field);
621
+
622
+ // alterations over already resolved data (previous fields)
623
+ if (newField.altered_by?.length > 0) {
624
+ newField = alter(newField, options.relative, data);
625
+
626
+ // remove altered_by only if it's not an array -> array alterations must be kept for front-end rendering
627
+ if (options.alter) { delete newField.altered_by; }
628
+ }
629
+
630
+ const alteredField: Field = _.cloneDeep(newField);
631
+
598
632
  // ÑAPA: if the field is a file, replace it
599
- const newField = field.type === `file` ? fileReplacer(field) : _.cloneDeep(field);
633
+ if (newField.type === `file`) { newField = fileReplacer(newField); }
600
634
 
601
635
  const relativeKey = options.relative ?
602
636
  options.relative.endsWith(`]`) ?
@@ -613,61 +647,45 @@ const recursive_schema = async (field: Field, data: Record<string, any>, options
613
647
  // get schema for field with data
614
648
  let calculatedSchema: Joi.Schema;
615
649
  if (newField.as_array && resolved) {
616
-
617
650
  const itemSchemas: Joi.Schema[] = [];
618
651
  if (_.isArray(resolved)) {
619
652
  for (let i = 0; i < (resolved || []).length; i++) {
620
- const itemSchema = await recursive_schema(
653
+ const { schema: itemSchema } = await recursive_schema(
621
654
  _.omit(newField, [`as_array`, `array_min`, `array_max`, `array_length`]) as Field,
622
- data, { relative: `${relativeKey}[${i}]`, array: true, context: options.context });
655
+ data, { alter: false, relative: `${relativeKey}[${i}]`, array: true, context: options.context });
623
656
  if (itemSchema) { itemSchemas.push(itemSchema); }
624
657
  }
625
658
  }
626
659
 
627
660
  calculatedSchema = getArrayFieldSchema(newField, itemSchemas);
628
661
  } else if (newField.type === `dict` && newField.items) {
629
- const itemSchemas: { key: string, schema: Joi.Schema }[] = [];
662
+ const items: { key: string, schema: Joi.Schema, field: Field }[] = [];
630
663
 
631
664
  if (_.isObject(resolved)) {
632
665
  for (const item of newField.items) {
633
- const itemSchema = await recursive_schema(item, data, { relative: relativeKey, context: options.context });
634
- if (itemSchema) { itemSchemas.push({ key: item.key, schema: itemSchema }); }
666
+ const recursive = await recursive_schema(item, data, { alter: options.alter && true, relative: relativeKey, context: options.context });
667
+ if (recursive) { items.push({ key: item.key, schema: recursive.schema, field: recursive.field }); }
635
668
  }
636
669
  }
637
670
 
638
- calculatedSchema = getObjectFieldSchema(newField, itemSchemas);
671
+ calculatedSchema = getObjectFieldSchema(newField, items);
672
+ newField.items = items.map((i) => i.field);
639
673
  } else {
640
674
  calculatedSchema = getSimpleFieldSchema(newField);
641
675
  }
642
676
 
643
677
  if (!calculatedSchema) { throw new Error(`Could not resolve schema for field ${newField.key}`); }
644
678
 
645
- // alterations over data
646
- if (newField.altered_by?.length > 0) {
647
- let hide = false;
648
- for (const alteration of newField.altered_by) {
649
- const condition = relative_condition(alteration.condition, options.relative);
650
-
651
- const matches = where(data, [condition]);
652
- if (matches) {
653
- if (`overrides` in alteration) {
654
- _.assign(newField, alteration.overrides);
655
- } else if (`show` in alteration) {
656
- hide = !alteration.show;
657
- }
658
- }
659
- }
660
-
661
- delete newField.altered_by;
662
-
663
- if (hide) {
664
- // if the field is hidden, we don't need to validate it
665
- _.unset(data, relativeKey);
666
- return;
667
- }
679
+ if (typeof newField.show === `boolean` && !newField.show) {
680
+ // if the field is hidden, we don't need to validate it
681
+ _.unset(data, relativeKey);
682
+ return;
683
+ } else if (newField.disabled) {
684
+ // TODO: check this
685
+ _.unset(data, relativeKey);
668
686
  }
669
687
 
670
- return calculatedSchema;
688
+ return { schema: calculatedSchema, field: field.type === `file` ? alteredField : newField };
671
689
  } catch (e) { throw e; }
672
690
  };
673
691
 
@@ -681,14 +699,15 @@ const validate_internal = async (fields: Field[], data: Record<string, any>, opt
681
699
 
682
700
  const cloned = _.cloneDeep(data);
683
701
 
684
- const schema = await recursive_schema(field, cloned, { context: options.context });
702
+ const recursive = await recursive_schema(field, cloned, { alter: true, context: options.context });
703
+ if (!recursive) { continue; }
685
704
 
686
705
  const resolved = _.get(cloned, field.key, field.default);
687
706
 
688
707
  let validation: any;
689
708
  let error: any;
690
709
  try {
691
- validation = await schema.validateAsync(resolved, { abortEarly: false });
710
+ validation = await recursive.schema.validateAsync(resolved, { abortEarly: false });
692
711
  } catch (e) { error = e; }
693
712
 
694
713
  let extended: ValidationExtended;
@@ -713,7 +732,7 @@ const validate_internal = async (fields: Field[], data: Record<string, any>, opt
713
732
 
714
733
  response.valid &&= error ? false : true;
715
734
  response.fields.push({
716
- ...field as any,
735
+ ...recursive.field as any,
717
736
  ...options.secure ? await secure(field, extended) : extended,
718
737
  });
719
738
 
@@ -55,7 +55,7 @@ const commandSchema = Joi.alternatives(
55
55
  );
56
56
 
57
57
  const API_SOURCES = (provider: Provider, configuration: Protocol.SourcesConfiguration, context: { organization: string, workspace: number }): SourceFunctions => ({
58
- register: async (source, data) => {
58
+ register: async (source, sandbox, data) => {
59
59
  try {
60
60
  const { data: [created] } = await axios.post(`/api/v1/sources`, {
61
61
  organization: context.organization,
@@ -63,6 +63,7 @@ const API_SOURCES = (provider: Provider, configuration: Protocol.SourcesConfigur
63
63
  provider: provider.id,
64
64
  version: provider.version,
65
65
  source: source,
66
+ sandbox: sandbox,
66
67
  data: data,
67
68
  created_date: moment().format(`YYYY-MM-DDTHH:mm:ssZ`),
68
69
  }, {
@@ -75,7 +76,7 @@ const API_SOURCES = (provider: Provider, configuration: Protocol.SourcesConfigur
75
76
  return { id: created.uuid };
76
77
  } catch (e) { throw e; }
77
78
  },
78
- unregister: async (source, id) => {
79
+ unregister: async (source, sandbox, id) => {
79
80
  try {
80
81
  const { data: [get] } = await axios.post(`/api/v1/sources/list`, {
81
82
  organization: context.organization,
@@ -83,6 +84,7 @@ const API_SOURCES = (provider: Provider, configuration: Protocol.SourcesConfigur
83
84
  provider: provider.id,
84
85
  version: provider.version,
85
86
  source: source,
87
+ sandbox: sandbox,
86
88
  uuid: id,
87
89
  }, {
88
90
  baseURL: configuration.host,
@@ -100,7 +102,7 @@ const API_SOURCES = (provider: Provider, configuration: Protocol.SourcesConfigur
100
102
  });
101
103
  } catch (e) { throw e; }
102
104
  },
103
- consume: async (source) => {
105
+ consume: async (source, sandbox) => {
104
106
  try {
105
107
  const { data: sources } = await axios.post(`/api/v1/sources/list`, {
106
108
  organization: context.organization,
@@ -108,6 +110,7 @@ const API_SOURCES = (provider: Provider, configuration: Protocol.SourcesConfigur
108
110
  provider: provider.id,
109
111
  version: provider.version,
110
112
  source: source,
113
+ sandbox: sandbox,
111
114
  }, {
112
115
  baseURL: configuration.host,
113
116
  headers: {
@@ -15,4 +15,6 @@ export type DeepPartial<T extends Record<string, any>> = {
15
15
  export type AsyncReturnType<T extends (...args: any[]) => any> =
16
16
  T extends (...args: any[]) => Promise<infer U> ? U :
17
17
  T extends (...args: any[]) => infer U ? U :
18
- any;
18
+ any;
19
+
20
+ export type OneOrArray<T> = T | T[];
package/tsconfig.json CHANGED
@@ -11,10 +11,9 @@
11
11
  "declaration": true
12
12
  },
13
13
  "include": [
14
- "src/**/*.ts",
14
+ "src/**/*.ts"
15
15
  ],
16
16
  "exclude": [
17
- "./test",
18
17
  "node_modules",
19
18
  "dist/**/*.js"
20
19
  ]