Skip to content
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
12 changes: 2 additions & 10 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,9 @@
{
"files": ["*.tsx"],
"parser": "@typescript-eslint/parser",
"settings": {
"react": {
"version": "detect"
}
},
"extends": [
"plugin:@typescript-eslint/recommended",

"plugin:react/recommended",
"plugin:react-hooks/recommended",
"plugin:jsx-a11y/recommended",
"@remix-run/eslint-config",
"@remix-run/eslint-config/node",

// prettier config will turn rules off according to prettier, it should always be at the end
"prettier"
Expand Down
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
dist
build
node_modules
generated
coverage
Expand Down
11 changes: 9 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,19 @@
"version": "0.2.0",
"configurations": [
{
"name": "Attach",
"port": 9229,
"name": "Attach API",
"port": 9230,
"request": "attach",
"skipFiles": ["<node_internals>/**"],
"type": "pwa-node",
"restart": true
},
{
"name": "Attach APP",
"port": 9229,
"request": "attach",
"type": "node",
"restart": true
}
]
}
24 changes: 12 additions & 12 deletions api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@
"private": true,
"scripts": {
"build:apps": "tsc --pretty",
"dev": "node --inspect=0.0.0.0:9229 --enable-source-maps --no-lazy -r dotenv-flow/config ./dist/src",
"dev": "node --inspect=0.0.0.0:9230 --enable-source-maps --no-lazy -r dotenv-flow/config ./dist/src",
"codegen:prisma": "yarn prisma generate",
"codegen:graphql": "graphql-codegen --config codegen.yml",
"lint": "tsc --noEmit",
"prisma": "dotenv-flow -- prisma",
"reset-database": "dotenv-flow -- prisma migrate reset -f --skip-generate"
},
"dependencies": {
"@graphql-tools/mock": "^8.5.2",
"@graphql-tools/schema": "^8.3.2",
"@graphql-tools/utils": "^8.6.2",
"@prisma/client": "^3.10.0",
"@graphql-tools/mock": "^8.6.1",
"@graphql-tools/schema": "^8.3.3",
"@graphql-tools/utils": "^8.6.3",
"@prisma/client": "^3.11.0",
"faker": "^5.5.3",
"graphql": "^16.3.0",
"graphql-tag": "^2.12.6",
Expand All @@ -31,22 +31,22 @@
"@graphql-codegen/add": "^3.1.1",
"@graphql-codegen/cli": "^2.6.2",
"@graphql-codegen/schema-ast": "^2.4.1",
"@graphql-codegen/typescript": "^2.4.7",
"@graphql-codegen/typescript-operations": "^2.3.4",
"@graphql-codegen/typescript-resolvers": "^2.5.4",
"@graphql-codegen/typescript": "^2.4.8",
"@graphql-codegen/typescript-operations": "^2.3.5",
"@graphql-codegen/typescript-resolvers": "^2.6.1",
"@types/dotenv-flow": "^3.2.0",
"@types/faker": "^5.5.9",
"@types/ioredis": "^4.28.8",
"@types/ioredis": "^4.28.9",
"@types/ioredis-mock": "^5.6.0",
"@types/koa": "^2.13.4",
"@types/koa-bodyparser": "^4.3.6",
"@types/koa-helmet": "^6.0.4",
"@types/lodash": "^4.14.179",
"@types/lodash": "^4.14.180",
"dotenv-flow": "^3.2.0",
"dotenv-flow-cli": "^1.0.0",
"ioredis-mock": "^5.9.1",
"ioredis-mock": "^7.1.0",
"node-mocks-http": "^1.11.0",
"prisma": "^3.10.0",
"prisma": "^3.11.0",
"typescript": "^4.6.2"
},
"prisma": {
Expand Down
58 changes: 43 additions & 15 deletions api/src/core/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,49 @@ async function processSafelist(ctx: Context, { safelists = [], lazyLoadSafelist
ctx.request.body.query = query
}

function getDeduplicatedErrors(errors: GraphQLError[]) {
return errors
.reduce<GraphQLError[]>((acc, error) => {
const existingError = error.stack && acc.find(({ stack }) => stack === error.stack)

if (!existingError) {
const newError = new GraphQLError(error.message, {
...error,
path: undefined,
extensions: {
count: 1,
paths: [error.path],
},
})
newError.stack = error.stack
acc.push(newError)
} else {
existingError.extensions.count = Number(existingError.extensions.count) + 1
;(existingError.extensions.paths as unknown[]).push(error.path)
}

return acc
}, [])
.map((error) => {
if (typeof error.extensions.count === 'number' && error.extensions.count > 1) {
error.message = `(x${error.extensions.count}) ${error.message}`
}
return error
})
}

async function processGraphql(ctx: Context, schema: GraphQLSchema, mockedSchema: GraphQLSchema) {
const { body } = ctx.request
const { query, operationName, variables, extensions } = body

if (typeof query !== 'string' || typeof operationName !== 'string') {
throw new Error(ErrorCode.INTERNAL_SERVER_ERROR, {
cause: new Error(
'body.query or body.operationName is invalid, did you forget to enable Safelist ?'
),
})
}

ctx.body = await graphql({
schema: extensions?.mock ? mockedSchema : schema,
operationName,
Expand All @@ -98,24 +137,13 @@ async function processGraphql(ctx: Context, schema: GraphQLSchema, mockedSchema:
})

if (ctx.body.errors) {
const warnings: GraphQLError[] = []
const errors: GraphQLError[] = []

for (const error of ctx.body.errors) {
if (process.env.NODE_ENV !== 'production') {
error.extensions.stack = error.stack
}

getDeduplicatedErrors([...ctx.body.errors]).forEach((error) => {
if (error.message in ErrorCode) {
warnings.push(error)
log.warn(error)
} else {
errors.push(error)
log.error(error)
}
}

if (errors.length) log.error(errors)

if (warnings.length) log.warn(warnings)
})
}
}

Expand Down
6 changes: 1 addition & 5 deletions api/src/core/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,7 @@ const defaultFormat = format.combine(
const developmentFormat = format.combine(
defaultFormat,
format.colorize(),
// eslint-disable-next-line @typescript-eslint/no-unused-vars
format.printf(({ timestamp, level, message, stack }) => {
if (Array.isArray(message) && message.every((msg) => msg instanceof Error)) {
return `${message.map((err: Error) => `[${level}]: ${err.message}\n${err.stack}\n \n`)}`
}
format.printf(({ level, message, stack }) => {
return `[${level}]: ${message}${stack ? `\n${stack}}\n` : ''}`
})
)
Expand Down
2 changes: 1 addition & 1 deletion api/src/core/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ export function telemetryMiddleware({
`${ctx.method} ${ctx.path}${
ctx.request.body?.operationName ? ` ${ctx.request.body?.operationName}` : ''
} -> ${ctx.status}: ${duration}ms`,
error
error instanceof Error && error.cause ? error.cause : error
)
}
}
18 changes: 7 additions & 11 deletions api/src/todo.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const typeDefs = gql`
extend type Mutation {
addTodo(label: String!): Todo!
removeTodo(id: ID!): Boolean!
updateTodo(input: TodoInput!): Todo!
setTodoIsCompleted(id: ID!, isCompleted: Boolean!): Todo!
}
`

Expand All @@ -45,13 +45,10 @@ export const resolvers: Resolvers = {
return true
},

updateTodo: (parent, { input }, { prisma }) => {
setTodoIsCompleted: (parent, { id, isCompleted }, { prisma }) => {
return prisma.todo.update({
where: { id: input.id },
data: {
label: input.label,
isCompleted: input.isCompleted,
},
where: { id },
data: { isCompleted: isCompleted },
})
},
},
Expand Down Expand Up @@ -81,11 +78,10 @@ export const mockResolvers: MockResolvers = {

removeTodo: () => true,

updateTodo: (parent, { input }) => {
setTodoIsCompleted: (parent, { id, isCompleted }) => {
return {
id: input.id,
label: input.label || undefined,
isCompleted: input.isCompleted || false,
id,
isCompleted: isCompleted,
}
},
},
Expand Down
3 changes: 0 additions & 3 deletions app/.env.development

This file was deleted.

6 changes: 6 additions & 0 deletions app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules

/.cache
/build
/public/build
.env
53 changes: 53 additions & 0 deletions app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Welcome to Remix!

- [Remix Docs](https://remix.run/docs)

## Development

From your terminal:

```sh
npm run dev
```

This starts your app in development mode, rebuilding assets on file changes.

## Deployment

First, build your app for production:

```sh
npm run build
```

Then run the app in production mode:

```sh
npm start
```

Now you'll need to pick a host to deploy it to.

### DIY

If you're familiar with deploying node applications, the built-in Remix app server is production-ready.

Make sure to deploy the output of `remix build`

- `build/`
- `public/build/`

### Using a Template

When you ran `npx create-remix@latest` there were a few choices for hosting. You can run that again to create a new project, then copy over your `app/` folder to the new project that's pre-configured for your target server.

```sh
cd ..
# create a new project, and pick a pre-configured host
npx create-remix@latest
cd my-new-remix-app
# remove the new project's app (not the old one!)
rm -rf app
# copy your app over
cp -R ../my-old-remix-app/app app
```
14 changes: 4 additions & 10 deletions app/codegen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,20 @@ hooks:
afterAllFileWrite:
- prettier --write
generates:
src/generated/graphql.tsx:
src/generated/graphql.ts:
plugins:
- typescript
- typescript-operations
- typescript-react-apollo
- named-operations-object
- typescript-apollo-client-helpers
- ../node_modules/fork-typescript-generic-sdk
config:
identifierName: ListAllOperations
rawRequest: true
skipTypename: true
scalars:
Date: 'string'
reactApolloVersion: 3
exportFragmentSpreadSubTypes: true
namingConvention:
enumValues: keep

src/generated/fragments.json:
plugins:
- fragment-matcher

src/generated/safelist.json:
plugins:
- '@cobbl/graphql-codegen-operations-safelist':
Expand Down
Loading