Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
7ea7c7c
refactor: move content of README to docs/full-stack-interview, feat: …
FrcGustavo Mar 12, 2021
c885c87
feat: init backend proyect
FrcGustavo Mar 13, 2021
8d1b3a3
feat: init frontend project
FrcGustavo Mar 13, 2021
05bee6f
backend:feat: add service branches
FrcGustavo Mar 13, 2021
a7e38c6
backend:feat: add util to connect with github client and complete rou…
FrcGustavo Mar 13, 2021
69386fe
backend:feat: route to get commits
FrcGustavo Mar 13, 2021
963e996
backend:feat: add route to get one commit
FrcGustavo Mar 13, 2021
bcdfe0d
fronten:feat: add page Home and Branches where we can se all branches
FrcGustavo Mar 14, 2021
58da188
fronten:feat: add page Home and Branches where we can se all branches
FrcGustavo Mar 14, 2021
58ed50a
frontend:feat: add page of commits
FrcGustavo Mar 14, 2021
63ca997
frontend:feat: add styles to page Branches and Commits
FrcGustavo Mar 14, 2021
6c39e4c
frontend:feat: add page DetailCommit
FrcGustavo Mar 14, 2021
dd4bdd0
feat: integrate backend with frontend
FrcGustavo Mar 14, 2021
396a7bf
feat: integrate backend with frontend
FrcGustavo Mar 14, 2021
7dadbb3
feat: add styles to page of branches
FrcGustavo Mar 14, 2021
865a177
feat: add styles to page of Commits
FrcGustavo Mar 14, 2021
aa19143
feat: add styles to page of DetailCommit
FrcGustavo Mar 14, 2021
2bc8804
feat: add list of pull request in backend and frontend
FrcGustavo Mar 15, 2021
2cd3204
fix: error when commit has a empty file
FrcGustavo Mar 15, 2021
033a319
feat: create page to CreatePull
FrcGustavo Mar 15, 2021
3932794
feat: create page to CreatePull
FrcGustavo Mar 15, 2021
edcce86
feat: create endpoint to create a Pull
FrcGustavo Mar 15, 2021
2e4c03e
feat: create endpoint to create a Merge
FrcGustavo Mar 15, 2021
9a76938
fix: styles and handle errors
FrcGustavo Mar 15, 2021
32c40d7
improve: complete flow to create PR and chage state
FrcGustavo Mar 15, 2021
84513a1
improve: complete flow to create a merge
FrcGustavo Mar 15, 2021
214427a
feat: dokerize app
FrcGustavo Mar 16, 2021
c260dbc
feat: add test to routes
FrcGustavo Mar 16, 2021
8e57121
improve: page home
FrcGustavo Mar 16, 2021
aa2405a
improve: add documentation
FrcGustavo Mar 16, 2021
d16486c
improve: add documentation to run test
FrcGustavo Mar 16, 2021
770e7e7
improve: change documentation
FrcGustavo Mar 17, 2021
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
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
TOKEN=
85 changes: 34 additions & 51 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,51 +1,34 @@
# fullstack-interview-test
Interview test for fullstack Software Engineers

## Welcome!
If you're reading this, it means we're interested in working with you and solving amazing and difficult problems in real-estate tech in Mexico.

This README provides the instructions to a small, self-contained test for a FullStack Software Engineer position.

## What we're looking for
We're looking for a talented and driven full-stack engineer, comfortable with building responsive front end experiences, as well as with designing and building rigorous APIs and backend services.

This means that this test is designed to gather signal on your coding structure, the tradeoffs and decisions you make on API design, and how you build a lightweight frontend app to show the data coming from your API. We're excited to see what you build!

## The test
Today, we'll be building an API wrapper around the git information of this project. We suggest forking this repo and start working on it on your private fork, the url of which is the only thing you need to send us when you're done.

The main objects we'll be dealing with are:
- Commits
- Authors
- Branches
- PRs

We'd like to see a visual representation of the git history of this repo as a **JS web-app**, using the API previously described. To be specific, we'd like to see the following:
- A view where we can see the existing branches
- A branch detail view where we can see all the commits to one specific branch, with commit messages, authors and timestamps.
- A commit detail view where we can see the commit message, timestamp, number of files changed and author names / emails.
- A "PR" create view, where we can choose two branches (base and compare), and merge them together, just like Pull Requests work in Github.
- A "PR" list view, where we see all created PRs and the following info: Author, Title, Description and Status (`Open`, `Closed`, `Merged`). If the status is `Open`, there should be a button that allows us to mark it as `Closed`.

For the **PR create view**, we'll ask the user for a PR title and description, and we'll give them 2 options: either save it (Status = `Open`), or merge it. Note that merge operations can fail due to conflicts or other reasons, so make sure you handle and show whatever error happens when merging. There's no need to do something about these errors other than show them in the frontend. After a successful merge, the PR should move to a `Merged` status.

