Skip to content

Commit

Permalink
Merge branch 'develop' into feature/CloudManager-using-OAuth-PKCE
Browse files Browse the repository at this point in the history
  • Loading branch information
bnussman committed Jul 10, 2024
2 parents 801259a + facfc62 commit cdb9764
Show file tree
Hide file tree
Showing 1,014 changed files with 32,103 additions and 14,944 deletions.
15 changes: 15 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,21 @@ jobs:
- run: yarn --frozen-lockfile
- run: yarn workspace linode-manager run test

test-search:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: "18.14"
- uses: actions/cache@v3
with:
path: |
**/node_modules
key: ${{ runner.os }}-${{ hashFiles('**/yarn.lock') }}
- run: yarn --frozen-lockfile
- run: yarn workspace @linode/search run test

typecheck-manager:
runs-on: ubuntu-latest
needs:
Expand Down
5 changes: 1 addition & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,4 @@ packages/manager/bundle_analyzer_report.html
**/manager/src/dev-tools/*.local.*

# vitepress
docs/.vitepress/cache

# vitest-preview
.vitest-preview
docs/.vitepress/cache
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ This repository is home to the Akamai Connected **[Cloud Manager](https://cloud.

## Developing Locally

To get started running Cloud Manager locally, please see the [_Getting Started_ guide](docs/GETTING_STARTED.md).
To get started running Cloud Manager locally, please see the [Getting Started guide](https://linode.github.io/manager/GETTING_STARTED.html).

## Contributing

If you already have your development environment set up, please read the [contributing guidelines](docs/CONTRIBUTING.md) to get help in creating your first Pull Request.
If you already have your development environment set up, please read the [Contributing Guidelines](https://linode.github.io/manager/CONTRIBUTING.html) to get help in creating your first Pull Request.

To report a bug or request a feature in Cloud Manager, please [open a GitHub Issue](https://github.com/linode/manager/issues/new). For general feedback, use [linode.com/feedback](https://www.linode.com/feedback/).

Expand Down
19 changes: 10 additions & 9 deletions docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,26 @@ Feel free to open an issue to report a bug or request a feature.
5. Commit message format standard: `<commit type>: [JIRA-ticket-number] - <description>`

**Commit Types:**
`feat`: New feature for the user (not a part of the code, or ci, ...).
`fix`: Bugfix for the user (not a fix to build something, ...).
`change`: Modifying an existing visual UI instance. Such as a component or a feature.
`refactor`: Restructuring existing code without changing its external behavior or visual UI. Typically to improve readability, maintainability, and performance.
`test`: New tests or changes to existing tests. Does not change the production code.
`upcoming`: A new feature that is in progress, not visible to users yet, and usually behind a feature flag.
- `feat`: New feature for the user (not a part of the code, or ci, ...).
- `fix`: Bugfix for the user (not a fix to build something, ...).
- `change`: Modifying an existing visual UI instance. Such as a component or a feature.
- `refactor`: Restructuring existing code without changing its external behavior or visual UI. Typically to improve readability, maintainability, and performance.
- `test`: New tests or changes to existing tests. Does not change the production code.
- `upcoming`: A new feature that is in progress, not visible to users yet, and usually behind a feature flag.

**Example:** `feat: [M3-1234] - Allow user to view their login history`

6. Open a pull request against `develop` and make sure the title follows the same format as the commit message.
7. If needed, create a changeset to populate our changelog
- If you don't have the Github CLI installed or need to update it (you need GH CLI 2.21.0 or greater),
- install it via `brew`: https://cli.github.com/manual/installation or upgrade with `brew upgrade gh`
- If you don't have the Github CLI installed or need to update it (you need GH CLI 2.21.0 or greater),
- install it via `brew`: https://github.com/cli/cli#installation or upgrade with `brew upgrade gh`
- Once installed, run `gh repo set-default` and pick `linode/manager` (only > 2.21.0)
- You can also just create the changeset manually, in this case make sure to use the proper formatting for it.
- Run `yarn changeset`from the root, choose the package to create a changeset for, and provide a description for the change.
You can either have it committed automatically or do it manually if you need to edit it.
- A changeset is optional, it merely depends if it falls in one of the following categories:
- A changeset is optional, but should be included if the PR falls in one of the following categories:<br>
`Added`, `Fixed`, `Changed`, `Removed`, `Tech Stories`, `Tests`, `Upcoming Features`
- Select the changeset category that matches the commit type in your PR title. (Where this isn't a 1:1 match: generally, a `feat` commit type falls under an `Added` change and `refactor` falls under `Tech Stories`.)

Two reviews from members of the Cloud Manager team are required before merge. After approval, all pull requests are squash merged.

Expand Down
98 changes: 98 additions & 0 deletions docs/development-guide/05-fetching-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,104 @@ console.log(errorMap);
}
```

#### Scrolling to errors

For deep forms, we provide a utility that will scroll to the first error encountered within a defined container. We do this to improve error visibility, because the user can be unaware of an error that isn't in the viewport.
An error can be a notice (API error) or a Formik field error. In order to implement this often needed functionality, we must declare a form or form container via ref, then pass it to the `scrollErrorIntoViewV2` util (works both for class & functional components).

Note: the legacy `scrollErrorIntoView` is deprecated in favor of `scrollErrorIntoViewV2`.

Since Cloud Manager uses different ways of handling forms and validation, the `scrollErrorIntoViewV2` util should be implemented using the following patterns to ensure consistency.

##### Formik
```Typescript
import * as React from 'react';

import { scrollErrorIntoViewV2 } from 'src/utilities/scrollErrorIntoViewV2';

export const MyComponent = () => {
const formContainerRef = React.useRef<HTMLFormElement>(null);

const {
values,
// other handlers
} = useFormik({
initialValues: {},
onSubmit: mySubmitFormHandler,
validate: () => {
scrollErrorIntoViewV2(formRef);
},
validationSchema: myValidationSchema,
});

return (
<form onSubmit={handleSubmit} ref={formContainerRef}>
<Error />
{/* form fields */}
<button type="submit">Submit</button>
</form>
);
};
```

##### React Hook Forms
```Typescript
import * as React from 'react';

