Skip to content

Commit 3c54163

Browse files
committed
Merge branch 'master' of github.com:getsentry/sentry-docs into smi/quick-start/review-nestjs
2 parents 352fc65 + 3601e00 commit 3c54163

File tree

205 files changed

+2373
-1042
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

205 files changed

+2373
-1042
lines changed

data-schemas

Submodule data-schemas deleted from 6d2c435

develop-docs/backend/api/design.mdx

Lines changed: 39 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ Use the following guidelines for naming resources and their collections:
3131

3232
- **Do** use lowercase and hyphenated collection names, e.g. `commit-files`.
3333
- **Do** use plural collection names. Avoid using uncountable words because the user can't know whether the GET returns one item or a list.
34-
- **Do** use `snake_case` for path parameters. e.g. `tags/\{tag_name}/`.
35-
- **Do** consistently shorten parameters that are excessively long when the term will unambiguous. e.g. `organization` -> `org`.
34+
- **Do** use `snake_case` for path parameters. e.g. `tags/{tag_name}/`.
35+
- **Do** consistently shorten parameters that are excessively long when the term is unambiguous. e.g. `organization` -> `org`.
3636

3737
Standard path parameters that should be shortened in routes:
3838

@@ -42,8 +42,8 @@ Standard path parameters that should be shortened in routes:
4242

4343
Information in Sentry is typically constrained by tenants. That is, almost all information is scoped to an organization. All endpoints which query customer data **must** be scoped to an organization:
4444

45-
- **Do** prefix resource organizations collections with `organizations/\{org}`.
46-
- **Do** prefix resource project collections with `projects/\{org}/\{project}`.
45+
- **Do** prefix organization resource collections with `/organizations/{org}/`.
46+
- **Do** prefix project resource collections with `/projects/{org}/{project}/`.
4747
- **Do not** expose endpoints which require `org` as a query parameter (it should always be a path parameter).
4848

4949
Knowing when to choose which constraint to couple an endpoint to will be based on the purpose of an endpoint. For example, if an endpoint is only ever going to be used to query data for a single project, it should be prefixed with `/projects/{org}/{project}/things`. If an endpoint would need to exist to query multiple projects (which is common with cross-project queries), you likely should expose it as `/organizations/{org}/things`, and expose a query param to filter on the project(s).
@@ -57,34 +57,34 @@ Exceptions to these rules include:
5757

5858
**Do not** exceed three levels of resource nesting.
5959

60-
Nesting resources such as `/organizations/\{org}/projects/`, is **preferred** over flattened resources like `/0/projects/`. This improves readability and exposes a natural understanding of resource hierarchy and relationships. However, nesting can make URLs too long and hard to use. Sentry uses 3-level nesting as a hybrid solution.
60+
Nesting resources such as `/organizations/{org}/projects/`, is **preferred** over flattened resources like `/0/projects/`. This improves readability and exposes a natural understanding of resource hierarchy and relationships. However, nesting can make URLs too long and hard to use. Sentry uses 3-level nesting as a hybrid solution.
6161

6262
Here are some possible urls for values with this resource hierarchy: organization -> project -> tag -> value:
6363

64-
- 👍 `/projects/\{org}/\{project}/tags/\{tag}/values`
65-
- 👎 `/organizations/\{org}/projects/\{project}/tags/\{tag}/values/`
64+
- 👍 `/projects/{org}/{project}/tags/{tag}/values`
65+
- 👎 `/organizations/{org}/projects/{project}/tags/{tag}/values/`
6666
- 👎 `/values/`
6767

6868
Hierarchy here does not necessarily mean that one collection belongs to a parent collection, it simply implies a relationship. For example:
6969

70-
- `projects/\{project_identifier}/teams/` refers to the **teams** that have been added to specific project
71-
- `teams/\{team_identifier}/projects/` refers to the **projects** a specific team has been added to
70+
- `/projects/{project_identifier}/teams/` refers to the **teams** that have been added to specific project
71+
- `/teams/{team_identifier}/projects/` refers to the **projects** a specific team has been added to
7272

