Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions web/ui/react-app/src/modals/service-edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ const ServiceEditModalWithData: FC<ServiceEditModalWithDataProps> = ({
schemaData,
schemaDataDefaults,
mainDataDefaults,
typeDataDefaults,
serviceID: sID,
} = useSchemaContext();

Expand Down Expand Up @@ -180,6 +181,8 @@ const ServiceEditModalWithData: FC<ServiceEditModalWithDataProps> = ({
const dataPayload = mapServiceToAPIRequest(
dataParsed.data,
schemaDataDefaults,
mainDataDefaults,
typeDataDefaults,
);

await mutateAsync({ data: dataPayload, serviceID: serviceID ?? null })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,32 @@ import {
type NotifiersSchemaOutgoing,
type NotifySchemaOutgoing,
type NotifySchemaValues,
notifySchemaMapOutgoing,
type NotifyTypeSchema,
notifySchemaMapOutgoingWithDefaults,
} from '@/utils/api/types/config-edit/notify/schemas';
import { applyDefaultsRecursive } from '@/utils/api/types/config-edit/util';
import diffLists from '@/utils/diff-lists';

/**
* Converts a `NotifiersSchema` object to a `NotifiersSchemaOutgoing` object.
*
* @param data - The `NotifiersSchema` data to map.
* @param defaultValue - The default values to compare against (and omit if all defaults used and unmodified).
* @param mainDefaults - The 'notify' globals.
* @param typeDefaults - Type-specific notify form data.
* @returns A `NotifiersSchemaOutgoing` representing the `NotifiersSchema`.
*/
export const mapNotifiersSchemaToAPIPayload = (
data: NotifiersSchema,
defaultValue?: NotifiersSchema,
mainDefaults?: Record<string, NotifySchemaValues>,
typeDefaults?: NotifyTypeSchema,
): NotifiersSchemaOutgoing => {
const dataMinimised = data.map((item, idx) => {
const defaultsForItem = defaultValue?.[idx];
const dataMinimised = data.map((item) => {
const defaultsForItem = applyDefaultsRecursive(
mainDefaults?.[item.name] ?? null,
typeDefaults?.[item.type],
);
const d = mapNotifySchemaToAPIPayload(item, defaultsForItem);
return removeEmptyValues(d) as NotifySchemaOutgoing;
});
Expand Down Expand Up @@ -58,13 +66,10 @@ export const mapNotifySchemaToAPIPayload = (
defaults?: NotifySchemaValues,
): NotifySchemaOutgoing => {
const itemType = item.type;
if (defaults?.type === itemType) {
const schema = notifySchemaMapOutgoingWithDefaults(defaults);
return removeEmptyValues(
schema.parse(item) as NotifySchemaOutgoing,
) as NotifySchemaOutgoing;
}
const defaultsTyped = defaults ?? ({ type: itemType } as NotifySchemaValues);
const schema = notifySchemaMapOutgoingWithDefaults(defaultsTyped);

return removeEmptyValues(
notifySchemaMapOutgoing[itemType].parse(item),
schema.parse(item) as NotifySchemaOutgoing,
) as NotifySchemaOutgoing;
};
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ import {
SMTPEncryptionEnum,
} from '@/utils/api/types/config-edit/notify/types/smtp';
import { TelegramParseModeEnum } from '@/utils/api/types/config-edit/notify/types/telegram';
import { preprocessStringFromHeaderArrayWithDefaults } from '@/utils/api/types/config-edit/shared/header/preprocess';
import { headersSchemaDefaults } from '@/utils/api/types/config-edit/shared/header/schemas';
import {
headersSchemaDefaults,
preprocessStringFromHeaderArrayWithDefaults,
} from '@/utils/api/types/config-edit/shared/header/preprocess';
import { nullString } from '@/utils/api/types/config-edit/shared/null-string'; /* Notify 'Options' Schema */
import {
preprocessBooleanFromString,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import {
ntfyPriorityOptions,
ntfySchemeOptions,
} from '@/utils/api/types/config/notify/ntfy';
import { flattenHeaderArray } from '@/utils/api/types/config-edit/shared/header/preprocess';
import { headersSchemaDefaults } from '@/utils/api/types/config-edit/shared/header/schemas';
import {
flattenHeaderArray,
headersSchemaDefaults,
} from '@/utils/api/types/config-edit/shared/header/preprocess';
import {
makeDefaultsAwareListPreprocessor,
preprocessArrayJSONFromString,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { removeEmptyValues } from '@/utils';
import { DEPLOYED_VERSION_LOOKUP_TYPE } from '@/utils/api/types/config/service/deployed-version';
import { mapCommandsSchemaToAPIPayload } from '@/utils/api/types/config-edit/command/api/conversions';
import { mapNotifiersSchemaToAPIPayload } from '@/utils/api/types/config-edit/notify/api/conversions';
import type { buildServiceSchemaWithFallbacks } from '@/utils/api/types/config-edit/service/form/builder';
import type {
ServiceSchema,
ServiceSchemaDefault,
Expand All @@ -14,10 +15,18 @@ import { mapWebHooksSchemaToAPIPayload } from '@/utils/api/types/config-edit/web
*
* @param data - The `ServiceSchema` data to map.
* @param defaults - The default values to compare against (and omit where all defaults used and unmodified).
* @param mainDefaults - The notify/webhook globals.
* @param typeDefaults - Type-specific notify/webhook form data.
*/
export const mapServiceToAPIRequest = (
data: ServiceSchema,
defaults: ServiceSchemaDefault | null,
mainDefaults: ReturnType<
typeof buildServiceSchemaWithFallbacks
>['mainDataDefaults'],
typeDefaults: ReturnType<
typeof buildServiceSchemaWithFallbacks
>['typeDataDefaults'],
): ServiceSchemaOutgoing => {
const dv = data.deployed_version;
let deployedVersion = null;
Expand All @@ -34,7 +43,17 @@ export const mapServiceToAPIRequest = (
command: mapCommandsSchemaToAPIPayload(data.command, defaults?.command),
deployed_version: deployedVersion,
id_name_separator: null,
notify: mapNotifiersSchemaToAPIPayload(data.notify, defaults?.notify),
webhook: mapWebHooksSchemaToAPIPayload(data.webhook, defaults?.webhook),
notify: mapNotifiersSchemaToAPIPayload(
data.notify,
defaults?.notify,
mainDefaults?.notify,
typeDefaults?.notify,
),
webhook: mapWebHooksSchemaToAPIPayload(
data.webhook,
defaults?.webhook,
mainDefaults?.webhook,
typeDefaults?.webhook,
),
}) as ServiceSchemaOutgoing;
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
type DeployedVersionLookupURLMethod,
deployedVersionLookupTypeOptions,
} from '@/utils/api/types/config/service/deployed-version';
import { headersSchemaDefaults } from '@/utils/api/types/config-edit/shared/header/schemas';
import { headersSchemaDefaults } from '@/utils/api/types/config-edit/shared/header/preprocess';
import { nullString } from '@/utils/api/types/config-edit/shared/null-string';
import { regexStringWithFallback } from '@/utils/api/types/config-edit/validators';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { addZodIssuesToContext } from '@/utils/api/types/config-edit/shared/add-
import {
headersSchema,
headersSchemaDefaults,
} from '@/utils/api/types/config-edit/shared/header/schemas';
} from '@/utils/api/types/config-edit/shared/header/preprocess';
import { overrideSchemaDefault } from '@/utils/api/types/config-edit/shared/override-schema-default';
import { safeParse } from '@/utils/api/types/config-edit/shared/safeparse';
import type { BuilderResponse } from '@/utils/api/types/config-edit/shared/types';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import {
z,
} from 'zod';
import type { CustomHeaders } from '@/utils/api/types/config/shared';
import {
headerSchema,
headerSchemaDefaults,
} from '@/utils/api/types/config-edit/shared/header/schemas';
import { makeDefaultsAwareListPreprocessor } from '@/utils/api/types/config-edit/shared/preprocess';

/**
Expand Down Expand Up @@ -96,3 +100,15 @@ export const preprocessToHeadersArray = <T extends z.ZodType>(
return arg;
}, z.array(schema))
.default([]);

/* Array of Header objects (min length 1 on key and value) */
export const headersSchema = preprocessToHeadersArray(headerSchema);
/* Array of Header objects (no validation) */
export const headersSchemaDefaults =
preprocessToHeadersArray(headerSchemaDefaults);

export const preprocessHeaderArrayWithDefaults = (defaults?: CustomHeaders) =>
makeDefaultsAwareListPreprocessor(headersSchemaDefaults.nullable(), {
defaults: defaults,
matchingFields: ['key', 'value'],
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { z } from 'zod';
import { preprocessToHeadersArray } from '@/utils/api/types/config-edit/shared/header/preprocess';
import { REQUIRED_MESSAGE } from '@/utils/api/types/config-edit/validators';

/* Header object (min length 1 on key and value) */
Expand All @@ -14,9 +13,3 @@ export const headerSchemaDefaults = z.object({
old_index: z.number().nullable().default(null),
value: z.string(),
});

/* Array of Header objects (min length 1 on key and value) */
export const headersSchema = preprocessToHeadersArray(headerSchema);
/* Array of Header objects (no validation) */
export const headersSchemaDefaults =
preprocessToHeadersArray(headerSchemaDefaults);
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { type ZodEnum, z } from 'zod';
import { nullString } from '@/utils/api/types/config-edit/shared/null-string';
import { isUsingDefaults } from '@/utils/api/types/config-edit/validators';

/**
Expand Down Expand Up @@ -84,7 +85,7 @@ export const preprocessStringFromNumber = z.preprocess((arg) => {
*/
export const preprocessStringFromZodEnum = (enumSchema: ZodEnum) =>
z.preprocess((val: unknown) => {
if (val == null) return undefined;
if (val == null || val === nullString) return undefined;

const enumValues = enumSchema.options;
if (enumValues.includes(val as string)) return val;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,35 @@
import { removeEmptyValues } from '@/utils';
import { applyDefaultsRecursive } from '@/utils/api/types/config-edit/util';
import {
type WebHookSchema,
type WebHookSchemaOutgoing,
type WebHooksSchema,
type WebHooksSchemaOutgoing,
webhookSchemaOutgoing,
webhookSchemaMapOutgoingWithDefaults,
} from '@/utils/api/types/config-edit/webhook/schemas';
import diffLists from '@/utils/diff-lists';

/**
* Maps the webhook form schema to an API payload.
*
* @param data - The form schema.
* @param defaultValue - Default values for the form schema.
* @param defaultValue - The default values to compare against (and omit if all defaults used and unmodified).
* @param mainDefaults - The 'webhook' globals.
* @param typeDefaults - Type-specific webhook form data.
* @returns The API payload with matching defaults removed.
*/
export const mapWebHooksSchemaToAPIPayload = (
data: WebHooksSchema,
defaultValue?: WebHooksSchema,
mainDefaults?: Record<string, WebHookSchema>,
typeDefaults?: WebHookSchema,
): WebHooksSchemaOutgoing => {
const dataMinimised = data.map((item) => {
const d = mapWebHookSchemaToAPIPayload(item);
const defaultsForItem = applyDefaultsRecursive(
mainDefaults?.[item.name] ?? null,
typeDefaults,
);
const d = mapWebHookSchemaToAPIPayload(item, defaultsForItem);
return removeEmptyValues(d) as WebHookSchemaOutgoing;
});

Expand Down Expand Up @@ -48,7 +57,16 @@ export const mapWebHooksSchemaToAPIPayload = (
* Maps the webhook form schema to an API payload.
*
* @param item - The form schema.
* @param defaults - The default values to compare against (and omit where used).
* @returns A `WebHookSchemaOutgoing` representing the `WebHookSchema`.
*/
export const mapWebHookSchemaToAPIPayload = (
item: WebHookSchema,
): WebHookSchemaOutgoing => webhookSchemaOutgoing.parse(item);
defaults?: WebHookSchema,
): WebHookSchemaOutgoing => {
const schema = webhookSchemaMapOutgoingWithDefaults(defaults);

return removeEmptyValues(
schema.parse(item) as WebHookSchemaOutgoing,
) as WebHookSchemaOutgoing;
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { z } from 'zod';
import { toZodEnumTuple } from '@/types/util';
import { WEBHOOK_TYPE } from '@/utils/api/types/config/webhook';
import { headersSchemaDefaults } from '@/utils/api/types/config-edit/shared/header/schemas';
import {
headersSchemaDefaults,
preprocessHeaderArrayWithDefaults,
} from '@/utils/api/types/config-edit/shared/header/preprocess';
import {
preprocessNumberFromString,
preprocessStringFromNumber,
Expand Down Expand Up @@ -49,3 +52,19 @@ export const webhooksSchemaOutgoing = z
.nullable()
.default(null);
export type WebHooksSchemaOutgoing = z.infer<typeof webhooksSchemaOutgoing>;

/**
* Outgoing schemas that are defaults-aware for list-like fields.
*
* @returns a per-type schema with the provided defaults where
* preprocessors can null fields that match the defaults.
*/
export const webhookSchemaMapOutgoingWithDefaults = (
defaults?: WebHookSchema,
) => {
return webhookSchema.extend({
custom_headers: preprocessHeaderArrayWithDefaults(defaults?.custom_headers),
desired_status_code: preprocessNumberFromString,
max_tries: preprocessNumberFromString,
});
};