Skip to content

Commit c1cb984

Browse files
committed
Fixes from going through the tutorial
1 parent edf0252 commit c1cb984

File tree

4 files changed

+299
-104
lines changed

4 files changed

+299
-104
lines changed

docs/intro/quickstart/access.rst

Lines changed: 120 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,48 @@ Adding Access Control
6161
}
6262
}
6363
64+
.. edb:split-section::
65+
66+
We will create this migration which will also trigger the query builder to be regenerated.
67+
68+
.. code-block:: sh
69+
70+
$ npx gel migration create
71+
did you create global 'default::access_token'? [y,n,l,c,b,s,q,?]
72+
> y
73+
did you create object type 'default::User'? [y,n,l,c,b,s,q,?]
74+
> y
75+
did you create object type 'default::AccessToken'? [y,n,l,c,b,s,q,?]
76+
> y
77+
did you create global 'default::current_user'? [y,n,l,c,b,s,q,?]
78+
> y
79+
did you alter object type 'default::Deck'? [y,n,l,c,b,s,q,?]
80+
> y
81+
did you create access policy 'deck_creator_has_full_access' of object type 'default::Card'? [y,n,l,c,b,s,q,?]
82+
> y
83+
Created /home/strinh/projects/flashcards/dbschema/migrations/00003-m1solvt.edgeql, id: m1solvta35uzsbs4axzqmkwfx7zatjtkozpr43cjs56fp75qzbrg5q
84+
85+
$ npx gel migrate
86+
Applying m1solvta35uzsbs4axzqmkwfx7zatjtkozpr43cjs56fp75qzbrg5q (00003-m1solvt.edgeql)
87+
... parsed
88+
... applied
89+
Generating query builder...
90+
Detected tsconfig.json, generating TypeScript files.
91+
To override this, use the --target flag.
92+
Run `npx @gel/generate --help` for full options.
93+
Introspecting database schema...
94+
Generating runtime spec...
95+
Generating cast maps...
96+
Generating scalars...
97+
Generating object types...
98+
Generating function types...
99+
Generating operators...
100+
Generating set impl...
101+
Generating globals...
102+
Generating index...
103+
Writing files to ./dbschema/edgeql-js
104+
Generation complete! 🤘
105+
64106
.. edb:split-section::
65107
66108
Let's create a page for creating a new user and getting an access token. Let's start by creating the query to create a new user which will return the ``AccessToken.id`` which we will use as the access token itself. We will save this access token in a cookie so that we can authenticate requests in other server actions and route handlers.
@@ -151,7 +193,6 @@ Adding Access Control
151193
);
152194
}
153195

154-
155196
.. edb:split-section::
156197
157198
We should see this page when we navigate to the signup page.
@@ -186,23 +227,74 @@ Limiting access
186227
187228
.. edb:split-section::
188229
189-
Along with allowing us to take advantage of our access policies in our queries, this will also allow us to redirect unauthenticated users to the signup page from any of our pages which should require authentication. Let's update our ``page.tsx`` file to redirect to the signup page if the user is not authenticated.
230+
Along with allowing us to take advantage of our access policies in our queries, this will also allow us to redirect unauthenticated users to the signup page from any of our pages which should require authentication. Let's update our ``page.tsx`` file to redirect to the signup page if the user is not authenticated. We will also show the list of decks on this page.
190231

191-
.. code-block:: typescript-diff
192-
:caption: app/page.tsx
232+
.. tabs::
193233

194-
import { ImportForm } from "./form";
195-
+ import { getAuthenticatedClient } from "@/lib/gel";
196-
+ import { redirect } from "next/navigation";
234+
.. code-tab:: typescript-diff
235+
:caption: app/actions.ts
197236