import { scrollErrorIntoViewV2 } from 'src/utilities/scrollErrorIntoViewV2';

export const MyComponent = () => {
const formContainerRef = React.useRef<HTMLFormElement>(null);

const methods = useForm<LinodeCreateFormValues>({
defaultValues,
mode: 'onBlur',
resolver: myResolvers,
// other methods
});

return (
<FormProvider {...methods}>
<form
onSubmit={methods.handleSubmit(onSubmit, () => scrollErrorIntoViewV2(formRef))}
ref={formContainerRef}
>
<Error />
{/* form fields */}
<button type="submit">Submit</button>
</form>
</>
);
};
```

##### Uncontrolled forms
```Typescript
import * as React from 'react';

import { scrollErrorIntoViewV2 } from 'src/utilities/scrollErrorIntoViewV2';

export const MyComponent = () => {
const formContainerRef = React.useRef<HTMLFormElement>(null);

const handleSubmit = () => {
try {
// form submission logic
} catch {
scrollErrorIntoViewV2(formContainerRef);
}
};

return (
<form onSubmit={handleSubmit} ref={formContainerRef}>
<Error />
{/* form fields */}
<button type="submit">Submit</button>
</form>
);
};
```

### Toast / Event Message Punctuation
**Best practice:**
- If a message is a sentence or a sentence fragment with a subject and a verb, add punctuation. Otherwise, leave punctuation off.
Expand Down
30 changes: 4 additions & 26 deletions docs/development-guide/08-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,29 +43,6 @@ yarn workspace linode-manager run test:debug

Test execution will stop at the debugger statement, and you will be able to use Chrome's normal debugger to step through the tests (open `chrome://inspect/#devices` in Chrome).

### Visual debugging

