Files
pitchfast/lib/campaign-validation.ts

219 lines
5.4 KiB
TypeScript

import { CAMPAIGN_COUNTRY_CODE, CAMPAIGN_COUNTRY_NAME, CAMPAIGN_RECURRENCES, CAMPAIGN_STATUSES, ORTHODOX_POSTAL_CODE_MESSAGE } from "./campaign-form";
import { CampaignRecurrence, CampaignStatus } from "./campaign-scheduling";
const CAMPAIGN_POSTAL_CODE_REGEX = /^\d{5}$/;
const MIN_RADIUS_KM = 1;
const MAX_RADIUS_KM = 5000;
const MIN_LEADS_PER_RUN = 1;
const MAX_LEADS_PER_RUN = 9999;
const MIN_AUDITS_PER_RUN = 1;
const MAX_AUDITS_PER_RUN = 9999;
function assert(condition: boolean, message: string): asserts condition {
if (!condition) {
throw new Error(message);
}
}
function assertFiniteInteger(
value: number,
fieldLabel: string,
min: number,
max: number,
) {
assert(Number.isFinite(value), `${fieldLabel} muss eine Zahl sein.`);
assert(Number.isInteger(value), `${fieldLabel} muss eine ganze Zahl sein.`);
assert(
value >= min,
`${fieldLabel} muss mindestens ${min} sein.`,
);
assert(
value <= max,
`${fieldLabel} darf höchstens ${max} sein.`,
);
}
function assertAllowed<T extends string>(
value: string,
allowed: readonly T[],
errorMessage: string,
): T {
assert(allowed.includes(value as T), errorMessage);
return value as T;
}
function assertCountryContext(countryCode?: string, country?: string) {
const hasCountryCode = countryCode !== undefined;
const hasCountry = country !== undefined;
assert(
!(hasCountryCode || hasCountry) || (hasCountryCode && hasCountry),
"Deutschland-Kontext muss vollständig gesetzt oder ausgelassen werden.",
);
if (hasCountryCode || hasCountry) {
assert(
countryCode === CAMPAIGN_COUNTRY_CODE && country === CAMPAIGN_COUNTRY_NAME,
"Nur Deutschland-Context ist erlaubt.",
);
}
}
export type CampaignCreatePayload = {
status: CampaignStatus;
recurrence: CampaignRecurrence;
postalCode: string;
radiusKm: number;
maxNewLeadsPerRun: number;
maxAuditsPerRun: number;
countryCode: string;
country: string;
};
export type CampaignUpdatePayload = Partial<
Omit<CampaignCreatePayload, "status" | "recurrence">
> & {
status?: CampaignStatus;
recurrence?: CampaignRecurrence;
countryCode: string;
country: string;
};
type CampaignCreateInput = {
status: string;
recurrence: string;
postalCode: string;
radiusKm: number;
maxNewLeadsPerRun: number;
maxAuditsPerRun: number;
countryCode?: string;
country?: string;
};
type CampaignUpdateInput = {
postalCode?: string;
radiusKm?: number;
maxNewLeadsPerRun?: number;
maxAuditsPerRun?: number;
status?: string;
recurrence?: string;
countryCode?: string;
country?: string;
};
export function validateCampaignCreateInput(
input: CampaignCreateInput,
): CampaignCreatePayload {
assertCountryContext(input.countryCode, input.country);
const status = assertAllowed(
input.status,
CAMPAIGN_STATUSES,
"Status ist ungültig.",
);
const recurrence = assertAllowed(
input.recurrence,
CAMPAIGN_RECURRENCES,
"Frequenz ist ungültig.",
);
assert(
CAMPAIGN_POSTAL_CODE_REGEX.test(input.postalCode),
ORTHODOX_POSTAL_CODE_MESSAGE,
);
assertFiniteInteger(input.radiusKm, "Radius", MIN_RADIUS_KM, MAX_RADIUS_KM);
assertFiniteInteger(
input.maxNewLeadsPerRun,
"Max. neue Leads",
MIN_LEADS_PER_RUN,
MAX_LEADS_PER_RUN,
);
assertFiniteInteger(
input.maxAuditsPerRun,
"Max. Audits",
MIN_AUDITS_PER_RUN,
MAX_AUDITS_PER_RUN,
);
return {
status,
recurrence,
postalCode: input.postalCode,
radiusKm: input.radiusKm,
maxNewLeadsPerRun: input.maxNewLeadsPerRun,
maxAuditsPerRun: input.maxAuditsPerRun,
countryCode: CAMPAIGN_COUNTRY_CODE,
country: CAMPAIGN_COUNTRY_NAME,
};
}
export function validateCampaignUpdateInput(
input: CampaignUpdateInput,
): CampaignUpdatePayload {
const updates: CampaignUpdatePayload = {
countryCode: CAMPAIGN_COUNTRY_CODE,
country: CAMPAIGN_COUNTRY_NAME,
};
assertCountryContext(input.countryCode, input.country);
if (input.status !== undefined) {
updates.status = assertAllowed(
input.status,
CAMPAIGN_STATUSES,
"Status ist ungültig.",
);
}
if (input.recurrence !== undefined) {
updates.recurrence = assertAllowed(
input.recurrence,
CAMPAIGN_RECURRENCES,
"Frequenz ist ungültig.",
);
}
if (input.postalCode !== undefined) {
assert(
CAMPAIGN_POSTAL_CODE_REGEX.test(input.postalCode),
ORTHODOX_POSTAL_CODE_MESSAGE,
);
updates.postalCode = input.postalCode;
}
if (input.radiusKm !== undefined) {
assertFiniteInteger(input.radiusKm, "Radius", MIN_RADIUS_KM, MAX_RADIUS_KM);
updates.radiusKm = input.radiusKm;
}
if (input.maxNewLeadsPerRun !== undefined) {
assertFiniteInteger(
input.maxNewLeadsPerRun,
"Max. neue Leads",
MIN_LEADS_PER_RUN,
MAX_LEADS_PER_RUN,
);
updates.maxNewLeadsPerRun = input.maxNewLeadsPerRun;
}
if (input.maxAuditsPerRun !== undefined) {
assertFiniteInteger(
input.maxAuditsPerRun,
"Max. Audits",
MIN_AUDITS_PER_RUN,
MAX_AUDITS_PER_RUN,
);
updates.maxAuditsPerRun = input.maxAuditsPerRun;
}
if (
input.countryCode !== undefined
|| input.country !== undefined
|| input.status !== undefined
|| input.recurrence !== undefined
|| input.postalCode !== undefined
|| input.radiusKm !== undefined
|| input.maxNewLeadsPerRun !== undefined
|| input.maxAuditsPerRun !== undefined
) {
return updates;
}
return updates;
}