198-
export default async function Page() {
199-
+ const client = await getAuthenticatedClient();
200-
+ if (!client) {
201-
+ redirect("/signup");
202-
+ }
203-
+
204-
return <ImportForm />;
205-
}
237+
"use server";
238+
- import { client } from "@/lib/gel";
239+
+ import { getAuthenticatedClient } from "@/lib/gel";
240+
import { createDeck } from "./create-deck.query";
241+
+ import e from "@/dbschema/edgeql-js";
242+
243+
export async function importDeck(formData: FormData) {
244+
const deck = formData.get("deck");
245+
if (typeof deck !== "string") {
246+
return;
247+
}
248+
+
249+
+ const client = await getAuthenticatedClient();
250+
+ if (!client) {
251+
+ return;
252+
+ }
253+
254+
await createDeck(client, JSON.parse(deck));
255+
}
256+
+
257+
+ export async function getDecks() {
258+
+ const client = await getAuthenticatedClient();
259+
+ if (!client) {
260+
+ return [];
261+
+ }
262+
+
263+
+ return e.select(e.Deck, (d) => ({
264+
+ id: true,
265+
+ name: true,
266+
+ })).run(client);
267+
+ }
268+
269+
.. code-tab:: typescript-diff
270+
:caption: app/page.tsx
271+
272+
import { ImportForm } from "./form";
273+
+ import { getAuthenticatedClient } from "@/lib/gel";
274+
+ import { redirect } from "next/navigation";
275+
+ import { getDecks } from "./actions";
276+
277+
export default async function Page() {
278+
+ const client = await getAuthenticatedClient();
279+
+ if (!client) {
280+
+ redirect("/signup");
281+
+ }
282+
+
283+
+ const decks = await getDecks(client);
284+
+
285+
- return <ImportForm />;
286+
+ return (
287+
+ <div>
288+
+ <h1>Decks</h1>
289+
+ <ul>
290+
+ {decks.map((deck) => (
291+
+ <li key={deck.id}>{deck.name}</li>
292+
+ ))}
293+
+ </ul>
294+
+ <ImportForm />
295+
+ </div>
296+
+ );
297+
}
206298

