libs/newsletters-data-client/src/lib/newsletter-storage/InMemoryNewsletterStorage.ts (202 lines of code) (raw):
import type { DraftNewsletterDataWithMeta } from '../schemas/draft-newsletter-data-type';
import { makeBlankMeta } from '../schemas/meta-data-type';
import type {
NewsletterData,
NewsletterDataWithMeta,
NewsletterDataWithoutMeta,
} from '../schemas/newsletter-data-type';
import { isNewsletterDataWithMeta } from '../schemas/newsletter-data-type';
import { StorageRequestFailureReason } from '../storage-response-types';
import type {
SuccessfulStorageResponse,
UnsuccessfulStorageResponse,
} from '../storage-response-types';
import type { UserProfile } from '../user-profile';
import { NewsletterStorage } from './NewsletterStorage';
// TODO - serialise Drafts before returning
// so objects in memory can't be directly modified outside the Storage
export class InMemoryNewsletterStorage implements NewsletterStorage {
private memory: NewsletterDataWithMeta[];
constructor(newsletters?: NewsletterData[]) {
this.memory = newsletters
? newsletters.map((n) => ({
...n,
meta: makeBlankMeta(),
}))
: [];
}
create(draft: DraftNewsletterDataWithMeta, user: UserProfile) {
// TODO - use the schema.safeParse and if the test fails,
// use the list of issues to generate a message with the
// wrong/missing fields listed.
const draftReady = isNewsletterDataWithMeta(draft);
if (!draftReady) {
const error: UnsuccessfulStorageResponse = {
ok: false,
message: `draft was not ready to be live`,
reason: StorageRequestFailureReason.InvalidDataInput,
};
return Promise.resolve(error);
}
const newsletterWithTheSameIdentityName = this.memory.find(
(item) => item.identityName === draft.identityName,
);
if (newsletterWithTheSameIdentityName) {
const error: UnsuccessfulStorageResponse = {
ok: false,
message: `draft has the same identityName (${newsletterWithTheSameIdentityName.identityName}) as newsletter #${newsletterWithTheSameIdentityName.listId}`,
reason: StorageRequestFailureReason.InvalidDataInput,
};
return Promise.resolve(error);
}
const newNewsletterWithNewId: NewsletterDataWithMeta = {
...draft,
listId: this.getNextId(),
meta: this.updateMetaForLaunch(draft.meta, user),
};
this.memory.push(newNewsletterWithNewId);
const response: SuccessfulStorageResponse<NewsletterDataWithoutMeta> = {
ok: true,
data: this.stripMeta(newNewsletterWithNewId),
};
return Promise.resolve(response);
}
read(listId: number) {
const match = this.memory.find(
(newsletter) => newsletter.listId === listId,
);
if (!match) {
return Promise.resolve(this.buildNoItemError(listId));
}
const response: SuccessfulStorageResponse<NewsletterDataWithoutMeta> = {
ok: true,
data: this.stripMeta(match),
};
return Promise.resolve(response);
}
readWithMeta(listId: number) {
const match = this.memory.find(
(newsletter) => newsletter.listId === listId,
);
if (!match) {
return Promise.resolve(this.buildNoItemError(listId));
}
const response: SuccessfulStorageResponse<NewsletterDataWithMeta> = {
ok: true,
data: match,
};
return Promise.resolve(response);
}
readByName(identityName: string) {
const match = this.memory.find(
(newsletter) => newsletter.identityName === identityName,
);
if (!match) {
return Promise.resolve(this.buildNoItemError(identityName));
}
const response: SuccessfulStorageResponse<NewsletterDataWithoutMeta> = {
ok: true,
data: this.stripMeta(match),
};
return Promise.resolve(response);
}
readByNameWithMeta(identityName: string) {
const match = this.memory.find(
(newsletter) => newsletter.identityName === identityName,
);
if (!match) {
return Promise.resolve(this.buildNoItemError(identityName));
}
const response: SuccessfulStorageResponse<NewsletterDataWithMeta> = {
ok: true,
data: match,
};
return Promise.resolve(response);
}
update(
listId: number,
modifications: Partial<NewsletterDataWithoutMeta>,
user: UserProfile,
) {
const modificationError = this.getModificationError(modifications);
if (modificationError) {
return Promise.resolve(modificationError);
}
const match = this.memory.find((item) => item.listId === listId);
if (!match) {
return Promise.resolve(this.buildNoItemError(listId));
}
const updatedItem: NewsletterDataWithMeta = {
...match,
...modifications,
meta: this.updateMeta(match.meta, user),
};
this.memory.splice(this.memory.indexOf(match), 1, updatedItem);
const response: SuccessfulStorageResponse<NewsletterDataWithoutMeta> = {
ok: true,
data: this.stripMeta(updatedItem),
};
return Promise.resolve(response);
}
async replace(
listId: number,
newsletter: NewsletterDataWithoutMeta,
user: UserProfile,
) {
const match = this.memory.find((item) => item.listId === listId);
if (!match) {
return Promise.resolve(this.buildNoItemError(listId));
}
if (
newsletter.identityName !== match.identityName ||
newsletter.listId !== match.listId
) {
console.error(
`newsletter identityName or listId mismatch for newsletter with id ${listId}`,
);
throw new Error(
`newsletter identityName or listId mismatch for newsletter with id ${listId}`,
);
}
const updatedItem: NewsletterDataWithMeta = {
...newsletter,
meta: this.updateMeta(match.meta, user),
};
this.memory.splice(this.memory.indexOf(match), 1, updatedItem);
const response: SuccessfulStorageResponse<NewsletterDataWithoutMeta> = {
ok: true,
data: this.stripMeta(updatedItem),
};
return Promise.resolve(response);
}
delete(listId: number) {
const match = this.memory.find((item) => item.listId === listId);
if (!match) {
return Promise.resolve(this.buildNoItemError(listId));
}
this.memory.splice(this.memory.indexOf(match), 1);
const response: SuccessfulStorageResponse<NewsletterDataWithoutMeta> = {
ok: true,
data: this.stripMeta(match),
};
return Promise.resolve(response);
}
list() {
const response: SuccessfulStorageResponse<NewsletterDataWithoutMeta[]> = {
ok: true,
data: [...this.memory].map(this.stripMeta).map((item) => ({ ...item })),
};
return Promise.resolve(response);
}
private getNextId(): number {
const currentHighestListId = this.memory.reduce<number>(
(highestListIdSoFar, nextDraft) => {
const { listId = 0 } = nextDraft;
return Math.max(listId, highestListIdSoFar);
},
0,
);
return currentHighestListId + 1;
}
getModificationError = NewsletterStorage.prototype.getModificationError;
buildNoItemError = NewsletterStorage.prototype.buildNoItemError;
stripMeta = NewsletterStorage.prototype.stripMeta;
createNewMeta = NewsletterStorage.prototype.createNewMeta;
updateMeta = NewsletterStorage.prototype.updateMeta;
updateMetaForLaunch = NewsletterStorage.prototype.updateMetaForLaunch;
}