Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: redwoodjs/redwood-tutorial
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: main
Choose a base ref
...
head repository: tktcorporation/redwood-tutorial
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref
Can’t automatically merge. Don’t worry, you can still create the pull request.

Commits on Oct 10, 2022

  1. Copy the full SHA
    5647ac6 View commit details
  2. Copy the full SHA
    b17d2d2 View commit details
  3. Copy the full SHA
    25a2e2e View commit details
  4. 🎨 rw lint --fix

    tktcorporation committed Oct 10, 2022
    Copy the full SHA
    aae8eb6 View commit details
  5. Copy the full SHA
    0ef23ae View commit details
  6. Copy the full SHA
    fce8e6b View commit details
  7. Copy the full SHA
    3e8de4f View commit details
  8. Copy the full SHA
    d546899 View commit details
  9. Copy the full SHA
    114dee4 View commit details
  10. Copy the full SHA
    3d22080 View commit details
  11. Copy the full SHA
    25b3dd7 View commit details
  12. Copy the full SHA
    d091894 View commit details
  13. Copy the full SHA
    119fe2d View commit details
  14. Copy the full SHA
    e0b09f2 View commit details
  15. Copy the full SHA
    398010e View commit details
  16. Copy the full SHA
    3d74401 View commit details
  17. Copy the full SHA
    a13f566 View commit details
  18. Copy the full SHA
    7214f2a View commit details
  19. Copy the full SHA
    0ff871b View commit details
  20. Copy the full SHA
    8ceb198 View commit details
  21. Copy the full SHA
    f06c364 View commit details
  22. Copy the full SHA
    90c08fb View commit details
  23. Copy the full SHA
    7aaedf7 View commit details
  24. Copy the full SHA
    757e2d9 View commit details
  25. Copy the full SHA
    068cc74 View commit details
  26. Copy the full SHA
    2d238d6 View commit details
  27. Copy the full SHA
    156e48a View commit details
  28. Copy the full SHA
    ab6b3f5 View commit details
  29. Copy the full SHA
    5002e2c View commit details
  30. Copy the full SHA
    20525b9 View commit details
  31. Copy the full SHA
    6807633 View commit details