207299
.. edb:split-section::
208300
@@ -274,14 +366,15 @@ Limiting access
274366
.. code-block:: typescript-diff
275367
:caption: app/deck/[id]/page.tsx
276368
277-
import { redirect } from "next/navigation";
369+
import { notFound } from "next/navigation";
278370
- import { client } from "@/lib/gel";
279371
+ import { getAuthenticatedClient } from "@/lib/gel";
280372
import e from "@/dbschema/edgeql-js";
373+
import { Fragment } from "react";
281374
282-
const getDeckQuery = e.params({ deckId: e.uuid }, (params) =>
375+
const getDeckQuery = e.params({ id: e.uuid }, (params) =>
283376
e.select(e.Deck, (d) => ({
284-
filter_single: e.op(d.id, "=", params.deckId),
377+
filter_single: e.op(d.id, "=", params.id),
285378
id: true,
286379
name: true,
287380
description: true,
@@ -301,30 +394,30 @@ Limiting access
301394
export default async function DeckPage(
302395
{ params }: { params: Promise<{ id: string }> }
303396
) {
304-
const { id: deckId } = await params;
397+
const { id } = await params;
305398
+ const client = await getAuthenticatedClient();
306399
+ if (!client) {
307-
+ redirect("/signup");
400+
+ notFound();
308401
+ }
309-
310-
const deck = await getDeckQuery.run(client, { deckId });
402+
+
403+
const deck = await getDeckQuery.run(client, { id });
311404
312405
if (!deck) {
313-
redirect("/");
406+
notFound();
314407
}
315408
316409
return (
317410
<div>
318411
<h1>{deck.name}</h1>
319412
<p>{deck.description}</p>
320-
<ul>
413+
<dl>
321414
{deck.cards.map((card) => (
322-
<dl key={card.id}>
415+
<Fragment key={card.id}>
323416
<dt>{card.front}</dt>
324417
<dd>{card.back}</dd>
325-
</dl>
418+
</Fragment>
326419
))}
327-
</ul>
420+
</dl>
328421
</div>
329422
)
330423
}

docs/intro/quickstart/dynamic.rst

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,17 @@ The second approach using dynamic queries tends to be more performant and mainta
2626

2727
import { revalidatePath } from "next/cache";
2828
import e from "@/dbschema/edgeql-js";
29+
import { getAuthenticatedClient } from "@/lib/gel";
2930

3031
export async function updateDeck(data: FormData) {
3132
const id = data.get("id");
3233
if (!id) {
33-
return;
34+
throw new Error("Missing deck ID");
35+
}
36+
37+
const client = await getAuthenticatedClient();
38+
if (!client) {
39+
throw new Error("Unauthorized");
3440
}
3541

3642
const name = data.get("name");
@@ -44,7 +50,7 @@ The second approach using dynamic queries tends to be more performant and mainta
4450

4551
await e
4652
.update(e.Deck, (d) => ({
47-
filter_single: e.op(d.id, "=", id),
53+
filter_single: e.op(d.id, "=", e.uuid(id)),
4854
set: {
4955
...nameSet,
5056
...descriptionSet,
@@ -109,11 +115,11 @@ The second approach using dynamic queries tends to be more performant and mainta
109115
+ />
110116
+ <input
111117
+ name="name"
112-
+ initialValue={deck.name}
118+
+ defaultValue={deck.name}
113119
+ />
114120
+ <textarea
115121
+ name="description"
116-
+ initialValue={deck.description}
122+
+ defaultValue={deck.description}
117123
+ />
118124
+ <button type="submit">Update</button>
119125
+ </form>

docs/intro/quickstart/inheritance.rst

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,20 @@ Adding Shared Properties
2727
+ }
2828
+
2929
- type User {
30-
+ type User extends Timestamped {
30+
+ type User extending Timestamped {
3131
required name: str;
3232
}
3333
3434
- type AccessToken {
35-
+ type AccessToken extends Timestamped {
35+
+ type AccessToken extending Timestamped {
3636
required user: User;
3737
required token: str {
3838
constraint exclusive;
3939
};
4040
}
4141
4242
- type Deck {
43-
+ type Deck extends Timestamped {
43+
+ type Deck extending Timestamped {
4444
required name: str;
4545
description: str;
4646
@@ -56,7 +56,7 @@ Adding Shared Properties
5656
};
5757
5858
- type Card {
59-
+ type Card extends Timestamped {
59+
+ type Card extending Timestamped {
6060
required order: int64;
6161
required front: str;
6262
required back: str;
@@ -73,19 +73,30 @@ Adding Shared Properties
7373
7474
.. edb:split-section::
7575
76-
When we create a migration, we need to set initial values for the ``created_at`` and ``updated_at`` properties on all existing objects. Since we don't have historical data for when these objects were actually created or modified, we'll set both timestamps to the current time when the migration runs by using ``datetime_of_statement()``.
76+
When we create a migration, we need to set initial values for the ``created_at`` and ``updated_at`` properties on all existing objects. Since we don't have historical data for when these objects were actually created or modified, the migration will fall back to the default values we set in the ``Timestamped`` type.
7777

7878
.. code-block:: sh
7979
8080
$ npx gel migration create
81-
fill_expr> datetime_of_statement()
81+
did you create object type 'default::Timestamped'? [y,n,l,c,b,s,q,?]
82+
> y
83+
did you alter object type 'default::AccessToken'? [y,n,l,c,b,s,q,?]
84+
> y
85+
did you alter object type 'default::User'? [y,n,l,c,b,s,q,?]
86+
> y
87+
did you alter object type 'default::Card'? [y,n,l,c,b,s,q,?]
88+
> y
89+
did you alter object type 'default::Deck'? [y,n,l,c,b,s,q,?]
90+
> y
91+
Created /home/strinh/projects/flashcards/dbschema/migrations/00004-m1d2m5n.edgeql, id: m1d2m5n5ajkalyijrxdliioyginonqbtfzihvwdfdmfwodunszstya
8292
8393
$ npx gel migrate
94+
Applying m1d2m5n5ajkalyijrxdliioyginonqbtfzihvwdfdmfwodunszstya (00004-m1d2m5n.edgeql)
95+
... parsed
96+
... applied
8497
8598
.. edb:split-section::
8699
87100
Now when we look at the data in the UI, we will see the new properties on each of our object types.
88101

89-
.. code-block:: sh
90-
91-
$ echo
102+
.. image:: https://placehold.co/600x400?text=Show+timestamped+properties

0 commit comments

Comments
 (0)