## Deliverables
We expect this test to take around 4 hours, but not significantly more (your time is very valuable!). We're giving you a week from the date you receive it to complete it and send us your repo URL, using **whatever stack you feel most comfortable with**. Due to this, we ask that you also provide a `README` with instructions for running your project, both back and frontend, along with setup instructions (or provide a Dockerfile and a `docker run` instruction).

It's up to you to design how this should look code-wise, but we don't expect you to model all git objects in a DB. We're ok with reading them using a library wrapper for git like [GitPython](https://gitpython.readthedocs.io/en/stable/), on-demand. The only DB design required is the one for PRs.

## Grading
We'll grade this project according to completion percentage of the features requested, good coding style for both back and frontend. For us, good coding means:
- It's readable. We read code much more often than we write it, so it's important that we're clear on what we're doing and comment any hairy parts (which we don't really expect to have in this test!).
- It's reasonably well ordered and with a logically thought-out structure. We like to do separation of concerns, and deal with routing, DB models, serialization, etc in their own file structure / files.
- Clear, understandable variable names. No one wants to read the whole file to understand what the variable `c1_2` means.

We also care about being able to run your test without significant effort on our part, so make sure you test the instructions you provide on your README.

### Things we won't be grading
- Design chops: we care that you know enough CSS not to be surprised when you need to work on it, but we're not picky about your solution's UX/UI.

### Bonus points
- Your code has tests.

If you come across any questions or anything we didn't cover on this README, feel free to reach out to us and we'll get you an answer as soon as possible. Happy coding!
# PirateHub (fullstack-interview-test)

This project show you the history of all the changes, using an application in react and api that get all the information from the official github api.

You need to have installed docker and docker-compose for run this project and also you need a token of github author of this project.

Now for can run this project follow next steps
* Clone this repository
* Copy the file `.env.example` and rename like `.env` and complete the variables
```
// GITHUB TOKEN
TOKEN={author token of this repository}
```
* Build dockers files
```bash
docker-compose -f docker-compose.yml build
```
* Start project
```bash
docker-compose -f docker-compose.yml up
```
* Got to browser and write http://localhost:3000 this show you the web app
* Click in watch demo
* Can write http://localhost:5000 this is the url to api

Also you can run the test
* You need have to installed node version 15
* Go to backend folder
* Run `npm install`
* Now export the faketoken in the terminal `export TOKEN=1a2b3c4d5e6f7g8h9`
* Run tests `npm run test`
* Get coverge report `npm run test:coverage`

You can read more details on docs folder
1 change: 1 addition & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
TOKEN=
12 changes: 12 additions & 0 deletions backend/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"env": {
"commonjs": true,
"es2021": true,
"node": true
},
"extends": ["airbnb-base"],
"parserOptions": {
"ecmaVersion": 12
},
"rules": {}
}
3 changes: 3 additions & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/node_modules
.env
/coverage
4 changes: 4 additions & 0 deletions backend/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"semi": true,
"singleQuote": true
}
33 changes: 33 additions & 0 deletions backend/__tests__/components/branches/router.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const branchesRouter = require('../../../components/branches/router');
const testServer = require('../../../mocks/testServer');
const Controller = require('../../../components/branches/controller');

const fakeServie = {
findAll: async (repositoryName) => {
return { repositoryName };
},
};
const controller = Controller(fakeServie);

describe('router - branches', () => {
const request = testServer(branchesRouter, '/api', controller);

describe('GET /api/:author/:repositoryName/branches', () => {
test('should response with status code 200', (done) => {
request
.get('/api/franciscogustavo/bloging-client/branches')
.expect(200, done);
});

test('should response with a object json', (done) => {
request
.get('/api/franciscogustavo/bloging-client/branches')
.end((err, res) => {
expect(res.body).toEqual({
repositoryName: 'franciscogustavo/bloging-client',
});
done();
});
});
});
});
62 changes: 62 additions & 0 deletions backend/__tests__/components/commits/router.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
const branchesRouter = require('../../../components/commits/router');
const testServer = require('../../../mocks/testServer');
const Controller = require('../../../components/commits/controller');

const fakeServie = {
findAll: async (repositoryName, branchName) => {
return { repositoryName, branchName };
},

findOne: async (repositoryName, commitSha) => {
return { repositoryName, commitSha };
},
};
const controller = Controller(fakeServie);

