Skip to content

Commit

Permalink
Merge branch 'main' into patch-8
Browse files Browse the repository at this point in the history
  • Loading branch information
jtoar authored Jul 7, 2022
2 parents 242214a + d17f929 commit 6e362a9
Show file tree
Hide file tree
Showing 43 changed files with 1,210 additions and 469 deletions.
19 changes: 19 additions & 0 deletions docs/docs/cli-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,7 @@ yarn redwood destroy <type>
| `sdl <model>` | Destroy a GraphQL schema and service component based on a given DB schema Model |
| `service <name>` | Destroy a service component |
| `directive <name>` | Destroy a directive |
| `graphiql` | Destroy a generated graphiql file |
## exec
Expand Down Expand Up @@ -1671,6 +1672,24 @@ yarn redwood setup auth <provider>
See [Authentication](authentication.md).
### setup graphiQL headers
Redwood automatically sets up your authentication headers in your GraphiQL playground. Currently supported auth providers include Supabase, dbAuth, and Netlify.
A `generateGraphiQLHeader` file will be created in your `api/lib` folder and included in your gitignore. You can edit this file to customize your header. The function in the file is passed into your `createGraphQLHandler` and only called in dev.
```
yarn redwood setup graphiql <provider>
```
| Arguments & Options | Description |
| :------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `provider` | Auth provider to configure. Choices are `dbAuth`, `netlify`, and `supabase` |
| `--id, -i` | Unique id to identify current user (required only for DBAuth) |
| `--token, -t` | Generated JWT token. If not provided, a mock JWT payload is returned in `api/lib/generateGraphiQLHeader` that can be modified and turned into a token |
| `--expiry, -e` | Token expiry in minutes. Default is 60 |
| `--view, -v` | Print out generated headers to console |
### setup custom-web-index
Redwood automatically mounts your `<App />` to the DOM, but if you want to customize how that happens, you can use this setup command to generate an `index.js` file in `web/src`.
Expand Down
42 changes: 42 additions & 0 deletions docs/docs/directives.md
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,26 @@ describe('isSubscriber directive', () => {
})
```
:::tip
If your Validator Directive is asynchronous, you can use `mockAsyncRedwoodDirective` instead.
```ts
import { mockAsyncRedwoodDirective } from '@redwoodjs/testing/api'
// ...
describe('isSubscriber directive', () => {
it('has a isSubscriber throws an error if validation does not pass', async () => {
const mockExecution = mockAsyncRedwoodDirective(isSubscriber, {})
await expect(mockExecution()).rejects.toThrowError(
'Implementation missing for isSubscriber'
)
})
})
```
:::
### Transformer
Let's create a `@maskedEmail` directive that checks roles to see if the user should see the complete email address or if it should be obfuscated from prying eyes:
Expand Down Expand Up @@ -654,3 +674,25 @@ describe('maskedEmail directive', () => {
})
})
```
:::tip
If your Transformer Directive is asynchronous, you can use `mockAsyncRedwoodDirective` instead.
```ts
import { mockAsyncRedwoodDirective } from '@redwoodjs/testing/api'
// ...
import maskedEmail from './maskedEmail'
describe('maskedEmail directive', () => {
it('has a maskedEmail implementation transforms the value', async () => {
const mockExecution = mockAsyncRedwoodDirective(maskedEmail, {
mockedResolvedValue: 'foo',
})
await expect(mockExecution()).resolves.toBe('bar')
})
})
```
:::
196 changes: 196 additions & 0 deletions docs/docs/router.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ The router is designed to list all routes in a single file, with limited nesting

The first thing you need is a `Router`. It will contain all of your routes. The router will attempt to match the current URL to each route in turn, and only render those with a matching `path`. The only exception to this is the `notfound` route, which can be placed anywhere in the list and only matches when no other routes do.

:::note The `notfound` route can't be nested in a `Set`