Using `vite-preview`, you can view a preview of the tested component in the browser.

First, add the following lines to your test:

```
import { debug } from 'vitest-preview';
// Inside your tests
describe('my test', () => {
render(<MyComponent />);
debug(); // 👈 Add this line
}
```

Start the `vitest-preview` server:
```
yarn vitest-preview
```

Finally, run the test to view the component in the browser.

### React Testing Library

This library provides a set of tools to render React components from within the Vitest environment. The library's philosophy is that components should be tested as closely as possible to how they are used.
Expand Down Expand Up @@ -209,9 +186,10 @@ These environment variables are specific to Cloud Manager UI tests. They can be
###### General
Environment variables related to the general operation of the Cloud Manager Cypress tests.

| Environment Variable | Description | Example | Default |
|----------------------|-------------------------------------------------------------------------------------------------------|----------|---------------------------------|
| `CY_TEST_SUITE` | Name of the Cloud Manager UI test suite to run. Possible values are `core`, `region`, or `synthetic`. | `region` | Unset; defaults to `core` suite |
| Environment Variable | Description | Example | Default |
|----------------------|-------------------------------------------------------------------------------------------------------|--------------|---------------------------------|
| `CY_TEST_SUITE` | Name of the Cloud Manager UI test suite to run. Possible values are `core`, `region`, or `synthetic`. | `region` | Unset; defaults to `core` suite |
| `CY_TEST_TAGS` | Query identifying tests that should run by specifying allowed and disallowed tags. | `method:e2e` | Unset; all tests run by default |

