diff --git a/docs/docs/guides/developer-guide/custom-fields/index.md b/docs/docs/guides/developer-guide/custom-fields/index.md
index 10589226a2..7afa720de4 100644
--- a/docs/docs/guides/developer-guide/custom-fields/index.md
+++ b/docs/docs/guides/developer-guide/custom-fields/index.md
@@ -515,7 +515,7 @@ const config = {
#### validate
-
+
A custom validation function. If the value is valid, then the function should not return a value. If a string or LocalizedString array is returned, this is interpreted as an error message.
@@ -563,7 +563,7 @@ const config = {
name: 'partCode',
type: 'string',
// highlight-start
- validate: async (value, injector) => {
+ validate: async (value, injector, ctx) => {
const partCodeService = injector.get(PartCodeService);
const isValid = await partCodeService.validateCode(value);
if (!isValid) {
diff --git a/packages/core/src/api/common/validate-custom-field-value.spec.ts b/packages/core/src/api/common/validate-custom-field-value.spec.ts
index 49a1a51be1..d95fffb715 100644
--- a/packages/core/src/api/common/validate-custom-field-value.spec.ts
+++ b/packages/core/src/api/common/validate-custom-field-value.spec.ts
@@ -3,13 +3,14 @@ import { fail } from 'assert';
import { describe, expect, it } from 'vitest';
import { Injector } from '../../common/injector';
+import { RequestContext } from './request-context';
import { validateCustomFieldValue } from './validate-custom-field-value';
describe('validateCustomFieldValue()', () => {
const injector = new Injector({} as any);
- async function assertThrowsError(validateFn: () => Promise, message: string) {
+ async function assertThrowsError(validateFn: (() => Promise) | (() => void), message: string) {
try {
await validateFn();
fail('Should have thrown');
@@ -18,6 +19,8 @@ describe('validateCustomFieldValue()', () => {
}
}
+ const ctx = RequestContext.empty();
+
describe('string & localeString', () => {
const validate = (value: string) => () =>
validateCustomFieldValue(
@@ -28,6 +31,7 @@ describe('validateCustomFieldValue()', () => {
},
value,
injector,
+ ctx,
);
it('passes valid pattern', async () => {
@@ -53,6 +57,7 @@ describe('validateCustomFieldValue()', () => {
},
value,
injector,
+ ctx,
);
it('passes valid option', async () => {
@@ -78,6 +83,7 @@ describe('validateCustomFieldValue()', () => {
},
value,
injector,
+ ctx,
);
it('passes valid range', async () => {
@@ -104,6 +110,7 @@ describe('validateCustomFieldValue()', () => {
},
value,
injector,
+ ctx,
);
it('passes valid range', async () => {
@@ -138,9 +145,14 @@ describe('validateCustomFieldValue()', () => {
},
value,
injector,
+ ctx,
);
- const validate2 = (value: string, languageCode: LanguageCode) => () =>
- validateCustomFieldValue(
+ const validate2 = (value: string, languageCode: LanguageCode) => () => {
+ const ctxWithLanguage = new RequestContext({
+ languageCode,
+ apiType: 'admin',
+ } as any);
+ return validateCustomFieldValue(
{
name: 'test',
type: 'string',
@@ -155,8 +167,9 @@ describe('validateCustomFieldValue()', () => {
},
value,
injector,
- languageCode,
+ ctxWithLanguage,
);
+ };
it('passes validate fn string', async () => {
expect(validate1('valid')).not.toThrow();
@@ -192,6 +205,7 @@ describe('validateCustomFieldValue()', () => {
},
value,
injector,
+ ctx,
);
expect(validate([1, 2, 6])).not.toThrow();
@@ -209,6 +223,7 @@ describe('validateCustomFieldValue()', () => {
},
value,
injector,
+ ctx,
);
expect(validate(['small', 'large'])).not.toThrow();
@@ -230,6 +245,7 @@ describe('validateCustomFieldValue()', () => {
},
value,
injector,
+ ctx,
);
expect(validate(['valid', 'valid'])).not.toThrow();
diff --git a/packages/core/src/api/common/validate-custom-field-value.ts b/packages/core/src/api/common/validate-custom-field-value.ts
index 64417691de..b04b7a3518 100644
--- a/packages/core/src/api/common/validate-custom-field-value.ts
+++ b/packages/core/src/api/common/validate-custom-field-value.ts
@@ -12,6 +12,7 @@ import {
StringCustomFieldConfig,
TypedCustomFieldConfig,
} from '../../config/custom-field/custom-field-types';
+import { RequestContext } from './request-context';
/**
* Validates the value of a custom field input against any configured constraints.
@@ -21,7 +22,7 @@ export async function validateCustomFieldValue(
config: CustomFieldConfig,
value: any | any[],
injector: Injector,
- languageCode?: LanguageCode,
+ ctx: RequestContext,
): Promise {
if (config.readonly) {
throw new UserInputError('error.field-invalid-readonly', { name: config.name });
@@ -40,7 +41,7 @@ export async function validateCustomFieldValue(
} else {
validateSingleValue(config, value);
}
- await validateCustomFunction(config as TypedCustomFieldConfig, value, injector, languageCode);
+ await validateCustomFunction(config as TypedCustomFieldConfig, value, injector, ctx);
}
function validateSingleValue(config: CustomFieldConfig, value: any) {
@@ -70,15 +71,15 @@ async function validateCustomFunction
config: T,
value: any,
injector: Injector,
- languageCode?: LanguageCode,
+ ctx: RequestContext,
) {
if (typeof config.validate === 'function') {
- const error = await config.validate(value, injector);
+ const error = await config.validate(value, injector, ctx);
if (typeof error === 'string') {
throw new UserInputError(error);
}
if (Array.isArray(error)) {
- const localizedError = error.find(e => e.languageCode === languageCode) || error[0];
+ const localizedError = error.find(e => e.languageCode === ctx.languageCode) || error[0];
throw new UserInputError(localizedError.value);
}
}
diff --git a/packages/core/src/api/config/get-custom-fields-config-without-interfaces.ts b/packages/core/src/api/config/get-custom-fields-config-without-interfaces.ts
index d41e131f3d..42a343e838 100644
--- a/packages/core/src/api/config/get-custom-fields-config-without-interfaces.ts
+++ b/packages/core/src/api/config/get-custom-fields-config-without-interfaces.ts
@@ -1,6 +1,6 @@
import { GraphQLSchema, isInterfaceType } from 'graphql';
-import { CustomFields, CustomFieldConfig } from '../../config/custom-field/custom-field-types';
+import { CustomFieldConfig, CustomFields } from '../../config/custom-field/custom-field-types';
/**
* @description
@@ -23,7 +23,7 @@ export function getCustomFieldsConfigWithoutInterfaces(
entries.splice(regionIndex, 1);
for (const implementation of implementations.objects) {
- entries.push([implementation.name, customFieldConfig.Region]);
+ entries.push([implementation.name, customFieldConfig.Region ?? []]);
}
}
}
diff --git a/packages/core/src/api/middleware/validate-custom-fields-interceptor.ts b/packages/core/src/api/middleware/validate-custom-fields-interceptor.ts
index 7779fb6c2f..5ff534e47f 100644
--- a/packages/core/src/api/middleware/validate-custom-fields-interceptor.ts
+++ b/packages/core/src/api/middleware/validate-custom-fields-interceptor.ts
@@ -59,7 +59,7 @@ export class ValidateCustomFieldsInterceptor implements NestInterceptor {
: [variables[inputName]];
for (const inputVariable of inputVariables) {
- await this.validateInput(typeName, ctx.languageCode, injector, inputVariable);
+ await this.validateInput(typeName, ctx, injector, inputVariable);
}
}
}
@@ -71,7 +71,7 @@ export class ValidateCustomFieldsInterceptor implements NestInterceptor {
private async validateInput(
typeName: string,
- languageCode: LanguageCode,
+ ctx: RequestContext,
injector: Injector,
variableValues?: { [key: string]: any },
) {
@@ -83,7 +83,7 @@ export class ValidateCustomFieldsInterceptor implements NestInterceptor {
// mutations.
await this.validateCustomFieldsObject(
this.configService.customFields.OrderLine,
- languageCode,
+ ctx,
variableValues,
injector,
);
@@ -91,7 +91,7 @@ export class ValidateCustomFieldsInterceptor implements NestInterceptor {
if (variableValues.customFields) {
await this.validateCustomFieldsObject(
customFieldConfig,
- languageCode,
+ ctx,
variableValues.customFields,
injector,
);
@@ -102,7 +102,7 @@ export class ValidateCustomFieldsInterceptor implements NestInterceptor {
if (translation.customFields) {
await this.validateCustomFieldsObject(
customFieldConfig,
- languageCode,
+ ctx,
translation.customFields,
injector,
);
@@ -114,14 +114,14 @@ export class ValidateCustomFieldsInterceptor implements NestInterceptor {
private async validateCustomFieldsObject(
customFieldConfig: CustomFieldConfig[],
- languageCode: LanguageCode,
+ ctx: RequestContext,
customFieldsObject: { [key: string]: any },
injector: Injector,
) {
for (const [key, value] of Object.entries(customFieldsObject)) {
const config = customFieldConfig.find(c => getGraphQlInputName(c) === key);
if (config) {
- await validateCustomFieldValue(config, value, injector, languageCode);
+ await validateCustomFieldValue(config, value, injector, ctx);
}
}
}
diff --git a/packages/core/src/config/custom-field/custom-field-types.ts b/packages/core/src/config/custom-field/custom-field-types.ts
index 3aa6c2edc4..0f52100df8 100644
--- a/packages/core/src/config/custom-field/custom-field-types.ts
+++ b/packages/core/src/config/custom-field/custom-field-types.ts
@@ -19,6 +19,7 @@ import {
UiComponentConfig,
} from '@vendure/common/lib/shared-types';
+import { RequestContext } from '../../api/index';
import { Injector } from '../../common/injector';
import { VendureEntity } from '../../entity/base/base.entity';
@@ -61,6 +62,7 @@ export type TypedCustomSingleFieldConfig<
validate?: (
value: DefaultValueType,
injector: Injector,
+ ctx: RequestContext,
) => string | LocalizedString[] | void | Promise;
};
@@ -172,7 +174,7 @@ export type CustomFields = {
TaxRate?: CustomFieldConfig[];
User?: CustomFieldConfig[];
Zone?: CustomFieldConfig[];
-} & { [entity: string]: CustomFieldConfig[] | undefined };
+} & { [entity: string]: CustomFieldConfig[] };
/**
* This interface should be implemented by any entity which can be extended