Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Challenge submission! #10

Open
wants to merge 73 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
f758ee7
Setting up mobile template, Backend image, docker-compose
shpetimselaci Jun 14, 2024
c83f3e6
Setup Dockerfile, Listen to tsc & bundle
shpetimselaci Jun 14, 2024
ff38574
Added linter, code formatter, some db configuration, and environment …
shpetimselaci Jun 17, 2024
8701260
Switch over to ts-node for runtime
shpetimselaci Jun 17, 2024
16bb5ff
Schemas based on requirements
shpetimselaci Jun 17, 2024
4f78173
Start apollo server & gather type definitions
shpetimselaci Jun 17, 2024
47aadcd
Change runtime to nodemon + ts-node, Setting up convention for types,…
shpetimselaci Jun 17, 2024
113002e
Remove extension
shpetimselaci Jun 17, 2024
6f86b8b
Changed convention
shpetimselaci Jun 17, 2024
74099b4
Updated query schemas, and added all mutation schemas required for FE
shpetimselaci Jun 17, 2024
be6b1c4
Add limit & pagination to queries, Update types
shpetimselaci Jun 17, 2024
e9ebc60
Update layout, Add Select & Insert types
shpetimselaci Jun 17, 2024
9e9d8b7
Updated Apollo Server to include resolvers, a bit more setup around t…
shpetimselaci Jun 17, 2024
66393f2
Fixed parsing environment variables due to .env file not loading -- u…
shpetimselaci Jun 17, 2024
d32fd29
Generate gql types!
shpetimselaci Jun 17, 2024
7c03422
Add Date scalar, update types, update queries, fix services params to…
shpetimselaci Jun 17, 2024
bda7c4d
Improve errors thrown
shpetimselaci Jun 17, 2024
54e2dee
Fix runtime error related to Date Scalar
shpetimselaci Jun 17, 2024
70c4a65
Fix backend Dockerfile & docker-compose.yml so I can whether things a…
shpetimselaci Jun 17, 2024
7f47324
Fix migrations of tables!
shpetimselaci Jun 17, 2024
723985e
Fix typo (using unique instead of index function) when creating indexes
shpetimselaci Jun 17, 2024
bef16aa
Fix running migrations on runtime, Add image pgadmin client
shpetimselaci Jun 17, 2024
3d80a82
Add db health checkout so migrations run after db is up and running
shpetimselaci Jun 17, 2024
b1e6b35
Fix schemas to include timestamps, add generated migrations
shpetimselaci Jun 18, 2024
ae24a88
Update resolvers to include services & fixed types, added services fo…
shpetimselaci Jun 18, 2024
47b01e6
Fix timestamps columns!
shpetimselaci Jun 18, 2024
43fbe41
Add migrations for column changes
shpetimselaci Jun 18, 2024
ccd3938
Increase length of text in column
shpetimselaci Jun 18, 2024
a4c07ba
Generate migrations for columns changed
shpetimselaci Jun 18, 2024
e58bc42
Add seed script, update scripts in package.json
shpetimselaci Jun 18, 2024
f54bea9
Add seed of users!!
shpetimselaci Jun 18, 2024
59382cc
Fix seed to include ids that are from insers
shpetimselaci Jun 18, 2024
3fe1f3a
Fix column of title
shpetimselaci Jun 18, 2024
0e3c73e
Fix listQuestions service query, Fix listUserQuestions query
shpetimselaci Jun 18, 2024
dff3875
Remove any type
shpetimselaci Jun 18, 2024
8c5de59
Fix listQuestionAnswers query
shpetimselaci Jun 18, 2024
61b4ee6
Remove unused code
shpetimselaci Jun 18, 2024
b419081
Fix seed script to end connection when done
shpetimselaci Jun 18, 2024
4da7157
Add versioning resolver
shpetimselaci Jun 18, 2024
eafe79f
Add version resolver
shpetimselaci Jun 18, 2024
1f0b434
Update mutations to return the right data from update queries ran
shpetimselaci Jun 18, 2024
1340851
Put loggers under utils
shpetimselaci Jun 18, 2024
e9e2d9b
Fixed linting & some formatting
shpetimselaci Jun 20, 2024
a08c748
Generate types to ./generated-types folder instead
shpetimselaci Jun 20, 2024
17d5f23
Fix ignoring files to lint
shpetimselaci Jun 20, 2024
5c8077a
Update dockerfile
shpetimselaci Jun 20, 2024
caa9c94
Mapping context to resolvers
shpetimselaci Jun 20, 2024
a4e60d7
Update Docker user creation, Add auth and some middlewares related to…
shpetimselaci Jun 20, 2024
470bd75
Fix linting issues
shpetimselaci Jun 20, 2024
06078b6
Update testing suite
shpetimselaci Jun 20, 2024
7fd88e8
Move drizzle folder to migrations, add jest & mocha
shpetimselaci Jun 21, 2024
0738a90
Switch to expo for a faster poc
shpetimselaci Jun 21, 2024
a94a801
Some components created, layout, clients, persisting on mobx, hooks f…
shpetimselaci Jun 21, 2024
0db37d9
Authentication & some screens
shpetimselaci Jun 21, 2024
dbea526
Added all queries & mutations that are going to happen
shpetimselaci Jun 21, 2024
91d3fe2
Expose endpoint when developing so you can generate types easily
shpetimselaci Jun 22, 2024
a1fc419
Added codegen for mobile, Fixed auth context throwing 500,
shpetimselaci Jun 25, 2024
031e8d7
Questions tab done!
shpetimselaci Jun 25, 2024
93184c1
Users screen, User screen with tabs to questions & answers, Questions…
shpetimselaci Jun 27, 2024
e82d169
Fix some queries, and add ts-ignore to some expo-router dynamically t…
shpetimselaci Jun 28, 2024
d3dbba9
Add question answers screen
shpetimselaci Jun 30, 2024
267d272
Fix mutation types not being generated
shpetimselaci Jun 30, 2024
58a852a
Cosmetic touches
shpetimselaci Jun 30, 2024
07e6558
Add order by to find newly created stuff easier
shpetimselaci Jun 30, 2024
d9f4a72
Wrapped up hooks for creating & editing questions and answers, UI & w…
shpetimselaci Jun 30, 2024
7044f90
Show errors when validating, some cosmetic changes
shpetimselaci Jul 1, 2024
3c084a4
Add edit, delete post!
shpetimselaci Jul 1, 2024
86d175a
Fix pagination, add pagination on question answers, fix editing not b…
shpetimselaci Jul 1, 2024
9019f7b
Added action sheets for answers by long pressing! Delete action imple…
shpetimselaci Jul 1, 2024
5686a14
Merge pull request #2 from shpetimselaci/feature/app-mvp
shpetimselaci Jul 1, 2024
6d9d003
Update README.md
shpetimselaci Jul 1, 2024
c9308fb
Updating README.md to be able to run things!
shpetimselaci Jul 1, 2024
a422a30
Update README.md
shpetimselaci Jul 1, 2024
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: 12 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# EditorConfig is awesome: https://EditorConfig.org