###### Regions
These environment variables are used by Cloud Manager's UI tests to override region selection behavior. This can be useful for testing Cloud Manager functionality against a specific region.
Expand Down
2 changes: 1 addition & 1 deletion docs/development-guide/13-coding-standards.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ We use [ESLint](https://eslint.org/) to enforce coding and formatting standards.
- **prettier** (code formatting)
- **scanjs** (security)

If you are using VSCode it is highly recommended to use the ESlint extension. The Prettier extension is also recommended, as it can be configured to format your code on save.
If you are using VSCode it is **highly** recommended to use the [ESlint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint). The [Prettier extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) is also recommended, as it can be configured to format your code on save.

## React

Expand Down
108 changes: 108 additions & 0 deletions docs/development-guide/15-api-events.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# API Events

In order to display Events, Cloud Manager polls the [account/events](https://www.linode.com/docs/api/account/#events-list) endpoint at a 16 second interval, or every 2 seconds if there are “in-progress” events.

In order to display these messages in the application (Notification Center, /events page), we compose messages according to the Event key (`EventAction`). Each key requires an entry and set of custom messages for each status (`EventStatus`), dictated by API specs. Not every Status is required for a given Action.

## Adding a new Action and Composing Messages

In order to add a new Action, one must add a new key to the read-only `EventActionKeys` constant array in the api-v4 package.
Once that's done, a related entry must be added to the `eventMessages` Event Map. In order to do so, the entry can either be added to an existing Event Factory or a new one. `eventMessages` is strictly typed, so the action needs to be added to an existing factory or a new one, depending on its key (in this example the action starts with `linode_` so it belongs in the `linode.tsx` factory):

```Typescript
import { EventLink } from '../EventLink';

import type { PartialEventMap } from '../types';

export const linode: PartialEventMap<'linode'> = {
linode_addip: {
notification: (e) => (
<>
An IP address has been <strong>added</strong> to Linode{' '}
<EventLink event={e} to="entity" />.
</>
),
},
};
```

The convention to compose the message is as follows:
- Use the `<EventLink />` component for linking `entity` or `secondary_entity`. This component includes a lookup util to format the link `href` according to the feature.
- The bolding should only be applied to:
- the primary action: (ex: `<strong>created</strong>`)
- its correlated negation for negative actions (ex: `could <strong>not</strong> be <strong>created</strong>.`)
- The `message` should be also handled via the `<EventMessage message={e.message} />` in order to handle potential formatting from the API string (ticks to indicate code blocks).
- The message composition can be enhanced by using custom components. For instance, if we need to fetch extra data based on an event entity, we can simply write a new component to include in the message:

```Typescript
export const linode: PartialEventMap<'linode'> = {
linode_migrate_datacenter: {
started: (e) => <LinodeMigrateDataCenterMessage event={e} />,
},
};

const LinodeMigrateDataCenterMessage = ({ event }: { event: Event }) => {
const { data: linode } = useLinodeQuery(event.entity?.id ?? -1);
const { data: regions } = useRegionsQuery();
const region = regions?.find((r) => r.id === linode?.region);

return (
<>
Linode <EventLink event={event} to="entity" /> is being{' '}
<strong>migrated</strong>
{region && (
<>
{' '}
to <strong>{region.label}</strong>
</>
)}
.
</>
);
};
```

## In Progress Events

Some event messages are meant to be displayed alongside a progress bar to show the user the percentage of the action's completion. When an action is in progress, the polling interval switches to every two seconds to provide real-time feedback.

Despite receiving a `percent_complete` value from the API, not all actions are suitable for displaying visual progress, often because they're too short, or we only receive 0% and 100% from the endpoint. To allow only certain events to feature the progress bar, their action keys must be added to the `ACTIONS_TO_INCLUDE_AS_PROGRESS_EVENTS` constant.

## Displaying Events in snackbars

We can leverage the Event Message factories in order to display events in snackbars/toasts when a given action gets triggered via APIv4.

```Typescript
const { enqueueSnackbar } = useSnackbar();

try {
const successMessage = getEventMessage({
action: 'image_upload',
entity: {
label: 'Entity',
url: '/image/123',
},
status: 'notification',
});

const showToast = (variant: any) =>
enqueueSnackbar(successMessage, {
'success',
});
}, catch {
const failureMessage = getEventMessage({
action: 'image_upload',
// in this case we don't add an entity since we know we can't link to it
status: 'failed',
});

const showToast = (variant: any) =>
enqueueSnackbar(failureMessage, {
'error',
});
}
```

Both `action` and `status` are required. The `entity` and `secondary_entity` can optionally be passed to allow for linking. **Note**: it is possible the Event Message linking will be missing if the action status message expects either value but isn't not provided by the instance call.

If a corresponding status does not exist (ex: "failed"), it's encouraged to add it to the Action. Event if not triggered by the API, it can be useful to have a reusable Event Message to use through the App.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"npm-run-all": "^4.1.5",
"patch-package": "^7.0.0",
"postinstall": "^0.6.0",
"typescript": "^4.9.5"
"typescript": "^5.4.5"
},
"husky": {
"hooks": {
Expand All @@ -33,7 +33,6 @@
"start:manager:ci": "yarn workspace linode-manager start:ci",
"clean": "rm -rf node_modules && rm -rf packages/@linode/api-v4/node_modules && rm -rf packages/manager/node_modules && rm -rf packages/@linode/validation/node_modules",
"test": "yarn workspace linode-manager test",
"vitest-preview": "yarn workspace linode-manager vitest-preview",
"package-versions": "node ./scripts/package-versions/index.js",
"storybook": "yarn workspace linode-manager storybook",
"cy:run": "yarn workspace linode-manager cy:run",
Expand All @@ -46,6 +45,7 @@
"coverage": "yarn workspace linode-manager coverage",
"coverage:summary": "yarn workspace linode-manager coverage:summary",
"junit:summary": "ts-node scripts/junit-summary/index.ts",
"generate-tod": "ts-node scripts/tod-payload/index.ts",
"docs": "bunx vitepress@1.0.0-rc.44 dev docs"
},
"resolutions": {
Expand Down
5 changes: 0 additions & 5 deletions packages/api-v4/.changeset/pr-10443-added-1715882636050.md

This file was deleted.

Loading

0 comments on commit cdb9764

Please sign in to comment.