7373
## Parameter Design
7474

7575
- **Do** use `camelCase` for query params and request body params. e.g. `/foo/?userId=123`.
76-
- **Do** use `camelCase` for all response attributes. e.g. `\{userId: "123"}`.
76+
- **Do** use `camelCase` for all response attributes. e.g. `{userId: "123"}`.
7777

7878
For consistency, we also try to re-use well known parameters across endpoints.
7979

80-
- **Do** use `sortBy` for sorting. e.g. `sortBy=-dateCreated`.
81-
- **Do** use `orderBy` for ordering. e.g. `orderBy=asc` or `orderBy=desc`.
82-
- **Do** use `limit` for limiting the number of results returned. e.g. `limit=10`.
80+
- **Do** use `sortBy` for sorting. e.g. `?sortBy=-dateCreated`.
81+
- **Do** use `orderBy` for ordering. e.g. `?orderBy=asc` or `?orderBy=desc`.
82+
- **Do** use `limit` for limiting the number of results returned. e.g. `?limit=10`.
8383
- **Do** use `cursor` for pagination.
8484

8585
### Resource Identifiers
8686

87-
Identifiers exist both within the route (`/organizations/\{organization}/projects/`) as well as within other parameters such as query strings (`organization=123`) and request bodies (`\{organization: "123"}`).
87+
Identifiers exist both within the route (`/organizations/{organization}/projects/`) as well as within other parameters such as query strings (`?organization=123`) and request bodies (`{organization: "123"}`).
8888

8989
The most important concern here is to ensure that a single identifier is exposed to key to resources. For example, it is preferred to use `organization` and accept both `organization_id` and `organization_slug` as valid identifiers.
9090

@@ -119,24 +119,24 @@ POST /resources/{id}
119119

120120
### Batch Operations
121121

122-
Resources can get complicated when you need to expose batch operations vs single resource operations. For batch operations it it is preferred to expose them as a `POST` request on the collection when possible.
122+
Resources can get complicated when you need to expose batch operations vs single resource operations. For batch operations it is preferred to expose them as a `POST` request on the collection when possible.
123123

124124
Let's say for example we have an endpoint that mutates an issue:
125125

126126
```
127-
POST /api/0/organizations/:org/issues/:issue/
127+
POST /api/0/organizations/{org}/issues/{issue}/
128128
```
129129

130130
When designing a batch interface, we simply expose it on the collection instead of the individual resource:
131131

132132
```
133-
POST /api/0/organizations/:org/issues/
133+
POST /api/0/organizations/{org}/issues/
134134
```
135135

136136
You may also need to expose selectors on batch resources, which can be done through normal request parameters:
137137

138138
```
139-
POST /api/0/organizations/:org/issues/
139+
POST /api/0/organizations/{org}/issues/
140140
{
141141
"issues": [1, 2, 3]
142142
}
@@ -166,7 +166,7 @@ Here are some examples of how to use standard methods to represent complex tasks
166166

167167
**Retrieve statistics for a resource**
168168

169-
The best approach here is to encoded it as an attribute in the resource:
169+
The best approach here is to encode it as an attribute in the resource:
170170

171171
```
172172
GET /api/0/projects/{project}/
@@ -182,7 +182,7 @@ In some cases this will be returned as part of an HTTP header, specifically for
182182

183183
Order and filtering should happen as part of list api query parameters. Here's a [good read](https://www.moesif.com/blog/technical/api-design/REST-API-Design-Filtering-Sorting-and-Pagination/).
184184

185-
- **Do** rely on `orderBy` and `sortBy`. e.g. `/api/0/issues/\{issue_id}/events?orderBy=-date`
185+
- **Do** rely on `orderBy` and `sortBy`. e.g. `/api/0/issues/{issue_id}/events?orderBy=-date`
186186
- **Do not** create dedicated routes for these behaviors.
187187

