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

chore(docs): some updates to our engineering handbook #3057

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
74 changes: 61 additions & 13 deletions docs/1-tldr.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,20 @@ Routing configuration is stored in a modules `routes.ts` file i.e.
`@/app/module.name/routes.ts`. If your route is brand new add the JSON
configuration in there.

:::danger
All route views should use `RouteView`s and `AppView`s. Please make use of
Vue's nested routing/`RouterView` when required. Remember you can pass data
down through `RouterView`s.
:::

For more detail see:

- [RouteView](/src/app/application/components/route-view/README)
- [AppView](/src/app/application/components/app-view/README)
TLDR:

```vue
<RouteView
v-slot="{ route }"
name="the-name-of-the-route"
:params="{
onlySpecifyTheParamsYouNeed: '',
policyName: '',
page: 1,
}"
Expand All @@ -56,43 +56,91 @@ For more detail see:
<!-- your content goes in here -->
Page: {{ route.params.page }}

<!-- Make use of Nested Routing -->
<RouterView v-slot="{ Component }">
<!-- Please make use of Nested Routing when necessary -->
<RouterView
v-slot="{ Component }"
>
<component
:is="Component"
:data="..."
:data="{ passDataToTheChildRoute: true }"
/>
</RouterView>
</AppView>
</RouteView>
```

For more detail see:

- [RouteView](/src/app/application/components/route-view/README)
- [AppView](/src/app/application/components/app-view/README)

## Working with data

All read data interactions should use `DataSource` and or `DataLoader`.

:::warning
`DataSource` is **non-blocking** i.e. its contents will show always whether the data
is loaded or not.

`DataLoader` is **blocking** and can be used with or without `DataSource`
depending on which parts of the UI you want to be blocked. `DataLoader` can
also load multiple `DataSource`s.
:::

We often use `DataCollection` for filtering/finding data inline and providing
empty states.

For more detail see:
:::warning
`DataSource` has no visual elements/states.

