├── README.md ├── docker-compose.yml └── packages └── corrections ├── .eslintrc.js ├── .gitignore ├── .prettierrc ├── README.md ├── nest-cli.json ├── package.json ├── src ├── app.controller.ts ├── app.module.ts └── main.ts ├── tsconfig.build.json ├── tsconfig.json └── yarn.lock /README.md: -------------------------------------------------------------------------------- 1 | ![image](https://user-images.githubusercontent.com/40845824/121069742-3accdb00-c7a4-11eb-87d0-3dc47e433762.png) 2 | 3 | # 🚀 Back end challenge 4 | 5 | Bem-vindo(a). Este é o desafio Back end! 6 | 7 | O objetivo deste desafio é avaliar suas habilidades de programação. 8 | Quando sua solução estiver pronta, basta responder o e-mail que recebeu com o link do seu repo aqui no Github! 9 | Em seguida, enviaremos o feedback e as instruções dos próximos passos! 10 | 11 | Caso tenha alguma dúvida, pode enviá-las em resposta ao e-mail que recebeu o teste. Bom desafio! 12 | Bom desafio! 13 | 14 | > ⚠️ **É importante que o seu repo esteja público, caso contrário não iremos conseguir avaliar sua resposta** 15 | 16 | --- 17 | 18 | - [🧠 Contexto](#-contexto) 19 | - [🚰 Fluxo esperado](#-fluxo-esperado) 20 | - [⚔️ Desafio](#️-desafio) 21 | - [📓 Submissão](#-submissão) 22 | - [✔️ Critérios de Avaliação](#️-critérios-de-avaliação) 23 | - [😎 Seria legal](#-seria-legal) 24 | - [:rocket: Instruções](#rocket-instruções) 25 | - [Docker](#docker) 26 | - [Kafka](#kafka) 27 | - [GraphQL](#graphql) 28 | - [:notebook: To-do list](#notebook-to-do-list) 29 | 30 | # 🧠 Contexto 31 | 32 | Para deixar a jornada dos nossos alunos mais completa, a Rocketseat disponibiliza desafios que estimulam a prática do conteúdo estudado. Sabendo disso, este projeto consiste em implementar um serviço que gerencie os desafios enviados por nossos alunos. 33 | 34 | Neste projeto, está incluído o **[corrections](packages/corrections) (serviço de correção das submissões)** já pré-configurado, sua missão será implementar os fluxos de: 35 | 36 | - Interação com Desafios e Submissões. (Criar, buscar, editar e remover); 37 | - Atualização das submissões utilizando a integração com o serviço [corrections](packages/corrections); 38 | 39 | ### 🚰 Fluxo esperado 40 | 41 | - Uma submissão de um desafio é **enviada**; 42 | - A submissão é registrada com o status `Pending`; 43 | - :warning: **Caso não exista o desafio** ou a **url não seja um repositório do github** a submissão é registrada com status `Error` e um erro é retornado ao usuário, dando fim a esse fluxo; 44 | - O serviço [corrections](packages/corrections) é notificado e retorna a correção da submissão; 45 | - O status e a nota da submissão são **atualizados**; 46 | 47 | ### ⚔️ Desafio 48 | 49 | | Atributo | Tipo | 50 | | --------------- | -------- | 51 | | Identificador | `uuidv4` | 52 | | Titulo | `texto` | 53 | | Descrição | `texto` | 54 | | Data de criação | `data` | 55 | 56 | **Operações necessárias** 57 | 58 | - [ ] Criar 59 | - [ ] Remover 60 | - [ ] Editar 61 | - [ ] Listar 62 | - [ ] Paginação 63 | - [ ] Busca por título e descrição 64 | 65 | ### 📓 Submissão 66 | 67 | | Atributo | Tipo | 68 | | ------------------------ | ---------------------- | 69 | | Identificador | `uuidv4` | 70 | | Identificador do desafio | `uuidv4` | 71 | | Link para o reposítorio | `texto` | 72 | | Data de criação | `data` | 73 | | Status | `Pending, Error, Done` | 74 | | Nota | `númerico` | 75 | 76 | **Operações necessárias** 77 | 78 | - [ ] Enviar 79 | - [ ] Listar 80 | - [ ] Filtros: desafio, intervalo de datas, status 81 | - [ ] Paginação 82 | 83 | ## ✔️ Critérios de Avaliação 84 | 85 | Além dos requisitos levantados acima, iremos olhar para os seguintes critérios durante a correção do desafio: 86 | 87 | - Arquitetura (DDD, Clean Architecture) 88 | - Documentação (comente sobre decisões técnicas, escolhas, requisitos, etc) 89 | - Código limpo (utilização de princípios como DRY, KISS, SOLID, YAGNI) 90 | - Testes (unitários, e2e, etc) 91 | - Padrão de commits (Conventional) 92 | 93 | ### 😎 Seria legal 94 | 95 | - **Utilizar [Nest.js](https://nestjs.com/)** 96 | - Custom Scalar Types 97 | 98 | ## :rocket: Instruções 99 | 100 | Chegou a hora de colocar a mão na massa! 101 | 102 | Neste projeto já incluímos alguns arquivos para a configuração do projeto. 103 | 104 | ### Docker 105 | 106 | Criamos um `docker-compose` que faz a configuração de 3 _containers_ incluindo as credenciais (login do postgres, database, etc): 107 | 108 | | Container | Ports | 109 | | --------- | ----------- | 110 | | Postgres | `5432:5432` | 111 | | Kafka | `9092:9092` | 112 | | Zookeper | `2181:2181` | 113 | 114 | ### Kafka 115 | 116 | Escolhemos o utilizar o [Kafka](https://kafka.apache.org/) para a comunicação com o serviço de [corrections](packages/corrections). Caso você utilize Nest.js, o mesmo possui uma [integração completa com essa ferramenta](https://docs.nestjs.com/microservices/kafka). 117 | 118 | Nas instruções do serviço de [corrections](packages/corrections) estão especificados os tópicos e eventos que a aplicação deve utilizar. 119 | 120 | ![image](https://user-images.githubusercontent.com/40845824/122421461-c3950500-cf62-11eb-903a-0b629cc8502f.png) 121 | 122 | :warning: É necessário iniciar o serviço de [corrections](packages/corrections) para que os tópicos do Kafka sejam criados. 123 | 124 | ### GraphQL 125 | 126 | A interação com os desafios e submissões deve ser feita via GraphQL, para isso deixamos uma sugestão das operações a serem criadas, porém sinta-se livre para modelar seu _schema_ da melhor forma: 127 | 128 | ```graphql 129 | Query { 130 | challenges(...): [Challenge!]! 131 | answers(...): [Answer!]! 132 | } 133 | 134 | Mutation { 135 | createChallenge(...): Challenge! 136 | updateChallenge(...): Challenge! 137 | deleteChallenge(...): Challenge! 138 | 139 | answerChallenge(...): Answer! 140 | } 141 | ``` 142 | 143 | ### :notebook: To-do list 144 | - [ ] Fazer o fork do projeto 145 | - [ ] Configurar ambiente de desenvolvimento (inclusive executar o serviço de [corrections](packages/corrections)) 146 | - [ ] Criar uma [API GraphQL](https://docs.nestjs.com/graphql/quick-start) com o [contexto](#-contexto) acima utilizando Typescript 147 | - [ ] Fazer a integração com o serviço de [corrections](packages/corrections) através do Apache Kafka 148 | - [ ] Incluir no README as instruções de instalação do projeto 149 | 150 | :information_source: _Sinta-se livre para incluir quaisquer observações que achar necessário_ 151 | 152 | --- 153 | 154 | _O desafio acima foi cuidadosamente construído para propósitos de avaliação apenas. Já possuimos uma funcionalidade similar na nossa plataforma._ 155 | 156 | Made with 💜 at Rocketseat 157 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | postgres: 5 | container_name: challenge_postgres 6 | image: "bitnami/postgresql" 7 | restart: always 8 | ports: 9 | - "5432:5432" 10 | environment: 11 | - POSTGRESQL_USERNAME=docker 12 | - POSTGRESQL_PASSWORD=docker 13 | - POSTGRESQL_DATABASE=rocketseat_challenge 14 | volumes: 15 | - "postgresql_data:/bitnami/postgresql" 16 | 17 | zookeeper: 18 | container_name: challenge_zookeeper 19 | image: "bitnami/zookeeper:3" 20 | ports: 21 | - "2181:2181" 22 | volumes: 23 | - "zookeeper_data:/bitnami" 24 | environment: 25 | - ALLOW_ANONYMOUS_LOGIN=yes 26 | networks: 27 | - app-net 28 | 29 | kafka: 30 | container_name: challenge_kafka 31 | image: "bitnami/kafka:2" 32 | ports: 33 | - "9092:9092" 34 | volumes: 35 | - "kafka_data:/bitnami" 36 | environment: 37 | - KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181 38 | - KAFKA_CFG_OFFSETS_TOPIC_REPLICATION_FACTOR=1 39 | - KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://localhost:9092 40 | - ALLOW_PLAINTEXT_LISTENER=yes 41 | depends_on: 42 | - zookeeper 43 | networks: 44 | - app-net 45 | 46 | networks: 47 | app-net: 48 | driver: bridge 49 | 50 | volumes: 51 | zookeeper_data: 52 | driver: local 53 | kafka_data: 54 | driver: local 55 | postgresql_data: 56 | driver: local 57 | -------------------------------------------------------------------------------- /packages/corrections/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | parserOptions: { 4 | project: 'tsconfig.json', 5 | sourceType: 'module', 6 | }, 7 | plugins: ['@typescript-eslint/eslint-plugin'], 8 | extends: [ 9 | 'plugin:@typescript-eslint/recommended', 10 | 'plugin:prettier/recommended', 11 | ], 12 | root: true, 13 | env: { 14 | node: true, 15 | jest: true, 16 | }, 17 | ignorePatterns: ['.eslintrc.js'], 18 | rules: { 19 | '@typescript-eslint/interface-name-prefix': 'off', 20 | '@typescript-eslint/explicit-function-return-type': 'off', 21 | '@typescript-eslint/explicit-module-boundary-types': 'off', 22 | '@typescript-eslint/no-explicit-any': 'off', 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /packages/corrections/.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /node_modules 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | lerna-debug.log* 12 | 13 | # OS 14 | .DS_Store 15 | 16 | # Tests 17 | /coverage 18 | /.nyc_output 19 | 20 | # IDEs and editors 21 | /.idea 22 | .project 23 | .classpath 24 | .c9/ 25 | *.launch 26 | .settings/ 27 | *.sublime-workspace 28 | 29 | # IDE - VSCode 30 | .vscode/* 31 | !.vscode/settings.json 32 | !.vscode/tasks.json 33 | !.vscode/launch.json 34 | !.vscode/extensions.json -------------------------------------------------------------------------------- /packages/corrections/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } -------------------------------------------------------------------------------- /packages/corrections/README.md: -------------------------------------------------------------------------------- 1 | # Corrections 2 | 3 | Esse é o serviço responsável pela correção dos desafios e nele toda a comunicação é feita via Apache Kafka. 4 | Aqui, nos comunicamos utilizando o tópico `challenge.correction` (consumer groupId: `challenge-consumer`). 5 | 6 | ```typescript 7 | interface CorrectLessonMessage { 8 | value: { 9 | submissionId: string; 10 | repositoryUrl: string; 11 | }; 12 | } 13 | 14 | interface CorrectLessonResponse { 15 | submissionId: string; 16 | repositoryUrl: string; 17 | grade: number; 18 | status: 'Pending' | 'Error' | 'Done'; 19 | } 20 | ``` 21 | 22 | ### Executando o app 23 | 24 | ```bash 25 | # development 26 | $ npm run start 27 | 28 | # watch mode 29 | $ npm run start:dev 30 | 31 | # production mode 32 | $ npm run start:prod 33 | ``` 34 | -------------------------------------------------------------------------------- /packages/corrections/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "collection": "@nestjs/schematics", 3 | "sourceRoot": "src" 4 | } 5 | -------------------------------------------------------------------------------- /packages/corrections/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "corrections", 3 | "version": "0.0.1", 4 | "description": "", 5 | "author": "", 6 | "private": true, 7 | "license": "UNLICENSED", 8 | "scripts": { 9 | "prebuild": "rimraf dist", 10 | "build": "nest build", 11 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", 12 | "start": "nest start", 13 | "start:dev": "nest start --watch", 14 | "start:debug": "nest start --debug --watch", 15 | "start:prod": "node dist/main", 16 | "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", 17 | "test": "jest", 18 | "test:watch": "jest --watch", 19 | "test:cov": "jest --coverage", 20 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", 21 | "test:e2e": "jest --config ./test/jest-e2e.json" 22 | }, 23 | "dependencies": { 24 | "@nestjs/common": "^7.6.15", 25 | "@nestjs/core": "^7.6.15", 26 | "@nestjs/microservices": "^7.6.18", 27 | "@nestjs/platform-express": "^7.6.15", 28 | "kafkajs": "^1.15.0", 29 | "reflect-metadata": "^0.1.13", 30 | "rimraf": "^3.0.2", 31 | "rxjs": "^6.6.6" 32 | }, 33 | "devDependencies": { 34 | "@nestjs/cli": "^7.6.0", 35 | "@nestjs/schematics": "^7.3.0", 36 | "@nestjs/testing": "^7.6.15", 37 | "@types/express": "^4.17.11", 38 | "@types/jest": "^26.0.22", 39 | "@types/node": "^14.14.36", 40 | "@types/supertest": "^2.0.10", 41 | "@typescript-eslint/eslint-plugin": "^4.19.0", 42 | "@typescript-eslint/parser": "^4.19.0", 43 | "eslint": "^7.22.0", 44 | "eslint-config-prettier": "^8.1.0", 45 | "eslint-plugin-prettier": "^3.3.1", 46 | "jest": "^26.6.3", 47 | "prettier": "^2.2.1", 48 | "supertest": "^6.1.3", 49 | "ts-jest": "^26.5.4", 50 | "ts-loader": "^8.0.18", 51 | "ts-node": "^9.1.1", 52 | "tsconfig-paths": "^3.9.0", 53 | "typescript": "^4.2.3" 54 | }, 55 | "jest": { 56 | "moduleFileExtensions": [ 57 | "js", 58 | "json", 59 | "ts" 60 | ], 61 | "rootDir": "src", 62 | "testRegex": ".*\\.spec\\.ts$", 63 | "transform": { 64 | "^.+\\.(t|j)s$": "ts-jest" 65 | }, 66 | "collectCoverageFrom": [ 67 | "**/*.(t|j)s" 68 | ], 69 | "coverageDirectory": "../coverage", 70 | "testEnvironment": "node" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /packages/corrections/src/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller } from '@nestjs/common'; 2 | import { MessagePattern, Payload } from '@nestjs/microservices'; 3 | 4 | interface CorrectLessonMessage { 5 | value: { 6 | submissionId: string; 7 | repositoryUrl: string; 8 | }; 9 | } 10 | 11 | interface CorrectLessonResponse { 12 | submissionId: string; 13 | repositoryUrl: string; 14 | grade: number; 15 | status: 'Pending' | 'Error' | 'Done'; 16 | } 17 | 18 | @Controller() 19 | export class AppController { 20 | @MessagePattern('challenge.correction') 21 | correctLesson( 22 | @Payload() message: CorrectLessonMessage, 23 | ): CorrectLessonResponse { 24 | const { submissionId, repositoryUrl } = message.value; 25 | 26 | return { 27 | submissionId, 28 | repositoryUrl, 29 | grade: Math.floor(Math.random() * 10) + 1, 30 | status: 'Done', 31 | }; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/corrections/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { AppController } from './app.controller'; 3 | 4 | @Module({ 5 | imports: [], 6 | controllers: [AppController], 7 | providers: [], 8 | }) 9 | export class AppModule {} 10 | -------------------------------------------------------------------------------- /packages/corrections/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | import { MicroserviceOptions, Transport } from '@nestjs/microservices'; 3 | import { AppModule } from './app.module'; 4 | 5 | async function bootstrap() { 6 | const app = await NestFactory.createMicroservice( 7 | AppModule, 8 | { 9 | transport: Transport.KAFKA, 10 | options: { 11 | client: { 12 | brokers: ['localhost:9092'], 13 | }, 14 | consumer: { 15 | groupId: 'challenge-consumer', 16 | }, 17 | }, 18 | }, 19 | ); 20 | 21 | app.listen(() => console.log('Kafka consumer service is listening!')); 22 | } 23 | bootstrap(); 24 | -------------------------------------------------------------------------------- /packages/corrections/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/corrections/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "allowSyntheticDefaultImports": true, 9 | "target": "es2017", 10 | "sourceMap": true, 11 | "outDir": "./dist", 12 | "baseUrl": "./", 13 | "incremental": true 14 | } 15 | } 16 | --------------------------------------------------------------------------------