describe('router - branches', () => {
const request = testServer(branchesRouter, '/api', controller);

describe('GET /api/:author/:repositoryName/branches/:branchName/commits', () => {
test('should response with status code 200', (done) => {
request
.get('/api/franciscogustavo/bloging-client/branches/main/commits')
.expect(200, done);
});

test('should response with a object json', (done) => {
request
.get('/api/franciscogustavo/bloging-client/branches/main/commits')
.end((err, res) => {
expect(res.body).toEqual({
repositoryName: 'franciscogustavo/bloging-client',
branchName: 'main',
});
done();
});
});
});

describe('GET /api/:author/:repositoryName/branches/:branchName/commits/:commitSha', () => {
test('should response with status code 200', (done) => {
request
.get(
'/api/franciscogustavo/bloging-client/branches/main/commits/b5b571f28498575a7a47abc241854008b439e93e'
)
.expect(200, done);
});

test('should response with a object json', (done) => {
request
.get(
'/api/franciscogustavo/bloging-client/branches/main/commits/b5b571f28498575a7a47abc241854008b439e93e'
)
.end((err, res) => {
expect(res.body).toEqual({
commitSha: 'b5b571f28498575a7a47abc241854008b439e93e',
repositoryName: 'franciscogustavo/bloging-client',
});
done();
});
});
});
});
75 changes: 75 additions & 0 deletions backend/__tests__/components/pulls/router.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
const branchesRouter = require('../../../components/pulls/router');
const testServer = require('../../../mocks/testServer');
const Controller = require('../../../components/pulls/controller');

const fakeServie = {
findAll: async (repositoryName, { state }) => {
return { repositoryName, state };
},
compareBranches: async () => {},
createPull: async (repositoryName, { title, body, head, base }) => {
return { repositoryName, title, body, head, base };
},
createMerge: async () => {},
updatePull: async () => {},
};
const controller = Controller(fakeServie);

describe('router - branches', () => {
const request = testServer(branchesRouter, '/api', controller);

describe('GET /api/franciscogustavo/bloging-client/pulls?state=all', () => {
test('should response with status code 200', (done) => {
request
.get('/api/franciscogustavo/bloging-client/pulls?state=all')
.expect(200, done);
});

test('should response with a object json', (done) => {
request
.get('/api/franciscogustavo/bloging-client/pulls?state=all')
.end((err, res) => {
expect(res.body).toEqual({
repositoryName: 'franciscogustavo/bloging-client',
state: 'all',
});
done();
});
});
});

describe('POST /api/franciscogustavo/bloging-client/pulls?state=all', () => {
test('should response with status code 200', (done) => {
request
.post('/api/franciscogustavo/bloging-client/pulls?state=all')
.send({
title: 'Testing branch',
body: 'The description is important',
head: 'new-feature',
base: 'main',
})
.expect(200, done);
});

test('should response with a object json', (done) => {
request
.post('/api/franciscogustavo/bloging-client/pulls?state=all')
.send({
title: 'Testing branch',
body: 'The description is important',
head: 'new-feature',
base: 'main',
})
.end((err, res) => {
expect(res.body).toEqual({
repositoryName: 'franciscogustavo/bloging-client',
title: 'Testing branch',
body: 'The description is important',
head: 'new-feature',
base: 'main',
});
done();
});
});
});
});
16 changes: 16 additions & 0 deletions backend/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const express = require('express');
const morgan = require('morgan');
const cors = require('cors');
const config = require('config');
const app = express();

const API_V1_ROUTES = require('./router');

app.use(cors());
app.use(morgan(config.get('logger')));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));

API_V1_ROUTES(app);

module.exports = app;
14 changes: 14 additions & 0 deletions backend/bin/www
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env node

const http = require('http');
const config = require('config');
const debug = require('debug')('backend:app');
const app = require('../app');

const PORT = config.get('port');

const server = http.createServer(app);

server.listen(PORT, () => {
debug(`Server is runing on port ${PORT}`);
});
11 changes: 11 additions & 0 deletions backend/components/branches/branches.dto.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const branchesDto = (resources) => {
return resources.map((resource) => ({
name: resource.name,
commit: resource.commit.sha,
protected: resource.protected,
}));
};

module.exports = {
branchesDto,
};
19 changes: 19 additions & 0 deletions backend/components/branches/controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const Controller = (service) => {
const findAll = async (req, res, next) => {
const { author, repositoryName } = req.params;
try {
const retrievedBranches = await service.findAll(
`${author}/${repositoryName}`
);
return res.status(200).json(retrievedBranches);
} catch (error) {
return next(error);
}
};

return {
findAll,
};
};

module.exports = Controller;
11 changes: 11 additions & 0 deletions backend/components/branches/model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const Model = (GitHubRepository) => {
const findAllBranches = async (repositoryName) => {
const ghRepo = GitHubRepository(repositoryName);
return await ghRepo.branches();
};
return {
findAllBranches,
};
};

module.exports = Model;
20 changes: 20 additions & 0 deletions backend/components/branches/router.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const express = require('express');
const router = express.Router();

const { GitHubRepository } = require('../../utils/githubClient');
const Model = require('./model');
const Service = require('./service');
const Controller = require('./controller');

const model = Model(GitHubRepository);
const service = Service(model);
const controller = Controller(service);

const branchesRouter = (app, route, ctr = controller) => {
const { findAll } = ctr;
router.get('/:author/:repositoryName/branches', findAll);

app.use(route, router);
};

module.exports = branchesRouter;
Loading