# top-most EditorConfig file
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = true
11 changes: 11 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
POSTGRES_DB=development
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_HOST=db
POSTGRES_PORT=5432
[email protected]
PGADMIN_DEFAULT_PASSWORD=123456
NODE_ENV=development
API_VERSION=1
SECRET=d11f4d5d143433e597e35aa6fdf009a94b0d6bcf009f24fc22fbde69b5c6c7e9
REFRESH_SECRET=8a8b7d77bc34a93e551fdff13c77db7c3776a4980fff4be6cd20ee9c2f776f4f
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.env

5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnType": false
}
25 changes: 23 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,32 @@
# Starting off

Make sure you have docker & docker-compose installed in your machine.
I'm using a mac, so I've utilized colima, docker, and docker compose installed by [brew](https://brew.sh/) using these links:

```bash
brew install colima
brew install docker docker-compose
```

# To run

```bash
colima start
docker compose build
docker compose up
```

It will automatically setup the db instance, seed, and whatever you need to just open up the app!

### CHALLENGE

# Limbic Fullstack Code Challenge

This is Limbic’s FullStack Challenge that combines bits from the frontend and backend challenges in one.

Jane is a clinical therapist and wants her clients to answer simple questionnaires in order to better understand them. She needs a way to add/delete/edit questions and also see the answers of each client.

## Backend
## Backend

You are tasked with writing an API to create/edit/delete Users, Questions, and Answers. It should be a NodeJS/ExpressJS server with the following endpoints:

Expand Down Expand Up @@ -62,7 +84,6 @@ You are tasked with writing a React/React Native app to consume the backend API.

You can use anything you want for state management. We use MobX and the Context API a lot so it's a big plus if you can also **implement some/all of the state handling with MobX and Context API**.


## Instructions

1. **Submitting Code**
Expand Down
5 changes: 5 additions & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules
.env
dist
schema.graphql
generated-types
1 change: 1 addition & 0 deletions backend/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
drizzle
4 changes: 4 additions & 0 deletions backend/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"printWidth": 100,
"singleQuote": true
}
26 changes: 26 additions & 0 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
FROM node:22.3 as base
RUN addgroup nodeusr && adduser --system --group nodeusr
WORKDIR /home/backend

FROM base as install

RUN mkdir -p /temp/dev/

COPY package.json package-lock.json /temp/dev/

RUN cd /temp/dev/ && npm install --frozen-lockfile

FROM install as run

RUN cd /home/backend

