├── .env.example ├── .eslintrc.json ├── .gitignore ├── README.md ├── database ├── data.sql └── schema.sql ├── package-lock.json ├── package.json └── src ├── http ├── modules │ ├── categories.js │ ├── products.js │ └── users.js └── routes.js ├── index.js ├── server ├── cors.js ├── index.js └── jwtMiddleware.js └── services └── mysql ├── auth.js ├── categories.js ├── index.js ├── products.js ├── tests ├── auth.test.js ├── categories.test.js ├── products.test.js ├── setup.js └── users.test.js └── users.js /.env.example: -------------------------------------------------------------------------------- 1 | MYSQL_HOST= 2 | MYSQL_USERNAME= 3 | MYSQL_PASSWORD= 4 | MYSQL_DATABASE= 5 | MYSQL_TEST_DATABASE= 6 | 7 | JWT_SECRET= 8 | 9 | PORT=3456 -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "standard" 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | node_modules 3 | curso-spa.paw -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RESTful com Node.js, Restify e MySql 2 | 3 | > Este projeto foi desenvolvido durante a gravação da série de screencasts disponível em [https://www.youtube.com/playlist?list=PLFJmwzuHdBRTBbkyH0gATtDhj6ikOIkMy](https://www.youtube.com/playlist?list=PLFJmwzuHdBRTBbkyH0gATtDhj6ikOIkMy) e é pré-requisito para o curso **Single Page Application com Vue.js** [http://www.treinatom.com.br/pt/edukee/detalhes-do-evento/190edc6b7593e3081a858f55652abd92a9d07353](http://www.treinatom.com.br/pt/edukee/detalhes-do-evento/190edc6b7593e3081a858f55652abd92a9d07353) 4 | 5 | ### Pré-requisitos 6 | 7 | - **Node.js** versão 8 ou superior; 8 | - **Nodemon** - `npm i -g nodemon`. 9 | 10 | ### Instalação e Execução 11 | 12 | 1. Faça o clone do repositório e no terminal navegue até a pasta; 13 | 2. Instale as dependências do projeto com `npm install`; 14 | 3. Faça uma cópia do **.env.example** com o nome **.env** e adicione as informações de conexão com seu banco de dados MySQL (produção e teste). 15 | 4. Na pasta **database** se encontram dois arquivos: **schema.sql** e **data.sql**. Utilize-os para criar as tabelas e - opcionalmente - popula-las com dados; 16 | 5. Rode o servidor de desenvolvimento com `npm run dev`; 17 | 6. Rode os testes com `npm test`. Para observar os arquivos durante o desenvolvimento utilize `npm test -- --watch`; 18 | 7. O *endpoint* do serviço estará disponível em http://localhost:3456 . 19 | 20 | ### Sugestão 21 | 22 | Utilize o Postman para testar suas chamadas. [https://www.getpostman.com/](https://www.getpostman.com/). -------------------------------------------------------------------------------- /database/data.sql: -------------------------------------------------------------------------------- 1 | # ************************************************************ 2 | # Sequel Pro SQL dump 3 | # Version 4541 4 | # 5 | # http://www.sequelpro.com/ 6 | # https://github.com/sequelpro/sequelpro 7 | # 8 | # Host: 127.0.0.1 (MySQL 5.7.12) 9 | # Database: restful_ws 10 | # Generation Time: 2017-11-10 18:03:45 +0000 11 | # ************************************************************ 12 | 13 | 14 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 15 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 16 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 17 | /*!40101 SET NAMES utf8 */; 18 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; 19 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; 20 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; 21 | 22 | 23 | # Dump of table categories 24 | # ------------------------------------------------------------ 25 | 26 | LOCK TABLES `categories` WRITE; 27 | /*!40000 ALTER TABLE `categories` DISABLE KEYS */; 28 | 29 | INSERT INTO `categories` (`id`, `name`) 30 | VALUES 31 | (1,'at lorem integer'), 32 | (2,'mauris enim leo rhoncus sed'), 33 | (3,'lobortis est phasellus sit amet'), 34 | (4,'at lorem integer'), 35 | (5,'fusce consequat nulla nisl nunc'), 36 | (6,'morbi vel lectus in quam'), 37 | (7,'ornare imperdiet sapien urna'), 38 | (8,'pellentesque ultrices mattis odio'), 39 | (9,'id justo sit amet'), 40 | (10,'erat curabitur gravida nisi at'), 41 | (11,'dictumst etiam faucibus'), 42 | (12,'eget tincidunt eget'), 43 | (13,'mi sit amet'), 44 | (14,'mauris sit amet eros suspendisse'), 45 | (15,'curae nulla dapibus dolor'), 46 | (16,'libero nam dui proin leo'), 47 | (17,'mauris enim leo rhoncus sed'), 48 | (18,'lacus morbi sem'), 49 | (19,'pulvinar nulla pede ullamcorper augue'), 50 | (20,'quis justo maecenas'); 51 | 52 | /*!40000 ALTER TABLE `categories` ENABLE KEYS */; 53 | UNLOCK TABLES; 54 | 55 | 56 | # Dump of table products 57 | # ------------------------------------------------------------ 58 | 59 | LOCK TABLES `products` WRITE; 60 | /*!40000 ALTER TABLE `products` DISABLE KEYS */; 61 | 62 | INSERT INTO `products` (`id`, `category_id`, `name`) 63 | VALUES 64 | (1,20,'nunc commodo placerat'), 65 | (2,7,'ipsum integer a'), 66 | (3,11,'quisque id justo'), 67 | (4,9,'amet turpis elementum ligula vehicula'), 68 | (5,13,'in faucibus orci'), 69 | (7,5,'maecenas rhoncus aliquam'), 70 | (8,6,'lacinia nisi venenatis tristique'), 71 | (9,1,'leo pellentesque ultrices'), 72 | (10,20,'massa id lobortis'), 73 | (11,10,'ullamcorper purus sit amet nulla'), 74 | (12,3,'posuere cubilia curae'), 75 | (13,15,'nibh in lectus pellentesque'), 76 | (14,7,'in faucibus orci luctus'), 77 | (15,13,'vestibulum sagittis sapien cum'), 78 | (16,8,'montes nascetur ridiculus'), 79 | (17,20,'metus arcu adipiscing molestie'), 80 | (18,3,'tincidunt eget tempus'), 81 | (19,6,'mi in porttitor pede'), 82 | (20,20,'sapien sapien non mi'), 83 | (21,15,'duis at velit eu est'), 84 | (22,18,'interdum mauris non ligula'), 85 | (23,7,'cum sociis natoque'), 86 | (24,15,'tincidunt nulla mollis'), 87 | (25,17,'sed interdum venenatis turpis'), 88 | (26,17,'non lectus aliquam'), 89 | (27,5,'hac habitasse platea'), 90 | (28,11,'sit amet eleifend'), 91 | (29,8,'sed augue aliquam erat'), 92 | (30,5,'quis tortor id nulla'), 93 | (31,2,'eget eleifend luctus ultricies eu'), 94 | (32,4,'pretium quis lectus suspendisse potenti'), 95 | (33,2,'tempus sit amet sem'), 96 | (34,5,'amet nunc viverra'), 97 | (35,5,'cum sociis natoque penatibus'), 98 | (36,8,'magna bibendum imperdiet nullam orci'), 99 | (37,16,'duis mattis egestas metus aenean'), 100 | (38,3,'sapien arcu sed augue'), 101 | (39,1,'libero ut massa volutpat'), 102 | (40,8,'turpis eget elit sodales'), 103 | (41,19,'nibh ligula nec sem duis'), 104 | (42,11,'vestibulum rutrum rutrum neque'), 105 | (43,15,'lacus purus aliquet at'), 106 | (44,2,'luctus tincidunt nulla mollis molestie'), 107 | (45,5,'non mi integer ac'), 108 | (46,2,'cursus id turpis'), 109 | (47,6,'morbi vel lectus in'), 110 | (48,1,'urna ut tellus'), 111 | (49,2,'praesent lectus vestibulum'), 112 | (50,18,'nulla mollis molestie'), 113 | (51,20,'sed lacus morbi sem'), 114 | (52,17,'amet consectetuer adipiscing elit'), 115 | (53,11,'sit amet sapien dignissim'), 116 | (54,19,'ac enim in'), 117 | (55,16,'pretium nisl ut volutpat sapien'), 118 | (56,13,'amet lobortis sapien sapien'), 119 | (57,18,'id massa id nisl venenatis'), 120 | (58,1,'pede malesuada in imperdiet et'), 121 | (59,3,'fermentum donec ut'), 122 | (60,9,'a ipsum integer a'), 123 | (61,1,'justo maecenas rhoncus aliquam'), 124 | (62,6,'faucibus orci luctus'), 125 | (63,11,'dictumst aliquam augue'), 126 | (64,6,'eget vulputate ut ultrices'), 127 | (65,8,'odio donec vitae nisi nam'), 128 | (66,18,'blandit ultrices enim lorem'), 129 | (67,3,'justo eu massa donec'), 130 | (68,7,'orci luctus et'), 131 | (69,1,'magna bibendum imperdiet nullam'), 132 | (70,17,'vitae mattis nibh ligula'), 133 | (71,15,'ante ipsum primis'), 134 | (72,3,'ultrices libero non'), 135 | (73,3,'vitae ipsum aliquam non mauris'), 136 | (74,10,'tortor quis turpis sed ante'), 137 | (75,19,'mauris eget massa tempor convallis'), 138 | (76,12,'semper sapien a'), 139 | (77,9,'vestibulum proin eu mi nulla'), 140 | (78,10,'pretium iaculis justo in hac'), 141 | (79,9,'odio odio elementum eu'), 142 | (80,2,'eget semper rutrum'), 143 | (81,5,'faucibus orci luctus'), 144 | (82,2,'dui nec nisi volutpat'), 145 | (83,19,'mus vivamus vestibulum'), 146 | (84,18,'interdum in ante vestibulum ante'), 147 | (85,20,'dapibus at diam nam tristique'), 148 | (86,14,'rutrum rutrum neque'), 149 | (87,6,'aliquet maecenas leo odio'), 150 | (88,18,'penatibus et magnis dis parturient'), 151 | (89,20,'enim leo rhoncus'), 152 | (90,12,'id massa id nisl venenatis'), 153 | (91,5,'justo sit amet sapien'), 154 | (92,2,'sapien in sapien iaculis congue'), 155 | (93,7,'in congue etiam'), 156 | (94,16,'sapien quis libero nullam sit'), 157 | (95,19,'posuere metus vitae ipsum aliquam'), 158 | (96,13,'ac nulla sed vel'), 159 | (97,1,'primis in faucibus orci'), 160 | (98,13,'sit amet sem fusce consequat'), 161 | (99,17,'ultrices vel augue vestibulum ante'), 162 | (100,2,'neque aenean auctor'), 163 | (101,10,'habitasse platea dictumst'), 164 | (102,2,'at turpis a pede posuere'), 165 | (103,10,'curabitur at ipsum ac'), 166 | (104,15,'felis sed lacus morbi sem'), 167 | (105,12,'libero rutrum ac lobortis vel'), 168 | (106,19,'vulputate ut ultrices vel augue'), 169 | (107,1,'a odio in'), 170 | (108,9,'tellus in sagittis dui'), 171 | (109,13,'morbi vel lectus in'), 172 | (110,5,'non mauris morbi non lectus'), 173 | (111,9,'sapien quis libero'), 174 | (112,15,'tristique in tempus sit amet'), 175 | (113,14,'justo maecenas rhoncus aliquam lacus'), 176 | (114,7,'hac habitasse platea dictumst etiam'), 177 | (115,15,'nec nisi vulputate nonummy maecenas'), 178 | (116,19,'elit proin risus praesent'), 179 | (117,6,'convallis morbi odio odio'), 180 | (118,3,'morbi porttitor lorem id ligula'), 181 | (119,7,'sed interdum venenatis'), 182 | (120,13,'ridiculus mus vivamus vestibulum sagittis'), 183 | (121,8,'quis libero nullam'), 184 | (122,2,'luctus et ultrices'), 185 | (123,10,'luctus et ultrices'), 186 | (124,15,'consectetuer eget rutrum at'), 187 | (125,20,'tortor id nulla ultrices aliquet'), 188 | (126,8,'non mauris morbi'), 189 | (127,13,'nulla ac enim in'), 190 | (128,8,'volutpat quam pede lobortis ligula'), 191 | (129,8,'leo odio porttitor id consequat'), 192 | (130,15,'posuere cubilia curae duis'), 193 | (131,2,'consectetuer adipiscing elit'), 194 | (132,5,'amet cursus id'), 195 | (133,8,'nulla suspendisse potenti cras'), 196 | (134,15,'odio in hac habitasse platea'), 197 | (135,3,'curabitur at ipsum'), 198 | (136,19,'pede libero quis orci nullam'), 199 | (137,4,'platea dictumst morbi'), 200 | (138,16,'curabitur gravida nisi at'), 201 | (139,8,'praesent lectus vestibulum'), 202 | (140,18,'pharetra magna vestibulum aliquet'), 203 | (141,19,'quam suspendisse potenti nullam porttitor'), 204 | (142,18,'ultrices posuere cubilia curae'), 205 | (143,5,'amet consectetuer adipiscing elit'), 206 | (144,3,'dui proin leo'), 207 | (145,4,'erat volutpat in congue etiam'), 208 | (146,2,'morbi ut odio cras'), 209 | (147,5,'dictumst maecenas ut massa quis'), 210 | (148,10,'est risus auctor'), 211 | (149,2,'blandit non interdum'), 212 | (150,2,'nibh quisque id'), 213 | (151,18,'neque aenean auctor gravida sem'), 214 | (152,6,'nisi vulputate nonummy maecenas'), 215 | (153,5,'id nisl venenatis lacinia aenean'), 216 | (154,3,'faucibus orci luctus et ultrices'), 217 | (155,6,'sapien non mi integer'), 218 | (156,4,'hac habitasse platea dictumst'), 219 | (157,14,'nulla dapibus dolor vel'), 220 | (158,1,'ut odio cras'), 221 | (159,15,'vel sem sed sagittis nam'), 222 | (160,8,'curae mauris viverra diam'), 223 | (161,16,'id mauris vulputate elementum nullam'), 224 | (162,15,'lorem vitae mattis nibh'), 225 | (163,6,'elit proin interdum'), 226 | (164,8,'aenean lectus pellentesque eget nunc'), 227 | (165,9,'non pretium quis lectus suspendisse'), 228 | (166,10,'sapien in sapien iaculis'), 229 | (167,8,'vivamus metus arcu adipiscing'), 230 | (168,1,'id sapien in'), 231 | (169,12,'posuere nonummy integer'), 232 | (170,13,'ultrices vel augue vestibulum'), 233 | (171,6,'ante vivamus tortor duis mattis'), 234 | (172,4,'lacus purus aliquet at feugiat'), 235 | (173,8,'nullam varius nulla facilisi'), 236 | (174,13,'rutrum neque aenean'), 237 | (175,19,'varius nulla facilisi cras non'), 238 | (176,10,'vivamus vestibulum sagittis'), 239 | (177,17,'massa volutpat convallis morbi odio'), 240 | (178,15,'vestibulum ante ipsum primis'), 241 | (179,18,'accumsan odio curabitur convallis'), 242 | (180,1,'eros viverra eget congue'), 243 | (181,15,'metus arcu adipiscing molestie'), 244 | (182,13,'donec pharetra magna vestibulum aliquet'), 245 | (183,2,'nibh in quis justo maecenas'), 246 | (184,4,'ut tellus nulla ut'), 247 | (185,8,'bibendum felis sed interdum'), 248 | (186,13,'eu interdum eu'), 249 | (187,5,'dignissim vestibulum vestibulum'), 250 | (188,18,'donec vitae nisi nam'), 251 | (189,18,'elementum ligula vehicula consequat'), 252 | (190,9,'praesent id massa id nisl'), 253 | (191,1,'augue vel accumsan'), 254 | (192,20,'felis fusce posuere felis'), 255 | (193,3,'libero ut massa volutpat'), 256 | (194,10,'curae nulla dapibus dolor vel'), 257 | (195,2,'turpis elementum ligula vehicula consequat'), 258 | (196,4,'nisl aenean lectus pellentesque'), 259 | (197,13,'in consequat ut nulla'), 260 | (198,13,'ipsum dolor sit amet'), 261 | (199,16,'ultrices posuere cubilia curae'), 262 | (200,20,'cubilia curae nulla dapibus dolor'), 263 | (201,1,'venenatis turpis enim'), 264 | (202,3,'tristique fusce congue diam'), 265 | (203,1,'nam congue risus semper'), 266 | (204,17,'convallis morbi odio odio elementum'), 267 | (205,7,'eros vestibulum ac est lacinia'), 268 | (206,14,'nec sem duis aliquam'), 269 | (207,12,'amet erat nulla tempus vivamus'), 270 | (208,9,'suspendisse ornare consequat'), 271 | (209,3,'sit amet erat nulla tempus'), 272 | (210,2,'ut volutpat sapien arcu'), 273 | (211,3,'nulla nunc purus phasellus in'), 274 | (212,15,'nonummy integer non'), 275 | (213,17,'mauris viverra diam'), 276 | (214,15,'amet lobortis sapien sapien'), 277 | (215,18,'luctus tincidunt nulla'), 278 | (216,1,'potenti in eleifend quam'), 279 | (217,5,'a pede posuere nonummy'), 280 | (218,2,'diam id ornare imperdiet'), 281 | (219,5,'ante nulla justo aliquam'), 282 | (220,5,'mattis pulvinar nulla pede ullamcorper'), 283 | (221,13,'augue aliquam erat volutpat in'), 284 | (222,9,'pede ac diam cras pellentesque'), 285 | (223,18,'sit amet lobortis sapien'), 286 | (224,8,'donec dapibus duis at velit'), 287 | (225,6,'in congue etiam justo'), 288 | (226,14,'vestibulum quam sapien varius ut'), 289 | (227,1,'eget nunc donec quis'), 290 | (228,2,'ante vestibulum ante ipsum'), 291 | (229,13,'dictumst morbi vestibulum'), 292 | (230,5,'ac consequat metus sapien'), 293 | (231,10,'blandit mi in porttitor pede'), 294 | (232,11,'vestibulum eget vulputate ut'), 295 | (233,19,'sagittis nam congue risus semper'), 296 | (234,2,'lobortis convallis tortor'), 297 | (235,9,'lorem id ligula suspendisse ornare'), 298 | (236,13,'eget eros elementum pellentesque'), 299 | (237,19,'placerat praesent blandit nam'), 300 | (238,10,'suscipit nulla elit'), 301 | (239,9,'vestibulum ac est lacinia'), 302 | (240,1,'suscipit nulla elit'), 303 | (241,3,'sed tincidunt eu felis fusce'), 304 | (242,18,'faucibus accumsan odio'), 305 | (243,10,'lacinia eget tincidunt eget'), 306 | (244,19,'eu est congue elementum'), 307 | (245,13,'ante ipsum primis in'), 308 | (246,5,'eleifend luctus ultricies eu nibh'), 309 | (247,2,'platea dictumst maecenas'), 310 | (248,15,'rhoncus aliquet pulvinar sed nisl'), 311 | (249,6,'parturient montes nascetur ridiculus mus'), 312 | (250,19,'ut blandit non interdum in'), 313 | (251,3,'luctus et ultrices'), 314 | (252,2,'velit id pretium iaculis diam'), 315 | (253,9,'tincidunt nulla mollis'), 316 | (254,11,'nibh ligula nec sem duis'), 317 | (255,16,'quis lectus suspendisse'), 318 | (256,3,'tincidunt ante vel ipsum praesent'), 319 | (257,15,'in sagittis dui vel'), 320 | (258,14,'proin eu mi'), 321 | (259,19,'dolor morbi vel lectus'), 322 | (260,14,'eros viverra eget'), 323 | (261,14,'sapien cursus vestibulum'), 324 | (262,2,'fusce lacus purus'), 325 | (263,12,'tristique est et tempus'), 326 | (264,20,'turpis elementum ligula vehicula consequat'), 327 | (265,7,'et tempus semper'), 328 | (266,1,'platea dictumst etiam'), 329 | (267,13,'ultricies eu nibh'), 330 | (268,20,'condimentum curabitur in libero ut'), 331 | (269,17,'accumsan tellus nisi'), 332 | (270,1,'a ipsum integer'), 333 | (271,15,'platea dictumst morbi'), 334 | (272,8,'non interdum in ante vestibulum'), 335 | (273,5,'vel nisl duis ac'), 336 | (274,18,'proin interdum mauris'), 337 | (275,13,'lorem id ligula'), 338 | (276,14,'in magna bibendum imperdiet nullam'), 339 | (277,10,'consequat dui nec nisi volutpat'), 340 | (278,20,'orci eget orci vehicula'), 341 | (279,2,'sed sagittis nam congue risus'), 342 | (280,17,'at dolor quis odio consequat'), 343 | (281,19,'in tempus sit amet sem'), 344 | (282,4,'magna at nunc commodo placerat'), 345 | (283,2,'leo odio condimentum id'), 346 | (284,15,'fermentum justo nec condimentum neque'), 347 | (285,9,'sit amet consectetuer adipiscing elit'), 348 | (286,15,'ac consequat metus'), 349 | (287,19,'congue etiam justo etiam pretium'), 350 | (288,7,'maecenas tincidunt lacus at'), 351 | (289,7,'pulvinar nulla pede ullamcorper augue'), 352 | (290,2,'imperdiet et commodo'), 353 | (291,18,'in felis donec semper'), 354 | (292,15,'sapien quis libero'), 355 | (293,12,'id massa id'), 356 | (294,12,'in congue etiam justo'), 357 | (295,11,'in sagittis dui vel nisl'), 358 | (296,14,'vitae consectetuer eget rutrum'), 359 | (297,2,'orci luctus et ultrices posuere'), 360 | (298,13,'morbi sem mauris laoreet ut'), 361 | (299,14,'odio in hac habitasse platea'), 362 | (300,10,'orci mauris lacinia sapien quis'), 363 | (301,16,'risus praesent lectus vestibulum'), 364 | (302,19,'in hac habitasse platea'), 365 | (303,13,'blandit non interdum in'), 366 | (304,17,'dolor sit amet consectetuer'), 367 | (305,7,'lorem ipsum dolor sit amet'), 368 | (306,19,'quam sollicitudin vitae'), 369 | (307,15,'ac consequat metus sapien ut'), 370 | (308,13,'et ultrices posuere cubilia curae'), 371 | (309,20,'in lacus curabitur'), 372 | (310,6,'volutpat in congue etiam justo'), 373 | (311,8,'quis odio consequat varius integer'), 374 | (312,10,'nulla integer pede justo'), 375 | (313,16,'sem sed sagittis'), 376 | (314,7,'erat nulla tempus'), 377 | (315,8,'duis faucibus accumsan'), 378 | (316,6,'augue quam sollicitudin'), 379 | (317,11,'tristique est et tempus semper'), 380 | (318,17,'eget congue eget semper rutrum'), 381 | (319,14,'in quis justo maecenas rhoncus'), 382 | (320,5,'vel ipsum praesent blandit'), 383 | (321,2,'bibendum imperdiet nullam orci pede'), 384 | (322,2,'nunc commodo placerat'), 385 | (323,7,'vel lectus in quam fringilla'), 386 | (324,5,'in felis eu'), 387 | (325,8,'suspendisse ornare consequat lectus in'), 388 | (326,20,'posuere cubilia curae mauris'), 389 | (327,4,'ac consequat metus sapien'), 390 | (328,1,'ligula nec sem'), 391 | (329,3,'risus dapibus augue vel'), 392 | (330,14,'lacinia sapien quis libero nullam'), 393 | (331,1,'platea dictumst morbi vestibulum'), 394 | (332,7,'lobortis sapien sapien non'), 395 | (333,3,'sapien dignissim vestibulum vestibulum'), 396 | (334,16,'vestibulum sit amet cursus id'), 397 | (335,20,'vel nulla eget eros elementum'), 398 | (336,9,'at velit eu'), 399 | (337,6,'ac neque duis bibendum morbi'), 400 | (338,13,'nec condimentum neque sapien placerat'), 401 | (339,10,'justo in hac'), 402 | (340,16,'quisque arcu libero rutrum'), 403 | (341,4,'nec euismod scelerisque'), 404 | (342,5,'quam turpis adipiscing lorem vitae'), 405 | (343,2,'placerat ante nulla justo'), 406 | (344,17,'malesuada in imperdiet et'), 407 | (345,1,'cras pellentesque volutpat dui maecenas'), 408 | (346,10,'aliquet ultrices erat tortor'), 409 | (347,15,'vestibulum sed magna at'), 410 | (348,1,'pretium iaculis diam'), 411 | (349,13,'justo pellentesque viverra'), 412 | (350,15,'erat quisque erat eros'), 413 | (351,11,'facilisi cras non velit'), 414 | (352,20,'convallis nunc proin at turpis'), 415 | (353,16,'erat curabitur gravida nisi'), 416 | (354,20,'nisl duis ac nibh fusce'), 417 | (355,3,'volutpat dui maecenas'), 418 | (356,18,'placerat ante nulla'), 419 | (357,5,'odio in hac habitasse platea'), 420 | (358,16,'vitae mattis nibh ligula'), 421 | (359,20,'etiam pretium iaculis justo in'), 422 | (360,3,'amet erat nulla tempus vivamus'), 423 | (361,15,'odio elementum eu interdum'), 424 | (362,16,'id consequat in consequat'), 425 | (363,20,'hac habitasse platea dictumst'), 426 | (364,3,'mi pede malesuada in'), 427 | (365,12,'metus sapien ut nunc'), 428 | (366,1,'donec diam neque'), 429 | (367,6,'lectus in est risus'), 430 | (368,4,'rhoncus aliquet pulvinar sed'), 431 | (369,3,'fusce congue diam id'), 432 | (370,14,'platea dictumst aliquam augue'), 433 | (371,12,'vel nulla eget'), 434 | (372,3,'platea dictumst maecenas ut massa'), 435 | (373,10,'commodo placerat praesent blandit nam'), 436 | (374,19,'quis turpis sed ante'), 437 | (375,3,'nisl aenean lectus pellentesque eget'), 438 | (376,6,'justo sit amet sapien'), 439 | (377,6,'amet consectetuer adipiscing'), 440 | (378,18,'dictumst etiam faucibus cursus urna'), 441 | (379,11,'eu nibh quisque'), 442 | (380,5,'semper interdum mauris ullamcorper'), 443 | (381,7,'placerat praesent blandit'), 444 | (382,1,'malesuada in imperdiet et'), 445 | (383,17,'rutrum neque aenean'), 446 | (384,13,'semper sapien a'), 447 | (385,2,'non mauris morbi non lectus'), 448 | (386,15,'nulla ac enim in tempor'), 449 | (387,11,'odio odio elementum'), 450 | (388,16,'in hac habitasse platea dictumst'), 451 | (389,17,'ultrices phasellus id sapien'), 452 | (390,3,'ultrices posuere cubilia curae'), 453 | (391,4,'eu interdum eu tincidunt in'), 454 | (392,14,'at vulputate vitae'), 455 | (393,20,'pede libero quis orci nullam'), 456 | (394,3,'pede malesuada in imperdiet et'), 457 | (395,5,'sapien iaculis congue vivamus metus'), 458 | (396,18,'in felis eu sapien'), 459 | (397,18,'curae nulla dapibus dolor'), 460 | (398,9,'primis in faucibus orci'), 461 | (399,18,'vel augue vestibulum ante ipsum'), 462 | (400,18,'purus phasellus in'), 463 | (401,13,'sapien cum sociis natoque penatibus'), 464 | (402,19,'dolor morbi vel'), 465 | (403,8,'volutpat eleifend donec ut'), 466 | (404,6,'at turpis a pede'), 467 | (405,19,'maecenas leo odio'), 468 | (406,7,'sapien quis libero'), 469 | (407,18,'lobortis convallis tortor'), 470 | (408,4,'tempus sit amet sem'), 471 | (409,5,'habitasse platea dictumst maecenas ut'), 472 | (410,6,'morbi odio odio'), 473 | (411,10,'nam ultrices libero non'), 474 | (412,4,'orci mauris lacinia sapien quis'), 475 | (413,2,'duis faucibus accumsan odio curabitur'), 476 | (414,2,'platea dictumst maecenas'), 477 | (415,8,'tincidunt ante vel ipsum praesent'), 478 | (416,6,'id luctus nec molestie sed'), 479 | (417,19,'mauris vulputate elementum nullam varius'), 480 | (418,6,'platea dictumst maecenas'), 481 | (419,8,'mi sit amet'), 482 | (420,11,'in ante vestibulum ante'), 483 | (421,5,'ridiculus mus vivamus'), 484 | (422,4,'libero ut massa'), 485 | (423,11,'scelerisque quam turpis adipiscing lorem'), 486 | (424,19,'in congue etiam'), 487 | (425,20,'ante vestibulum ante ipsum'), 488 | (426,14,'at turpis a pede posuere'), 489 | (427,6,'quam turpis adipiscing lorem'), 490 | (428,4,'aenean fermentum donec'), 491 | (429,18,'ut erat id mauris vulputate'), 492 | (430,19,'in hac habitasse'), 493 | (431,1,'at nulla suspendisse'), 494 | (432,11,'non interdum in'), 495 | (433,13,'viverra pede ac diam cras'), 496 | (434,9,'rutrum rutrum neque aenean auctor'), 497 | (435,4,'vitae quam suspendisse potenti'), 498 | (436,11,'pede posuere nonummy integer'), 499 | (437,13,'suspendisse potenti in eleifend'), 500 | (438,12,'quam sapien varius ut'), 501 | (439,17,'duis consequat dui'), 502 | (440,6,'vestibulum eget vulputate'), 503 | (441,2,'semper rutrum nulla'), 504 | (442,20,'morbi a ipsum integer'), 505 | (443,8,'turpis donec posuere'), 506 | (444,6,'aliquam non mauris morbi non'), 507 | (445,5,'sit amet consectetuer'), 508 | (446,8,'metus sapien ut'), 509 | (447,1,'odio elementum eu'), 510 | (448,1,'dolor morbi vel lectus in'), 511 | (449,20,'mi in porttitor'), 512 | (450,19,'sociis natoque penatibus et'), 513 | (451,16,'nulla nisl nunc nisl duis'), 514 | (452,19,'diam in magna'), 515 | (453,3,'nascetur ridiculus mus'), 516 | (454,3,'elementum eu interdum'), 517 | (455,4,'quis justo maecenas rhoncus aliquam'), 518 | (456,13,'ut massa quis'), 519 | (457,18,'diam vitae quam'), 520 | (458,6,'vel est donec'), 521 | (459,6,'in hac habitasse platea'), 522 | (460,7,'id mauris vulputate elementum'), 523 | (461,19,'duis at velit eu'), 524 | (462,17,'magna at nunc commodo placerat'), 525 | (463,16,'dignissim vestibulum vestibulum ante'), 526 | (464,4,'sapien quis libero nullam'), 527 | (465,16,'bibendum imperdiet nullam orci'), 528 | (466,1,'dapibus nulla suscipit ligula'), 529 | (467,10,'ut mauris eget massa'), 530 | (468,4,'elementum ligula vehicula consequat morbi'), 531 | (469,18,'proin at turpis'), 532 | (470,1,'consequat nulla nisl'), 533 | (471,11,'aliquam lacus morbi quis tortor'), 534 | (472,20,'vel est donec odio'), 535 | (473,6,'egestas metus aenean'), 536 | (474,13,'magna vestibulum aliquet'), 537 | (475,17,'vitae mattis nibh'), 538 | (476,4,'eu mi nulla ac'), 539 | (477,19,'interdum in ante vestibulum'), 540 | (478,14,'aenean lectus pellentesque'), 541 | (479,10,'amet sapien dignissim'), 542 | (480,20,'quis orci eget orci'), 543 | (481,20,'mauris vulputate elementum nullam varius'), 544 | (482,13,'pede posuere nonummy integer non'), 545 | (483,19,'donec semper sapien a'), 546 | (484,8,'commodo vulputate justo in blandit'), 547 | (485,12,'erat vestibulum sed'), 548 | (486,5,'congue eget semper rutrum'), 549 | (487,9,'dui maecenas tristique est'), 550 | (488,7,'eget semper rutrum nulla'), 551 | (489,9,'lectus vestibulum quam sapien varius'), 552 | (490,9,'nisl aenean lectus pellentesque eget'), 553 | (491,12,'proin leo odio'), 554 | (492,17,'pede ullamcorper augue a suscipit'), 555 | (493,19,'dolor sit amet consectetuer adipiscing'), 556 | (494,8,'mauris sit amet eros'), 557 | (495,7,'placerat praesent blandit nam'), 558 | (496,15,'maecenas rhoncus aliquam lacus'), 559 | (497,2,'at diam nam tristique tortor'), 560 | (498,16,'ut tellus nulla'), 561 | (499,6,'montes nascetur ridiculus mus vivamus'), 562 | (500,4,'in tempus sit'), 563 | (501,3,'ut massa volutpat convallis morbi'), 564 | (502,15,'in congue etiam justo'), 565 | (503,5,'blandit nam nulla'), 566 | (504,3,'et tempus semper est quam'), 567 | (505,12,'ligula suspendisse ornare consequat'), 568 | (506,14,'platea dictumst etiam'), 569 | (507,20,'vitae quam suspendisse potenti'), 570 | (508,20,'lacinia erat vestibulum sed'), 571 | (509,18,'ut at dolor'), 572 | (510,10,'sed tincidunt eu felis'), 573 | (511,16,'rhoncus aliquet pulvinar sed'), 574 | (512,1,'nulla quisque arcu libero rutrum'), 575 | (513,20,'odio donec vitae nisi'), 576 | (514,11,'convallis nunc proin at turpis'), 577 | (515,9,'nulla integer pede justo lacinia'), 578 | (516,16,'quis lectus suspendisse potenti'), 579 | (517,2,'proin interdum mauris non ligula'), 580 | (518,8,'eget vulputate ut ultrices'), 581 | (519,11,'quam sollicitudin vitae consectetuer'), 582 | (520,4,'sodales sed tincidunt'), 583 | (521,6,'eget elit sodales scelerisque'), 584 | (522,2,'in lectus pellentesque at'), 585 | (523,8,'malesuada in imperdiet'), 586 | (524,16,'aliquam quis turpis'), 587 | (525,4,'orci luctus et ultrices posuere'), 588 | (526,9,'dapibus dolor vel est donec'), 589 | (527,4,'aenean lectus pellentesque eget'), 590 | (528,1,'lorem integer tincidunt'), 591 | (529,10,'in lacus curabitur at'), 592 | (530,12,'nisi at nibh in hac'), 593 | (531,10,'ut blandit non'), 594 | (532,15,'parturient montes nascetur ridiculus'), 595 | (533,7,'congue eget semper'), 596 | (534,12,'enim blandit mi in porttitor'), 597 | (535,15,'ac nibh fusce'), 598 | (536,3,'adipiscing molestie hendrerit at'), 599 | (537,1,'ut mauris eget massa tempor'), 600 | (538,6,'ac tellus semper interdum mauris'), 601 | (539,12,'a suscipit nulla'), 602 | (540,13,'praesent id massa id nisl'), 603 | (541,19,'libero quis orci nullam molestie'), 604 | (542,6,'in felis eu sapien'), 605 | (543,10,'ullamcorper purus sit amet nulla'), 606 | (544,13,'orci pede venenatis non'), 607 | (545,1,'aenean auctor gravida sem praesent'), 608 | (546,14,'pede morbi porttitor'), 609 | (547,18,'tincidunt eget tempus vel pede'), 610 | (548,20,'erat curabitur gravida'), 611 | (549,3,'a suscipit nulla elit ac'), 612 | (550,1,'mauris eget massa tempor convallis'), 613 | (551,14,'nulla justo aliquam quis'), 614 | (552,8,'ipsum aliquam non mauris morbi'), 615 | (553,2,'donec ut dolor morbi vel'), 616 | (554,10,'proin risus praesent'), 617 | (555,16,'ipsum ac tellus semper'), 618 | (556,5,'amet diam in magna'), 619 | (557,6,'in felis donec'), 620 | (558,6,'arcu libero rutrum ac'), 621 | (559,6,'volutpat sapien arcu'), 622 | (560,7,'nulla nunc purus'), 623 | (561,3,'non lectus aliquam sit amet'), 624 | (562,8,'ut massa quis'), 625 | (563,14,'consectetuer adipiscing elit'), 626 | (564,11,'vivamus in felis'), 627 | (565,7,'rutrum nulla nunc purus'), 628 | (566,20,'duis at velit eu est'), 629 | (567,3,'sollicitudin mi sit amet'), 630 | (568,4,'consequat metus sapien ut nunc'), 631 | (569,8,'magna vulputate luctus cum sociis'), 632 | (570,3,'ullamcorper purus sit amet nulla'), 633 | (571,18,'auctor sed tristique in'), 634 | (572,16,'pede justo lacinia eget'), 635 | (573,7,'platea dictumst aliquam augue quam'), 636 | (574,1,'volutpat in congue etiam justo'), 637 | (575,1,'tortor sollicitudin mi'), 638 | (576,9,'erat quisque erat eros viverra'), 639 | (577,6,'at velit eu est'), 640 | (578,6,'nisl duis bibendum felis sed'), 641 | (579,11,'commodo vulputate justo in blandit'), 642 | (580,14,'tristique in tempus sit amet'), 643 | (581,16,'est lacinia nisi'), 644 | (582,12,'amet consectetuer adipiscing elit proin'), 645 | (583,13,'purus sit amet nulla quisque'), 646 | (584,9,'dui luctus rutrum nulla'), 647 | (585,14,'mi pede malesuada'), 648 | (586,15,'eu sapien cursus'), 649 | (587,2,'penatibus et magnis dis parturient'), 650 | (588,3,'id nulla ultrices aliquet'), 651 | (589,3,'sagittis nam congue risus semper'), 652 | (590,2,'condimentum neque sapien placerat'), 653 | (591,4,'sed tincidunt eu felis fusce'), 654 | (592,2,'mi integer ac neque'), 655 | (593,15,'massa donec dapibus'), 656 | (594,10,'condimentum id luctus nec molestie'), 657 | (595,19,'nam dui proin leo'), 658 | (596,10,'interdum mauris non ligula'), 659 | (597,18,'proin interdum mauris non ligula'), 660 | (598,11,'molestie lorem quisque ut'), 661 | (599,6,'sit amet nulla'), 662 | (600,3,'libero quis orci nullam'), 663 | (601,7,'lobortis est phasellus'), 664 | (602,16,'nulla justo aliquam quis turpis'), 665 | (603,10,'ante ipsum primis in'), 666 | (604,9,'orci mauris lacinia'), 667 | (605,9,'faucibus orci luctus et ultrices'), 668 | (606,13,'ligula nec sem'), 669 | (607,20,'quam suspendisse potenti nullam'), 670 | (608,17,'nunc vestibulum ante ipsum primis'), 671 | (609,11,'lectus pellentesque eget nunc'), 672 | (610,6,'in tempor turpis nec euismod'), 673 | (611,18,'venenatis tristique fusce congue'), 674 | (612,5,'curae nulla dapibus dolor'), 675 | (613,4,'vel est donec odio'), 676 | (614,10,'ullamcorper purus sit amet'), 677 | (615,5,'id mauris vulputate elementum nullam'), 678 | (616,1,'potenti in eleifend'), 679 | (617,18,'tempus sit amet sem fusce'), 680 | (618,8,'quam turpis adipiscing lorem'), 681 | (619,17,'curae duis faucibus accumsan odio'), 682 | (620,6,'nullam orci pede venenatis non'), 683 | (621,11,'amet turpis elementum'), 684 | (622,19,'porttitor lacus at turpis'), 685 | (623,3,'lacinia aenean sit amet'), 686 | (624,7,'proin risus praesent lectus'), 687 | (625,11,'euismod scelerisque quam'), 688 | (626,10,'viverra pede ac'), 689 | (627,9,'pellentesque eget nunc'), 690 | (628,14,'vel lectus in quam fringilla'), 691 | (629,13,'ut tellus nulla'), 692 | (630,6,'suspendisse potenti cras'), 693 | (631,4,'elementum ligula vehicula consequat morbi'), 694 | (632,4,'dui nec nisi volutpat'), 695 | (633,19,'amet eros suspendisse accumsan tortor'), 696 | (634,17,'in libero ut massa volutpat'), 697 | (635,9,'pellentesque viverra pede ac diam'), 698 | (636,15,'aenean lectus pellentesque'), 699 | (637,8,'venenatis tristique fusce'), 700 | (638,14,'eros vestibulum ac est'), 701 | (639,20,'volutpat convallis morbi odio odio'), 702 | (640,20,'turpis adipiscing lorem vitae mattis'), 703 | (641,11,'donec semper sapien a'), 704 | (642,12,'augue aliquam erat'), 705 | (643,2,'imperdiet et commodo'), 706 | (644,6,'erat tortor sollicitudin mi'), 707 | (645,1,'augue luctus tincidunt nulla mollis'), 708 | (646,9,'nec sem duis aliquam'), 709 | (647,13,'non velit nec nisi'), 710 | (648,17,'cubilia curae donec'), 711 | (649,8,'magnis dis parturient montes'), 712 | (650,5,'risus auctor sed tristique'), 713 | (651,11,'eu orci mauris lacinia'), 714 | (652,19,'ac est lacinia nisi venenatis'), 715 | (653,2,'dapibus at diam nam tristique'), 716 | (654,9,'erat eros viverra eget'), 717 | (655,14,'sociis natoque penatibus et magnis'), 718 | (656,9,'quisque erat eros viverra eget'), 719 | (657,10,'vivamus vestibulum sagittis'), 720 | (658,10,'eu tincidunt in'), 721 | (659,17,'magnis dis parturient montes nascetur'), 722 | (660,10,'vestibulum sed magna at'), 723 | (661,3,'porttitor lorem id ligula suspendisse'), 724 | (662,3,'erat curabitur gravida'), 725 | (663,16,'congue risus semper porta'), 726 | (664,15,'eu sapien cursus'), 727 | (665,1,'proin leo odio porttitor id'), 728 | (666,10,'nisi eu orci mauris'), 729 | (667,13,'habitasse platea dictumst morbi'), 730 | (668,6,'condimentum id luctus'), 731 | (669,2,'velit nec nisi vulputate nonummy'), 732 | (670,20,'metus vitae ipsum aliquam non'), 733 | (671,11,'justo in hac habitasse'), 734 | (672,19,'nam tristique tortor'), 735 | (673,13,'volutpat sapien arcu sed'), 736 | (674,13,'condimentum id luctus nec molestie'), 737 | (675,13,'rutrum rutrum neque aenean'), 738 | (676,5,'tincidunt nulla mollis molestie'), 739 | (677,11,'libero rutrum ac'), 740 | (678,6,'sit amet eros'), 741 | (679,9,'morbi sem mauris'), 742 | (680,11,'libero nam dui proin'), 743 | (681,19,'eu magna vulputate luctus cum'), 744 | (682,1,'platea dictumst aliquam augue'), 745 | (683,13,'blandit nam nulla'), 746 | (684,7,'justo in hac habitasse'), 747 | (685,8,'vivamus in felis eu sapien'), 748 | (686,6,'non sodales sed tincidunt'), 749 | (687,18,'maecenas leo odio condimentum'), 750 | (688,16,'duis bibendum morbi non quam'), 751 | (689,17,'et ultrices posuere'), 752 | (690,13,'ipsum dolor sit'), 753 | (691,1,'lectus suspendisse potenti in'), 754 | (692,18,'felis donec semper sapien'), 755 | (693,10,'turpis eget elit sodales scelerisque'), 756 | (694,19,'ligula pellentesque ultrices'), 757 | (695,14,'tincidunt lacus at velit'), 758 | (696,13,'posuere cubilia curae nulla'), 759 | (697,11,'quam pharetra magna ac'), 760 | (698,12,'cubilia curae duis'), 761 | (699,6,'vel nulla eget'), 762 | (700,17,'integer tincidunt ante vel'), 763 | (701,4,'potenti in eleifend quam a'), 764 | (702,11,'sit amet erat'), 765 | (703,10,'nulla suspendisse potenti cras'), 766 | (704,2,'faucibus orci luctus'), 767 | (705,10,'viverra pede ac'), 768 | (706,3,'vehicula condimentum curabitur in'), 769 | (707,15,'eget semper rutrum nulla nunc'), 770 | (708,16,'sit amet consectetuer adipiscing'), 771 | (709,2,'nulla facilisi cras'), 772 | (710,9,'sagittis nam congue risus semper'), 773 | (711,4,'lectus in quam fringilla rhoncus'), 774 | (712,7,'velit id pretium iaculis'), 775 | (713,1,'platea dictumst maecenas ut massa'), 776 | (714,9,'lacus purus aliquet at'), 777 | (715,10,'ac consequat metus'), 778 | (716,15,'at nulla suspendisse potenti'), 779 | (717,13,'id massa id'), 780 | (718,12,'condimentum neque sapien'), 781 | (719,17,'turpis enim blandit mi in'), 782 | (720,15,'fermentum donec ut'), 783 | (721,16,'est quam pharetra magna'), 784 | (722,11,'lacinia aenean sit'), 785 | (723,14,'tellus semper interdum'), 786 | (724,9,'suspendisse potenti in eleifend'), 787 | (725,11,'interdum mauris ullamcorper'), 788 | (726,18,'consequat metus sapien ut nunc'), 789 | (727,7,'posuere cubilia curae duis faucibus'), 790 | (728,7,'ligula sit amet'), 791 | (729,19,'odio porttitor id consequat in'), 792 | (730,7,'duis at velit'), 793 | (731,12,'nunc proin at turpis'), 794 | (732,15,'amet cursus id'), 795 | (733,1,'lacinia aenean sit amet'), 796 | (734,14,'tristique est et'), 797 | (735,6,'nullam porttitor lacus at turpis'), 798 | (736,5,'leo pellentesque ultrices mattis'), 799 | (737,2,'cras in purus eu'), 800 | (738,11,'sed lacus morbi sem mauris'), 801 | (739,11,'dignissim vestibulum vestibulum'), 802 | (740,6,'convallis nunc proin at'), 803 | (741,11,'sit amet lobortis'), 804 | (742,16,'elementum nullam varius nulla'), 805 | (743,6,'accumsan tortor quis turpis sed'), 806 | (744,20,'donec odio justo'), 807 | (745,3,'convallis nunc proin at turpis'), 808 | (746,3,'magna at nunc commodo'), 809 | (747,5,'rutrum at lorem integer'), 810 | (748,3,'dapibus nulla suscipit ligula in'), 811 | (749,7,'pellentesque ultrices mattis odio donec'), 812 | (750,19,'nullam molestie nibh in'), 813 | (751,6,'massa tempor convallis nulla neque'), 814 | (752,5,'sapien ut nunc vestibulum ante'), 815 | (753,7,'cras in purus'), 816 | (754,6,'porttitor id consequat in'), 817 | (755,20,'ut tellus nulla ut'), 818 | (756,20,'morbi non quam nec dui'), 819 | (757,14,'nullam sit amet'), 820 | (758,6,'at lorem integer'), 821 | (759,2,'sit amet cursus id turpis'), 822 | (760,2,'a ipsum integer'), 823 | (761,10,'augue vestibulum rutrum rutrum neque'), 824 | (762,17,'quam pharetra magna ac consequat'), 825 | (763,12,'pellentesque at nulla suspendisse'), 826 | (764,5,'orci luctus et'), 827 | (765,17,'dui vel nisl'), 828 | (766,14,'odio in hac'), 829 | (767,6,'mauris ullamcorper purus sit amet'), 830 | (768,20,'eu nibh quisque id'), 831 | (769,11,'sollicitudin mi sit amet lobortis'), 832 | (770,18,'praesent blandit lacinia erat vestibulum'), 833 | (771,16,'sit amet justo morbi ut'), 834 | (772,6,'luctus ultricies eu nibh'), 835 | (773,4,'magna vestibulum aliquet ultrices erat'), 836 | (774,16,'eu mi nulla'), 837 | (775,18,'auctor gravida sem'), 838 | (776,19,'praesent blandit lacinia'), 839 | (777,3,'quam pede lobortis ligula'), 840 | (778,3,'elit ac nulla sed vel'), 841 | (779,3,'neque vestibulum eget vulputate'), 842 | (780,8,'ante vel ipsum praesent'), 843 | (781,20,'quis turpis sed ante vivamus'), 844 | (782,11,'ac diam cras pellentesque'), 845 | (783,4,'augue vel accumsan tellus'), 846 | (784,16,'morbi vestibulum velit'), 847 | (785,9,'adipiscing elit proin risus'), 848 | (786,5,'hac habitasse platea dictumst etiam'), 849 | (787,14,'molestie lorem quisque'), 850 | (788,2,'est donec odio justo sollicitudin'), 851 | (789,17,'diam neque vestibulum'), 852 | (790,2,'eros elementum pellentesque'), 853 | (791,13,'vivamus vestibulum sagittis sapien'), 854 | (792,20,'dui luctus rutrum nulla'), 855 | (793,14,'feugiat et eros'), 856 | (794,4,'primis in faucibus orci luctus'), 857 | (795,3,'ante ipsum primis'), 858 | (796,2,'vel lectus in quam'), 859 | (797,17,'quam pede lobortis ligula'), 860 | (798,7,'felis ut at dolor quis'), 861 | (799,20,'praesent blandit nam'), 862 | (800,14,'at dolor quis'), 863 | (801,13,'ac neque duis'), 864 | (802,19,'ipsum integer a'), 865 | (803,2,'vel augue vestibulum'), 866 | (804,15,'id luctus nec'), 867 | (805,5,'platea dictumst morbi vestibulum velit'), 868 | (806,16,'amet eros suspendisse'), 869 | (807,17,'non velit donec diam'), 870 | (808,3,'amet turpis elementum ligula'), 871 | (809,7,'cras pellentesque volutpat dui maecenas'), 872 | (810,14,'mauris viverra diam vitae quam'), 873 | (811,15,'pulvinar sed nisl'), 874 | (812,14,'vel ipsum praesent'), 875 | (813,3,'luctus et ultrices'), 876 | (814,15,'cum sociis natoque penatibus'), 877 | (815,20,'aenean lectus pellentesque'), 878 | (816,11,'consequat dui nec'), 879 | (817,1,'aliquam convallis nunc proin'), 880 | (818,9,'massa donec dapibus duis'), 881 | (819,16,'sollicitudin vitae consectetuer eget'), 882 | (820,2,'libero nam dui proin'), 883 | (821,15,'et ultrices posuere'), 884 | (822,12,'amet consectetuer adipiscing elit proin'), 885 | (823,8,'justo maecenas rhoncus'), 886 | (824,20,'nisl ut volutpat sapien'), 887 | (825,8,'morbi vel lectus in'), 888 | (826,2,'dapibus nulla suscipit ligula'), 889 | (827,3,'quis tortor id nulla ultrices'), 890 | (828,8,'libero ut massa volutpat convallis'), 891 | (829,6,'ultrices posuere cubilia'), 892 | (830,4,'augue a suscipit'), 893 | (831,14,'sagittis nam congue risus semper'), 894 | (832,7,'habitasse platea dictumst'), 895 | (833,3,'vestibulum proin eu mi'), 896 | (834,9,'duis faucibus accumsan odio curabitur'), 897 | (835,17,'cubilia curae donec pharetra magna'), 898 | (836,3,'lectus in est'), 899 | (837,11,'velit nec nisi vulputate nonummy'), 900 | (838,9,'quam pede lobortis'), 901 | (839,1,'luctus et ultrices posuere'), 902 | (840,19,'leo odio porttitor id'), 903 | (841,14,'amet consectetuer adipiscing'), 904 | (842,18,'convallis duis consequat dui nec'), 905 | (843,17,'nulla tempus vivamus in'), 906 | (844,1,'bibendum felis sed interdum venenatis'), 907 | (845,18,'eleifend luctus ultricies'), 908 | (846,11,'erat eros viverra eget'), 909 | (847,1,'odio condimentum id luctus nec'), 910 | (848,19,'enim leo rhoncus'), 911 | (849,8,'in quis justo'), 912 | (850,12,'quisque ut erat curabitur gravida'), 913 | (851,2,'nulla nisl nunc nisl duis'), 914 | (852,5,'posuere cubilia curae'), 915 | (853,17,'non velit donec diam'), 916 | (854,2,'donec posuere metus vitae'), 917 | (855,11,'nulla pede ullamcorper'), 918 | (856,8,'orci luctus et ultrices'), 919 | (857,8,'sed vel enim sit'), 920 | (858,15,'molestie lorem quisque'), 921 | (859,13,'posuere cubilia curae'), 922 | (860,8,'pede lobortis ligula'), 923 | (861,20,'pulvinar lobortis est phasellus'), 924 | (862,2,'ac diam cras'), 925 | (863,16,'ut massa quis augue'), 926 | (864,10,'pede justo eu massa'), 927 | (865,1,'nulla ac enim in'), 928 | (866,7,'sit amet diam'), 929 | (867,6,'mattis odio donec'), 930 | (868,11,'in hac habitasse platea'), 931 | (869,15,'duis bibendum felis'), 932 | (870,13,'justo pellentesque viverra'), 933 | (871,15,'vestibulum rutrum rutrum neque aenean'), 934 | (872,11,'turpis donec posuere metus vitae'), 935 | (873,12,'ante ipsum primis'), 936 | (874,5,'neque vestibulum eget vulputate ut'), 937 | (875,2,'quam a odio in'), 938 | (876,13,'aliquam non mauris'), 939 | (877,5,'in ante vestibulum ante'), 940 | (878,14,'non velit donec diam'), 941 | (879,5,'ac neque duis'), 942 | (880,10,'cubilia curae donec pharetra magna'), 943 | (881,19,'curae duis faucibus accumsan odio'), 944 | (882,18,'amet erat nulla tempus'), 945 | (883,13,'quam a odio'), 946 | (884,5,'elementum pellentesque quisque porta volutpat'), 947 | (885,20,'duis at velit'), 948 | (886,2,'purus sit amet'), 949 | (887,4,'suspendisse ornare consequat'), 950 | (888,1,'vel est donec odio justo'), 951 | (889,12,'ut erat curabitur gravida'), 952 | (890,15,'est quam pharetra magna ac'), 953 | (891,16,'nisl aenean lectus pellentesque eget'), 954 | (892,17,'convallis nunc proin at'), 955 | (893,10,'erat tortor sollicitudin'), 956 | (894,2,'donec dapibus duis at'), 957 | (895,14,'vulputate luctus cum sociis'), 958 | (896,8,'mi integer ac neque duis'), 959 | (897,14,'nec sem duis'), 960 | (898,2,'lacus at turpis donec'), 961 | (899,18,'blandit non interdum in ante'), 962 | (900,4,'in hac habitasse platea dictumst'), 963 | (901,19,'ultrices libero non mattis pulvinar'), 964 | (902,19,'mi integer ac neque duis'), 965 | (903,19,'lectus pellentesque eget'), 966 | (904,8,'sit amet eleifend pede'), 967 | (905,13,'cras pellentesque volutpat dui'), 968 | (906,5,'lorem id ligula'), 969 | (907,15,'dolor sit amet'), 970 | (908,6,'magna at nunc commodo placerat'), 971 | (909,20,'pharetra magna ac consequat'), 972 | (910,12,'non lectus aliquam'), 973 | (911,8,'augue vel accumsan tellus nisi'), 974 | (912,6,'lobortis convallis tortor risus dapibus'), 975 | (913,19,'vel nulla eget'), 976 | (914,13,'cubilia curae mauris viverra diam'), 977 | (915,17,'quis turpis sed ante'), 978 | (916,5,'turpis a pede'), 979 | (917,5,'proin eu mi nulla'), 980 | (918,4,'posuere nonummy integer non velit'), 981 | (919,7,'integer ac leo pellentesque ultrices'), 982 | (920,7,'est et tempus'), 983 | (921,7,'vel accumsan tellus nisi'), 984 | (922,20,'a odio in hac habitasse'), 985 | (923,9,'id luctus nec molestie sed'), 986 | (924,11,'nulla suscipit ligula'), 987 | (925,6,'est phasellus sit amet erat'), 988 | (926,18,'bibendum imperdiet nullam'), 989 | (927,7,'ultricies eu nibh quisque'), 990 | (928,10,'consequat in consequat ut nulla'), 991 | (929,12,'nisl venenatis lacinia aenean'), 992 | (930,8,'tincidunt nulla mollis'), 993 | (931,11,'amet turpis elementum ligula vehicula'), 994 | (932,17,'erat fermentum justo nec condimentum'), 995 | (933,2,'vel nulla eget eros elementum'), 996 | (934,6,'purus aliquet at feugiat non'), 997 | (935,3,'nunc vestibulum ante'), 998 | (936,12,'erat tortor sollicitudin mi'), 999 | (937,16,'odio condimentum id luctus nec'), 1000 | (938,19,'penatibus et magnis dis parturient'), 1001 | (939,15,'donec odio justo sollicitudin ut'), 1002 | (940,2,'libero rutrum ac'), 1003 | (941,9,'varius ut blandit non'), 1004 | (942,11,'sem mauris laoreet ut'), 1005 | (943,20,'quam a odio in'), 1006 | (944,3,'lacinia erat vestibulum'), 1007 | (945,15,'convallis eget eleifend luctus'), 1008 | (946,10,'ut suscipit a feugiat'), 1009 | (947,1,'commodo placerat praesent'), 1010 | (948,6,'quis orci nullam molestie nibh'), 1011 | (949,5,'maecenas rhoncus aliquam'), 1012 | (950,17,'massa donec dapibus'), 1013 | (951,17,'in hac habitasse'), 1014 | (952,11,'ac tellus semper interdum mauris'), 1015 | (953,12,'aenean lectus pellentesque'), 1016 | (954,17,'ac lobortis vel'), 1017 | (955,2,'praesent blandit nam nulla integer'), 1018 | (956,16,'in faucibus orci'), 1019 | (957,20,'tincidunt in leo maecenas'), 1020 | (958,2,'integer a nibh in quis'), 1021 | (959,5,'vivamus metus arcu'), 1022 | (960,7,'morbi quis tortor id'), 1023 | (961,20,'donec quis orci eget'), 1024 | (962,18,'varius ut blandit'), 1025 | (963,7,'platea dictumst aliquam augue quam'), 1026 | (964,16,'et ultrices posuere cubilia'), 1027 | (965,5,'lectus in quam'), 1028 | (966,20,'sapien cursus vestibulum'), 1029 | (967,11,'nullam orci pede venenatis'), 1030 | (968,19,'curabitur gravida nisi at'), 1031 | (969,4,'in tempus sit amet sem'), 1032 | (970,18,'maecenas tristique est et'), 1033 | (971,6,'mauris lacinia sapien quis'), 1034 | (972,20,'nunc rhoncus dui vel'), 1035 | (973,12,'nisi eu orci mauris lacinia'), 1036 | (974,3,'felis fusce posuere felis sed'), 1037 | (975,17,'amet consectetuer adipiscing'), 1038 | (976,18,'quis justo maecenas rhoncus'), 1039 | (977,5,'nulla elit ac'), 1040 | (978,9,'nam nulla integer pede justo'), 1041 | (979,19,'nulla mollis molestie lorem'), 1042 | (980,18,'aliquet ultrices erat tortor sollicitudin'), 1043 | (981,8,'sem praesent id'), 1044 | (982,3,'ipsum dolor sit'), 1045 | (983,14,'natoque penatibus et magnis'), 1046 | (984,1,'massa id nisl'), 1047 | (985,3,'convallis eget eleifend'), 1048 | (986,13,'nisl nunc nisl duis'), 1049 | (987,14,'sapien non mi'), 1050 | (988,11,'proin interdum mauris non ligula'), 1051 | (989,15,'quam nec dui luctus'), 1052 | (990,11,'nibh fusce lacus purus'), 1053 | (991,12,'eu massa donec'), 1054 | (992,16,'luctus rutrum nulla'), 1055 | (993,19,'dictumst morbi vestibulum velit'), 1056 | (994,17,'vel nulla eget eros'), 1057 | (995,18,'nisl aenean lectus pellentesque'), 1058 | (996,13,'amet sapien dignissim vestibulum'), 1059 | (997,15,'non quam nec dui'), 1060 | (998,4,'integer aliquet massa'), 1061 | (999,15,'ut suscipit a feugiat'), 1062 | (1000,1,'arcu libero rutrum ac'), 1063 | (1001,1,'integer!'), 1064 | (1002,1,'Integer 2'), 1065 | (1003,1,'ddd'), 1066 | (1004,1,'ddddddd'); 1067 | 1068 | /*!40000 ALTER TABLE `products` ENABLE KEYS */; 1069 | UNLOCK TABLES; 1070 | 1071 | 1072 | # Dump of table users 1073 | # ------------------------------------------------------------ 1074 | 1075 | LOCK TABLES `users` WRITE; 1076 | /*!40000 ALTER TABLE `users` DISABLE KEYS */; 1077 | 1078 | INSERT INTO `users` (`id`, `email`, `password`) 1079 | VALUES 1080 | (1,'vedovelli@gmail.com','7c4a8d09ca3762af61e59520943dc26494f8941b'), 1081 | (4,'fabio.vedovelli@hotmail.com','7c4a8d09ca3762af61e59520943dc26494f8941b'); 1082 | 1083 | /*!40000 ALTER TABLE `users` ENABLE KEYS */; 1084 | UNLOCK TABLES; 1085 | 1086 | 1087 | 1088 | /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; 1089 | /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; 1090 | /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; 1091 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 1092 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 1093 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 1094 | -------------------------------------------------------------------------------- /database/schema.sql: -------------------------------------------------------------------------------- 1 | -- Create syntax for TABLE 'categories' 2 | CREATE TABLE `categories` ( 3 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 4 | `name` varchar(255) DEFAULT NULL, 5 | PRIMARY KEY (`id`) 6 | ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; 7 | 8 | -- Create syntax for TABLE 'products' 9 | CREATE TABLE `products` ( 10 | `id` int(11) NOT NULL AUTO_INCREMENT, 11 | `category_id` int(11) DEFAULT NULL, 12 | `name` text, 13 | PRIMARY KEY (`id`) 14 | ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; 15 | 16 | -- Create syntax for TABLE 'users' 17 | CREATE TABLE `users` ( 18 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 19 | `email` varchar(255) NOT NULL DEFAULT '', 20 | `password` varchar(40) NOT NULL DEFAULT '', 21 | PRIMARY KEY (`id`) 22 | ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "restful-ws", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "node_env=DEVELOPMENT nodemon src/index.js", 8 | "test": "ava --verbose" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "ava": "^0.25.0", 15 | "dotenv": "^4.0.0", 16 | "eslint": "^4.19.1", 17 | "eslint-config-standard": "^10.2.1", 18 | "eslint-plugin-import": "^2.12.0", 19 | "eslint-plugin-node": "^5.1.1", 20 | "eslint-plugin-promise": "^3.5.0", 21 | "eslint-plugin-standard": "^3.0.1", 22 | "jsonwebtoken": "^8.0.1", 23 | "mysql": "^2.14.1", 24 | "restify": "^7.2.1", 25 | "restify-cors-middleware": "^1.0.1", 26 | "sha1": "^1.1.1" 27 | }, 28 | "dependencies": {} 29 | } 30 | -------------------------------------------------------------------------------- /src/http/modules/categories.js: -------------------------------------------------------------------------------- 1 | 2 | const db = require('../../services/mysql') 3 | 4 | module.exports = function categories (server) { 5 | server.get('/categoria', async (req, res, next) => { 6 | try { 7 | res.send(await db.categories().all()) 8 | } catch (error) { 9 | res.send(422, error) 10 | } 11 | next() 12 | }) 13 | 14 | server.get('/categoria/:id', async (req, res, next) => { 15 | try { 16 | res.send(await db.categories().one(req.params.id)) 17 | } catch (error) { 18 | res.send(422, error) 19 | } 20 | next() 21 | }) 22 | 23 | server.post('/categoria', async (req, res, next) => { 24 | const { name } = req.body 25 | try { 26 | res.send(await db.categories().save(name)) 27 | } catch (error) { 28 | res.send(422, error) 29 | } 30 | next() 31 | }) 32 | 33 | server.put('/categoria', async (req, res, next) => { 34 | const { id, name } = req.body 35 | try { 36 | res.send(await db.categories().update(id, name)) 37 | } catch (error) { 38 | res.send(422, error) 39 | } 40 | next() 41 | }) 42 | 43 | server.del('/categoria/:id', async (req, res, next) => { 44 | const { id } = req.params 45 | try { 46 | res.send(await db.categories().del(id)) 47 | } catch (error) { 48 | res.send(422, error) 49 | } 50 | next() 51 | }) 52 | } 53 | -------------------------------------------------------------------------------- /src/http/modules/products.js: -------------------------------------------------------------------------------- 1 | 2 | const db = require('../../services/mysql') 3 | 4 | module.exports = function products (server) { 5 | server.get('/produto', async (req, res, next) => { 6 | try { 7 | res.send(await db.products().all()) 8 | } catch (error) { 9 | res.send(422, error) 10 | } 11 | next() 12 | }) 13 | 14 | server.get('/produto/categoria/:id', async (req, res, next) => { 15 | try { 16 | const products = await db.products().list({ category_id: req.params.id }) 17 | res.send(products) 18 | } catch (error) { 19 | res.send(422, error) 20 | } 21 | next() 22 | }) 23 | 24 | server.get('/produto/:id', async (req, res, next) => { 25 | try { 26 | res.send(await db.products().one(req.params.id)) 27 | } catch (error) { 28 | res.send(422, error) 29 | } 30 | next() 31 | }) 32 | 33 | server.post('/produto', async (req, res, next) => { 34 | const { name, category_id } = req.body 35 | try { 36 | res.send(await db.products().save(name, category_id)) 37 | } catch (error) { 38 | res.send(422, error) 39 | } 40 | next() 41 | }) 42 | 43 | server.put('/produto', async (req, res, next) => { 44 | const { id, name, category_id } = req.body 45 | try { 46 | res.send(await db.products().update(id, name, category_id)) 47 | } catch (error) { 48 | res.send(422, error) 49 | } 50 | next() 51 | }) 52 | 53 | server.del('/produto/:id', async (req, res, next) => { 54 | const { id } = req.params 55 | try { 56 | res.send(await db.products().del(id)) 57 | } catch (error) { 58 | res.send(422, error) 59 | } 60 | next() 61 | }) 62 | } 63 | -------------------------------------------------------------------------------- /src/http/modules/users.js: -------------------------------------------------------------------------------- 1 | 2 | const db = require('../../services/mysql') 3 | 4 | module.exports = function users (server) { 5 | server.get('/usuario', async (req, res, next) => { 6 | try { 7 | res.send(await db.users().all()) 8 | } catch (error) { 9 | res.send(422, error) 10 | } 11 | next() 12 | }) 13 | 14 | server.get('/usuario/:id', async (req, res, next) => { 15 | try { 16 | res.send(await db.users().one(req.params.id)) 17 | } catch (error) { 18 | res.send(error) 19 | } 20 | next() 21 | }) 22 | 23 | server.post('/usuario', async (req, res, next) => { 24 | const { email, password } = req.body 25 | try { 26 | res.send(await db.users().save(email, password)) 27 | } catch (error) { 28 | res.send(422, error) 29 | } 30 | next() 31 | }) 32 | 33 | server.put('/usuario', async (req, res, next) => { 34 | const { id, name } = req.body 35 | try { 36 | res.send(await db.users().update(id, name)) 37 | } catch (error) { 38 | res.send(422, error) 39 | } 40 | next() 41 | }) 42 | 43 | server.del('/usuario/:id', async (req, res, next) => { 44 | const { id } = req.params 45 | try { 46 | res.send(await db.users().del(id)) 47 | } catch (error) { 48 | res.send(422, error) 49 | } 50 | next() 51 | }) 52 | } 53 | -------------------------------------------------------------------------------- /src/http/routes.js: -------------------------------------------------------------------------------- 1 | 2 | const categories = require('./modules/categories') 3 | const products = require('./modules/products') 4 | const users = require('./modules/users') 5 | 6 | const db = require('../services/mysql') 7 | 8 | const routes = (server) => { 9 | categories(server) 10 | products(server) 11 | users(server) 12 | 13 | server.post('/autenticacao', async (req, res, next) => { 14 | try { 15 | const { email, password } = req.body 16 | res.send(await db.auth().authenticate(email, password)) 17 | } catch (error) { 18 | res.send(422, error) 19 | } 20 | next() 21 | }) 22 | 23 | server.get('/', (req, res, next) => { 24 | res.send('Enjoy the silence!') 25 | next() 26 | }) 27 | } 28 | 29 | module.exports = routes 30 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 2 | require('dotenv').config() 3 | 4 | const server = require('./server') 5 | 6 | server.listen(process.env.PORT) 7 | -------------------------------------------------------------------------------- /src/server/cors.js: -------------------------------------------------------------------------------- 1 | 2 | const corsMiddleware = require('restify-cors-middleware') 3 | 4 | const cors = corsMiddleware({ 5 | preflightMaxAge: 5, 6 | origins: ['*'], 7 | allowHeaders: ['*', 'x-access-token'], 8 | exposeHeaders: ['*'] 9 | }) 10 | 11 | module.exports = cors 12 | -------------------------------------------------------------------------------- /src/server/index.js: -------------------------------------------------------------------------------- 1 | 2 | const restify = require('restify') 3 | const server = restify.createServer() 4 | const routes = require('../http/routes') 5 | const cors = require('./cors') 6 | const jwtMiddleware = require('./jwtMiddleware') 7 | 8 | const exclusions = ['/autenticacao'] 9 | 10 | server.pre(cors.preflight) 11 | server.use(cors.actual) 12 | server.use(restify.plugins.bodyParser()) 13 | server.use(jwtMiddleware({ exclusions })) 14 | 15 | routes(server) 16 | 17 | module.exports = server 18 | -------------------------------------------------------------------------------- /src/server/jwtMiddleware.js: -------------------------------------------------------------------------------- 1 | 2 | const jwt = require('jsonwebtoken') 3 | 4 | const jwtMiddleware = (deps) => { 5 | return async (req, res, next) => { 6 | if (!deps.exclusions.includes(req.href())) { 7 | const token = req.headers['x-access-token'] 8 | 9 | if (!token) { 10 | res.send(403, { error: 'Token não fornecido' }) 11 | return false 12 | } 13 | 14 | try { 15 | req.decoded = jwt.verify(token, process.env.JWT_SECRET) 16 | } catch (error) { 17 | res.send(403, { error: 'Falha ao autenticar o token' }) 18 | return false 19 | } 20 | } 21 | 22 | next() 23 | } 24 | } 25 | 26 | module.exports = jwtMiddleware 27 | -------------------------------------------------------------------------------- /src/services/mysql/auth.js: -------------------------------------------------------------------------------- 1 | 2 | const sha1 = require('sha1') 3 | const jwt = require('jsonwebtoken') 4 | 5 | const auth = deps => { 6 | return { 7 | authenticate: (email, password) => { 8 | return new Promise((resolve, reject) => { 9 | const { connection, errorHandler } = deps 10 | const queryString = 'SELECT id, email FROM users WHERE email = ? AND password = ?' 11 | const queryData = [email, sha1(password)] 12 | 13 | connection.query(queryString, queryData, (error, results) => { 14 | if (error || !results.length) { 15 | errorHandler(error, 'Falha ao localizar o usuário', reject) 16 | return false 17 | } 18 | 19 | const { email, id } = results[0] 20 | 21 | const token = jwt.sign( 22 | { email, id }, 23 | process.env.JWT_SECRET, 24 | { expiresIn: 60 * 60 * 24 } 25 | ) 26 | 27 | resolve({ token }) 28 | }) 29 | }) 30 | } 31 | } 32 | } 33 | 34 | module.exports = auth 35 | -------------------------------------------------------------------------------- /src/services/mysql/categories.js: -------------------------------------------------------------------------------- 1 | 2 | const uniqBy = require('lodash/uniqBy') 3 | 4 | const formatResponse = (results) => { 5 | const categories = uniqBy(results, item => item.category_id) 6 | 7 | return categories.map(category => { 8 | let products = [] 9 | 10 | if (category.product_id != null) { 11 | products = results.filter(item => item.category_id === category.id).map(item => item.product) 12 | } 13 | 14 | category.products = products 15 | 16 | delete category.product_id 17 | delete category.product 18 | delete category.category_id 19 | 20 | return category 21 | }) 22 | } 23 | 24 | const categories = deps => { 25 | return { 26 | all: () => { 27 | return new Promise((resolve, reject) => { 28 | const { connection, errorHandler } = deps 29 | const query = 'SELECT c.id, c.name, p.id as product_id, p.category_id, p.name as product FROM products as p RIGHT JOIN categories as c ON c.id = p.category_id ORDER BY c.id' 30 | 31 | connection.query(query, (error, results) => { 32 | if (error) { 33 | errorHandler(error, 'Falha ao listar as categorias', reject) 34 | return false 35 | } 36 | 37 | const categories = formatResponse(results) 38 | 39 | resolve({ categories }) 40 | }) 41 | }) 42 | }, 43 | products: (categoryId) => { 44 | return new Promise((resolve, reject) => { 45 | const { connection, errorHandler } = deps 46 | 47 | connection.query('SELECT * FROM products WHERE category_id = ?', categoryId, (error, results) => { 48 | if (error) { 49 | errorHandler(error, 'Falha ao obter os produtos', reject) 50 | return false 51 | } 52 | resolve({ products: results }) 53 | }) 54 | }) 55 | }, 56 | one: (id) => { 57 | return new Promise((resolve, reject) => { 58 | const { connection, errorHandler } = deps 59 | 60 | connection.query('SELECT * FROM categories WHERE id = ?', id, (error, results) => { 61 | if (error) { 62 | errorHandler(error, 'Falha ao obter a categoria', reject) 63 | return false 64 | } 65 | resolve({ category: results[0] }) 66 | }) 67 | }) 68 | }, 69 | save: (name) => { 70 | return new Promise((resolve, reject) => { 71 | const { connection, errorHandler } = deps 72 | 73 | connection.query('INSERT INTO categories (name) VALUES (?)', [name], (error, results) => { 74 | if (error) { 75 | errorHandler(error, `Falha ao salvar a categoria ${name}`, reject) 76 | return false 77 | } 78 | resolve({ category: { name, id: results.insertId } }) 79 | }) 80 | }) 81 | }, 82 | update: (id, name) => { 83 | return new Promise((resolve, reject) => { 84 | const { connection, errorHandler } = deps 85 | 86 | connection.query('UPDATE categories SET name = ? WHERE id = ?', [name, id], (error, results) => { 87 | if (error || !results.affectedRows) { 88 | errorHandler(error, `Falha ao atualizar a categoria ${name}`, reject) 89 | return false 90 | } 91 | resolve({ category: { name, id }, affectedRows: results.affectedRows }) 92 | }) 93 | }) 94 | }, 95 | del: (id) => { 96 | return new Promise((resolve, reject) => { 97 | const { connection, errorHandler } = deps 98 | 99 | connection.query('DELETE FROM categories WHERE id = ?', [id], (error, results) => { 100 | if (error || !results.affectedRows) { 101 | errorHandler(error, `Falha ao remover a categoria de id ${id}`, reject) 102 | return false 103 | } 104 | resolve({ message: 'Categoria removida com sucesso!', affectedRows: results.affectedRows }) 105 | }) 106 | }) 107 | } 108 | } 109 | } 110 | 111 | module.exports = categories 112 | -------------------------------------------------------------------------------- /src/services/mysql/index.js: -------------------------------------------------------------------------------- 1 | 2 | const mysqlServer = require('mysql') 3 | 4 | const connection = mysqlServer.createConnection({ 5 | host: process.env.MYSQL_HOST, 6 | user: process.env.MYSQL_USERNAME, 7 | password: process.env.MYSQL_PASSWORD, 8 | database: process.env.MYSQL_DATABASE 9 | }) 10 | 11 | const errorHandler = (error, msg, rejectFunction) => { 12 | if (error) console.error(error) 13 | rejectFunction({ error: msg }) 14 | } 15 | 16 | const dependencies = { connection, errorHandler } 17 | 18 | const categoryModule = require('./categories')(dependencies) 19 | const usersModule = require('./users')(dependencies) 20 | const productsModule = require('./products')(dependencies) 21 | const authModule = require('./auth')(dependencies) 22 | 23 | module.exports = { 24 | categories: () => categoryModule, 25 | products: () => productsModule, 26 | users: () => usersModule, 27 | auth: () => authModule 28 | } 29 | -------------------------------------------------------------------------------- /src/services/mysql/products.js: -------------------------------------------------------------------------------- 1 | 2 | const products = deps => { 3 | return { 4 | all: () => { 5 | return new Promise((resolve, reject) => { 6 | const { connection, errorHandler } = deps 7 | 8 | connection.query('SELECT * FROM products', (error, results) => { 9 | if (error) { 10 | errorHandler(error, 'Falha ao listar as produtos', reject) 11 | return false 12 | } 13 | resolve({ products: results }) 14 | }) 15 | }) 16 | }, 17 | list: (category) => { 18 | return new Promise((resolve, reject) => { 19 | const { connection, errorHandler } = deps 20 | const { category_id: categoryId } = category 21 | 22 | connection.query('SELECT * FROM products WHERE category_id = ?', [categoryId], (error, results) => { 23 | if (error) { 24 | errorHandler(error, 'Falha ao listar as produtos', reject) 25 | return false 26 | } 27 | resolve({ products: results }) 28 | }) 29 | }) 30 | }, 31 | one: (id) => { 32 | return new Promise((resolve, reject) => { 33 | const { connection, errorHandler } = deps 34 | 35 | connection.query('SELECT * FROM products WHERE id = ?', id, (error, results) => { 36 | if (error) { 37 | errorHandler(error, 'Falha ao obter a produto', reject) 38 | return false 39 | } 40 | resolve({ product: results[0] }) 41 | }) 42 | }) 43 | }, 44 | save: (name, categoryId) => { 45 | return new Promise((resolve, reject) => { 46 | const { connection, errorHandler } = deps 47 | 48 | connection.query('INSERT INTO products (name, category_id) VALUES (?, ?)', [name, categoryId], (error, results) => { 49 | if (error) { 50 | errorHandler(error, `Falha ao salvar a produto ${name}`, reject) 51 | return false 52 | } 53 | resolve({ product: { name, category_id: categoryId, id: results.insertId } }) 54 | }) 55 | }) 56 | }, 57 | update: (id, name, categoryId) => { 58 | return new Promise((resolve, reject) => { 59 | const { connection, errorHandler } = deps 60 | connection.query('UPDATE products SET name = ?, category_id = ? WHERE id = ?', [name, categoryId, id], (error, results) => { 61 | if (error || !results.affectedRows) { 62 | errorHandler(error, `Falha ao atualizar a produto ${name}`, reject) 63 | return false 64 | } 65 | resolve({ product: { name, category_id: categoryId, id }, affectedRows: results.affectedRows }) 66 | }) 67 | }) 68 | }, 69 | del: (id) => { 70 | return new Promise((resolve, reject) => { 71 | const { connection, errorHandler } = deps 72 | 73 | connection.query('DELETE FROM products WHERE id = ?', [id], (error, results) => { 74 | if (error || !results.affectedRows) { 75 | errorHandler(error, `Falha ao remover a produto de id ${id}`, reject) 76 | return false 77 | } 78 | resolve({ message: 'Produto removido com sucesso!', affectedRows: results.affectedRows }) 79 | }) 80 | }) 81 | } 82 | } 83 | } 84 | 85 | module.exports = products 86 | -------------------------------------------------------------------------------- /src/services/mysql/tests/auth.test.js: -------------------------------------------------------------------------------- 1 | 2 | const test = require('ava') 3 | const { connection, errorHandler } = require('./setup') 4 | const users = require('../users')({ connection, errorHandler }) 5 | const auth = require('../auth')({ connection, errorHandler }) 6 | const create = () => users.save('user@test.com', '123456') 7 | 8 | test.beforeEach(t => connection.query('TRUNCATE TABLE users')) 9 | test.after.always(t => connection.query('TRUNCATE TABLE users')) 10 | 11 | test('Login de usuário - sucesso', async t => { 12 | await create() 13 | const result = await auth.authenticate('user@test.com', '123456') 14 | t.not(result.token, null) 15 | t.not(result.token.length, 0) 16 | }) 17 | 18 | test('Login de usuário - falha', async t => { 19 | await create() 20 | const promise = auth.authenticate('user2@test.com', '123456') 21 | const error = await t.throws(promise) 22 | t.is(error.error, 'Falha ao localizar o usuário') 23 | }) 24 | -------------------------------------------------------------------------------- /src/services/mysql/tests/categories.test.js: -------------------------------------------------------------------------------- 1 | 2 | const test = require('ava') 3 | const { connection, errorHandler } = require('./setup') 4 | const dependencies = { connection, errorHandler } 5 | const categories = require('../categories')(dependencies) 6 | const products = require('../products')(dependencies) 7 | const createCategory = () => categories.save('category-test') 8 | const createProduct = (categoryId) => products.save('product-test', categoryId) 9 | 10 | test.beforeEach(t => { 11 | connection.query('TRUNCATE TABLE categories') 12 | connection.query('TRUNCATE TABLE products') 13 | }) 14 | 15 | test.after.always(t => { 16 | connection.query('TRUNCATE TABLE categories') 17 | connection.query('TRUNCATE TABLE products') 18 | }) 19 | 20 | test('Lista de categorias com produtos', async t => { 21 | const id = (await createCategory()).id 22 | await createProduct(id) 23 | await createProduct(id) 24 | const list = await categories.all() 25 | t.is(list.categories.length, 1) 26 | t.is(list.categories[0].products.length, 2) 27 | t.is(list.categories[0].products[0], 'product-test') 28 | }) 29 | 30 | test('Criação de categoria', async t => { 31 | const result = await createCategory() 32 | t.is(result.category.name, 'category-test') 33 | }) 34 | 35 | test('Atualizacao de categoria', async t => { 36 | await createCategory() 37 | const updated = await categories.update(1, 'category-test-updated') 38 | t.is(updated.category.name, 'category-test-updated') 39 | t.is(updated.affectedRows, 1) 40 | }) 41 | 42 | test('Remoção de categoria', async t => { 43 | await createCategory() 44 | const removed = await categories.del(1) 45 | t.is(removed.affectedRows, 1) 46 | }) 47 | -------------------------------------------------------------------------------- /src/services/mysql/tests/products.test.js: -------------------------------------------------------------------------------- 1 | 2 | const test = require('ava') 3 | const { connection, errorHandler } = require('./setup') 4 | const products = require('../products')({ connection, errorHandler }) 5 | const categoryId = 1 6 | const create = () => products.save('product-test', categoryId) 7 | 8 | test.beforeEach(t => connection.query('TRUNCATE TABLE products')) 9 | test.after.always(t => connection.query('TRUNCATE TABLE products')) 10 | 11 | test('Lista de produtos', async t => { 12 | await create() 13 | const list = await products.all() 14 | t.is(list.products.length, 1) 15 | t.is(list.products[0].name, 'product-test') 16 | }) 17 | 18 | test('Lista de produtos baseada no ID de determinada categoria', async t => { 19 | products.save('product-test1', 1) 20 | products.save('product-test2', 1) 21 | products.save('product-test3', 2) 22 | 23 | const list = await products.list({ category_id: 1 }) 24 | 25 | t.is(list.products.length, 2) 26 | }) 27 | 28 | test('Criação de produto', async t => { 29 | const result = await create() 30 | t.is(result.product.name, 'product-test') 31 | t.is(result.product.category_id, 1) 32 | }) 33 | 34 | test('Atualizacao de produto', async t => { 35 | await create() 36 | const updated = await products.update(1, 'product-test-updated', 2) 37 | t.is(updated.product.name, 'product-test-updated') 38 | t.is(updated.product.category_id, 2) 39 | t.is(updated.affectedRows, 1) 40 | }) 41 | 42 | test('Remoção de produto', async t => { 43 | await create() 44 | const removed = await products.del(1) 45 | t.is(removed.affectedRows, 1) 46 | }) 47 | -------------------------------------------------------------------------------- /src/services/mysql/tests/setup.js: -------------------------------------------------------------------------------- 1 | 2 | require('dotenv').config() 3 | 4 | const mysqlServer = require('mysql') 5 | 6 | const connection = mysqlServer.createConnection({ 7 | host: process.env.MYSQL_HOST, 8 | user: process.env.MYSQL_USERNAME, 9 | password: process.env.MYSQL_PASSWORD, 10 | database: process.env.MYSQL_TEST_DATABASE 11 | }) 12 | 13 | const errorHandler = (error, msg, rejectFunction) => { 14 | if (error) console.error(error) 15 | rejectFunction({ error: msg }) 16 | } 17 | 18 | module.exports = { connection, errorHandler } 19 | -------------------------------------------------------------------------------- /src/services/mysql/tests/users.test.js: -------------------------------------------------------------------------------- 1 | 2 | const test = require('ava') 3 | const { connection, errorHandler } = require('./setup') 4 | const users = require('../users')({ connection, errorHandler }) 5 | const create = () => users.save('user@test.com', '123456') 6 | 7 | test.beforeEach(t => connection.query('TRUNCATE TABLE users')) 8 | test.after.always(t => connection.query('TRUNCATE TABLE users')) 9 | 10 | test('Lista de usuários', async t => { 11 | await create() 12 | const list = await users.all() 13 | t.is(list.users.length, 1) 14 | t.is(list.users[0].email, 'user@test.com') 15 | }) 16 | 17 | test('Criação de usuário', async t => { 18 | const result = await create() 19 | t.is(result.user.email, 'user@test.com') 20 | }) 21 | 22 | test('Atualizacao de usuário', async t => { 23 | await create() 24 | const updated = await users.update(1, '123456789') 25 | t.is(updated.affectedRows, 1) 26 | }) 27 | 28 | test('Remoção de usuário', async t => { 29 | await create() 30 | const removed = await users.del(1) 31 | t.is(removed.affectedRows, 1) 32 | }) 33 | -------------------------------------------------------------------------------- /src/services/mysql/users.js: -------------------------------------------------------------------------------- 1 | 2 | const sha1 = require('sha1') 3 | 4 | const users = deps => { 5 | return { 6 | all: () => { 7 | return new Promise((resolve, reject) => { 8 | const { connection, errorHandler } = deps 9 | 10 | connection.query('SELECT id, email FROM users', (error, results) => { 11 | if (error) { 12 | errorHandler(error, 'Falha ao listar as usuários', reject) 13 | return false 14 | } 15 | resolve({ users: results }) 16 | }) 17 | }) 18 | }, 19 | save: (email, password) => { 20 | return new Promise((resolve, reject) => { 21 | const { connection, errorHandler } = deps 22 | 23 | connection.query('INSERT INTO users (email, password) VALUES (?, ?)', [email, sha1(password)], (error, results) => { 24 | if (error) { 25 | errorHandler(error, `Falha ao salvar a usuário ${email}`, reject) 26 | return false 27 | } 28 | resolve({ user: { email, id: results.insertId } }) 29 | }) 30 | }) 31 | }, 32 | update: (id, password) => { 33 | return new Promise((resolve, reject) => { 34 | const { connection, errorHandler } = deps 35 | 36 | connection.query('UPDATE users SET password = ? WHERE id = ?', [sha1(password), id], (error, results) => { 37 | if (error || !results.affectedRows) { 38 | errorHandler(error, `Falha ao atualizar a usuário de id ${id}`, reject) 39 | return false 40 | } 41 | resolve({ user: { id }, affectedRows: results.affectedRows }) 42 | }) 43 | }) 44 | }, 45 | del: (id) => { 46 | return new Promise((resolve, reject) => { 47 | const { connection, errorHandler } = deps 48 | 49 | connection.query('DELETE FROM users WHERE id = ?', [id], (error, results) => { 50 | if (error || !results.affectedRows) { 51 | errorHandler(error, `Falha ao remover a usuário de id ${id}`, reject) 52 | return false 53 | } 54 | resolve({ message: 'usuário removida com sucesso!', affectedRows: results.affectedRows }) 55 | }) 56 | }) 57 | } 58 | } 59 | } 60 | 61 | module.exports = users 62 | --------------------------------------------------------------------------------