Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [M3-6723] – VPC Subnet endpoints, validation, & React Query queries #9390

Merged
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
5 changes: 5 additions & 0 deletions packages/api-v4/.changeset/pr-9390-added-1689111404354.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/api-v4": Added
---

Endpoints for VPC Subnets ([#9390](https://github.com/linode/manager/pull/9390))
14 changes: 9 additions & 5 deletions packages/api-v4/src/vpcs/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,27 @@ export interface CreateVPCPayload {
label: string;
description?: string;
region: string;
subnets?: SubnetPostObject[];
subnets?: CreateSubnetPayload[];
}

export interface UpdateVPCPayload {
label?: string;
description?: string;
}

export interface SubnetPostObject {
export interface CreateSubnetPayload {
label: string;
ipv4: string;
ipv6: string;
ipv4?: string;
ipv6?: string;
}

export interface Subnet extends SubnetPostObject {
export interface Subnet extends CreateSubnetPayload {
id: number;
linodes: number[];
created: string;
updated: string;
}

export interface ModifySubnetPayload {
label: string;
}
90 changes: 89 additions & 1 deletion packages/api-v4/src/vpcs/vpcs.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import {
createSubnetSchema,
createVPCSchema,
modifySubnetSchema,
updateVPCSchema,
} from '@linode/validation/lib/vpcs.schema';
import { BETA_API_ROOT as API_ROOT } from '../constants';
Expand All @@ -11,7 +13,14 @@ import Request, {
setXFilter,
} from '../request';
import { Filter, ResourcePage as Page, Params } from '../types';
import { CreateVPCPayload, UpdateVPCPayload, VPC } from './types';
import {
CreateSubnetPayload,
CreateVPCPayload,
ModifySubnetPayload,
Subnet,
UpdateVPCPayload,
VPC,
} from './types';

// VPC methods
/**
Expand Down Expand Up @@ -79,3 +88,82 @@ export const deleteVPC = (vpcID: number) =>
);

// Subnet methods
/**
* getSubnets
*
* Return a paginated list of subnets under a specified VPC.
*
*/
export const getSubnets = (vpcID: number, params?: Params, filter?: Filter) =>
Request<Page<Subnet>>(
setURL(`${API_ROOT}/vpcs/${encodeURIComponent(vpcID)}/subnets`),
setMethod('GET'),
setParams(params),
setXFilter(filter)
);

/**
* getSubnet
*
* Return details for a single specified subnet under a specified VPC.
*
*/
export const getSubnet = (vpcID: number, subnetID: number) =>
Request<Subnet>(
setURL(
`${API_ROOT}/vpcs/${encodeURIComponent(
vpcID
)}/subnets/${encodeURIComponent(subnetID)}`
),
setMethod('GET')
);

/**
* createSubnet
*
* Create a new subnet under an existing VPC.
*
*/
export const createSubnet = (vpcID: number, data: CreateSubnetPayload) =>
Request<Subnet>(
setURL(`${API_ROOT}/vpcs/${encodeURIComponent(vpcID)}/subnets`),
setMethod('POST'),
setData(data, createSubnetSchema)
);

/**
* modifySubnet
*
* Modify an existing subnet.
*
*/
export const modifySubnet = (
vpcID: number,
subnetID: number,
data: ModifySubnetPayload
) =>
Request<Subnet>(
setURL(
`${API_ROOT}/vpcs/${encodeURIComponent(
vpcID
)}/subnets/${encodeURIComponent(subnetID)}`
),
setMethod('PUT'),
setData(data, modifySubnetSchema)
);

/**
* deleteSubnet
*
* Delete a single specified subnet belonging to a specified VPC.
*
*/
export const deleteSubnet = (vpcID: number, subnetID: number) =>
Request<{}>(
setURL(
`${API_ROOT}/vpcs/${encodeURIComponent(
vpcID
)}/subnets/${encodeURIComponent(subnetID)}`
),
setMethod('DELETE')
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Tech Stories
---

React Query queries for VPC Subnets ([#9390](https://github.com/linode/manager/pull/9390))
121 changes: 112 additions & 9 deletions packages/manager/src/queries/vpcs.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import {
CreateSubnetPayload,
CreateVPCPayload,
ModifySubnetPayload,
Subnet,
UpdateVPCPayload,
VPC,
createSubnet,
createVPC,
deleteSubnet,
deleteVPC,
getSubnet,
getSubnets,
getVPC,
getVPCs,
modifySubnet,
updateVPC,
} from '@linode/api-v4';
import {
Expand All @@ -16,26 +24,28 @@ import {
} from '@linode/api-v4/lib/types';
import { useMutation, useQuery, useQueryClient } from 'react-query';

export const queryKey = 'vpcs';
export const vpcQueryKey = 'vpcs';
export const subnetQueryKey = 'subnets';

// VPC queries
export const useVPCsQuery = (params: Params, filter: Filter) => {
return useQuery<ResourcePage<VPC>, APIError[]>(
[queryKey, 'paginated', params, filter],
[vpcQueryKey, 'paginated', params, filter],
() => getVPCs(params, filter),
{ keepPreviousData: true }
);
};

export const useVPCQuery = (id: number) => {
return useQuery<VPC, APIError[]>([queryKey, 'vpc', id], () => getVPC(id));
return useQuery<VPC, APIError[]>([vpcQueryKey, 'vpc', id], () => getVPC(id));
};

export const useCreateVPCMutation = () => {
const queryClient = useQueryClient();
return useMutation<VPC, APIError[], CreateVPCPayload>(createVPC, {
onSuccess: (VPC) => {
queryClient.invalidateQueries([queryKey, 'paginated']);
queryClient.setQueryData([queryKey, 'vpc', VPC.id], VPC);
queryClient.invalidateQueries([vpcQueryKey, 'paginated']);
queryClient.setQueryData([vpcQueryKey, 'vpc', VPC.id], VPC);
},
});
};
Expand All @@ -46,8 +56,8 @@ export const useUpdateVPCMutation = (id: number) => {
(data) => updateVPC(id, data),
{
onSuccess: (VPC) => {
queryClient.invalidateQueries([queryKey, 'paginated']);
queryClient.setQueryData<VPC>([queryKey, 'vpc', VPC.id], VPC);
queryClient.invalidateQueries([vpcQueryKey, 'paginated']);
queryClient.setQueryData<VPC>([vpcQueryKey, 'vpc', VPC.id], VPC);
},
}
);
Expand All @@ -57,8 +67,101 @@ export const useDeleteVPCMutation = (id: number) => {
const queryClient = useQueryClient();
return useMutation<{}, APIError[]>(() => deleteVPC(id), {
onSuccess: () => {
queryClient.invalidateQueries([queryKey, 'paginated']);
queryClient.removeQueries([queryKey, 'vpc', id]);
queryClient.invalidateQueries([vpcQueryKey, 'paginated']);
queryClient.removeQueries([vpcQueryKey, 'vpc', id]);
},
});
};

// Subnet queries
export const useSubnetsQuery = (
vpcID: number,
params: Params,
filter: Filter
) => {
return useQuery<ResourcePage<Subnet>, APIError[]>(
[vpcQueryKey, 'vpc', vpcID, subnetQueryKey, 'paginated', params, filter],
() => getSubnets(vpcID, params, filter),
{ keepPreviousData: true }
);
};

export const useSubnetQuery = (vpcID: number, subnetID: number) => {
return useQuery<Subnet, APIError[]>(
[vpcQueryKey, 'vpc', vpcID, subnetQueryKey, 'subnet', subnetID],
() => getSubnet(vpcID, subnetID)
);
};

export const useCreateSubnetMutation = (vpcID: number) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The invalidation in the onSuccess functions for useCreateSubnetMutation, useUpdateSubnetMutation, and useDeleteSubnetMutation can probably be streamlined/improved, but I'd rather optimize those later in the process in tickets where those queries are made use of as opposed to over-optimizing at this early stage.

const queryClient = useQueryClient();
return useMutation<Subnet, APIError[], CreateSubnetPayload>(
(data) => createSubnet(vpcID, data),
{
onSuccess: () => {
// New subnet created --> refresh the paginated and individual VPC queries, plus the /subnets VPC query
queryClient.invalidateQueries([vpcQueryKey, 'paginated']);
queryClient.invalidateQueries([vpcQueryKey, 'vpc', vpcID]);
queryClient.invalidateQueries([
vpcQueryKey,
'vpc',
vpcID,
subnetQueryKey,
]);
},
}
);
};

export const useUpdateSubnetMutation = (vpcID: number, subnetID: number) => {
const queryClient = useQueryClient();
return useMutation<Subnet, APIError[], ModifySubnetPayload>(
(data) => modifySubnet(vpcID, subnetID, data),
{
onSuccess: () => {
// Subnet modified --> refresh the paginated and individual VPC queries, plus the paginated & individual subnet queries
queryClient.invalidateQueries([vpcQueryKey, 'paginated']);
queryClient.invalidateQueries([vpcQueryKey, 'vpc', vpcID]);
queryClient.invalidateQueries([
vpcQueryKey,
'vpc',
vpcID,
subnetQueryKey,
]);
queryClient.invalidateQueries([
vpcQueryKey,
'vpc',
vpcID,
subnetQueryKey,
'subnet',
subnetID,
]);
},
}
);
};

export const useDeleteSubnetMutation = (vpcID: number, subnetID: number) => {
const queryClient = useQueryClient();
return useMutation<{}, APIError[]>(() => deleteSubnet(vpcID, subnetID), {
onSuccess: () => {
// Subnet deleted --> refresh the paginated and individual VPC queries, plus the paginated subnet query, & clear the individual subnet query
queryClient.invalidateQueries([vpcQueryKey, 'paginated']);
queryClient.invalidateQueries([vpcQueryKey, 'vpc', vpcID]);
queryClient.invalidateQueries([
vpcQueryKey,
'vpc',
vpcID,
subnetQueryKey,
]);
queryClient.removeQueries([
vpcQueryKey,
'vpc',
vpcID,
subnetQueryKey,
'subnet',
subnetID,
]);
},
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/validation": Added
---

Validation for VPC subnet creation and modifications ([#9390](https://github.com/linode/manager/pull/9390))
Loading