diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..db23757 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +node_modules +build +.env +.rest +_query_template.ts +_template.ts +addFakeUsers.ts +fakeData diff --git a/README.md b/README.md index 978a24d..5e234ec 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,32 @@ -## LabenuSystem: - -Você estuda na Labenu_ há tanto tempo que já parecem anos, não é? Então, hoje, vamos pedir para criar um sistema que represente o básico da nossa organização. - -Ele deve possuir, ao menos, as 3 entidades importantes: - -1. Estudantes - - Representa estudantes da nossa instituição. Eles devem possuir: id, nome, email, data de nascimento e os principais hobbies dele. - -2. Docente - - Representa docentes da nossa instituição. Eles devem possuir: id, nome, email, data de nascimento e todas as especialidades dele. Há 7 especialidades: React, Redux, CSS, Testes, Typescript, Programação Orientada a Objetos e Backend - -3. Turma - - Toda turma é composta das seguintes características: id, nome, data de início, data de término, lista de professores responsáveis, uma lista de alunos e módulo atual em que a turma está. - - O módulo pode assumir os valores de 1 a 7 ou `undefined`, indicando que as aulas dessa turma ainda não começaram. Para esse exercício, vamos considerar que existam dois tipos de turma: integral ou noturna. Há uma restrição para o nome das turmas noturnas: tem que terminar com `-na-night`. - -As funcionalidades básicas são: - -→ Criar estudante; - -→ Criar docente; - -→ Criar turma; - -→ Adicionar estudante na turma; - -→ Adicionar docente na turma; - -→ Pegar a idade de algum estudante a partir do id +# LABENU SYSTEM + +## 📚📰 Documentação no Postman +https://documenter.getpostman.com/view/14148928/TzCMe8Jy + +## ✨ Tecnologias +Esse projeto foi desenvolvido com as seguintes tecnologias: + +* React.js +* Typescript +* Express +* MySQL +* Knex +* dotEnv + +## 💻 Projeto +Projeto desenvolvido durante a semana 17 do curso de Fullstack da Labenu. + +## 🚀 Como executar +* Instale as dependências com +``` + npm install + ``` +* Inicie o servidor com + ``` + npm run start + ``` + +## Desenvolvido por: +* Fabio Dos Santos +* Jéssica Damasceno +* Marivone Araujo diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..b71f619 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1396 @@ +{ + "name": "labenu_task", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.34", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.34.tgz", + "integrity": "sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/cors": { + "version": "2.8.10", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.10.tgz", + "integrity": "sha512-C7srjHiVG3Ey1nR6d511dtDkCEjxuN9W1HWAEjGq8kpcwmNM6JJkpC0xvabM7BXTG2wDq8Eu33iH9aQKa7IvLQ==", + "dev": true + }, + "@types/express": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.11.tgz", + "integrity": "sha512-no+R6rW60JEc59977wIxreQVsIEOAYwgCqldrA/vkpCnbD7MqTefO97lmoBe4WE0F156bC4uLSP1XHDOySnChg==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.19.tgz", + "integrity": "sha512-DJOSHzX7pCiSElWaGR8kCprwibCB/3yW6vcT8VG3P0SJjnv19gnWG/AZMfM60Xj/YJIp/YCaDHyvzsFVeniARA==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/knex": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/@types/knex/-/knex-0.16.1.tgz", + "integrity": "sha512-54gWD1HWwdVx5iLHaJ1qxH3I6KyBsj5fFqzRpXFn7REWiEB2jwspeVCombNsocSrqPd7IRPqKrsIME7/cD+TFQ==", + "requires": { + "knex": "*" + } + }, + "@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", + "dev": true + }, + "@types/node": { + "version": "14.14.36", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.36.tgz", + "integrity": "sha512-kjivUwDJfIjngzbhooRnOLhGYz6oRFi+L+EpMjxroDYXwDw9lHrJJ43E+dJ6KAd3V3WxWAJ/qZE9XKYHhjPOFQ==", + "dev": true + }, + "@types/qs": { + "version": "6.9.6", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.6.tgz", + "integrity": "sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", + "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==", + "dev": true + }, + "@types/serve-static": { + "version": "1.13.9", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.9.tgz", + "integrity": "sha512-ZFqF6qa48XsPdjXV5Gsz0Zqmux2PerNd3a/ktL45mHpa19cuMi/cL8tcxdAx497yRh+QtYPuofjT9oWw9P7nkA==", + "dev": true, + "requires": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "@types/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=", + "dev": true + }, + "@types/strip-json-comments": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", + "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", + "dev": true + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "bignumber.js": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", + "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==" + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + } + }, + "chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.3.1", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" + } + }, + "colorette": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz", + "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==" + }, + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "dateformat": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1", + "meow": "^3.3.0" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" + }, + "dynamic-dedupe": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/dynamic-dedupe/-/dynamic-dedupe-0.3.0.tgz", + "integrity": "sha1-BuRMIj9eTpTXjvnbI6ZRXOL5YqE=", + "dev": true, + "requires": { + "xtend": "^4.0.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "getopts": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/getopts/-/getopts-2.2.5.tgz", + "integrity": "sha512-9jb7AW5p3in+IiJWhQiZmmwkpLaR/ccTWdWQCtZM66HJcHHLegowh4q4tSD7gouUyeNvFWRavfK9GXosQHDpFA==" + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "graceful-fs": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-core-module": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", + "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", + "requires": { + "has": "^1.0.3" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "knex": { + "version": "0.95.4", + "resolved": "https://registry.npmjs.org/knex/-/knex-0.95.4.tgz", + "integrity": "sha512-IwUcHr6AkZPL707mJCOal1P4jlgxKMy17IMjJm5W23yrkM1jO2/APBM1eyw/MhQ61w8T7NpzGD+LEkr8M46mWw==", + "requires": { + "colorette": "1.2.1", + "commander": "^7.1.0", + "debug": "4.3.1", + "escalade": "^3.1.1", + "esm": "^3.2.25", + "getopts": "2.2.5", + "interpret": "^2.2.0", + "lodash": "^4.17.21", + "pg-connection-string": "2.4.0", + "rechoir": "^0.7.0", + "resolve-from": "^5.0.0", + "tarn": "^3.0.1", + "tildify": "2.0.0" + }, + "dependencies": { + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz", + "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==" + }, + "mime-types": { + "version": "2.1.29", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz", + "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==", + "requires": { + "mime-db": "1.46.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "mysql": { + "version": "2.18.1", + "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz", + "integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==", + "requires": { + "bignumber.js": "9.0.0", + "readable-stream": "2.3.7", + "safe-buffer": "5.1.2", + "sqlstring": "2.3.1" + } + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pg-connection-string": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.4.0.tgz", + "integrity": "sha512-3iBXuv7XKvxeMrIgym7njT+HlZkwZqqGX4Bu9cci8xHZNT+Um1gWKqCsAzcC0d95rcKMU5WBg6YRUcHyV0HZKQ==" + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "rechoir": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.0.tgz", + "integrity": "sha512-ADsDEH2bvbjltXEP+hTIAmeFekTFK0V2BTxMkok6qILyAJEXV0AFfoWcAq4yfll5VdIMd/RVXq0lR+wQi5ZU3Q==", + "requires": { + "resolve": "^1.9.0" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", + "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==", + "dev": true + }, + "sqlstring": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", + "integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A=" + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "tarn": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.1.tgz", + "integrity": "sha512-6usSlV9KyHsspvwu2duKH+FMUhqJnAh6J5J/4MITl8s94iSUQTLkJggdiewKv4RyARQccnigV48Z+khiuVZDJw==" + }, + "tildify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tildify/-/tildify-2.0.0.tgz", + "integrity": "sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==" + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + }, + "ts-node": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", + "integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + } + }, + "ts-node-dev": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/ts-node-dev/-/ts-node-dev-1.1.6.tgz", + "integrity": "sha512-RTUi7mHMNQospArGz07KiraQcdgUVNXKsgO2HAi7FoiyPMdTDqdniB6K1dqyaIxT7c9v/VpSbfBZPS6uVpaFLQ==", + "dev": true, + "requires": { + "chokidar": "^3.5.1", + "dateformat": "~1.0.4-1.2.3", + "dynamic-dedupe": "^0.3.0", + "minimist": "^1.2.5", + "mkdirp": "^1.0.4", + "resolve": "^1.0.0", + "rimraf": "^2.6.1", + "source-map-support": "^0.5.12", + "tree-kill": "^1.2.2", + "ts-node": "^9.0.0", + "tsconfig": "^7.0.0" + } + }, + "tsconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", + "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", + "dev": true, + "requires": { + "@types/strip-bom": "^3.0.0", + "@types/strip-json-comments": "0.0.30", + "strip-bom": "^3.0.0", + "strip-json-comments": "^2.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typescript": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz", + "integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..b57df46 --- /dev/null +++ b/package.json @@ -0,0 +1,27 @@ +{ + "name": "labenu_task", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "start": "tsc && node ./build/index.js", + "dev-start": "nodemon ./src/index.ts" + }, + "author": "Fabio dos Santos", + "license": "ISC", + "dependencies": { + "@types/knex": "^0.16.1", + "cors": "^2.8.5", + "dotenv": "^8.2.0", + "express": "^4.17.1", + "knex": "^0.95.2", + "mysql": "^2.18.1" + }, + "devDependencies": { + "@types/cors": "^2.8.10", + "@types/express": "^4.17.11", + "@types/knex": "^0.16.1", + "@types/node": "^14.14.35", + "ts-node-dev": "^1.1.6", + "typescript": "^4.2.3" + } +} diff --git a/src/app.ts b/src/app.ts new file mode 100644 index 0000000..dc8bf78 --- /dev/null +++ b/src/app.ts @@ -0,0 +1,19 @@ +import express from "express"; +import cors from "cors"; +import { AddressInfo } from "net"; + +const app = express(); + +app.use(express.json()); +app.use(cors()); + +const server = app.listen(process.env.PORT || 3003, () => { + if (server) { + const address = server.address() as AddressInfo; + console.log(`Server is running in http://localhost:${address.port}`); + } else { + console.error(`Failure upon starting server.`); + } +}); + +export default app \ No newline at end of file diff --git a/src/connection.ts b/src/connection.ts new file mode 100644 index 0000000..6020fd2 --- /dev/null +++ b/src/connection.ts @@ -0,0 +1,18 @@ +import knex from "knex"; +import dotenv from "dotenv"; + +dotenv.config(); + +const connection = knex({ + client: "mysql", + connection: { + host: process.env.DB_HOST, + port: 3306, + user: process.env.DB_USER, + password: process.env.DB_PASSWORD, + database: process.env.DB_SCHEMA, + multipleStatements: true + }, +}); + +export default connection \ No newline at end of file diff --git a/src/endpoints/addClass.ts b/src/endpoints/addClass.ts new file mode 100644 index 0000000..fb4fbd7 --- /dev/null +++ b/src/endpoints/addClass.ts @@ -0,0 +1,104 @@ +import { Request, Response } from "express"; +import insertClass from "../queries/insertClass"; +import { strDateToDate } from "./../functions/strDateToDate"; +import { dateDiff } from "../functions/dateDiff"; +import { checkDate } from "./../functions/checkDate"; +import { createId } from "./../functions/createId"; +import { classBody, classBodyType } from "../types/classBody"; +import findData from "../queries/findData"; + +const addClass = async (req: Request, res: Response): Promise => { + let errorCode: number = 400; + try { + // Parâmetros do Body + const { + name, + period, + startDate, + endDate, + module, + } = req.body as classBodyType; + + // Gera id + const id: number = await createId("class"); + + // Converte as datas de string para Date format + const convStartDate: Date = strDateToDate(startDate); + const convEndDate: Date = strDateToDate(endDate); + + // VALIDAÇÕES + // Se existe campo vazio ou ausente do body + for (let item in classBody) { + if (!(item in req.body)) { + errorCode = 422; + throw new Error(`'${item}' field is missing.`); + } + } + + // Se nome tem menos de 3 caracteres + if (name.length < 3) { + errorCode = 422; + throw new Error("Name must be at least 3 characters."); + } + + // Se nome já foi registrado + const findClass = await findData("class", "name", name); + if (findClass) { + errorCode = 422; + throw new Error(`class '${name}' is already registered!`); + } + + // Se a categoria é válida + if ( + period.toLowerCase() !== "noturno" && + period.toLowerCase() !== "integral" + ) { + errorCode = 422; + throw new Error(`'period' options are: 'Noturno' or 'Integral'.`); + } + + // Se a turma for noturna, adiciona '-na-night' no nome + let modName: string = name; + if (period.toLowerCase() === "noturno") { + modName = name + "-na-night"; + } + + // Se o fomrato de data do usuário é DD/MM/YYYY + if (!checkDate(startDate) || !checkDate(endDate)) { + errorCode = 422; + throw new Error(`invalid date type. Use the format DD/MM/YYYY`); + } + + // Se data de fim é maior que data de início + if (dateDiff(convStartDate, convEndDate) < 0) { + errorCode = 422; + throw new Error("'endDate' must be greater than 'startDate'!"); + } + + // Se a data de fim do curso é superior ao dia atual + if (dateDiff(new Date(), convEndDate) < 0) { + errorCode = 422; + throw new Error("'endDate' must be greater than 'today's date'!"); + } + + // Se o valor inserido é um número, inteiro, positivo e entre 0 e 7 + if (isNaN(module) || module % 1 !== 0 || module < 0 || module > 7) { + errorCode = 422; + throw new Error( + "'module' must be a positive and integer number between 0 and 7!" + ); + } + + // Insere as informações no Banco de Dados + await insertClass(id, modName, startDate, endDate, module); + + // Resposta para o usuário + res + .status(201) + .send({ message: `Class '${name}' has been successfully registered!` }); + } catch (error) { + res.status(errorCode).send({ message: error.message || error.sqlMessage }); + } +}; + +export default addClass; diff --git a/src/endpoints/addHobby.ts b/src/endpoints/addHobby.ts new file mode 100644 index 0000000..2a727d6 --- /dev/null +++ b/src/endpoints/addHobby.ts @@ -0,0 +1,49 @@ +import { Request, Response } from "express"; +import insertInfo from "../queries/insertInfo"; +import findData from "../queries/findData"; +import { createId } from './../functions/createId'; + +const addHobby = async (req: Request, res: Response): Promise => { + let errorCode: number = 400; + try { + // Parâmetros do Body + const title: string = req.body.title as string; + + // Gera id + const id: number = await createId("hobby"); + + // VALIDAÇÕES + // Se existe campo vazio ou ausente do body + if (!title) { + errorCode = 422; + throw new Error("'title' is mandatory in body."); + } + + // Se o título é uma palavra e tem menos de 3 caracteres + if (!(/^[A-Za-z]+$/.test(title))) { + errorCode = 422; + throw new Error("'title' must be a word."); + } + if (title.length < 3) { + errorCode = 422; + throw new Error("'title' must be at least 3 characters."); + } + + // Se o hobby já foi cadastrado + const findHobby = await findData("hobby", "title", title); + if (findHobby) { + errorCode = 409; + throw new Error(`Hobby '${title}' is already registered!`); + } + + // Insere as informações no Banco de Dados + await insertInfo("hobby", id, title); + + // Resposta para o usuário + res.status(201).send({ message: "Hobby registered!" }); + } catch (error) { + res.status(errorCode).send({ message: error.message || error.sqlMessage }); + } +}; + +export default addHobby; diff --git a/src/endpoints/addSpeciality.ts b/src/endpoints/addSpeciality.ts new file mode 100644 index 0000000..8ad888e --- /dev/null +++ b/src/endpoints/addSpeciality.ts @@ -0,0 +1,49 @@ +import { Request, Response } from "express"; +import insertInfo from "../queries/insertInfo"; +import findData from "../queries/findData"; +import { createId } from "../functions/createId"; + +const addSpeciality = async (req: Request, res: Response): Promise => { + let errorCode: number = 400; + try { + // Parâmetros do Body + const title: string = req.body.title; + + // Gera id + const id: number = await createId("teacher"); + + // VALIDAÇÕES + // Se existe campo vazio ou ausente do body + if (!title) { + errorCode = 422; + throw new Error("'title' is mandatory in body."); + } + + // Se o título é uma palavra e tem menos de 3 caracteres + if (!/^[A-Za-z]+$/.test(title)) { + errorCode = 422; + throw new Error("'title' must be a word."); + } + if (title.length < 3) { + errorCode = 422; + throw new Error("'title' must be at least 3 characters."); + } + + // Se a especialidade já foi cadastrada + const findSpeciality: any = await findData("speciality", "title", title); + if (findSpeciality.length > 0) { + errorCode = 409; + throw new Error(`Speciality '${title}' is already registered!`); + } + + // Insere as informações no Banco de Dados + await insertInfo("speciality", id, title); + + // Resposta para o usuário + res.status(201).send({ message: "Speciality registered!" }); + } catch (error) { + res.status(errorCode).send({ message: error.message || error.sqlMessage }); + } +}; + +export default addSpeciality; diff --git a/src/endpoints/addStudent.ts b/src/endpoints/addStudent.ts new file mode 100644 index 0000000..128dc91 --- /dev/null +++ b/src/endpoints/addStudent.ts @@ -0,0 +1,80 @@ +import { Request, Response } from "express"; +import insertUser from "../queries/insertUser"; +import { checkDate } from "../functions/checkDate"; +import { checkEmail } from "../functions/checkEmail"; +import { strDateToDate } from "../functions/strDateToDate"; +import { createId } from "./../functions/createId"; +import findData from "../queries/findData"; +import { findAge } from "./../functions/findAge"; +import { personBody, personBodyType } from "../types/personBody"; + +const addStudent = async (req: Request, res: Response) => { + let errorCode: number = 400; + try { + // Parâmetros do Body + const { name, email, birthdate } = req.body as personBodyType; + + // Gera id + const id: number = await createId("student"); + + // VALIDAÇÕES + // Se existe campo vazio ou ausente do body + for (let item in personBody) { + if (!(item in req.body)) { + errorCode = 422; + throw new Error(`'${item}' field is missing.`); + } + } + + // Se nome tem pelo menos 3 caracteres + if (name.length < 3) { + errorCode = 422; + throw new Error(`'name' field must be at least 3 characters.`); + } + + // Se o formato do e-mail é válido + const sufixTest = email.split("."); + if (sufixTest[sufixTest.length - 1].length > 3) { + errorCode = 422; + throw new Error(`invalid e-mail type.`); + } + if (!checkEmail(email)) { + errorCode = 422; + throw new Error(`invalid e-mail type.`); + } + + // Se o e-mail já foi cadastrado + const findStudentEmail: any = await findData("student", "email", email); + const findTeacherEmail: any = await findData("teacher", "email", email); + if (findStudentEmail || findTeacherEmail) { + errorCode = 422; + throw new Error(`E-mail already registered.`); + } + + // Se o fomrato de data do usuário é DD/MM/YYYY + let modDate: Date; + if (!checkDate(birthdate)) { + errorCode = 422; + throw new Error(`invalid date type. Use the format DD/MM/YYYY`); + } else { + // Converte a data de string pra Date + modDate = strDateToDate(birthdate); + } + + // Se o usuário tem pelo menos 18 anos + if (findAge(modDate) < 18) { + errorCode = 422; + throw new Error(`Invalid age. You must be over 18 to register.`); + } + + // Insere as informações no Banco de Dados + await insertUser("student", id, name, email, birthdate); + + // Resposta para o usuário + res.status(200).send({ message: `Student registered!` }); + } catch (error) { + res.status(errorCode).send({ message: error.message || error.sqlMessage }); + } +}; + +export default addStudent; diff --git a/src/endpoints/addStudentToClass.ts b/src/endpoints/addStudentToClass.ts new file mode 100644 index 0000000..86dd5b2 --- /dev/null +++ b/src/endpoints/addStudentToClass.ts @@ -0,0 +1,73 @@ +import { Request, Response } from "express"; +import findData from "../queries/findData"; +import insertUserToClass from "../queries/insertUserToClass"; +import findDuplicate from "../queries/findDuplicate"; + +const addStudentToClass = async (req: Request, res: Response): Promise => { + let errorCode: number = 400; + try { + // Parâmetros do Body + const { id, classId } = req.body; + const body = ["id", "classId"]; + + // VALIDAÇÕES + // Se existe campo vazio ou ausente do body + body.forEach((item) => { + if (!(item in req.body)) { + errorCode = 422; + throw new Error(`'${item}' field is missing.`); + } + }); + + // Se o valor inserido em 'id' é um número, inteiro e positivo + if (isNaN(id) || id % 1 !== 0) { + errorCode = 422; + throw new Error("'id' must be a positive and integer number!"); + } + + //Se o id está na base de usuário selecionada + const findUser = await findData("student", "id", id); + if (!findUser) { + errorCode = 404; + throw new Error(`User id ${id} not found.`); + } + + // Se o valor inserido em 'classId' é um número, inteiro e positivo + if (isNaN(classId) || classId % 1 !== 0) { + errorCode = 422; + throw new Error("'classId' must be a positive and integer number!"); + } + + //Se o id está na base de turmas + const findClass = await findData("class", "id", classId); + if (!findClass) { + errorCode = 404; + throw new Error(`Class id ${classId} not found.`); + } + + //Se a relaçao usuário-turma já foi cadastrada + const findRecord = await findDuplicate( + "student_class", + "student_id", + id, + "class_id", + classId + ); + if (findRecord) { + errorCode = 409; + throw new Error(`This register has already been made.`); + } + + //Insere as informações no Banco de Dados + await insertUserToClass("student", id, classId); + + //Resposta para o usuário + res.send({ + message: `Student ${findUser.name} assigned to class ${findClass.name}!`, + }); + } catch (error) { + res.status(errorCode).send({ message: error.message || error.sqlMessage }); + } +}; + +export default addStudentToClass; diff --git a/src/endpoints/addTeacher.ts b/src/endpoints/addTeacher.ts new file mode 100644 index 0000000..72b36fc --- /dev/null +++ b/src/endpoints/addTeacher.ts @@ -0,0 +1,80 @@ +import { Request, Response } from "express"; +import insertUser from "../queries/insertUser"; +import { checkDate } from "../functions/checkDate"; +import { checkEmail } from "../functions/checkEmail"; +import { strDateToDate } from "../functions/strDateToDate"; +import { createId } from "./../functions/createId"; +import findData from "../queries/findData"; +import { findAge } from "./../functions/findAge"; +import { personBody, personBodyType } from "../types/personBody"; + +const addTeacher = async (req: Request, res: Response) => { + let errorCode: number = 400; + try { + // Parâmetros do Body + const { name, email, birthdate } = req.body as personBodyType; + + // Gera id + const id: number = await createId("teacher"); + + // VALIDAÇÕES + // Se existe campo vazio ou ausente do body + for (let item in personBody) { + if (!(item in req.body)) { + errorCode = 422; + throw new Error(`'${item}' field is missing.`); + } + } + + // Se nome tem pelo menos 3 caracteres + if (name.length < 3) { + errorCode = 422; + throw new Error(`'name' field must be at least 3 characters.`); + } + + // Se o formato do e-mail é válido + const sufixTest = email.split("."); + if (sufixTest[sufixTest.length - 1].length > 3) { + errorCode = 422; + throw new Error(`invalid e-mail type.`); + } + if (!checkEmail(email)) { + errorCode = 422; + throw new Error(`invalid e-mail type.`); + } + + // Se o e-mail já foi cadastrado + const findStudentEmail: any = await findData("teacher", "email", email); + const findTeacherEmail: any = await findData("teacher", "email", email); + if (findTeacherEmail || findTeacherEmail) { + errorCode = 422; + throw new Error(`E-mail already registered.`); + } + + // Se o fomrato de data do usuário é DD/MM/YYYY + let modDate: Date; + if (!checkDate(birthdate)) { + errorCode = 422; + throw new Error(`invalid date type. Use the format DD/MM/YYYY`); + } else { + // Converte a data de string pra Date + modDate = strDateToDate(birthdate); + } + + // Se o usuário tem pelo menos 18 anos + if (findAge(modDate) < 18) { + errorCode = 422; + throw new Error(`Invalid age. You must be over 18 to register.`); + } + + // Insere as informações no Banco de Dados + await insertUser("teacher", id, name, email, birthdate); + + // Resposta para o usuário + res.status(200).send(`Teacher registered!`); + } catch (error) { + res.status(errorCode).send({ message: error.message || error.sqlMessage }); + } +}; + +export default addTeacher; diff --git a/src/endpoints/addTeacherToClass.ts b/src/endpoints/addTeacherToClass.ts new file mode 100644 index 0000000..259eca9 --- /dev/null +++ b/src/endpoints/addTeacherToClass.ts @@ -0,0 +1,73 @@ +import { Request, Response } from "express"; +import findData from "../queries/findData"; +import insertUserToClass from "../queries/insertUserToClass"; +import findDuplicate from "../queries/findDuplicate"; + +const addTeacherToClass = async (req: Request, res: Response): Promise => { + let errorCode: number = 400; + try { + // Parâmetros do Body + const { id, classId } = req.body; + const body = ["id", "classId"]; + + // VALIDAÇÕES + // Se existe campo vazio ou ausente do body + body.forEach((item) => { + if (!(item in req.body)) { + errorCode = 422; + throw new Error(`'${item}' field is missing.`); + } + }); + + // Se o valor inserido em 'id' é um número, inteiro e positivo + if (isNaN(id) || id % 1 !== 0) { + errorCode = 422; + throw new Error("'id' must be a positive and integer number!"); + } + + //Se o id está na base de usuário selecionada + const findUser = await findData("teacher", "id", id); + if (!findUser) { + errorCode = 404; + throw new Error(`User id ${id} not found.`); + } + + // Se o valor inserido em 'classId' é um número, inteiro e positivo + if (isNaN(classId) || classId % 1 !== 0) { + errorCode = 422; + throw new Error("'classId' must be a positive and integer number!"); + } + + //Se o id está na base de turmas + const findClass = await findData("class", "id", classId); + if (!findClass) { + errorCode = 404; + throw new Error(`Class id ${id} not found.`); + } + + //Se a relaçao usuário-turma já foi cadastrada + const findRecord = await findDuplicate( + "teacher_class", + "teacher_id", + id, + "class_id", + classId + ); + if (findRecord) { + errorCode = 409; + throw new Error(`This register has already been made.`); + } + + //Insere as informações no Banco de Dados + await insertUserToClass("teacher", id, classId); + + //Resposta para o usuário + res.send({ + message: `Teacher ${findUser.name} assigned to class ${findClass.name}!`, + }); + } catch (error) { + res.status(errorCode).send({ message: error.message || error.sqlMessage }); + } +}; + +export default addTeacherToClass; diff --git a/src/endpoints/assignHobbyToStudent.ts b/src/endpoints/assignHobbyToStudent.ts new file mode 100644 index 0000000..987d473 --- /dev/null +++ b/src/endpoints/assignHobbyToStudent.ts @@ -0,0 +1,73 @@ +import { Request, Response } from "express"; +import findData from "../queries/findData"; +import findDuplicate from "../queries/findDuplicate"; +import insertHobbyToStudent from '../queries/insertHobbyToStudent' + +const assignHobbyToStudent = async ( + req: Request, + res: Response +): Promise => { + let errorCode: number = 400; + try { + const { id, hobbyId } = req.body; + const body = ["id", "hobbyId"]; + + // VALIDAÇÕES + // Se existe campo vazio ou ausente do body + body.forEach((item) => { + if (!(item in req.body)) { + errorCode = 422; + throw new Error(`'${item}' field is missing.`); + } + }); + + // Se o valor inserido em 'id' é um número, inteiro e positivo + if (isNaN(id) || id % 1 !== 0) { + errorCode = 422; + throw new Error("'id' must be a positive and integer number!"); + } + + // Se o usuário existe + const findStudent = await findData("student", "id", id); + if (!findStudent) { + errorCode = 404; + throw new Error(`Student id '${id}' not found.`); + } + + // Se o valor inserido em 'hobbyId' é um número, inteiro e positivo + if (isNaN(hobbyId) || hobbyId % 1 !== 0) { + errorCode = 422; + throw new Error("'hobbyId' must be a positive and integer number!"); + } + + // Se o info existe + const findHobby = await findData("hobby", "id", hobbyId); + if (!findHobby) { + errorCode = 404; + throw new Error(`Hobby id '${hobbyId}' not found.`); + } + + //Se o regsitro já foi realizado + const findRecord = await findDuplicate( + "student_hobby", + "student_id", + id, + "hobby_id", + hobbyId + ); + if (findRecord) { + errorCode = 409; + throw new Error("This assignment has already been made!"); + } + + // Insere as informações no Banco de Dados + await insertHobbyToStudent(id, hobbyId); + + // Resposta para o usuário + res.status(201).send({ message: "Hobby assigned" }); + } catch (error) { + res.status(errorCode).send({ message: error.message || error.sqlMessage }); + } +}; + +export default assignHobbyToStudent; diff --git a/src/endpoints/assignSpecialityToTeacher.ts b/src/endpoints/assignSpecialityToTeacher.ts new file mode 100644 index 0000000..90206e6 --- /dev/null +++ b/src/endpoints/assignSpecialityToTeacher.ts @@ -0,0 +1,73 @@ +import { Request, Response } from "express"; +import findData from "../queries/findData"; +import findDuplicate from "../queries/findDuplicate"; +import insertSpecialityToTeacher from "../queries/insertSpecialityToTeacher"; + +const assignSpecialityToTeacher = async ( + req: Request, + res: Response +): Promise => { + let errorCode: number = 400; + try { + const { id, specialityId } = req.body; + const body = ["id", "specialityId"]; + + // VALIDAÇÕES + // Se existe campo vazio ou ausente do body + body.forEach((item) => { + if (!(item in req.body)) { + errorCode = 422; + throw new Error(`'${item}' field is missing.`); + } + }); + + // Se o valor inserido em 'id' é um número, inteiro e positivo + if (isNaN(id) || id % 1 !== 0) { + errorCode = 422; + throw new Error("'id' must be a positive and integer number!"); + } + + // Se o usuário existe + const findUser = await findData("teacher", "id", id); + if (!findUser) { + errorCode = 404; + throw new Error(`Teacher id '${id}' not found.`); + } + + // Se o valor inserido em 'specialityId' é um número, inteiro e positivo + if (isNaN(specialityId) || specialityId % 1 !== 0) { + errorCode = 422; + throw new Error("'specialityId' must be a positive and integer number!"); + } + + // Se o info existe + const findInfo = await findData("speciality", "id", specialityId); + if (!findInfo) { + errorCode = 404; + throw new Error(`Speciality id '${specialityId}' not found.`); + } + + //Se o regsitro já foi realizado + const findRecord = await findDuplicate( + "teacher_speciality", + "teacher_id", + id, + "speciality_id", + specialityId + ); + if (findRecord) { + errorCode = 409; + throw new Error("This assignment has already been made!"); + } + + // Insere as informações no Banco de Dados + await insertSpecialityToTeacher(id, specialityId); + + // Resposta para o usuário + res.status(201).send({ message: "Speciality assigned!" }); + } catch (error) { + res.status(errorCode).send({ message: error.message || error.sqlMessage }); + } +}; + +export default assignSpecialityToTeacher; diff --git a/src/endpoints/changeClassModule.ts b/src/endpoints/changeClassModule.ts new file mode 100644 index 0000000..2ff2b3e --- /dev/null +++ b/src/endpoints/changeClassModule.ts @@ -0,0 +1,54 @@ +import { Request, Response } from "express"; +import editClassModule from "../queries/editClassModule"; +import findData from "../queries/findData"; + +const changeClassModule = async ( + req: Request, + res: Response +): Promise => { + let errorCode: number = 400; + try { + const { id, module } = req.body; + const body = ["id", "module"]; + + // VALIDAÇÕES + // Se existe campo vazio ou ausente do body + body.forEach((item) => { + if (!(item in req.body)) { + errorCode = 422; + throw new Error(`'${item}' field is missing.`); + } + }); + + // Se o valor inserido em 'id' é um número, inteiro e positivo + if (isNaN(id) || id % 1 !== 0) { + errorCode = 422; + throw new Error("'classId' must be a positive and integer number!"); + } + + // Se o valor inserido em 'module' é um número, inteiro, positivo e entre 0 e 7 + if (isNaN(module) || module % 1 !== 0 || module < 0 || module > 7) { + errorCode = 422; + throw new Error( + "'module' must be a positive and integer number between 0 and 7!" + ); + } + + // Se a turma existe + const findClass = await findData("class", "id", id); + if (!findClass) { + errorCode = 404; + throw new Error(`Class id '${module}' not found.`); + } + + // Edita informação + await editClassModule(id, module); + + // Resposta para o usuário + res.send({ message: "Class successfully modified!" }); + } catch (error) { + res.status(errorCode).send({ message: error.message || error.sqlMessage }); + } +}; + +export default changeClassModule; diff --git a/src/endpoints/changeStudentClass.ts b/src/endpoints/changeStudentClass.ts new file mode 100644 index 0000000..dc27c7a --- /dev/null +++ b/src/endpoints/changeStudentClass.ts @@ -0,0 +1,73 @@ +import { Request, Response } from "express"; +import editUserClass from "../queries/editUserClass"; +import findData from "../queries/findData"; +import findDuplicate from "../queries/findDuplicate"; + +const changeStudentClass = async ( + req: Request, + res: Response +): Promise => { + let errorCode: number = 400; + try { + const { id, classId } = req.body; + const body = ["id", "classId"]; + + // VALIDAÇÕES + // Se existe campo vazio ou ausente do body + body.forEach((item) => { + if (!(item in req.body)) { + errorCode = 422; + throw new Error(`'${item}' field is missing.`); + } + }); + + // Se o valor inserido em 'id' é um número, inteiro e positivo + if (isNaN(id) || id % 1 !== 0) { + errorCode = 422; + throw new Error("'id' must be a positive and integer number!"); + } + + // Se o usuário existe + const findStudent = await findData("student", "id", id); + if (!findStudent) { + errorCode = 404; + throw new Error(`Student id '${id}' not found.`); + } + + // Se o valor inserido em 'classId' é um número, inteiro e positivo + if (isNaN(classId) || classId % 1 !== 0) { + errorCode = 422; + throw new Error("'classId' must be a positive and integer number!"); + } + + // Se a turma existe + const findClass = await findData("class", "id", classId); + if (!findClass) { + errorCode = 404; + throw new Error(`Class id '${classId}' not found.`); + } + + //Se a relação existe + const findRecord = await findDuplicate( + "student_class", + "student_id", + id, + "class_id", + classId + ); + if (findRecord) { + errorCode = 409; + throw new Error("This student is already assigned to this class!"); + } + + // Edita informação + await editUserClass("student", id, classId); + + // Resposta para o usuário + res.send({ message: "Class successfully modified!" }); + } catch (error) { + res.status(errorCode).send({ message: error.message || error.sqlMessage }); + } +}; + +export default changeStudentClass; diff --git a/src/endpoints/changeTeacherClass.ts b/src/endpoints/changeTeacherClass.ts new file mode 100644 index 0000000..ecc7045 --- /dev/null +++ b/src/endpoints/changeTeacherClass.ts @@ -0,0 +1,73 @@ +import { Request, Response } from "express"; +import editUserClass from "../queries/editUserClass"; +import findData from "../queries/findData"; +import findDuplicate from "../queries/findDuplicate"; + +const changeTeacherClass = async ( + req: Request, + res: Response +): Promise => { + let errorCode: number = 400; + try { + const { id, classId } = req.body; + const body = ["id", "classId"]; + + // VALIDAÇÕES + // Se existe campo vazio ou ausente do body + body.forEach((item) => { + if (!(item in req.body)) { + errorCode = 422; + throw new Error(`'${item}' field is missing.`); + } + }); + + // Se o valor inserido em 'id' é um número, inteiro e positivo + if (isNaN(id) || id % 1 !== 0) { + errorCode = 422; + throw new Error("'classId' must be a positive and integer number!"); + } + + // Se o usuário existe + const findTeacher = await findData("teacher", "id", id); + if (!findTeacher) { + errorCode = 404; + throw new Error(`Teacher id '${id}' not found.`); + } + + // Se o valor inserido em 'classId' é um número, inteiro e positivo + if (isNaN(classId) || classId % 1 !== 0) { + errorCode = 422; + throw new Error("'classId' must be a positive and integer number!"); + } + + // Se a turma existe + const findClass = await findData("class", "id", classId); + if (!findClass) { + errorCode = 404; + throw new Error(`Class id '${classId}' not found.`); + } + + //Se a relação existe + const findRecord = await findDuplicate( + "teacher_class", + "teacher_id", + id, + "class_id", + classId + ); + if (!findRecord) { + errorCode = 409; + throw new Error("This teacher is not assigned to this class!"); + } + + // Edita informação + await editUserClass("teacher", id, classId); + + // Resposta para o usuário + res.send({ message: "Class successfully modified!" }); + } catch (error) { + res.status(errorCode).send({ message: error.message || error.sqlMessage }); + } +}; + +export default changeTeacherClass; diff --git a/src/endpoints/getClassStudents.ts b/src/endpoints/getClassStudents.ts new file mode 100644 index 0000000..051c244 --- /dev/null +++ b/src/endpoints/getClassStudents.ts @@ -0,0 +1,37 @@ +import { Request, Response } from "express"; +// import getStudentByClass from "../queries/getStudentByClass"; +import getUserByClass from "../queries/getUserByClass"; +import getHobbies from "../queries/getHobbies"; + +const getClassStudents = async (req: Request, res: Response): Promise => { + let errorCode: number = 400; + try { + // Parâmetros do query + const classId = req.params.classId as string; + + // Requisição do banco de dados + const user = await getUserByClass("student",classId) + const hobbies = await getHobbies(); + const hobbiesGroup = hobbies.reduce((acc:any, item:any) => { + acc[item.student_id] = acc[item.student_id] || []; + acc[item.student_id].push(item.title); + return acc; + }, {}); + + // Tratando as informações que devolverá ao usuário + const students = user.map((student: any): any => { + return { + name: student.name, + birthdate: student.birthdate.toLocaleDateString(), + hobbies: hobbiesGroup[student.id] + }; + }); + + // Resposta para o usuário + res.send(students); + } catch (error) { + res.status(errorCode).send({ message: error.message || error.sqlMessage }); + } +}; + +export default getClassStudents; diff --git a/src/endpoints/getClassTeachers.ts b/src/endpoints/getClassTeachers.ts new file mode 100644 index 0000000..81ff94f --- /dev/null +++ b/src/endpoints/getClassTeachers.ts @@ -0,0 +1,36 @@ +import { Request, Response } from "express"; +import getSpecs from "../queries/getSpecs"; +import getUserByClass from "../queries/getUserByClass"; + +const getClassTeachers = async (req: Request, res: Response): Promise => { + let errorCode: number = 400; + try { + // Parâmetros do query + const classId = req.params.classId as string; + + // Requisição do banco de dados + const user = await getUserByClass("teacher", classId); + const specs = await getSpecs(); + const specsGroup = specs.reduce((acc: any, item: any) => { + acc[item.teacher_id] = acc[item.teacher_id] || []; + acc[item.teacher_id].push(item.title); + return acc; + }, {}); + + // Tratando as informações que devolverá ao usuário + const teachers = user.map((teacher: any): any => { + return { + name: teacher.name, + birthdate: teacher.birthdate.toLocaleDateString(), + specialities: specsGroup[teacher.id], + }; + }); + + // Resposta para o usuário + res.send({ teachers: teachers }); + } catch (error) { + res.status(errorCode).send({ message: error.message || error.sqlMessage }); + } +}; + +export default getClassTeachers; diff --git a/src/endpoints/getStudentAge.ts b/src/endpoints/getStudentAge.ts new file mode 100644 index 0000000..03ca554 --- /dev/null +++ b/src/endpoints/getStudentAge.ts @@ -0,0 +1,45 @@ +import { Request, Response } from "express"; +import findData from "../queries/findData"; +import getHobbies from "../queries/getHobbies"; +import { findAge } from "./../functions/findAge"; + +const getStudentAge = async (req: Request, res: Response): Promise => { + let errorCode: number = 400; + try { + // Parâmetro do path + const id: number = Number(req.params.id); + + // Pegando usuário no banco de dados + const profile: any = await findData("student", "id", id); + + if (!profile) { + errorCode = 404; + throw new Error("Student not found."); + } + + const hobbies = await getHobbies(); + const hobbiesGroup = hobbies.reduce((acc: any, item: any) => { + acc[item.student_id] = acc[item.student_id] || []; + acc[item.student_id].push(item.title); + return acc; + }, {}); + + // Calculando idade pela data de nascimento + const age: number = findAge(profile.birthdate); + + // Resposta para o usuário + res + .status(200) + .send({ + user: { + name: profile.name, + age: age, + hobbies: hobbiesGroup[profile.id], + }, + }); + } catch (error) { + res.status(errorCode).send({ message: error.message || error.sqlMessage }); + } +}; + +export default getStudentAge; diff --git a/src/endpoints/getTeacherAge.ts b/src/endpoints/getTeacherAge.ts new file mode 100644 index 0000000..b8f6f41 --- /dev/null +++ b/src/endpoints/getTeacherAge.ts @@ -0,0 +1,43 @@ +import { Request, Response } from "express"; +import findData from "../queries/findData"; +import { findAge } from "../functions/findAge"; +import getSpecs from "../queries/getSpecs"; + +const getTeacherAge = async (req: Request, res: Response): Promise => { + let errorCode: number = 400; + try { + // Parâmetro do path + const id: number = Number(req.params.id); + + // Pegando usuário no banco de dados + const profile: any = await findData("teacher", "id", id); + + if (!profile) { + errorCode = 404; + throw new Error("Teacher not found."); + } + + const specs = await getSpecs(); + const specsGroup = specs.reduce((acc: any, item: any) => { + acc[item.teacher_id] = acc[item.teacher_id] || []; + acc[item.teacher_id].push(item.title); + return acc; + }, {}); + + // Calculando idade pela data de nascimento + const age: number = findAge(profile.birthdate); + + // Resposta para o usuário + res.status(200).send({ + user: { + name: profile.name, + age: age, + hobbies: specsGroup[profile.id], + }, + }); + } catch (error) { + res.status(errorCode).send({ message: error.message || error.sqlMessage }); + } +}; + +export default getTeacherAge; diff --git a/src/endpoints/removeStudent.ts b/src/endpoints/removeStudent.ts new file mode 100644 index 0000000..7c980ac --- /dev/null +++ b/src/endpoints/removeStudent.ts @@ -0,0 +1,47 @@ +import { Request, Response } from "express"; +import deleteClassAssignment from "../queries/deleteClassAssignment"; +import deleteUser from "../queries/deleteUser"; +import findData from "../queries/findData"; + +const removeStudent = async ( + req: Request, + res: Response +): Promise => { + let errorCode: number = 400; + try { + const id = Number(req.params.id); + + // VALIDAÇÕES + // Se id é um número + if (isNaN(id)) { + errorCode = 422; + throw new Error(`Invalid id type`); + } + + // Se o usuário existe + const findStudent = await findData("student", "id", id); + if (!findStudent) { + errorCode = 404; + throw new Error(`Student id '${id}' not found.`); + } + + // Se já está inscrito em alguma turma + const findClass = await findData("student_class", "student_id", id); + if (findClass) { + const classId = findClass.class_id; + + // Remove o aluno da turma + await deleteClassAssignment("student", id, classId); + } + + // Remove o aluno da base de dados + await deleteUser("student", id); + + // Resposta para o usuário + res.send({ message: "Student removed from database!" }); + } catch (error) { + res.status(errorCode).send({ message: error.message || error.sqlMessage }); + } +}; + +export default removeStudent; diff --git a/src/endpoints/removeStudentFromClass.ts b/src/endpoints/removeStudentFromClass.ts new file mode 100644 index 0000000..e06eeba --- /dev/null +++ b/src/endpoints/removeStudentFromClass.ts @@ -0,0 +1,49 @@ +import { Request, Response } from "express"; +import deleteClassAssignment from "../queries/deleteClassAssignment"; +import findData from "../queries/findData"; +import findDuplicate from "../queries/findDuplicate"; + +const removeStudentFromClass = async ( + req: Request, + res: Response +): Promise => { + let errorCode: number = 400; + try { + const { id, classId } = req.params; + + // VALIDAÇÕES + // Se o usuário existe + const findStudent = await findData("student", "id", id); + if (!findStudent) { + errorCode = 404; + throw new Error(`Student id '${id}' not found.`); + } + + // Se a turma existe + const findClass = await findData("class", "id", classId); + if (!findClass) { + errorCode = 404; + throw new Error(`Class id '${classId}' not found.`); + } + + //Se a relação existe + const findRecord = await findDuplicate( + "student_class", + "student_id", + id, + "class_id", + classId + ); + if (!findRecord) { + errorCode = 409; + throw new Error("This student is not assigned to this class!"); + } + + await deleteClassAssignment("student", Number(id), Number(classId)); + res.send({ message: "Student removed from class!" }); + } catch (error) { + res.status(errorCode).send({ message: error.message || error.sqlMessage }); + } +}; + +export default removeStudentFromClass; diff --git a/src/endpoints/removeTeacher.ts b/src/endpoints/removeTeacher.ts new file mode 100644 index 0000000..621616a --- /dev/null +++ b/src/endpoints/removeTeacher.ts @@ -0,0 +1,44 @@ +import { Request, Response } from "express"; +import deleteClassAssignment from "../queries/deleteClassAssignment"; +import deleteUser from "../queries/deleteUser"; +import findData from "../queries/findData"; + +const removeTeacher = async (req: Request, res: Response): Promise => { + let errorCode: number = 400; + try { + const id = Number(req.params.id); + + // VALIDAÇÕES + // Se id é um número + if (isNaN(id)) { + errorCode = 422; + throw new Error(`Invalid id type`); + } + + // Se o usuário existe + const findTeacher = await findData("teacher", "id", id); + if (!findTeacher) { + errorCode = 404; + throw new Error(`Teacher id '${id}' not found.`); + } + + // Se já está inscrito em alguma turma + const findClass = await findData("teacher_class", "teacher_id", id); + if (findClass) { + const classId = findClass.class_id; + + // Remove o aluno da turma + await deleteClassAssignment("teacher", id, classId); + } + + // Remove o aluno da base de dados + await deleteUser("teacher", id); + + // Resposta para o usuário + res.send({ message: "Teacher removed from database!" }); + } catch (error) { + res.status(errorCode).send({ message: error.message || error.sqlMessage }); + } +}; + +export default removeTeacher; diff --git a/src/endpoints/removeTeacherFromClass.ts b/src/endpoints/removeTeacherFromClass.ts new file mode 100644 index 0000000..2325c3e --- /dev/null +++ b/src/endpoints/removeTeacherFromClass.ts @@ -0,0 +1,49 @@ +import { Request, Response } from "express"; +import deleteClassAssignment from "../queries/deleteClassAssignment"; +import findData from "../queries/findData"; +import findDuplicate from "../queries/findDuplicate"; + +const removeTeacherFromClass = async ( + req: Request, + res: Response +): Promise => { + let errorCode: number = 400; + try { + const { id, classId } = req.params; + + // VALIDAÇÕES + // Se o usuário existe + const findTeacher = await findData("teacher", "id", id); + if (!findTeacher) { + errorCode = 404; + throw new Error(`Teacher id '${id}' not found.`); + } + + // Se a info existe + const findHobby = await findData("hobby", "id", classId); + if (!findHobby) { + errorCode = 404; + throw new Error(`Hobby id '${classId}' not found.`); + } + + //Se a relação existe + const findRecord = await findDuplicate( + "teacher_class", + "teacher_id", + id, + "class_id", + classId + ); + if (findRecord) { + errorCode = 409; + throw new Error("This teacher is not assigned to this class!"); + } + + await deleteClassAssignment("teacher", Number(id), Number(classId)); + res.send({ message: "Teacher removed from class!" }); + } catch (error) { + res.status(errorCode).send({ message: error.message || error.sqlMessage }); + } +}; + +export default removeTeacherFromClass; diff --git a/src/functions/checkDate.ts b/src/functions/checkDate.ts new file mode 100644 index 0000000..d32c073 --- /dev/null +++ b/src/functions/checkDate.ts @@ -0,0 +1,4 @@ +export const checkDate = (date: string) => { + const re = /^([0-2][0-9]|(3)[0-1])(\/)(((0)[0-9])|((1)[0-2]))(\/)\d{4}$/; + return re.test(date); +}; diff --git a/src/functions/checkEmail.ts b/src/functions/checkEmail.ts new file mode 100644 index 0000000..603001a --- /dev/null +++ b/src/functions/checkEmail.ts @@ -0,0 +1,4 @@ +export const checkEmail = (email: string) => { + const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + return re.test(String(email).toLowerCase()); +}; diff --git a/src/functions/createId.ts b/src/functions/createId.ts new file mode 100644 index 0000000..ae52a4d --- /dev/null +++ b/src/functions/createId.ts @@ -0,0 +1,8 @@ +import getMaxIdNumber from "../queries/getMaxIdNumber"; + +export const createId = async (table: string) => { + const getMaxId = await getMaxIdNumber(table); + const maxId = Object.values(getMaxId); + const newId = Number(maxId[0]) + 1; + return newId; +}; diff --git a/src/functions/dateDiff.ts b/src/functions/dateDiff.ts new file mode 100644 index 0000000..49cf82f --- /dev/null +++ b/src/functions/dateDiff.ts @@ -0,0 +1,5 @@ +export const dateDiff = (date1: Date, date2: Date) => { + const d1 = new Date(date1).getTime(); + const d2 = new Date(date2).getTime(); + return d2 - d1; +}; diff --git a/src/functions/findAge.ts b/src/functions/findAge.ts new file mode 100644 index 0000000..9c23098 --- /dev/null +++ b/src/functions/findAge.ts @@ -0,0 +1,27 @@ +export const findAge = (birthDate: Date): number => { +// const birthToNumber = new Date.(birthDate).getTime(); + const userYear = new Date(birthDate).getFullYear(); + const userMonth = new Date(birthDate).getMonth()+1; + const userDay = new Date(birthDate).getDate(); + + // Pegando os dados da data de hoje + const todayYear = new Date().getFullYear(); + const todayMonth = new Date().getMonth()+1; + const todayDay = new Date().getDate(); + + // Checando idade no dia de hoje + let age: number; + if (todayDay < userDay) { + if (todayMonth <= userMonth) { + return (age = todayYear - userYear - 1); + } else { + return (age = todayYear - userYear); + } + } else { + if (todayMonth <= userMonth) { + return (age = todayYear - userYear); + } else { + return (age = todayYear - userYear - 1); + } + } +}; \ No newline at end of file diff --git a/src/functions/strDateToDate.ts b/src/functions/strDateToDate.ts new file mode 100644 index 0000000..893fc0c --- /dev/null +++ b/src/functions/strDateToDate.ts @@ -0,0 +1,4 @@ +export const strDateToDate = (dateString: string) => { + const arrDateString = dateString.split("/"); + return new Date(Number(arrDateString[2]), Number(arrDateString[1])-1, Number(arrDateString[0])); +}; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..48b5a73 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,97 @@ +import app from "./app"; +import addClass from "./endpoints/addClass"; +import addStudentToClass from "./endpoints/addStudentToClass"; +import addTeacherToClass from "./endpoints/addTeacherToClass"; +import addHobby from "./endpoints/addHobby"; +import addSpeciality from "./endpoints/addSpeciality"; +import addStudent from "./endpoints/addStudent"; +import addTeacher from "./endpoints/addTeacher"; +import assignHobbyToStudent from "./endpoints/assignHobbyToStudent"; +import assignSpecialityToTeacher from "./endpoints/assignSpecialityToTeacher"; +import changeClassModule from "./endpoints/changeClassModule"; +import changeStudentClass from "./endpoints/changeStudentClass"; +import changeTeacherClass from "./endpoints/changeTeacherClass"; +import getClassStudents from "./endpoints/getClassStudents"; + +// import getClassStudents2 from "./endpoints/getClassStudents2"; + +import getClassTeachers from "./endpoints/getClassTeachers"; +import getStudentAge from "./endpoints/getStudentAge"; +import removeStudent from "./endpoints/removeStudent"; +import removeStudentFromClass from "./endpoints/removeStudentFromClass"; +import removeTeacher from "./endpoints/removeTeacher"; +import removeTeacherFromClass from './endpoints/removeTeacherFromClass'; +import getTeacherAge from './endpoints/getTeacherAge'; + + +// POST ----------------------------------- +// Cadastrar aluno +app.post("/student/create", addStudent); + +// Cadastrar profesor +app.post("/teacher/create", addTeacher); + +// Cadastrar hobby +app.post("/hobby/create", addHobby); + +// Cadastrar especialidade +app.post("/speciality/create", addSpeciality); + +// Cadastrar turma +app.post("/class/create", addClass); + +// Cadastrar aluno em uma turma +app.post("/student/class/assign", addStudentToClass); + +// Cadastrar professor em uma turma +app.post("/teacher/class/assign", addTeacherToClass); + + +// Atribuir hobby a um estudante +app.post("/student/hobby/assign",assignHobbyToStudent) + +// Atribuir especialidade a um professor +app.post("/teacher/speciality/assign",assignSpecialityToTeacher); + +// GET ----------------------------------- +// Pegar estudante pelo ID +app.get("/student/:id", getStudentAge); + +// Pegar professor pelo ID +app.get("/teacher/:id", getTeacherAge); + +// Pegar estudantes por turma +app.get("/student/list/:classId", getClassStudents); + +// Pegar professores por turma +app.get("/teacher/list/:classId", getClassTeachers); + +// PUT ----------------------------------- +// Alterar módulo da turma +app.put("/class/module/edit",changeClassModule) + +// Alterar turma do aluno +app.put("/student/class/edit", changeStudentClass); + +// Alterar turma do professor +app.put("/teacher/class/edit", changeTeacherClass); + +// DELETE ----------------------------------- +// Remover estudate da base +app.delete("/student/:id/delete",removeStudent); + +// Remover professor da base +app.delete("/teacher/:id/delete", removeTeacher); + +// Remover estudate da turma +app.delete("/student/:id/class/:classId/delete", removeStudentFromClass); + +// Remover professor da turma +app.delete("/teacher/:id/class/:classId/delete", removeTeacherFromClass); + + + + + + + diff --git a/src/queries/deleteClassAssignment.ts b/src/queries/deleteClassAssignment.ts new file mode 100644 index 0000000..615d23d --- /dev/null +++ b/src/queries/deleteClassAssignment.ts @@ -0,0 +1,13 @@ +import connection from "../connection"; + +export default async function deleteClassAssignment( + category: string, + userId: number, + classId: number +): Promise { + await connection.raw(` + DELETE FROM ${category}_class + WHERE ${category}_id = ${userId} + AND class_id = ${classId} + `); +} diff --git a/src/queries/deleteUser.ts b/src/queries/deleteUser.ts new file mode 100644 index 0000000..c28b2d1 --- /dev/null +++ b/src/queries/deleteUser.ts @@ -0,0 +1,8 @@ +import connection from "../connection"; + +export default async function deleteUser(category:string, id:number): Promise { + await connection.raw(` + DELETE FROM ${category} + WHERE id = ${id} + `); +} diff --git a/src/queries/editClassModule.ts b/src/queries/editClassModule.ts new file mode 100644 index 0000000..133992a --- /dev/null +++ b/src/queries/editClassModule.ts @@ -0,0 +1,12 @@ +import connection from "../connection"; + +export default async function editClassModule( + id: number, + module: number +): Promise { + await connection.raw(` + UPDATE class + SET module = ${module} + WHERE id = ${id} + `); +} diff --git a/src/queries/editUserClass.ts b/src/queries/editUserClass.ts new file mode 100644 index 0000000..c41778e --- /dev/null +++ b/src/queries/editUserClass.ts @@ -0,0 +1,13 @@ +import connection from "../connection"; + +export default async function editUserClass( + category: string, + id: number, + classId: number +): Promise { + await connection.raw(` + UPDATE ${category}_class + SET class_id = ${classId} + WHERE ${category}_id = ${id} + `); +} diff --git a/src/queries/findData.ts b/src/queries/findData.ts new file mode 100644 index 0000000..f7b7225 --- /dev/null +++ b/src/queries/findData.ts @@ -0,0 +1,14 @@ +import connection from "../connection"; + +export default async function findData( + table: string, + columnName: string, + value: string | number +): Promise { + const result = await connection.raw(` + SELECT * + FROM ${table} + WHERE ${columnName} = "${value}" + `); + return result[0][0]; +} diff --git a/src/queries/findDuplicate.ts b/src/queries/findDuplicate.ts new file mode 100644 index 0000000..65af0b5 --- /dev/null +++ b/src/queries/findDuplicate.ts @@ -0,0 +1,17 @@ +import connection from "../connection"; + +export default async function findDuplicate( + table: string, + columnName1: string, + value1: string | number, + columnName2: string, + value2: string | number +): Promise { + const result = await connection.raw( + `SELECT * + FROM ${table} + WHERE ${columnName1} = "${value1}" + AND ${columnName2} = "${value2}"` + ); + return result[0][0]; +} diff --git a/src/queries/getHobbies.ts b/src/queries/getHobbies.ts new file mode 100644 index 0000000..f8d35ef --- /dev/null +++ b/src/queries/getHobbies.ts @@ -0,0 +1,14 @@ +import connection from "../connection"; + +export default async function getHobbies(): Promise { + const result = await connection.raw(` + SELECT student_hobby.*, hobby.title + FROM student_hobby + JOIN student + ON student_hobby.student_id = student.id + JOIN hobby + ON student_hobby.hobby_id = hobby.id + `); + + return result[0]; +} diff --git a/src/queries/getMaxIdNumber.ts b/src/queries/getMaxIdNumber.ts new file mode 100644 index 0000000..3b3b506 --- /dev/null +++ b/src/queries/getMaxIdNumber.ts @@ -0,0 +1,13 @@ +import connection from "../connection"; + +export default async function getMaxIdNumber(table: string): Promise { + const result = await connection.raw(` + SELECT MAX(id) + FROM ${table}; + `); + if (result[0].length < 1) { + return 0; + } else { + return result[0][0]; + } +} diff --git a/src/queries/getSpecs.ts b/src/queries/getSpecs.ts new file mode 100644 index 0000000..adf434f --- /dev/null +++ b/src/queries/getSpecs.ts @@ -0,0 +1,14 @@ +import connection from "../connection"; + +export default async function getSpecs(): Promise { + const result = await connection.raw(` + SELECT teacher_speciality.*, speciality.title + FROM teacher_speciality + JOIN teacher + ON teacher_speciality.teacher_id = teacher.id + JOIN speciality + ON teacher_speciality.speciality_id = speciality.id + `); + + return result[0]; +} diff --git a/src/queries/getStudentByClass.ts b/src/queries/getStudentByClass.ts new file mode 100644 index 0000000..65a77f2 --- /dev/null +++ b/src/queries/getStudentByClass.ts @@ -0,0 +1,17 @@ +import connection from "../connection"; + +export default async function getStudentByClass(classId: string): Promise { + const result = await connection.raw(` + SELECT student.id, student.name, student.birthdate, student_class.class_id, student_hobby.hobby_id, hobby.title + FROM student_class + JOIN student + ON student_class.student_id = student.id + LEFT JOIN student_hobby + ON student.id = student_hobby.student_id + LEFT JOIN hobby + ON student_hobby.hobby_id = hobby.id + WHERE student_class.class_id = ${classId} + `); + + return result[0]; +} diff --git a/src/queries/getStudentProfile.ts b/src/queries/getStudentProfile.ts new file mode 100644 index 0000000..4efaf9f --- /dev/null +++ b/src/queries/getStudentProfile.ts @@ -0,0 +1,11 @@ +import connection from '../connection'; + +export default async function getStudentProfile(id:number): Promise { + const result = await connection.raw(` + SELECT id, name, birthdate + FROM student + WHERE id = ${id} + `); + + return result[0][0]; +} diff --git a/src/queries/getTeacherByClass.ts b/src/queries/getTeacherByClass.ts new file mode 100644 index 0000000..3a26688 --- /dev/null +++ b/src/queries/getTeacherByClass.ts @@ -0,0 +1,13 @@ +import connection from "../connection"; + +export default async function getUserByClass(table:string, classId: string): Promise { + const result = await connection.raw(` + SELECT ${table}.id, ${table}.name, ${table}.birthdate, ${table}_class.class_id + FROM ${table} + JOIN ${table}_class + ON ${table}.id = ${table}_class.${table}_id + WHERE ${table}_class.class_id = "${classId}" + `); + + return result[0]; +} diff --git a/src/queries/getUserByClass.ts b/src/queries/getUserByClass.ts new file mode 100644 index 0000000..deea057 --- /dev/null +++ b/src/queries/getUserByClass.ts @@ -0,0 +1,13 @@ +import connection from "../connection"; + +export default async function getUserByClass(table:string, classId: string): Promise { + const result = await connection.raw(` + SELECT * + FROM ${table} + JOIN ${table}_class + ON ${table}.id = ${table}_class.${table}_id + WHERE ${table}_class.class_id = "${classId}" + `); + + return result[0]; +} diff --git a/src/queries/insertClass.ts b/src/queries/insertClass.ts new file mode 100644 index 0000000..c21fcd0 --- /dev/null +++ b/src/queries/insertClass.ts @@ -0,0 +1,15 @@ +import connection from "../connection"; + +export default async function insertClass( + id: number, + name: string, + startDate: string, + endDate: string, + module: number +): Promise { + await connection.raw(` + INSERT INTO class (id, name, start_date, end_date, module) + VALUES + (${id}, "${name}", str_to_date("${startDate}", '%d/%m/%Y'), str_to_date("${endDate}", '%d/%m/%Y'), ${module}) + `); +} diff --git a/src/queries/insertHobbyToStudent.ts b/src/queries/insertHobbyToStudent.ts new file mode 100644 index 0000000..4a9cd83 --- /dev/null +++ b/src/queries/insertHobbyToStudent.ts @@ -0,0 +1,15 @@ +import connection from "../connection"; + +export default async function insertHobbyToStudent( + studentId: number, + hobbyId: number +): Promise { + await connection.raw( + ` + INSERT + INTO student_hobby (student_id, hobby_id) + VALUES + ("${studentId}", "${hobbyId}") + ` + ); +} diff --git a/src/queries/insertInfo.ts b/src/queries/insertInfo.ts new file mode 100644 index 0000000..9a792e1 --- /dev/null +++ b/src/queries/insertInfo.ts @@ -0,0 +1,13 @@ +import connection from "../connection"; + +export default async function insertInfo( + table: string, + id: number, + title: string +): Promise { + await connection.raw(` + INSERT INTO ${table} (id, title) + VALUES + (${id}, "${title}") + `); +} diff --git a/src/queries/insertSpecialityToTeacher.ts b/src/queries/insertSpecialityToTeacher.ts new file mode 100644 index 0000000..7eb7e44 --- /dev/null +++ b/src/queries/insertSpecialityToTeacher.ts @@ -0,0 +1,15 @@ +import connection from "../connection"; + +export default async function insertSpecialityToTeacher( + teacherId: number, + specialityId: number +): Promise { + await connection.raw( + ` + INSERT + INTO teacher_speciality (teacher_id, speciality_id) + VALUES + ("${teacherId}", "${specialityId}") + ` + ); +} diff --git a/src/queries/insertUser.ts b/src/queries/insertUser.ts new file mode 100644 index 0000000..81056bd --- /dev/null +++ b/src/queries/insertUser.ts @@ -0,0 +1,15 @@ +import connection from "../connection"; + +export default async function insertUser( + table: string, + id: number, + name: string, + email: string, + birthdate: string +): Promise { + await connection.raw(` + INSERT INTO ${table} (id, name, email, birthdate) + VALUES + (${id}, "${name}", "${email}", str_to_date("${birthdate}", '%d/%m/%Y')) + `); +} diff --git a/src/queries/insertUserToClass.ts b/src/queries/insertUserToClass.ts new file mode 100644 index 0000000..95db070 --- /dev/null +++ b/src/queries/insertUserToClass.ts @@ -0,0 +1,16 @@ +import connection from "../connection"; + +export default async function insertUserToClass( + table: string, + userId: number, + classId: number +): Promise { + await connection.raw( + ` + INSERT + INTO ${table}_class (${table}_id, class_id) + VALUES + ("${userId}", "${classId}") + ` + ); +} diff --git a/src/requests.rest b/src/requests.rest new file mode 100644 index 0000000..eb0ba58 --- /dev/null +++ b/src/requests.rest @@ -0,0 +1,152 @@ +# Criar estudante +POST http://localhost:3003/student/create +Content-Type: application/json + +{ + "category": "student", + "name": "user", + "email": "user@labenu.com.br", + "birthdate": "01/01/2000" +} + +### +# Criar professor +POST http://localhost:3003/teacher/create +Content-Type: application/json + +{ + "category": "student", + "name": "user", + "email": "user@labenu.com.br", + "birthdate": "01/01/2000" +} + +### +# Criar hobby +POST http://localhost:3003/hobby/create +Content-Type: application/json + +{ + "title": "Codar" +} + +### +# Criar especialidade +POST http://localhost:3003/speciality/create +Content-Type: application/json + +{ + "title": "React000" +} + +### +# Criar turma +POST http://localhost:3003/class/create +Content-Type: application/json + +{ + "name": "Muñoz", + "period": "Noturno", + "startDate": "30/02/2020", + "endDate": "31/10/2021", + "module": 1 +} + +### +# Cadastrar aluno em uma turma +POST http://localhost:3003/student/class/assign +Content-Type: application/json + +{ + "id": 10, + "classId": 2 +} + +### +# Cadastrar professor em uma turma +POST http://localhost:3003/teacher/class/assign +Content-Type: application/json + +{ + "id": 1, + "classId": 1 +} + +### +# Atribuir hobby a um estudante +POST http://localhost:3003/student/hobby/assign +Content-Type: application/json + +{ + "id": 1, + "hobbyId": 1 +} + +### +# Atribuir especialidade a um professor +POST http://localhost:3003/teacher/speciality/assign +Content-Type: application/json + +{ + "id": 1, + "scpecialityId": 1 +} + +### +# Pegar estudante pelo ID +GET http://localhost:3003/student/10000 + +### +# Pegar professor pelo ID +GET http://localhost:3003/teacher/162 + +### +# Pegar estudantes por turma +GET http://localhost:3003/student/list/200 + +### +# Pegar professores por turma +GET http://localhost:3003/teacher/list/1 + +### +# Alterar módulo da turma +PUT http://localhost:3003/class/module/edit +Content-Type: application/json + +{ + "id": 1, + "module": 1 +} + +### +# Alterar turma do aluno +PUT http://localhost:3003/student/class/edit +Content-Type: application/json + +{ + "id": 1, + "classId": 1 +} + +### +# Alterar turma do professor +PUT http://localhost:3003/teacher/class/edit +Content-Type: application/json + +{ + "id": 1, + "classId": 1 +} + +### +# Remover estudate da base +DELETE http://localhost:3003/student/1/delete + +# Remover professor da base +DELETE http://localhost:3003/teacher/1/delete + +# Remover estudate da turma +DELETE http://localhost:3003/student/1/class/1/delete + +# Remover professor da turma +DELETE http://localhost:3003/teacher/1/class/1/delete \ No newline at end of file diff --git a/src/sql/database.sql b/src/sql/database.sql new file mode 100644 index 0000000..4087271 --- /dev/null +++ b/src/sql/database.sql @@ -0,0 +1,70 @@ + +USER `epps-fabio-santos`; + +CREATE TABLE class ( + id INT PRIMARY KEY, + name VARCHAR(50) NOT NULL UNIQUE, + start_date DATE NOT NULL, + end_date DATE NOT NULL, + module INT NOT NULL CHECK (module BETWEEN 0 AND 7) +); + +CREATE TABLE student ( + id INT PRIMARY KEY, + name VARCHAR(50) NOT NULL, + email VARCHAR(50) NOT NULL UNIQUE, + birthdate DATE NOT NULL, +); + +CREATE TABLE teacher ( + id INT PRIMARY KEY, + name VARCHAR(50) NOT NULL, + email VARCHAR(50) NOT NULL UNIQUE, + birthdate DATE NOT NULL, +); + +CREATE TABLE hobby ( + id INT PRIMARY KEY AUTO_INCREMENT, + title VARCHAR(50) NOT NULL +); + +CREATE TABLE speciality ( + id INT PRIMARY KEY AUTO_INCREMENT, + title VARCHAR(50) NOT NULL +); + +INSERT INTO speciality VALUES(1,"React"); +INSERT INTO speciality VALUES(2,"Redux"); +INSERT INTO speciality VALUES(3,"CSS"); +INSERT INTO speciality VALUES(4,"Testes"); +INSERT INTO speciality VALUES(5,"Typescript"); +INSERT INTO speciality VALUES(6,"Programação Orientada a Objetos"); +INSERT INTO speciality VALUES(7,"Backend"); + +CREATE TABLE student_hobby ( + student_id INT NOT NULL, + hobby_id INT NOT NULL, + FOREIGN KEY (student_id) REFERENCES student(id), + FOREIGN KEY (hobby_id) REFERENCES hobby(id) +); + +CREATE TABLE teacher_speciality ( + teacher_id INT NOT NULL, + speciality_id INT NOT NULL, + FOREIGN KEY (teacher_id) REFERENCES teacher(id), + FOREIGN KEY (speciality_id) REFERENCES speciality(id) +); + +CREATE TABLE student_class ( + student_id INT NOT NULL, + class_id INT NOT NULL, + FOREIGN KEY (student_id) REFERENCES student(id), + FOREIGN KEY (class_id) REFERENCES class(id) +); + +CREATE TABLE teacher_class ( + teacher_id INT NOT NULL, + class_id INT NOT NULL, + FOREIGN KEY (teacher_id) REFERENCES teacher(id), + FOREIGN KEY (class_id) REFERENCES class(id) +); \ No newline at end of file diff --git a/src/types/classBody.ts b/src/types/classBody.ts new file mode 100644 index 0000000..d172eb7 --- /dev/null +++ b/src/types/classBody.ts @@ -0,0 +1,15 @@ +export type classBodyType = { + name: string; + period: string; + startDate: string; + endDate: string; + module: number; +}; + +export enum classBody { + name = "name", + period = "period", + startDate = "startDate", + endDate = "endDate", + module = "module", +} \ No newline at end of file diff --git a/src/types/infoBody.ts b/src/types/infoBody.ts new file mode 100644 index 0000000..84c82f6 --- /dev/null +++ b/src/types/infoBody.ts @@ -0,0 +1,9 @@ +export type infoType = { + id: number; + title: string; +}; + +export enum infoBody { + id = "id", + title = "title" +} diff --git a/src/types/personBody.ts b/src/types/personBody.ts new file mode 100644 index 0000000..b804eee --- /dev/null +++ b/src/types/personBody.ts @@ -0,0 +1,11 @@ +export type personBodyType = { + name: string, + email: string, + birthdate: string +}; + +export enum personBody { + name= "name", + email= "email", + birthdate= "birthdate" +}; \ No newline at end of file diff --git a/src/types/specialities.ts b/src/types/specialities.ts new file mode 100644 index 0000000..e0d8550 --- /dev/null +++ b/src/types/specialities.ts @@ -0,0 +1,9 @@ +export enum specList { + REACT = "React", + REDUX = "Redux", + CSS = "CSS", + TESTES = "Testes", + TYPESCRIPT = "Typescript", + POO = "Programação Orientada a Objetos", + BACKEND = "Backend" +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..b6574ed --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "sourceMap": true, + "outDir": "./build", + "rootDir": "./src", + "removeComments": true, + "strict": true, + "noImplicitAny": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true + } +} \ No newline at end of file