Showing with 1,141 additions and 222 deletions.
  1. +52 −0 .devcontainer/devcontainer.json
  2. +1 −1 .env.defaults
  3. +10 −0 Dockerfile
  4. +0 −16 api/db/migrations/20210222013102_init_database/migration.sql
  5. +0 −13 api/db/migrations/20220208231150_create_user/migration.sql
  6. +36 −0 api/db/migrations/20221010082026_/migration.sql
  7. +13 −0 api/db/migrations/20221010100550_create_comment/migration.sql
  8. +2 −0 api/db/migrations/20221010121520_add_roles_to_user/migration.sql
  9. +1 −1 api/db/migrations/migration_lock.toml
  10. +12 −1 api/db/schema.prisma
  11. +4 −3 api/src/functions/auth.js
  12. +32 −0 api/src/graphql/comments.sdl.ts
  13. +23 −16 api/src/lib/{auth.js → auth.ts}
  14. +44 −0 api/src/services/comments/comments.scenarios.ts
  15. +86 −0 api/src/services/comments/comments.test.ts
  16. +36 −0 api/src/services/comments/comments.ts
  17. +2 −1 api/src/services/contacts/contacts.js
  18. +2 −2 api/src/services/posts/posts.scenarios.js
  19. +34 −0 api/tsconfig.json
  20. +42 −0 docker-compose.yml
  21. +2 −0 redwood.toml
  22. +1 −1 web/src/App.js
  23. +23 −6 web/src/{Routes.js → Routes.tsx}
  24. +0 −16 web/src/components/Article/Article.js
  25. +0 −17 web/src/components/Article/Article.stories.js
  26. +17 −0 web/src/components/Article/Article.stories.tsx
  27. +0 −18 web/src/components/Article/Article.test.js
  28. +50 −0 web/src/components/Article/Article.test.tsx
  29. +33 −0 web/src/components/Article/Article.tsx
  30. +1 −1 web/src/components/ArticleCell/ArticleCell.mock.js
  31. +1 −0 web/src/components/ArticleCell/ArticleCell.test.js
  32. +0 −28 web/src/components/ArticlesCell/ArticlesCell.js
  33. +0 −19 web/src/components/ArticlesCell/ArticlesCell.mock.js
  34. +16 −0 web/src/components/ArticlesCell/ArticlesCell.mock.ts
  35. 0 web/src/components/ArticlesCell/{ArticlesCell.stories.js → ArticlesCell.stories.tsx}
  36. +0 −34 web/src/components/ArticlesCell/ArticlesCell.test.js
  37. +43 −0 web/src/components/ArticlesCell/ArticlesCell.test.tsx
  38. +34 −0 web/src/components/ArticlesCell/ArticlesCell.tsx
  39. +40 −0 web/src/components/Comment/Comment.stories.tsx
  40. +44 −0 web/src/components/Comment/Comment.test.tsx
  41. +66 −0 web/src/components/Comment/Comment.tsx
  42. +33 −0 web/src/components/CommentForm/CommentForm.stories.tsx
  43. +14 −0 web/src/components/CommentForm/CommentForm.test.tsx
  44. +93 −0 web/src/components/CommentForm/CommentForm.tsx
  45. +18 −0 web/src/components/CommentsCell/CommentsCell.mock.ts
  46. +22 −0 web/src/components/CommentsCell/CommentsCell.stories.tsx
  47. +46 −0 web/src/components/CommentsCell/CommentsCell.test.tsx
  48. +37 −0 web/src/components/CommentsCell/CommentsCell.tsx
  49. +2 −1 web/src/components/Post/EditPostCell/EditPostCell.js
  50. +2 −1 web/src/components/Post/NewPost/NewPost.js
  51. +5 −6 web/src/components/Post/Post/Post.js
  52. +1 −1 web/src/components/Post/PostForm/PostForm.js
  53. +5 −5 web/src/components/Post/Posts/Posts.js
  54. +4 −2 web/src/layouts/BlogLayout/{BlogLayout.js → BlogLayout.tsx}
  55. +1 −0 web/src/pages/ArticlePage/ArticlePage.js
  56. +1 −1 web/src/pages/ContactPage/ContactPage.js
  57. +2 −1 web/src/pages/ForgotPasswordPage/ForgotPasswordPage.js
  58. +1 −0 web/src/pages/HomePage/HomePage.js
  59. +4 −3 web/src/pages/LoginPage/LoginPage.js
  60. +5 −4 web/src/pages/ResetPasswordPage/ResetPasswordPage.js
  61. +4 −3 web/src/pages/SignupPage/SignupPage.js
  62. +38 −0 web/tsconfig.json
52 changes: 52 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"name": "redwoodjs-v3-tutrial-2nd",
"image": "mcr.microsoft.com/vscode/devcontainers/universal:linux",

// Set *default* container specific settings.json values on container create.
"settings": {
"runOnSave.commands": [
{
"globMatch": "**/*",
"command": "docker-compose exec -T app /bin/bash -c \"dum rw lint --fix\"",
"runIn": "backend"
}
]
},

// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"ms-azuretools.vscode-docker",
"GitHub.copilot",
"donjayamanne.githistory",
"eamodio.gitlens",
"mhutchie.git-graph",
"steoates.autoimport",
"pucelle.run-on-save",
"Prisma.prisma",
"WallabyJs.console-ninja",
"antfu.iconify",
"Atishay-Jain.All-Autocomplete",
"usernamehw.errorlens",
"GraphQL.vscode-graphql",
"dbaeumer.vscode-eslint",
"eamodio.gitlens",
"ofhumanbondage.react-proptypes-intellisense",
"mgmcdermott.vscode-language-babel",
"wix.vscode-import-cost",
"pflannery.vscode-versionlens",
"editorconfig.editorconfig",
"prisma.prisma",
"WakaTime.vscode-wakatime"
],

// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],

// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "gh extension install davidraviv/gh-clean-branches",

// Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
"features": {
"homebrew": "latest"
}
}
2 changes: 1 addition & 1 deletion .env.defaults
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@
# into version control.

# schema.prisma defaults
DATABASE_URL=file:./dev.db
# DATABASE_URL=file:./dev.db

# location of the test database for api service scenarios (defaults to ./.redwood/test.db if not set)
# TEST_DATABASE_URL=file:./.redwood/test.db
10 changes: 10 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM node:18.10.0-bullseye-slim AS build-env

ENV LC_ALL=C.UTF-8

RUN apt-get update && \
apt-get install -y \
curl \
git

