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

Fullstack interview assignment submission #109

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

victorpedrocs
Copy link

@victorpedrocs victorpedrocs commented May 8, 2023

Fullstack interview assignment submission

Hello Contra team!

I'm submitting my solution for the interview assignment. I chose the backend option, but also did a small part of the frontend part, as an extra.

Checkout my Loom

How to run the code

First, copy and rename the .env.sample file to .env, it will store the environment variables

cp .env.sample .env

then, spin up the postgres container

yarn docker:up

it will run and detach the container.

Now you can run the database migrations

yarn migrate:up

and seed the database

yarn seed

With that, you can run the dev server

yarn dev

The api can be found in localhost:5000/graphql and the UI can be found in localhost:3000

Overall changes

In an effort to get closer to the stack described in the job description, I opted to make this into a monorepo using TurboRepo, and used graphql-helix for graphql on the server.

Root module

  • I'm using dotenv-cli to load the environment variables, that are exemplified in the .env.sample file.
  • Added scripts to the root package.json to run turbo actions
  • Created a turbo.json file to setup turbo monorepo options

Backend

Database Schema

This is the database schema diagram.

  • The value in the user_feature_flag table is optional and allows to override the original flag value for each user, meaning that if it is not filled, the user will receive the flags original value.

Seed

yarn seed

Added a script to seed the database with flags and users located in backend/src/bin/seed

  • There is a file to generate mock users and flags.
  • Use faker to generate fake data
  • Export a function to generate fake objects receiving a parameter to specify how many, and another to generate the INSERT query.

Graphql schema

yarn codegen

The graphql schema follows the same structure as the database schema, except for the relationship table.

  • The User type includes a flags: [Flag!]! collection to allow fetching the user's feature flags. (inner join)
  • The Flag resolver within the user type will fetch only the flags assigned to the user.
  • Although I didn't add something like dataloader to fetch the user's flags without making multiple calls to the database, this is not something I would recommend in a real-world scenario.

I used the typescript-graphql-request and typescript-operations plugins to generate an sdk for the schema, writing the operations in a separate file. That makes it very easy to generate a graphql client client to query the api on integration tests.

Config

I implement a function to validate environment variables and export the database connection. It uses the envalid library for that in src/config/databaseConfiguration.ts.

Tests

# with the dev server running
cd backend
yarn test

I implemented integration tests for each api endpoint. These tests are used to test the endpoints automatically without having to open postman or the graphiql tool.

The test files are in the same folder as the resolver files, and all of them follow the setup, execute and assert, making it very easy to read and understand. All tests also build and teardown any needed test objects, so there isn't dirty data left in the database after the tests.

Resolvers

The resolvers are very simple, they receive the query parameters, call the database layer, and parse the results back to the response.

Usually I also make sure to log the request context and parameters on each call using structure logs, but because of the limited time, I dind't do it for the assignment.

Frontend

As an extra, I decided to implement the modal stacking and closing feature on the frontend assignment. This feature works by having a global modal context managing a shared modal state.

  • It stores all open modals in a stack
  • There are methods to add a new modal, pop a modal from the stack, or remove one, when they are closed without pressing the Escape key.
  • A single event listener is added to the document.body by the ModalContextWrapper.
  • When the Esc key is pressed, it will pop the topmost modal in the stack, closing it.
  • The state is managed using the zustand, which makes this whole process very easy.

@victorpedrocs
Copy link
Author

I added this last commit because I forgot to push the last change I made in the schema/operations/queries.graphql file, moving the Fragments to a separate fragments.graphql file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

None yet

1 participant