Skip to content

Commit

Permalink
refactor: parse global messages using zod (#301)
Browse files Browse the repository at this point in the history
* refactor: parse global messages using zod

* fix: use default false instead of optional

* refactor: use optional chaining for function call

Co-authored-by: Mikael Brevik <[email protected]>

---------

Co-authored-by: Mikael Brevik <[email protected]>
  • Loading branch information
mortennordseth and mikaelbr authored Jun 6, 2024
1 parent 1a037c6 commit 9cebb97
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 92 deletions.
98 changes: 21 additions & 77 deletions src/modules/global-messages/converters.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,5 @@
import { isArray } from 'lodash';
import { GlobalMessageContextEnum, GlobalMessageType } from './types';
import { MessageMode } from '@atb/components/message-box';
import { QueryDocumentSnapshot } from 'firebase/firestore';
import { LocalizedString } from '@atb/translations/commons';

export function mapToLocalizedStringArray(
data: any,
): LocalizedString[] | undefined {
if (!data) return;
if (!isArray(data)) return;

return data
.map((ls: any) => mapToLocalizedString(ls))
.filter((lv): lv is LocalizedString => !!lv);
}

function mapToLocalizedString(data: any): LocalizedString | undefined {
if (!data) return;
if (data.lang != 'nob' && data.lang != 'eng' && data.lang != 'nno') return;

return {
lang: data.lang,
value: data.value,
};
}

function mapToContexts(data: any): GlobalMessageContextEnum[] | undefined {
if (!isArray(data)) return;

return data
.map((context: any) => mapToContext(context))
.filter(Boolean) as GlobalMessageContextEnum[];
}

function mapToContext(data: any): GlobalMessageContextEnum | undefined {
if (!Object.values(GlobalMessageContextEnum).includes(data)) return;
return data as GlobalMessageContextEnum;
}

function mapToMessageType(type: any) {
if (typeof type !== 'string') return;

const options = ['info', 'valid', 'warning', 'error'];
if (!options.includes(type)) return;
return type as MessageMode;
}
import { GlobalMessageType, globalMessageTypeSchema } from './types';
import { QueryDocumentSnapshot, Timestamp } from 'firebase/firestore';

export const globalMessageConverter = {
toFirestore(_any: any) {
Expand All @@ -54,40 +9,29 @@ export const globalMessageConverter = {
snapshot: QueryDocumentSnapshot<GlobalMessageType>,
): GlobalMessageType | undefined {
const data = snapshot.data();
const potential = {
id: snapshot.id,
active: data.active,
title: data.title,
body: data.body,
type: data.type,
subtle: data.subtle,
context: data.context,
isDismissable: data.isDismissable,
startDate: firestoreTimestampToDate(data.startDate),
endDate: firestoreTimestampToDate(data.endDate),
};

if (!hasNecessaryGlobalMessageTypeFields(data)) {
return undefined;
}

const active = data.active;
const body = mapToLocalizedStringArray(data.body);
const title = mapToLocalizedStringArray(data.title);
const context = mapToContexts(data.context);
const type = mapToMessageType(data.type);
const subtle = data.subtle ?? false;
const isDismissable = data.isDismissable ?? false;
const startDate = data.startDate;
const endDate = data.endDate;
const validated = globalMessageTypeSchema.safeParse(potential);

if (!body || !context || !type) return;
if (validated.success) {
return validated.data;
}

return {
id: snapshot.id,
type,
subtle,
active,
context,
body,
title,
isDismissable,
startDate,
endDate,
};
return undefined;
},
};

function hasNecessaryGlobalMessageTypeFields(data: any) {
return (
'active' in data && 'body' in data && 'context' in data && 'type' in data
);
function firestoreTimestampToDate(timestamp: any) {
return timestamp?.toDate?.();
}
38 changes: 23 additions & 15 deletions src/modules/global-messages/types.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
import { MessageMode } from '@atb/components/message-box';
import { LocalizedString } from '@atb/translations/commons';
import { Timestamp } from 'firebase/firestore';
import { languageAndTextSchema } from '@atb/translations/types';
import { z } from 'zod';

const messageModeSchema = z.union([
z.literal('info'),
z.literal('valid'),
z.literal('warning'),
z.literal('error'),
]);

export enum GlobalMessageContextEnum {
plannerWebAssistant = 'planner-web-assistant',
}

export type GlobalMessageType = {
id: string;
active: boolean;
title?: LocalizedString[];
body: LocalizedString[];
type: MessageMode;
subtle?: boolean;
context: GlobalMessageContextEnum[];
isDismissable?: boolean;
startDate?: Timestamp;
endDate?: Timestamp;
};
export const globalMessageTypeSchema = z.object({
id: z.string(),
active: z.boolean(),
title: z.array(languageAndTextSchema).optional(),
body: z.array(languageAndTextSchema),
type: messageModeSchema,
subtle: z.boolean().default(false),
context: z.array(z.nativeEnum(GlobalMessageContextEnum)),
isDismissable: z.boolean().default(false),
startDate: z.date().optional(),
endDate: z.date().optional(),
});

export type GlobalMessageType = z.infer<typeof globalMessageTypeSchema>;

0 comments on commit 9cebb97

Please sign in to comment.