RUN curl -sSL https://bina.egoist.sh/egoist/dum | bash
16 changes: 0 additions & 16 deletions api/db/migrations/20210222013102_init_database/migration.sql

This file was deleted.

13 changes: 0 additions & 13 deletions api/db/migrations/20220208231150_create_user/migration.sql

This file was deleted.

36 changes: 36 additions & 0 deletions api/db/migrations/20221010082026_/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
-- CreateTable
CREATE TABLE "Post" (
"id" SERIAL NOT NULL,
"title" TEXT NOT NULL,
"body" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,

CONSTRAINT "Post_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "Contact" (
"id" SERIAL NOT NULL,
"name" TEXT NOT NULL,
"email" TEXT NOT NULL,
"message" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,

CONSTRAINT "Contact_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "User" (
"id" SERIAL NOT NULL,
"name" TEXT,
"email" TEXT NOT NULL,
"hashedPassword" TEXT NOT NULL,
"salt" TEXT NOT NULL,
"resetToken" TEXT,
"resetTokenExpiresAt" TIMESTAMP(3),

CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
13 changes: 13 additions & 0 deletions api/db/migrations/20221010100550_create_comment/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
-- CreateTable
CREATE TABLE "Comment" (
"id" SERIAL NOT NULL,
"name" TEXT NOT NULL,
"body" TEXT NOT NULL,
"postId" INTEGER NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,

CONSTRAINT "Comment_pkey" PRIMARY KEY ("id")
);

-- AddForeignKey
ALTER TABLE "Comment" ADD CONSTRAINT "Comment_postId_fkey" FOREIGN KEY ("postId") REFERENCES "Post"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "User" ADD COLUMN "roles" TEXT NOT NULL DEFAULT 'moderator';
2 changes: 1 addition & 1 deletion api/db/migrations/migration_lock.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "sqlite"
provider = "postgresql"
13 changes: 12 additions & 1 deletion api/db/schema.prisma
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
datasource db {
provider = "sqlite"
provider = "postgresql"
url = env("DATABASE_URL")
}

@@ -12,6 +12,7 @@ model Post {
id Int @id @default(autoincrement())
title String
body String
comments Comment[]
createdAt DateTime @default(now())
}

@@ -31,4 +32,14 @@ model User {
salt String
resetToken String?
resetTokenExpiresAt DateTime?
roles String @default("moderator")
}

model Comment {
id Int @id @default(autoincrement())
name String
body String
post Post @relation(fields: [postId], references: [id])
postId Int
createdAt DateTime @default(now())
}
7 changes: 4 additions & 3 deletions api/src/functions/auth.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { db } from 'src/lib/db'
import { DbAuthHandler } from '@redwoodjs/api'

import { db } from 'src/lib/db'

export const handler = async (event, context) => {
const forgotPasswordOptions = {
// handler() is invoked after verifying that a user was found with the given
@@ -101,13 +102,13 @@ export const handler = async (event, context) => {
//
// If this returns anything else, it will be returned by the
// `signUp()` function in the form of: `{ message: 'String here' }`.
handler: ({ username, hashedPassword, salt, userAttributes }) => {
handler: ({ username, hashedPassword, salt, _userAttributes }) => {
return db.user.create({
data: {
email: username,
hashedPassword: hashedPassword,
salt: salt,
// name: userAttributes.name
roles: 'moderator',
},
})
},
32 changes: 32 additions & 0 deletions api/src/graphql/comments.sdl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
export const schema = gql`
type Comment {
id: Int!
name: String!
body: String!
post: Post!
postId: Int!
createdAt: DateTime!
}
type Query {
comments(postId: Int!): [Comment!]! @skipAuth
comment(id: Int!): Comment! @requireAuth(roles: ["moderator"])
}
input CreateCommentInput {
name: String!
body: String!
postId: Int!
}
input UpdateCommentInput {
name: String
body: String
postId: Int
}
type Mutation {
createComment(input: CreateCommentInput!): Comment! @skipAuth
deleteComment(id: Int!): Comment! @requireAuth(roles: "moderator")
}
`
39 changes: 23 additions & 16 deletions api/src/lib/auth.js → api/src/lib/auth.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import type { Decoded } from '@redwoodjs/api'
import { AuthenticationError, ForbiddenError } from '@redwoodjs/graphql-server'

import { db } from './db'

/**
@@ -18,10 +20,14 @@ import { db } from './db'
* fields to the `select` object below once you've decided they are safe to be
* seen if someone were to open the Web Inspector in their browser.
*/
export const getCurrentUser = async (session) => {
export const getCurrentUser = async (session: Decoded) => {
if (!session || typeof session.id !== 'number') {
throw new Error('Invalid session')
}

return await db.user.findUnique({
where: { id: session.id },
select: { id: true, email: true },
select: { id: true, email: true, roles: true },
})
}

@@ -30,19 +36,25 @@ export const getCurrentUser = async (session) => {
*
* @returns {boolean} - If the currentUser is authenticated
*/
export const isAuthenticated = () => {
export const isAuthenticated = (): boolean => {
return !!context.currentUser
}

/**
* When checking role membership, roles can be a single value, a list, or none.
* You can use Prisma enums too (if you're using them for roles), just import your enum type from `@prisma/client`
*/
type AllowedRoles = string | string[] | undefined

/**
* Checks if the currentUser is authenticated (and assigned one of the given roles)
*
* @param roles: AllowedRoles - Checks if the currentUser is assigned one of these roles
* @param roles: {@link AllowedRoles} - Checks if the currentUser is assigned one of these roles
*
* @returns {boolean} - Returns true if the currentUser is logged in and assigned one of the given roles,
* or when no roles are provided to check against. Otherwise returns false.
*/
export const hasRole = (roles) => {
export const hasRole = (roles: AllowedRoles): boolean => {
if (!isAuthenticated()) {
return false
}
@@ -53,9 +65,6 @@ export const hasRole = (roles) => {
if (typeof currentUserRoles === 'string') {
// roles to check is a string, currentUser.roles is a string
return currentUserRoles === roles
} else if (Array.isArray(currentUserRoles)) {
// roles to check is a string, currentUser.roles is an array
return currentUserRoles?.some((allowedRole) => roles === allowedRole)
}
}

@@ -65,11 +74,9 @@ export const hasRole = (roles) => {
return currentUserRoles?.some((allowedRole) =>
roles.includes(allowedRole)
)
} else if (typeof context.currentUser.roles === 'string') {
} else if (typeof currentUserRoles === 'string') {
// roles to check is an array, currentUser.roles is a string
return roles.some(
(allowedRole) => context.currentUser?.roles === allowedRole
)
return roles.some((allowedRole) => currentUserRoles === allowedRole)
}
}

@@ -82,16 +89,16 @@ export const hasRole = (roles) => {
* whether or not they are assigned a role, and optionally raise an
* error if they're not.
*
* @param roles: AllowedRoles - When checking role membership, these roles grant access.
* @param roles: {@link AllowedRoles} - When checking role membership, these roles grant access.
*
* @returns - If the currentUser is authenticated (and assigned one of the given roles)
*
* @throws {AuthenticationError} - If the currentUser is not authenticated
* @throws {ForbiddenError} If the currentUser is not allowed due to role permissions
* @throws {@link AuthenticationError} - If the currentUser is not authenticated
* @throws {@link ForbiddenError} If the currentUser is not allowed due to role permissions
*
* @see https://github.com/redwoodjs/redwood/tree/main/packages/auth for examples
*/
export const requireAuth = ({ roles }) => {
export const requireAuth = ({ roles }: { roles?: AllowedRoles } = {}) => {
if (!isAuthenticated()) {
throw new AuthenticationError("You don't have permission to do that.")
}
44 changes: 44 additions & 0 deletions api/src/services/comments/comments.scenarios.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type { Prisma } from '@prisma/client'

export const standard = defineScenario<Prisma.CommentCreateArgs>({
comment: {
jane: {
data: {
name: 'Jane Doe',
body: 'I like trees',
post: {
create: {
title: 'Redwood Leaves',
body: 'The quick brown fox jumped over the lazy dog.',
},
},
},
},
john: {
data: {
name: 'John Doe',
body: 'Hug a tree today',
post: {
create: {
title: 'Root Systems',
body: 'The five boxing wizards jump quickly.',
},
},
},
},
},
})

export const postOnly = defineScenario<Prisma.PostCreateArgs>({
post: {
bark: {
data: {
title: 'Bark',
body: "A tree's bark is worse than its bite",
},
},
},
})

export type StandardScenario = typeof standard
export type PostOnlyScenario = typeof postOnly
Loading