@webiny/api-headless-cms 6.5.0-beta.9 → 6.5.1-beta.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.
- package/crud/contentEntry/entryDataValidation.js +14 -7
- package/crud/contentEntry/entryDataValidation.js.map +1 -1
- package/domain/contentModel/validation/dateExpressions.d.ts +6 -0
- package/domain/contentModel/validation/dateExpressions.js +27 -0
- package/domain/contentModel/validation/dateExpressions.js.map +1 -0
- package/domain/contentModel/validation/modelFields.js +2 -0
- package/domain/contentModel/validation/modelFields.js.map +1 -1
- package/features/contentEntry/PublishEntry/PublishEntryUseCase.d.ts +2 -2
- package/features/contentEntry/PublishEntry/PublishEntryUseCase.js +6 -2
- package/features/contentEntry/PublishEntry/PublishEntryUseCase.js.map +1 -1
- package/features/contentEntry/PublishEntry/abstractions.d.ts +8 -1
- package/features/contentEntry/PublishEntry/abstractions.js.map +1 -1
- package/features/modelBuilder/fields/DataFieldBuilder.d.ts +9 -0
- package/features/modelBuilder/fields/DataFieldBuilder.js +1 -0
- package/features/modelBuilder/fields/DataFieldBuilder.js.map +1 -1
- package/features/modelBuilder/fields/DateTimeFieldType.js +26 -12
- package/features/modelBuilder/fields/DateTimeFieldType.js.map +1 -1
- package/features/modelBuilder/fields/fieldTypeValidator.d.ts +5 -3
- package/features/validation/abstractions/CmsModelFieldValidator.d.ts +5 -0
- package/features/validation/abstractions/CmsModelFieldValidator.js.map +1 -1
- package/features/validation/validators/DateGteValidator.d.ts +2 -0
- package/features/validation/validators/DateGteValidator.js +12 -2
- package/features/validation/validators/DateGteValidator.js.map +1 -1
- package/features/validation/validators/DateLteValidator.d.ts +2 -0
- package/features/validation/validators/DateLteValidator.js +12 -2
- package/features/validation/validators/DateLteValidator.js.map +1 -1
- package/features/validation/validators/dateLimit.d.ts +9 -0
- package/features/validation/validators/dateLimit.js +18 -0
- package/features/validation/validators/dateLimit.js.map +1 -0
- package/graphql/schema/contentEntries.js +17 -2
- package/graphql/schema/contentEntries.js.map +1 -1
- package/package.json +24 -24
|
@@ -8,16 +8,20 @@ const validateValue = async (params, fieldValidators, value)=>{
|
|
|
8
8
|
const name = fieldValidator.name;
|
|
9
9
|
const validations = validatorList[name];
|
|
10
10
|
if (!validations || 0 === validations.length) return `There are no "${name}" validators defined.`;
|
|
11
|
-
for (const
|
|
12
|
-
const
|
|
11
|
+
for (const validation of validations){
|
|
12
|
+
const validateParams = {
|
|
13
13
|
value,
|
|
14
14
|
context,
|
|
15
15
|
validator: fieldValidator,
|
|
16
16
|
field,
|
|
17
17
|
model,
|
|
18
18
|
entry
|
|
19
|
-
}
|
|
20
|
-
|
|
19
|
+
};
|
|
20
|
+
const result = await validation.validate(validateParams);
|
|
21
|
+
if (!result) {
|
|
22
|
+
const message = fieldValidator.message;
|
|
23
|
+
return message && validation.formatMessage ? validation.formatMessage(message, validateParams) : message;
|
|
24
|
+
}
|
|
21
25
|
}
|
|
22
26
|
}
|
|
23
27
|
} catch (ex) {
|
|
@@ -99,9 +103,12 @@ const validateModelEntryData = async (params)=>{
|
|
|
99
103
|
for (const validator of validators){
|
|
100
104
|
const name = validator.name;
|
|
101
105
|
if (!validatorList[name]) validatorList[name] = [];
|
|
102
|
-
validatorList[name].push(
|
|
106
|
+
validatorList[name].push({
|
|
107
|
+
validate: (params)=>validator.validate(params),
|
|
108
|
+
formatMessage: validator.formatMessage ? (message, params)=>validator.formatMessage(message, params) : void 0
|
|
109
|
+
});
|
|
103
110
|
}
|
|
104
|
-
return await
|
|
111
|
+
return await validate({
|
|
105
112
|
validatorList,
|
|
106
113
|
context,
|
|
107
114
|
model,
|
|
@@ -216,7 +223,7 @@ const executeFieldValidation = async (params)=>{
|
|
|
216
223
|
}
|
|
217
224
|
];
|
|
218
225
|
};
|
|
219
|
-
const
|
|
226
|
+
const validate = async (params)=>{
|
|
220
227
|
const { fields } = params;
|
|
221
228
|
const errors = [];
|
|
222
229
|
const results = await Promise.all(fields.map(async (field)=>await executeFieldValidation({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crud/contentEntry/entryDataValidation.js","sources":["../../../src/crud/contentEntry/entryDataValidation.ts"],"sourcesContent":["import type {\n CmsContext,\n CmsDynamicZoneTemplate,\n CmsEntry,\n CmsEntryValues,\n CmsModel,\n CmsModelField,\n CmsModelFieldValidation\n} from \"~/types/index.js\";\nimport { EntryValidationError } from \"~/domain/contentEntry/errors.js\";\nimport {\n type CmsModelFieldValidator,\n CmsModelFieldValidatorRegistry\n} from \"~/features/validation/index.js\";\n\ntype PluginValidationCallable = (\n params: CmsModelFieldValidator.Params\n) => CmsModelFieldValidator.Response;\ntype PluginValidationList = Record<string, PluginValidationCallable[]>;\n\ninterface ExecuteValidationParams<TValues extends CmsEntryValues = CmsEntryValues> {\n validatorList: PluginValidationList;\n field: CmsModelField;\n model: CmsModel;\n values: TValues;\n context: CmsContext;\n entry?: CmsEntry<TValues>;\n}\n\ntype PossibleValue = boolean | number | string | null | undefined;\n\nconst validateValue = async <TValues extends CmsEntryValues = CmsEntryValues>(\n params: ExecuteValidationParams<TValues>,\n fieldValidators: CmsModelFieldValidation[],\n value: PossibleValue | PossibleValue[]\n): Promise<string | null> => {\n if (!fieldValidators) {\n return null;\n }\n const { validatorList, context, field, model, entry } = params;\n try {\n for (const fieldValidator of fieldValidators) {\n const name = fieldValidator.name;\n const validations = validatorList[name];\n if (!validations || validations.length === 0) {\n return `There are no \"${name}\" validators defined.`;\n }\n for (const validate of validations) {\n const result = await validate({\n value,\n context,\n validator: fieldValidator,\n field,\n model,\n entry\n });\n if (!result) {\n return fieldValidator.message;\n }\n }\n }\n } catch (ex) {\n return ex.message;\n }\n\n return null;\n};\n\nconst validatePredefinedValue = (field: CmsModelField, value: any | any[]): string | null => {\n const { enabled = false, values: predefinedValues = [] } = field.predefinedValues || {};\n if (!enabled) {\n return null;\n } else if (Array.isArray(predefinedValues) === false || predefinedValues.length === 0) {\n return \"Missing predefined values to validate against.\";\n } else if (value === \"\" || value === null || value === undefined) {\n return null;\n }\n for (const predefinedValue of predefinedValues) {\n /**\n * No strict compare because the value sent can be 12345 (number) and predefinedValue can be \"12345\" (string),\n * and we want it to match.\n */\n if (predefinedValue.value == value) {\n return null;\n }\n }\n return \"Value sent does not match any of the available predefined values.\";\n};\n\nconst DATE_RE = /^\\d{4}-\\d{2}-\\d{2}$/;\nconst TIME_RE = /^\\d{2}:\\d{2}(:\\d{2})?$/;\nconst DATETIME_RE = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}/;\n\nconst validateDatetimeFormat = (field: CmsModelField, value: any): string | null => {\n if (field.type !== \"datetime\") {\n return null;\n }\n if (value === null || value === undefined || value === \"\") {\n return null;\n }\n if (typeof value !== \"string\") {\n return null;\n }\n\n const dateType = field.settings?.type;\n\n switch (dateType) {\n case \"date\":\n if (!DATE_RE.test(value)) {\n return `Invalid date format for field \"${field.fieldId}\". Expected YYYY-MM-DD.`;\n }\n return null;\n case \"time\":\n if (!TIME_RE.test(value)) {\n return `Invalid time format for field \"${field.fieldId}\". Expected HH:mm or HH:mm:ss.`;\n }\n return null;\n case \"dateTimeWithTimezone\":\n case \"dateTimeWithoutTimezone\":\n if (!DATETIME_RE.test(value)) {\n return `Invalid dateTime format for field \"${field.fieldId}\". Expected ISO 8601 dateTime.`;\n }\n return null;\n default:\n return null;\n }\n};\n\nconst getFieldValidation = (\n listValidation?: CmsModelFieldValidation[]\n): CmsModelFieldValidation[] => {\n if (!listValidation?.length) {\n return [];\n }\n // Filter out the legacy \"dynamicZone\" validator that was never implemented.\n // Existing models may still have it in their persisted listValidation data.\n return listValidation.filter(item => item.name !== \"dynamicZone\");\n};\n/**\n * When multiple values is selected we must run validations on the array containing the values\n * And then on each value in the array\n */\nconst runFieldMultipleValuesValidations = async <TValues extends CmsEntryValues = CmsEntryValues>(\n params: ExecuteValidationParams<TValues>\n): Promise<string | null> => {\n const { field, values: initialValues } = params;\n const values = initialValues[field.fieldId as keyof TValues] as\n | PossibleValue[]\n | null\n | undefined;\n const valuesError = await validateValue(\n params,\n getFieldValidation(field.listValidation),\n values\n );\n if (valuesError) {\n return valuesError;\n }\n if (values === null || values === undefined) {\n return null;\n }\n for (const value of values) {\n const datetimeError = validateDatetimeFormat(field, value);\n if (datetimeError) {\n return datetimeError;\n }\n const valueError = await validateValue(params, getFieldValidation(field.validation), value);\n if (valueError) {\n return valueError;\n }\n const predefinedValueError = validatePredefinedValue(field, value);\n if (predefinedValueError) {\n return predefinedValueError;\n }\n }\n return null;\n};\n/**\n * Runs validation on given value.\n */\nconst runFieldValueValidations = async <TValues extends CmsEntryValues = CmsEntryValues>(\n params: ExecuteValidationParams<TValues>\n): Promise<string | null> => {\n const { values, field } = params;\n const value = values[field.fieldId as keyof TValues];\n const datetimeError = validateDatetimeFormat(field, value);\n if (datetimeError) {\n return datetimeError;\n }\n const error = await validateValue(params, field.validation || [], value);\n if (error) {\n return error;\n }\n return validatePredefinedValue(field, value);\n};\n\nconst execValidation = async (params: ExecuteValidationParams): Promise<string | null> => {\n const { field } = params;\n if (field.list) {\n return await runFieldMultipleValuesValidations(params);\n }\n return await runFieldValueValidations(params);\n};\n\ninterface IValidateModelEntryDataParams<TValues extends CmsEntryValues = CmsEntryValues> {\n context: CmsContext;\n model: CmsModel;\n values: TValues;\n entry?: CmsEntry<TValues>;\n skipValidation?: boolean;\n}\n\nexport const validateModelEntryData = async <TValues extends CmsEntryValues = CmsEntryValues>(\n params: IValidateModelEntryDataParams<TValues>\n) => {\n const { context, model, entry, values, skipValidation } = params;\n\n if (skipValidation) {\n return [];\n }\n\n const validatorList: PluginValidationList = {};\n const registry = context.container.resolve(CmsModelFieldValidatorRegistry);\n const validators = registry.getAll();\n for (const validator of validators) {\n const name = validator.name;\n if (!validatorList[name]) {\n validatorList[name] = [];\n }\n validatorList[name].push(params => {\n return validator.validate(params);\n });\n }\n\n return await validate<TValues>({\n validatorList,\n context,\n model,\n entry,\n parents: [],\n fields: model.fields,\n values: {\n ...entry?.values,\n ...values\n }\n });\n};\n\nexport const validateModelEntryDataOrThrow = async <\n TValues extends CmsEntryValues = CmsEntryValues\n>(\n params: IValidateModelEntryDataParams<TValues>\n) => {\n const invalidFields = await validateModelEntryData(params);\n if (invalidFields.length === 0) {\n return;\n }\n throw new EntryValidationError(\"Validation failed.\", invalidFields);\n};\n\n/**\n *\n */\ninterface FieldError {\n id: string;\n fieldId: string;\n storageId: string;\n error: any;\n parents: string[];\n}\n\ninterface ValidateFieldParams<TValues extends CmsEntryValues = CmsEntryValues> {\n validatorList: PluginValidationList;\n parents: string[];\n model: CmsModel;\n values: TValues;\n context: CmsContext;\n field: CmsModelField;\n entry?: CmsEntry<TValues>;\n}\n\nconst executeFieldValidation = async <TValues extends CmsEntryValues = CmsEntryValues>(\n params: ValidateFieldParams<TValues>\n): Promise<FieldError[]> => {\n // TODO put per-field validation into plugins.\n const { field } = params;\n /**\n * Object field.\n */\n if (field.type === \"object\") {\n const fields = field.settings?.fields;\n if (!Array.isArray(fields)) {\n return [];\n }\n const validations: FieldError[] = [];\n /**\n * We need to validate the object field as well.\n */\n const error = await execValidation({\n ...params,\n field\n });\n if (error) {\n validations.push({\n id: field.id,\n fieldId: field.fieldId,\n storageId: field.storageId,\n error,\n parents: params.parents\n });\n }\n const objectValue = params.values[field.fieldId as keyof TValues];\n if (!objectValue) {\n return validations;\n }\n const values = (Array.isArray(objectValue) ? objectValue : [objectValue]) as TValues[];\n for (const index in values) {\n const parents = field.list ? [field.fieldId, index] : [field.fieldId];\n const value = values[index] as TValues;\n for (const childField of fields) {\n const errors = await executeFieldValidation<typeof value>({\n ...params,\n parents: params.parents.concat(parents),\n field: childField,\n values: value\n });\n if (errors.length === 0) {\n continue;\n }\n validations.push(...errors);\n }\n }\n return validations;\n }\n /**\n * Dynamic Zone Field\n */\n //\n else if (field.type === \"dynamicZone\") {\n const validations: FieldError[] = [];\n\n const error = await execValidation({\n ...params,\n field\n });\n if (error) {\n validations.push({\n id: field.id,\n fieldId: field.fieldId,\n storageId: field.storageId,\n error,\n parents: params.parents\n });\n }\n\n const templates = (field.settings?.templates || []) as CmsDynamicZoneTemplate[];\n for (const template of templates) {\n const fields = template.fields;\n const fieldData = params.values[field.fieldId as keyof TValues];\n if (!fieldData) {\n continue;\n }\n const values: TValues[keyof TValues][] = Array.isArray(fieldData)\n ? fieldData\n : [fieldData];\n for (const index in values) {\n const templateValue = values[index]?.[template.gqlTypeName];\n if (!templateValue) {\n continue;\n }\n /**\n * Order of the parents must be\n * - fieldId\n * - index (if multiple values)\n * - gqlTypeName\n */\n const parents = [field.fieldId];\n if (field.list) {\n parents.push(index);\n }\n parents.push(template.gqlTypeName);\n for (const childField of fields) {\n const errors = await executeFieldValidation({\n ...params,\n parents: params.parents.concat(parents),\n field: childField,\n values: templateValue\n });\n if (errors.length === 0) {\n continue;\n }\n validations.push(...errors);\n }\n }\n }\n\n return validations;\n }\n const error = await execValidation({\n ...params\n });\n if (!error) {\n return [];\n }\n return [\n {\n id: field.id,\n fieldId: field.fieldId,\n storageId: field.storageId,\n error,\n parents: params.parents\n }\n ];\n};\n\ninterface ValidateFieldsParams<TValues extends CmsEntryValues = CmsEntryValues> {\n validatorList: PluginValidationList;\n parents: string[];\n model: CmsModel;\n values: TValues;\n fields: CmsModelField[];\n context: CmsContext;\n entry?: CmsEntry<TValues>;\n}\n\nconst validate = async <TValues extends CmsEntryValues = CmsEntryValues>(\n params: ValidateFieldsParams<TValues>\n): Promise<FieldError[]> => {\n const { fields } = params;\n const errors: FieldError[] = [];\n\n const results = await Promise.all(\n fields.map(async field => {\n return await executeFieldValidation<TValues>({\n ...params,\n field\n });\n })\n );\n\n for (const result of results) {\n if (result.length === 0) {\n continue;\n }\n errors.push(...result);\n }\n return errors;\n};\n"],"names":["validateValue","params","fieldValidators","value","validatorList","context","field","model","entry","fieldValidator","name","validations","validate","result","ex","validatePredefinedValue","enabled","predefinedValues","Array","predefinedValue","DATE_RE","TIME_RE","DATETIME_RE","validateDatetimeFormat","dateType","getFieldValidation","listValidation","item","runFieldMultipleValuesValidations","initialValues","values","valuesError","datetimeError","valueError","predefinedValueError","runFieldValueValidations","error","execValidation","validateModelEntryData","skipValidation","registry","CmsModelFieldValidatorRegistry","validators","validator","validateModelEntryDataOrThrow","invalidFields","EntryValidationError","executeFieldValidation","fields","objectValue","index","parents","childField","errors","templates","template","fieldData","templateValue","results","Promise"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"crud/contentEntry/entryDataValidation.js","sources":["../../../src/crud/contentEntry/entryDataValidation.ts"],"sourcesContent":["import type {\n CmsContext,\n CmsDynamicZoneTemplate,\n CmsEntry,\n CmsEntryValues,\n CmsModel,\n CmsModelField,\n CmsModelFieldValidation\n} from \"~/types/index.js\";\nimport { EntryValidationError } from \"~/domain/contentEntry/errors.js\";\nimport {\n type CmsModelFieldValidator,\n CmsModelFieldValidatorRegistry\n} from \"~/features/validation/index.js\";\n\ntype PluginValidationList = Record<\n string,\n Pick<CmsModelFieldValidator.Interface, \"validate\" | \"formatMessage\">[]\n>;\n\ninterface ExecuteValidationParams<TValues extends CmsEntryValues = CmsEntryValues> {\n validatorList: PluginValidationList;\n field: CmsModelField;\n model: CmsModel;\n values: TValues;\n context: CmsContext;\n entry?: CmsEntry<TValues>;\n}\n\ntype PossibleValue = boolean | number | string | null | undefined;\n\nconst validateValue = async <TValues extends CmsEntryValues = CmsEntryValues>(\n params: ExecuteValidationParams<TValues>,\n fieldValidators: CmsModelFieldValidation[],\n value: PossibleValue | PossibleValue[]\n): Promise<string | null> => {\n if (!fieldValidators) {\n return null;\n }\n const { validatorList, context, field, model, entry } = params;\n try {\n for (const fieldValidator of fieldValidators) {\n const name = fieldValidator.name;\n const validations = validatorList[name];\n if (!validations || validations.length === 0) {\n return `There are no \"${name}\" validators defined.`;\n }\n for (const validation of validations) {\n const validateParams: CmsModelFieldValidator.Params = {\n value,\n context,\n validator: fieldValidator,\n field,\n model,\n entry\n };\n const result = await validation.validate(validateParams);\n if (!result) {\n const message = fieldValidator.message;\n return message && validation.formatMessage\n ? validation.formatMessage(message, validateParams)\n : message;\n }\n }\n }\n } catch (ex) {\n return ex.message;\n }\n\n return null;\n};\n\nconst validatePredefinedValue = (field: CmsModelField, value: any | any[]): string | null => {\n const { enabled = false, values: predefinedValues = [] } = field.predefinedValues || {};\n if (!enabled) {\n return null;\n } else if (Array.isArray(predefinedValues) === false || predefinedValues.length === 0) {\n return \"Missing predefined values to validate against.\";\n } else if (value === \"\" || value === null || value === undefined) {\n return null;\n }\n for (const predefinedValue of predefinedValues) {\n /**\n * No strict compare because the value sent can be 12345 (number) and predefinedValue can be \"12345\" (string),\n * and we want it to match.\n */\n if (predefinedValue.value == value) {\n return null;\n }\n }\n return \"Value sent does not match any of the available predefined values.\";\n};\n\nconst DATE_RE = /^\\d{4}-\\d{2}-\\d{2}$/;\nconst TIME_RE = /^\\d{2}:\\d{2}(:\\d{2})?$/;\nconst DATETIME_RE = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}/;\n\nconst validateDatetimeFormat = (field: CmsModelField, value: any): string | null => {\n if (field.type !== \"datetime\") {\n return null;\n }\n if (value === null || value === undefined || value === \"\") {\n return null;\n }\n if (typeof value !== \"string\") {\n return null;\n }\n\n const dateType = field.settings?.type;\n\n switch (dateType) {\n case \"date\":\n if (!DATE_RE.test(value)) {\n return `Invalid date format for field \"${field.fieldId}\". Expected YYYY-MM-DD.`;\n }\n return null;\n case \"time\":\n if (!TIME_RE.test(value)) {\n return `Invalid time format for field \"${field.fieldId}\". Expected HH:mm or HH:mm:ss.`;\n }\n return null;\n case \"dateTimeWithTimezone\":\n case \"dateTimeWithoutTimezone\":\n if (!DATETIME_RE.test(value)) {\n return `Invalid dateTime format for field \"${field.fieldId}\". Expected ISO 8601 dateTime.`;\n }\n return null;\n default:\n return null;\n }\n};\n\nconst getFieldValidation = (\n listValidation?: CmsModelFieldValidation[]\n): CmsModelFieldValidation[] => {\n if (!listValidation?.length) {\n return [];\n }\n // Filter out the legacy \"dynamicZone\" validator that was never implemented.\n // Existing models may still have it in their persisted listValidation data.\n return listValidation.filter(item => item.name !== \"dynamicZone\");\n};\n/**\n * When multiple values is selected we must run validations on the array containing the values\n * And then on each value in the array\n */\nconst runFieldMultipleValuesValidations = async <TValues extends CmsEntryValues = CmsEntryValues>(\n params: ExecuteValidationParams<TValues>\n): Promise<string | null> => {\n const { field, values: initialValues } = params;\n const values = initialValues[field.fieldId as keyof TValues] as\n | PossibleValue[]\n | null\n | undefined;\n const valuesError = await validateValue(\n params,\n getFieldValidation(field.listValidation),\n values\n );\n if (valuesError) {\n return valuesError;\n }\n if (values === null || values === undefined) {\n return null;\n }\n for (const value of values) {\n const datetimeError = validateDatetimeFormat(field, value);\n if (datetimeError) {\n return datetimeError;\n }\n const valueError = await validateValue(params, getFieldValidation(field.validation), value);\n if (valueError) {\n return valueError;\n }\n const predefinedValueError = validatePredefinedValue(field, value);\n if (predefinedValueError) {\n return predefinedValueError;\n }\n }\n return null;\n};\n/**\n * Runs validation on given value.\n */\nconst runFieldValueValidations = async <TValues extends CmsEntryValues = CmsEntryValues>(\n params: ExecuteValidationParams<TValues>\n): Promise<string | null> => {\n const { values, field } = params;\n const value = values[field.fieldId as keyof TValues];\n const datetimeError = validateDatetimeFormat(field, value);\n if (datetimeError) {\n return datetimeError;\n }\n const error = await validateValue(params, field.validation || [], value);\n if (error) {\n return error;\n }\n return validatePredefinedValue(field, value);\n};\n\nconst execValidation = async (params: ExecuteValidationParams): Promise<string | null> => {\n const { field } = params;\n if (field.list) {\n return await runFieldMultipleValuesValidations(params);\n }\n return await runFieldValueValidations(params);\n};\n\ninterface IValidateModelEntryDataParams<TValues extends CmsEntryValues = CmsEntryValues> {\n context: CmsContext;\n model: CmsModel;\n values: TValues;\n entry?: CmsEntry<TValues>;\n skipValidation?: boolean;\n}\n\nexport const validateModelEntryData = async <TValues extends CmsEntryValues = CmsEntryValues>(\n params: IValidateModelEntryDataParams<TValues>\n) => {\n const { context, model, entry, values, skipValidation } = params;\n\n if (skipValidation) {\n return [];\n }\n\n const validatorList: PluginValidationList = {};\n const registry = context.container.resolve(CmsModelFieldValidatorRegistry);\n const validators = registry.getAll();\n for (const validator of validators) {\n const name = validator.name;\n if (!validatorList[name]) {\n validatorList[name] = [];\n }\n validatorList[name].push({\n validate: params => validator.validate(params),\n formatMessage: validator.formatMessage\n ? (message, params) => validator.formatMessage!(message, params)\n : undefined\n });\n }\n\n return await validate<TValues>({\n validatorList,\n context,\n model,\n entry,\n parents: [],\n fields: model.fields,\n values: {\n ...entry?.values,\n ...values\n }\n });\n};\n\nexport const validateModelEntryDataOrThrow = async <\n TValues extends CmsEntryValues = CmsEntryValues\n>(\n params: IValidateModelEntryDataParams<TValues>\n) => {\n const invalidFields = await validateModelEntryData(params);\n if (invalidFields.length === 0) {\n return;\n }\n throw new EntryValidationError(\"Validation failed.\", invalidFields);\n};\n\n/**\n *\n */\ninterface FieldError {\n id: string;\n fieldId: string;\n storageId: string;\n error: any;\n parents: string[];\n}\n\ninterface ValidateFieldParams<TValues extends CmsEntryValues = CmsEntryValues> {\n validatorList: PluginValidationList;\n parents: string[];\n model: CmsModel;\n values: TValues;\n context: CmsContext;\n field: CmsModelField;\n entry?: CmsEntry<TValues>;\n}\n\nconst executeFieldValidation = async <TValues extends CmsEntryValues = CmsEntryValues>(\n params: ValidateFieldParams<TValues>\n): Promise<FieldError[]> => {\n // TODO put per-field validation into plugins.\n const { field } = params;\n /**\n * Object field.\n */\n if (field.type === \"object\") {\n const fields = field.settings?.fields;\n if (!Array.isArray(fields)) {\n return [];\n }\n const validations: FieldError[] = [];\n /**\n * We need to validate the object field as well.\n */\n const error = await execValidation({\n ...params,\n field\n });\n if (error) {\n validations.push({\n id: field.id,\n fieldId: field.fieldId,\n storageId: field.storageId,\n error,\n parents: params.parents\n });\n }\n const objectValue = params.values[field.fieldId as keyof TValues];\n if (!objectValue) {\n return validations;\n }\n const values = (Array.isArray(objectValue) ? objectValue : [objectValue]) as TValues[];\n for (const index in values) {\n const parents = field.list ? [field.fieldId, index] : [field.fieldId];\n const value = values[index] as TValues;\n for (const childField of fields) {\n const errors = await executeFieldValidation<typeof value>({\n ...params,\n parents: params.parents.concat(parents),\n field: childField,\n values: value\n });\n if (errors.length === 0) {\n continue;\n }\n validations.push(...errors);\n }\n }\n return validations;\n }\n /**\n * Dynamic Zone Field\n */\n //\n else if (field.type === \"dynamicZone\") {\n const validations: FieldError[] = [];\n\n const error = await execValidation({\n ...params,\n field\n });\n if (error) {\n validations.push({\n id: field.id,\n fieldId: field.fieldId,\n storageId: field.storageId,\n error,\n parents: params.parents\n });\n }\n\n const templates = (field.settings?.templates || []) as CmsDynamicZoneTemplate[];\n for (const template of templates) {\n const fields = template.fields;\n const fieldData = params.values[field.fieldId as keyof TValues];\n if (!fieldData) {\n continue;\n }\n const values: TValues[keyof TValues][] = Array.isArray(fieldData)\n ? fieldData\n : [fieldData];\n for (const index in values) {\n const templateValue = values[index]?.[template.gqlTypeName];\n if (!templateValue) {\n continue;\n }\n /**\n * Order of the parents must be\n * - fieldId\n * - index (if multiple values)\n * - gqlTypeName\n */\n const parents = [field.fieldId];\n if (field.list) {\n parents.push(index);\n }\n parents.push(template.gqlTypeName);\n for (const childField of fields) {\n const errors = await executeFieldValidation({\n ...params,\n parents: params.parents.concat(parents),\n field: childField,\n values: templateValue\n });\n if (errors.length === 0) {\n continue;\n }\n validations.push(...errors);\n }\n }\n }\n\n return validations;\n }\n const error = await execValidation({\n ...params\n });\n if (!error) {\n return [];\n }\n return [\n {\n id: field.id,\n fieldId: field.fieldId,\n storageId: field.storageId,\n error,\n parents: params.parents\n }\n ];\n};\n\ninterface ValidateFieldsParams<TValues extends CmsEntryValues = CmsEntryValues> {\n validatorList: PluginValidationList;\n parents: string[];\n model: CmsModel;\n values: TValues;\n fields: CmsModelField[];\n context: CmsContext;\n entry?: CmsEntry<TValues>;\n}\n\nconst validate = async <TValues extends CmsEntryValues = CmsEntryValues>(\n params: ValidateFieldsParams<TValues>\n): Promise<FieldError[]> => {\n const { fields } = params;\n const errors: FieldError[] = [];\n\n const results = await Promise.all(\n fields.map(async field => {\n return await executeFieldValidation<TValues>({\n ...params,\n field\n });\n })\n );\n\n for (const result of results) {\n if (result.length === 0) {\n continue;\n }\n errors.push(...result);\n }\n return errors;\n};\n"],"names":["validateValue","params","fieldValidators","value","validatorList","context","field","model","entry","fieldValidator","name","validations","validation","validateParams","result","message","ex","validatePredefinedValue","enabled","predefinedValues","Array","predefinedValue","DATE_RE","TIME_RE","DATETIME_RE","validateDatetimeFormat","dateType","getFieldValidation","listValidation","item","runFieldMultipleValuesValidations","initialValues","values","valuesError","datetimeError","valueError","predefinedValueError","runFieldValueValidations","error","execValidation","validateModelEntryData","skipValidation","registry","CmsModelFieldValidatorRegistry","validators","validator","undefined","validate","validateModelEntryDataOrThrow","invalidFields","EntryValidationError","executeFieldValidation","fields","objectValue","index","parents","childField","errors","templates","template","fieldData","templateValue","results","Promise"],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { CmsModelField } from "../../../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* A `dateGte`/`dateLte` limit given as `{ expression }` must parse, and is allowed only on a
|
|
4
|
+
* date-only field: on a time or datetime field, "today" has no single meaning.
|
|
5
|
+
*/
|
|
6
|
+
export declare const validateDateExpressions: (field: Pick<CmsModelField, "fieldId" | "type" | "settings" | "validation">) => void;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import error from "@webiny/error";
|
|
2
|
+
import { isDateExpression } from "@webiny/utils";
|
|
3
|
+
const DATE_LIMIT_VALIDATORS = [
|
|
4
|
+
"dateGte",
|
|
5
|
+
"dateLte"
|
|
6
|
+
];
|
|
7
|
+
const validateDateExpressions = (field)=>{
|
|
8
|
+
for (const validator of field.validation || []){
|
|
9
|
+
if (!DATE_LIMIT_VALIDATORS.includes(validator.name)) continue;
|
|
10
|
+
const expression = validator.settings?.expression;
|
|
11
|
+
if (null != expression) {
|
|
12
|
+
if ("string" != typeof expression || !isDateExpression(expression)) throw new error(`Field "${field.fieldId}" has an invalid date expression "${expression}" in its "${validator.name}" validator. Use "today", or "today" plus or minus a number of days, months or years, like "today-18y".`, "INVALID_DATE_EXPRESSION", {
|
|
13
|
+
fieldId: field.fieldId,
|
|
14
|
+
validator: validator.name,
|
|
15
|
+
expression
|
|
16
|
+
});
|
|
17
|
+
if ("datetime" !== field.type || field.settings?.type !== "date") throw new error(`Field "${field.fieldId}" uses a date expression in its "${validator.name}" validator, but expressions are only allowed on date-only fields.`, "DATE_EXPRESSION_NOT_ALLOWED", {
|
|
18
|
+
fieldId: field.fieldId,
|
|
19
|
+
validator: validator.name,
|
|
20
|
+
expression
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
export { validateDateExpressions };
|
|
26
|
+
|
|
27
|
+
//# sourceMappingURL=dateExpressions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"domain/contentModel/validation/dateExpressions.js","sources":["../../../../src/domain/contentModel/validation/dateExpressions.ts"],"sourcesContent":["import WebinyError from \"@webiny/error\";\nimport { isDateExpression } from \"@webiny/utils\";\nimport type { CmsModelField } from \"~/types/index.js\";\n\nconst DATE_LIMIT_VALIDATORS = [\"dateGte\", \"dateLte\"];\n\n/**\n * A `dateGte`/`dateLte` limit given as `{ expression }` must parse, and is allowed only on a\n * date-only field: on a time or datetime field, \"today\" has no single meaning.\n */\nexport const validateDateExpressions = (\n field: Pick<CmsModelField, \"fieldId\" | \"type\" | \"settings\" | \"validation\">\n): void => {\n for (const validator of field.validation || []) {\n if (!DATE_LIMIT_VALIDATORS.includes(validator.name)) {\n continue;\n }\n const expression = validator.settings?.expression;\n if (expression === undefined || expression === null) {\n continue;\n }\n if (typeof expression !== \"string\" || !isDateExpression(expression)) {\n throw new WebinyError(\n `Field \"${field.fieldId}\" has an invalid date expression \"${expression}\" in its \"${validator.name}\" validator. Use \"today\", or \"today\" plus or minus a number of days, months or years, like \"today-18y\".`,\n \"INVALID_DATE_EXPRESSION\",\n { fieldId: field.fieldId, validator: validator.name, expression }\n );\n }\n if (field.type !== \"datetime\" || field.settings?.type !== \"date\") {\n throw new WebinyError(\n `Field \"${field.fieldId}\" uses a date expression in its \"${validator.name}\" validator, but expressions are only allowed on date-only fields.`,\n \"DATE_EXPRESSION_NOT_ALLOWED\",\n { fieldId: field.fieldId, validator: validator.name, expression }\n );\n }\n }\n};\n"],"names":["DATE_LIMIT_VALIDATORS","validateDateExpressions","field","validator","expression","isDateExpression","WebinyError"],"mappings":";;AAIA,MAAMA,wBAAwB;IAAC;IAAW;CAAU;AAM7C,MAAMC,0BAA0B,CACnCC;IAEA,KAAK,MAAMC,aAAaD,MAAM,UAAU,IAAI,EAAE,CAAE;QAC5C,IAAI,CAACF,sBAAsB,QAAQ,CAACG,UAAU,IAAI,GAC9C;QAEJ,MAAMC,aAAaD,UAAU,QAAQ,EAAE;QACvC,IAAIC,QAAAA;YAGJ,IAAI,AAAsB,YAAtB,OAAOA,cAA2B,CAACC,iBAAiBD,aACpD,MAAM,IAAIE,MACN,CAAC,OAAO,EAAEJ,MAAM,OAAO,CAAC,kCAAkC,EAAEE,WAAW,UAAU,EAAED,UAAU,IAAI,CAAC,uGAAuG,CAAC,EAC1M,2BACA;gBAAE,SAASD,MAAM,OAAO;gBAAE,WAAWC,UAAU,IAAI;gBAAEC;YAAW;YAGxE,IAAIF,AAAe,eAAfA,MAAM,IAAI,IAAmBA,MAAM,QAAQ,EAAE,SAAS,QACtD,MAAM,IAAII,MACN,CAAC,OAAO,EAAEJ,MAAM,OAAO,CAAC,iCAAiC,EAAEC,UAAU,IAAI,CAAC,kEAAkE,CAAC,EAC7I,+BACA;gBAAE,SAASD,MAAM,OAAO;gBAAE,WAAWC,UAAU,IAAI;gBAAEC;YAAW;;IAG5E;AACJ"}
|
|
@@ -7,6 +7,7 @@ import { getBaseFieldType } from "../../../utils/getBaseFieldType.js";
|
|
|
7
7
|
import { getContentModelTitleFieldId } from "./fields/titleField.js";
|
|
8
8
|
import { getContentModelDescriptionFieldId } from "./fields/descriptionField.js";
|
|
9
9
|
import { getContentModelImageFieldId } from "./fields/imageField.js";
|
|
10
|
+
import { validateDateExpressions } from "./dateExpressions.js";
|
|
10
11
|
import { CmsGraphQLSchemaPlugin } from "../../../plugins/index.js";
|
|
11
12
|
import { buildSchemaPlugins } from "../../../graphql/buildSchemaPlugins.js";
|
|
12
13
|
import { createExecutableSchema } from "../../../graphql/createExecutableSchema.js";
|
|
@@ -76,6 +77,7 @@ const validateFields = (params)=>{
|
|
|
76
77
|
fieldIdList.push(field.fieldId);
|
|
77
78
|
if (storageIdList.includes(field.storageId)) throw new _webiny_error(`Cannot update content model because field "${field.label}" has storageId "${field.storageId}", which is already used.`);
|
|
78
79
|
storageIdList.push(field.storageId);
|
|
80
|
+
validateDateExpressions(field);
|
|
79
81
|
if (!fieldImpl.validateChildFields) continue;
|
|
80
82
|
const validateChildFields = createValidateChildFields(registry);
|
|
81
83
|
fieldImpl.validateChildFields({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"domain/contentModel/validation/modelFields.js","sources":["../../../../src/domain/contentModel/validation/modelFields.ts"],"sourcesContent":["import gql from \"graphql-tag\";\nimport { generateAlphaNumericId } from \"@webiny/utils\";\nimport WebinyError from \"@webiny/error\";\nimport type { CmsContext, CmsModel, CmsModelField } from \"~/types/index.js\";\nimport type { ValidateChildFieldsValidate } from \"~/features/graphql/fields/abstractions/CmsModelFieldToGraphQL.js\";\nimport { createManageSDL } from \"~/graphql/schema/createManageSDL.js\";\nimport { createFieldStorageId } from \"../createFieldStorageId.js\";\nimport type { GraphQLError } from \"graphql\";\nimport { getBaseFieldType } from \"~/utils/getBaseFieldType.js\";\nimport { getContentModelTitleFieldId } from \"./fields/titleField.js\";\nimport { getContentModelDescriptionFieldId } from \"./fields/descriptionField.js\";\nimport { getContentModelImageFieldId } from \"./fields/imageField.js\";\nimport type { ICmsGraphQLSchemaPlugin } from \"~/plugins/index.js\";\nimport { CmsGraphQLSchemaPlugin } from \"~/plugins/index.js\";\nimport { buildSchemaPlugins } from \"~/graphql/buildSchemaPlugins.js\";\nimport { createExecutableSchema } from \"~/graphql/createExecutableSchema.js\";\nimport {\n CmsGraphQLSchemaSorter,\n CmsModelFieldToGraphQLRegistry\n} from \"~/features/graphql/index.js\";\n\nconst extractInvalidField = (model: CmsModel, err: GraphQLError) => {\n const sdl = err.source?.body || \"\";\n\n /**\n * Find the invalid type\n */\n const { line: lineNumber } = err.locations\n ? err.locations[0]\n : {\n line: 0\n };\n const sdlLines = sdl.split(\"\\n\");\n let sdlLine;\n let gqlType;\n for (let i = lineNumber; i > 0; i--) {\n if (sdlLine && sdlLine.includes(\"type \")) {\n gqlType = sdlLine.match(/type\\s+(.*?)\\s+{/);\n break;\n }\n\n sdlLine = sdlLines[i];\n }\n\n let invalidField: string | undefined = undefined;\n if (Array.isArray(gqlType)) {\n const fieldRegex = new RegExp(`([^\\\\s+].*?):\\\\s+\\\\[?${gqlType[1]}!?\\\\]?`);\n\n const matched = sdl.match(fieldRegex);\n if (matched) {\n invalidField = matched[1];\n }\n }\n\n let message = `See more details in the browser console.`;\n if (invalidField) {\n message = `Please review the definition of \"${invalidField}\" field.`;\n }\n\n return {\n data: {\n modelId: model.modelId,\n sdl,\n invalidField\n },\n code: \"INVALID_MODEL_DEFINITION\",\n message: [`Model \"${model.modelId}\" was not saved!`, message].join(\"\\n\")\n };\n};\n\nconst createValidateChildFields = (\n registry: CmsModelFieldToGraphQLRegistry.Interface\n): ValidateChildFieldsValidate => {\n return ({ fields, originalFields }) => {\n if (fields.length === 0 && originalFields.length === 0) {\n return;\n }\n validateFields({\n fields,\n originalFields,\n registry\n });\n };\n};\n\ninterface ValidateFieldsParams {\n registry: CmsModelFieldToGraphQLRegistry.Interface;\n fields: CmsModelField[];\n originalFields: CmsModelField[];\n}\n\nconst validateFields = (params: ValidateFieldsParams) => {\n const { registry, fields, originalFields } = params;\n\n const idList: string[] = [];\n const fieldIdList: string[] = [];\n const storageIdList: string[] = [];\n\n for (const field of fields) {\n const baseType = getBaseFieldType(field);\n const fieldImpl = registry.get(baseType);\n\n if (!fieldImpl) {\n throw new Error(\n `Cannot update content model because of the unknown \"${baseType}\" field.`\n );\n }\n /**\n * Check the field's id against existing ones.\n * There cannot be two fields with the same id.\n */\n if (idList.includes(field.id)) {\n throw new WebinyError(\n `Cannot update content model because field \"${\n field.storageId || field.fieldId\n }\" has id \"${field.id}\", which is already used.`\n );\n }\n idList.push(field.id);\n\n const originalField = originalFields.find(f => f.id === field.id);\n /**\n * Field MUST have an fieldId defined.\n */\n if (!field.fieldId) {\n throw new WebinyError(`Field does not have an \"fieldId\" defined.`, \"MISSING_FIELD_ID\", {\n field\n });\n }\n /**\n * If storageId does not match a certain pattern, add that pattern, but only if field is not locked (used) already.\n * This is to avoid errors in the already installed systems.\n *\n * Why are we using the @?\n *\n * It is not part of special characters for the query syntax in the Lucene.\n *\n * Relevant links:\n * https://lucene.apache.org/core/3_4_0/queryparsersyntax.html\n * https://discuss.elastic.co/t/special-characters-in-field-names/10658/3\n * https://discuss.elastic.co/t/illegal-characters-in-elasticsearch-field-names/17196/2\n */\n if (!field.storageId) {\n /**\n * When having an original field, set the storageId to value from the originalField\n */\n if (originalField) {\n field.storageId = originalField.storageId;\n } else {\n field.storageId = createFieldStorageId(field);\n }\n }\n /**\n * Check the field's fieldId against existing ones.\n * There cannot be two fields with the same fieldId - outside world identifier.\n */\n if (fieldIdList.includes(field.fieldId)) {\n throw new WebinyError(\n `Cannot update content model because field \"${field.storageId}\" has fieldId \"${field.fieldId}\", which is already used.`\n );\n }\n fieldIdList.push(field.fieldId);\n /**\n * Check the field's storageId against the existing ones.\n * There cannot be two fields with the same storageId.\n */\n if (storageIdList.includes(field.storageId)) {\n throw new WebinyError(\n `Cannot update content model because field \"${field.label}\" has storageId \"${field.storageId}\", which is already used.`\n );\n }\n storageIdList.push(field.storageId);\n /**\n * There might be some plugins which allow child fields.\n * We use this method to validate them as well.\n */\n if (!fieldImpl.validateChildFields) {\n continue;\n }\n const validateChildFields = createValidateChildFields(registry);\n fieldImpl.validateChildFields({\n field,\n originalField,\n validate: validateChildFields\n });\n }\n};\n\ninterface CreateGraphQLSchemaParams {\n context: CmsContext;\n model: CmsModel;\n}\n\nconst createGraphQLSchema = async (params: CreateGraphQLSchemaParams): Promise<any> => {\n const { context, model } = params;\n\n const models = await context.security.withoutAuthorization(async () => {\n return (await context.cms.listModels()).filter((model): model is CmsModel => {\n return model.isPrivate !== true;\n });\n });\n\n const modelPlugins = await buildSchemaPlugins({\n context,\n /**\n * Must remove the current model from the list, as we are possibly updating it - there would be two if we didn't.\n */\n models: models.filter(m => m.modelId !== model.modelId).concat([model])\n });\n\n const plugins = context.plugins\n .byType<ICmsGraphQLSchemaPlugin>(CmsGraphQLSchemaPlugin.type)\n .filter(plugin => plugin.isApplicable(context))\n .reduce<Record<string, ICmsGraphQLSchemaPlugin>>((collection, plugin) => {\n const name =\n plugin.name || `${CmsGraphQLSchemaPlugin.type}-${generateAlphaNumericId(16)}`;\n collection[name] = plugin;\n return collection;\n }, {});\n for (const plugin of modelPlugins) {\n const name = plugin.name || `${plugin.type}-${generateAlphaNumericId(16)}`;\n plugins[name] = plugin;\n }\n\n return createExecutableSchema({\n plugins: Object.values(plugins)\n });\n};\n\nconst extractErrorObject = (error: any) => {\n return [\"message\", \"code\", \"data\", \"stack\"].reduce<Record<string, any>>((output, key) => {\n if (!error[key]) {\n return output;\n }\n output[key] = error[key];\n return output;\n }, {});\n};\n\ninterface ValidateModelFieldsParams {\n models: CmsModel[];\n model: CmsModel;\n original?: CmsModel;\n context: CmsContext;\n}\n\nexport const validateModelFields = async (params: ValidateModelFieldsParams): Promise<void> => {\n const { models, model, original, context } = params;\n const { titleFieldId, descriptionFieldId, imageFieldId } = model;\n\n /**\n * There should be fields/locked fields in either model or data to be updated.\n */\n const { fields = [] } = model;\n\n /**\n * Let's inspect the fields of the received content model. We prevent saving of a content model if it\n * contains a field for which a \"cms-model-field-to-graphql\" plugin does not exist on the backend.\n */\n const fieldRegistry = context.container.resolve(CmsModelFieldToGraphQLRegistry);\n const sorters = context.container.resolveAll(CmsGraphQLSchemaSorter);\n\n validateFields({\n fields,\n originalFields: original?.fields || [],\n registry: fieldRegistry\n });\n\n if (fields.length) {\n /**\n * Make sure that this model can be safely converted to a GraphQL SDL\n */\n const schema = createManageSDL({\n models,\n model,\n fieldRegistry,\n sorters\n });\n\n try {\n gql(schema);\n } catch (err) {\n throw new WebinyError(extractInvalidField(model, err));\n }\n /**\n *\n */\n try {\n await createGraphQLSchema({\n context,\n model\n });\n } catch (err) {\n throw new WebinyError({\n message:\n \"Cannot generate GraphQL schema when testing with the given model. Please check the response for more details.\",\n code: \"GRAPHQL_SCHEMA_ERROR\",\n data: {\n modelId: model.modelId,\n error: extractErrorObject(err)\n }\n });\n }\n }\n\n model.titleFieldId = getContentModelTitleFieldId(fields, titleFieldId);\n model.descriptionFieldId = getContentModelDescriptionFieldId(fields, descriptionFieldId);\n model.imageFieldId = getContentModelImageFieldId(fields, imageFieldId);\n};\n"],"names":["extractInvalidField","model","err","sdl","lineNumber","sdlLines","sdlLine","gqlType","i","invalidField","Array","fieldRegex","RegExp","matched","message","createValidateChildFields","registry","fields","originalFields","validateFields","params","idList","fieldIdList","storageIdList","field","baseType","getBaseFieldType","fieldImpl","Error","WebinyError","originalField","f","createFieldStorageId","validateChildFields","createGraphQLSchema","context","models","modelPlugins","buildSchemaPlugins","m","plugins","CmsGraphQLSchemaPlugin","plugin","collection","name","generateAlphaNumericId","createExecutableSchema","Object","extractErrorObject","error","output","key","validateModelFields","original","titleFieldId","descriptionFieldId","imageFieldId","fieldRegistry","CmsModelFieldToGraphQLRegistry","sorters","CmsGraphQLSchemaSorter","schema","createManageSDL","gql","getContentModelTitleFieldId","getContentModelDescriptionFieldId","getContentModelImageFieldId"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"domain/contentModel/validation/modelFields.js","sources":["../../../../src/domain/contentModel/validation/modelFields.ts"],"sourcesContent":["import gql from \"graphql-tag\";\nimport { generateAlphaNumericId } from \"@webiny/utils\";\nimport WebinyError from \"@webiny/error\";\nimport type { CmsContext, CmsModel, CmsModelField } from \"~/types/index.js\";\nimport type { ValidateChildFieldsValidate } from \"~/features/graphql/fields/abstractions/CmsModelFieldToGraphQL.js\";\nimport { createManageSDL } from \"~/graphql/schema/createManageSDL.js\";\nimport { createFieldStorageId } from \"../createFieldStorageId.js\";\nimport type { GraphQLError } from \"graphql\";\nimport { getBaseFieldType } from \"~/utils/getBaseFieldType.js\";\nimport { getContentModelTitleFieldId } from \"./fields/titleField.js\";\nimport { getContentModelDescriptionFieldId } from \"./fields/descriptionField.js\";\nimport { getContentModelImageFieldId } from \"./fields/imageField.js\";\nimport { validateDateExpressions } from \"./dateExpressions.js\";\nimport type { ICmsGraphQLSchemaPlugin } from \"~/plugins/index.js\";\nimport { CmsGraphQLSchemaPlugin } from \"~/plugins/index.js\";\nimport { buildSchemaPlugins } from \"~/graphql/buildSchemaPlugins.js\";\nimport { createExecutableSchema } from \"~/graphql/createExecutableSchema.js\";\nimport {\n CmsGraphQLSchemaSorter,\n CmsModelFieldToGraphQLRegistry\n} from \"~/features/graphql/index.js\";\n\nconst extractInvalidField = (model: CmsModel, err: GraphQLError) => {\n const sdl = err.source?.body || \"\";\n\n /**\n * Find the invalid type\n */\n const { line: lineNumber } = err.locations\n ? err.locations[0]\n : {\n line: 0\n };\n const sdlLines = sdl.split(\"\\n\");\n let sdlLine;\n let gqlType;\n for (let i = lineNumber; i > 0; i--) {\n if (sdlLine && sdlLine.includes(\"type \")) {\n gqlType = sdlLine.match(/type\\s+(.*?)\\s+{/);\n break;\n }\n\n sdlLine = sdlLines[i];\n }\n\n let invalidField: string | undefined = undefined;\n if (Array.isArray(gqlType)) {\n const fieldRegex = new RegExp(`([^\\\\s+].*?):\\\\s+\\\\[?${gqlType[1]}!?\\\\]?`);\n\n const matched = sdl.match(fieldRegex);\n if (matched) {\n invalidField = matched[1];\n }\n }\n\n let message = `See more details in the browser console.`;\n if (invalidField) {\n message = `Please review the definition of \"${invalidField}\" field.`;\n }\n\n return {\n data: {\n modelId: model.modelId,\n sdl,\n invalidField\n },\n code: \"INVALID_MODEL_DEFINITION\",\n message: [`Model \"${model.modelId}\" was not saved!`, message].join(\"\\n\")\n };\n};\n\nconst createValidateChildFields = (\n registry: CmsModelFieldToGraphQLRegistry.Interface\n): ValidateChildFieldsValidate => {\n return ({ fields, originalFields }) => {\n if (fields.length === 0 && originalFields.length === 0) {\n return;\n }\n validateFields({\n fields,\n originalFields,\n registry\n });\n };\n};\n\ninterface ValidateFieldsParams {\n registry: CmsModelFieldToGraphQLRegistry.Interface;\n fields: CmsModelField[];\n originalFields: CmsModelField[];\n}\n\nconst validateFields = (params: ValidateFieldsParams) => {\n const { registry, fields, originalFields } = params;\n\n const idList: string[] = [];\n const fieldIdList: string[] = [];\n const storageIdList: string[] = [];\n\n for (const field of fields) {\n const baseType = getBaseFieldType(field);\n const fieldImpl = registry.get(baseType);\n\n if (!fieldImpl) {\n throw new Error(\n `Cannot update content model because of the unknown \"${baseType}\" field.`\n );\n }\n /**\n * Check the field's id against existing ones.\n * There cannot be two fields with the same id.\n */\n if (idList.includes(field.id)) {\n throw new WebinyError(\n `Cannot update content model because field \"${\n field.storageId || field.fieldId\n }\" has id \"${field.id}\", which is already used.`\n );\n }\n idList.push(field.id);\n\n const originalField = originalFields.find(f => f.id === field.id);\n /**\n * Field MUST have an fieldId defined.\n */\n if (!field.fieldId) {\n throw new WebinyError(`Field does not have an \"fieldId\" defined.`, \"MISSING_FIELD_ID\", {\n field\n });\n }\n /**\n * If storageId does not match a certain pattern, add that pattern, but only if field is not locked (used) already.\n * This is to avoid errors in the already installed systems.\n *\n * Why are we using the @?\n *\n * It is not part of special characters for the query syntax in the Lucene.\n *\n * Relevant links:\n * https://lucene.apache.org/core/3_4_0/queryparsersyntax.html\n * https://discuss.elastic.co/t/special-characters-in-field-names/10658/3\n * https://discuss.elastic.co/t/illegal-characters-in-elasticsearch-field-names/17196/2\n */\n if (!field.storageId) {\n /**\n * When having an original field, set the storageId to value from the originalField\n */\n if (originalField) {\n field.storageId = originalField.storageId;\n } else {\n field.storageId = createFieldStorageId(field);\n }\n }\n /**\n * Check the field's fieldId against existing ones.\n * There cannot be two fields with the same fieldId - outside world identifier.\n */\n if (fieldIdList.includes(field.fieldId)) {\n throw new WebinyError(\n `Cannot update content model because field \"${field.storageId}\" has fieldId \"${field.fieldId}\", which is already used.`\n );\n }\n fieldIdList.push(field.fieldId);\n /**\n * Check the field's storageId against the existing ones.\n * There cannot be two fields with the same storageId.\n */\n if (storageIdList.includes(field.storageId)) {\n throw new WebinyError(\n `Cannot update content model because field \"${field.label}\" has storageId \"${field.storageId}\", which is already used.`\n );\n }\n storageIdList.push(field.storageId);\n\n validateDateExpressions(field);\n /**\n * There might be some plugins which allow child fields.\n * We use this method to validate them as well.\n */\n if (!fieldImpl.validateChildFields) {\n continue;\n }\n const validateChildFields = createValidateChildFields(registry);\n fieldImpl.validateChildFields({\n field,\n originalField,\n validate: validateChildFields\n });\n }\n};\n\ninterface CreateGraphQLSchemaParams {\n context: CmsContext;\n model: CmsModel;\n}\n\nconst createGraphQLSchema = async (params: CreateGraphQLSchemaParams): Promise<any> => {\n const { context, model } = params;\n\n const models = await context.security.withoutAuthorization(async () => {\n return (await context.cms.listModels()).filter((model): model is CmsModel => {\n return model.isPrivate !== true;\n });\n });\n\n const modelPlugins = await buildSchemaPlugins({\n context,\n /**\n * Must remove the current model from the list, as we are possibly updating it - there would be two if we didn't.\n */\n models: models.filter(m => m.modelId !== model.modelId).concat([model])\n });\n\n const plugins = context.plugins\n .byType<ICmsGraphQLSchemaPlugin>(CmsGraphQLSchemaPlugin.type)\n .filter(plugin => plugin.isApplicable(context))\n .reduce<Record<string, ICmsGraphQLSchemaPlugin>>((collection, plugin) => {\n const name =\n plugin.name || `${CmsGraphQLSchemaPlugin.type}-${generateAlphaNumericId(16)}`;\n collection[name] = plugin;\n return collection;\n }, {});\n for (const plugin of modelPlugins) {\n const name = plugin.name || `${plugin.type}-${generateAlphaNumericId(16)}`;\n plugins[name] = plugin;\n }\n\n return createExecutableSchema({\n plugins: Object.values(plugins)\n });\n};\n\nconst extractErrorObject = (error: any) => {\n return [\"message\", \"code\", \"data\", \"stack\"].reduce<Record<string, any>>((output, key) => {\n if (!error[key]) {\n return output;\n }\n output[key] = error[key];\n return output;\n }, {});\n};\n\ninterface ValidateModelFieldsParams {\n models: CmsModel[];\n model: CmsModel;\n original?: CmsModel;\n context: CmsContext;\n}\n\nexport const validateModelFields = async (params: ValidateModelFieldsParams): Promise<void> => {\n const { models, model, original, context } = params;\n const { titleFieldId, descriptionFieldId, imageFieldId } = model;\n\n /**\n * There should be fields/locked fields in either model or data to be updated.\n */\n const { fields = [] } = model;\n\n /**\n * Let's inspect the fields of the received content model. We prevent saving of a content model if it\n * contains a field for which a \"cms-model-field-to-graphql\" plugin does not exist on the backend.\n */\n const fieldRegistry = context.container.resolve(CmsModelFieldToGraphQLRegistry);\n const sorters = context.container.resolveAll(CmsGraphQLSchemaSorter);\n\n validateFields({\n fields,\n originalFields: original?.fields || [],\n registry: fieldRegistry\n });\n\n if (fields.length) {\n /**\n * Make sure that this model can be safely converted to a GraphQL SDL\n */\n const schema = createManageSDL({\n models,\n model,\n fieldRegistry,\n sorters\n });\n\n try {\n gql(schema);\n } catch (err) {\n throw new WebinyError(extractInvalidField(model, err));\n }\n /**\n *\n */\n try {\n await createGraphQLSchema({\n context,\n model\n });\n } catch (err) {\n throw new WebinyError({\n message:\n \"Cannot generate GraphQL schema when testing with the given model. Please check the response for more details.\",\n code: \"GRAPHQL_SCHEMA_ERROR\",\n data: {\n modelId: model.modelId,\n error: extractErrorObject(err)\n }\n });\n }\n }\n\n model.titleFieldId = getContentModelTitleFieldId(fields, titleFieldId);\n model.descriptionFieldId = getContentModelDescriptionFieldId(fields, descriptionFieldId);\n model.imageFieldId = getContentModelImageFieldId(fields, imageFieldId);\n};\n"],"names":["extractInvalidField","model","err","sdl","lineNumber","sdlLines","sdlLine","gqlType","i","invalidField","Array","fieldRegex","RegExp","matched","message","createValidateChildFields","registry","fields","originalFields","validateFields","params","idList","fieldIdList","storageIdList","field","baseType","getBaseFieldType","fieldImpl","Error","WebinyError","originalField","f","createFieldStorageId","validateDateExpressions","validateChildFields","createGraphQLSchema","context","models","modelPlugins","buildSchemaPlugins","m","plugins","CmsGraphQLSchemaPlugin","plugin","collection","name","generateAlphaNumericId","createExecutableSchema","Object","extractErrorObject","error","output","key","validateModelFields","original","titleFieldId","descriptionFieldId","imageFieldId","fieldRegistry","CmsModelFieldToGraphQLRegistry","sorters","CmsGraphQLSchemaSorter","schema","createManageSDL","gql","getContentModelTitleFieldId","getContentModelDescriptionFieldId","getContentModelImageFieldId"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Result } from "@webiny/feature/api";
|
|
2
2
|
import { EntryEventPublisher } from "../../../features/contentEntry/EntryEventPublisher/index.js";
|
|
3
|
-
import { PublishEntryUseCase as UseCaseAbstraction } from "./abstractions.js";
|
|
3
|
+
import { PublishEntryUseCase as UseCaseAbstraction, type IPublishEntryUseCaseOptions } from "./abstractions.js";
|
|
4
4
|
import { PublishEntryRepository } from "./abstractions.js";
|
|
5
5
|
import { AccessControl } from "../../../features/shared/abstractions.js";
|
|
6
6
|
import { GetRevisionByIdUseCase } from "../../../features/contentEntry/GetRevisionById/index.js";
|
|
@@ -13,7 +13,7 @@ declare class PublishEntryUseCaseImpl implements UseCaseAbstraction.Interface {
|
|
|
13
13
|
private eventPublisher;
|
|
14
14
|
private createPublishEntryDataFactory;
|
|
15
15
|
constructor(repository: PublishEntryRepository.Interface, accessControl: AccessControl.Interface, getRevisionById: GetRevisionByIdUseCase.Interface, eventPublisher: EntryEventPublisher.Interface, createPublishEntryDataFactory: CreatePublishEntryDataFactory.Interface);
|
|
16
|
-
execute<T extends CmsEntryValues = CmsEntryValues>(model: CmsModel, id: string): Promise<Result<CmsEntry<T>, UseCaseAbstraction.Error>>;
|
|
16
|
+
execute<T extends CmsEntryValues = CmsEntryValues>(model: CmsModel, id: string, options?: IPublishEntryUseCaseOptions<T>): Promise<Result<CmsEntry<T>, UseCaseAbstraction.Error>>;
|
|
17
17
|
}
|
|
18
18
|
export declare const PublishEntryUseCase: import("@webiny/di").Implementation<typeof PublishEntryUseCaseImpl>;
|
|
19
19
|
export {};
|
|
@@ -14,7 +14,7 @@ class PublishEntryUseCaseImpl {
|
|
|
14
14
|
this.eventPublisher = eventPublisher;
|
|
15
15
|
this.createPublishEntryDataFactory = createPublishEntryDataFactory;
|
|
16
16
|
}
|
|
17
|
-
async execute(model, id) {
|
|
17
|
+
async execute(model, id, options) {
|
|
18
18
|
const canAccess = await this.accessControl.canAccessEntry({
|
|
19
19
|
model,
|
|
20
20
|
pw: "p"
|
|
@@ -29,7 +29,11 @@ class PublishEntryUseCaseImpl {
|
|
|
29
29
|
pw: "p"
|
|
30
30
|
});
|
|
31
31
|
if (!canAccessEntry) return Result.fail(EntryNotAuthorizedError.fromModel(model));
|
|
32
|
-
const
|
|
32
|
+
const source = options?.values ? {
|
|
33
|
+
...originalEntry,
|
|
34
|
+
values: options.values
|
|
35
|
+
} : originalEntry;
|
|
36
|
+
const dataResult = await this.createPublishEntryDataFactory.create(model, source);
|
|
33
37
|
if (dataResult.isFail()) return Result.fail(dataResult.error);
|
|
34
38
|
const { entry } = dataResult.value;
|
|
35
39
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/contentEntry/PublishEntry/PublishEntryUseCase.js","sources":["../../../../src/features/contentEntry/PublishEntry/PublishEntryUseCase.ts"],"sourcesContent":["import { Result } from \"@webiny/feature/api\";\nimport { createImplementation } from \"@webiny/feature/api\";\nimport { EntryEventPublisher } from \"~/features/contentEntry/EntryEventPublisher/index.js\";\nimport {
|
|
1
|
+
{"version":3,"file":"features/contentEntry/PublishEntry/PublishEntryUseCase.js","sources":["../../../../src/features/contentEntry/PublishEntry/PublishEntryUseCase.ts"],"sourcesContent":["import { Result } from \"@webiny/feature/api\";\nimport { createImplementation } from \"@webiny/feature/api\";\nimport { EntryEventPublisher } from \"~/features/contentEntry/EntryEventPublisher/index.js\";\nimport {\n PublishEntryUseCase as UseCaseAbstraction,\n type IPublishEntryUseCaseOptions\n} from \"./abstractions.js\";\nimport { PublishEntryRepository } from \"./abstractions.js\";\nimport { AccessControl } from \"~/features/shared/abstractions.js\";\nimport { GetRevisionByIdUseCase } from \"~/features/contentEntry/GetRevisionById/index.js\";\nimport type { CmsEntry, CmsEntryValues, CmsModel } from \"~/types/index.js\";\nimport {\n EntryBeforePublishEvent,\n EntryAfterPublishEvent,\n EntryPublishErrorEvent\n} from \"./events.js\";\nimport { EntryNotAuthorizedError, EntryNotFoundError } from \"~/domain/contentEntry/errors.js\";\nimport { CreatePublishEntryDataFactory } from \"~/features/contentEntry/entryDataFactories/CreatePublishEntryDataFactory/index.js\";\n\nclass PublishEntryUseCaseImpl implements UseCaseAbstraction.Interface {\n public constructor(\n private repository: PublishEntryRepository.Interface,\n private accessControl: AccessControl.Interface,\n private getRevisionById: GetRevisionByIdUseCase.Interface,\n private eventPublisher: EntryEventPublisher.Interface,\n private createPublishEntryDataFactory: CreatePublishEntryDataFactory.Interface\n ) {}\n\n async execute<T extends CmsEntryValues = CmsEntryValues>(\n model: CmsModel,\n id: string,\n options?: IPublishEntryUseCaseOptions<T>\n ): Promise<Result<CmsEntry<T>, UseCaseAbstraction.Error>> {\n const canAccess = await this.accessControl.canAccessEntry({ model, pw: \"p\" });\n if (!canAccess) {\n return Result.fail(EntryNotAuthorizedError.fromModel(model));\n }\n\n const result = await this.getRevisionById.execute<T>(model, id);\n\n if (result.isFail()) {\n return Result.fail(new EntryNotFoundError(id));\n }\n\n const originalEntry = result.value;\n\n const canAccessEntry = await this.accessControl.canAccessEntry({\n model,\n entry: originalEntry,\n pw: \"p\"\n });\n\n if (!canAccessEntry) {\n return Result.fail(EntryNotAuthorizedError.fromModel(model));\n }\n\n const source = options?.values\n ? { ...originalEntry, values: options.values }\n : originalEntry;\n const dataResult = await this.createPublishEntryDataFactory.create<T>(model, source);\n if (dataResult.isFail()) {\n return Result.fail(dataResult.error);\n }\n const { entry } = dataResult.value;\n\n try {\n await this.eventPublisher.publish(\n new EntryBeforePublishEvent({\n entry,\n original: originalEntry,\n model\n })\n );\n\n const repositoryResult = await this.repository.execute(model, entry);\n\n if (repositoryResult.isFail()) {\n await this.eventPublisher.publish(\n new EntryPublishErrorEvent({\n entry,\n original: originalEntry,\n model,\n error: repositoryResult.error\n })\n );\n return Result.fail(repositoryResult.error);\n }\n\n const publishedEntry = repositoryResult.value;\n\n await this.eventPublisher.publish(\n new EntryAfterPublishEvent({\n entry: publishedEntry,\n original: originalEntry,\n model\n })\n );\n\n return Result.ok(entry);\n } catch (error) {\n await this.eventPublisher.publish(\n new EntryPublishErrorEvent({\n entry,\n original: originalEntry,\n model,\n error: error as Error\n })\n );\n return Result.fail(error as UseCaseAbstraction.Error);\n }\n }\n}\n\nexport const PublishEntryUseCase = createImplementation({\n abstraction: UseCaseAbstraction,\n implementation: PublishEntryUseCaseImpl,\n dependencies: [\n PublishEntryRepository,\n AccessControl,\n GetRevisionByIdUseCase,\n EntryEventPublisher,\n CreatePublishEntryDataFactory\n ]\n});\n"],"names":["PublishEntryUseCaseImpl","repository","accessControl","getRevisionById","eventPublisher","createPublishEntryDataFactory","model","id","options","canAccess","Result","EntryNotAuthorizedError","result","EntryNotFoundError","originalEntry","canAccessEntry","source","dataResult","entry","EntryBeforePublishEvent","repositoryResult","EntryPublishErrorEvent","publishedEntry","EntryAfterPublishEvent","error","PublishEntryUseCase","createImplementation","UseCaseAbstraction","PublishEntryRepository","AccessControl","GetRevisionByIdUseCase","EntryEventPublisher","CreatePublishEntryDataFactory"],"mappings":";;;;;;;;AAmBA,MAAMA;IACF,YACYC,UAA4C,EAC5CC,aAAsC,EACtCC,eAAiD,EACjDC,cAA6C,EAC7CC,6BAAsE,CAChF;aALUJ,UAAU,GAAVA;aACAC,aAAa,GAAbA;aACAC,eAAe,GAAfA;aACAC,cAAc,GAAdA;aACAC,6BAA6B,GAA7BA;IACT;IAEH,MAAM,QACFC,KAAe,EACfC,EAAU,EACVC,OAAwC,EACc;QACtD,MAAMC,YAAY,MAAM,IAAI,CAAC,aAAa,CAAC,cAAc,CAAC;YAAEH;YAAO,IAAI;QAAI;QAC3E,IAAI,CAACG,WACD,OAAOC,OAAO,IAAI,CAACC,wBAAwB,SAAS,CAACL;QAGzD,MAAMM,SAAS,MAAM,IAAI,CAAC,eAAe,CAAC,OAAO,CAAIN,OAAOC;QAE5D,IAAIK,OAAO,MAAM,IACb,OAAOF,OAAO,IAAI,CAAC,IAAIG,mBAAmBN;QAG9C,MAAMO,gBAAgBF,OAAO,KAAK;QAElC,MAAMG,iBAAiB,MAAM,IAAI,CAAC,aAAa,CAAC,cAAc,CAAC;YAC3DT;YACA,OAAOQ;YACP,IAAI;QACR;QAEA,IAAI,CAACC,gBACD,OAAOL,OAAO,IAAI,CAACC,wBAAwB,SAAS,CAACL;QAGzD,MAAMU,SAASR,SAAS,SAClB;YAAE,GAAGM,aAAa;YAAE,QAAQN,QAAQ,MAAM;QAAC,IAC3CM;QACN,MAAMG,aAAa,MAAM,IAAI,CAAC,6BAA6B,CAAC,MAAM,CAAIX,OAAOU;QAC7E,IAAIC,WAAW,MAAM,IACjB,OAAOP,OAAO,IAAI,CAACO,WAAW,KAAK;QAEvC,MAAM,EAAEC,KAAK,EAAE,GAAGD,WAAW,KAAK;QAElC,IAAI;YACA,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAC7B,IAAIE,wBAAwB;gBACxBD;gBACA,UAAUJ;gBACVR;YACJ;YAGJ,MAAMc,mBAAmB,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAACd,OAAOY;YAE9D,IAAIE,iBAAiB,MAAM,IAAI;gBAC3B,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAC7B,IAAIC,uBAAuB;oBACvBH;oBACA,UAAUJ;oBACVR;oBACA,OAAOc,iBAAiB,KAAK;gBACjC;gBAEJ,OAAOV,OAAO,IAAI,CAACU,iBAAiB,KAAK;YAC7C;YAEA,MAAME,iBAAiBF,iBAAiB,KAAK;YAE7C,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAC7B,IAAIG,uBAAuB;gBACvB,OAAOD;gBACP,UAAUR;gBACVR;YACJ;YAGJ,OAAOI,OAAO,EAAE,CAACQ;QACrB,EAAE,OAAOM,OAAO;YACZ,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAC7B,IAAIH,uBAAuB;gBACvBH;gBACA,UAAUJ;gBACVR;gBACA,OAAOkB;YACX;YAEJ,OAAOd,OAAO,IAAI,CAACc;QACvB;IACJ;AACJ;AAEO,MAAMC,0CAAsBC,qBAAqB;IACpD,aAAaC;IACb,gBAAgB3B;IAChB,cAAc;QACV4B;QACAC;QACAC;QACAC;QACAC;KACH;AACL"}
|
|
@@ -4,8 +4,15 @@ import type { EntryNotAuthorizedError, EntryNotFoundError, EntryPersistenceError
|
|
|
4
4
|
/**
|
|
5
5
|
* PublishEntry Use Case - Publishes an entry revision.
|
|
6
6
|
*/
|
|
7
|
+
export interface IPublishEntryUseCaseOptions<T extends CmsEntryValues = CmsEntryValues> {
|
|
8
|
+
/**
|
|
9
|
+
* Values to publish instead of the revision's stored values. They go through the same
|
|
10
|
+
* validation as stored values do.
|
|
11
|
+
*/
|
|
12
|
+
values?: T;
|
|
13
|
+
}
|
|
7
14
|
export interface IPublishEntryUseCase {
|
|
8
|
-
execute<T extends CmsEntryValues = CmsEntryValues>(model: CmsModel, id: string): Promise<Result<CmsEntry<T>, UseCaseError>>;
|
|
15
|
+
execute<T extends CmsEntryValues = CmsEntryValues>(model: CmsModel, id: string, options?: IPublishEntryUseCaseOptions<T>): Promise<Result<CmsEntry<T>, UseCaseError>>;
|
|
9
16
|
}
|
|
10
17
|
export interface IPublishEntryUseCaseErrors {
|
|
11
18
|
notAuthorized: EntryNotAuthorizedError;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/contentEntry/PublishEntry/abstractions.js","sources":["../../../../src/features/contentEntry/PublishEntry/abstractions.ts"],"sourcesContent":["import { createAbstraction, Result } from \"@webiny/feature/api\";\nimport type { CmsEntry, CmsEntryValues, CmsModel } from \"~/types/index.js\";\nimport type {\n EntryNotAuthorizedError,\n EntryNotFoundError,\n EntryPersistenceError,\n EntryValidationError\n} from \"~/domain/contentEntry/errors.js\";\n\n/**\n * PublishEntry Use Case - Publishes an entry revision.\n */\nexport interface IPublishEntryUseCase {\n execute<T extends CmsEntryValues = CmsEntryValues>(\n model: CmsModel,\n id: string
|
|
1
|
+
{"version":3,"file":"features/contentEntry/PublishEntry/abstractions.js","sources":["../../../../src/features/contentEntry/PublishEntry/abstractions.ts"],"sourcesContent":["import { createAbstraction, Result } from \"@webiny/feature/api\";\nimport type { CmsEntry, CmsEntryValues, CmsModel } from \"~/types/index.js\";\nimport type {\n EntryNotAuthorizedError,\n EntryNotFoundError,\n EntryPersistenceError,\n EntryValidationError\n} from \"~/domain/contentEntry/errors.js\";\n\n/**\n * PublishEntry Use Case - Publishes an entry revision.\n */\nexport interface IPublishEntryUseCaseOptions<T extends CmsEntryValues = CmsEntryValues> {\n /**\n * Values to publish instead of the revision's stored values. They go through the same\n * validation as stored values do.\n */\n values?: T;\n}\n\nexport interface IPublishEntryUseCase {\n execute<T extends CmsEntryValues = CmsEntryValues>(\n model: CmsModel,\n id: string,\n options?: IPublishEntryUseCaseOptions<T>\n ): Promise<Result<CmsEntry<T>, UseCaseError>>;\n}\n\nexport interface IPublishEntryUseCaseErrors {\n notAuthorized: EntryNotAuthorizedError;\n notFound: EntryNotFoundError;\n validation: EntryValidationError;\n storage: EntryPersistenceError;\n}\n\ntype UseCaseError = IPublishEntryUseCaseErrors[keyof IPublishEntryUseCaseErrors];\n\n/** Publish a content entry. */\nexport const PublishEntryUseCase = createAbstraction<IPublishEntryUseCase>(\"PublishEntryUseCase\");\n\nexport namespace PublishEntryUseCase {\n export type Interface = IPublishEntryUseCase;\n export type Error = UseCaseError;\n export type Return<T extends CmsEntryValues = CmsEntryValues> = Promise<\n Result<CmsEntry<T>, UseCaseError>\n >;\n}\n\n/**\n * Payload for before publish event\n */\nexport interface EntryBeforePublishEventPayload {\n entry: CmsEntry;\n original: CmsEntry;\n model: CmsModel;\n}\n\n/**\n * Payload for after publish event\n */\nexport interface EntryAfterPublishEventPayload {\n entry: CmsEntry;\n original: CmsEntry;\n model: CmsModel;\n}\n\n/**\n * Payload for publish error event\n */\nexport interface EntryPublishErrorEventPayload {\n entry: CmsEntry;\n original: CmsEntry;\n model: CmsModel;\n error: Error;\n}\n\n/**\n * PublishEntryRepository - Handles storage operations for publishing entries.\n */\nexport interface IPublishEntryRepository {\n execute<T extends CmsEntryValues = CmsEntryValues>(\n model: CmsModel,\n entry: CmsEntry<T>\n ): Promise<Result<CmsEntry<T>, RepositoryError>>;\n}\n\nexport interface IPublishEntryRepositoryErrors {\n storage: EntryPersistenceError;\n}\n\ntype RepositoryError = IPublishEntryRepositoryErrors[keyof IPublishEntryRepositoryErrors];\n\nexport const PublishEntryRepository =\n createAbstraction<IPublishEntryRepository>(\"PublishEntryRepository\");\n\nexport namespace PublishEntryRepository {\n export type Interface = IPublishEntryRepository;\n export type Error = RepositoryError;\n}\n"],"names":["PublishEntryUseCase","createAbstraction","PublishEntryRepository"],"mappings":";AAsCO,MAAMA,sBAAsBC,kBAAwC;AAsDpE,MAAMC,yBACTD,kBAA2C"}
|
|
@@ -22,6 +22,15 @@ export interface IFieldRendererRegistry {
|
|
|
22
22
|
fieldType: "datetime";
|
|
23
23
|
settings: undefined;
|
|
24
24
|
};
|
|
25
|
+
/**
|
|
26
|
+
* Picks a day, then a month, then a year. For date-only fields (`dateOnly()`); single-value
|
|
27
|
+
* only, not for `.list()`. It offers only the dates the field's `dateGte`/`dateLte` limits
|
|
28
|
+
* allow; without limits, years run from this year back 120 years.
|
|
29
|
+
*/
|
|
30
|
+
birthdayInput: {
|
|
31
|
+
fieldType: "datetime";
|
|
32
|
+
settings: undefined;
|
|
33
|
+
};
|
|
25
34
|
dateTimeInputs: {
|
|
26
35
|
fieldType: "datetime";
|
|
27
36
|
settings?: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/modelBuilder/fields/DataFieldBuilder.js","sources":["../../../../src/features/modelBuilder/fields/DataFieldBuilder.ts"],"sourcesContent":["import camelCase from \"lodash/camelCase.js\";\nimport type {\n CmsModelField,\n CmsModelFieldPredefinedValues,\n CmsModelFieldValidation\n} from \"~/types/index.js\";\nimport { getBaseFieldType } from \"~/utils/getBaseFieldType.js\";\nimport { BaseFieldBuilder, type DataFieldBuildResult } from \"./BaseFieldBuilder.js\";\n\nexport interface FieldBuilderConfig extends Omit<\n CmsModelField,\n \"id\" | \"fieldId\" | \"storageId\" | \"type\"\n> {\n _storageId?: string;\n _fieldId?: string;\n description?: string | null;\n note?: string | null;\n}\n\n/**\n * Augmentable renderer registry. Each entry maps a renderer name to its applicable field type(s) and settings.\n */\nexport interface IFieldRendererRegistry {\n switch: {\n fieldType: \"boolean\";\n settings: undefined;\n };\n checkboxes: {\n fieldType: \"text\" | \"number\";\n settings: undefined;\n };\n dateTimeInput: {\n fieldType: \"datetime\";\n settings: undefined;\n };\n dateTimeInputs: {\n fieldType: \"datetime\";\n settings?: {\n addItemLabel?: string;\n };\n };\n dynamicZone: {\n fieldType: \"dynamicZone\";\n settings?: {\n /** Whether the accordion is expanded by default. */\n open?: boolean;\n /** Wrap the zone in a container panel. Set to `false` for a flat inline layout. */\n container?: boolean;\n /** Label for the \"add item\" button (defaults to \"Add Item\"). */\n addItemLabel?: string;\n };\n };\n hidden: {\n fieldType: string;\n settings: undefined;\n };\n lexicalEditor: {\n fieldType: \"rich-text\";\n settings: undefined;\n };\n lexicalEditors: {\n fieldType: \"rich-text\";\n settings?: {\n addItemLabel?: string;\n };\n };\n textarea: {\n fieldType: \"long-text\";\n settings: undefined;\n };\n textareas: {\n fieldType: \"long-text\";\n settings: {\n addItemLabel?: string;\n };\n };\n numberInput: {\n fieldType: \"number\";\n settings: undefined;\n };\n numberInputs: {\n fieldType: \"number\";\n settings?: {\n addItemLabel?: string;\n };\n };\n objectAccordionSingle: {\n fieldType: \"object\";\n settings?: {\n /** Whether the accordion is expanded by default. */\n open?: boolean;\n /** Wrap the object in a container panel. Set to `false` for a flat inline layout. */\n container?: boolean;\n /** Field ID whose value is used as the accordion title, or a function receiving the object data. */\n itemTitle?: string;\n /** Field ID whose value is used as the accordion description, or a function receiving the object data. */\n itemDescription?: string;\n };\n };\n objectAccordionMultiple: {\n fieldType: \"object\";\n settings?: {\n /** Whether each accordion item is expanded by default. */\n open?: boolean;\n /** Wrap the list in a container panel. Set to `false` for a flat inline layout. */\n container?: boolean;\n /** Field ID whose value is used as each item's accordion title. */\n itemTitle?: string;\n /** Field ID whose value is used as each item's accordion description. */\n itemDescription?: string;\n /** Label for the \"add item\" button (defaults to \"Add {fieldLabel}\"). */\n addItemLabel?: string;\n };\n };\n passthrough: {\n fieldType: string;\n settings: undefined;\n };\n radioButtons: {\n fieldType: \"text\" | \"number\";\n settings: undefined;\n };\n refDialogSingle: {\n fieldType: \"ref\";\n settings: undefined;\n };\n refDialogMultiple: {\n fieldType: \"ref\";\n settings?: {\n /** Where newly picked references are inserted: `\"first\"` (top) or `\"last\"` (bottom, default). */\n newItemPosition?: \"first\" | \"last\";\n };\n };\n refAutocompleteSingle: {\n fieldType: \"ref\";\n settings: undefined;\n };\n refAutocompleteMultiple: {\n fieldType: \"ref\";\n settings: undefined;\n };\n refCheckboxes: {\n fieldType: \"ref\";\n settings: undefined;\n };\n refRadioButtons: {\n fieldType: \"ref\";\n settings: undefined;\n };\n select: {\n fieldType: \"text\" | \"number\";\n settings: undefined;\n };\n /** @deprecated Use \"select\" instead. */\n dropdown: {\n fieldType: \"text\" | \"number\";\n settings: undefined;\n };\n tags: {\n fieldType: \"text\";\n settings: undefined;\n };\n textInput: {\n fieldType: \"text\";\n settings: undefined;\n };\n textInputs: {\n fieldType: \"text\";\n settings?: {\n addItemLabel?: string;\n };\n };\n /** @deprecated Use `assetField` instead. */\n file: {\n fieldType: \"file\";\n settings?: {\n /** Only allow image files to be selected. */\n imagesOnly?: boolean;\n };\n };\n /** @deprecated Use `assetFields` instead. */\n files: {\n fieldType: \"file\";\n settings?: {\n /** Only allow image files to be selected. */\n imagesOnly?: boolean;\n };\n };\n assetField: {\n fieldType: \"asset\";\n settings?: {\n /** Only allow image files to be selected. */\n imagesOnly?: boolean;\n /** MIME types or extensions to allow (e.g. `[\"image/png\", \"application/pdf\"]`). Leave empty to allow all. */\n accept?: string[];\n };\n };\n assetFields: {\n fieldType: \"asset\";\n settings?: {\n /** Only allow image files to be selected. */\n imagesOnly?: boolean;\n /** MIME types or extensions to allow (e.g. `[\"image/png\", \"application/pdf\"]`). Leave empty to allow all. */\n accept?: string[];\n };\n };\n uiSeparator: {\n fieldType: \"ui\";\n settings: undefined;\n };\n uiAlert: {\n fieldType: \"ui\";\n settings: {\n type: \"info\" | \"success\" | \"warning\" | \"danger\";\n };\n };\n uiTabs: {\n fieldType: \"ui\";\n settings: undefined;\n };\n}\n\n/**\n * Maps camelCase renderer names (used in the builder API) to the\n * kebab-case names expected by the frontend renderer registry.\n */\nconst rendererNameMap: Record<keyof IFieldRendererRegistry, string> = {\n switch: \"boolean-input\",\n checkboxes: \"checkboxes\",\n dateTimeInput: \"date-time-input\",\n dateTimeInputs: \"date-time-inputs\",\n dynamicZone: \"dynamicZone\",\n hidden: \"hidden\",\n lexicalEditor: \"lexical-text-input\",\n lexicalEditors: \"lexical-text-inputs\",\n textarea: \"long-text-text-area\",\n textareas: \"long-text-inputs\",\n numberInput: \"number-input\",\n numberInputs: \"number-inputs\",\n objectAccordionSingle: \"object-accordion\",\n objectAccordionMultiple: \"objects-accordion\",\n passthrough: \"passthrough\",\n radioButtons: \"radio-buttons\",\n refDialogSingle: \"ref-advanced-single\",\n refDialogMultiple: \"ref-advanced-multiple\",\n refAutocompleteSingle: \"ref-input\",\n refAutocompleteMultiple: \"ref-inputs\",\n refCheckboxes: \"ref-simple-multiple\",\n refRadioButtons: \"ref-simple-single\",\n select: \"select-box\",\n dropdown: \"select-box\",\n tags: \"tags\",\n textInput: \"text-input\",\n textInputs: \"text-inputs\",\n file: \"file-input\",\n files: \"file-inputs\",\n assetField: \"asset-input\",\n assetFields: \"asset-inputs\",\n uiSeparator: \"uiSeparator\",\n uiAlert: \"uiAlert\",\n uiTabs: \"uiTabs\"\n};\n\n/**\n * Resolves a camelCase renderer name to the kebab-case name used by the frontend.\n */\nfunction resolveRendererName(name: string): string {\n return rendererNameMap[name as keyof IFieldRendererRegistry] ?? name;\n}\n\n/**\n * Extracts renderer names valid for the given field type.\n * When TType is a broad `string`, all renderer names are returned.\n */\nexport type FieldRendererName<TType extends string = string> = string extends TType\n ? keyof IFieldRendererRegistry & string\n : {\n [K in keyof IFieldRendererRegistry]: TType extends IFieldRendererRegistry[K][\"fieldType\"]\n ? K\n : never;\n }[keyof IFieldRendererRegistry] &\n string;\n\nexport type FieldRendererSettings<TName extends string> = TName extends keyof IFieldRendererRegistry\n ? IFieldRendererRegistry[TName][\"settings\"]\n : Record<string, any> | undefined;\n\n/**\n * DataFieldBuilder class for data fields that produce CmsModelField instances.\n * Provides storageId, list, validation, renderer, and other data-field methods.\n */\nexport class DataFieldBuilder<TType extends string = string> extends BaseFieldBuilder<TType> {\n protected override config: FieldBuilderConfig;\n\n public constructor(type: TType, label?: string) {\n super(type, label);\n this.config = {\n label: label || \"\",\n validation: [],\n listValidation: [],\n list: false,\n predefinedValues: {\n enabled: false,\n values: []\n },\n help: null,\n placeholder: null,\n description: null,\n note: null,\n renderer: null,\n settings: {},\n tags: []\n };\n }\n\n placeholder(text: string): this {\n this.config.placeholder = text;\n return this;\n }\n\n storageId(id: string): this {\n // We do not allow developers to specify the field type!\n this.config._storageId = id.split(\"@\").pop();\n return this;\n }\n\n defaultValue(value: any): this {\n this.config.settings = { ...this.config.settings, defaultValue: value };\n return this;\n }\n\n list(): this {\n this.config.list = true;\n return this as this;\n }\n\n /**\n * Renders the field in the admin app, but doesn't let anyone edit its value.\n * Handy for values your own code owns, like an external ID or a generated slug.\n *\n * Only the admin app honours this. The API doesn't check it, so the field can\n * still be written to over GraphQL.\n */\n disabled(value = true): this {\n this.config.disabled = value;\n return this;\n }\n\n tags(tags: string[]): this {\n this.config.tags = tags;\n return this;\n }\n\n /**\n * List validators - these methods are available after calling list()\n */\n listMinLength(value: number, message?: string): this {\n return this.listValidation({\n name: \"minLength\",\n message: message || `At least ${value} item(s) required.`,\n settings: { value }\n });\n }\n\n listMaxLength(value: number, message?: string): this {\n return this.listValidation({\n name: \"maxLength\",\n message: message || `At most ${value} item(s) allowed.`,\n settings: { value }\n });\n }\n\n /**\n * Add a validation rule to this field.\n * This method is protected and should only be used by field-specific validator methods.\n * @internal\n */\n protected validation(validation: CmsModelFieldValidation): this {\n this.config.validation = this.config.validation || [];\n this.config.validation.push(validation);\n return this;\n }\n\n /**\n * Add a list validation rule to this field (for list fields).\n * This method is protected and should only be used by field-specific validator methods.\n * @internal\n */\n protected listValidation(validation: CmsModelFieldValidation): this {\n this.config.listValidation = this.config.listValidation || [];\n this.config.listValidation.push(validation);\n return this;\n }\n\n predefinedValues(values: CmsModelFieldPredefinedValues[\"values\"]): this {\n this.config.predefinedValues = {\n enabled: true,\n values\n };\n return this;\n }\n\n renderer<TName extends FieldRendererName<TType>>(\n name: TName,\n ...args: undefined extends FieldRendererSettings<TName>\n ? [settings?: FieldRendererSettings<TName>]\n : FieldRendererSettings<TName> extends undefined\n ? []\n : [settings: FieldRendererSettings<TName>]\n ): this {\n this.config.renderer = {\n name: resolveRendererName(name),\n settings: args[0] ?? null\n };\n return this;\n }\n\n settings(settings: Record<string, any>): this {\n this.config.settings = { ...this.config.settings, ...settings };\n return this;\n }\n\n /**\n * Build the final CmsModelField\n * @internal\n */\n build(): DataFieldBuildResult {\n const fieldId = this.config._fieldId || camelCase(this.config.label);\n const baseType = getBaseFieldType({\n type: this.type\n });\n const storageId = `${baseType}@${this.config._storageId ?? fieldId}`;\n\n return {\n type: \"data\",\n field: {\n id: fieldId,\n fieldId,\n storageId,\n type: this.getFieldType(),\n label: this.config.label,\n validation: this.config.validation || [],\n listValidation: this.config.listValidation || [],\n list: this.config.list || false,\n disabled: this.config.disabled || false,\n predefinedValues: this.config.predefinedValues || {\n enabled: false,\n values: []\n },\n help: this.config.help || null,\n placeholder: this.config.placeholder || null,\n description: this.config.description || null,\n note: this.config.note || null,\n renderer: this.config.renderer || null,\n settings: this.config.settings || {},\n tags: this.config.tags || [],\n rules: this.config.rules\n }\n };\n }\n\n private getFieldType(): string {\n if (!this.subType?.length) {\n return this.type;\n }\n return `${this.type}:${this.subType}`;\n }\n}\n"],"names":["rendererNameMap","resolveRendererName","name","DataFieldBuilder","BaseFieldBuilder","type","label","text","id","value","tags","message","validation","values","args","settings","fieldId","camelCase","baseType","getBaseFieldType","storageId"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"features/modelBuilder/fields/DataFieldBuilder.js","sources":["../../../../src/features/modelBuilder/fields/DataFieldBuilder.ts"],"sourcesContent":["import camelCase from \"lodash/camelCase.js\";\nimport type {\n CmsModelField,\n CmsModelFieldPredefinedValues,\n CmsModelFieldValidation\n} from \"~/types/index.js\";\nimport { getBaseFieldType } from \"~/utils/getBaseFieldType.js\";\nimport { BaseFieldBuilder, type DataFieldBuildResult } from \"./BaseFieldBuilder.js\";\n\nexport interface FieldBuilderConfig extends Omit<\n CmsModelField,\n \"id\" | \"fieldId\" | \"storageId\" | \"type\"\n> {\n _storageId?: string;\n _fieldId?: string;\n description?: string | null;\n note?: string | null;\n}\n\n/**\n * Augmentable renderer registry. Each entry maps a renderer name to its applicable field type(s) and settings.\n */\nexport interface IFieldRendererRegistry {\n switch: {\n fieldType: \"boolean\";\n settings: undefined;\n };\n checkboxes: {\n fieldType: \"text\" | \"number\";\n settings: undefined;\n };\n dateTimeInput: {\n fieldType: \"datetime\";\n settings: undefined;\n };\n /**\n * Picks a day, then a month, then a year. For date-only fields (`dateOnly()`); single-value\n * only, not for `.list()`. It offers only the dates the field's `dateGte`/`dateLte` limits\n * allow; without limits, years run from this year back 120 years.\n */\n birthdayInput: {\n fieldType: \"datetime\";\n settings: undefined;\n };\n dateTimeInputs: {\n fieldType: \"datetime\";\n settings?: {\n addItemLabel?: string;\n };\n };\n dynamicZone: {\n fieldType: \"dynamicZone\";\n settings?: {\n /** Whether the accordion is expanded by default. */\n open?: boolean;\n /** Wrap the zone in a container panel. Set to `false` for a flat inline layout. */\n container?: boolean;\n /** Label for the \"add item\" button (defaults to \"Add Item\"). */\n addItemLabel?: string;\n };\n };\n hidden: {\n fieldType: string;\n settings: undefined;\n };\n lexicalEditor: {\n fieldType: \"rich-text\";\n settings: undefined;\n };\n lexicalEditors: {\n fieldType: \"rich-text\";\n settings?: {\n addItemLabel?: string;\n };\n };\n textarea: {\n fieldType: \"long-text\";\n settings: undefined;\n };\n textareas: {\n fieldType: \"long-text\";\n settings: {\n addItemLabel?: string;\n };\n };\n numberInput: {\n fieldType: \"number\";\n settings: undefined;\n };\n numberInputs: {\n fieldType: \"number\";\n settings?: {\n addItemLabel?: string;\n };\n };\n objectAccordionSingle: {\n fieldType: \"object\";\n settings?: {\n /** Whether the accordion is expanded by default. */\n open?: boolean;\n /** Wrap the object in a container panel. Set to `false` for a flat inline layout. */\n container?: boolean;\n /** Field ID whose value is used as the accordion title, or a function receiving the object data. */\n itemTitle?: string;\n /** Field ID whose value is used as the accordion description, or a function receiving the object data. */\n itemDescription?: string;\n };\n };\n objectAccordionMultiple: {\n fieldType: \"object\";\n settings?: {\n /** Whether each accordion item is expanded by default. */\n open?: boolean;\n /** Wrap the list in a container panel. Set to `false` for a flat inline layout. */\n container?: boolean;\n /** Field ID whose value is used as each item's accordion title. */\n itemTitle?: string;\n /** Field ID whose value is used as each item's accordion description. */\n itemDescription?: string;\n /** Label for the \"add item\" button (defaults to \"Add {fieldLabel}\"). */\n addItemLabel?: string;\n };\n };\n passthrough: {\n fieldType: string;\n settings: undefined;\n };\n radioButtons: {\n fieldType: \"text\" | \"number\";\n settings: undefined;\n };\n refDialogSingle: {\n fieldType: \"ref\";\n settings: undefined;\n };\n refDialogMultiple: {\n fieldType: \"ref\";\n settings?: {\n /** Where newly picked references are inserted: `\"first\"` (top) or `\"last\"` (bottom, default). */\n newItemPosition?: \"first\" | \"last\";\n };\n };\n refAutocompleteSingle: {\n fieldType: \"ref\";\n settings: undefined;\n };\n refAutocompleteMultiple: {\n fieldType: \"ref\";\n settings: undefined;\n };\n refCheckboxes: {\n fieldType: \"ref\";\n settings: undefined;\n };\n refRadioButtons: {\n fieldType: \"ref\";\n settings: undefined;\n };\n select: {\n fieldType: \"text\" | \"number\";\n settings: undefined;\n };\n /** @deprecated Use \"select\" instead. */\n dropdown: {\n fieldType: \"text\" | \"number\";\n settings: undefined;\n };\n tags: {\n fieldType: \"text\";\n settings: undefined;\n };\n textInput: {\n fieldType: \"text\";\n settings: undefined;\n };\n textInputs: {\n fieldType: \"text\";\n settings?: {\n addItemLabel?: string;\n };\n };\n /** @deprecated Use `assetField` instead. */\n file: {\n fieldType: \"file\";\n settings?: {\n /** Only allow image files to be selected. */\n imagesOnly?: boolean;\n };\n };\n /** @deprecated Use `assetFields` instead. */\n files: {\n fieldType: \"file\";\n settings?: {\n /** Only allow image files to be selected. */\n imagesOnly?: boolean;\n };\n };\n assetField: {\n fieldType: \"asset\";\n settings?: {\n /** Only allow image files to be selected. */\n imagesOnly?: boolean;\n /** MIME types or extensions to allow (e.g. `[\"image/png\", \"application/pdf\"]`). Leave empty to allow all. */\n accept?: string[];\n };\n };\n assetFields: {\n fieldType: \"asset\";\n settings?: {\n /** Only allow image files to be selected. */\n imagesOnly?: boolean;\n /** MIME types or extensions to allow (e.g. `[\"image/png\", \"application/pdf\"]`). Leave empty to allow all. */\n accept?: string[];\n };\n };\n uiSeparator: {\n fieldType: \"ui\";\n settings: undefined;\n };\n uiAlert: {\n fieldType: \"ui\";\n settings: {\n type: \"info\" | \"success\" | \"warning\" | \"danger\";\n };\n };\n uiTabs: {\n fieldType: \"ui\";\n settings: undefined;\n };\n}\n\n/**\n * Maps camelCase renderer names (used in the builder API) to the\n * kebab-case names expected by the frontend renderer registry.\n */\nconst rendererNameMap: Record<keyof IFieldRendererRegistry, string> = {\n switch: \"boolean-input\",\n checkboxes: \"checkboxes\",\n dateTimeInput: \"date-time-input\",\n dateTimeInputs: \"date-time-inputs\",\n birthdayInput: \"birthday-input\",\n dynamicZone: \"dynamicZone\",\n hidden: \"hidden\",\n lexicalEditor: \"lexical-text-input\",\n lexicalEditors: \"lexical-text-inputs\",\n textarea: \"long-text-text-area\",\n textareas: \"long-text-inputs\",\n numberInput: \"number-input\",\n numberInputs: \"number-inputs\",\n objectAccordionSingle: \"object-accordion\",\n objectAccordionMultiple: \"objects-accordion\",\n passthrough: \"passthrough\",\n radioButtons: \"radio-buttons\",\n refDialogSingle: \"ref-advanced-single\",\n refDialogMultiple: \"ref-advanced-multiple\",\n refAutocompleteSingle: \"ref-input\",\n refAutocompleteMultiple: \"ref-inputs\",\n refCheckboxes: \"ref-simple-multiple\",\n refRadioButtons: \"ref-simple-single\",\n select: \"select-box\",\n dropdown: \"select-box\",\n tags: \"tags\",\n textInput: \"text-input\",\n textInputs: \"text-inputs\",\n file: \"file-input\",\n files: \"file-inputs\",\n assetField: \"asset-input\",\n assetFields: \"asset-inputs\",\n uiSeparator: \"uiSeparator\",\n uiAlert: \"uiAlert\",\n uiTabs: \"uiTabs\"\n};\n\n/**\n * Resolves a camelCase renderer name to the kebab-case name used by the frontend.\n */\nfunction resolveRendererName(name: string): string {\n return rendererNameMap[name as keyof IFieldRendererRegistry] ?? name;\n}\n\n/**\n * Extracts renderer names valid for the given field type.\n * When TType is a broad `string`, all renderer names are returned.\n */\nexport type FieldRendererName<TType extends string = string> = string extends TType\n ? keyof IFieldRendererRegistry & string\n : {\n [K in keyof IFieldRendererRegistry]: TType extends IFieldRendererRegistry[K][\"fieldType\"]\n ? K\n : never;\n }[keyof IFieldRendererRegistry] &\n string;\n\nexport type FieldRendererSettings<TName extends string> = TName extends keyof IFieldRendererRegistry\n ? IFieldRendererRegistry[TName][\"settings\"]\n : Record<string, any> | undefined;\n\n/**\n * DataFieldBuilder class for data fields that produce CmsModelField instances.\n * Provides storageId, list, validation, renderer, and other data-field methods.\n */\nexport class DataFieldBuilder<TType extends string = string> extends BaseFieldBuilder<TType> {\n protected override config: FieldBuilderConfig;\n\n public constructor(type: TType, label?: string) {\n super(type, label);\n this.config = {\n label: label || \"\",\n validation: [],\n listValidation: [],\n list: false,\n predefinedValues: {\n enabled: false,\n values: []\n },\n help: null,\n placeholder: null,\n description: null,\n note: null,\n renderer: null,\n settings: {},\n tags: []\n };\n }\n\n placeholder(text: string): this {\n this.config.placeholder = text;\n return this;\n }\n\n storageId(id: string): this {\n // We do not allow developers to specify the field type!\n this.config._storageId = id.split(\"@\").pop();\n return this;\n }\n\n defaultValue(value: any): this {\n this.config.settings = { ...this.config.settings, defaultValue: value };\n return this;\n }\n\n list(): this {\n this.config.list = true;\n return this as this;\n }\n\n /**\n * Renders the field in the admin app, but doesn't let anyone edit its value.\n * Handy for values your own code owns, like an external ID or a generated slug.\n *\n * Only the admin app honours this. The API doesn't check it, so the field can\n * still be written to over GraphQL.\n */\n disabled(value = true): this {\n this.config.disabled = value;\n return this;\n }\n\n tags(tags: string[]): this {\n this.config.tags = tags;\n return this;\n }\n\n /**\n * List validators - these methods are available after calling list()\n */\n listMinLength(value: number, message?: string): this {\n return this.listValidation({\n name: \"minLength\",\n message: message || `At least ${value} item(s) required.`,\n settings: { value }\n });\n }\n\n listMaxLength(value: number, message?: string): this {\n return this.listValidation({\n name: \"maxLength\",\n message: message || `At most ${value} item(s) allowed.`,\n settings: { value }\n });\n }\n\n /**\n * Add a validation rule to this field.\n * This method is protected and should only be used by field-specific validator methods.\n * @internal\n */\n protected validation(validation: CmsModelFieldValidation): this {\n this.config.validation = this.config.validation || [];\n this.config.validation.push(validation);\n return this;\n }\n\n /**\n * Add a list validation rule to this field (for list fields).\n * This method is protected and should only be used by field-specific validator methods.\n * @internal\n */\n protected listValidation(validation: CmsModelFieldValidation): this {\n this.config.listValidation = this.config.listValidation || [];\n this.config.listValidation.push(validation);\n return this;\n }\n\n predefinedValues(values: CmsModelFieldPredefinedValues[\"values\"]): this {\n this.config.predefinedValues = {\n enabled: true,\n values\n };\n return this;\n }\n\n renderer<TName extends FieldRendererName<TType>>(\n name: TName,\n ...args: undefined extends FieldRendererSettings<TName>\n ? [settings?: FieldRendererSettings<TName>]\n : FieldRendererSettings<TName> extends undefined\n ? []\n : [settings: FieldRendererSettings<TName>]\n ): this {\n this.config.renderer = {\n name: resolveRendererName(name),\n settings: args[0] ?? null\n };\n return this;\n }\n\n settings(settings: Record<string, any>): this {\n this.config.settings = { ...this.config.settings, ...settings };\n return this;\n }\n\n /**\n * Build the final CmsModelField\n * @internal\n */\n build(): DataFieldBuildResult {\n const fieldId = this.config._fieldId || camelCase(this.config.label);\n const baseType = getBaseFieldType({\n type: this.type\n });\n const storageId = `${baseType}@${this.config._storageId ?? fieldId}`;\n\n return {\n type: \"data\",\n field: {\n id: fieldId,\n fieldId,\n storageId,\n type: this.getFieldType(),\n label: this.config.label,\n validation: this.config.validation || [],\n listValidation: this.config.listValidation || [],\n list: this.config.list || false,\n disabled: this.config.disabled || false,\n predefinedValues: this.config.predefinedValues || {\n enabled: false,\n values: []\n },\n help: this.config.help || null,\n placeholder: this.config.placeholder || null,\n description: this.config.description || null,\n note: this.config.note || null,\n renderer: this.config.renderer || null,\n settings: this.config.settings || {},\n tags: this.config.tags || [],\n rules: this.config.rules\n }\n };\n }\n\n private getFieldType(): string {\n if (!this.subType?.length) {\n return this.type;\n }\n return `${this.type}:${this.subType}`;\n }\n}\n"],"names":["rendererNameMap","resolveRendererName","name","DataFieldBuilder","BaseFieldBuilder","type","label","text","id","value","tags","message","validation","values","args","settings","fieldId","camelCase","baseType","getBaseFieldType","storageId"],"mappings":"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"}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import error from "@webiny/error";
|
|
2
|
+
import { isDateExpression } from "@webiny/utils";
|
|
3
|
+
import { validateDateExpressions } from "../../../domain/contentModel/validation/dateExpressions.js";
|
|
1
4
|
import { FieldType } from "./abstractions.js";
|
|
2
5
|
import { DataFieldBuilder } from "./FieldBuilder.js";
|
|
3
6
|
class DateTimeFieldBuilder extends DataFieldBuilder {
|
|
@@ -30,26 +33,37 @@ class DateTimeFieldBuilder extends DataFieldBuilder {
|
|
|
30
33
|
return this.dateTimeType("dateTimeWithoutTimezone");
|
|
31
34
|
}
|
|
32
35
|
dateGte(value, message) {
|
|
33
|
-
const type = this.config.settings?.type || "date";
|
|
34
36
|
return this.validation({
|
|
35
37
|
name: "dateGte",
|
|
36
|
-
message: message || "
|
|
37
|
-
settings:
|
|
38
|
-
value,
|
|
39
|
-
type
|
|
40
|
-
}
|
|
38
|
+
message: message || "Must be on or after {date}.",
|
|
39
|
+
settings: this.dateLimitSettings(value)
|
|
41
40
|
});
|
|
42
41
|
}
|
|
43
42
|
dateLte(value, message) {
|
|
44
|
-
const type = this.config.settings?.type || "date";
|
|
45
43
|
return this.validation({
|
|
46
44
|
name: "dateLte",
|
|
47
|
-
message: message || "
|
|
48
|
-
settings:
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
45
|
+
message: message || "Must be on or before {date}.",
|
|
46
|
+
settings: this.dateLimitSettings(value)
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
build() {
|
|
50
|
+
const result = super.build();
|
|
51
|
+
validateDateExpressions(result.field);
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
dateLimitSettings(value) {
|
|
55
|
+
const type = this.config.settings?.type || "date";
|
|
56
|
+
if ("string" == typeof value) return {
|
|
57
|
+
value,
|
|
58
|
+
type
|
|
59
|
+
};
|
|
60
|
+
if (!isDateExpression(value.expression)) throw new error(`Invalid date expression "${value.expression}". Use "today", or "today" plus or minus a number of days, months or years, like "today-18y".`, "INVALID_DATE_EXPRESSION", {
|
|
61
|
+
expression: value.expression
|
|
52
62
|
});
|
|
63
|
+
return {
|
|
64
|
+
expression: value.expression,
|
|
65
|
+
type
|
|
66
|
+
};
|
|
53
67
|
}
|
|
54
68
|
}
|
|
55
69
|
class DateTimeFieldTypeFactory {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/modelBuilder/fields/DateTimeFieldType.js","sources":["../../../../src/features/modelBuilder/fields/DateTimeFieldType.ts"],"sourcesContent":["import { FieldType, type IFieldTypeFactory } from \"./abstractions.js\";\nimport { DataFieldBuilder } from \"./FieldBuilder.js\";\nimport { RequiredValidator, DateGteValidator, DateLteValidator } from \"./fieldTypeValidator.js\";\n\nexport type DateTimeType = \"date\" | \"time\" | \"dateTimeWithTimezone\" | \"dateTimeWithoutTimezone\";\n\nexport interface IDateTimeFieldBuilder\n extends DataFieldBuilder<\"datetime\">, RequiredValidator, DateGteValidator, DateLteValidator {\n dateTimeType(type: DateTimeType): this;\n dateOnly(): this;\n timeOnly(): this;\n withTimezone(): this;\n withoutTimezone(): this;\n}\n\nclass DateTimeFieldBuilder extends DataFieldBuilder<\"datetime\"> implements IDateTimeFieldBuilder {\n public constructor() {\n super(\"datetime\");\n /**\n * Without a subtype the GraphQL, storage and admin layers each assume a different\n * format. Default to an ISO datetime with timezone; subtype methods override it.\n */\n this.withTimezone();\n }\n\n required(message?: string): this {\n return this.validation({\n name: \"required\",\n message: message || \"Value is required.\",\n settings: {}\n });\n }\n\n dateTimeType(type: DateTimeType): this {\n this.config.settings = this.config.settings || {};\n this.config.settings.type = type;\n return this;\n }\n\n /**\n * Just a calendar date, no time, no timezone. Useful for things like birthdays, holidays, or due dates where the time of day doesn't matter.\n * Example: 2026-04-30\n */\n dateOnly(): this {\n return this.dateTimeType(\"date\");\n }\n\n /**\n * Time in 24-hours format.\n * Example: 19:35\n */\n timeOnly(): this {\n return this.dateTimeType(\"time\");\n }\n\n /**\n * This is an ISO 8601 datetime with an explicit timezone offset.\n * Example: 2026-04-29T13:25:00+02:00\n */\n withTimezone(): this {\n return this.dateTimeType(\"dateTimeWithTimezone\");\n }\n\n /**\n * This is an ISO 8601 datetime in UTC (Zulu time). \"Z\" means \"this is UTC, no offset.\"\n * Example: 2026-04-30T13:25:00.000Z\n */\n withoutTimezone(): this {\n return this.dateTimeType(\"dateTimeWithoutTimezone\");\n }\n\n dateGte(value:
|
|
1
|
+
{"version":3,"file":"features/modelBuilder/fields/DateTimeFieldType.js","sources":["../../../../src/features/modelBuilder/fields/DateTimeFieldType.ts"],"sourcesContent":["import WebinyError from \"@webiny/error\";\nimport { isDateExpression, type DateBound } from \"@webiny/utils\";\nimport { validateDateExpressions } from \"~/domain/contentModel/validation/dateExpressions.js\";\nimport { FieldType, type IFieldTypeFactory } from \"./abstractions.js\";\nimport { DataFieldBuilder } from \"./FieldBuilder.js\";\nimport { RequiredValidator, DateGteValidator, DateLteValidator } from \"./fieldTypeValidator.js\";\n\nexport type DateTimeType = \"date\" | \"time\" | \"dateTimeWithTimezone\" | \"dateTimeWithoutTimezone\";\n\nexport interface IDateTimeFieldBuilder\n extends DataFieldBuilder<\"datetime\">, RequiredValidator, DateGteValidator, DateLteValidator {\n dateTimeType(type: DateTimeType): this;\n dateOnly(): this;\n timeOnly(): this;\n withTimezone(): this;\n withoutTimezone(): this;\n}\n\nclass DateTimeFieldBuilder extends DataFieldBuilder<\"datetime\"> implements IDateTimeFieldBuilder {\n public constructor() {\n super(\"datetime\");\n /**\n * Without a subtype the GraphQL, storage and admin layers each assume a different\n * format. Default to an ISO datetime with timezone; subtype methods override it.\n */\n this.withTimezone();\n }\n\n required(message?: string): this {\n return this.validation({\n name: \"required\",\n message: message || \"Value is required.\",\n settings: {}\n });\n }\n\n dateTimeType(type: DateTimeType): this {\n this.config.settings = this.config.settings || {};\n this.config.settings.type = type;\n return this;\n }\n\n /**\n * Just a calendar date, no time, no timezone. Useful for things like birthdays, holidays, or due dates where the time of day doesn't matter.\n * Example: 2026-04-30\n *\n * @example\n * fields.datetime().dateOnly()\n * // day, then month, then year; single values only, not for `.list()`.\n * // The picker offers only the dates the `dateGte`/`dateLte` limits allow.\n * fields\n * .datetime()\n * .dateOnly()\n * .dateGte({ expression: \"today-100y\" })\n * .dateLte({ expression: \"today-18y\" }, \"You must be 18 or older.\")\n * .renderer(\"birthdayInput\")\n */\n dateOnly(): this {\n return this.dateTimeType(\"date\");\n }\n\n /**\n * Time in 24-hours format.\n * Example: 19:35\n */\n timeOnly(): this {\n return this.dateTimeType(\"time\");\n }\n\n /**\n * This is an ISO 8601 datetime with an explicit timezone offset.\n * Example: 2026-04-29T13:25:00+02:00\n */\n withTimezone(): this {\n return this.dateTimeType(\"dateTimeWithTimezone\");\n }\n\n /**\n * This is an ISO 8601 datetime in UTC (Zulu time). \"Z\" means \"this is UTC, no offset.\"\n * Example: 2026-04-30T13:25:00.000Z\n */\n withoutTimezone(): this {\n return this.dateTimeType(\"dateTimeWithoutTimezone\");\n }\n\n /**\n * The value must be on or after the given one: a fixed value, or, on a date-only field, an\n * expression worked out from today's date every time the value is checked.\n *\n * Expressions: `today`, or `today` plus or minus a number of days (`d`), months (`m`) or\n * years (`y`). The birthday picker reads these limits, so dates outside them can't be picked.\n *\n * In `message`, `{date}` is the limit as it was worked out. Without a message, the field\n * says \"Must be on or after {date}.\" (`dateLte`: \"Must be on or before {date}.\").\n *\n * @example\n * fields.datetime().dateOnly().dateGte(\"1900-01-01\")\n * // not older than 100 years\n * fields.datetime().dateOnly().dateGte({ expression: \"today-100y\" })\n */\n dateGte(value: DateBound, message?: string): this {\n return this.validation({\n name: \"dateGte\",\n message: message || \"Must be on or after {date}.\",\n settings: this.dateLimitSettings(value)\n });\n }\n\n /**\n * The value must be on or before the given one: a fixed value, or, on a date-only field, an\n * expression worked out from today's date every time the value is checked.\n *\n * @example\n * fields.datetime().dateOnly().dateLte(\"2026-12-31\")\n * // 18 or older\n * fields\n * .datetime()\n * .dateOnly()\n * .dateLte({ expression: \"today-18y\" }, \"You must be 18 or older.\")\n * .renderer(\"birthdayInput\")\n */\n dateLte(value: DateBound, message?: string): this {\n return this.validation({\n name: \"dateLte\",\n message: message || \"Must be on or before {date}.\",\n settings: this.dateLimitSettings(value)\n });\n }\n\n override build() {\n const result = super.build();\n validateDateExpressions(result.field);\n return result;\n }\n\n private dateLimitSettings(value: DateBound): Record<string, string> {\n const type = this.config.settings?.type || \"date\";\n if (typeof value === \"string\") {\n return { value, type };\n }\n if (!isDateExpression(value.expression)) {\n throw new WebinyError(\n `Invalid date expression \"${value.expression}\". Use \"today\", or \"today\" plus or minus a number of days, months or years, like \"today-18y\".`,\n \"INVALID_DATE_EXPRESSION\",\n { expression: value.expression }\n );\n }\n return { expression: value.expression, type };\n }\n}\n\nclass DateTimeFieldTypeFactory implements IFieldTypeFactory {\n readonly type = \"datetime\";\n\n create(): IDateTimeFieldBuilder {\n return new DateTimeFieldBuilder();\n }\n}\n\nexport const DateTimeFieldType = FieldType.createImplementation({\n implementation: DateTimeFieldTypeFactory,\n dependencies: []\n});\n\n// Module augmentation for TypeScript autocomplete\ndeclare module \"../abstractions.js\" {\n interface IFieldBuilderRegistry {\n datetime(): IDateTimeFieldBuilder;\n }\n}\n"],"names":["DateTimeFieldBuilder","DataFieldBuilder","message","type","value","result","validateDateExpressions","isDateExpression","WebinyError","DateTimeFieldTypeFactory","DateTimeFieldType","FieldType"],"mappings":";;;;;AAkBA,MAAMA,6BAA6BC;IAC/B,aAAqB;QACjB,KAAK,CAAC;QAKN,IAAI,CAAC,YAAY;IACrB;IAEA,SAASC,OAAgB,EAAQ;QAC7B,OAAO,IAAI,CAAC,UAAU,CAAC;YACnB,MAAM;YACN,SAASA,WAAW;YACpB,UAAU,CAAC;QACf;IACJ;IAEA,aAAaC,IAAkB,EAAQ;QACnC,IAAI,CAAC,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC;QAChD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAGA;QAC5B,OAAO,IAAI;IACf;IAiBA,WAAiB;QACb,OAAO,IAAI,CAAC,YAAY,CAAC;IAC7B;IAMA,WAAiB;QACb,OAAO,IAAI,CAAC,YAAY,CAAC;IAC7B;IAMA,eAAqB;QACjB,OAAO,IAAI,CAAC,YAAY,CAAC;IAC7B;IAMA,kBAAwB;QACpB,OAAO,IAAI,CAAC,YAAY,CAAC;IAC7B;IAiBA,QAAQC,KAAgB,EAAEF,OAAgB,EAAQ;QAC9C,OAAO,IAAI,CAAC,UAAU,CAAC;YACnB,MAAM;YACN,SAASA,WAAW;YACpB,UAAU,IAAI,CAAC,iBAAiB,CAACE;QACrC;IACJ;IAeA,QAAQA,KAAgB,EAAEF,OAAgB,EAAQ;QAC9C,OAAO,IAAI,CAAC,UAAU,CAAC;YACnB,MAAM;YACN,SAASA,WAAW;YACpB,UAAU,IAAI,CAAC,iBAAiB,CAACE;QACrC;IACJ;IAES,QAAQ;QACb,MAAMC,SAAS,KAAK,CAAC;QACrBC,wBAAwBD,OAAO,KAAK;QACpC,OAAOA;IACX;IAEQ,kBAAkBD,KAAgB,EAA0B;QAChE,MAAMD,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,QAAQ;QAC3C,IAAI,AAAiB,YAAjB,OAAOC,OACP,OAAO;YAAEA;YAAOD;QAAK;QAEzB,IAAI,CAACI,iBAAiBH,MAAM,UAAU,GAClC,MAAM,IAAII,MACN,CAAC,yBAAyB,EAAEJ,MAAM,UAAU,CAAC,6FAA6F,CAAC,EAC3I,2BACA;YAAE,YAAYA,MAAM,UAAU;QAAC;QAGvC,OAAO;YAAE,YAAYA,MAAM,UAAU;YAAED;QAAK;IAChD;AACJ;AAEA,MAAMM;IAGF,SAAgC;QAC5B,OAAO,IAAIT;IACf;;aAJS,IAAI,GAAG;;AAKpB;AAEO,MAAMU,oBAAoBC,UAAU,oBAAoB,CAAC;IAC5D,gBAAgBF;IAChB,cAAc,EAAE;AACpB"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Validator interface mixins for type-safe field validators.
|
|
3
3
|
* These interfaces provide TypeScript autocomplete and type safety for field validation methods.
|
|
4
4
|
*/
|
|
5
|
+
import type { DateBound } from "@webiny/utils";
|
|
5
6
|
export interface RequiredValidator {
|
|
6
7
|
required(message?: string): this;
|
|
7
8
|
}
|
|
@@ -50,13 +51,14 @@ export interface LteValidator {
|
|
|
50
51
|
lte(value: number, message?: string): this;
|
|
51
52
|
}
|
|
52
53
|
/**
|
|
53
|
-
* Date/time comparison validators
|
|
54
|
+
* Date/time comparison validators. The limit is a fixed value, or, on a date-only field,
|
|
55
|
+
* `{ expression }` worked out from today's date every time the value is checked.
|
|
54
56
|
*/
|
|
55
57
|
export interface DateGteValidator {
|
|
56
|
-
dateGte(value:
|
|
58
|
+
dateGte(value: DateBound, message?: string): this;
|
|
57
59
|
}
|
|
58
60
|
export interface DateLteValidator {
|
|
59
|
-
dateLte(value:
|
|
61
|
+
dateLte(value: DateBound, message?: string): this;
|
|
60
62
|
}
|
|
61
63
|
/**
|
|
62
64
|
* Unique value validator (ensures field value is unique across entries)
|
|
@@ -2,6 +2,11 @@ import type { CmsModelFieldValidatorValidateParams } from "../../../types/index.
|
|
|
2
2
|
export interface ICmsModelFieldValidator {
|
|
3
3
|
readonly name: string;
|
|
4
4
|
validate(params: CmsModelFieldValidatorValidateParams): Promise<boolean>;
|
|
5
|
+
/**
|
|
6
|
+
* The message for a value that failed, when the validator fills in variables of its own.
|
|
7
|
+
* Without it, the validator's message is used as it is.
|
|
8
|
+
*/
|
|
9
|
+
formatMessage?(message: string, params: CmsModelFieldValidatorValidateParams): string;
|
|
5
10
|
}
|
|
6
11
|
export declare const CmsModelFieldValidator: import("@webiny/di").Abstraction<ICmsModelFieldValidator>;
|
|
7
12
|
export declare namespace CmsModelFieldValidator {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/validation/abstractions/CmsModelFieldValidator.js","sources":["../../../../src/features/validation/abstractions/CmsModelFieldValidator.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/createAbstraction.js\";\nimport type { CmsModelFieldValidatorValidateParams } from \"~/types/index.js\";\n\nexport interface ICmsModelFieldValidator {\n readonly name: string;\n validate(params: CmsModelFieldValidatorValidateParams): Promise<boolean>;\n}\n\nexport const CmsModelFieldValidator = createAbstraction<ICmsModelFieldValidator>(\n \"Cms/Model/Field/Validator\"\n);\n\nexport namespace CmsModelFieldValidator {\n export type Interface = ICmsModelFieldValidator;\n export type Params = CmsModelFieldValidatorValidateParams;\n export type Response = Promise<boolean>;\n}\n"],"names":["CmsModelFieldValidator","createAbstraction"],"mappings":";
|
|
1
|
+
{"version":3,"file":"features/validation/abstractions/CmsModelFieldValidator.js","sources":["../../../../src/features/validation/abstractions/CmsModelFieldValidator.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/createAbstraction.js\";\nimport type { CmsModelFieldValidatorValidateParams } from \"~/types/index.js\";\n\nexport interface ICmsModelFieldValidator {\n readonly name: string;\n validate(params: CmsModelFieldValidatorValidateParams): Promise<boolean>;\n /**\n * The message for a value that failed, when the validator fills in variables of its own.\n * Without it, the validator's message is used as it is.\n */\n formatMessage?(message: string, params: CmsModelFieldValidatorValidateParams): string;\n}\n\nexport const CmsModelFieldValidator = createAbstraction<ICmsModelFieldValidator>(\n \"Cms/Model/Field/Validator\"\n);\n\nexport namespace CmsModelFieldValidator {\n export type Interface = ICmsModelFieldValidator;\n export type Params = CmsModelFieldValidatorValidateParams;\n export type Response = Promise<boolean>;\n}\n"],"names":["CmsModelFieldValidator","createAbstraction"],"mappings":";AAaO,MAAMA,yBAAyBC,kBAClC"}
|
|
@@ -3,6 +3,8 @@ import type { CmsModelFieldValidatorValidateParams } from "../../../types/types.
|
|
|
3
3
|
declare class DateGteValidatorImpl implements CmsModelFieldValidator.Interface {
|
|
4
4
|
readonly name = "dateGte";
|
|
5
5
|
validate({ value, validator }: CmsModelFieldValidatorValidateParams): Promise<boolean>;
|
|
6
|
+
/** A message's `{date}` is the limit, as `2008-10-19`. */
|
|
7
|
+
formatMessage(message: string, { validator }: CmsModelFieldValidatorValidateParams): string;
|
|
6
8
|
}
|
|
7
9
|
export declare const DateGteValidator: typeof DateGteValidatorImpl & {
|
|
8
10
|
__abstraction: import("@webiny/di").Abstraction<import("../abstractions/CmsModelFieldValidator.js").ICmsModelFieldValidator>;
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
import { validation } from "@webiny/validation";
|
|
2
2
|
import { CmsModelFieldValidator } from "../abstractions/CmsModelFieldValidator.js";
|
|
3
|
+
import { formatDateLimitMessage, getDateLimit } from "./dateLimit.js";
|
|
3
4
|
class DateGteValidatorImpl {
|
|
4
5
|
async validate({ value, validator }) {
|
|
5
|
-
const {
|
|
6
|
+
const { expression, type } = validator.settings || {};
|
|
7
|
+
let gteValue;
|
|
8
|
+
try {
|
|
9
|
+
gteValue = getDateLimit(validator.settings, "gte");
|
|
10
|
+
} catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
6
13
|
if (void 0 === gteValue) return true;
|
|
7
|
-
else if ("time" === type) return validation.validate(value, `timeGte:${gteValue}`).then((v)=>true === v).catch(()=>false);
|
|
14
|
+
else if ("time" === type && "string" != typeof expression) return validation.validate(value, `timeGte:${gteValue}`).then((v)=>true === v).catch(()=>false);
|
|
8
15
|
return validation.validate(value, `dateGte:${gteValue}`).then((v)=>true === v).catch(()=>false);
|
|
9
16
|
}
|
|
17
|
+
formatMessage(message, { validator }) {
|
|
18
|
+
return formatDateLimitMessage(message, validator.settings, "gte");
|
|
19
|
+
}
|
|
10
20
|
constructor(){
|
|
11
21
|
this.name = "dateGte";
|
|
12
22
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/validation/validators/DateGteValidator.js","sources":["../../../../src/features/validation/validators/DateGteValidator.ts"],"sourcesContent":["import { validation } from \"@webiny/validation\";\nimport { CmsModelFieldValidator } from \"../abstractions/CmsModelFieldValidator.js\";\nimport type { CmsModelFieldValidatorValidateParams } from \"~/types/types.js\";\n\nclass DateGteValidatorImpl implements CmsModelFieldValidator.Interface {\n public readonly name = \"dateGte\";\n\n public async validate({\n value,\n validator\n }: CmsModelFieldValidatorValidateParams): Promise<boolean> {\n const {
|
|
1
|
+
{"version":3,"file":"features/validation/validators/DateGteValidator.js","sources":["../../../../src/features/validation/validators/DateGteValidator.ts"],"sourcesContent":["import { validation } from \"@webiny/validation\";\nimport { CmsModelFieldValidator } from \"../abstractions/CmsModelFieldValidator.js\";\nimport type { CmsModelFieldValidatorValidateParams } from \"~/types/types.js\";\nimport { formatDateLimitMessage, getDateLimit } from \"./dateLimit.js\";\n\nclass DateGteValidatorImpl implements CmsModelFieldValidator.Interface {\n public readonly name = \"dateGte\";\n\n public async validate({\n value,\n validator\n }: CmsModelFieldValidatorValidateParams): Promise<boolean> {\n const { expression, type } = validator.settings || {};\n let gteValue: unknown;\n try {\n gteValue = getDateLimit(validator.settings, \"gte\");\n } catch {\n return false;\n }\n if (typeof gteValue === \"undefined\") {\n return true;\n } else if (type === \"time\" && typeof expression !== \"string\") {\n return validation\n .validate(value, `timeGte:${gteValue}`)\n .then(v => v === true)\n .catch(() => false);\n }\n return validation\n .validate(value, `dateGte:${gteValue}`)\n .then(v => v === true)\n .catch(() => false);\n }\n\n /** A message's `{date}` is the limit, as `2008-10-19`. */\n public formatMessage(message: string, { validator }: CmsModelFieldValidatorValidateParams) {\n return formatDateLimitMessage(message, validator.settings, \"gte\");\n }\n}\n\nexport const DateGteValidator = CmsModelFieldValidator.createImplementation({\n implementation: DateGteValidatorImpl,\n dependencies: []\n});\n"],"names":["DateGteValidatorImpl","value","validator","expression","type","gteValue","getDateLimit","validation","v","message","formatDateLimitMessage","DateGteValidator","CmsModelFieldValidator"],"mappings":";;;AAKA,MAAMA;IAGF,MAAa,SAAS,EAClBC,KAAK,EACLC,SAAS,EAC0B,EAAoB;QACvD,MAAM,EAAEC,UAAU,EAAEC,IAAI,EAAE,GAAGF,UAAU,QAAQ,IAAI,CAAC;QACpD,IAAIG;QACJ,IAAI;YACAA,WAAWC,aAAaJ,UAAU,QAAQ,EAAE;QAChD,EAAE,OAAM;YACJ,OAAO;QACX;QACA,IAAI,AAAoB,WAAbG,UACP,OAAO;aACJ,IAAID,AAAS,WAATA,QAAmB,AAAsB,YAAtB,OAAOD,YACjC,OAAOI,WAAAA,QACM,CAACN,OAAO,CAAC,QAAQ,EAAEI,UAAU,EACrC,IAAI,CAACG,CAAAA,IAAKA,AAAM,SAANA,GACV,KAAK,CAAC,IAAM;QAErB,OAAOD,WAAAA,QACM,CAACN,OAAO,CAAC,QAAQ,EAAEI,UAAU,EACrC,IAAI,CAACG,CAAAA,IAAKA,AAAM,SAANA,GACV,KAAK,CAAC,IAAM;IACrB;IAGO,cAAcC,OAAe,EAAE,EAAEP,SAAS,EAAwC,EAAE;QACvF,OAAOQ,uBAAuBD,SAASP,UAAU,QAAQ,EAAE;IAC/D;;aA9BgB,IAAI,GAAG;;AA+B3B;AAEO,MAAMS,mBAAmBC,uBAAuB,oBAAoB,CAAC;IACxE,gBAAgBZ;IAChB,cAAc,EAAE;AACpB"}
|
|
@@ -3,6 +3,8 @@ import type { CmsModelFieldValidatorValidateParams } from "../../../types/types.
|
|
|
3
3
|
declare class DateLteValidatorImpl implements CmsModelFieldValidator.Interface {
|
|
4
4
|
readonly name = "dateLte";
|
|
5
5
|
validate({ value, validator }: CmsModelFieldValidatorValidateParams): Promise<boolean>;
|
|
6
|
+
/** A message's `{date}` is the limit, as `2008-10-19`. */
|
|
7
|
+
formatMessage(message: string, { validator }: CmsModelFieldValidatorValidateParams): string;
|
|
6
8
|
}
|
|
7
9
|
export declare const DateLteValidator: typeof DateLteValidatorImpl & {
|
|
8
10
|
__abstraction: import("@webiny/di").Abstraction<import("../abstractions/CmsModelFieldValidator.js").ICmsModelFieldValidator>;
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
import { validation } from "@webiny/validation";
|
|
2
2
|
import { CmsModelFieldValidator } from "../abstractions/CmsModelFieldValidator.js";
|
|
3
|
+
import { formatDateLimitMessage, getDateLimit } from "./dateLimit.js";
|
|
3
4
|
class DateLteValidatorImpl {
|
|
4
5
|
async validate({ value, validator }) {
|
|
5
|
-
const {
|
|
6
|
+
const { expression, type } = validator.settings || {};
|
|
7
|
+
let lteValue;
|
|
8
|
+
try {
|
|
9
|
+
lteValue = getDateLimit(validator.settings, "lte");
|
|
10
|
+
} catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
6
13
|
if (void 0 === lteValue) return true;
|
|
7
|
-
else if ("time" === type) return validation.validate(value, `timeLte:${lteValue}`).then((v)=>true === v).catch(()=>false);
|
|
14
|
+
else if ("time" === type && "string" != typeof expression) return validation.validate(value, `timeLte:${lteValue}`).then((v)=>true === v).catch(()=>false);
|
|
8
15
|
return validation.validate(value, `dateLte:${lteValue}`).then((v)=>true === v).catch(()=>false);
|
|
9
16
|
}
|
|
17
|
+
formatMessage(message, { validator }) {
|
|
18
|
+
return formatDateLimitMessage(message, validator.settings, "lte");
|
|
19
|
+
}
|
|
10
20
|
constructor(){
|
|
11
21
|
this.name = "dateLte";
|
|
12
22
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/validation/validators/DateLteValidator.js","sources":["../../../../src/features/validation/validators/DateLteValidator.ts"],"sourcesContent":["import { validation } from \"@webiny/validation\";\nimport { CmsModelFieldValidator } from \"../abstractions/CmsModelFieldValidator.js\";\nimport type { CmsModelFieldValidatorValidateParams } from \"~/types/types.js\";\n\nclass DateLteValidatorImpl implements CmsModelFieldValidator.Interface {\n public readonly name = \"dateLte\";\n\n public async validate({\n value,\n validator\n }: CmsModelFieldValidatorValidateParams): Promise<boolean> {\n const {
|
|
1
|
+
{"version":3,"file":"features/validation/validators/DateLteValidator.js","sources":["../../../../src/features/validation/validators/DateLteValidator.ts"],"sourcesContent":["import { validation } from \"@webiny/validation\";\nimport { CmsModelFieldValidator } from \"../abstractions/CmsModelFieldValidator.js\";\nimport type { CmsModelFieldValidatorValidateParams } from \"~/types/types.js\";\nimport { formatDateLimitMessage, getDateLimit } from \"./dateLimit.js\";\n\nclass DateLteValidatorImpl implements CmsModelFieldValidator.Interface {\n public readonly name = \"dateLte\";\n\n public async validate({\n value,\n validator\n }: CmsModelFieldValidatorValidateParams): Promise<boolean> {\n const { expression, type } = validator.settings || {};\n let lteValue: unknown;\n try {\n lteValue = getDateLimit(validator.settings, \"lte\");\n } catch {\n return false;\n }\n if (typeof lteValue === \"undefined\") {\n return true;\n } else if (type === \"time\" && typeof expression !== \"string\") {\n return validation\n .validate(value, `timeLte:${lteValue}`)\n .then(v => v === true)\n .catch(() => false);\n }\n return validation\n .validate(value, `dateLte:${lteValue}`)\n .then(v => v === true)\n .catch(() => false);\n }\n\n /** A message's `{date}` is the limit, as `2008-10-19`. */\n public formatMessage(message: string, { validator }: CmsModelFieldValidatorValidateParams) {\n return formatDateLimitMessage(message, validator.settings, \"lte\");\n }\n}\n\nexport const DateLteValidator = CmsModelFieldValidator.createImplementation({\n implementation: DateLteValidatorImpl,\n dependencies: []\n});\n"],"names":["DateLteValidatorImpl","value","validator","expression","type","lteValue","getDateLimit","validation","v","message","formatDateLimitMessage","DateLteValidator","CmsModelFieldValidator"],"mappings":";;;AAKA,MAAMA;IAGF,MAAa,SAAS,EAClBC,KAAK,EACLC,SAAS,EAC0B,EAAoB;QACvD,MAAM,EAAEC,UAAU,EAAEC,IAAI,EAAE,GAAGF,UAAU,QAAQ,IAAI,CAAC;QACpD,IAAIG;QACJ,IAAI;YACAA,WAAWC,aAAaJ,UAAU,QAAQ,EAAE;QAChD,EAAE,OAAM;YACJ,OAAO;QACX;QACA,IAAI,AAAoB,WAAbG,UACP,OAAO;aACJ,IAAID,AAAS,WAATA,QAAmB,AAAsB,YAAtB,OAAOD,YACjC,OAAOI,WAAAA,QACM,CAACN,OAAO,CAAC,QAAQ,EAAEI,UAAU,EACrC,IAAI,CAACG,CAAAA,IAAKA,AAAM,SAANA,GACV,KAAK,CAAC,IAAM;QAErB,OAAOD,WAAAA,QACM,CAACN,OAAO,CAAC,QAAQ,EAAEI,UAAU,EACrC,IAAI,CAACG,CAAAA,IAAKA,AAAM,SAANA,GACV,KAAK,CAAC,IAAM;IACrB;IAGO,cAAcC,OAAe,EAAE,EAAEP,SAAS,EAAwC,EAAE;QACvF,OAAOQ,uBAAuBD,SAASP,UAAU,QAAQ,EAAE;IAC/D;;aA9BgB,IAAI,GAAG;;AA+B3B;AAEO,MAAMS,mBAAmBC,uBAAuB,oBAAoB,CAAC;IACxE,gBAAgBZ;IAChB,cAAc,EAAE;AACpB"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The limit a `dateGte`/`dateLte` validator stands for: its fixed value, or its expression
|
|
3
|
+
* worked out against "today". The server can't know the user's timezone, so a lower limit uses
|
|
4
|
+
* the earliest date on Earth and an upper limit the latest: a date the user's own today allows
|
|
5
|
+
* always passes. Throws when an expression can't be read.
|
|
6
|
+
*/
|
|
7
|
+
export declare function getDateLimit(settings: Record<string, any> | undefined, limit: "gte" | "lte"): unknown;
|
|
8
|
+
/** Puts the limit into a message's `{date}`, as it is stored (`2008-10-19`). */
|
|
9
|
+
export declare function formatDateLimitMessage(message: string, settings: Record<string, any> | undefined, limit: "gte" | "lte"): string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { getLenientToday, resolveDateExpression } from "@webiny/utils";
|
|
2
|
+
function getDateLimit(settings, limit) {
|
|
3
|
+
const { value, expression } = settings || {};
|
|
4
|
+
if ("string" == typeof expression) return resolveDateExpression(expression, getLenientToday(limit));
|
|
5
|
+
return value;
|
|
6
|
+
}
|
|
7
|
+
function formatDateLimitMessage(message, settings, limit) {
|
|
8
|
+
let date;
|
|
9
|
+
try {
|
|
10
|
+
date = getDateLimit(settings, limit);
|
|
11
|
+
} catch {
|
|
12
|
+
return message;
|
|
13
|
+
}
|
|
14
|
+
return null == date ? message : message.replace(/\{date}/g, String(date));
|
|
15
|
+
}
|
|
16
|
+
export { formatDateLimitMessage, getDateLimit };
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=dateLimit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/validation/validators/dateLimit.js","sources":["../../../../src/features/validation/validators/dateLimit.ts"],"sourcesContent":["import { getLenientToday, resolveDateExpression } from \"@webiny/utils\";\n\n/**\n * The limit a `dateGte`/`dateLte` validator stands for: its fixed value, or its expression\n * worked out against \"today\". The server can't know the user's timezone, so a lower limit uses\n * the earliest date on Earth and an upper limit the latest: a date the user's own today allows\n * always passes. Throws when an expression can't be read.\n */\nexport function getDateLimit(\n settings: Record<string, any> | undefined,\n limit: \"gte\" | \"lte\"\n): unknown {\n const { value, expression } = settings || {};\n if (typeof expression === \"string\") {\n return resolveDateExpression(expression, getLenientToday(limit));\n }\n return value;\n}\n\n/** Puts the limit into a message's `{date}`, as it is stored (`2008-10-19`). */\nexport function formatDateLimitMessage(\n message: string,\n settings: Record<string, any> | undefined,\n limit: \"gte\" | \"lte\"\n): string {\n let date: unknown;\n try {\n date = getDateLimit(settings, limit);\n } catch {\n return message;\n }\n return date == null ? message : message.replace(/\\{date}/g, String(date));\n}\n"],"names":["getDateLimit","settings","limit","value","expression","resolveDateExpression","getLenientToday","formatDateLimitMessage","message","date","String"],"mappings":";AAQO,SAASA,aACZC,QAAyC,EACzCC,KAAoB;IAEpB,MAAM,EAAEC,KAAK,EAAEC,UAAU,EAAE,GAAGH,YAAY,CAAC;IAC3C,IAAI,AAAsB,YAAtB,OAAOG,YACP,OAAOC,sBAAsBD,YAAYE,gBAAgBJ;IAE7D,OAAOC;AACX;AAGO,SAASI,uBACZC,OAAe,EACfP,QAAyC,EACzCC,KAAoB;IAEpB,IAAIO;IACJ,IAAI;QACAA,OAAOT,aAAaC,UAAUC;IAClC,EAAE,OAAM;QACJ,OAAOM;IACX;IACA,OAAOC,AAAQ,QAARA,OAAeD,UAAUA,QAAQ,OAAO,CAAC,YAAYE,OAAOD;AACvE"}
|
|
@@ -16,6 +16,16 @@ const createDate = (date)=>{
|
|
|
16
16
|
return new Date();
|
|
17
17
|
}
|
|
18
18
|
};
|
|
19
|
+
const SEARCH_CANDIDATES_LIMIT = 100;
|
|
20
|
+
const getSearchRank = (title, query)=>{
|
|
21
|
+
const normalizedQuery = query?.trim().toLowerCase();
|
|
22
|
+
if (!normalizedQuery) return 0;
|
|
23
|
+
const normalizedTitle = (title || "").trim().toLowerCase();
|
|
24
|
+
if (normalizedTitle === normalizedQuery) return 0;
|
|
25
|
+
else if (normalizedTitle.startsWith(normalizedQuery)) return 1;
|
|
26
|
+
else if (normalizedTitle.includes(normalizedQuery)) return 2;
|
|
27
|
+
return 3;
|
|
28
|
+
};
|
|
19
29
|
const createCmsEntryRecord = (model, entry)=>({
|
|
20
30
|
id: entry.id,
|
|
21
31
|
entryId: entry.entryId,
|
|
@@ -239,11 +249,12 @@ const createContentEntriesSchema = ({ context })=>{
|
|
|
239
249
|
async searchContentEntries (_, args, context) {
|
|
240
250
|
const { modelIds, fields, query, limit = 10 } = args;
|
|
241
251
|
const models = await context.cms.listModels();
|
|
252
|
+
const storageLimit = query ? Math.max(limit, SEARCH_CANDIDATES_LIMIT) : limit;
|
|
242
253
|
const getters = models.filter((model)=>modelIds.includes(model.modelId)).map(async (model)=>{
|
|
243
254
|
const listLatest = await context.container.resolve(ListLatestEntriesUseCase);
|
|
244
255
|
const where = {};
|
|
245
256
|
const result = await listLatest.execute(model, {
|
|
246
|
-
limit,
|
|
257
|
+
limit: storageLimit,
|
|
247
258
|
where,
|
|
248
259
|
search: query ? query : void 0,
|
|
249
260
|
fields: fields || []
|
|
@@ -254,7 +265,11 @@ const createContentEntriesSchema = ({ context })=>{
|
|
|
254
265
|
});
|
|
255
266
|
try {
|
|
256
267
|
const entries = await Promise.all(getters).then((results)=>results.reduce((result, item)=>result.concat(item), []));
|
|
257
|
-
return new Response(entries.sort((a, b)=>
|
|
268
|
+
return new Response(entries.sort((a, b)=>{
|
|
269
|
+
const rank = getSearchRank(a.title, query) - getSearchRank(b.title, query);
|
|
270
|
+
if (0 !== rank) return rank;
|
|
271
|
+
return b.savedOn.getTime() - a.savedOn.getTime();
|
|
272
|
+
}).slice(0, limit));
|
|
258
273
|
} catch (ex) {
|
|
259
274
|
return new ErrorResponse(ex);
|
|
260
275
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graphql/schema/contentEntries.js","sources":["../../../src/graphql/schema/contentEntries.ts"],"sourcesContent":["import WebinyError from \"@webiny/error\";\nimport { ErrorResponse, Response } from \"@webiny/handler-graphql\";\nimport type {\n CmsContext,\n CmsEntry,\n CmsEntryListWhere,\n CmsIdentity,\n CmsModel\n} from \"~/types/index.js\";\nimport { getEntryTitle } from \"~/utils/getEntryTitle.js\";\nimport type { ICmsGraphQLSchemaPlugin } from \"~/plugins/index.js\";\nimport { createCmsGraphQLSchemaPlugin } from \"~/plugins/index.js\";\nimport { getEntryDescription } from \"~/utils/getEntryDescription.js\";\nimport { getEntryImage } from \"~/utils/getEntryImage.js\";\nimport { entryFieldFromStorageTransform } from \"~/utils/entryStorage.js\";\nimport type { GraphQLFieldResolver } from \"@webiny/handler-graphql/types.js\";\nimport { ENTRY_META_FIELDS, isDateTimeEntryMetaField } from \"~/constants.js\";\nimport NotAuthorizedResponse from \"@webiny/api-core/graphql/security/NotAuthorizedResponse.js\";\nimport { ListLatestEntriesUseCase } from \"~/features/contentEntry/ListEntries/index.js\";\n\ninterface EntriesByModel {\n [key: string]: string[];\n}\n\ntype GetContentEntryType = \"latest\" | \"published\" | \"exact\";\n\nconst createDate = (date: string | null): Date | null => {\n if (!date) {\n return null;\n }\n\n try {\n return new Date(date);\n } catch {\n return new Date();\n }\n};\n\ninterface CmsEntryRecord {\n id: string;\n entryId: string;\n model: {\n modelId: string;\n name: string;\n };\n status: string;\n title: string;\n description?: string | null;\n image?: string | null;\n\n /**\n * Entry-level meta fields. 👇\n */\n createdOn: Date;\n modifiedOn: Date | null;\n savedOn: Date;\n createdBy: CmsIdentity;\n modifiedBy: CmsIdentity | null;\n savedBy: CmsIdentity;\n firstPublishedOn: Date | null;\n lastPublishedOn: Date | null;\n firstPublishedBy: CmsIdentity | null;\n lastPublishedBy: CmsIdentity | null;\n\n /**\n * Revision-level meta fields. 👇\n */\n revisionCreatedOn: Date;\n revisionModifiedOn: Date | null;\n revisionSavedOn: Date;\n revisionCreatedBy: CmsIdentity;\n revisionModifiedBy: CmsIdentity | null;\n revisionSavedBy: CmsIdentity;\n revisionFirstPublishedOn: Date | null;\n revisionLastPublishedOn: Date | null;\n revisionFirstPublishedBy: CmsIdentity | null;\n revisionLastPublishedBy: CmsIdentity | null;\n\n wbyAco_location?: {\n folderId?: string | null;\n };\n}\n\nconst createCmsEntryRecord = (model: CmsModel, entry: CmsEntry): CmsEntryRecord => {\n return {\n id: entry.id,\n entryId: entry.entryId,\n model: {\n modelId: model.modelId,\n name: model.name\n },\n status: entry.status,\n title: getEntryTitle(model, entry),\n description: getEntryDescription(model, entry),\n image: getEntryImage(model, entry),\n\n /**\n * Entry-level meta fields. 👇\n */\n createdOn: createDate(entry.createdOn)!,\n modifiedOn: createDate(entry.modifiedOn),\n savedOn: createDate(entry.savedOn)!,\n createdBy: entry.createdBy,\n savedBy: entry.savedBy,\n modifiedBy: entry.modifiedBy,\n firstPublishedOn: createDate(entry.firstPublishedOn),\n lastPublishedOn: createDate(entry.lastPublishedOn),\n firstPublishedBy: entry.firstPublishedBy,\n lastPublishedBy: entry.lastPublishedBy,\n\n /**\n * Revision-level meta fields. 👇\n */\n revisionCreatedOn: createDate(entry.revisionCreatedOn)!,\n revisionSavedOn: createDate(entry.revisionSavedOn)!,\n revisionModifiedOn: createDate(entry.revisionModifiedOn),\n revisionCreatedBy: entry.revisionCreatedBy,\n revisionModifiedBy: entry.revisionModifiedBy,\n revisionSavedBy: entry.revisionSavedBy,\n revisionFirstPublishedOn: createDate(entry.revisionFirstPublishedOn),\n revisionLastPublishedOn: createDate(entry.revisionLastPublishedOn),\n revisionFirstPublishedBy: entry.revisionFirstPublishedBy,\n revisionLastPublishedBy: entry.revisionLastPublishedBy,\n\n wbyAco_location: {\n folderId: entry.location?.folderId || null\n }\n };\n};\n\ninterface FetchMethod {\n (model: CmsModel, ids: string[]): Promise<CmsEntry[]>;\n}\n\nconst getFetchMethod = (type: GetContentEntryType, context: CmsContext): FetchMethod => {\n if (!getContentEntriesMethods[type]) {\n throw new WebinyError(\n `Unknown getContentEntries method \"${type}\". Could not fetch content entries.`,\n \"UNKNOWN_METHOD_ERROR\",\n {\n type\n }\n );\n }\n const methodName = getContentEntriesMethods[type] as GetContentEntryMethods;\n if (!context.cms[methodName]) {\n throw new WebinyError(\n `Unknown context.cms method \"${methodName}\". Could not fetch content entries.`,\n \"UNKNOWN_METHOD_ERROR\",\n {\n type,\n methodName\n }\n );\n }\n\n return context.cms[methodName];\n};\n\n/**\n * Function to get the list of content entries depending on latest, published or exact GraphQL queries.\n */\ninterface GetContentEntriesParams {\n args: {\n entries: Pick<CmsEntry, \"id\" | \"modelId\">[];\n };\n context: CmsContext;\n type: GetContentEntryType;\n}\n\nenum GetContentEntryMethods {\n getLatestEntriesByIds = \"getLatestEntriesByIds\",\n getPublishedEntriesByIds = \"getPublishedEntriesByIds\",\n getEntriesByIds = \"getEntriesByIds\"\n}\n\nconst getContentEntriesMethods = {\n latest: \"getLatestEntriesByIds\",\n published: \"getPublishedEntriesByIds\",\n exact: \"getEntriesByIds\"\n};\nconst getContentEntries = async (\n params: GetContentEntriesParams\n): Promise<Response | ErrorResponse> => {\n const { args, context, type } = params;\n\n const method = getFetchMethod(type, context);\n\n const models = await context.cms.listModels();\n\n const modelsMap = models.reduce(\n (collection, model) => {\n collection[model.modelId] = model;\n return collection;\n },\n {} as Record<string, CmsModel>\n );\n\n const argsEntries = args.entries as Pick<CmsEntry, \"id\" | \"modelId\">[];\n\n const entriesByModel = argsEntries.reduce((collection, ref) => {\n if (!collection[ref.modelId]) {\n collection[ref.modelId] = [];\n } else if (collection[ref.modelId].includes(ref.id)) {\n return collection;\n }\n collection[ref.modelId].push(ref.id);\n return collection;\n }, {} as EntriesByModel);\n\n const getters: Promise<CmsEntry[]>[] = Object.keys(entriesByModel).map(async modelId => {\n return method(modelsMap[modelId], entriesByModel[modelId]);\n });\n\n if (getters.length === 0) {\n return new Response([]);\n }\n\n try {\n const results = await Promise.all(getters);\n\n const entries = results\n .reduce<CmsEntryRecord[]>((collection, items) => {\n return collection.concat(\n items.map(item => {\n const model = modelsMap[item.modelId];\n\n return createCmsEntryRecord(model, item);\n })\n );\n }, [])\n .filter(Boolean);\n\n return new Response(entries);\n } catch (ex) {\n return new ErrorResponse(ex);\n }\n};\n\n/**\n * Function to fetch a single content entry depending on latest, published or exact GraphQL query.\n */\ninterface GetContentEntryParams {\n args: {\n entry: Pick<CmsEntry, \"id\" | \"modelId\">;\n };\n context: CmsContext;\n type: \"latest\" | \"published\" | \"exact\";\n}\n\nconst getContentEntry = async (\n params: GetContentEntryParams\n): Promise<Response<CmsEntryRecord | null> | NotAuthorizedResponse> => {\n const { args, context, type } = params;\n if (!getContentEntriesMethods[type]) {\n throw new WebinyError(\n `Unknown getContentEntry method \"${type}\". Could not fetch content entry.`,\n \"UNKNOWN_METHOD_ERROR\",\n {\n args,\n type\n }\n );\n }\n\n const method = getFetchMethod(type, context);\n\n const { modelId, id } = args.entry;\n const models = await context.cms.listModels();\n const model = models.find(m => m.modelId === modelId);\n\n if (!model) {\n return new NotAuthorizedResponse({\n data: {\n modelId\n }\n });\n }\n\n const result = await method(model, [id]);\n\n const entry = result.shift();\n if (!entry) {\n return new Response(null);\n }\n\n return new Response(createCmsEntryRecord(model, entry));\n};\n/**\n * As we support description field, we need to transform the value from storage.\n */\nconst createResolveDescription = (): GraphQLFieldResolver<any, any, CmsContext> => {\n return async (parent, _, context) => {\n const models = await context.cms.listModels();\n const model = models.find(({ modelId }) => {\n return parent.model.modelId === modelId;\n });\n if (!model) {\n return null;\n }\n const field = model.fields.find(f => f.fieldId === model.descriptionFieldId);\n if (!field) {\n return null;\n }\n const value = parent.description || parent[field.fieldId];\n if (!value) {\n return null;\n }\n return entryFieldFromStorageTransform({\n context,\n model,\n field,\n value\n });\n };\n};\n\ninterface Params {\n context: CmsContext;\n}\n\nexport const createContentEntriesSchema = ({\n context\n}: Params): ICmsGraphQLSchemaPlugin<CmsContext> => {\n if (!context.cms.MANAGE) {\n const plugin = createCmsGraphQLSchemaPlugin({\n typeDefs: \"\",\n resolvers: {}\n });\n plugin.name = `headless-cms.graphql.schema.${context.cms.type}.empty`;\n return plugin;\n }\n\n const onByMetaFields = ENTRY_META_FIELDS.map(field => {\n const fieldType = isDateTimeEntryMetaField(field) ? \"DateTime\" : \"CmsIdentity\";\n\n return `${field}: ${fieldType}`;\n }).join(\"\\n\");\n\n const plugin = createCmsGraphQLSchemaPlugin({\n // Had to remove /* GraphQL */ because it causes issues with oxfmt formatting.\n typeDefs: `\n type CmsModelMeta {\n modelId: String!\n name: String!\n }\n\n type CmsPublishedContentEntry {\n id: ID!\n entryId: String!\n title: String\n description: String\n image: String\n }\n\n type CmsContentEntry {\n id: ID!\n entryId: String!\n model: CmsModelMeta!\n status: String!\n published: CmsPublishedContentEntry\n title: String!\n description: String\n image: String\n \n ${onByMetaFields}\n \n wbyAco_location: WbyAcoLocation\n }\n\n type CmsContentEntriesResponse {\n data: [CmsContentEntry!]\n error: CmsError\n }\n\n type CmsContentEntryResponse {\n data: CmsContentEntry\n error: CmsError\n }\n\n input CmsModelEntryInput {\n modelId: ID!\n id: ID!\n }\n\n extend type Query {\n # Search content entries for given content models using the query string.\n searchContentEntries(\n modelIds: [ID!]!\n query: String\n fields: [String!]\n limit: Int\n ): CmsContentEntriesResponse!\n\n # Get content entry meta data\n getContentEntry(entry: CmsModelEntryInput!): CmsContentEntryResponse!\n\n getLatestContentEntry(entry: CmsModelEntryInput!): CmsContentEntryResponse!\n getPublishedContentEntry(entry: CmsModelEntryInput!): CmsContentEntryResponse!\n\n # Get content entries meta data\n getContentEntries(entries: [CmsModelEntryInput!]!): CmsContentEntriesResponse!\n getLatestContentEntries(entries: [CmsModelEntryInput!]!): CmsContentEntriesResponse!\n getPublishedContentEntries(\n entries: [CmsModelEntryInput!]!\n ): CmsContentEntriesResponse!\n }\n `,\n resolvers: {\n CmsContentEntry: {\n published: async (parent, _, context) => {\n try {\n const models = await context.cms.listModels();\n const model = models.find(({ modelId }) => {\n return parent.model.modelId === modelId;\n });\n if (!model) {\n return null;\n }\n const [entry] = await context.cms.getPublishedEntriesByIds(model, [\n parent.id\n ]);\n if (!entry) {\n return null;\n }\n return createCmsEntryRecord(model, entry);\n } catch {\n return null;\n }\n },\n description: createResolveDescription()\n },\n CmsPublishedContentEntry: {\n description: createResolveDescription()\n },\n Query: {\n async searchContentEntries(_, args: any, context) {\n const { modelIds, fields, query, limit = 10 } = args;\n const models = await context.cms.listModels();\n\n const getters = models\n .filter(model => modelIds.includes(model.modelId))\n .map(async model => {\n const listLatest =\n await context.container.resolve(ListLatestEntriesUseCase);\n const where: CmsEntryListWhere = {};\n\n const result = await listLatest.execute(model, {\n limit,\n where,\n search: !!query ? query : undefined,\n fields: fields || []\n });\n // TODO figure a better way to handle errors in parallel execution\n if (result.isFail()) {\n throw result.error;\n }\n const { entries } = result.value;\n return entries.map((entry: CmsEntry) => {\n return createCmsEntryRecord(model, entry);\n });\n });\n\n try {\n const entries = await Promise.all(getters).then(results =>\n results.reduce((result, item) => result.concat(item), [])\n );\n\n return new Response(\n entries\n .sort((a, b) => b.savedOn.getTime() - a.savedOn.getTime())\n .slice(0, limit)\n );\n } catch (ex) {\n return new ErrorResponse(ex);\n }\n },\n async getContentEntry(_, args: any, context) {\n return getContentEntry({\n args,\n context,\n type: \"exact\"\n });\n },\n async getLatestContentEntry(_, args: any, context) {\n return getContentEntry({\n args,\n context,\n type: \"latest\"\n });\n },\n async getPublishedContentEntry(_, args: any, context) {\n return getContentEntry({\n args,\n context,\n type: \"published\"\n });\n },\n async getContentEntries(_, args: any, context) {\n return getContentEntries({\n args,\n context,\n type: \"exact\"\n });\n },\n async getLatestContentEntries(_, args: any, context) {\n return getContentEntries({\n args,\n context,\n type: \"latest\"\n });\n },\n async getPublishedContentEntries(_, args: any, context) {\n return getContentEntries({\n args,\n context,\n type: \"published\"\n });\n }\n }\n }\n });\n\n plugin.name = `headless-cms.graphql.schema.${context.cms.type}.content-entries`;\n\n return plugin;\n};\n"],"names":["createDate","date","Date","createCmsEntryRecord","model","entry","getEntryTitle","getEntryDescription","getEntryImage","getFetchMethod","type","context","getContentEntriesMethods","WebinyError","methodName","getContentEntries","params","args","method","models","modelsMap","collection","argsEntries","entriesByModel","ref","getters","Object","modelId","Response","results","Promise","entries","items","item","Boolean","ex","ErrorResponse","getContentEntry","id","m","NotAuthorizedResponse","result","createResolveDescription","parent","_","field","f","value","entryFieldFromStorageTransform","createContentEntriesSchema","plugin","createCmsGraphQLSchemaPlugin","onByMetaFields","ENTRY_META_FIELDS","fieldType","isDateTimeEntryMetaField","modelIds","fields","query","limit","listLatest","ListLatestEntriesUseCase","where","undefined","a","b"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"graphql/schema/contentEntries.js","sources":["../../../src/graphql/schema/contentEntries.ts"],"sourcesContent":["import WebinyError from \"@webiny/error\";\nimport { ErrorResponse, Response } from \"@webiny/handler-graphql\";\nimport type {\n CmsContext,\n CmsEntry,\n CmsEntryListWhere,\n CmsIdentity,\n CmsModel\n} from \"~/types/index.js\";\nimport { getEntryTitle } from \"~/utils/getEntryTitle.js\";\nimport type { ICmsGraphQLSchemaPlugin } from \"~/plugins/index.js\";\nimport { createCmsGraphQLSchemaPlugin } from \"~/plugins/index.js\";\nimport { getEntryDescription } from \"~/utils/getEntryDescription.js\";\nimport { getEntryImage } from \"~/utils/getEntryImage.js\";\nimport { entryFieldFromStorageTransform } from \"~/utils/entryStorage.js\";\nimport type { GraphQLFieldResolver } from \"@webiny/handler-graphql/types.js\";\nimport { ENTRY_META_FIELDS, isDateTimeEntryMetaField } from \"~/constants.js\";\nimport NotAuthorizedResponse from \"@webiny/api-core/graphql/security/NotAuthorizedResponse.js\";\nimport { ListLatestEntriesUseCase } from \"~/features/contentEntry/ListEntries/index.js\";\n\ninterface EntriesByModel {\n [key: string]: string[];\n}\n\ntype GetContentEntryType = \"latest\" | \"published\" | \"exact\";\n\nconst createDate = (date: string | null): Date | null => {\n if (!date) {\n return null;\n }\n\n try {\n return new Date(date);\n } catch {\n return new Date();\n }\n};\n\n/**\n * How many matches per model `searchContentEntries` reads before ranking them by title.\n */\nconst SEARCH_CANDIDATES_LIMIT = 100;\n\n/**\n * Lower rank comes first: an exact title match, then a title starting with the query,\n * then a title containing it, then entries that matched on some other field.\n */\nconst getSearchRank = (title: string, query?: string): number => {\n const normalizedQuery = query?.trim().toLowerCase();\n if (!normalizedQuery) {\n return 0;\n }\n const normalizedTitle = (title || \"\").trim().toLowerCase();\n if (normalizedTitle === normalizedQuery) {\n return 0;\n } else if (normalizedTitle.startsWith(normalizedQuery)) {\n return 1;\n } else if (normalizedTitle.includes(normalizedQuery)) {\n return 2;\n }\n return 3;\n};\n\ninterface CmsEntryRecord {\n id: string;\n entryId: string;\n model: {\n modelId: string;\n name: string;\n };\n status: string;\n title: string;\n description?: string | null;\n image?: string | null;\n\n /**\n * Entry-level meta fields. 👇\n */\n createdOn: Date;\n modifiedOn: Date | null;\n savedOn: Date;\n createdBy: CmsIdentity;\n modifiedBy: CmsIdentity | null;\n savedBy: CmsIdentity;\n firstPublishedOn: Date | null;\n lastPublishedOn: Date | null;\n firstPublishedBy: CmsIdentity | null;\n lastPublishedBy: CmsIdentity | null;\n\n /**\n * Revision-level meta fields. 👇\n */\n revisionCreatedOn: Date;\n revisionModifiedOn: Date | null;\n revisionSavedOn: Date;\n revisionCreatedBy: CmsIdentity;\n revisionModifiedBy: CmsIdentity | null;\n revisionSavedBy: CmsIdentity;\n revisionFirstPublishedOn: Date | null;\n revisionLastPublishedOn: Date | null;\n revisionFirstPublishedBy: CmsIdentity | null;\n revisionLastPublishedBy: CmsIdentity | null;\n\n wbyAco_location?: {\n folderId?: string | null;\n };\n}\n\nconst createCmsEntryRecord = (model: CmsModel, entry: CmsEntry): CmsEntryRecord => {\n return {\n id: entry.id,\n entryId: entry.entryId,\n model: {\n modelId: model.modelId,\n name: model.name\n },\n status: entry.status,\n title: getEntryTitle(model, entry),\n description: getEntryDescription(model, entry),\n image: getEntryImage(model, entry),\n\n /**\n * Entry-level meta fields. 👇\n */\n createdOn: createDate(entry.createdOn)!,\n modifiedOn: createDate(entry.modifiedOn),\n savedOn: createDate(entry.savedOn)!,\n createdBy: entry.createdBy,\n savedBy: entry.savedBy,\n modifiedBy: entry.modifiedBy,\n firstPublishedOn: createDate(entry.firstPublishedOn),\n lastPublishedOn: createDate(entry.lastPublishedOn),\n firstPublishedBy: entry.firstPublishedBy,\n lastPublishedBy: entry.lastPublishedBy,\n\n /**\n * Revision-level meta fields. 👇\n */\n revisionCreatedOn: createDate(entry.revisionCreatedOn)!,\n revisionSavedOn: createDate(entry.revisionSavedOn)!,\n revisionModifiedOn: createDate(entry.revisionModifiedOn),\n revisionCreatedBy: entry.revisionCreatedBy,\n revisionModifiedBy: entry.revisionModifiedBy,\n revisionSavedBy: entry.revisionSavedBy,\n revisionFirstPublishedOn: createDate(entry.revisionFirstPublishedOn),\n revisionLastPublishedOn: createDate(entry.revisionLastPublishedOn),\n revisionFirstPublishedBy: entry.revisionFirstPublishedBy,\n revisionLastPublishedBy: entry.revisionLastPublishedBy,\n\n wbyAco_location: {\n folderId: entry.location?.folderId || null\n }\n };\n};\n\ninterface FetchMethod {\n (model: CmsModel, ids: string[]): Promise<CmsEntry[]>;\n}\n\nconst getFetchMethod = (type: GetContentEntryType, context: CmsContext): FetchMethod => {\n if (!getContentEntriesMethods[type]) {\n throw new WebinyError(\n `Unknown getContentEntries method \"${type}\". Could not fetch content entries.`,\n \"UNKNOWN_METHOD_ERROR\",\n {\n type\n }\n );\n }\n const methodName = getContentEntriesMethods[type] as GetContentEntryMethods;\n if (!context.cms[methodName]) {\n throw new WebinyError(\n `Unknown context.cms method \"${methodName}\". Could not fetch content entries.`,\n \"UNKNOWN_METHOD_ERROR\",\n {\n type,\n methodName\n }\n );\n }\n\n return context.cms[methodName];\n};\n\n/**\n * Function to get the list of content entries depending on latest, published or exact GraphQL queries.\n */\ninterface GetContentEntriesParams {\n args: {\n entries: Pick<CmsEntry, \"id\" | \"modelId\">[];\n };\n context: CmsContext;\n type: GetContentEntryType;\n}\n\nenum GetContentEntryMethods {\n getLatestEntriesByIds = \"getLatestEntriesByIds\",\n getPublishedEntriesByIds = \"getPublishedEntriesByIds\",\n getEntriesByIds = \"getEntriesByIds\"\n}\n\nconst getContentEntriesMethods = {\n latest: \"getLatestEntriesByIds\",\n published: \"getPublishedEntriesByIds\",\n exact: \"getEntriesByIds\"\n};\nconst getContentEntries = async (\n params: GetContentEntriesParams\n): Promise<Response | ErrorResponse> => {\n const { args, context, type } = params;\n\n const method = getFetchMethod(type, context);\n\n const models = await context.cms.listModels();\n\n const modelsMap = models.reduce(\n (collection, model) => {\n collection[model.modelId] = model;\n return collection;\n },\n {} as Record<string, CmsModel>\n );\n\n const argsEntries = args.entries as Pick<CmsEntry, \"id\" | \"modelId\">[];\n\n const entriesByModel = argsEntries.reduce((collection, ref) => {\n if (!collection[ref.modelId]) {\n collection[ref.modelId] = [];\n } else if (collection[ref.modelId].includes(ref.id)) {\n return collection;\n }\n collection[ref.modelId].push(ref.id);\n return collection;\n }, {} as EntriesByModel);\n\n const getters: Promise<CmsEntry[]>[] = Object.keys(entriesByModel).map(async modelId => {\n return method(modelsMap[modelId], entriesByModel[modelId]);\n });\n\n if (getters.length === 0) {\n return new Response([]);\n }\n\n try {\n const results = await Promise.all(getters);\n\n const entries = results\n .reduce<CmsEntryRecord[]>((collection, items) => {\n return collection.concat(\n items.map(item => {\n const model = modelsMap[item.modelId];\n\n return createCmsEntryRecord(model, item);\n })\n );\n }, [])\n .filter(Boolean);\n\n return new Response(entries);\n } catch (ex) {\n return new ErrorResponse(ex);\n }\n};\n\n/**\n * Function to fetch a single content entry depending on latest, published or exact GraphQL query.\n */\ninterface GetContentEntryParams {\n args: {\n entry: Pick<CmsEntry, \"id\" | \"modelId\">;\n };\n context: CmsContext;\n type: \"latest\" | \"published\" | \"exact\";\n}\n\nconst getContentEntry = async (\n params: GetContentEntryParams\n): Promise<Response<CmsEntryRecord | null> | NotAuthorizedResponse> => {\n const { args, context, type } = params;\n if (!getContentEntriesMethods[type]) {\n throw new WebinyError(\n `Unknown getContentEntry method \"${type}\". Could not fetch content entry.`,\n \"UNKNOWN_METHOD_ERROR\",\n {\n args,\n type\n }\n );\n }\n\n const method = getFetchMethod(type, context);\n\n const { modelId, id } = args.entry;\n const models = await context.cms.listModels();\n const model = models.find(m => m.modelId === modelId);\n\n if (!model) {\n return new NotAuthorizedResponse({\n data: {\n modelId\n }\n });\n }\n\n const result = await method(model, [id]);\n\n const entry = result.shift();\n if (!entry) {\n return new Response(null);\n }\n\n return new Response(createCmsEntryRecord(model, entry));\n};\n/**\n * As we support description field, we need to transform the value from storage.\n */\nconst createResolveDescription = (): GraphQLFieldResolver<any, any, CmsContext> => {\n return async (parent, _, context) => {\n const models = await context.cms.listModels();\n const model = models.find(({ modelId }) => {\n return parent.model.modelId === modelId;\n });\n if (!model) {\n return null;\n }\n const field = model.fields.find(f => f.fieldId === model.descriptionFieldId);\n if (!field) {\n return null;\n }\n const value = parent.description || parent[field.fieldId];\n if (!value) {\n return null;\n }\n return entryFieldFromStorageTransform({\n context,\n model,\n field,\n value\n });\n };\n};\n\ninterface Params {\n context: CmsContext;\n}\n\nexport const createContentEntriesSchema = ({\n context\n}: Params): ICmsGraphQLSchemaPlugin<CmsContext> => {\n if (!context.cms.MANAGE) {\n const plugin = createCmsGraphQLSchemaPlugin({\n typeDefs: \"\",\n resolvers: {}\n });\n plugin.name = `headless-cms.graphql.schema.${context.cms.type}.empty`;\n return plugin;\n }\n\n const onByMetaFields = ENTRY_META_FIELDS.map(field => {\n const fieldType = isDateTimeEntryMetaField(field) ? \"DateTime\" : \"CmsIdentity\";\n\n return `${field}: ${fieldType}`;\n }).join(\"\\n\");\n\n const plugin = createCmsGraphQLSchemaPlugin({\n // Had to remove /* GraphQL */ because it causes issues with oxfmt formatting.\n typeDefs: `\n type CmsModelMeta {\n modelId: String!\n name: String!\n }\n\n type CmsPublishedContentEntry {\n id: ID!\n entryId: String!\n title: String\n description: String\n image: String\n }\n\n type CmsContentEntry {\n id: ID!\n entryId: String!\n model: CmsModelMeta!\n status: String!\n published: CmsPublishedContentEntry\n title: String!\n description: String\n image: String\n \n ${onByMetaFields}\n \n wbyAco_location: WbyAcoLocation\n }\n\n type CmsContentEntriesResponse {\n data: [CmsContentEntry!]\n error: CmsError\n }\n\n type CmsContentEntryResponse {\n data: CmsContentEntry\n error: CmsError\n }\n\n input CmsModelEntryInput {\n modelId: ID!\n id: ID!\n }\n\n extend type Query {\n # Search content entries for given content models using the query string.\n searchContentEntries(\n modelIds: [ID!]!\n query: String\n fields: [String!]\n limit: Int\n ): CmsContentEntriesResponse!\n\n # Get content entry meta data\n getContentEntry(entry: CmsModelEntryInput!): CmsContentEntryResponse!\n\n getLatestContentEntry(entry: CmsModelEntryInput!): CmsContentEntryResponse!\n getPublishedContentEntry(entry: CmsModelEntryInput!): CmsContentEntryResponse!\n\n # Get content entries meta data\n getContentEntries(entries: [CmsModelEntryInput!]!): CmsContentEntriesResponse!\n getLatestContentEntries(entries: [CmsModelEntryInput!]!): CmsContentEntriesResponse!\n getPublishedContentEntries(\n entries: [CmsModelEntryInput!]!\n ): CmsContentEntriesResponse!\n }\n `,\n resolvers: {\n CmsContentEntry: {\n published: async (parent, _, context) => {\n try {\n const models = await context.cms.listModels();\n const model = models.find(({ modelId }) => {\n return parent.model.modelId === modelId;\n });\n if (!model) {\n return null;\n }\n const [entry] = await context.cms.getPublishedEntriesByIds(model, [\n parent.id\n ]);\n if (!entry) {\n return null;\n }\n return createCmsEntryRecord(model, entry);\n } catch {\n return null;\n }\n },\n description: createResolveDescription()\n },\n CmsPublishedContentEntry: {\n description: createResolveDescription()\n },\n Query: {\n async searchContentEntries(_, args: any, context) {\n const { modelIds, fields, query, limit = 10 } = args;\n const models = await context.cms.listModels();\n\n /**\n * Full-text search matches the query against every searchable field, and the storage\n * returns the newest-created matches first. When many entries mention the query somewhere\n * in their content, an older entry whose title is the query falls outside the first `limit`.\n * So when searching, read more candidates than requested, rank them by title, then cut.\n */\n const storageLimit = query ? Math.max(limit, SEARCH_CANDIDATES_LIMIT) : limit;\n\n const getters = models\n .filter(model => modelIds.includes(model.modelId))\n .map(async model => {\n const listLatest =\n await context.container.resolve(ListLatestEntriesUseCase);\n const where: CmsEntryListWhere = {};\n\n const result = await listLatest.execute(model, {\n limit: storageLimit,\n where,\n search: !!query ? query : undefined,\n fields: fields || []\n });\n // TODO figure a better way to handle errors in parallel execution\n if (result.isFail()) {\n throw result.error;\n }\n const { entries } = result.value;\n return entries.map((entry: CmsEntry) => {\n return createCmsEntryRecord(model, entry);\n });\n });\n\n try {\n const entries = await Promise.all(getters).then(results =>\n results.reduce((result, item) => result.concat(item), [])\n );\n\n return new Response(\n entries\n .sort((a, b) => {\n const rank =\n getSearchRank(a.title, query) -\n getSearchRank(b.title, query);\n if (rank !== 0) {\n return rank;\n }\n return b.savedOn.getTime() - a.savedOn.getTime();\n })\n .slice(0, limit)\n );\n } catch (ex) {\n return new ErrorResponse(ex);\n }\n },\n async getContentEntry(_, args: any, context) {\n return getContentEntry({\n args,\n context,\n type: \"exact\"\n });\n },\n async getLatestContentEntry(_, args: any, context) {\n return getContentEntry({\n args,\n context,\n type: \"latest\"\n });\n },\n async getPublishedContentEntry(_, args: any, context) {\n return getContentEntry({\n args,\n context,\n type: \"published\"\n });\n },\n async getContentEntries(_, args: any, context) {\n return getContentEntries({\n args,\n context,\n type: \"exact\"\n });\n },\n async getLatestContentEntries(_, args: any, context) {\n return getContentEntries({\n args,\n context,\n type: \"latest\"\n });\n },\n async getPublishedContentEntries(_, args: any, context) {\n return getContentEntries({\n args,\n context,\n type: \"published\"\n });\n }\n }\n }\n });\n\n plugin.name = `headless-cms.graphql.schema.${context.cms.type}.content-entries`;\n\n return plugin;\n};\n"],"names":["createDate","date","Date","SEARCH_CANDIDATES_LIMIT","getSearchRank","title","query","normalizedQuery","normalizedTitle","createCmsEntryRecord","model","entry","getEntryTitle","getEntryDescription","getEntryImage","getFetchMethod","type","context","getContentEntriesMethods","WebinyError","methodName","getContentEntries","params","args","method","models","modelsMap","collection","argsEntries","entriesByModel","ref","getters","Object","modelId","Response","results","Promise","entries","items","item","Boolean","ex","ErrorResponse","getContentEntry","id","m","NotAuthorizedResponse","result","createResolveDescription","parent","_","field","f","value","entryFieldFromStorageTransform","createContentEntriesSchema","plugin","createCmsGraphQLSchemaPlugin","onByMetaFields","ENTRY_META_FIELDS","fieldType","isDateTimeEntryMetaField","modelIds","fields","limit","storageLimit","Math","listLatest","ListLatestEntriesUseCase","where","undefined","a","b","rank"],"mappings":";;;;;;;;;;AA0BA,MAAMA,aAAa,CAACC;IAChB,IAAI,CAACA,MACD,OAAO;IAGX,IAAI;QACA,OAAO,IAAIC,KAAKD;IACpB,EAAE,OAAM;QACJ,OAAO,IAAIC;IACf;AACJ;AAKA,MAAMC,0BAA0B;AAMhC,MAAMC,gBAAgB,CAACC,OAAeC;IAClC,MAAMC,kBAAkBD,OAAO,OAAO;IACtC,IAAI,CAACC,iBACD,OAAO;IAEX,MAAMC,kBAAmBH,AAAAA,CAAAA,SAAS,EAAC,EAAG,IAAI,GAAG,WAAW;IACxD,IAAIG,oBAAoBD,iBACpB,OAAO;SACJ,IAAIC,gBAAgB,UAAU,CAACD,kBAClC,OAAO;SACJ,IAAIC,gBAAgB,QAAQ,CAACD,kBAChC,OAAO;IAEX,OAAO;AACX;AA+CA,MAAME,uBAAuB,CAACC,OAAiBC,QACpC;QACH,IAAIA,MAAM,EAAE;QACZ,SAASA,MAAM,OAAO;QACtB,OAAO;YACH,SAASD,MAAM,OAAO;YACtB,MAAMA,MAAM,IAAI;QACpB;QACA,QAAQC,MAAM,MAAM;QACpB,OAAOC,cAAcF,OAAOC;QAC5B,aAAaE,oBAAoBH,OAAOC;QACxC,OAAOG,cAAcJ,OAAOC;QAK5B,WAAWX,WAAWW,MAAM,SAAS;QACrC,YAAYX,WAAWW,MAAM,UAAU;QACvC,SAASX,WAAWW,MAAM,OAAO;QACjC,WAAWA,MAAM,SAAS;QAC1B,SAASA,MAAM,OAAO;QACtB,YAAYA,MAAM,UAAU;QAC5B,kBAAkBX,WAAWW,MAAM,gBAAgB;QACnD,iBAAiBX,WAAWW,MAAM,eAAe;QACjD,kBAAkBA,MAAM,gBAAgB;QACxC,iBAAiBA,MAAM,eAAe;QAKtC,mBAAmBX,WAAWW,MAAM,iBAAiB;QACrD,iBAAiBX,WAAWW,MAAM,eAAe;QACjD,oBAAoBX,WAAWW,MAAM,kBAAkB;QACvD,mBAAmBA,MAAM,iBAAiB;QAC1C,oBAAoBA,MAAM,kBAAkB;QAC5C,iBAAiBA,MAAM,eAAe;QACtC,0BAA0BX,WAAWW,MAAM,wBAAwB;QACnE,yBAAyBX,WAAWW,MAAM,uBAAuB;QACjE,0BAA0BA,MAAM,wBAAwB;QACxD,yBAAyBA,MAAM,uBAAuB;QAEtD,iBAAiB;YACb,UAAUA,MAAM,QAAQ,EAAE,YAAY;QAC1C;IACJ;AAOJ,MAAMI,iBAAiB,CAACC,MAA2BC;IAC/C,IAAI,CAACC,wBAAwB,CAACF,KAAK,EAC/B,MAAM,IAAIG,MACN,CAAC,kCAAkC,EAAEH,KAAK,mCAAmC,CAAC,EAC9E,wBACA;QACIA;IACJ;IAGR,MAAMI,aAAaF,wBAAwB,CAACF,KAAK;IACjD,IAAI,CAACC,QAAQ,GAAG,CAACG,WAAW,EACxB,MAAM,IAAID,MACN,CAAC,4BAA4B,EAAEC,WAAW,mCAAmC,CAAC,EAC9E,wBACA;QACIJ;QACAI;IACJ;IAIR,OAAOH,QAAQ,GAAG,CAACG,WAAW;AAClC;AAmBA,MAAMF,2BAA2B;IAC7B,QAAQ;IACR,WAAW;IACX,OAAO;AACX;AACA,MAAMG,oBAAoB,OACtBC;IAEA,MAAM,EAAEC,IAAI,EAAEN,OAAO,EAAED,IAAI,EAAE,GAAGM;IAEhC,MAAME,SAAST,eAAeC,MAAMC;IAEpC,MAAMQ,SAAS,MAAMR,QAAQ,GAAG,CAAC,UAAU;IAE3C,MAAMS,YAAYD,OAAO,MAAM,CAC3B,CAACE,YAAYjB;QACTiB,UAAU,CAACjB,MAAM,OAAO,CAAC,GAAGA;QAC5B,OAAOiB;IACX,GACA,CAAC;IAGL,MAAMC,cAAcL,KAAK,OAAO;IAEhC,MAAMM,iBAAiBD,YAAY,MAAM,CAAC,CAACD,YAAYG;QACnD,IAAKH,UAAU,CAACG,IAAI,OAAO,CAAC,EAErB;YAAA,IAAIH,UAAU,CAACG,IAAI,OAAO,CAAC,CAAC,QAAQ,CAACA,IAAI,EAAE,GAC9C,OAAOH;QACX,OAHIA,UAAU,CAACG,IAAI,OAAO,CAAC,GAAG,EAAE;QAIhCH,UAAU,CAACG,IAAI,OAAO,CAAC,CAAC,IAAI,CAACA,IAAI,EAAE;QACnC,OAAOH;IACX,GAAG,CAAC;IAEJ,MAAMI,UAAiCC,OAAO,IAAI,CAACH,gBAAgB,GAAG,CAAC,OAAMI,UAClET,OAAOE,SAAS,CAACO,QAAQ,EAAEJ,cAAc,CAACI,QAAQ;IAG7D,IAAIF,AAAmB,MAAnBA,QAAQ,MAAM,EACd,OAAO,IAAIG,SAAS,EAAE;IAG1B,IAAI;QACA,MAAMC,UAAU,MAAMC,QAAQ,GAAG,CAACL;QAElC,MAAMM,UAAUF,QACX,MAAM,CAAmB,CAACR,YAAYW,QAC5BX,WAAW,MAAM,CACpBW,MAAM,GAAG,CAACC,CAAAA;gBACN,MAAM7B,QAAQgB,SAAS,CAACa,KAAK,OAAO,CAAC;gBAErC,OAAO9B,qBAAqBC,OAAO6B;YACvC,KAEL,EAAE,EACJ,MAAM,CAACC;QAEZ,OAAO,IAAIN,SAASG;IACxB,EAAE,OAAOI,IAAI;QACT,OAAO,IAAIC,cAAcD;IAC7B;AACJ;AAaA,MAAME,kBAAkB,OACpBrB;IAEA,MAAM,EAAEC,IAAI,EAAEN,OAAO,EAAED,IAAI,EAAE,GAAGM;IAChC,IAAI,CAACJ,wBAAwB,CAACF,KAAK,EAC/B,MAAM,IAAIG,MACN,CAAC,gCAAgC,EAAEH,KAAK,iCAAiC,CAAC,EAC1E,wBACA;QACIO;QACAP;IACJ;IAIR,MAAMQ,SAAST,eAAeC,MAAMC;IAEpC,MAAM,EAAEgB,OAAO,EAAEW,EAAE,EAAE,GAAGrB,KAAK,KAAK;IAClC,MAAME,SAAS,MAAMR,QAAQ,GAAG,CAAC,UAAU;IAC3C,MAAMP,QAAQe,OAAO,IAAI,CAACoB,CAAAA,IAAKA,EAAE,OAAO,KAAKZ;IAE7C,IAAI,CAACvB,OACD,OAAO,IAAIoC,sBAAsB;QAC7B,MAAM;YACFb;QACJ;IACJ;IAGJ,MAAMc,SAAS,MAAMvB,OAAOd,OAAO;QAACkC;KAAG;IAEvC,MAAMjC,QAAQoC,OAAO,KAAK;IAC1B,IAAI,CAACpC,OACD,OAAO,IAAIuB,SAAS;IAGxB,OAAO,IAAIA,SAASzB,qBAAqBC,OAAOC;AACpD;AAIA,MAAMqC,2BAA2B,IACtB,OAAOC,QAAQC,GAAGjC;QACrB,MAAMQ,SAAS,MAAMR,QAAQ,GAAG,CAAC,UAAU;QAC3C,MAAMP,QAAQe,OAAO,IAAI,CAAC,CAAC,EAAEQ,OAAO,EAAE,GAC3BgB,OAAO,KAAK,CAAC,OAAO,KAAKhB;QAEpC,IAAI,CAACvB,OACD,OAAO;QAEX,MAAMyC,QAAQzC,MAAM,MAAM,CAAC,IAAI,CAAC0C,CAAAA,IAAKA,EAAE,OAAO,KAAK1C,MAAM,kBAAkB;QAC3E,IAAI,CAACyC,OACD,OAAO;QAEX,MAAME,QAAQJ,OAAO,WAAW,IAAIA,MAAM,CAACE,MAAM,OAAO,CAAC;QACzD,IAAI,CAACE,OACD,OAAO;QAEX,OAAOC,+BAA+B;YAClCrC;YACAP;YACAyC;YACAE;QACJ;IACJ;AAOG,MAAME,6BAA6B,CAAC,EACvCtC,OAAO,EACF;IACL,IAAI,CAACA,QAAQ,GAAG,CAAC,MAAM,EAAE;QACrB,MAAMuC,SAASC,6BAA6B;YACxC,UAAU;YACV,WAAW,CAAC;QAChB;QACAD,OAAO,IAAI,GAAG,CAAC,4BAA4B,EAAEvC,QAAQ,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC;QACrE,OAAOuC;IACX;IAEA,MAAME,iBAAiBC,kBAAkB,GAAG,CAACR,CAAAA;QACzC,MAAMS,YAAYC,yBAAyBV,SAAS,aAAa;QAEjE,OAAO,GAAGA,MAAM,EAAE,EAAES,WAAW;IACnC,GAAG,IAAI,CAAC;IAER,MAAMJ,SAASC,6BAA6B;QAExC,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;gBAwBH,EAAEC,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA0CzB,CAAC;QACD,WAAW;YACP,iBAAiB;gBACb,WAAW,OAAOT,QAAQC,GAAGjC;oBACzB,IAAI;wBACA,MAAMQ,SAAS,MAAMR,QAAQ,GAAG,CAAC,UAAU;wBAC3C,MAAMP,QAAQe,OAAO,IAAI,CAAC,CAAC,EAAEQ,OAAO,EAAE,GAC3BgB,OAAO,KAAK,CAAC,OAAO,KAAKhB;wBAEpC,IAAI,CAACvB,OACD,OAAO;wBAEX,MAAM,CAACC,MAAM,GAAG,MAAMM,QAAQ,GAAG,CAAC,wBAAwB,CAACP,OAAO;4BAC9DuC,OAAO,EAAE;yBACZ;wBACD,IAAI,CAACtC,OACD,OAAO;wBAEX,OAAOF,qBAAqBC,OAAOC;oBACvC,EAAE,OAAM;wBACJ,OAAO;oBACX;gBACJ;gBACA,aAAaqC;YACjB;YACA,0BAA0B;gBACtB,aAAaA;YACjB;YACA,OAAO;gBACH,MAAM,sBAAqBE,CAAC,EAAE3B,IAAS,EAAEN,OAAO;oBAC5C,MAAM,EAAE6C,QAAQ,EAAEC,MAAM,EAAEzD,KAAK,EAAE0D,QAAQ,EAAE,EAAE,GAAGzC;oBAChD,MAAME,SAAS,MAAMR,QAAQ,GAAG,CAAC,UAAU;oBAQ3C,MAAMgD,eAAe3D,QAAQ4D,KAAK,GAAG,CAACF,OAAO7D,2BAA2B6D;oBAExE,MAAMjC,UAAUN,OACX,MAAM,CAACf,CAAAA,QAASoD,SAAS,QAAQ,CAACpD,MAAM,OAAO,GAC/C,GAAG,CAAC,OAAMA;wBACP,MAAMyD,aACF,MAAMlD,QAAQ,SAAS,CAAC,OAAO,CAACmD;wBACpC,MAAMC,QAA2B,CAAC;wBAElC,MAAMtB,SAAS,MAAMoB,WAAW,OAAO,CAACzD,OAAO;4BAC3C,OAAOuD;4BACPI;4BACA,QAAQ,AAAE/D,QAAQA,QAAQgE;4BAC1B,QAAQP,UAAU,EAAE;wBACxB;wBAEA,IAAIhB,OAAO,MAAM,IACb,MAAMA,OAAO,KAAK;wBAEtB,MAAM,EAAEV,OAAO,EAAE,GAAGU,OAAO,KAAK;wBAChC,OAAOV,QAAQ,GAAG,CAAC,CAAC1B,QACTF,qBAAqBC,OAAOC;oBAE3C;oBAEJ,IAAI;wBACA,MAAM0B,UAAU,MAAMD,QAAQ,GAAG,CAACL,SAAS,IAAI,CAACI,CAAAA,UAC5CA,QAAQ,MAAM,CAAC,CAACY,QAAQR,OAASQ,OAAO,MAAM,CAACR,OAAO,EAAE;wBAG5D,OAAO,IAAIL,SACPG,QACK,IAAI,CAAC,CAACkC,GAAGC;4BACN,MAAMC,OACFrE,cAAcmE,EAAE,KAAK,EAAEjE,SACvBF,cAAcoE,EAAE,KAAK,EAAElE;4BAC3B,IAAImE,AAAS,MAATA,MACA,OAAOA;4BAEX,OAAOD,EAAE,OAAO,CAAC,OAAO,KAAKD,EAAE,OAAO,CAAC,OAAO;wBAClD,GACC,KAAK,CAAC,GAAGP;oBAEtB,EAAE,OAAOvB,IAAI;wBACT,OAAO,IAAIC,cAAcD;oBAC7B;gBACJ;gBACA,MAAM,iBAAgBS,CAAC,EAAE3B,IAAS,EAAEN,OAAO;oBACvC,OAAO0B,gBAAgB;wBACnBpB;wBACAN;wBACA,MAAM;oBACV;gBACJ;gBACA,MAAM,uBAAsBiC,CAAC,EAAE3B,IAAS,EAAEN,OAAO;oBAC7C,OAAO0B,gBAAgB;wBACnBpB;wBACAN;wBACA,MAAM;oBACV;gBACJ;gBACA,MAAM,0BAAyBiC,CAAC,EAAE3B,IAAS,EAAEN,OAAO;oBAChD,OAAO0B,gBAAgB;wBACnBpB;wBACAN;wBACA,MAAM;oBACV;gBACJ;gBACA,MAAM,mBAAkBiC,CAAC,EAAE3B,IAAS,EAAEN,OAAO;oBACzC,OAAOI,kBAAkB;wBACrBE;wBACAN;wBACA,MAAM;oBACV;gBACJ;gBACA,MAAM,yBAAwBiC,CAAC,EAAE3B,IAAS,EAAEN,OAAO;oBAC/C,OAAOI,kBAAkB;wBACrBE;wBACAN;wBACA,MAAM;oBACV;gBACJ;gBACA,MAAM,4BAA2BiC,CAAC,EAAE3B,IAAS,EAAEN,OAAO;oBAClD,OAAOI,kBAAkB;wBACrBE;wBACAN;wBACA,MAAM;oBACV;gBACJ;YACJ;QACJ;IACJ;IAEAuC,OAAO,IAAI,GAAG,CAAC,4BAA4B,EAAEvC,QAAQ,GAAG,CAAC,IAAI,CAAC,gBAAgB,CAAC;IAE/E,OAAOuC;AACX"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webiny/api-headless-cms",
|
|
3
|
-
"version": "6.5.
|
|
3
|
+
"version": "6.5.1-beta.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./index.js",
|
|
@@ -23,44 +23,44 @@
|
|
|
23
23
|
"license": "MIT",
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@babel/code-frame": "8.0.6",
|
|
26
|
-
"@graphql-tools/merge": "9.2.
|
|
27
|
-
"@graphql-tools/schema": "10.1.
|
|
28
|
-
"@webiny/api": "6.5.
|
|
29
|
-
"@webiny/api-core": "6.5.
|
|
26
|
+
"@graphql-tools/merge": "9.2.8",
|
|
27
|
+
"@graphql-tools/schema": "10.1.5",
|
|
28
|
+
"@webiny/api": "6.5.1-beta.0",
|
|
29
|
+
"@webiny/api-core": "6.5.1-beta.0",
|
|
30
30
|
"@webiny/di": "1.1.0",
|
|
31
|
-
"@webiny/error": "6.5.
|
|
32
|
-
"@webiny/feature": "6.5.
|
|
33
|
-
"@webiny/handler": "6.5.
|
|
34
|
-
"@webiny/handler-aws": "6.5.
|
|
35
|
-
"@webiny/handler-db": "6.5.
|
|
36
|
-
"@webiny/handler-graphql": "6.5.
|
|
37
|
-
"@webiny/plugins": "6.5.
|
|
38
|
-
"@webiny/project": "6.5.
|
|
39
|
-
"@webiny/utils": "6.5.
|
|
40
|
-
"@webiny/validation": "6.5.
|
|
31
|
+
"@webiny/error": "6.5.1-beta.0",
|
|
32
|
+
"@webiny/feature": "6.5.1-beta.0",
|
|
33
|
+
"@webiny/handler": "6.5.1-beta.0",
|
|
34
|
+
"@webiny/handler-aws": "6.5.1-beta.0",
|
|
35
|
+
"@webiny/handler-db": "6.5.1-beta.0",
|
|
36
|
+
"@webiny/handler-graphql": "6.5.1-beta.0",
|
|
37
|
+
"@webiny/plugins": "6.5.1-beta.0",
|
|
38
|
+
"@webiny/project": "6.5.1-beta.0",
|
|
39
|
+
"@webiny/utils": "6.5.1-beta.0",
|
|
40
|
+
"@webiny/validation": "6.5.1-beta.0",
|
|
41
41
|
"dot-prop-immutable": "2.1.1",
|
|
42
42
|
"graphql": "16.14.2",
|
|
43
43
|
"graphql-tag": "2.12.7",
|
|
44
44
|
"lodash": "4.18.1",
|
|
45
|
-
"p-map": "7.
|
|
45
|
+
"p-map": "7.1.1",
|
|
46
46
|
"p-reduce": "3.0.0",
|
|
47
47
|
"pluralize": "8.0.0",
|
|
48
48
|
"slugify": "1.6.9",
|
|
49
|
-
"zod": "4.6.
|
|
49
|
+
"zod": "4.6.7"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@types/pluralize": "0.0.33",
|
|
53
|
-
"@webiny/build-tools": "6.5.
|
|
54
|
-
"@webiny/handler-db": "6.5.
|
|
55
|
-
"@webiny/project-utils": "6.5.
|
|
56
|
-
"@webiny/sdk": "6.5.
|
|
57
|
-
"@webiny/wcp": "6.5.
|
|
53
|
+
"@webiny/build-tools": "6.5.1-beta.0",
|
|
54
|
+
"@webiny/handler-db": "6.5.1-beta.0",
|
|
55
|
+
"@webiny/project-utils": "6.5.1-beta.0",
|
|
56
|
+
"@webiny/sdk": "6.5.1-beta.0",
|
|
57
|
+
"@webiny/wcp": "6.5.1-beta.0",
|
|
58
58
|
"apollo-graphql": "0.9.7",
|
|
59
59
|
"graphql": "16.14.2",
|
|
60
|
-
"oxfmt": "0.
|
|
60
|
+
"oxfmt": "0.73.0",
|
|
61
61
|
"rimraf": "6.1.3",
|
|
62
62
|
"typescript": "7.0.2",
|
|
63
|
-
"vitest": "5.0.
|
|
63
|
+
"vitest": "5.0.5",
|
|
64
64
|
"write-json-file": "7.0.0"
|
|
65
65
|
},
|
|
66
66
|
"publishConfig": {
|