- [DataSource](/src/app/application/components/data-source/README)
- [DataLoader](/src/app/application/components/data-source/README#simple-dataloader-usage)
- [DataCollection](src/app/application/components/data-collection/README)
`DataLoader` has automatic (but slottable) loading and error states.

`DataCollection` has an automatic (but slottable) empty state.
:::

As `DataLoader` has visual states for loading and error states, we generally
only use `DataSource` for more low level data-requesting and/or loading from
multiple sources at once and/or more complex data requesting sequences.

TLDR:

```vue
<DataSource v-slot="{ data }" src="/mesh-insights">
{{ data?.items.length }}
<DataSource
v-slot="{ data }"
src="/mesh-insights"
>
Always show me (guard for data is required as it might be undefined)
{{ data?.items.length }}
</DataSource>
...
<DataLoader
v-slot="{ data }"
src="/mesh-insights"
>
Only show me once the data is loaded
{{ data.items.length }}

<DataCollection
:items="data.items"
>
We are guaranteed to have at least one item,
otherwise the default empty state is shown.
{{ data.items[0].name }}
</DataCollection>
</DataLoader>
```

You could also use our `uri` helper for creating URIs/URLs for the `src`
parameter to enable auto-completion of URIs and type hints for the outputted
data.

For more detail see:

- [DataSource](/src/app/application/components/data-source/README)
- [DataLoader](/src/app/application/components/data-source/README#simple-dataloader-usage)
- [DataCollection](/src/app/application/components/data-collection/README)


## GUI Components

Apart from our `Data*` components we also have a set of `X*` components which
Expand Down
11 changes: 10 additions & 1 deletion docs/2-engineering-utilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,20 @@ make your own and if any become really common feel free to add here.
| ---- | ----- |
| <a href="javascript:(function()%7B(()%3D%3E%7Bconst%20prefix%3D'KUMA_'%3Bconst%20str%3Ddocument.cookie.split('%3B').map((item)%3D%3Eitem.trim()).filter((item)%3D%3Eitem!%3D%3D'').filter((item)%3D%3Eitem.split('%3D').shift().startsWith(prefix)).join('%3B')%3Bconst%20tab%3Dwindow.open(''%2C%20'_blank')%3Btab.document.write(%60%3Cbody%3E%3Cpre%3E%24%7Blocation.href%7D%23%24%7Bstr%7D%3C%2Fpre%3E%3Cbr%20%2F%3E%3Ca%20href%3D&quot;javascript%3A(function()%257B((str)%253D%253E%257Bstr.split('%253B').map((item)%253D%253Eitem.trim()).filter((item)%253D%253Eitem!%253D%253D'').forEach((item)%253D%253E%257Bdocument.cookie%2520%253D%2520%2560%2524%257Bitem%257D%253BPath%253D%252F%2560%253B%257D)%253Blocation.reload()%253B%257D)('%24%7Bstr%7D')%257D)()%253B&quot;%3EScenario%3C%2Fa%3E%3C%2Fbody%3E%60)%3B%7D)()%7D)()%3B">* Create Scenario</a> | Creates a new bookmarklet and link from your current cookies for dragging to your bookmark bar |
| --- | --- |
| <a href="javascript:(function()%7B((str)%3D%3E%7Bstr.split('%3B').map((item)%3D%3Eitem.trim()).filter((item)%3D%3Eitem!%3D%3D'').forEach((item)%3D%3E%7Bdocument.cookie%20%3D%20%60%24%7Bitem%7D%3BPath%3D%2F%60%3B%7D)%3Blocation.reload()%3B%7D)('KUMA_MOCK_API_ENABLED=false')%7D)()%3B">Disable Mocking</a>| Turn off HTTP API mocking. HTTP requests will be sent to a locally running kuma installation at http://localhost:5681 |
| <a href="javascript:(function()%7B((str)%3D%3E%7Bstr.split('%3B').map((item)%3D%3Eitem.trim()).filter((item)%3D%3Eitem!%3D%3D'').forEach((item)%3D%3E%7Bdocument.cookie%20%3D%20%60%24%7Bitem%7D%3BPath%3D%2F%60%3B%7D)%3Blocation.reload()%3B%7D)('KUMA_ENVIRONMENT=kubernetes')%7D)()%3B">Environment: kubernetes</a>| Set the `KUMA_ENVIRONMENT` to `kubernetes` |
| <a href="javascript:(function()%7B((str)%3D%3E%7Bstr.split('%3B').map((item)%3D%3Eitem.trim()).filter((item)%3D%3Eitem!%3D%3D'').forEach((item)%3D%3E%7Bdocument.cookie%20%3D%20%60%24%7Bitem%7D%3BPath%3D%2F%60%3B%7D)%3Blocation.reload()%3B%7D)('KUMA_ENVIRONMENT=universal')%7D)()%3B">Environment: kubernetes</a>| Set the `KUMA_ENVIRONMENT` to `universal` |
| <a href="javascript:(function()%7B((str)%3D%3E%7Bstr.split('%3B').map((item)%3D%3Eitem.trim()).filter((item)%3D%3Eitem!%3D%3D'').forEach((item)%3D%3E%7Bdocument.cookie%20%3D%20%60%24%7Bitem%7D%3BPath%3D%2F%60%3B%7D)%3Blocation.reload()%3B%7D)('KUMA_MOCK_API_ENABLED=false')%7D)()%3B">Disable Mocking</a>| Turn off HTTP API mocking. HTTP requests will be sent to a locally running kuma installation at http://localhost:5681 |
| <a href="javascript:(function()%7B((str)%3D%3E%7Bstr.split('%3B').map((item)%3D%3Eitem.trim()).filter((item)%3D%3Eitem!%3D%3D'').forEach((item)%3D%3E%7Bdocument.cookie%20%3D%20%60%24%7Bitem%7D%3BPath%3D%2F%60%3B%7D)%3Blocation.reload()%3B%7D)('KUMA_MODE=standalone')%7D)()%3B">Disable Zones</a>| Set the `KUMA_MODE` to `standalone` i.e. disable `zones` and only use a non-federated kuma with only Zone Egress |
| <a href="javascript:(function()%7B((str)%3D%3E%7Bstr.split('%3B').map((item)%3D%3Eitem.trim()).filter((item)%3D%3Eitem!%3D%3D'').forEach((item)%3D%3E%7Bdocument.cookie%20%3D%20%60%24%7Bitem%7D%3BPath%3D%2F%60%3B%7D)%3Blocation.reload()%3B%7D)('KUMA_MODE=global')%7D)()%3B">Enable Zones</a>| Set the `KUMA_MODE` to `global` i.e. enable `zones` and use a federated kuma with Zone support |
| <a href="javascript:(function()%7B((str)%3D>%7Bstr.split('%3B').map((item)%3D>item.trim()).filter((item)%3D>item!%3D%3D'').forEach((item)%3D>%7Bdocument.cookie %3D %60%24%7Bitem%7D%3BPath%3D%2F%60%3B%7D)%3Blocation.reload()%3B%7D)('KUMA_I18N_DEBUG_ENABLED=1')%7D)()%3B">Debug i18n strings</a>| Show i18n keys instead of strings |
| <a href="javascript:(function()%7B((str)%3D>%7B%0Astr.split('%3B').map((item)%3D>item.trim()).filter((item)%3D>item!%3D%3D'').forEach((item)%3D>%7B%0Adocument.cookie %3D %60%24%7Bitem.replace('%25s'%2C prompt(item%2C 3000))%7D%3BPath%3D%2F%60%3B%0A %7D)%3B%0A location.reload()%3B%0A%7D)('KUMA_LATENCY%3D%25s')%7D)()%3B">Latency: 3000</a>| Set the HTTP API latency (defaults to 3000ms) |

::: tip
There are very many `KUMA_<resource-type>_COUNT` variables to allow you to
control the amount of data returned by the HTTP API. Using for example
`KUMA_MESH_COUNT=0` is useful for viewing empty states.
:::


## `debug` service containers

Expand Down
62 changes: 44 additions & 18 deletions docs/4-modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,36 @@ section: Overview
---
# Modules

In `src/app/$module/`.
Our modules/packages are currently in `src/app/$module/`, but will eventually
be moved to fully fledged npm modules via workspaces.

A big part of the project’s business logic is loosely grouped into modules. For example, there is a zones module in `src/app/zones/`, a services module in `src/app/services/`, etc. A module consists of components and scripts for utility functions, route definitions, etc.
The application is grouped into separate modules/packages, mostly by "Kuma
Noun". For example, there is a zones module in `src/app/zones/`, a services
module in `src/app/services/`, etc.

There are other more generic/framework modules such as `application`, `msw`,
`me`, `x` etc.

A module consists of several things such as:

- `index`: service container configuration
- `routes`: Vue routing configuration
- `sources`: definitions for URIs/DataSources i.e. data retrieval
- `features`: user 'abilities' definitions, i.e. feature flags
- `components/`: components specific to this package
- `views/`: views/pages/routes specific to this package
- `data/`: data manipulation/correction/reshaping
- `locales/` i18n strings

## Components

In `src/app/$module/components/` or `src/app/$module/views/`.
The application has two distinct types of "component". "View", "Route" or
"Page" components and re-usable components.

A module’s components are placed in either a `components` or a `views` directory. Views are components that are hooked up to route definitions and are placed in the `views` directory. Any other component is placed in the `components` directory.
Route components are assigned to each specific route within `route.ts`. These
components currently live in a flat structure in `src/app/$module/views/`.

::: tip NOTE
Components that aren’t module-specific (or shared by multiple modules) are placed in `src/app/common/` instead.
:::
Other re-usable components live in `src/app/$module/components/`

## Locales

Expand All @@ -25,13 +42,10 @@ In `src/app/$module/locales/$locale/`.
Learn more about [i18n](/src/app/application/services/i18n/README.md)
:::

A module typically has message files which contain the visible/human-readable UI text in the form of a YAML object. Each piece of text is associated to a key in the form of a path that represents the location of the message in the YAML object.

## Utilities

In `src/app/$module/utilities/`.

Holds module-specific utility functions.
A module typically has message files which contain the visible/human-readable
UI text in the form of a YAML object. Each piece of text is associated to a key
in the form of a path that represents the location of the message in the YAML
object.

## Features

Expand All @@ -41,7 +55,10 @@ Learn more about [can](/src/app/application/services/can/README.md)

In `src/app/$module/features.ts`.

Holds a module’s feature definitions. Features are used to lock down or expose parts of the application based on some runtime state using the [can](/src/app/application/services/can/README.md) utility. Features are _defined_ in a module but can be used outside of modules.
Holds a module’s feature definitions. Features are used to lock down or expose
parts of the application based on some runtime state using the
[can](/src/app/application/services/can/README.md) utility. Features are
_defined_ in a module but can be used outside of modules.

## Routes

Expand All @@ -51,7 +68,8 @@ In `src/app/$module/routes.ts`.
Learn more about [routing](/docs/routing.md)
:::

Holds a module’s route definitions. They’re used to set-up the application routes.
Holds a module’s route definitions. They’re used to set-up the application
routes.

## Sources

Expand All @@ -61,7 +79,12 @@ In `src/app/$module/sources.ts`.
Learn more about [DataSource](/src/app/application/services/data-source/README.md)
:::

Holds a module’s data source definitions. They’re used to provide read access to data (most commonly via our HTTP API).
Holds a module’s data source definitions. They’re used to provide read access
to data (most commonly via our HTTP API).

## Data

In `src/app/$module/data/`

## Service definition

Expand All @@ -71,5 +94,8 @@ In `src/app/$module/index.ts`.
Learn more about [services](/src/services/README.md)
:::

Defines the module so it can be exposed to our dependency injection layer. This makes it so that a module’s features and sources are available throughout the application.
Defines the module so it can be exposed to our dependency injection layer. This
makes it so that a module’s features and sources are available throughout the
application in a way that can be configured, grouped, decorated and overridden
from The Outside i.e. without altering the code of the application itself.

58 changes: 39 additions & 19 deletions docs/5-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,28 @@ section: Overview
---
# Routing

In out application, documents (or views) are associated with URL paths using [vue-router](https://router.vuejs.org/). Every view has its own URL. Most views are representations of a resource (e.g. `/meshes` is a list of meshes, `/meshes/default` is a detail of the default mesh, `/meshes/default/services` is a list of services in the mesh “default”, etc.).
In the application, documents (or views) are associated with URL paths using
[vue-router](https://router.vuejs.org/). Every view has its own URL. Most views
are representations of a resource (e.g. `/meshes` is a list of meshes,
`/meshes/default` is a detail of the default mesh, `/meshes/default/services`
is a list of services in the mesh “default”, etc.).

## How to set-up module routes

::: tip
Learn more about [Modules](/docs/modules.md)
:::

To create a consistent set of URLs for our application, we follow a couple of rules. One key objective is to make sure that views in their current state have robust URLs (i.e. I can send you the URL of the Data Plane Proxy list view filtered by name “demo” on page 2 and that same state is restored when you access that URL).
To create a consistent set of URLs for our application, we follow a couple of
rules. One key objective is to make sure that views in their current state have
robust URLs (i.e. I can send you the URL of the Data Plane Proxy list view
filtered by name “demo” on page 2 and that same state is restored when you
access that URL).

### Naming

Each route definition has a path segment. All path segments are lowercase and dash-delimited. Resource names are plural (if there can be multiple instances of that resource).
Each route definition has a path segment. All path segments are lowercase and
dash-delimited. Resource names are plural (if there can be multiple instances
of that resource).

An example of route paths and their associated views based on the rules laid out below:
An example of route paths and their associated views based on the rules laid
out below:

| Path | View |
| ------------------------ | ----------------------------- |
Expand All @@ -31,10 +38,13 @@ An example of route paths and their associated views based on the rules laid out

**Example**: `/meshes`

For a resource list, the path segment is the plural variant of the resource name (e.g. `Mesh` → `/meshes`).
For a resource list, the path segment is the plural variant of the resource
name (e.g. `Mesh` → `/meshes`).

::: tip NOTE
We do stretch this rule occasionally. For example, the service list view is populated using `ServiceInsight` objects and its path segment is `/services` (not `/service-insights`).
We do stretch this rule occasionally. For example, the service list view is
populated using `ServiceInsight` objects and its path segment is `/services`
(not `/service-insights`).
:::

### Create
Expand All @@ -43,29 +53,39 @@ We do stretch this rule occasionally. For example, the service list view is popu

For a create view, a single static path segment `/-create` is used.

::: tip WARNING
A create view route (e.g. `/meshes/-create`) **must** be defined before routes for list views with a selected resource (e.g. `/meshes/default`) to make sure the create view route is matched first.
::: warning
A create view route (e.g. `/meshes/-create`) **must** be defined before routes
for list views with a selected resource (e.g. `/meshes/default`) to make sure
the create view route is matched first.
:::

::: tip NOTE
The path segment is chosen to be an invalid DNS name (which can’t start with a `-` character). This ensures the path is unambiguous and doesn’t conflict with a resource of the same name (e.g. a resource called `create`).
:::
The path segment is chosen to be an invalid DNS name (which can’t start with a
`-` character). This ensures the path is unambiguous and doesn’t conflict with
a resource of the same name (e.g. a resource called `create`).

### List with selected resource

**Example**: `/meshes/:mesh`

For a resource list that allows selecting an individual resource in order to display a kind of summary, a single dynamic path segment is used. This segment is dynamic and is either the unique name of a resource or the unique ID of a resource.
For a resource list that allows selecting an individual resource in order to
display a kind of summary, a single dynamic path segment is used. This segment
is dynamic and is either the unique name of a resource or the unique ID of a
resource.

### Detail

**Example**: `/meshes/:mesh/overview`

For a resource detail, two path segments are used. Detail views **must** be defined as child routes of the corresponding list view unless the resource only exists once (and so a list view doesn’t exist for it in the first place).
For a resource detail, two path segments are used. Detail views **must** be
defined as child routes of the corresponding list view unless the resource only
exists once (and so a list view doesn’t exist for it in the first place).

The first path segment is dynamic and is either the unique name of a resource or the unique ID of a resource.
The first path segment is dynamic and is either the unique name of a resource
or the unique ID of a resource.

The second path segment is static and should describe the kind of resource detail as there can be multiple detail views for a single resource. It should be a noun. For the default/main detail view, we usually use `/overview`.
The second path segment is static and should describe the kind of resource
detail as there can be multiple detail views for a single resource. It should
be a noun. For the default/main detail view, we usually use `/overview`.

### Update

Expand Down
Loading