COPY --from=install /temp/dev/package.json /temp/dev/package-lock.json ./
COPY --from=install /temp/dev/node_modules node_modules
## rest of the stuff..
COPY . .

RUN npm run generate:types

USER nodeusr

ENTRYPOINT ["npm", "run", "dev"]
15 changes: 15 additions & 0 deletions backend/codegen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { CodegenConfig } from '@graphql-codegen/cli';

const config: CodegenConfig = {
schema: 'schema.graphql',
generates: {
'./generated-types/index.ts': {
config: {
useIndexSignature: true,
},
plugins: ['typescript', 'typescript-resolvers'],
},
},
};

export default config;
4 changes: 4 additions & 0 deletions backend/common/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const authErrors = {
USER_NOT_FOUND: 'USER_NOT_FOUND',
INVALID_TOKEN: 'INVALID_TOKEN',
};
8 changes: 8 additions & 0 deletions backend/common/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export type PaginationParams = {
limit?: number | null;
offset?: number | null;
};

export type NonNullableObject<T> = {
[P in keyof T]-?: T[P] extends object ? NonNullableObject<T[P]> : NonNullable<T[P]>;
};
2 changes: 2 additions & 0 deletions backend/config/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const DEFAULT_EXPIRES_IN = 60 * 60 * 24;
export const DEFAULT_REFRESH_EXPIRES_IN = 60 * 60 * 24 * 30;
17 changes: 17 additions & 0 deletions backend/config/env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as z from 'zod';

const envSchema = z.object({
POSTGRES_DB: z.string(),
POSTGRES_USER: z.string(),
POSTGRES_PASSWORD: z.string(),
POSTGRES_HOST: z.string(),
POSTGRES_PORT: z.coerce.number(),
API_VERSION: z.coerce.number(),
SECRET: z.string(),
REFRESH_SECRET: z.string(),
NODE_ENV: z.enum(['development', 'production']),
});

const environmentVariables = envSchema.parse(process.env);

export default environmentVariables;
17 changes: 17 additions & 0 deletions backend/config/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import environmentVariables from './env';

const config = {
db: {
host: environmentVariables.POSTGRES_HOST,
port: environmentVariables.POSTGRES_PORT || 5432,
user: environmentVariables.POSTGRES_USER,
password: environmentVariables.POSTGRES_PASSWORD,
database: environmentVariables.POSTGRES_DB,
},
version: environmentVariables.API_VERSION,
secret: environmentVariables.SECRET,
refreshSecret: environmentVariables.REFRESH_SECRET,
nodeEnvironment: environmentVariables.NODE_ENV,
};

export default config;
2 changes: 2 additions & 0 deletions backend/constants/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// constants go here
export const DEFAULT_LIMIT = 10;
10 changes: 10 additions & 0 deletions backend/db/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
import config from '../config';
import * as schema from './schemas';

const pool = new Pool(config.db);

const db = drizzle(pool, { schema });

export default db;
38 changes: 38 additions & 0 deletions backend/db/schemas/answers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { integer, pgTable, serial, timestamp, index, varchar } from 'drizzle-orm/pg-core';
import { relations } from 'drizzle-orm';
import { questions } from './questions';
import { users } from './users';
import { boolean } from 'drizzle-orm/pg-core';

export const answers = pgTable(
'answers',
{
id: serial('id').unique().primaryKey(),
answer: varchar('name', { length: 512 }).notNull(),
questionId: integer('question_id').notNull(),
creatorId: integer('creator_id').notNull(),
deleted: boolean('deleted').default(false).notNull(),
createdAt: timestamp('created_at').defaultNow().notNull(),
updatedAt: timestamp('updated_at').defaultNow().notNull(),
},
(t) => {
return {
questionAnswersIndex: index('question_id_answer_id_created_at').on(t.questionId, t.id),
userAnswersIndex: index('user_id_answer_id_created_at').on(t.creatorId, t.id),
};
},
);

export const answerRelation = relations(answers, ({ one }) => ({
question: one(questions, {
fields: [answers.questionId],
references: [questions.id],
}),
author: one(users, {
fields: [answers.creatorId],
references: [users.id],
}),
}));

export type Answer = typeof answers.$inferSelect;
export type NewAnswer = typeof answers.$inferInsert;
3 changes: 3 additions & 0 deletions backend/db/schemas/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './users';
export * from './answers';
export * from './questions';
26 changes: 26 additions & 0 deletions backend/db/schemas/questions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { relations } from 'drizzle-orm';
import { integer, pgTable, serial, timestamp, varchar } from 'drizzle-orm/pg-core';
import { answers } from './answers';
import { users } from './users';
import { boolean } from 'drizzle-orm/pg-core';

