Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
c8a19b4
Save work
abailly-akamai Mar 13, 2024
54f3d21
Save work
abailly-akamai Mar 13, 2024
2c6b531
Save work
abailly-akamai Mar 13, 2024
459f538
Save work
abailly-akamai Mar 14, 2024
926ab47
Update worker
abailly-akamai Mar 14, 2024
3c884d3
fixing tests
abailly-akamai Mar 14, 2024
bd15dcc
Last touches
abailly-akamai Mar 14, 2024
70004ba
new tests after rebase
abailly-akamai Mar 14, 2024
77e850f
improve methods
abailly-akamai Mar 14, 2024
36832dd
Added changeset: Upgrade MSW to 2.2.3
abailly-akamai Mar 14, 2024
49fb8e7
feedback
abailly-akamai Mar 14, 2024
b73cc08
upcoming: [M3-7868] - Gecko Beta Demo feedback (#10284)
hana-akamai Mar 18, 2024
888cc13
chore: Update `axios` to resolve `follow-redirects` CVE-2024-28849 (…
bnussman-akamai Mar 18, 2024
4c366d3
test: Fix Firewall update test flake by using less specific selectors…
jdamore-linode Mar 18, 2024
3c7874f
Merge branch 'master' into develop
abailly-akamai Mar 18, 2024
e97fbc2
Merge pull request #10285 from abailly-akamai/M3-7859
abailly-akamai Mar 18, 2024
7952cfc
refactor: [M3-7436] - Update Account Queries to use Query Key Factory…
bnussman-akamai Mar 18, 2024
fbc0f88
fix: SMTPRestrictionText.test.tsx MSW Change (#10295)
bnussman-akamai Mar 18, 2024
b783d23
Initial commit - save work
abailly-akamai Mar 7, 2024
b9522eb
Update UI and concitional rendering
abailly-akamai Mar 11, 2024
60562db
Wrap up UI
abailly-akamai Mar 12, 2024
d81059f
Testing and cleaning up
abailly-akamai Mar 13, 2024
85f4ab6
Wrap up testing and cleaning
abailly-akamai Mar 13, 2024
eddbca8
mooooar cleanup
abailly-akamai Mar 13, 2024
577077e
Added changeset: Add Create Placement Group flow in Linode Details Panel
abailly-akamai Mar 13, 2024
bc7fafa
small cleanup
abailly-akamai Mar 14, 2024
ffe2789
small cleanup
abailly-akamai Mar 14, 2024
c094a4e
feedback
abailly-akamai Mar 19, 2024
ef4321e
feedback 2
abailly-akamai Mar 19, 2024
eb1d77b
Better handling of at capacity PGs
abailly-akamai Mar 19, 2024
ca4e966
improve coverage
abailly-akamai Mar 19, 2024
95e2065
cleanup
abailly-akamai Mar 19, 2024
b3d1ef5
feedback some moar
abailly-akamai Mar 19, 2024
28e966a
oops
abailly-akamai Mar 19, 2024
23ca2ca
aaand the test
abailly-akamai Mar 19, 2024
cb1723a
Hide bullet for single notice (#10298)
hkhalil-akamai Mar 20, 2024
bd8054f
Merge pull request #10273 from abailly-akamai/M3-7691
abailly-akamai Mar 20, 2024
db36c7f
Save work: update known types
abailly-akamai Mar 20, 2024
34d1c98
Post rebase update
abailly-akamai Mar 20, 2024
6223170
test: [M3-7517 & M3-7519] - Cypress integration tests for Child -> Pa…
jdamore-linode Mar 21, 2024
2ca1896
upcoming: [M3-7869] - Linode Create Refactor - Part 2 - Images and Di…
bnussman-akamai Mar 21, 2024
874584c
change: [UIE-7308] - Notifications for database resize events (#10262)
mpolotsk-akamai Mar 21, 2024
f6ea8a2
Fix handling of newly created pg in linode create flow
abailly-akamai Mar 21, 2024
e8ff56e
Added changeset: Placement Groups types & payload updates
abailly-akamai Mar 21, 2024
1022582
Add placement group to checkout summary
abailly-akamai Mar 21, 2024
2200e87
Added changeset: Add placement group item to checkout summary
abailly-akamai Mar 21, 2024
1a6fb5a
Merge pull request #10300 from abailly-akamai/M3-7871
abailly-akamai Mar 21, 2024
3b5cf29
refactor: [M3-7897] - Query Key Factory for Regions (#10301)
bnussman-akamai Mar 21, 2024
c06cb80
refactor: [M3-7893] – Post-GA VPC cleanup (#10299)
dwiley-akamai Mar 22, 2024
d241493
Merge pull request #10304 from abailly-akamai/M3-7431
abailly-akamai Mar 22, 2024
a6da30b
fix: [M3-7881] - Restore loading state to Users & Grants table (#10303)
mjac0bs Mar 22, 2024
abaf03c
fix: [M3-7909] - Hide the Child Account Access table header for paren…
mjac0bs Mar 22, 2024
46ca9e1
upcoming: [M3-7878] - Linode Create UI Refactor - Part 3 - Details Se…
bnussman-akamai Mar 22, 2024
e95cfbc
upcoming: [M3-7613] - Grants and Permissions for Placement Groups (#1…
carrillo-erik Mar 22, 2024
fda2aae
refactor: [M3-7895] – Remove VPC feature flag (#10306)
dwiley-akamai Mar 22, 2024
d7b6b88
fix: [M3-7862] - Fix display of TagsPanel in read-only entities (#10275)
hkhalil-akamai Mar 25, 2024
d8adf13
upcoming: [M3-7879] - Linode Create Refactor - Part 4 - Access (#10308)
bnussman-akamai Mar 25, 2024
e4fe05b
fix: [M3-6606] - Add spacing between copy and Linode Rebuild button. …
cpathipa Mar 25, 2024
488ff12
change: [M3-7922] - Clear ACLB configuration certificates if `http` o…
bnussman-akamai Mar 25, 2024
d0a3af5
refactor: [M3-7433] - Query Key Factory for Images (#10302)
bnussman-akamai Mar 26, 2024
9500152
EW-7508 Add tests to check parent and child Close Account flows (#10296)
cliu-akamai Mar 26, 2024
a115ded
Replacing what I found
abailly-akamai Mar 22, 2024
c25f97a
More instances replaced
abailly-akamai Mar 23, 2024
d0a3b5e
Moaar instances
abailly-akamai Mar 24, 2024
d5487f0
cleanup
abailly-akamai Mar 24, 2024
42f4d9f
Added changeset: Typescript: replace intersections with interfaces
abailly-akamai Mar 24, 2024
ff332d3
Oops
abailly-akamai Mar 25, 2024
ba86b1f
and oops
abailly-akamai Mar 25, 2024
a187d69
few more - all set
abailly-akamai Mar 25, 2024
094703e
feedback
abailly-akamai Mar 26, 2024
4876bb4
feedback 2
abailly-akamai Mar 26, 2024
3115624
fix conflix
abailly-akamai Mar 26, 2024
087dd45
Merge pull request #10309 from abailly-akamai/M3-7917
abailly-akamai Mar 26, 2024
860b0bd
upcoming: [M3-7903] - Placement Groups: Edit Drawer & Delete Modal UI…
abailly-akamai Mar 26, 2024
efc2fb4
upcoming: [M3-7925] - Linode Create Refactor - Add Firewall - Part 5 …
bnussman-akamai Mar 26, 2024
0fdc974
upcoming: [M3-7840] - Placement Groups Query Key Factory (#10314)
abailly-akamai Mar 26, 2024
a5d9483
feat: [M3-7888] - Revoke proxy PAT when switching accounts (#10313)
jaalah-akamai Mar 27, 2024
d4e0d0a
upcoming: [M3-7927] - Linode Create Refactor - Part 6 - Add-ons (#10319)
bnussman-akamai Mar 27, 2024
b0e50de
feat: [M3-7366] - Improved Primary Navigation UX (#10137)
jaalah-akamai Mar 28, 2024
7187a7f
upcoming: [M3-7905] - Update PG landing page table (#10318)
abailly-akamai Mar 28, 2024
adb3a4d
fix: [UIE-7491] - Hide DBaaS resize tab behind feature flag (#10324)
mpolotsk-akamai Mar 28, 2024
3ed6b48
fix: [M3-7605] - Wrong status indicator when provisioning a LKE (#10320)
hana-akamai Mar 28, 2024
6df1ff8
Cloud version 1.116.0, API v4 version 1.113.0, and Validation version…
hana-akamai Mar 28, 2024
510bb1a
fix api changelog version number
hana-akamai Apr 1, 2024
1eb4e18
Merge pull request #10327 from linode/release-v1.116.0
hana-akamai Apr 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 2 additions & 2 deletions docs/development-guide/09-mocking-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ To enable the MSW, open the Local Dev Tools and check the "Mock Service Worker"
import { rest } from "msw";

const handlers = [
rest.get("*/profile", (req, res, ctx) => {
http.get("*/profile", () => {
//
const profile = profileFactory.build({ restricted: true });
return res(ctx.json(profile));
return HttpResponse.json((profile));
}),
// ... other handlers
];
Expand Down
29 changes: 28 additions & 1 deletion docs/development-guide/13-coding-standards.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ The styles for Cloud Manager are located in three places:
- The breakpoints can be modified at `/foundations/breakpoints/index.ts`.
- Component-specific styles may be defined either at the end of the component file or in a dedicated file, named `ComponentName.styles.tsx`. Refer to the guidelines outlined in the "Styles" section of [Component Structure](02-component-structure.md#styles).

## Typesript Unions, Const Enums and Objects
## Typescript Unions, Const Enums, Objects and Intersections
In our development process, we often encounter scenarios where we need to handle various messages or descriptions in our application. These messages can range from short, pithy statements to longer, more descriptive texts. To ensure a consistent and maintainable approach, we can use union types for lists of pithy data and const enums or plain old JavaScript objects (POJOs) for longer descriptions.

### Union Types for Pithy Data
Expand Down Expand Up @@ -132,6 +132,33 @@ myFunction(CreateTypes.Backup); // Works
myFunction('fromBackup'); // Works
```

### Preferring Interfaces Over Intersections

Much of the time, a simple type alias to an object type acts very similarly to an interface.

```Typescript
interface Foo { prop: string }

type Bar = { prop: string };
```

However, and as soon as you need to compose two or more types, you have the option of extending those types with an interface, or intersecting them in a type alias, and that's when the differences start to matter.

Interfaces create a single flat object type that detects property conflicts, which are usually important to resolve! Intersections on the other hand just recursively merge properties, and in some cases produce never. Interfaces also display consistently better, whereas type aliases to intersections can't be displayed in part of other intersections. Type relationships between interfaces are also cached, as opposed to intersection types as a whole. A final noteworthy difference is that when checking against a target intersection type, every constituent is checked before checking against the "effective"/"flattened" type.

For this reason, extending types with interfaces/extends is suggested over creating intersection types.

```Typescript
- type Foo = Bar & Baz & {
- someProp: string;
- }
+ interface Foo extends Bar, Baz {
+ someProp: string;
+ }
```

Source: [TypeScript Wiki](https://github.com/microsoft/TypeScript/wiki/Performance#preferring-interfaces-over-intersections)

## Adobe Analytics

### Writing a Custom Event
Expand Down
53 changes: 43 additions & 10 deletions docs/tooling/react-query.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

[TanStack Query](https://tanstack.com/query/latest) (formerly React Query) is Cloud Manager's primary tool for fetching and caching API data. For a quick introduction, read our [Fetching Data](../development-guide/05-fetching-data.md#react-query) development guide.

You can find all of Cloud Manager's queries and mutations in `packages/manager/src/queries`.

## Query Keys

React Query's cache is a simple key-value store. Query Keys are serializable strings that uniquely identify a query's data in the cache. You can read more about the concept [here](https://tanstack.com/query/latest/docs/framework/react/guides/query-keys) in the TanStack Query docs.
Expand All @@ -12,6 +14,12 @@ Because of Cloud Manager's complexity, we use [`@lukemorales/query-key-factory`]

#### Simple Query

> [!note]
> Queries that have no parameters _must_ specify `null` for the query key.
> Using `null` tells the query key factory to not append any params to the query key.
> (See the profile example below)


```ts
import { useQuery } from "@tanstack/react-query";
import { getProfile } from "@linode/api-v4";
Expand All @@ -20,7 +28,7 @@ import type { APIError, Profile } from "@linode/api-v4";
const profileQueries = createQueryKeys('profile', {
profile: {
queryFn: getProfile,
queryKey: null,
queryKey: null, // queryKey will be ["profile", "profile"]
},
});

Expand All @@ -41,22 +49,31 @@ import type { APIError, Linode } from "@linode/api-v4";
const linodeQueries = createQueryKeys('linodes', {
linode: (id: number) => ({
queryFn: () => getLinode(id),
queryKey: [id],
queryKey: [id], // queryKey will be ["linodes", "linode", id]
}),
});

export const useLinodeQuery = (id: number) =>
useQuery<Linode, APIError[]>(linodeQueries.linode(1));
```

### Additional Reading on Query Keys

- https://tkdodo.eu/blog/effective-react-query-keys#effective-react-query-keys
- https://tanstack.com/query/latest/docs/framework/react/guides/query-keys

## Maintaining the Cache

> A significant challenge of React Query is keeping the client state in sync with the server.
A significant challenge of React Query is keeping the client state in sync with the server.

The two easiest ways of updating the cache using React Query are
- Using `invalidateQueries` to mark data as stale (which will trigger a refetch the next time the query is mounted)
- Using `setQueryData` to manually update the cache

> [!note]
> Place `invalidateQueries` and `setQueryData` calls in the `onSuccess` of `useMutation` hooks
> whenever possible.

### `invalidateQueries`

This will mark data as stale in the React Query cache, which will cause Cloud Manager to refetch the data if the corresponding query is mounted.
Expand Down Expand Up @@ -85,11 +102,11 @@ import type { APIError, Linode, ResourcePage } from "@linode/api-v4";
const linodeQueries = createQueryKeys('linodes', {
linode: (id: number) => ({
queryFn: () => getLinode(id),
queryKey: [id],
queryKey: [id], // queryKey will be ["linodes", "linode", id]
}),
linodes: (params: Params = {}, filter: Filter = {}) => ({
paginated: (params: Params = {}, filter: Filter = {}) => ({
queryFn: () => getLinodes(params, filter),
queryKey: [params, filter],
queryKey: [params, filter], // queryKey will be ["linodes", "paginated", params, filter]
}),
});

Expand All @@ -102,7 +119,7 @@ export const useLinodeUpdateMutation = (id: number) => {
mutationFn: (data) => updateLinode(id, data),
onSuccess(linode) {
// Invalidate all paginated pages in the cache.
queryClient.invalidateQueries(linodeQueries.linodes._def);
queryClient.invalidateQueries(linodeQueries.paginated._def);
// Because we have the updated Linode, we can manually set the cache for the `useLinode` query.
queryClient.setQueryData(linodeQueries.linode(id).queryKey, linode);
},
Expand All @@ -114,8 +131,10 @@ export const useDeleteLinodeMutation = (id: number) => {
return useMutation<{}, APIError[]>({
mutationFn: () => deleteLinode(id),
onSuccess() {
// Invalidate all paginated pages in the cache.
queryClient.invalidateQueries(linodeQueries.paginated._def);
// Remove the deleted linode from the cache
queryClient.removeQueries(linodeQueries.linode(id).queryKey);
queryClient.invalidateQueries(linodeQueries.linodes._def);
},
});
};
Expand All @@ -126,10 +145,24 @@ export const useCreateLinodeMutation = () => {
mutationFn: createLinode,
onSuccess(linode) {
// Invalidate all paginated pages in the cache. We don't know what page the new Linode will be on.
queryClient.invalidateQueries(linodeQueries.linodes._def);
queryClient.invalidateQueries(linodeQueries.paginated._def);
// Because we have the new Linode, we can manually set the cache for the `useLinode` query.
queryClient.setQueryData(linodeQueries.linode(id).queryKey, linode);
},
});
}
```
```

## Frequently Asked Questions

### Are we storing dupdate data in the cache? Why?

Yes, there is potential for the same data to exist many times in the cache.

For example, we have a query `useVolumesQuery` with the query key `["volumes", "paginated", { page: 1 }]` that contains the first 100 volumes on your account.
One of those same volumes could also be stored in the cache by using `useVolumeQuery` with query key `["linodes", "linode", 5]`.
This creates a senerio where the same volume is cached by React Query under multiple query keys.

This is a legitimate disadvantage of React Query's caching strategy. **We must be aware of this when we perform cache updates (using invalidations or manually updating the cache) so that the entity is update everywhere in the cache.**

Some data fetching tools like Apollo Client are able to intelligently detect duplicate entities and merge them. React Query does not do this. See [this tweet](https://twitter.com/tannerlinsley/status/1557395389531074560) from the creator of React Query.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"resolutions": {
"@babel/traverse": "^7.23.3",
"minimist": "^1.2.3",
"yargs-parser": "^18.1.3",
"yargs-parser": "^21.1.1",
"kind-of": "^6.0.3",
"node-fetch": "^2.6.7",
"ua-parser-js": "^0.7.33",
Expand Down
16 changes: 16 additions & 0 deletions packages/api-v4/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
## [2024-04-01] - v0.113.0

### Added:

- Event type for database resize create (#10262)
- jsdoc style comments to `CreateLinodeRequest` based on API documentation ([#10319](https://github.com/linode/manager/pull/10319))

### Changed:

- Allow `image` to be `null` in `CreateLinodeRequest` ([#10281](https://github.com/linode/manager/pull/10281))
- Allow `firewall_id` to be `null` in `CreateLinodeRequest` ([#10319](https://github.com/linode/manager/pull/10319))

### Tech Stories:

- Update `axios` to resolve `follow-redirects` CVE-2024-28849 ([#10291](https://github.com/linode/manager/pull/10291))

## [2024-03-18] - v0.112.0


Expand Down
4 changes: 2 additions & 2 deletions packages/api-v4/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@linode/api-v4",
"version": "0.112.0",
"version": "0.113.0",
"homepage": "https://github.com/linode/manager/tree/develop/packages/api-v4",
"bugs": {
"url": "https://github.com/linode/manager/issues"
Expand Down Expand Up @@ -41,7 +41,7 @@
"unpkg": "./lib/index.global.js",
"dependencies": {
"@linode/validation": "*",
"axios": "~1.6.5",
"axios": "~1.6.8",
"ipaddr.js": "^2.0.0",
"yup": "^0.32.9"
},
Expand Down
8 changes: 6 additions & 2 deletions packages/api-v4/src/account/oauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,12 @@ export const getOAuthClient = (clientId: number) =>
*
*/

interface OAuthClientWithSecret extends OAuthClient {
secret: string;
}

export const createOAuthClient = (data: OAuthClientRequest) =>
Request<OAuthClient & { secret: string }>(
Request<OAuthClientWithSecret>(
setURL(`${API_ROOT}/account/oauth-clients`),
setMethod('POST'),
setData(data, createOAuthClientSchema)
Expand All @@ -69,7 +73,7 @@ export const createOAuthClient = (data: OAuthClientRequest) =>
*
*/
export const resetOAuthClientSecret = (clientId: number | string) =>
Request<OAuthClient & { secret: string }>(
Request<OAuthClientWithSecret>(
setURL(
`${API_ROOT}/account/oauth-clients/${encodeURIComponent(
clientId
Expand Down
1 change: 1 addition & 0 deletions packages/api-v4/src/account/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ export type EventAction =
| 'credit_card_updated'
| 'database_low_disk_space'
| 'database_resize'
| 'database_resize_create'
| 'database_backup_restore'
| 'database_create'
| 'database_credentials_reset'
Expand Down
3 changes: 2 additions & 1 deletion packages/api-v4/src/databases/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ export type DatabaseStatus =
| 'resuming'
| 'restoring'
| 'failed'
| 'degraded';
| 'degraded'
| 'resizing';

export type DatabaseBackupType = 'snapshot' | 'auto';

Expand Down
84 changes: 82 additions & 2 deletions packages/api-v4/src/linodes/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -350,24 +350,104 @@ export interface CreateLinodePlacementGroupPayload {
}

export interface CreateLinodeRequest {
/**
* The Linode Type of the Linode you are creating.
*/
type: string;
/**
* The Region where the Linode will be located.
*/
region: string;
/**
* A StackScript ID that will cause the referenced StackScript to be run during deployment of this Linode.
*
* This field cannot be used when deploying from a Backup or a Private Image.
*/
stackscript_id?: number;
/**
* A Backup ID from another Linode’s available backups.
*
* Your User must have read_write access to that Linode,
* the Backup must have a status of successful,
* and the Linode must be deployed to the same region as the Backup.
*
* This field and the image field are mutually exclusive.
*/
backup_id?: number;
/**
* When deploying from an Image, this field is optional, otherwise it is ignored.
* This is used to set the swap disk size for the newly-created Linode.
* @default 512
*/
swap_size?: number;
image?: string;
/**
* An Image ID to deploy the Linode Disk from.
*/
image?: string | null;
/**
* This sets the root user’s password on a newly-created Linode Disk when deploying from an Image.
*/
root_pass?: string;
/**
* A list of public SSH keys that will be automatically appended to the root user’s
* `~/.ssh/authorized_keys`file when deploying from an Image.
*/
authorized_keys?: string[];
/**
* If this field is set to true, the created Linode will automatically be enrolled in the Linode Backup service.
* This will incur an additional charge. The cost for the Backup service is dependent on the Type of Linode deployed.
*
* This option is always treated as true if the account-wide backups_enabled setting is true.
*
* @default false
*/
backups_enabled?: boolean;
/**
* This field is required only if the StackScript being deployed requires input data from the User for successful completion
*/
stackscript_data?: any;
/**
* If it is deployed from an Image or a Backup and you wish it to remain offline after deployment, set this to false.
* @default true if the Linode is created with an Image or from a Backup.
*/
booted?: boolean;
/**
* The Linode’s label is for display purposes only.
* If no label is provided for a Linode, a default will be assigned.
*/
label?: string;
/**
* An array of tags applied to this object.
*
* Tags are for organizational purposes only.
*/
tags?: string[];
/**
* If true, the created Linode will have private networking enabled and assigned a private IPv4 address.
* @default false
*/
private_ip?: boolean;
/**
* A list of usernames. If the usernames have associated SSH keys,
* the keys will be appended to the root users `~/.ssh/authorized_keys`
* file automatically when deploying from an Image.
*/
authorized_users?: string[];
/**
* An array of Network Interfaces to add to this Linode’s Configuration Profile.
*/
interfaces?: InterfacePayload[];
/**
* An object containing user-defined data relevant to the creation of Linodes.
*/
metadata?: UserData;
firewall_id?: number;
/**
* The `id` of the Firewall to attach this Linode to upon creation.
*/
firewall_id?: number | null;
/**
* An object that assigns this the Linode to a placment group upon creation.
*/
placement_group?: CreateLinodePlacementGroupPayload;
}

Expand Down
Loading