If you want to wrap your custom notfound page in a `Layout`, then you should add the `Layout` to the page instead. See [customizing the NotFoundPage](#customizing-the-notfoundpage).

:::

Each route is specified with a `Route`. Our first route will tell the router what to render when no other route matches:

```jsx title="Routes.js"
Expand Down Expand Up @@ -565,3 +571,193 @@ In order to display a loader while auth details are being retrieved you can add
</Private>
</Router>
```

## `FatalErrorPage`

Every Redwood project ships with a default `FatalErrorPage` located in `web/src/pages/FatalErrorPage`.
This page gets rendered when an error makes its way all the way to the top of your app without being handled by a catch block or a React error boundary.

Note that this page behaves differently in development than in production.

### In Development

In development, the `FatalErrorPage` provides helpful debugging information about the error and any GraphQL request that's involved.

For example, if there's a missing component that's causing an error, this's what you'll see:

![fatal_error_message](/img/router/fatal_error_message.png)

Or if the variable passed as a prop to a component can't be found:

![fatal_error_message_query](/img/router/fatal_error_message_query.png)

And if the page has a Cell, you'll see the Cell's request and response which may have contributed to the error:

![fatal_error_message_request](/img/router/fatal_error_request.png)

### In Production

By default, the `FatalErrorPage` in production is barebones:

![fatal_something_went_wrong](/img/router/fatal_something_went_wrong.png)

### Customizing the `FatalErrorPage`

You can customize the production `FatalErrorPage`, but it's important to keep things simple to avoid the possibility that it'll cause its own error.
If it does, the router still renders a generic error page, but your users will appreciate something a bit more thoughtful:

![fatal_something_went_wrong_custom](/img/router/fatal_something_went_wrong_custom.png)

```jsx title="web/src/pages/FatalErrorPage/FatalErrorPage.js"
import { Link, routes } from '@redwoodjs/router'

// ...

export default RedwoodDevFatalErrorPage ||
(() => (
<div className="bg-white min-h-full px-4 py-16 sm:px-6 sm:py-24 md:grid md:place-items-center lg:px-8">
<div className="max-w-max mx-auto">
<main className="sm:flex">
<p className="text-4xl font-extrabold text-blue-600 sm:text-5xl">
🤦‍♂️ Oops.
</p>
<div className="sm:ml-6">
<div className="sm:border-l sm:border-gray-200 sm:pl-6">
<h1 className="text-4xl font-extrabold text-gray-900 tracking-tight sm:text-5xl">
Something went wrong
</h1>
<p className="mt-1 text-base text-gray-500">
Sorry about that. Please contact support for help.
</p>
</div>
<div className="mt-10 flex space-x-3 sm:border-l sm:border-transparent sm:pl-6">
<Link
to={routes.home()}
className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
Home
</Link>
<Link
to={routes.support()}
className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-blue-700 bg-blue-100 hover:bg-blue-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
Contact Support
</Link>
</div>
</div>
</main>
</div>
</div>
))
```

Note that if you're copy-pasting this example, it uses [Tailwind CSS](https://tailwindcss.com), so you'll have to set that up first. See the [setup ui](./cli-commands.md#setup-ui) CLI command to add it to your project.

:::note Can I customize the development one?

As it's part of the RedwoodJS framework, you can't. But if there's a feature you want to add, let us know on the [forums](https://community.redwoodjs.com/).

:::

## `NotFoundPage`

Every Redwood project ships with a default `NotFoundPage` located in `web/src/pages/NotFoundPage`.

But just because it's called `NotFoundPage` doesn't mean the router knows that. The only way the router knows which page is the `NotFoundPage` is via the `notfound` prop, which tells the router what to render when no routes match:

```jsx title="web/src/Routes.js"
import { Router, Route } from '@redwoodjs/router'

const Routes = () => (
<Router>
// highlight-next-line
<Route notfound page={NotFoundPage} />
</Router>
)

export default Routes
```

### Customizing the `NotFoundPage`

By default, the `NotFoundPage` is a basic HTML page with internal styles:

```jsx title="web/src/pages/NotFoundPage/NotFoundPage.js"
export default () => (
<main>
// ... some custom css
<section>
<h1>
<span>404 Page Not Found</span>
</h1>
</section>
</main>
)
```

You're free to customize it however you like. You can change the markup and even use CSS or UI libraries to style it.
Here's an example using [Tailwind CSS](https://tailwindcss.com).
(See the [setup ui](./cli-commands.md#setup-ui) CLI command to add it to your project.)

![custom_not_found](/img/router/custom_not_found_page.png)

```jsx title="web/src/pages/NotFoundPage/NotFoundPage.js"
import { Link, routes } from '@redwoodjs/router'

export default () => (
<div className="bg-white min-h-full px-4 py-16 sm:px-6 sm:py-24 md:grid md:place-items-center lg:px-8">
<div className="max-w-max mx-auto">
<main className="sm:flex">
<p className="text-4xl font-extrabold text-red-600 sm:text-5xl">404</p>
<div className="sm:ml-6">
<div className="sm:border-l sm:border-gray-200 sm:pl-6">
<h1 className="text-4xl font-extrabold text-gray-900 tracking-tight sm:text-5xl">
Page not found
</h1>
<p className="mt-1 text-base text-gray-500">
Check the URL in the address bar and please try again.
</p>
</div>
<div className="mt-10 flex space-x-3 sm:border-l sm:border-transparent sm:pl-6">
<Link
to={routes.home()}
className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
>
Home
</Link>
<Link
to={routes.support()}
className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-red-700 bg-red-100 hover:bg-red-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
>
Get Help
</Link>
</div>
</div>
</main>
</div>
</div>
)
```

While the `notfound` route can't be nested in a `Set` like other routes, you can still wrap it in Layouts by importing them into the page:

```jsx title="web/src/pages/NotFoundPage/NotFoundPage.js"
// highlight-next-line
import MainLayout from 'src/layouts/MainLayout/MainLayout'

export default () => (
// highlight-next-line
<MainLayout>
<main>
<section>
<h1>
<span>404 Page Not Found</span>
</h1>
</section>
</main>
// highlight-next-line
</MainLayout>
)
```

This means that the `NotFoundPage` can use Redwood features like Cells or auth to construct navigation options or detailed header and footer content to help your users find their way back to the main application.
25 changes: 23 additions & 2 deletions docs/docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -1239,9 +1239,27 @@ To simplify Service testing, rather than mess with your development database, Re
If you're using Postgres or MySQL locally you'll want to set that env var to your connection string for a test database in those services.
> Does anyone else find it confusing that the software itself is called a "database", but the container that actually holds your data is also called a "database," and you can have multiple databases (containers) within one instance of a database (software)?
:::info
Does anyone else find it confusing that the software itself is called a "database", but the container that actually holds your data is also called a "database," and you can have multiple databases (containers) within one instance of a database (software)?
:::
When you start your test suite you may notice some output from Prisma talking about migrating the database. Redwood will automatically run `yarn rw prisma db push` against your test database to make sure it's up-to-date.
:::caution What if I have custom migration SQL?
The `prisma db push` command only restores a snapshot of the current database schema (so that it runs as fast as possible). **It does not actually run migrations in sequence.** This can cause a [problem](https://github.com/redwoodjs/redwood/issues/5818) if you have certain database configuration that *must* occur as a result of the SQL statements inside the migration files.
When you start your test suite you may notice some output from Prisma talking about migrating the database. Redwood will automatically run `yarn rw prisma migrate dev` against your test database to make sure it's up-to-date.
In order to preserve those statements in your test database, you can set an additional ENV var which will use the command `yarn rw prisma migrate reset` instead. This will run each migration in sequence against your test database. The tradeoff is that starting your test suite will take a little longer depending on how many migrations you have:
```.env title=/.env
TEST_DATABASE_STRATEGY=reset
```
Set the variable to `push`, or remove it completely, and it will use the default behavior of running `yarn rw prisma db push`.
:::
### Writing Service Tests
Expand Down Expand Up @@ -1703,6 +1721,9 @@ Luckily, RedwoodJS has several api testing utilities to make [testing functions
>
> We have an entire testing section in the [Serverless Functions documentation](serverless-functions.md) that will walk your through an example of a function and a webhook.
## Testing GraphQL Directives
Please refer to the [Directives documentation](./directives.md) for details on how to write Redwood [Validator](./directives.md#writing-validator-tests) or [Transformer](./directives.md#writing-transformer-tests) Directives tests.
## Wrapping Up
So that's the world of testing according to Redwood. Did we miss anything? Can we make it even more awesome? Stop by [the community](https://community.redwoodjs.com) and ask questions, or if you've thought of a way to make this doc even better then [open a PR](https://github.com/redwoodjs/redwoodjs.com/pulls).
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/tutorial/chapter2/cells.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ As you'll see repeatedly going forward, Redwood has a generator for this feature
yarn rw g cell Articles
```

This command will result in a new file at `/web/src/components/ArticlesCell/ArticlesCell.{js,tsx}` (and `test.{js,tsx}` `mock.{js,ts}` and `stories.{js,tsx}` files—more on those in [chapter5](../chapter5/storybook.md) of the tutorial!). This file will contain some boilerplate to get you started:
This command will result in a new file at `/web/src/components/ArticlesCell/ArticlesCell.{js,tsx}` (and `test.{js,tsx}` `mock.{js,ts}` and `stories.{js,tsx}` files—more on those in [chapter 5 of the tutorial](../chapter5/storybook.md)!). This file will contain some boilerplate to get you started:

<Tabs groupId="js-ts">
<TabItem value="js" label="JavaScript">
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/tutorial/chapter2/side-quest.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Redwood likes GraphQL. We think it's the API of the future. Our GraphQL implemen

![Redwood Data Flow](https://user-images.githubusercontent.com/300/75402679-50bdd180-58ba-11ea-92c9-bb5a5f4da659.png)

The front-end uses [Apollo Client](https://www.apollographql.com/docs/react/) to create a GraphQL payload sent to [GraphQL Yoga](https://www.graphql-yoga.com) and [Envelop](https://www.envelop.dev/docs), for which that `graphql.{js,ts}` file acts as the entry-point to.
The front-end uses [Apollo Client](https://www.apollographql.com/docs/react/) to create a GraphQL payload sent to [GraphQL Yoga](https://www.graphql-yoga.com) and [Envelop](https://www.envelop.dev/docs), which that `graphql.{js,ts}` file acts as the entry-point to.

The `*.sdl.{js,ts}` files in `api/src/graphql` define the GraphQL [Object](https://www.apollographql.com/docs/tutorial/schema/#object-types), [Query](https://www.apollographql.com/docs/tutorial/schema/#the-query-type) and [Mutation](https://www.apollographql.com/docs/tutorial/schema/#the-mutation-type) types and thus the interface of your API.

Expand Down
12 changes: 7 additions & 5 deletions docs/docs/tutorial/foreword.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@

Welcome to Redwood! If you haven't yet, check out the [Redwood README](https://github.com/redwoodjs/redwood/blob/main/README.md) to get a little background on why we created Redwood and the problems it's meant to solve. Redwood brings several existing technologies together for the first time into what we think is the future of database-backed single page applications.

In this tutorial we're going to build a blog engine. In reality a blog is probably not the ideal candidate for a Redwood app: blog articles can be stored in a CMS and statically generated to HTML files and served as flat files from a CDN (the classic [Jamstack](https://jamstack.org/) use case). But as most developers are familiar with a blog, and it uses all of the features we want to demonstrate, we decided to build one anyway.

If you went through an earlier version of this tutorial you may remember it being split into parts 1 and 2. That was an artifact of the fact that most features demonstrated in part 2 didn't exist in the framework when part 1 was written. Once they were added we created part 2 to contain just those new features. Now that everything is integrated and working well we've moved each section into logically grouped chapters.

## Sign Up for Tutorial Reminders
:::info Sign up for tutorial reminders

There's a new Javascript framework coming out every week, we know it can be hard to keep up. If you'd like some non-spammy emails reminding you to go through the tutorial, give us your email below:

<MailchimpForm />

:::

In this tutorial we're going to build a blog engine. In reality a blog is probably not the ideal candidate for a Redwood app: blog articles can be stored in a CMS and statically generated to HTML files and served as flat files from a CDN (the classic [Jamstack](https://jamstack.org/) use case). But as most developers are familiar with a blog, and it uses all of the features we want to demonstrate, we decided to build one anyway.

If you went through an earlier version of this tutorial you may remember it being split into parts 1 and 2. That was an artifact of the fact that most features demonstrated in part 2 didn't exist in the framework when part 1 was written. Once they were added we created part 2 to contain just those new features. Now that everything is integrated and working well we've moved each section into logically grouped chapters.

## Callouts

You'll find some callouts throughout the text to draw your attention:
Expand Down
Binary file added docs/static/img/router/custom_not_found_page.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/static/img/router/fatal_error_message.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/static/img/router/fatal_error_request.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 6e362a9

Please sign in to comment.