export const questions = pgTable('questions', {
id: serial('id').unique().primaryKey().notNull(),
title: varchar('title', { length: 123 }).notNull(),
description: varchar('description', { length: 512 }).notNull(),
authorId: integer('author_id').notNull(),
deleted: boolean('deleted').default(false).notNull(),
createdAt: timestamp('created_at').defaultNow().notNull(),
updatedAt: timestamp('updated_at').defaultNow().notNull(),
});

export const questionsRelations = relations(questions, ({ many, one }) => ({
author: one(users, {
fields: [questions.authorId],
references: [users.id],
}),
answers: many(answers),
}));

export type Question = typeof questions.$inferSelect;
export type NewQuestion = typeof questions.$inferInsert;
20 changes: 20 additions & 0 deletions backend/db/schemas/users.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { relations } from 'drizzle-orm';
import { pgTable, serial, varchar, date, timestamp } from 'drizzle-orm/pg-core';
import { questions } from './questions';
import { answers } from './answers';

export const users = pgTable('users', {
id: serial('id').primaryKey().notNull(),
name: varchar('name', { length: 256 }).notNull(),
birthday: date('birthday', { mode: 'string' }).notNull(),
createdAt: timestamp('created_at').defaultNow().notNull(),
updatedAt: timestamp('updated_at').defaultNow().notNull(),
});

export const usersRelations = relations(users, ({ many }) => ({
questions: many(questions),
answers: many(answers),
}));

export type User = typeof users.$inferSelect;
export type NewUser = typeof users.$inferInsert;
9 changes: 9 additions & 0 deletions backend/drizzle.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { defineConfig } from 'drizzle-kit';
import config from './config/index';

export default defineConfig({
schema: ['db/schemas/answers.ts', 'db/schemas/questions.ts', 'db/schemas/users.ts'],
out: './drizzle',
dialect: 'postgresql', // 'postgresql' | 'mysql' | 'sqlite'
dbCredentials: config.db,
});
33 changes: 33 additions & 0 deletions backend/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const eslint = require('@eslint/js');
const tseslint = require('typescript-eslint');
const prettierConfig = require('eslint-config-prettier');

const { includeIgnoreFile } = require('@eslint/compat');
const path = require('node:path');
const gitignorePath = path.resolve(__dirname, '.gitignore');

module.exports = tseslint.config(
{
ignores: [...includeIgnoreFile(gitignorePath).ignores, 'eslint.config.js'],
},
eslint.configs.recommended,
...tseslint.configs.recommended,
prettierConfig,
{
ignores: [...includeIgnoreFile(gitignorePath).ignores, 'eslint.config.js'],
rules: {
'@typescript-eslint/no-unused-vars': [
'error',
{
args: 'all',
argsIgnorePattern: '^_',
caughtErrors: 'all',
caughtErrorsIgnorePattern: '^_',
destructuredArrayIgnorePattern: '^_',
varsIgnorePattern: '^_',
ignoreRestSiblings: true,
},
],
},
},
);
7 changes: 7 additions & 0 deletions backend/graphql/context/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { BaseContext } from '@apollo/server';
import { User } from '../../db/schemas';

export interface Context extends BaseContext {
user: User;
userId: number;
}
10 changes: 10 additions & 0 deletions backend/graphql/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import gql from 'graphql-tag';
import { mutationDefs } from './mutations';

import { queryDefs } from './queries';

import { typeDefs } from './types';

export const graphqlSchema = [...typeDefs, ...queryDefs, ...mutationDefs].join('\n');

export default gql(graphqlSchema);
3 changes: 3 additions & 0 deletions backend/graphql/mutations/_root.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
type Mutation {
version: String
}
5 changes: 5 additions & 0 deletions backend/graphql/mutations/answers.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
extend type Mutation {
addAnswer(questionId: Int!, answer: String!): Answer!
editAnswer(answerId: Int!, answer: String!): Answer!
deleteAnswer(answerId: Int!): Answer!
}
14 changes: 14 additions & 0 deletions backend/graphql/mutations/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { readdirSync, readFileSync } from 'fs';
import path from 'path';

const currentDirPath = path.resolve(__dirname, './');

const files = readdirSync(currentDirPath).map((file) =>
file.endsWith('.ts')
? ''
: readFileSync(path.resolve(currentDirPath, file), {
encoding: 'utf-8',
}),
);

export const mutationDefs = files;
5 changes: 5 additions & 0 deletions backend/graphql/mutations/questions.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
extend type Mutation {
addQuestion(title: String!, description: String!): Question!
editQuestion(questionId: Int!, title: String!, description: String!): Question!
deleteQuestion(questionId: Int!): Question!
}
3 changes: 3 additions & 0 deletions backend/graphql/queries/_root.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
type Query {
version: String!
}
3 changes: 3 additions & 0 deletions backend/graphql/queries/answers.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
extend type Query {
questionAnswers(id: Int!, limit: Int, offset: Int): [Answer!]!
}
Loading