├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── server.js └── src ├── app.js ├── controller └── empresasTecController.js ├── models └── empresasTec.json └── routes ├── empresasTecRoutes.js └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SEMANA 3 e 4 - Criação de API REST 2 | 3 | Olá, pessoal! Chegamos na semana tão aguardada, a da contrução de uma API, estrutura muito necessária para a comunicação do cliente e servidor através da troca de dados por requisições e respostas. 4 | 5 | Agora, é o momento de prestarem bastante atenção, focar nos conceitos e caprichar nos testes e repetições. 6 | 7 | ## O que é uma API REST? 8 | 9 | Antigamente quando desenvolvíamos uma aplicação WEB, não existia uma separação clara do código de FrontEnd e Backend. O código para fazer as telas (Frontend), era em conjunto com o código de negócio (Backend), criando uma forte dependência entre ambos. Hoje ainda é utilizado esse modelo em alguns casos. É sempre importante avaliarmos o projeto que precisamos fazer, para decidirmos o melhor caminho para desenvolvê-lo. 10 | 11 | Nesse curso estamos aprendendo como fazer uma API REST, que é uma API Backend, que irá disponilizar rotas, para que o Frontend (tela), consiga se comunicar com a parte da lógica por trás (Backend). Para isso criamos "Endpoints" na nossa API Rest do Backend e deixamos disponíveis para serem utilizadas pelo Frontend. Em outras palavras, criamos possibilidades de ações que podem ser chamadas pelo Frontend. 12 | 13 | ### Exemplo 14 | 15 | Criei um botão na tela (Frontend) que ao ser clicado precisa listar todas as alunas do nosso curso. Para isso, foi necessário criar uma API de alunas, e desenvolver uma rota que fornece como resposta a listagem de alunas do nosso curso. O Frontend então chama essa rota do nosso Backend, que responde a listagem de alunas. Com essa informação em mãos, o Frontend consegue trabalhar com esses dados, podendo colocar essas informção em uma tabela, em uma listagem, ou onde for necessário para a visualização do usuário. 16 | 17 | Caso no futuro eu queira mudar as telas (Frontend) ou mesmo o código da API (Backend), mantendo as rotas disponibilizadas, poderei fazer sem precisar trocar a outra parte. Com a API REST o código do Backend é independente do Frontend e vice-versa. 18 | 19 | ## Relembrando os Verbos HTTP 20 | 21 | O protocolo HTTP define alguns verbos para as requisições que indicam uma ação a ser executada. Isso significa que para realizar uma requisicão é necessário utilizar o verbo correto para a chamada que quero fazer. Os verbos mais utilizados são: 22 | 23 | | Verbo | Descrição 24 | | ---------- | -------------------------------------------------------------------------------------------------------------------------------------| 25 | | POST | É utilizado quando queremos criar uma informação por meio da api. | 26 | | GET | É utilizado quando queremos recuperar a representação de um recurso. Requisições utilizando esse verbo, devem retornar apenas dados. | 27 | | PUT | O verbo PUT é utilizado quando queremos alterar integralmente um recurso. | 28 | | PATCH | O verbo PUT é utilizado quando queremos alterar parcialmente um recurso. | 29 | | DELETE | O verbo DELETE é utilizado quando queremos remover um recurso. 30 | 31 | 32 | 33 | ## Relembrando o Http Status 34 | 35 | Quando fazemos uma requisição na API (quando chamamos uma rota), como vamos saber se deu tudo certo nessa chamada? Ou como vamos saber se deu algum erro? Como saber qual o status da resposta da chamada que fiz? Para isso existe o Http status, com códigos com uma convenção de resposta. Existem inúmeros códigos, mas vamos nos concentrar nos mais utilizados: 36 | 37 | | Código | Status | Descrição | Exemplo de Códigos | 38 | | ------ | ------------- | ---------------------------------- |-------------------------------------------------------- 39 | | 2xx | `SUCCESSFUL` | Quando deu certo o retorno | 200 OK, 202 Accepted, 204 No Content | 40 | | 3xx | `REDIRECTION` | Quando ocorre um redirecionamento | 301 Moved Permanently, 303 See Other, 304 Not Modified | 41 | | 4xx | `CLIENT ERROR`| Quando há erro do lado do cliente | 400 Bad Request, 401 Unauthorized, 403 Forbidden | 42 | | 5xx | `SERVER ERROR`| Quando há erro do lado do servidor | 500 Internal Server Error, 501 Not Implemented | 43 | 44 | 45 | Então na nossa API devemos informar o código ao responder as requisições feitas pelas rotas que desenvolvemos. Caso nossa resposta seja com sucesso, passamos então um status 200. Caso dê algum erro que foi ocasionado por responsabilidade do usuário, enviamos um erro 4xx. Por exemplo, se um usuário não tem permissão de acesso para chamar uma rota que criamos, devemos retornar para ele um status 401, que significa que ele não está autorizado. Porém caso dê algum erro que seja de responsabilidade da nossa API, poderemos retornar um status 500. 46 | 47 | # Projeto API Nodejs "Jansen's Films" 48 | 49 | Parabéns, você acaba de ser contratada por uma empresa de audio visual chamada Jansen's Films para desenvolver um novo produto que deverá ser lançado em breve. Nesse estágio inicial do produto, o mesmo consistirá em um aplicativo e uma página web onde o usuário poderá controlar uma lista com filmes que já assistiu e que gostaria de assistir. 50 | 51 | 52 | 53 | 54 | Você será a pessoa desenvolvedora backend responsável pelo desenvolvimento da API que deverá ser feito em Nodejs. Em paralelo, o time de Frontend irá desenvolver o aplicativo 55 | e a página web que irão se comunicar com a API que você irá desenvolver. 56 | 57 | A listagem de filmes será no seguinte formato: ```{ nome, genero, sinopse, assistido/ não assistido }``` 58 | 59 | O novo produto deverá: 60 | 61 | - [x] poder listar todos os filmes da lista do usuário 62 | - [x] poder adicionar um novo filme 63 | - [x] poder remover filme da lista 64 | - [x] poder alterar informações do filme 65 | - [x] poder marcar/desmarcar filme como assistido 66 | 67 | Sendo assim precisaremos criar 5 rotas: 68 | 69 | | Verbo | Descrição da Rota | 70 | | ------ | --------------------------------------| 71 | | POST | Adicionar novo filme | 72 | | GET | Recuperar filme | 73 | | DELETE | Remover filme | 74 | | PUT | Alterar informações do filme | 75 | | PATCH | Marcar/Desmarcar filme como assistido | 76 | 77 | 78 | ## Como criar uma nova API Nodejs? 79 | 80 | Primeiro, para a construção do backend do nosso produto em Nodejs criaremos uma pasta chamada "jansensfilms". Abriremos a mesma no programa Visual Studio Code e inicializaremos o terminal nessa mesma pasta. 81 | 82 | 83 | ### Iniciando a API Nodejs 84 | 85 | Com o terminal aberto na pasta "jansensfilms", para iniciar nossa API Nodejs, precisamos inicializar o *package manager*, que é o gerenciador de pacotes do Node. Para isso executaremos ```npm init``` no terminal. Pressionando “Enter”, serão exibidas uma sequência de perguntas que deverão ser preenchidas ou mantidas o valor padrão. 86 | 87 | Com isso um arquivo com o nome de package.json será criado. Esse arquivo é muito importante pois define que o nosso projeto como sendo Node. 88 | 89 | ### Instalando o Express 90 | 91 | Feito isso, precisaremos instalar o Express no nosso projeto, que é um framework que nos trará facilidades. Para isso executaremos no terminal: 92 | 93 | ``` npm install express --save ``` 94 | 95 | O *--save* é necessário para especificar que esse pacote do express é uma dependência da nossa aplicação e que o nosso projeto obrigatoriamente precisa dela para funcionar. Quando uma outra pessoa baixar seu projeto, ao instalar as dependências, esse pacote também será instalado. Isso porque quando você usa o --save, esse pacote é referenciado em “dependencies” no arquivo package.json. A sessão “ dependencies”, desse arquivo, lista justamente as dependências do nosso projeto. 96 | 97 | Ao rodar a instalação do express, uma *pasta node_modules* com os pacotes do meu projeto será criada. Se reparar, dentro dessa pasta teremos uma pasta chamada “express”. Toda vez que você rodar o comando ``` npm install``` essa pasta node_modules será atualizada com as últimas atualizações conforme o que estiver configurado no arquivo *package.json*. 98 | 99 | ### Criando o arquivo .gitignore 100 | 101 | Devemos criar na raíz do "jansensfilmes" o arquivo *.gitignore* e escrever nele ```node_modules/``` para o git nao trackear essa pasta para commit. 102 | 103 | ### Criando a estrutura da nossa API 104 | 105 | Primeiramente, iremos criar uma pasta chamada “src” (de “source”) na raiz do nosso projeto, onde armazenaremos todos os códigos da aplicação. Dentro dessa, criaremos três pastas: 106 | 107 | - [x] controllers - para armazenar a lógica de controle da nossa api 108 | - [x] models - para armazenar os nosso modelos (ex: nossos filmes) 109 | - [x] routes - para armazenar as rotas 110 | 111 | ``` 112 | jansensfilms 113 | ├── src 114 | │ ├── controllers 115 | │ ├── models 116 | │ ├── routes 117 | ├── package.json 118 | ``` 119 | ### Criando o servidor 120 | 121 | Deveremos criar dentro de *src/* um arquivo chamado *app.js*. Nesse arquivo faremos as configurações da nossa aplicação. Inicializaremos configuraremos a mesma para utilizar o express. Nesse arquivo criaremos uma constante express que receberá o módulo express. Utilizaremos essa constante para configurar nossa aplicação: 122 | 123 | ```app.js 124 | const express = require("express") 125 | const app = express() 126 | 127 | app.use(express.json()) 128 | 129 | app.use(function (req, res, next) { 130 | res.header("Access-Control-Allow-Origin", "*") // informo que minha api poderá ser chamada de qualquer lugar. Por um browser, por exemplo. 131 | res.header( 132 | "Access-Control-Allow-Headers", 133 | "Origin, X-Requested-With, Content-Type, Accept" 134 | ) 135 | next() 136 | // como criei uma função dentro do app.use, preciso dar um "next()" para mandar ele seguir para a próxima middleware. 137 | // se eu não faço isso, a requisição vai ficar travada aí. 138 | }) 139 | 140 | module.exports = app 141 | ``` 142 | 143 | O *app.use* adiciona uma middleware na nossa aplicação. Por exemplo, quando fazemos ```app.use(express.json())```, estou dizendo que minha api irá trabalhar com json. Isso significa, por exemplo, que quando eu fizer um POST, minha api irá entender que vou receber um json. 144 | 145 | Criaremos agora, na raíz de "jansensfilms", um arquivo chamado “server.js” para configurarmos nosso servidor. Nesse arquivo criaremos uma constante *app* que receberá nossa aplicação express que criamos no arquivo *app.js*. No caso definimos a porta 3000 para o nosso servidor rodar quando for inicializado. 146 | 147 | ```server.js 148 | const app = require("./src/app") 149 | const port = 3000; 150 | 151 | app.listen(port, () => { 152 | console.log(`Servidor está rodando na porta ${port}`); 153 | }); 154 | ``` 155 | 156 | Criamos nossa rota, porém ainda não fizemos nossa aplicação utilizá-la. Para isso temos que ir no arquivo *app.js*, definirmos uma constante para nossa rota "index" e setar para nossa aplicação utilizar: 157 | 158 | 159 | ```app.js 160 | const index = require("./routes/index") 161 | app.use("/", index) 162 | ``` 163 | Nosso arquivo *app.js* deverá então ficar da seguinte forma: 164 | 165 | ```app.js 166 | const express = require("express") 167 | const app = express() 168 | 169 | //rotas 170 | const index = require("./routes/index") 171 | 172 | app.use(express.json()) 173 | 174 | app.use(function (req, res, next) { 175 | res.header("Access-Control-Allow-Origin", "*") 176 | res.header( 177 | "Access-Control-Allow-Headers", 178 | "Origin, X-Requested-With, Content-Type, Accept" 179 | ) 180 | next() 181 | }) 182 | 183 | app.use("/", index) 184 | 185 | module.exports = app 186 | ``` 187 | 188 | Agora com a rota desenvolvida, ao executarmos no browser *http://localhost:3000* não deverá mais apresentar o erro de GET. 189 | 190 | 191 | Quando criamos o servidor utilizando o protocolo HTTP, definimos um callback que será executado sempre que recebermos uma requisição web. Nesse caso, esse callback seria executado quando o nosso servidor for iniciado e aparecerá a mensagem “Servidor está rodando na porta 3000”. 192 | 193 | Como nosso arquivo que irá inicializar o servidor se chama "server.js", devemos informar isso no arquivo *package.json* alterando ```"main": "index.js"``` para ```"main": "server.js"```. 194 | 195 | 196 | 197 | ### Testando o servidor 198 | 199 | Vamos testar nosso servidor? Para isso executaremos o comando ```node server.js``` no terminal. Ao executar o comando, a mensagem informando que o servidor está rodando será exibida. 200 | 201 | 202 | 203 | ### Nodemon 204 | 205 | Caso você esteja com o servidor rodando e tente alterar algum arquivo, para que o servidor capte essas alterações será necessário reiniciá-lo manualmente. 206 | Porém é bem chato ficar fazendo isso. Para evitar esse tipo de problema, podemos utilizar o *nodemon* para inicializar nosso servidor. Para utilizá-lo, deveremos primeiramente instalá-lo rodando o comando ```npm install nodemon --save```. Com o nodemon instalado, para rodar nosso servidor o utilizando, deveremos utilizar o comando ```nodemon server.js```. Com isso nosso servidor será inicializado com o nodemon e você poderá editar seus arquivos sem precisar reiniciá-lo. 207 | 208 | 209 | 210 | ### Scripts package.json 211 | 212 | Para não precisar ficar escrevendo ```nodemon server.js``` para inicializar o servidor, podemos ir no nosso arquivo *package.json* e editar o atributo "scripts" do json. Poderemos incluir um script de start, informando que quando ele for utilizado, executará o comando ```nodemon server.js```: 213 | 214 | ```package.json 215 | "scripts": { 216 | "test": "echo \"Error: no test specified\" && exit 1", 217 | "start": "nodemon server.js" 218 | } 219 | ``` 220 | 221 | Dessa forma para inicializar o servidor, basta digitar ```npm start``` no terminal e pressionar enter, que o mesmo já chamará automaticamente o comando ```nodemon server.js```. 222 | 223 | 224 | 225 | ### Vamos criar nossa primeira rota GET! 226 | 227 | Com o projeto configurado e com o servidor rodando, caso a gente tente executar no browser *http://localhost:3000*, vamos receber a mensagem “Cannot GET”. Isso significa que o nosso servidor ainda não está habilitado a devolver uma resposta do método GET no endereço “/“. Isso tudo porque ainda não definimos nenhuma rota no nosso projeto. 228 | 229 | Vamos então criar a primeira rota da nossa API! Dentro da pasta *routes/* deveremos criar um arquivo chamado *index.js*. Nesse arquivo iremos criar nossa primeira rota GET. Nesse iremos definir que quando chamarmos *http://localhost:3000* a mesma será chamada: 230 | 231 | ```index.js 232 | const express = require("express") 233 | const router = express.Router() 234 | 235 | router.get("/", function (req, res) { 236 | res.status(200).send({ 237 | title: "Reprograma - On7 Semana 11 - Projeto Revisão", 238 | version: "1.0.0" 239 | }) 240 | }) 241 | 242 | module.exports = router 243 | ``` 244 | 245 | 246 | 247 | ### Nova rota de GET para retornar os filmes 248 | 249 | A empresa Jansen's Films acabou de te enviar uma base de dados de exemplo chamado *movies.json*. Essa contém uma listagem de filmes que deveremos trabalhar. Com a listagem em mãos, poderemos desenvolver uma rota GET que exibirá essa listagem toda vez que uma requisição para listar os filmes seja chamada: 250 | 251 | ```json 252 | [ 253 | { 254 | "id": 1, 255 | "name": "Malévola", 256 | "genre": "Aventura", 257 | "synopsis": "Malévola, uma jovem de coração puro, vive em um pacífico reino na floresta (...)", 258 | "watched": true 259 | }, 260 | { 261 | "id": 2, 262 | "name": "Um sonho possível", 263 | "genre": "Drama", 264 | "synopsis": "Michael Oher é negro, pobre, grandalhão e calado. Ele foi abandonado pela mãe e agora (...)", 265 | "watched": false 266 | }, 267 | { 268 | "id": 3, 269 | "name": "Jogos Vorazes", 270 | "genre": "Aventura", 271 | "synopsis": "Num futuro distante, boa parte da população é controlada por um regime totalitário, que (...)", 272 | "watched": false 273 | } 274 | ] 275 | ``` 276 | 277 | Para que nosso projeto fique organizado, iremos colocar o arquivo *movies.json* que você recebeu dentro da pasta *models*. Iremos, em seguida, na pasta *routes* e criaremos um arquivo chamado *movies.js*. Nesse, iremos armazenar todas as rotas referentes aos filmes. Nosso projeto deverá estar com a seguinte estrutura: 278 | 279 | ``` 280 | jansensfilms 281 | ├── src 282 | │ ├── controllers 283 | │ ├── models 284 | | ├── movies.json 285 | │ ├── routes 286 | │ ├── index.js 287 | │ ├── movies.js 288 | | ├── app.js 289 | ├── package.json 290 | ├── server.js 291 | ``` 292 | 293 | Primeiramente, deveremos informar a nossa aplicação que iremos utilizar as rotas que iremos criar para os filmes. Para isso deveremos abrir a pasta *src* e editar o arquivo *app.js* 294 | 295 | ```app.js 296 | const movies = require("./routes/movies") 297 | app.use("/movies", movies) 298 | ``` 299 | 300 | Estamos dizendo para a aplicação utilizar as rotas do arquivo *movies.js* e utilizar a rota "/movies" para executá-las. Isso significa que toda vez que você chamar *http://localhost:3000/movies*, as nossas rotas de movies serão chamadas. 301 | 302 | Entretanto, ainda não escrevemos nenhuma rota. Para escrever nossa primeira rota que listará os filmes, deveremos abrir a pasta *routes* e editar o arquivo *movies.js*: 303 | 304 | ```movies.js 305 | const express = require("express") 306 | const router = express.Router() 307 | const controller = require("../controllers/movieController") 308 | 309 | router.get("/", controller.getAllMovies) 310 | 311 | module.exports = router; 312 | 313 | ``` 314 | 315 | Nessa estamos dizendo que toda vez que for utilizado o verbo GET na chamada *http://localhost:3000/movies*, o *controller.getAllMovies* será executado. Mas que *controller.getAllMovies* é esse? Precisamos criar ele ainda, certo? Então vamos lá! 316 | 317 | Primeiramente deveremos criar nosso controller de filmes. Então na pasta *controllers* deveremos criar o arquivo *movieController.js*. Nesse, deveremos criar a função *getAllMovies* que estamos chamando na nossa rota de GET: 318 | 319 | ```movieController.js 320 | const movies = require("../models/movies.json") 321 | 322 | const getAllMovies = (req, res) => { 323 | console.log(req.url) 324 | res.status(200).send(movies) 325 | } 326 | 327 | module.exports = { 328 | getAllMovies, 329 | } 330 | ``` 331 | Nesse arquivo atribuímos nosso json de filmes a uma constante que chamamos de "movies". Então ao chamar a função *getAllMovies* nós respondemos a requisição com o status 200, informando que deu tudo certo e enviando nosso json de filmes. Feito isso, nossa rota que lista todos os filmes está pronta! 332 | 333 | 334 | ### Testando a rota GET via Postman 335 | 336 | Entretanto, nós como desenvolvedoras backend, não iremos utilizar o front para ficar testando nossas rotas no momento de desenvolvimento. Usaremos uma ferramenta para isso, chamada Postman. Essa ferramenta permite testar serviços RESTful por meio do envio de requisições HTTP e da análise do seu retorno. Você pode salvar todas as suas collections e facilitar o seu dia-a-dia como pessoa desenvolvedora! 337 | 338 | Para testar nossa rota GET de listagem de todos os filmes no Postman, deveremos clicar em New > Request. Com a nova requisição aberta, deveremos escolher na combobox o verbo HTTP *GET* e digitar *http://localhost:3000/movies*. Ao clicar no botão *send* o array de json com nossos filmes será exibido. 339 | 340 | ![test_get_postman](https://i.imgur.com/Cby6pIZ.png) 341 | 342 | ### Criando a rota POST 343 | 344 | Para criar um novo filme na nossa listagem, precisaremos escrever uma rota de POST. Para isso no nosso arquivo de rotas de filmes (*routes/movies.js*), iremos incluir a seguinte rota: 345 | 346 | ```movies.js 347 | router.post("/", controller.createMovie) 348 | ``` 349 | Nosso controller ainda não possui a função createMovie que nossa rota está chamando. Então no arquivo *controllers/movieController.js* deveremos implementar a função com o código abaixo: 350 | 351 | ``` 352 | const fs = require("fs") 353 | 354 | const createMovie = (req, res) => { 355 | const { id, name, genre, synopsis, watched } = req.body 356 | movies.push({ id, name, genre, synopsis, watched }) 357 | fs.writeFile("./src/models/movies.json", JSON.stringify(movies), 'utf8', function (err) { // gravando novo filme no array de filmes 358 | if (err) { 359 | res.status(500).send({ message: err }) 360 | } else { 361 | console.log("Arquivo atualizado com sucesso!") 362 | const movieFound = movies.find(movie => movie.id == id) // recupero o filme que foi criei no array de filmes 363 | res.status(200).send(movieFound) 364 | } 365 | }) 366 | } 367 | 368 | module.exports = { 369 | createMovie, 370 | getAllMovies, 371 | } 372 | ``` 373 | De cara, na primeira linha que escrevemos, estamos importando uma biblioteca chamada fs. Essa biblioteca permite que você trabalhe com o sistema de arquivos em seu computador. Precisamos dela para escrever um novo filme no nosso arquivo *movies.json*. Entretanto, antes de utilizá-la, precisamos instalá-la na nossa aplicação. Para isso é necessário rodar no terminal o comando ```npm install fs --save``` 374 | 375 | Em seguida, dentro da função createMovie, extraímos do corpo da requisição enviada pelo cliente (req.body), as informações do filme que iremos adicionar. Em sequência adicionamos nossas informações no array de filmes (nossa listagem de filmes). Logo depois atualizamos nosso arquivo movies.json com o array de filmes com o filme que adicionamos. 376 | 377 | Dando algum erro, devolveremos o status 500 com a mensagem de erro. Caso dê certo, devolveremos o status 200, com o filme que adicionamos e gravamos no arquivo *movies.json*. 378 | 379 | ### Testando a rota POST via Postman 380 | 381 | Para testar via Postman, a rota POST que cria um novo filme na listagem filmes, deveremos clicar em New > Request. Com a nova requisição aberta, deveremos escolher na combobox o verbo HTTP *POST* e digitar *http://localhost:3000/movies*. Deveremos então, passar a informação do novo filme que iremos adicionar. Para isso deveremos clicar em *body* e clicar em *raw*. Logo após trocar a combobox "text" para *JSON*. Isso significa que estamos definindo que iremos enviar um JSON para nossa API quando enviarmos a requisição. Deveremos então informar o seguinte JSON: 382 | 383 | ``` 384 | { 385 | "id": 4, 386 | "name": "The Old Guard", 387 | "genre": "Ação", 388 | "synopsis": "The Old Guard é um filme de ação e ficção científica de super-heróis americano de 2020 dirigido por Gina (...)", 389 | "watched": false 390 | } 391 | ``` 392 | 393 | Ao clicar no botão *send*, enviaremos nosso novo filme para ser criado na nossa API. Dando certo, o filme que enviamos será retornado em tela para a gente. 394 | 395 | ![test_post_postman](https://i.imgur.com/Yq3otnK.png) 396 | 397 | ### Criando a rota GET (by id) 398 | 399 | Para verificarmos nosso novo filme criado, o buscando pelo id 4 (o id do filme que criamos). Será necessário criar uma nova rota de GET que trará o filme, dado um id. No caso queremos que quando chamarmos a rota GET *http://localhost:3000/movies/4* nosso filme que acabamos de criar seja retornado. 400 | 401 | Para isso, no nosso arquivo de rotas de filmes (*routes/movies.js*), deveremos incluir a seguinte rota: 402 | 403 | ```movies.js 404 | router.get("/:id", controller.getMovie) 405 | ``` 406 | Nessa rota informamos que será passado um valor de parâmetro na nossa rota que será o parâmetro id (ex: *http://localhost:3000/movies/4* ). Mais uma vez nosso controller ainda não possui a função que estamos chamando. Então no arquivo *controllers/movieController.js* deveremos implementar a função getMovie com o código abaixo: 407 | 408 | ```movieController.js 409 | const getMovie = (req, res) => { 410 | const movieId = req.params.id 411 | const movieFound = movies.find((movie) => movie.id == movieId) 412 | if (movieFound) { 413 | res.status(200).send(movieFound) 414 | } else { 415 | res.status(404).send({ message: "Filme não encontrado" }) 416 | } 417 | } 418 | 419 | module.exports = { 420 | createMovie, 421 | getMovie, 422 | getAllMovies, 423 | } 424 | ``` 425 | 426 | Nesse, atribuítmos o valor do parametro id (req.params.id) a constante *movieId*. Com isso sabemos o id que foi passado na requisição. Em seguida percorremos o array de filmes, verificando o id no filme (movie.id == movieId). Ao encontrar o filme com o id passado, o mesmo é atribuído a constante "movieFound". Se o "movieFound" tiver valor, em outras palavras, se o filme for encontrado por id, iremos responder a requisição com status 200 e enviaremos na resposta o filme encontrado. Senão, se ele não for encontrado, retornaremos um status 404 (NOT FOUND) na requisição, informando que o mesmo não foi encontrado. 427 | 428 | ### Testando a rota GET by id via Postman 429 | 430 | Para testar nossa rota GET passando o id como parâmetro, via Postman, deveremos clicar em New > Request. Com a nova requisição aberta, deveremos escolher na combobox o verbo HTTP *GET* e digitar *http://localhost:3000/movies/4* (escolhi o id 4 mas você pode testar com outros ids). Ao clicar no botão *send*, se você passou o id de um filme que existe na listagem, o mesmo deverá ser exibido como resposta. Mas caso você passe um id de um filme que não existe, ele deve retornar um status 404 informando que o filme não foi encontrado. 431 | 432 | ![test_get_id_postman](https://i.imgur.com/H1d2lHT.png) 433 | 434 | ### Criando a rota PUT 435 | 436 | Para alterarmos um filme existente no nosso arquivo *movies.json*, Deveremos implementar uma rota de PUT que deverá permitir realizar essa alteração. Para isso, no nosso arquivo de rotas de filmes (*routes/movies.js*), deveremos incluir a seguinte rota: 437 | 438 | ```movies.js 439 | router.put("/:id", controller.updateMovie) 440 | ``` 441 | Nessa rota informamos que será passado um valor de parâmetro na nossa rota que será o parâmetro id (ex: *http://localhost:3000/movies/4* ). Deveremos ir então no arquivo *controllers/movieController.js* para implementar a função *updateMovie*, que ainda não existe, com o código abaixo: 442 | 443 | ```movieController.js 444 | const updateMovie = (req, res) => { 445 | try { 446 | const movieId = req.params.id 447 | const movieToUpdate = req.body //Pego o corpo da requisição com as alterações 448 | 449 | const movieFound = movies.find(movie => movie.id == movieId) // separo o filme que irei atualizar 450 | const movieIndex = movies.indexOf(movieFound) // separo o indice do filme no array de filmes 451 | 452 | if (movieIndex >= 0) { // verifico se o filme existe no array de filmes 453 | movies.splice(movieIndex, 1, movieToUpdate) //busco no array o filme, excluo o registro antigo e substituo pelo novo 454 | } else { 455 | res.status(404).send({ message: "Filme não encontrado para ser atualizado" }) 456 | } 457 | 458 | fs.writeFile("./src/models/movies.json", JSON.stringify(movies), 'utf8', function (err) { // gravo meu json de filmes atualizado 459 | if (err) { 460 | res.status(500).send({ message: err }) // caso dê erro retorno status 500 461 | } else { 462 | console.log("Arquivo de filmes atualizado com sucesso!") 463 | const movieUpdated = movies.find(movie => movie.id == movieId) // separo o filme que modifiquei no array 464 | res.status(200).send(movieUpdated) // envio o filme modificado como resposta 465 | } 466 | }) 467 | } catch (err) { 468 | res.status(500).send({ message: err }) // caso dê erro retorno status 500 469 | } 470 | } 471 | 472 | module.exports = { 473 | createMovie, 474 | updateMovie, 475 | getMovie, 476 | getAllMovies, 477 | } 478 | ``` 479 | 480 | ### Testando a rota PUT via Postman 481 | 482 | Para testar, via Postman, a rota PUT que altera um filme na listagem filmes, deveremos clicar em New > Request. Com a nova requisição aberta, deveremos escolher na combobox o verbo HTTP *PUT* e digitar *http://localhost:3000/movies/4* (escolhi o id 4 mas poderia ter escolhido outro id qualquer existente na lista). Deveremos então, passar a nova informação filme que iremos atualizar. Para isso deveremos clicar em *body* e clicar em *raw*. Logo após trocar a combobox "text" para *JSON*. Isso significa que estamos definindo que iremos enviar um JSON para nossa API quando enviarmos a requisição. Deveremos então informar o seguinte JSON: 483 | 484 | ``` 485 | { 486 | "id": 4, 487 | "name": "The Old Guard", 488 | "genre": "Ficção científica", 489 | "synopsis": "The Old Guard é um filme de ação e ficção científica de super-heróis americano de 2020 dirigido por Gina (...)", 490 | "watched": false 491 | } 492 | ``` 493 | Ao clicar no botão *send*, se você passou o id de um filme que existe na listagem, o mesmo deverá ser retornado com a alteração feita na resposta. Mas caso você passe um id de um filme que não existe, ele deve retornar um status 404 informando que o filme não foi encontrado para ser atualizado. 494 | 495 | ![test_put_postman](https://i.imgur.com/t0PokjX.png) 496 | 497 | ### Criando a rota PATCH 498 | 499 | Precisamos criar uma rota para alterar apenas o status de assistido do nosso filme. Com isso poderemos informar se ele foi assistido ou não. Deveremos então implementar uma rota de PATCH que deverá permitir realizar essa alteração. Para isso, no nosso arquivo de rotas de filmes (*routes/movies.js*), deveremos incluir a seguinte rota: 500 | 501 | ```movies.js 502 | router.patch("/:id/watched", controller.updateWatchedStatus) 503 | ``` 504 | Nessa rota informamos que será passado um valor de parâmetro na nossa rota que será o parâmetro id (ex: *http://localhost:3000/movies/4/watched* ). Deveremos ir então no arquivo *controllers/movieController.js* para implementar a função *updateWatchedStatus*, que ainda não existe, com o código abaixo: 505 | 506 | ```movieController.js 507 | const updateWatchedStatus = (req, res) => { 508 | try { 509 | const movieId = req.params.id // pego a informação do id no parametro da requisição 510 | const watched = req.body.watched // pego a informação de watched no corpo da requisição. Ele terá valor true ou false, dependendo do que tiver sido passado 511 | 512 | const movieToUpdate = movies.find(movie => movie.id == movieId) // separo o filme que irei mudar o status 513 | const movieIndex = movies.indexOf(movieToUpdate) // identifico o índice do filme no meu array 514 | 515 | if (movieIndex >= 0) { // verifico se o filme existe no array de filmes 516 | movieToUpdate.watched = watched //atualizo o objeto com o novo status informando se foi assistido ou não 517 | movies.splice(movieIndex, 1, movieToUpdate) // removo o filme pelo índice substituindo pelo novo 518 | } else { 519 | res.status(404).send({ message: "Filme não encontrado para informar se foi assistido ou não" }) 520 | } 521 | 522 | fs.writeFile("./src/models/movies.json", JSON.stringify(movies), 'utf8', function (err) { // gravo meu json de filmes atualizado 523 | if (err) { 524 | res.status(500).send({ message: err }) 525 | } else { 526 | console.log("Arquivo atualizado com sucesso!") 527 | const movieUpdated = movies.find((movie) => movie.id == movieId) // separo o filme que modifiquei no array 528 | res.status(200).send(movieUpdated) // envio o filme modificado como resposta 529 | } 530 | }) 531 | } catch (err) { 532 | res.status(500).send({ message: err }) 533 | } 534 | } 535 | 536 | module.exports = { 537 | createMovie, 538 | updateMovie, 539 | updateWatchedStatus, 540 | getMovie, 541 | getAllMovies, 542 | } 543 | ``` 544 | 545 | ### Testando a rota PATCH via Postman 546 | 547 | Para testar, via Postman, a rota PATCH que altera o status de assistido do filme, deveremos clicar em New > Request. Com a nova requisição aberta, deveremos escolher na combobox o verbo HTTP *PATCH* e digitar *http://localhost:3000/movies/4* (escolhi o id 4 mas poderia ter escolhido outro id qualquer existente na lista). Deveremos então, passar a informação de que o filme foi assistido ou não para enviar junto na requisição. Para isso deveremos clicar em *body* e clicar em *raw*. Logo após trocar a combobox "text" para *JSON*. Deveremos então informar o seguinte JSON: 548 | 549 | ``` 550 | { 551 | "watched" : true 552 | } 553 | ``` 554 | Ao clicar no botão *send*, se você passou o id de um filme que existe na listagem, o mesmo deverá ser retornado com a alteração de status feita na resposta. Mas caso você passe um id de um filme que não existe, ele deve retornar um status 404 informando que o filme não foi encontrado para ser atualizado. 555 | 556 | ![test_patch_postman](https://i.imgur.com/LGqeSqd.png) 557 | 558 | ## Criando a rota de DELETE 559 | 560 | Precisamos criar uma rota para poder deletar um filme, dado um id. Deveremos então implementar uma rota de DELETE que deverá permitir deletar o filme da nossa listagem. Para isso, no nosso arquivo de rotas de filmes (*routes/movies.js*), deveremos incluir a seguinte rota: 561 | 562 | ```movies.js 563 | router.delete("/:id", controller.deleteMovie) 564 | ``` 565 | Nessa rota informamos que será passado um valor de parâmetro na nossa rota que será o parâmetro id (ex: *http://localhost:3000/movies/4* ). Deveremos ir então no arquivo *controllers/movieController.js* para implementar a função *deleteMovie*, que ainda não existe, com o código abaixo: 566 | 567 | ```movieController.js 568 | const deleteMovie = (req, res) => { 569 | try { 570 | const movieId = req.params.id 571 | const movieFound = movies.find(movie => movie.id == movieId) // encontro o filme pelo id 572 | const movieIndex = movies.indexOf(movieFound) // identifico o índice do filme no meu array 573 | 574 | if (movieIndex >= 0) { // verifico se o filme existe no array de filmes 575 | movies.splice(movieIndex, 1) // removo o filme pelo índice 576 | } else { 577 | res.status(404).send({ message: "Filme não encontrado para ser deletado" }) 578 | } 579 | 580 | fs.writeFile("./src/models/movies.json", JSON.stringify(movies), 'utf8', function (err) { // gravo meu array de filmes sem o filme que deletei 581 | if (err) { 582 | res.status(500).send({ message: err }) 583 | } else { 584 | console.log("Filme deletado com sucesso do arquivo!") 585 | res.sendStatus(204) 586 | } 587 | }) 588 | } catch (err) { 589 | console.log(err) 590 | res.status(500).send({ message: "Erro ao deletar o filme" }) 591 | } 592 | } 593 | 594 | module.exports = { 595 | createMovie, 596 | deleteMovie, 597 | updateMovie, 598 | updateWatchedStatus, 599 | getMovie, 600 | getAllMovies, 601 | } 602 | ``` 603 | 604 | ### Testando a rota DELETE via Postman 605 | 606 | Para testar, via Postman, a rota DELETE que deleta um filme, deveremos clicar em New > Request. Com a nova requisição aberta, deveremos escolher na combobox o verbo HTTP *DELETE* e digitar *http://localhost:3000/movies/4* (escolhi o id 4 mas poderia ter escolhido outro id qualquer existente na lista). Ao clicar no botão *send*, se você passou o id de um filme que existe na listagem, deverá ser retornado um 204 NO CONTENT da API, informando que ok deu tudo certo, não tem nada para retornar. Mas caso você passe um id de um filme que não existe, ele deve retornar um status 404 informando que o filme não foi encontrado para ser deletado. 607 | 608 | ![test_delete_postman](https://i.imgur.com/XQshRFn.png) 609 | 610 | ### API Pronta! 611 | 612 | Desenvolvemos todas as rotas necessárias para nosso produto do Jansen's Films. Criamos a rota de POST (que cria um novo filme), duas rotas de GET (uma para trazer todos os filmes e uma para trazer um filme dado o id), PUT (para alterar o filme), PATCH (para alterar o status de assistido do filme) e DELETE (para deletar o filme). Com todas as rotas desenvolvidas na nossa API de filmes, basta o pessoal do front terminar o desenvolvimento para termos o produto pronto para ser lançado! 613 | 614 | ### Acabamos, e agora? 615 | 616 | ![exercise](https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcQzkx9NbIzjUfe7io1-mvfkRybTZGH-C0RL0A&usqp=CAU) 617 | 618 | Agora que nossa API está implementada, podemos e devemos exercitar! Será que podemos melhorar nosso código? Temos linhas se códigos repetidas que poderiam virar funções e serem reaproveitadas? Sempre há algo para melhorar, então fique a vontade para mexer e melhorar o código! 619 | 620 | Espero que tenha gostado da atividade e o segredo é praticar!!! Quanto mais exercícios fizer, melhor :) Abs e até mais! 621 | 622 | --- 623 | 624 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "projeto-testesenac", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "projeto-testesenac", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "express": "^4.17.1", 13 | "fs": "^0.0.1-security" 14 | }, 15 | "devDependencies": {} 16 | }, 17 | "node_modules/accepts": { 18 | "version": "1.3.7", 19 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 20 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 21 | "dependencies": { 22 | "mime-types": "~2.1.24", 23 | "negotiator": "0.6.2" 24 | }, 25 | "engines": { 26 | "node": ">= 0.6" 27 | } 28 | }, 29 | "node_modules/array-flatten": { 30 | "version": "1.1.1", 31 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 32 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 33 | }, 34 | "node_modules/body-parser": { 35 | "version": "1.19.0", 36 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 37 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 38 | "dependencies": { 39 | "bytes": "3.1.0", 40 | "content-type": "~1.0.4", 41 | "debug": "2.6.9", 42 | "depd": "~1.1.2", 43 | "http-errors": "1.7.2", 44 | "iconv-lite": "0.4.24", 45 | "on-finished": "~2.3.0", 46 | "qs": "6.7.0", 47 | "raw-body": "2.4.0", 48 | "type-is": "~1.6.17" 49 | }, 50 | "engines": { 51 | "node": ">= 0.8" 52 | } 53 | }, 54 | "node_modules/bytes": { 55 | "version": "3.1.0", 56 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 57 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", 58 | "engines": { 59 | "node": ">= 0.8" 60 | } 61 | }, 62 | "node_modules/content-disposition": { 63 | "version": "0.5.3", 64 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", 65 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", 66 | "dependencies": { 67 | "safe-buffer": "5.1.2" 68 | }, 69 | "engines": { 70 | "node": ">= 0.6" 71 | } 72 | }, 73 | "node_modules/content-type": { 74 | "version": "1.0.4", 75 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 76 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", 77 | "engines": { 78 | "node": ">= 0.6" 79 | } 80 | }, 81 | "node_modules/cookie": { 82 | "version": "0.4.0", 83 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 84 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", 85 | "engines": { 86 | "node": ">= 0.6" 87 | } 88 | }, 89 | "node_modules/cookie-signature": { 90 | "version": "1.0.6", 91 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 92 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 93 | }, 94 | "node_modules/debug": { 95 | "version": "2.6.9", 96 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 97 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 98 | "dependencies": { 99 | "ms": "2.0.0" 100 | } 101 | }, 102 | "node_modules/depd": { 103 | "version": "1.1.2", 104 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 105 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", 106 | "engines": { 107 | "node": ">= 0.6" 108 | } 109 | }, 110 | "node_modules/destroy": { 111 | "version": "1.0.4", 112 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 113 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 114 | }, 115 | "node_modules/ee-first": { 116 | "version": "1.1.1", 117 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 118 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 119 | }, 120 | "node_modules/encodeurl": { 121 | "version": "1.0.2", 122 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 123 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", 124 | "engines": { 125 | "node": ">= 0.8" 126 | } 127 | }, 128 | "node_modules/escape-html": { 129 | "version": "1.0.3", 130 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 131 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 132 | }, 133 | "node_modules/etag": { 134 | "version": "1.8.1", 135 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 136 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", 137 | "engines": { 138 | "node": ">= 0.6" 139 | } 140 | }, 141 | "node_modules/express": { 142 | "version": "4.17.1", 143 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", 144 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", 145 | "dependencies": { 146 | "accepts": "~1.3.7", 147 | "array-flatten": "1.1.1", 148 | "body-parser": "1.19.0", 149 | "content-disposition": "0.5.3", 150 | "content-type": "~1.0.4", 151 | "cookie": "0.4.0", 152 | "cookie-signature": "1.0.6", 153 | "debug": "2.6.9", 154 | "depd": "~1.1.2", 155 | "encodeurl": "~1.0.2", 156 | "escape-html": "~1.0.3", 157 | "etag": "~1.8.1", 158 | "finalhandler": "~1.1.2", 159 | "fresh": "0.5.2", 160 | "merge-descriptors": "1.0.1", 161 | "methods": "~1.1.2", 162 | "on-finished": "~2.3.0", 163 | "parseurl": "~1.3.3", 164 | "path-to-regexp": "0.1.7", 165 | "proxy-addr": "~2.0.5", 166 | "qs": "6.7.0", 167 | "range-parser": "~1.2.1", 168 | "safe-buffer": "5.1.2", 169 | "send": "0.17.1", 170 | "serve-static": "1.14.1", 171 | "setprototypeof": "1.1.1", 172 | "statuses": "~1.5.0", 173 | "type-is": "~1.6.18", 174 | "utils-merge": "1.0.1", 175 | "vary": "~1.1.2" 176 | }, 177 | "engines": { 178 | "node": ">= 0.10.0" 179 | } 180 | }, 181 | "node_modules/finalhandler": { 182 | "version": "1.1.2", 183 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", 184 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", 185 | "dependencies": { 186 | "debug": "2.6.9", 187 | "encodeurl": "~1.0.2", 188 | "escape-html": "~1.0.3", 189 | "on-finished": "~2.3.0", 190 | "parseurl": "~1.3.3", 191 | "statuses": "~1.5.0", 192 | "unpipe": "~1.0.0" 193 | }, 194 | "engines": { 195 | "node": ">= 0.8" 196 | } 197 | }, 198 | "node_modules/forwarded": { 199 | "version": "0.2.0", 200 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 201 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", 202 | "engines": { 203 | "node": ">= 0.6" 204 | } 205 | }, 206 | "node_modules/fresh": { 207 | "version": "0.5.2", 208 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 209 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", 210 | "engines": { 211 | "node": ">= 0.6" 212 | } 213 | }, 214 | "node_modules/fs": { 215 | "version": "0.0.1-security", 216 | "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", 217 | "integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ=" 218 | }, 219 | "node_modules/http-errors": { 220 | "version": "1.7.2", 221 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 222 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 223 | "dependencies": { 224 | "depd": "~1.1.2", 225 | "inherits": "2.0.3", 226 | "setprototypeof": "1.1.1", 227 | "statuses": ">= 1.5.0 < 2", 228 | "toidentifier": "1.0.0" 229 | }, 230 | "engines": { 231 | "node": ">= 0.6" 232 | } 233 | }, 234 | "node_modules/iconv-lite": { 235 | "version": "0.4.24", 236 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 237 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 238 | "dependencies": { 239 | "safer-buffer": ">= 2.1.2 < 3" 240 | }, 241 | "engines": { 242 | "node": ">=0.10.0" 243 | } 244 | }, 245 | "node_modules/inherits": { 246 | "version": "2.0.3", 247 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 248 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 249 | }, 250 | "node_modules/ipaddr.js": { 251 | "version": "1.9.1", 252 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 253 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", 254 | "engines": { 255 | "node": ">= 0.10" 256 | } 257 | }, 258 | "node_modules/media-typer": { 259 | "version": "0.3.0", 260 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 261 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", 262 | "engines": { 263 | "node": ">= 0.6" 264 | } 265 | }, 266 | "node_modules/merge-descriptors": { 267 | "version": "1.0.1", 268 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 269 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 270 | }, 271 | "node_modules/methods": { 272 | "version": "1.1.2", 273 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 274 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", 275 | "engines": { 276 | "node": ">= 0.6" 277 | } 278 | }, 279 | "node_modules/mime": { 280 | "version": "1.6.0", 281 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 282 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 283 | "bin": { 284 | "mime": "cli.js" 285 | }, 286 | "engines": { 287 | "node": ">=4" 288 | } 289 | }, 290 | "node_modules/mime-db": { 291 | "version": "1.51.0", 292 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", 293 | "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==", 294 | "engines": { 295 | "node": ">= 0.6" 296 | } 297 | }, 298 | "node_modules/mime-types": { 299 | "version": "2.1.34", 300 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", 301 | "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", 302 | "dependencies": { 303 | "mime-db": "1.51.0" 304 | }, 305 | "engines": { 306 | "node": ">= 0.6" 307 | } 308 | }, 309 | "node_modules/ms": { 310 | "version": "2.0.0", 311 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 312 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 313 | }, 314 | "node_modules/negotiator": { 315 | "version": "0.6.2", 316 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 317 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", 318 | "engines": { 319 | "node": ">= 0.6" 320 | } 321 | }, 322 | "node_modules/on-finished": { 323 | "version": "2.3.0", 324 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 325 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 326 | "dependencies": { 327 | "ee-first": "1.1.1" 328 | }, 329 | "engines": { 330 | "node": ">= 0.8" 331 | } 332 | }, 333 | "node_modules/parseurl": { 334 | "version": "1.3.3", 335 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 336 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", 337 | "engines": { 338 | "node": ">= 0.8" 339 | } 340 | }, 341 | "node_modules/path-to-regexp": { 342 | "version": "0.1.7", 343 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 344 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 345 | }, 346 | "node_modules/proxy-addr": { 347 | "version": "2.0.7", 348 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 349 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 350 | "dependencies": { 351 | "forwarded": "0.2.0", 352 | "ipaddr.js": "1.9.1" 353 | }, 354 | "engines": { 355 | "node": ">= 0.10" 356 | } 357 | }, 358 | "node_modules/qs": { 359 | "version": "6.7.0", 360 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 361 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", 362 | "engines": { 363 | "node": ">=0.6" 364 | } 365 | }, 366 | "node_modules/range-parser": { 367 | "version": "1.2.1", 368 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 369 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", 370 | "engines": { 371 | "node": ">= 0.6" 372 | } 373 | }, 374 | "node_modules/raw-body": { 375 | "version": "2.4.0", 376 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 377 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 378 | "dependencies": { 379 | "bytes": "3.1.0", 380 | "http-errors": "1.7.2", 381 | "iconv-lite": "0.4.24", 382 | "unpipe": "1.0.0" 383 | }, 384 | "engines": { 385 | "node": ">= 0.8" 386 | } 387 | }, 388 | "node_modules/safe-buffer": { 389 | "version": "5.1.2", 390 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 391 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 392 | }, 393 | "node_modules/safer-buffer": { 394 | "version": "2.1.2", 395 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 396 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 397 | }, 398 | "node_modules/send": { 399 | "version": "0.17.1", 400 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", 401 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", 402 | "dependencies": { 403 | "debug": "2.6.9", 404 | "depd": "~1.1.2", 405 | "destroy": "~1.0.4", 406 | "encodeurl": "~1.0.2", 407 | "escape-html": "~1.0.3", 408 | "etag": "~1.8.1", 409 | "fresh": "0.5.2", 410 | "http-errors": "~1.7.2", 411 | "mime": "1.6.0", 412 | "ms": "2.1.1", 413 | "on-finished": "~2.3.0", 414 | "range-parser": "~1.2.1", 415 | "statuses": "~1.5.0" 416 | }, 417 | "engines": { 418 | "node": ">= 0.8.0" 419 | } 420 | }, 421 | "node_modules/send/node_modules/ms": { 422 | "version": "2.1.1", 423 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 424 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 425 | }, 426 | "node_modules/serve-static": { 427 | "version": "1.14.1", 428 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", 429 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", 430 | "dependencies": { 431 | "encodeurl": "~1.0.2", 432 | "escape-html": "~1.0.3", 433 | "parseurl": "~1.3.3", 434 | "send": "0.17.1" 435 | }, 436 | "engines": { 437 | "node": ">= 0.8.0" 438 | } 439 | }, 440 | "node_modules/setprototypeof": { 441 | "version": "1.1.1", 442 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 443 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 444 | }, 445 | "node_modules/statuses": { 446 | "version": "1.5.0", 447 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 448 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", 449 | "engines": { 450 | "node": ">= 0.6" 451 | } 452 | }, 453 | "node_modules/toidentifier": { 454 | "version": "1.0.0", 455 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 456 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", 457 | "engines": { 458 | "node": ">=0.6" 459 | } 460 | }, 461 | "node_modules/type-is": { 462 | "version": "1.6.18", 463 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 464 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 465 | "dependencies": { 466 | "media-typer": "0.3.0", 467 | "mime-types": "~2.1.24" 468 | }, 469 | "engines": { 470 | "node": ">= 0.6" 471 | } 472 | }, 473 | "node_modules/unpipe": { 474 | "version": "1.0.0", 475 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 476 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", 477 | "engines": { 478 | "node": ">= 0.8" 479 | } 480 | }, 481 | "node_modules/utils-merge": { 482 | "version": "1.0.1", 483 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 484 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", 485 | "engines": { 486 | "node": ">= 0.4.0" 487 | } 488 | }, 489 | "node_modules/vary": { 490 | "version": "1.1.2", 491 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 492 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", 493 | "engines": { 494 | "node": ">= 0.8" 495 | } 496 | } 497 | }, 498 | "dependencies": { 499 | "accepts": { 500 | "version": "1.3.7", 501 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 502 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 503 | "requires": { 504 | "mime-types": "~2.1.24", 505 | "negotiator": "0.6.2" 506 | } 507 | }, 508 | "array-flatten": { 509 | "version": "1.1.1", 510 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 511 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 512 | }, 513 | "body-parser": { 514 | "version": "1.19.0", 515 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 516 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 517 | "requires": { 518 | "bytes": "3.1.0", 519 | "content-type": "~1.0.4", 520 | "debug": "2.6.9", 521 | "depd": "~1.1.2", 522 | "http-errors": "1.7.2", 523 | "iconv-lite": "0.4.24", 524 | "on-finished": "~2.3.0", 525 | "qs": "6.7.0", 526 | "raw-body": "2.4.0", 527 | "type-is": "~1.6.17" 528 | } 529 | }, 530 | "bytes": { 531 | "version": "3.1.0", 532 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 533 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" 534 | }, 535 | "content-disposition": { 536 | "version": "0.5.3", 537 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", 538 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", 539 | "requires": { 540 | "safe-buffer": "5.1.2" 541 | } 542 | }, 543 | "content-type": { 544 | "version": "1.0.4", 545 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 546 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 547 | }, 548 | "cookie": { 549 | "version": "0.4.0", 550 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 551 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" 552 | }, 553 | "cookie-signature": { 554 | "version": "1.0.6", 555 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 556 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 557 | }, 558 | "debug": { 559 | "version": "2.6.9", 560 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 561 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 562 | "requires": { 563 | "ms": "2.0.0" 564 | } 565 | }, 566 | "depd": { 567 | "version": "1.1.2", 568 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 569 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 570 | }, 571 | "destroy": { 572 | "version": "1.0.4", 573 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 574 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 575 | }, 576 | "ee-first": { 577 | "version": "1.1.1", 578 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 579 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 580 | }, 581 | "encodeurl": { 582 | "version": "1.0.2", 583 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 584 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 585 | }, 586 | "escape-html": { 587 | "version": "1.0.3", 588 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 589 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 590 | }, 591 | "etag": { 592 | "version": "1.8.1", 593 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 594 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 595 | }, 596 | "express": { 597 | "version": "4.17.1", 598 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", 599 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", 600 | "requires": { 601 | "accepts": "~1.3.7", 602 | "array-flatten": "1.1.1", 603 | "body-parser": "1.19.0", 604 | "content-disposition": "0.5.3", 605 | "content-type": "~1.0.4", 606 | "cookie": "0.4.0", 607 | "cookie-signature": "1.0.6", 608 | "debug": "2.6.9", 609 | "depd": "~1.1.2", 610 | "encodeurl": "~1.0.2", 611 | "escape-html": "~1.0.3", 612 | "etag": "~1.8.1", 613 | "finalhandler": "~1.1.2", 614 | "fresh": "0.5.2", 615 | "merge-descriptors": "1.0.1", 616 | "methods": "~1.1.2", 617 | "on-finished": "~2.3.0", 618 | "parseurl": "~1.3.3", 619 | "path-to-regexp": "0.1.7", 620 | "proxy-addr": "~2.0.5", 621 | "qs": "6.7.0", 622 | "range-parser": "~1.2.1", 623 | "safe-buffer": "5.1.2", 624 | "send": "0.17.1", 625 | "serve-static": "1.14.1", 626 | "setprototypeof": "1.1.1", 627 | "statuses": "~1.5.0", 628 | "type-is": "~1.6.18", 629 | "utils-merge": "1.0.1", 630 | "vary": "~1.1.2" 631 | } 632 | }, 633 | "finalhandler": { 634 | "version": "1.1.2", 635 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", 636 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", 637 | "requires": { 638 | "debug": "2.6.9", 639 | "encodeurl": "~1.0.2", 640 | "escape-html": "~1.0.3", 641 | "on-finished": "~2.3.0", 642 | "parseurl": "~1.3.3", 643 | "statuses": "~1.5.0", 644 | "unpipe": "~1.0.0" 645 | } 646 | }, 647 | "forwarded": { 648 | "version": "0.2.0", 649 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 650 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" 651 | }, 652 | "fresh": { 653 | "version": "0.5.2", 654 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 655 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 656 | }, 657 | "fs": { 658 | "version": "0.0.1-security", 659 | "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", 660 | "integrity": "sha1-invTcYa23d84E/I4WLV+yq9eQdQ=" 661 | }, 662 | "http-errors": { 663 | "version": "1.7.2", 664 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 665 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 666 | "requires": { 667 | "depd": "~1.1.2", 668 | "inherits": "2.0.3", 669 | "setprototypeof": "1.1.1", 670 | "statuses": ">= 1.5.0 < 2", 671 | "toidentifier": "1.0.0" 672 | } 673 | }, 674 | "iconv-lite": { 675 | "version": "0.4.24", 676 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 677 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 678 | "requires": { 679 | "safer-buffer": ">= 2.1.2 < 3" 680 | } 681 | }, 682 | "inherits": { 683 | "version": "2.0.3", 684 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 685 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 686 | }, 687 | "ipaddr.js": { 688 | "version": "1.9.1", 689 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 690 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" 691 | }, 692 | "media-typer": { 693 | "version": "0.3.0", 694 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 695 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 696 | }, 697 | "merge-descriptors": { 698 | "version": "1.0.1", 699 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 700 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 701 | }, 702 | "methods": { 703 | "version": "1.1.2", 704 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 705 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 706 | }, 707 | "mime": { 708 | "version": "1.6.0", 709 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 710 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" 711 | }, 712 | "mime-db": { 713 | "version": "1.51.0", 714 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", 715 | "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" 716 | }, 717 | "mime-types": { 718 | "version": "2.1.34", 719 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", 720 | "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", 721 | "requires": { 722 | "mime-db": "1.51.0" 723 | } 724 | }, 725 | "ms": { 726 | "version": "2.0.0", 727 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 728 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 729 | }, 730 | "negotiator": { 731 | "version": "0.6.2", 732 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 733 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" 734 | }, 735 | "on-finished": { 736 | "version": "2.3.0", 737 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 738 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 739 | "requires": { 740 | "ee-first": "1.1.1" 741 | } 742 | }, 743 | "parseurl": { 744 | "version": "1.3.3", 745 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 746 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 747 | }, 748 | "path-to-regexp": { 749 | "version": "0.1.7", 750 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 751 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 752 | }, 753 | "proxy-addr": { 754 | "version": "2.0.7", 755 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 756 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 757 | "requires": { 758 | "forwarded": "0.2.0", 759 | "ipaddr.js": "1.9.1" 760 | } 761 | }, 762 | "qs": { 763 | "version": "6.7.0", 764 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 765 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" 766 | }, 767 | "range-parser": { 768 | "version": "1.2.1", 769 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 770 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 771 | }, 772 | "raw-body": { 773 | "version": "2.4.0", 774 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 775 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 776 | "requires": { 777 | "bytes": "3.1.0", 778 | "http-errors": "1.7.2", 779 | "iconv-lite": "0.4.24", 780 | "unpipe": "1.0.0" 781 | } 782 | }, 783 | "safe-buffer": { 784 | "version": "5.1.2", 785 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 786 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 787 | }, 788 | "safer-buffer": { 789 | "version": "2.1.2", 790 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 791 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 792 | }, 793 | "send": { 794 | "version": "0.17.1", 795 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", 796 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", 797 | "requires": { 798 | "debug": "2.6.9", 799 | "depd": "~1.1.2", 800 | "destroy": "~1.0.4", 801 | "encodeurl": "~1.0.2", 802 | "escape-html": "~1.0.3", 803 | "etag": "~1.8.1", 804 | "fresh": "0.5.2", 805 | "http-errors": "~1.7.2", 806 | "mime": "1.6.0", 807 | "ms": "2.1.1", 808 | "on-finished": "~2.3.0", 809 | "range-parser": "~1.2.1", 810 | "statuses": "~1.5.0" 811 | }, 812 | "dependencies": { 813 | "ms": { 814 | "version": "2.1.1", 815 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 816 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 817 | } 818 | } 819 | }, 820 | "serve-static": { 821 | "version": "1.14.1", 822 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", 823 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", 824 | "requires": { 825 | "encodeurl": "~1.0.2", 826 | "escape-html": "~1.0.3", 827 | "parseurl": "~1.3.3", 828 | "send": "0.17.1" 829 | } 830 | }, 831 | "setprototypeof": { 832 | "version": "1.1.1", 833 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 834 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 835 | }, 836 | "statuses": { 837 | "version": "1.5.0", 838 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 839 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 840 | }, 841 | "toidentifier": { 842 | "version": "1.0.0", 843 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 844 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" 845 | }, 846 | "type-is": { 847 | "version": "1.6.18", 848 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 849 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 850 | "requires": { 851 | "media-typer": "0.3.0", 852 | "mime-types": "~2.1.24" 853 | } 854 | }, 855 | "unpipe": { 856 | "version": "1.0.0", 857 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 858 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 859 | }, 860 | "utils-merge": { 861 | "version": "1.0.1", 862 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 863 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 864 | }, 865 | "vary": { 866 | "version": "1.1.2", 867 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 868 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 869 | } 870 | } 871 | } 872 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "projeto-testesenac", 3 | "version": "1.0.0", 4 | "description": "projeto teste do curso programação do senac.", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "nodemon server.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/devcode25/projeto-testeSenac.git" 13 | }, 14 | "author": "", 15 | "license": "ISC", 16 | "bugs": { 17 | "url": "https://github.com/devcode25/projeto-testeSenac/issues" 18 | }, 19 | "homepage": "https://github.com/devcode25/projeto-testeSenac#readme", 20 | "dependencies": { 21 | "express": "^4.17.1", 22 | "fs": "^0.0.1-security" 23 | }, 24 | "keywords": [ 25 | "API", 26 | "JSON" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const app = require('./src/app'); 2 | const port = 9000; 3 | 4 | app.listen(port, () => { 5 | console.log(`App esta rodando na porta ${port}`) 6 | }); -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | 4 | app.use(express.json()) 5 | 6 | const index = require('./routes/index'); 7 | const empresas = require('./routes/empresasTecRoutes'); 8 | 9 | app.use(function (req, res, next) { 10 | res.header("Access-Control-Allow-Origin", "*") // informo que minha api poderá ser chamada de qualquer lugar. Por um browser, por exemplo. 11 | res.header( 12 | "Access-Control-Allow-Headers", 13 | "Origin, X-Requested-With, Content-Type, Accept" 14 | ) 15 | next() 16 | // como criei uma função dentro do app.use, preciso dar 17 | //um "next()" para mandar ele seguir para a próxima middleware. 18 | // se eu não faço isso, a requisição vai ficar travada aí. 19 | }) 20 | 21 | app.use("/", index); 22 | app.use("/empresas", empresas); 23 | 24 | module.exports = app; 25 | -------------------------------------------------------------------------------- /src/controller/empresasTecController.js: -------------------------------------------------------------------------------- 1 | const empresas = require("../models/empresasTec.json") 2 | const fs = require("fs") 3 | 4 | 5 | const getAllEmpresas = (req, res) => { 6 | console.log(req.url) 7 | res.status(200).send(empresas) 8 | } 9 | 10 | const createEmpresas = (req, res) => { 11 | const { id, Nome, Atuação, Site, Telefone, Vagas } = req.body 12 | empresas.push({ id, Nome, Atuação, Site, Telefone, Vagas }) 13 | fs.writeFile("./src/models/empresasTec.json", JSON.stringify(empresas), 'utf8', function (err) { // gravando novo filme no array de filmes 14 | if (err) { 15 | res.status(500).send({ message: err }) 16 | } else { 17 | console.log("Arquivo atualizado com sucesso!") 18 | const empresasFound = empresas.find(empresas => empresas.id == id) // recupero o filme que foi criei no array de filmes 19 | res.status(200).send(empresasFound) 20 | } 21 | }) 22 | } 23 | 24 | const getEmpresas = (req, res) => { 25 | const empresasId = req.params.id 26 | const empresasFound = empresas.find((empresas) => empresas.id == empresasId) 27 | if (empresasFound) { 28 | res.status(200).send(empresasFound) 29 | } else { 30 | res.status(404).send({ message: "Empresa não encontrada" }) 31 | } 32 | 33 | next() 34 | } 35 | 36 | 37 | 38 | const updateEmpresas = (req, res) => { 39 | try { 40 | const empresasId = req.params.id 41 | const empresasToUpdate = req.body //Pego o corpo da requisição com as alterações 42 | 43 | const empresasFound = empresas.find(empresas => empresas.id == empresasId) // separo o item que irei atualizar 44 | const empresasIndex = empresas.indexOf(empresasFound) // separo o indice do item no array de empresas 45 | 46 | if (empresasIndex >= 0) { // verifico se o dado existe no array de filmes 47 | empresas.splice(empresasIndex, 1, empresasToUpdate) //busco no array o dado, excluo o registro antigo e substituo pelo novo 48 | } else { 49 | res.status(404).send({ message: "Empresa não encontrado para ser atualizado" }) 50 | } 51 | 52 | fs.writeFile("./src/models/empresasTec.json", JSON.stringify(empresas), 'utf8', function (err) { // gravo meu json de empresas atualizado 53 | if (err) { 54 | res.status(500).send({ message: err }) // caso dê erro retorno status 500 55 | } else { 56 | console.log("Arquivo de empresas atualizado com sucesso!") 57 | const empresasUpdated = empresas.find(empresas => empresas.id == empresasId) // separo o dado que modifiquei no array 58 | res.status(200).send(empresasUpdated) // envio o dado modificado como resposta 59 | } 60 | }) 61 | } catch (err) { 62 | res.status(500).send({ message: err }) // caso dê erro retorno status 500 63 | } 64 | } 65 | 66 | 67 | const updateVagasStatus = (req, res) => { 68 | try { 69 | const empresasId = req.params.id // pego a informação do id no parametro da requisição 70 | const Vagas = req.body.Vagas // pego a informação de watched no corpo da requisição. Ele terá valor true ou false, dependendo do que tiver sido passado 71 | 72 | const empresasToUpdate = empresas.find(empresas => empresas.id == empresasId) // separo o filme que irei mudar o status 73 | const empresasIndex = empresas.indexOf(empresasToUpdate) // identifico o índice do filme no meu array 74 | 75 | if (empresasIndex >= 0) { // verifico se o filme existe no array de filmes 76 | empresasToUpdate.Vagas = Vagas //atualizo o objeto com o novo status informando se foi assistido ou não 77 | empresas.splice(empresasIndex, 1, empresasToUpdate) // removo o filme pelo índice substituindo pelo novo 78 | } else { 79 | res.status(404).send({ message: "Empresa não encontrada para informar se foi assistido ou não" }) 80 | } 81 | fs.writeFile("./src/models/empresasTec.json", JSON.stringify(empresas), 'utf8', function (err) { // gravo meu json de filmes atualizado 82 | if (err) { 83 | res.status(500).send({ message: err }) 84 | } else { 85 | console.log("Arquivo atualizado com sucesso!") 86 | const empresasUpdated = empresas.find((empresas) => empresas.id == empresasId) // separo o filme que modifiquei no array 87 | res.status(200).send(empresasUpdated) // envio o item modificado como resposta 88 | } 89 | }) 90 | } catch (err) { 91 | res.status(500).send({ message: err }) 92 | } 93 | } 94 | 95 | 96 | 97 | 98 | 99 | 100 | module.exports = { 101 | updateVagasStatus, 102 | updateEmpresas, 103 | getEmpresas, 104 | createEmpresas, 105 | getAllEmpresas 106 | } 107 | 108 | 109 | -------------------------------------------------------------------------------- /src/models/empresasTec.json: -------------------------------------------------------------------------------- 1 | [{},{"id":2,"Nome":"Avanade3","Atuação":"Consultoria microsoft","Site":"https://www.avanade.com/pt-br","Telefone":"0000-0000","Vagas":false},{"id":3,"Nome":"Avantia Tecnologia","Atuação":"Tecnologia e segurança ","Site":"https://www.avantia.com.br/","Telefone":"3797-9304","Vagas":true},{"id":4,"Nome":"Banksystem","Atuação":"Tecnologia para gestão comercial","Site":" https://banksystem.com.br/","Telefone":"3462-3748","Vagas":true},{"id":5,"Nome":"DACCORD MUSIC SOFTWARE ","Atuação":"Software Musical","Site":"https://www.daccord.com.br/","Telefone":"4102-0011","Vagas":true},{"Nome":"Edukante","Atuação":"Softwares de Gestão Escolar","Site":"https://edukante.com/index.html","Telefone":"4141-9982","Vagas":true},{"Nome":"In Loco Media","Atuação":"tecnologia de geolocalização ","Site":" https://www.inloco.com.br/","Telefone":"3127-0881","Vagas":true},{"Nome":"MV","Atuação":"sistemas de gestão em Saúde ","Site":"http://www.mv.com.br/pt","Telefone":"3972-7028","Vagas":true},{"Nome":"Oncase","Atuação":"Sistemas Data-Driven","Site":" https://www.oncase.com.br/","Telefone":"3077-8558","Vagas":true},{"Nome":"Sterfanini ","Atuação":"Soluções Tecnológicas","Site":"https://stefanini.com/pt-br","Telefone":"3224-7576","Vagas":true},{"id":11,"Nome":"SENAC PE","Atuação":"Tecnologia e segurança ","Site":"https://senac-pe.org.br","Telefone":"00000-0000","Vagas":true},{"id":1,"Nome":"Accenture2","Atuação":"Televendas","Site":"https://www.accenture.com/br-pt","Telefone":"3333-6714","Vagas":true}] -------------------------------------------------------------------------------- /src/routes/empresasTecRoutes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const router = express.Router(); 3 | const controller = require('../controller/empresasTecController') 4 | 5 | router.get("/", controller.getAllEmpresas); 6 | router.post("/", controller.createEmpresas); 7 | router.get("/:id", controller.getEmpresas); 8 | router.put("/:id", controller.updateEmpresas); 9 | router.patch("/:id/vagas", controller.updateVagasStatus); 10 | 11 | 12 | module.exports = router; 13 | -------------------------------------------------------------------------------- /src/routes/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const router = express.Router(); 3 | 4 | router.get('/', function(req, res) { 5 | res.status(200).send({ 6 | titulo:'Empresas tec legais para trampar', 7 | data: '26/09/2020' 8 | }) 9 | }) 10 | 11 | module.exports = router; 12 | --------------------------------------------------------------------------------