188188
## Responses
@@ -191,13 +191,13 @@ Each response object returned from an API should be a serialized version of the
191191

192192
Some guidelines around the shape of responses:
193193

194-
- **Do** use `camelCase` for all response attributes. e.g. `\{numCount: "123"}`.
195-
- **Do** return a responses as a named resource (e.g. `\{"user": \{"id": "123"}}`).
196-
- **Do** indicate collections using plural nouns (e.g. `\{"users": []}`).
194+
- **Do** use `camelCase` for all response attributes. e.g. `{"numCount": "123"}`.
195+
- **Do** return a responses as a named resource (e.g. `{"user": {"id": "123"}}`).
196+
- **Do** indicate collections using plural nouns (e.g. `{"users": []}`).
197197
- **Do not** return custom objects. **Do** use a `Serializer` to serialize the resource.
198198
- **Do** return the smallest amount of data necessary to represent the resource.
199199

200-
Additionally because JavaScript is a primary consumer, be mindful of the restrictions are things like numbers. Generally speaking:
200+
Additionally because JavaScript is a primary consumer, be mindful of the restrictions on things like numbers. Generally speaking:
201201

202202
- **Do** return resource identifiers (even numbers) as strings.
203203
- **Do** return decimals as strings.
@@ -222,7 +222,7 @@ Whereas our guidelines state it should be nested:
222222
GET /api/0/projects/{project}/
223223
{
224224
"project": {
225-
"id": 5,
225+
"id": "5",
226226
"name": "foo",
227227
...
228228
}
@@ -273,13 +273,13 @@ GET /api/0/projects/{project}/teams
273273
[
274274
{
275275
"id": 1,
276-
"name": "Team 1",
277-
"slug": "team1",
276+
"name": "Team 1",
277+
"slug": "team1",
278278
},
279-
{
279+
{
280280
"id": 2,
281-
"name": "Team 2",
282-
"slug": "team2",
281+
"name": "Team 2",
282+
"slug": "team2",
283283
}
284284
]
285285

@@ -297,17 +297,11 @@ GET /api/0/projects/{project}/
297297
"id": 5,
298298
"name": "foo",
299299
"stats": {
300-
"24h": [
301-
[
302-
1629064800,
303-
27
304-
],
305-
[
306-
1629068400,
307-
24
308-
],
309-
...
310-
]
300+
"24h": [
301+
[1629064800, 27],
302+
[1629068400, 24],
303+
...
304+
]
311305
}
312306
}
313307
```
@@ -330,7 +324,9 @@ This is typically only needed if the endpoint is already public and we do not wa
330324
>> APIs often need to provide collections of data, most commonly in the `List` standard method. However, collections can be arbitrarily sized, and tend to grow over time, increasing lookup time as well as the size of the responses being sent over the wire. This is why it's important for collections to be paginated.
331325
332326
Paginating responses is a [standard practice for APIs](https://google.aip.dev/158), which Sentry follows.
327+
333328
We've seen an example of a `List` endpoint above; these endpoints have two tell-tale signs:
329+
334330
```json
335331
GET /api/0/projects/{project}/teams
336332
[
@@ -347,12 +343,14 @@ GET /api/0/projects/{project}/teams
347343
]
348344

349345
```
346+
350347
1. The endpoint returns an array, or multiple, objects instead of just one.
351348
2. The endpoint can sometimes end in a plural (s), but more importantly, it does __not__ end in an identifier (`*_slug`, or `*_id`).
352349

353350
To paginate a response at Sentry, you can leverage the [`self.paginate`](https://github.com/getsentry/sentry/blob/24.2.0/src/sentry/api/base.py#L463-L476) method as part of your endpoint.
354351
`self.paginate` is the standardized way we paginate at Sentry, and it helps us with unification of logging and monitoring.
355352
You can find multiple [examples of this](https://github.com/getsentry/sentry/blob/24.2.0/src/sentry/api/endpoints/api_applications.py#L22-L33) in the code base. They'll look something like:
353+
356354
```python
357355
def get(self, request: Request) -> Response:
358356
queryset = ApiApplication.objects.filter(

develop-docs/backend/application-domains/database-migrations/index.mdx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,12 @@ generally prefer that, since it averages the load out over a longer period of ti
135135

136136
### Indexes
137137

138-
We prefer to create indexes on large existing tables with `CREATE INDEX CONCURRENTLY`. Our migration framework will do this automatically when creating
139-
a new index. Note that when `CONCURRENTLY` is used we can't run the migration in a transaction, so it's important to use `atomic = False` to run these.
138+
These are automatically handled by the migration framework, you can just create them on the Django model and generate the migration.
140139

141140
When adding indexes to large tables you should use a `is_post_deployment` migration as creating the index could take longer than the migration statement timeout of 5s.
142141

142+
Note: These are created using `CONCURRENTLY`, so it's important to not set `atomic = True` for migrations that contain indexes. This is disabled by default.
143+
143144
### Deleting columns
144145

145146
This is complicated due to our deploy process. When we deploy, we run migrations, and then push out the application code, which takes a while. This means that if we just delete a column or model, then code in sentry will be looking for those columns/tables and erroring until the deploy completes. In some cases, this can mean Sentry is hard down until the deploy is finished.
@@ -419,7 +420,7 @@ This second PR will contain only the migration and related boilerplate.
419420

420421
### Foreign Keys
421422

422-
Creating foreign keys is mostly fine, but for some large/busy tables like `Project`, `Group` it can cause problems due to difficulties in acquiring a lock. You can still create a Django level foreign key though, without creating a database constraint. To do so, set `db_constraint=False` when defining the key.
423+
Creating foreign keys is mostly fine, but for some large/busy tables like `Project` and `Group` the migration can fail due to difficulties in acquiring a lock on the table. The general procedure here is to retry the migration until it goes through - The migration framework adds a lock acquisition timeout, so it's safe to do this.
423424

424425
### Renaming Tables
425426

develop-docs/development-infrastructure/devservices.mdx

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,23 +84,62 @@ devservices up --mode symbolicator
8484
8585
## Running a dependency locally
8686
87-
If you want to run a dependency locally rather than as a container, you can do so by toggling the runtime to LOCAL.
87+
You can run a dependency locally instead of as a container by toggling its runtime via the `devservices toggle` command.
8888
89-
For example, if you are running Sentry and want to use your local Snuba, you can do the following:
89+
### Toggle to local runtime
90+
91+
For example, if you're running Sentry and want to run Snuba **locally** from source, you can toggle Snuba to the local runtime like this:
92+
93+
```shell
94+
devservices toggle snuba
95+
```
96+
97+
or you can specify the runtime explicitly:
9098

9199
```shell
92100
devservices toggle snuba LOCAL
93101
```
94102

95-
This will tell devservices to not bring up snuba and its dependencies, allowing you to run snuba locally instead.
103+
This tells devservices to treat Snuba as a service that should be started alongside dependent services like Sentry. Dependencies of Snuba, such as Redis, Kafka, and Clickhouse, will still run as containers.
104+
105+
<Alert title="Note">
106+
Toggling to LOCAL does not start the dev server for that service. You'll need to start that manually.
107+
</Alert>
108+
109+
### What happens when you toggle
110+
111+
If Sentry is already running, toggling Snuba to LOCAL will:
112+
1. Stop Snuba's containers (unless shared by another service)
113+
2. Start Snuba's non-local dependencies (Redis, Kafka, Clickhouse) as containers
114+
115+
If Sentry is not yet running, the next time `devservices up` is run, Snuba (and any other services you toggle to LOCAL) will be automatically started as local services alongside Sentry.
116+
117+
If you want to bring up Snuba, or other local runtime dependencies in a non-default mode, you can:
118+
1. Tell devservices to not bring them up automatically by passing the `--exclude-local` flag to `devservices up`.
119+
2. Bring that mode up manually via `devservices up --mode <mode>` since modes are additive.
120+
3. Bring that service down and back up again with the new mode.
121+
122+
### Bringing down services with local runtimes
123+
124+
When stopping services with `devservices down`, the default behavior is to stop all services and their dependencies, including dependencies with local runtimes.
125+
126+
If you don't want dependencies with local runtimes to be brought down when bringing down a dependent service, you can pass the `--exclude-local` flag to `devservices down`. This tells devservices to only stop dependencies that are not running with local runtimes.
127+
128+
### Toggle back to containerized runtime
129+
130+
To switch a service back to running in a container, you can do the following:
131+
132+
```shell
133+
devservices toggle snuba
134+
```
96135

97-
To toggle the runtime back to using the container, you can do the following:
136+
Or explicitly specify the runtime:
98137

99138
```shell
100139
devservices toggle snuba CONTAINERIZED
101140
```
102141

103-
If you don't provide a runtime when toggling, it will toggle to the opposite of the current runtime.
142+
Replace `snuba` with the name of the service you want to toggle.
104143

105144
## Migrating data from the deprecated `sentry devservices`
106145

develop-docs/frontend/using-rtl.mdx

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,23 +32,53 @@ const organization = OrganizationFixture({access: ['org:admin'], features: ['my-
3232
render(<Example />, {organization});
3333
```
3434

35-
Router context can be overriden in the same way.
35+
## Testing route changes
36+
37+
When using `render()`, an in-memory router is used, which will react to navigations with `useNavigate()` or interations with `Link` components. If your component relies on the URL, you can define the initial state in `initialRouterConfig`. You can access the current router state by referencing the returned `router` class, as well as navigate programmatically with `router.navigate()`.
3638

3739
```tsx
38-
const {organization, router, routerContext} = initializeOrg({
39-
organization: {features: ['global-views', 'open-membership']},
40-
router: {
40+
const {router} = render(<TestComponent />, {
41+
initialRouterConfig: {
4142
location: {
42-
pathname: '/organizations/org-slug/issues/',
43-
query: {environment: 'prod'},
43+
pathname: '/foo/',
44+
query: {page: '1'},
4445
},
45-
params: {},
4646
},
4747
});
4848

49-
render(<Example />, {context: routerContext, organization});
50-
await userEvent.click(something);
51-
expect(router.push).toHaveBeenCalledTimes(1);
49+
// Uses passes in config to set initial location
50+
expect(router.location.pathname).toBe('/foo');
51+
expect(router.location.query.page).toBe('1');
52+
53+
// Clicking links goes to the correct location
54+
await userEvent.click(screen.getByRole('link', {name: 'Go to /bar/'}));
55+
56+
// Can check current route on the returned router
57+
expect(router.location.pathname).toBe('/bar/');
58+
59+
// Can test manual route changes with router.navigate
60+
router.navigate('/new/path/');
61+
router.navigate(-1); // Simulates clicking the back button
62+
```
63+
64+
If you need to test route param values (as in `useParams()`), the `route` will need to be provided in the config:
65+
66+
```tsx
67+
function TestComponent() {
68+
const {id} = useParams();
69+
return <div>{id}</div>;
70+
}
71+
72+
const {router} = render(<TestComponent />, {
73+
initialRouterConfig: {
74+
location: {
75+
pathname: '/foo/123/',
76+
},
77+
route: '/foo/:id/',
78+
}
79+
});
80+
81+
expect(screen.getByText('123')).toBeInTheDocument();
5282
```
5383

5484
## Querying

0 commit comments

Comments
 (0)