Skip to content

Commit

Permalink
tech-story: [M3-9017] - Add MSW crud domains (#11428)
Browse files Browse the repository at this point in the history
* add MSW crud domains

* Added changeset: Add MSW crud domains

* feedback @jaalah-akamai

* feebback @pmakode-akamai
  • Loading branch information
abailly-akamai authored Jan 8, 2025
1 parent bab026d commit caca6dd
Show file tree
Hide file tree
Showing 13 changed files with 354 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Tech Stories
---

Add MSW crud domains ([#11428](https://github.com/linode/manager/pull/11428))
1 change: 1 addition & 0 deletions packages/manager/src/dev-tools/load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export async function loadDevTools(
// Merge the contexts
const mergedContext: MockState = {
...initialContext,
domains: [...initialContext.domains, ...(seedContext?.domains || [])],
eventQueue: [
...initialContext.eventQueue,
...(seedContext?.eventQueue || []),
Expand Down
16 changes: 11 additions & 5 deletions packages/manager/src/features/Events/FormattedEventMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as React from 'react';
import { SupportLink } from 'src/components/SupportLink';

interface MessageLinkEntity {
fallback?: string;
message: null | string;
}

Expand All @@ -15,10 +16,10 @@ interface MessageLinkEntity {
* - render "contact support" strings as <Link>.
*/
export const FormattedEventMessage = (props: MessageLinkEntity) => {
const { message } = props;
const { fallback, message } = props;

if (!message) {
return null;
return fallback ? fallback : null;
}

return formatMessage(message);
Expand All @@ -34,9 +35,14 @@ const formatMessage = (message: string): JSX.Element => {
let formattedPart: JSX.Element | string = part;

if (part.startsWith('`') && part.endsWith('`')) {
formattedPart = (
<StyledPre key={`${i}-${part}`}>{part.slice(1, -1)}</StyledPre>
);
const content = part.slice(1, -1);
if (content.length > 0) {
formattedPart = (
<StyledPre key={`${i}-${part}`}>{content}</StyledPre>
);
} else {
formattedPart = '';
}
}

if (part.match(supportLinkMatch)) {
Expand Down
14 changes: 8 additions & 6 deletions packages/manager/src/features/Events/factories/domain.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@ export const domain: PartialEventMap<'domain'> = {
),
},
domain_record_create: {
notification: (e) => (
<>
<FormattedEventMessage message={e.message} /> has been{' '}
<strong>added</strong> to <EventLink event={e} to="entity" />.
</>
),
notification: (e) => {
return (
<>
<FormattedEventMessage fallback="A record" message={e.message} /> has
been <strong>added</strong> to <EventLink event={e} to="entity" />.
</>
);
},
},
domain_record_delete: {
notification: (e) => (
Expand Down
2 changes: 2 additions & 0 deletions packages/manager/src/mocks/mockState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export const getStateSeederGroups = (
};

export const emptyStore: MockState = {
domainRecords: [],
domains: [],
eventQueue: [],
firewalls: [],
linodeConfigs: [],
Expand Down
2 changes: 2 additions & 0 deletions packages/manager/src/mocks/presets/baseline/crud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
} from 'src/mocks/presets/crud/handlers/events';
import { linodeCrudPreset } from 'src/mocks/presets/crud/linodes';

import { domainCrudPreset } from '../crud/domains';
import { placementGroupsCrudPreset } from '../crud/placementGroups';
import { supportTicketCrudPreset } from '../crud/supportTickets';
import { volumeCrudPreset } from '../crud/volumes';
Expand All @@ -17,6 +18,7 @@ export const baselineCrudPreset: MockPresetBaseline = {
...placementGroupsCrudPreset.handlers,
...supportTicketCrudPreset.handlers,
...volumeCrudPreset.handlers,
...domainCrudPreset.handlers,

// Events.
getEvents,
Expand Down
24 changes: 24 additions & 0 deletions packages/manager/src/mocks/presets/crud/domains.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import {
cloneDomain,
createDomain,
deleteDomains,
getDomains,
importDomain,
updateDomain,
} from 'src/mocks/presets/crud/handlers/domains';

import type { MockPresetCrud } from 'src/mocks/types';

export const domainCrudPreset: MockPresetCrud = {
group: { id: 'Domains' },
handlers: [
createDomain,
deleteDomains,
updateDomain,
getDomains,
cloneDomain,
importDomain,
],
id: 'domains:crud',
label: 'Domains CRUD',
};
252 changes: 252 additions & 0 deletions packages/manager/src/mocks/presets/crud/handlers/domains.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
import { DateTime } from 'luxon';
import { http } from 'msw';

import { domainFactory, domainRecordFactory } from 'src/factories';
import { mswDB } from 'src/mocks/indexedDB';
import { queueEvents } from 'src/mocks/utilities/events';
import {
makeNotFoundResponse,
makePaginatedResponse,
makeResponse,
} from 'src/mocks/utilities/response';

import type { Domain, DomainRecord } from '@linode/api-v4';
import type { StrictResponse } from 'msw';
import type { MockState } from 'src/mocks/types';
import type {
APIErrorResponse,
APIPaginatedResponse,
} from 'src/mocks/utilities/response';

export const getDomains = () => [
http.get(
'*/v4/domains/:id/records',
async ({
request,
}): Promise<
StrictResponse<APIErrorResponse | APIPaginatedResponse<DomainRecord>>
> => {
const domainRecords = domainRecordFactory.buildList(3);

return makePaginatedResponse({
data: domainRecords,
request,
});
}
),

http.get(
'*/v4/domains',
async ({
request,
}): Promise<
StrictResponse<APIErrorResponse | APIPaginatedResponse<Domain>>
> => {
const domains = await mswDB.getAll('domains');

if (!domains) {
return makeNotFoundResponse();
}

return makePaginatedResponse({
data: domains,
request,
});
}
),

http.get(
'*/v4/domains/:id',
async ({ params }): Promise<StrictResponse<APIErrorResponse | Domain>> => {
const id = Number(params.id);
const domain = await mswDB.get('domains', id);

if (!domain) {
return makeNotFoundResponse();
}

return makeResponse(domain);
}
),
];

export const createDomain = (mockState: MockState) => [
http.post(
'*/v4/domains',
async ({ request }): Promise<StrictResponse<APIErrorResponse | Domain>> => {
const payload = await request.clone().json();

const domain = domainFactory.build({
...payload,
created: DateTime.now().toISO(),
last_updated: DateTime.now().toISO(),
updated: DateTime.now().toISO(),
});

await mswDB.add('domains', domain, mockState);

queueEvents({
event: {
action: 'domain_create',
entity: {
id: domain.id,
label: domain.domain,
type: 'domain',
url: `/v4/domains/${domain.id}`,
},
},
mockState,
sequence: [{ status: 'notification' }],
});

return makeResponse(domain);
}
),
];

export const updateDomain = (mockState: MockState) => [
http.put(
'*/v4/domains/:id',
async ({
params,
request,
}): Promise<StrictResponse<APIErrorResponse | Domain>> => {
const id = Number(params.id);
const domain = await mswDB.get('domains', id);

if (!domain) {
return makeNotFoundResponse();
}

const payload = {
...(await request.clone().json()),
last_updated: DateTime.now().toISO(),
updated: DateTime.now().toISO(),
};
const updatedDomain = { ...domain, ...payload };

await mswDB.update('domains', id, updatedDomain, mockState);

queueEvents({
event: {
action: 'domain_update',
entity: {
id: domain.id,
label: domain.domain,
type: 'domain',
url: `/v4/domains/${domain.id}`,
},
},
mockState,
sequence: [{ status: 'notification' }],
});

return makeResponse(updatedDomain);
}
),
];

export const cloneDomain = (mockState: MockState) => [
http.post(
'*/v4/domains/:id/clone',
async ({ params }): Promise<StrictResponse<APIErrorResponse | Domain>> => {
const id = Number(params.id);
const domain = await mswDB.get('domains', id);

if (!domain) {
return makeNotFoundResponse();
}

const clonedDomain = {
...domain,
created: DateTime.now().toISO(),
last_updated: DateTime.now().toISO(),
updated: DateTime.now().toISO(),
};

await mswDB.add('domains', clonedDomain, mockState);

queueEvents({
event: {
action: 'domain_create',
entity: {
id: domain.id,
label: domain.domain,
type: 'domain',
url: `/v4/domains/${domain.id}`,
},
},
mockState,
sequence: [{ status: 'notification' }],
});

return makeResponse(domain);
}
),
];

export const importDomain = (mockState: MockState) => [
http.post(
'*/v4/domains/import',
async ({ request }): Promise<StrictResponse<APIErrorResponse | Domain>> => {
const payload = await request.clone().json();

const domain = domainFactory.build({
...payload,
created: DateTime.now().toISO(),
last_updated: DateTime.now().toISO(),
updated: DateTime.now().toISO(),
});

await mswDB.add('domains', domain, mockState);

queueEvents({
event: {
action: 'domain_create',
entity: {
id: domain.id,
label: domain.domain,
type: 'domain',
url: `/v4/domains/${domain.id}`,
},
},
mockState,
sequence: [{ status: 'notification' }],
});

return makeResponse(domain);
}
),
];

export const deleteDomains = (mockState: MockState) => [
http.delete(
'*/v4/domains/:id',
async ({ params }): Promise<StrictResponse<{} | APIErrorResponse>> => {
const id = Number(params.id);
const domain = await mswDB.get('domains', id);

if (!domain) {
return makeNotFoundResponse();
}

await mswDB.delete('domains', id, mockState);

queueEvents({
event: {
action: 'domain_delete',
entity: {
id: domain.id,
label: domain.domain,
type: 'domain',
url: `/v4/domains/${domain.id}`,
},
},
mockState,
sequence: [{ status: 'notification' }],
});

return makeResponse({});
}
),
];
Loading

0 comments on commit caca6dd

Please sign in to comment.