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
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Tech Stories
---

Add MSW crud support for assigning Linodes to a VPC ([#12503](https://github.com/linode/manager/pull/12503))
1 change: 1 addition & 0 deletions packages/manager/src/mocks/mockState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const getStateSeederGroups = (

export const emptyStore: MockState = {
cloudnats: [],
configInterfaces: [],
domainRecords: [],
domains: [],
eventQueue: [],
Expand Down
161 changes: 161 additions & 0 deletions packages/manager/src/mocks/presets/crud/handlers/linodes/configs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import { linodeConfigInterfaceFactory } from '@linode/utilities';
import { http } from 'msw';

import {
makeNotFoundResponse,
makePaginatedResponse,
makeResponse,
} from 'src/mocks/utilities/response';

import { mswDB } from '../../../../indexedDB';
import { addInterfaceToSubnet, removeInterfaceFromSubnet } from './utils';

import type { Config, Interface } 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 getConfigs = () => [
http.get(
'*/v4*/linode/instances/:id/configs',
async ({
params,
request,
}): Promise<
StrictResponse<APIErrorResponse | APIPaginatedResponse<Config>>
> => {
const id = Number(params.id);
const linode = await mswDB.get('linodes', id);
const linodeConfigs = await mswDB.getAll('linodeConfigs');
const configInterfaces = await mswDB.getAll('configInterfaces');

if (!linode || !linodeConfigs || !configInterfaces) {
return makeNotFoundResponse();
}

const configs = linodeConfigs
.filter((configTuple) => configTuple[0] === id)
.map((configTuple) => {
const interfacesForConfig = configInterfaces
.filter((interfaceTuple) => interfaceTuple[0] === configTuple[1].id)
.map((interfaceTuple) => interfaceTuple[1]);
return { ...configTuple[1], interfaces: interfacesForConfig };
});

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

http.get(
'*/v4*/linode/instances/:id/configs/:configId',
async ({ params }): Promise<StrictResponse<APIErrorResponse | Config>> => {
const id = Number(params.id);
const linode = await mswDB.get('linodes', id);
const configId = Number(params.configId);
const linodeConfig = await mswDB.get('linodeConfigs', configId);
const configInterfaces = await mswDB.getAll('configInterfaces');

if (!linode || !linodeConfig || !configInterfaces) {
return makeNotFoundResponse();
}

const interfaces = configInterfaces
.filter((interfaceTuple) => interfaceTuple[0] === configId)
.map((interfaceTuple) => interfaceTuple[1]);

return makeResponse({
...linodeConfig[1],
interfaces,
});
}
),
];

export const appendConfigInterface = (mockState: MockState) => [
http.post(
'*/v4*/linode/instances/:id/configs/:configId/interfaces',
async ({
params,
request,
}): Promise<StrictResponse<APIErrorResponse | Interface>> => {
const linodeId = Number(params.id);
const linode = await mswDB.get('linodes', linodeId);
const configId = Number(params.configId);
const config = await mswDB.get('linodeConfigs', configId);

if (!linode || !config) {
return makeNotFoundResponse();
}

const interfacePayload = await request.clone().json();
const configInterface = linodeConfigInterfaceFactory.build({
...interfacePayload,
active: true,
});

const newlyAddedConfigInterface = await mswDB.add(
'configInterfaces',
[configId, configInterface],
mockState
);

if (interfacePayload.purpose === 'vpc') {
await addInterfaceToSubnet({
mockState,
interfaceId: newlyAddedConfigInterface[1].id,
isLinodeInterface: false,
linodeId: linode.id,
configId,
vpcId: configInterface.vpc_id,
subnetId: configInterface.subnet_id,
});
}

return makeResponse(newlyAddedConfigInterface[1]);
}
),
];

export const deleteConfigInterface = (mockState: MockState) => [
http.delete(
'*/v4*/linode/instances/:id/configs/:configId/interfaces/:interfaceId',
async ({ params }): Promise<StrictResponse<APIErrorResponse | {}>> => {
const linodeId = Number(params.id);
const configId = Number(params.configId);
const interfaceId = Number(params.interfaceId);
const linode = await mswDB.get('linodes', linodeId);
const config = await mswDB.get('linodeConfigs', configId);
const configInterfaceTuple = await mswDB.get(
'configInterfaces',
interfaceId
);

if (!linode || !config || !configInterfaceTuple) {
return makeNotFoundResponse();
}

// if the config interface is part of a VPC, we must update the VPC as well
const configInterface = configInterfaceTuple[1];
if (configInterface.purpose === 'vpc' && configInterface.subnet_id) {
await removeInterfaceFromSubnet({
mockState,
configId,
interfaceId: configInterface.id,
isLinodeInterface: false,
subnetId: configInterface.subnet_id,
vpcId: configInterface.vpc_id,
});
}

await mswDB.delete('configInterfaces', interfaceId, mockState);

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