, share this with others, make a pull request or if you feel really thankful you can always buy me a beer! Enjoy!
368 |
369 | ## License
370 |
371 | This project is open-sourced software licensed under the MIT License. See the LICENSE file for more information.
372 |
--------------------------------------------------------------------------------
/api-phalcon-micro.postman_collection.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "bd456712-1cdc-b9c5-4961-deef858101dc",
3 | "name": "api phalcon micro",
4 | "description": "",
5 | "auth": null,
6 | "events": null,
7 | "variables": [],
8 | "order": [],
9 | "folders_order": ["63ff5769-c1f5-dde5-89de-0913c849d29f", "b7a3d897-49ac-abd0-c890-9fdf0097b542", "d18dfd48-d3ae-0c30-29c5-ffa0e8e6c98f", "0468a31b-14b4-a0cd-f956-5b77bd88737b"],
10 | "folders": [{
11 | "id": "63ff5769-c1f5-dde5-89de-0913c849d29f",
12 | "name": "Cities",
13 | "description": "",
14 | "auth": null,
15 | "events": null,
16 | "collection": "bd456712-1cdc-b9c5-4961-deef858101dc",
17 | "folder": null,
18 | "order": ["732b4a4c-62ed-d256-0a2c-9484ac355d9a", "2d2dc26e-eff1-770a-647c-261a896ad4b5", "d74ea29e-dc42-39d8-4b0c-13cc3af6d97d", "1c5737da-a0e5-e6ad-a8c7-fb413f6e40a8", "045aa7d8-7521-f81e-f3fc-1a2765f47d7b"],
19 | "folders_order": [],
20 | "collectionId": "bd456712-1cdc-b9c5-4961-deef858101dc",
21 | "folderId": "63ff5769-c1f5-dde5-89de-0913c849d29f"
22 | }, {
23 | "id": "b7a3d897-49ac-abd0-c890-9fdf0097b542",
24 | "name": "Index",
25 | "description": "",
26 | "auth": null,
27 | "events": null,
28 | "collection": "bd456712-1cdc-b9c5-4961-deef858101dc",
29 | "folder": null,
30 | "order": ["6ba0116e-3c18-cbe8-8449-cf548b950bfc"],
31 | "folders_order": [],
32 | "collectionId": "bd456712-1cdc-b9c5-4961-deef858101dc",
33 | "folderId": "b7a3d897-49ac-abd0-c890-9fdf0097b542"
34 | }, {
35 | "id": "d18dfd48-d3ae-0c30-29c5-ffa0e8e6c98f",
36 | "name": "Profile",
37 | "description": "",
38 | "auth": null,
39 | "events": null,
40 | "collection": "bd456712-1cdc-b9c5-4961-deef858101dc",
41 | "folder": null,
42 | "order": ["634b17b9-3c8f-ff74-1263-eb01f1a36960", "38b8d9dd-5b98-89e7-e99b-650ba637b54c", "65068fc0-8fd7-7824-99cb-059a147c6a9c"],
43 | "folders_order": [],
44 | "collectionId": "bd456712-1cdc-b9c5-4961-deef858101dc",
45 | "folderId": "d18dfd48-d3ae-0c30-29c5-ffa0e8e6c98f"
46 | }, {
47 | "id": "0468a31b-14b4-a0cd-f956-5b77bd88737b",
48 | "name": "Users",
49 | "description": "",
50 | "auth": null,
51 | "events": null,
52 | "collection": "bd456712-1cdc-b9c5-4961-deef858101dc",
53 | "folder": null,
54 | "order": ["eb82e60a-952b-c51f-5954-d16db6fb5ca9", "b4ec44f8-a16f-9036-21e5-dadcfda27cd0", "bd55552c-2081-ffee-10a5-9b407b85ea20", "2923ffa1-ff09-5938-4c05-9efb56cb7f16", "666acbf2-52e2-2da0-10ff-204e2346fa10", "f3b827e3-ff06-4c39-afa1-025026eb7b4e"],
55 | "folders_order": [],
56 | "collectionId": "bd456712-1cdc-b9c5-4961-deef858101dc",
57 | "folderId": "0468a31b-14b4-a0cd-f956-5b77bd88737b"
58 | }],
59 | "requests": [{
60 | "id": "045aa7d8-7521-f81e-f3fc-1a2765f47d7b",
61 | "name": "\/cities\/delete\/{id}",
62 | "url": "http:\/\/api.myproject.local\/cities\/delete\/8",
63 | "description": "Delete city",
64 | "data": [],
65 | "dataMode": "params",
66 | "headerData": [{
67 | "key": "Authorization",
68 | "value": "Bearer {{authToken}}",
69 | "description": "",
70 | "enabled": true
71 | }],
72 | "method": "DELETE",
73 | "pathVariableData": [],
74 | "queryParams": [],
75 | "auth": null,
76 | "events": null,
77 | "folder": "63ff5769-c1f5-dde5-89de-0913c849d29f",
78 | "currentHelper": null,
79 | "helperAttributes": null,
80 | "collectionId": "bd456712-1cdc-b9c5-4961-deef858101dc",
81 | "headers": "Authorization: Bearer {{authToken}}\n",
82 | "pathVariables": []
83 | }, {
84 | "id": "1c5737da-a0e5-e6ad-a8c7-fb413f6e40a8",
85 | "name": "\/cities\/update\/{id}",
86 | "url": "http:\/\/api.myproject.local\/cities\/update\/7",
87 | "description": "Update city info",
88 | "data": [{
89 | "key": "name",
90 | "value": "Floridablanca",
91 | "type": "text",
92 | "enabled": true
93 | }, {
94 | "key": "country",
95 | "value": "Colombia",
96 | "description": "",
97 | "type": "text",
98 | "enabled": true
99 | }],
100 | "dataMode": "urlencoded",
101 | "headerData": [{
102 | "key": "Authorization",
103 | "value": "Bearer {{authToken}}",
104 | "description": "",
105 | "enabled": true
106 | }],
107 | "method": "PATCH",
108 | "pathVariableData": [],
109 | "queryParams": [],
110 | "auth": null,
111 | "events": null,
112 | "folder": "63ff5769-c1f5-dde5-89de-0913c849d29f",
113 | "currentHelper": null,
114 | "helperAttributes": null,
115 | "collectionId": "bd456712-1cdc-b9c5-4961-deef858101dc",
116 | "headers": "Authorization: Bearer {{authToken}}\n",
117 | "pathVariables": []
118 | }, {
119 | "id": "2923ffa1-ff09-5938-4c05-9efb56cb7f16",
120 | "name": "\/users\/update\/{id}",
121 | "url": "http:\/\/api.myproject.local\/users\/update\/1",
122 | "description": "Update user info",
123 | "data": [{
124 | "key": "email",
125 | "value": "onemore@email.com",
126 | "description": "",
127 | "type": "text",
128 | "enabled": true
129 | }, {
130 | "key": "firstname",
131 | "value": "Other name",
132 | "type": "text",
133 | "enabled": true
134 | }, {
135 | "key": "lastname",
136 | "value": "Other lastname",
137 | "type": "text",
138 | "enabled": true
139 | }, {
140 | "key": "birthday",
141 | "value": "1979-01-01",
142 | "description": "",
143 | "type": "text",
144 | "enabled": true
145 | }, {
146 | "key": "level",
147 | "value": "Superuser",
148 | "type": "text",
149 | "enabled": true
150 | }, {
151 | "key": "phone",
152 | "value": "4534534",
153 | "type": "text",
154 | "enabled": true
155 | }, {
156 | "key": "mobile",
157 | "value": "2425235",
158 | "type": "text",
159 | "enabled": true
160 | }, {
161 | "key": "address",
162 | "value": "Calle 12",
163 | "type": "text",
164 | "enabled": true
165 | }, {
166 | "key": "city",
167 | "value": "Bogot\u00e1",
168 | "type": "text",
169 | "enabled": true
170 | }, {
171 | "key": "country",
172 | "value": "Colombia",
173 | "type": "text",
174 | "enabled": true
175 | }, {
176 | "key": "authorised",
177 | "value": "1",
178 | "type": "text",
179 | "enabled": true
180 | }],
181 | "dataMode": "urlencoded",
182 | "headerData": [{
183 | "key": "Authorization",
184 | "value": "Bearer {{authToken}}",
185 | "description": "",
186 | "enabled": true
187 | }],
188 | "method": "PATCH",
189 | "pathVariableData": [],
190 | "queryParams": [],
191 | "auth": null,
192 | "events": null,
193 | "folder": "0468a31b-14b4-a0cd-f956-5b77bd88737b",
194 | "currentHelper": null,
195 | "helperAttributes": null,
196 | "collectionId": "bd456712-1cdc-b9c5-4961-deef858101dc",
197 | "headers": "Authorization: Bearer {{authToken}}\n",
198 | "pathVariables": []
199 | }, {
200 | "id": "2d2dc26e-eff1-770a-647c-261a896ad4b5",
201 | "name": "\/cities\/create",
202 | "url": "http:\/\/api.myproject.local\/cities\/create",
203 | "description": "Create city",
204 | "data": [{
205 | "key": "name",
206 | "value": "Floridablanca35",
207 | "type": "text",
208 | "enabled": true
209 | }, {
210 | "key": "country",
211 | "value": "Colombia",
212 | "description": "",
213 | "type": "text",
214 | "enabled": true
215 | }],
216 | "dataMode": "urlencoded",
217 | "headerData": [{
218 | "key": "Authorization",
219 | "value": "Bearer {{authToken}}",
220 | "description": "",
221 | "enabled": true
222 | }],
223 | "method": "POST",
224 | "pathVariableData": [],
225 | "queryParams": [],
226 | "auth": null,
227 | "events": null,
228 | "folder": "63ff5769-c1f5-dde5-89de-0913c849d29f",
229 | "currentHelper": null,
230 | "helperAttributes": null,
231 | "collectionId": "bd456712-1cdc-b9c5-4961-deef858101dc",
232 | "headers": "Authorization: Bearer {{authToken}}\n",
233 | "pathVariables": []
234 | }, {
235 | "id": "38b8d9dd-5b98-89e7-e99b-650ba637b54c",
236 | "name": "\/profile\/update",
237 | "url": "http:\/\/api.myproject.local\/profile\/update",
238 | "description": "Updates profile",
239 | "data": [{
240 | "key": "firstname",
241 | "value": "Other name",
242 | "type": "text",
243 | "enabled": true
244 | }, {
245 | "key": "lastname",
246 | "value": "Lastname",
247 | "type": "text",
248 | "enabled": true
249 | }, {
250 | "key": "birthday",
251 | "value": "1979-12-11",
252 | "type": "text",
253 | "enabled": true
254 | }, {
255 | "key": "phone",
256 | "value": "5345345",
257 | "type": "text",
258 | "enabled": true
259 | }, {
260 | "key": "mobile",
261 | "value": "34534534",
262 | "type": "text",
263 | "enabled": true
264 | }, {
265 | "key": "address",
266 | "value": "Calle15",
267 | "type": "text",
268 | "enabled": true
269 | }, {
270 | "key": "city",
271 | "value": "Bogot\u00e1",
272 | "type": "text",
273 | "enabled": true
274 | }, {
275 | "key": "email",
276 | "value": "asd@asd.com",
277 | "description": "",
278 | "type": "text",
279 | "enabled": true
280 | }],
281 | "dataMode": "urlencoded",
282 | "headerData": [{
283 | "key": "Authorization",
284 | "value": "Bearer {{authToken}}",
285 | "description": "",
286 | "enabled": true
287 | }],
288 | "method": "PATCH",
289 | "pathVariableData": [],
290 | "queryParams": [],
291 | "auth": null,
292 | "events": null,
293 | "folder": "d18dfd48-d3ae-0c30-29c5-ffa0e8e6c98f",
294 | "currentHelper": null,
295 | "helperAttributes": null,
296 | "collectionId": "bd456712-1cdc-b9c5-4961-deef858101dc",
297 | "headers": "Authorization: Bearer {{authToken}}\n",
298 | "pathVariables": []
299 | }, {
300 | "id": "634b17b9-3c8f-ff74-1263-eb01f1a36960",
301 | "name": "\/profile",
302 | "url": "http:\/\/api.myproject.local\/profile",
303 | "description": "User profile",
304 | "data": null,
305 | "dataMode": null,
306 | "headerData": [{
307 | "key": "Authorization",
308 | "value": "Bearer {{authToken}}",
309 | "description": "",
310 | "enabled": true
311 | }],
312 | "method": "GET",
313 | "pathVariableData": [],
314 | "queryParams": [],
315 | "auth": null,
316 | "events": null,
317 | "folder": "d18dfd48-d3ae-0c30-29c5-ffa0e8e6c98f",
318 | "currentHelper": null,
319 | "helperAttributes": null,
320 | "collectionId": "bd456712-1cdc-b9c5-4961-deef858101dc",
321 | "headers": "Authorization: Bearer {{authToken}}\n",
322 | "pathVariables": []
323 | }, {
324 | "id": "65068fc0-8fd7-7824-99cb-059a147c6a9c",
325 | "name": "\/profile\/change-password",
326 | "url": "http:\/\/api.myproject.local\/profile\/change-password",
327 | "description": "Updates password",
328 | "data": [{
329 | "key": "current_password",
330 | "value": "admin1234",
331 | "type": "text",
332 | "enabled": true
333 | }, {
334 | "key": "new_password",
335 | "value": "admin1234",
336 | "type": "text",
337 | "enabled": true
338 | }],
339 | "dataMode": "urlencoded",
340 | "headerData": [{
341 | "key": "Authorization",
342 | "value": "Bearer {{authToken}}",
343 | "description": "",
344 | "enabled": true
345 | }],
346 | "method": "PATCH",
347 | "pathVariableData": [],
348 | "queryParams": [],
349 | "auth": null,
350 | "events": null,
351 | "folder": "d18dfd48-d3ae-0c30-29c5-ffa0e8e6c98f",
352 | "currentHelper": null,
353 | "helperAttributes": null,
354 | "collectionId": "bd456712-1cdc-b9c5-4961-deef858101dc",
355 | "headers": "Authorization: Bearer {{authToken}}\n",
356 | "pathVariables": []
357 | }, {
358 | "id": "666acbf2-52e2-2da0-10ff-204e2346fa10",
359 | "name": "\/users\/change-password\/{id}",
360 | "url": "http:\/\/api.myproject.local\/users\/change-password\/2",
361 | "description": "Update user password",
362 | "data": [{
363 | "key": "new_password",
364 | "value": "admin1234",
365 | "type": "text",
366 | "enabled": true
367 | }],
368 | "dataMode": "urlencoded",
369 | "headerData": [{
370 | "key": "Authorization",
371 | "value": "Bearer {{authToken}}",
372 | "description": "",
373 | "enabled": true
374 | }],
375 | "method": "PATCH",
376 | "pathVariableData": [],
377 | "queryParams": [],
378 | "auth": null,
379 | "events": null,
380 | "folder": "0468a31b-14b4-a0cd-f956-5b77bd88737b",
381 | "currentHelper": null,
382 | "helperAttributes": null,
383 | "collectionId": "bd456712-1cdc-b9c5-4961-deef858101dc",
384 | "headers": "Authorization: Bearer {{authToken}}\n",
385 | "pathVariables": []
386 | }, {
387 | "id": "6ba0116e-3c18-cbe8-8449-cf548b950bfc",
388 | "name": "\/authenticate\/",
389 | "url": "http:\/\/api.myproject.local\/authenticate",
390 | "description": "User authentication",
391 | "data": [],
392 | "dataMode": "raw",
393 | "headerData": [{
394 | "key": "Authorization",
395 | "value": "Basic YWRtaW46YWRtaW4xMjM0",
396 | "description": "",
397 | "enabled": true
398 | }],
399 | "method": "POST",
400 | "pathVariableData": [],
401 | "queryParams": [],
402 | "auth": {
403 | "type": "basic",
404 | "basic": [{
405 | "key": "password",
406 | "value": "admin1234"
407 | }, {
408 | "key": "username",
409 | "value": "admin"
410 | }, {
411 | "key": "saveHelperData",
412 | "type": "any"
413 | }, {
414 | "key": "showPassword",
415 | "value": false,
416 | "type": "boolean"
417 | }]
418 | },
419 | "events": null,
420 | "folder": "b7a3d897-49ac-abd0-c890-9fdf0097b542",
421 | "currentHelper": "basicAuth",
422 | "helperAttributes": {
423 | "id": "basic",
424 | "username": "admin",
425 | "password": "admin1234"
426 | },
427 | "collectionId": "bd456712-1cdc-b9c5-4961-deef858101dc",
428 | "rawModeData": "",
429 | "headers": "Authorization: Basic YWRtaW46YWRtaW4xMjM0\n",
430 | "pathVariables": []
431 | }, {
432 | "id": "732b4a4c-62ed-d256-0a2c-9484ac355d9a",
433 | "name": "\/cities",
434 | "url": "http:\/\/api.myproject.local\/cities?limit=5&offset=0&sort=name&order=asc",
435 | "description": "Cities list",
436 | "data": null,
437 | "dataMode": null,
438 | "headerData": [{
439 | "key": "Authorization",
440 | "value": "Bearer {{authToken}}",
441 | "description": "",
442 | "enabled": true
443 | }],
444 | "method": "GET",
445 | "pathVariableData": [],
446 | "queryParams": [{
447 | "key": "limit",
448 | "value": "5",
449 | "equals": true,
450 | "description": "",
451 | "enabled": true
452 | }, {
453 | "key": "offset",
454 | "value": "0",
455 | "equals": true,
456 | "description": "",
457 | "enabled": true
458 | }, {
459 | "key": "sort",
460 | "value": "name",
461 | "equals": true,
462 | "description": "",
463 | "enabled": true
464 | }, {
465 | "key": "order",
466 | "value": "asc",
467 | "equals": true,
468 | "description": "",
469 | "enabled": true
470 | }],
471 | "auth": null,
472 | "events": null,
473 | "folder": "63ff5769-c1f5-dde5-89de-0913c849d29f",
474 | "currentHelper": null,
475 | "helperAttributes": null,
476 | "collectionId": "bd456712-1cdc-b9c5-4961-deef858101dc",
477 | "headers": "Authorization: Bearer {{authToken}}\n",
478 | "pathVariables": []
479 | }, {
480 | "id": "b4ec44f8-a16f-9036-21e5-dadcfda27cd0",
481 | "name": "\/users\/create",
482 | "url": "http:\/\/api.myproject.local\/users\/create",
483 | "description": "Create user",
484 | "data": [{
485 | "key": "email",
486 | "value": "another@email.com",
487 | "type": "text",
488 | "enabled": true
489 | }, {
490 | "key": "new_password",
491 | "value": "test123",
492 | "type": "text",
493 | "enabled": true
494 | }, {
495 | "key": "username",
496 | "value": "admintest",
497 | "type": "text",
498 | "enabled": true
499 | }, {
500 | "key": "firstname",
501 | "value": "My name",
502 | "type": "text",
503 | "enabled": true
504 | }, {
505 | "key": "lastname",
506 | "value": "My last name",
507 | "type": "text",
508 | "enabled": true
509 | }, {
510 | "key": "level",
511 | "value": "Superuser",
512 | "type": "text",
513 | "enabled": true
514 | }, {
515 | "key": "phone",
516 | "value": "12312312",
517 | "type": "text",
518 | "enabled": true
519 | }, {
520 | "key": "mobile",
521 | "value": "31312312",
522 | "type": "text",
523 | "enabled": true
524 | }, {
525 | "key": "address",
526 | "value": "Calle 10",
527 | "type": "text",
528 | "enabled": true
529 | }, {
530 | "key": "city",
531 | "value": "Bogot\u00e1",
532 | "type": "text",
533 | "enabled": true
534 | }, {
535 | "key": "country",
536 | "value": "Colombia",
537 | "description": "",
538 | "type": "text",
539 | "enabled": true
540 | }, {
541 | "key": "birthday",
542 | "value": "1979-01-01",
543 | "type": "text",
544 | "enabled": true
545 | }, {
546 | "key": "authorised",
547 | "value": "1",
548 | "type": "text",
549 | "enabled": true
550 | }],
551 | "dataMode": "urlencoded",
552 | "headerData": [{
553 | "key": "Authorization",
554 | "value": "Bearer {{authToken}}",
555 | "description": "",
556 | "enabled": true
557 | }],
558 | "method": "POST",
559 | "pathVariableData": [],
560 | "queryParams": [],
561 | "auth": null,
562 | "events": null,
563 | "folder": "0468a31b-14b4-a0cd-f956-5b77bd88737b",
564 | "currentHelper": null,
565 | "helperAttributes": null,
566 | "collectionId": "bd456712-1cdc-b9c5-4961-deef858101dc",
567 | "headers": "Authorization: Bearer {{authToken}}\n",
568 | "pathVariables": []
569 | }, {
570 | "id": "bd55552c-2081-ffee-10a5-9b407b85ea20",
571 | "name": "\/users\/get\/{id}",
572 | "url": "http:\/\/api.myproject.local\/users\/get\/1",
573 | "description": "Get user info",
574 | "data": null,
575 | "dataMode": null,
576 | "headerData": [{
577 | "key": "Authorization",
578 | "value": "Bearer {{authToken}}",
579 | "description": "",
580 | "enabled": true
581 | }],
582 | "method": "GET",
583 | "pathVariableData": [],
584 | "queryParams": [],
585 | "auth": null,
586 | "events": null,
587 | "folder": "0468a31b-14b4-a0cd-f956-5b77bd88737b",
588 | "currentHelper": null,
589 | "helperAttributes": null,
590 | "collectionId": "bd456712-1cdc-b9c5-4961-deef858101dc",
591 | "headers": "Authorization: Bearer {{authToken}}\n",
592 | "pathVariables": []
593 | }, {
594 | "id": "d74ea29e-dc42-39d8-4b0c-13cc3af6d97d",
595 | "name": "\/cities\/get\/{id}",
596 | "url": "http:\/\/api.myproject.local\/cities\/get\/1",
597 | "description": "Get city info",
598 | "data": null,
599 | "dataMode": null,
600 | "headerData": [{
601 | "key": "Authorization",
602 | "value": "Bearer {{authToken}}",
603 | "description": "",
604 | "enabled": true
605 | }],
606 | "method": "GET",
607 | "pathVariableData": [],
608 | "queryParams": [],
609 | "auth": null,
610 | "events": null,
611 | "folder": "63ff5769-c1f5-dde5-89de-0913c849d29f",
612 | "currentHelper": null,
613 | "helperAttributes": null,
614 | "collectionId": "bd456712-1cdc-b9c5-4961-deef858101dc",
615 | "headers": "Authorization: Bearer {{authToken}}\n",
616 | "pathVariables": []
617 | }, {
618 | "id": "eb82e60a-952b-c51f-5954-d16db6fb5ca9",
619 | "name": "\/users",
620 | "url": "http:\/\/api.myproject.local\/users",
621 | "description": "Users list",
622 | "data": null,
623 | "dataMode": null,
624 | "headerData": [{
625 | "key": "Authorization",
626 | "value": "Bearer {{authToken}}",
627 | "description": "",
628 | "enabled": true
629 | }],
630 | "method": "GET",
631 | "pathVariableData": [],
632 | "queryParams": [{
633 | "key": "limit",
634 | "value": "5",
635 | "equals": true,
636 | "description": "",
637 | "enabled": false
638 | }, {
639 | "key": "offset",
640 | "value": "0",
641 | "equals": true,
642 | "description": "",
643 | "enabled": false
644 | }, {
645 | "key": "sort",
646 | "value": "firstname, lastname",
647 | "equals": true,
648 | "description": "",
649 | "enabled": false
650 | }, {
651 | "key": "order",
652 | "value": "asc",
653 | "equals": true,
654 | "description": "",
655 | "enabled": false
656 | }, {
657 | "key": "filter",
658 | "value": "{\"user_phone\":60}",
659 | "description": "",
660 | "enabled": false
661 | }],
662 | "auth": null,
663 | "events": null,
664 | "folder": "0468a31b-14b4-a0cd-f956-5b77bd88737b",
665 | "currentHelper": null,
666 | "helperAttributes": null,
667 | "collectionId": "bd456712-1cdc-b9c5-4961-deef858101dc",
668 | "headers": "Authorization: Bearer {{authToken}}\n",
669 | "pathVariables": []
670 | }, {
671 | "id": "f3b827e3-ff06-4c39-afa1-025026eb7b4e",
672 | "name": "\/users\/delete\/{id}",
673 | "url": "http:\/\/api.myproject.local\/users\/delete\/2",
674 | "description": "Delete user",
675 | "data": [],
676 | "dataMode": "params",
677 | "headerData": [{
678 | "description": "",
679 | "enabled": true,
680 | "key": "Authorization",
681 | "value": "Bearer {{authToken}}"
682 | }],
683 | "method": "DELETE",
684 | "pathVariableData": [],
685 | "queryParams": [],
686 | "auth": null,
687 | "events": null,
688 | "folder": "0468a31b-14b4-a0cd-f956-5b77bd88737b",
689 | "currentHelper": null,
690 | "helperAttributes": null,
691 | "collectionId": "bd456712-1cdc-b9c5-4961-deef858101dc",
692 | "headers": "Authorization: Bearer {{authToken}}\n",
693 | "pathVariables": []
694 | }]
695 | }
696 |
--------------------------------------------------------------------------------
/app.php:
--------------------------------------------------------------------------------
1 | before(new AccessMiddleware());
14 |
15 | /**
16 | * Insert your Routes below
17 | */
18 |
19 | /**
20 | * Index
21 | */
22 | $index = new MicroCollection();
23 | $index->setHandler('IndexController', true);
24 | // Gets index
25 | $index->get('/', 'index');
26 | // Authenticates a user
27 | $index->post('/authenticate', 'login');
28 | // logout
29 | $index->get('/logout', 'logout');
30 | // Adds index routes to $app
31 | $app->mount($index);
32 |
33 | /**
34 | * Profile
35 | */
36 | $profile = new MicroCollection();
37 | $profile->setHandler('ProfileController', true);
38 | $profile->setPrefix('/profile');
39 | // Gets profile
40 | $profile->get('/', 'index');
41 | // // Updates user profile
42 | $profile->patch('/update', 'update');
43 | // Changes user password
44 | $profile->patch('/change-password', 'changePassword');
45 | // Adds profile routes to $app
46 | $app->mount($profile);
47 |
48 | /**
49 | * Users
50 | */
51 | $users = new MicroCollection();
52 | $users->setHandler('UsersController', true);
53 | $users->setPrefix('/users');
54 | // Gets all users
55 | $users->get('/', 'index');
56 | // Creates a new user
57 | $users->post('/create', 'create');
58 | // Gets user based on unique key
59 | $users->get('/get/{id}', 'get');
60 | // Updates user based on unique key
61 | $users->patch('/update/{id}', 'update');
62 | // Changes user password
63 | $users->patch('/change-password/{id}', 'changePassword');
64 | // Deletes user based on unique key
65 | $users->delete('/delete/{id}', 'delete');
66 | // Adds users routes to $app
67 | $app->mount($users);
68 |
69 | /**
70 | * Cities
71 | */
72 | $cities = new MicroCollection();
73 | $cities->setHandler('CitiesController', true);
74 | $cities->setPrefix('/cities');
75 | // Gets cities
76 | $cities->get('/', 'index');
77 | // Creates a new city
78 | $cities->post('/create', 'create');
79 | // Gets city based on unique key
80 | $cities->get('/get/{id}', 'get');
81 | // Updates city based on unique key
82 | $cities->patch('/update/{id}', 'update');
83 | // Deletes city based on unique key
84 | $cities->delete('/delete/{id}', 'delete');
85 | // Adds cities routes to $app
86 | $app->mount($cities);
87 |
88 | /**
89 | * Not found handler
90 | */
91 | $app->notFound(function () use ($app) {
92 | $app->response->setStatusCode(404, 'Not Found')->sendHeaders();
93 | $app->response->setContentType('application/json', 'UTF-8');
94 | $app->response->setJsonContent(array(
95 | 'status' => 'error',
96 | 'code' => '404',
97 | 'messages' => 'URL Not found',
98 | ));
99 | $app->response->send();
100 | });
101 |
102 | /**
103 | * Error handler
104 | */
105 | $app->error(
106 | function ($exception) {
107 | print_r('An error has occurred');
108 | }
109 | );
110 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "test": "./vendor/bin/phpunit --testdox tests"
4 | },
5 | "require": {
6 | "phalcon/devtools": "^3.2",
7 | "firebase/php-jwt": "^5.0",
8 | "phpunit/phpunit": "^7.5",
9 | "guzzlehttp/guzzle": "^6.3"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/config/acl.php:
--------------------------------------------------------------------------------
1 | setDefaultAction(Phalcon\Acl::DENY);
7 |
8 | /*
9 | * ROLES
10 | * Superuser - can do anything (Guest, User, and own things)
11 | * User - can do most things (Guest and own things)
12 | * Guest - Public
13 | * */
14 | $acl->addRole(new Phalcon\Acl\Role('Guest'));
15 | $acl->addRole(new Phalcon\Acl\Role('User'));
16 | $acl->addRole(new Phalcon\Acl\Role('Superuser'));
17 |
18 | // User can do everything a Guest can do
19 | $acl->addInherit('User', 'Guest');
20 | // Admin can do everything a User can do
21 | $acl->addInherit('Superuser', 'Guest');
22 | $acl->addInherit('Superuser', 'User');
23 |
24 | /*
25 | * RESOURCES
26 | * for each user, specify the 'controller' and 'method' they have access to ('user_type'=>['controller'=>['method','method','...']],...)
27 | * this is created in an array as we later loop over this structure to assign users to resources
28 | * */
29 | $arrResources = [
30 | 'Guest' => [
31 | 'Index' => ['login'],
32 | ],
33 | 'User' => [
34 | 'Profile' => ['index', 'update', 'changePassword'],
35 | 'Users' => ['index', 'create', 'get', 'search', 'update', 'logout'],
36 | 'Cities' => ['index', 'create', 'get', 'ajax', 'update', 'delete'],
37 | ],
38 | 'Superuser' => [
39 | 'Users' => ['changePassword', 'delete'],
40 | ],
41 | ];
42 |
43 | foreach ($arrResources as $arrResource) {
44 | foreach ($arrResource as $controller => $arrMethods) {
45 | $acl->addResource(new Phalcon\Acl\Resource($controller), $arrMethods);
46 | }
47 | }
48 |
49 | /*
50 | * ACCESS
51 | * */
52 | foreach ($acl->getRoles() as $objRole) {
53 | $roleName = $objRole->getName();
54 |
55 | // everyone gets access to global resources
56 | foreach ($arrResources['Guest'] as $resource => $method) {
57 | $acl->allow($roleName, $resource, $method);
58 | }
59 |
60 | // users
61 | if ($roleName == 'User') {
62 | foreach ($arrResources['User'] as $resource => $method) {
63 | $acl->allow($roleName, $resource, $method);
64 | }
65 | }
66 |
67 | // admins
68 | if ($roleName == 'Superuser') {
69 | foreach ($arrResources['Superuser'] as $resource => $method) {
70 | $acl->allow($roleName, $resource, $method);
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/config/config.php:
--------------------------------------------------------------------------------
1 | [
8 | 'title' => 'API REST',
9 | 'description' => 'API REST',
10 | 'controllersDir' => APP_PATH . '/controllers/',
11 | 'libraryDir' => APP_PATH . '/library/',
12 | 'modelsDir' => APP_PATH . '/models/',
13 | 'migrationsDir' => APP_PATH . '/migrations/',
14 | 'viewsDir' => APP_PATH . '/views/',
15 | 'middlewaresDir' => APP_PATH . '/middlewares/',
16 | 'baseUri' => '/',
17 | ],
18 | ]);
19 |
20 | $configOverride = new \Phalcon\Config(include_once __DIR__ . '/../config/server.' . APPLICATION_ENV . '.php');
21 |
22 | $config = $config->merge($configOverride);
23 |
24 | return $config;
25 |
--------------------------------------------------------------------------------
/config/loader.php:
--------------------------------------------------------------------------------
1 | registerDirs(
9 | array(
10 | $config->application->controllersDir,
11 | $config->application->middlewaresDir,
12 | $config->application->modelsDir,
13 | $config->application->libraryDir,
14 | )
15 | )->register();
16 |
--------------------------------------------------------------------------------
/config/server.development.php:
--------------------------------------------------------------------------------
1 | [
5 | 'adapter' => 'Mysql', /* Possible Values: Mysql, Postgres, Sqlite */
6 | 'host' => '127.0.0.1',
7 | 'username' => 'root',
8 | 'password' => 'mysql',
9 | 'dbname' => 'myproject',
10 | 'charset' => 'utf8',
11 | ],
12 | 'log_database' => [
13 | 'adapter' => 'Mysql', /* Possible Values: Mysql, Postgres, Sqlite */
14 | 'host' => '127.0.0.1',
15 | 'username' => 'root',
16 | 'password' => 'mysql',
17 | 'dbname' => 'myproject_log',
18 | 'charset' => 'utf8',
19 | ],
20 | 'authentication' => [
21 | 'secret' => 'your secret key to SIGN token', // This will sign the token. (still insecure)
22 | 'encryption_key' => 'Your ultra secret key to ENCRYPT the token', // Secure token with an ultra password
23 | 'expiration_time' => 86400 * 7, // One week till token expires
24 | 'iss' => 'myproject', // Token issuer eg. www.myproject.com
25 | 'aud' => 'myproject', // Token audience eg. www.myproject.com
26 | ],
27 | ];
28 |
--------------------------------------------------------------------------------
/config/server.production.php:
--------------------------------------------------------------------------------
1 | [
5 | 'adapter' => 'Mysql', /* Possible Values: Mysql, Postgres, Sqlite */
6 | 'host' => 'your_ip_or_hostname',
7 | 'username' => 'your_db_username',
8 | 'password' => 'your_db_password',
9 | 'dbname' => 'your_database_schema',
10 | 'charset' => 'utf8',
11 | ],
12 | 'log_database' => [
13 | 'adapter' => 'Mysql', /* Possible Values: Mysql, Postgres, Sqlite */
14 | 'host' => 'your_ip_or_hostname',
15 | 'username' => 'your_db_username',
16 | 'password' => 'your_db_password',
17 | 'dbname' => 'your_database_schema',
18 | 'charset' => 'utf8',
19 | ],
20 | 'authentication' => [
21 | 'secret' => 'your secret key to SIGN token', // This will sign the token. (still insecure)
22 | 'encryption_key' => 'Your ultra secret key to ENCRYPT the token', // Secure token with an ultra password
23 | 'expiration_time' => 86400 * 7, // One week till token expires
24 | 'iss' => 'myproject', // Token issuer eg. www.myproject.com
25 | 'aud' => 'myproject', // Token audience eg. www.myproject.com
26 | ],
27 | ];
28 |
--------------------------------------------------------------------------------
/config/server.staging.php:
--------------------------------------------------------------------------------
1 | [
5 | 'adapter' => 'Mysql', /* Possible Values: Mysql, Postgres, Sqlite */
6 | 'host' => 'your_ip_or_hostname',
7 | 'username' => 'your_db_username',
8 | 'password' => 'your_db_password',
9 | 'dbname' => 'your_database_schema',
10 | 'charset' => 'utf8',
11 | ],
12 | 'log_database' => [
13 | 'adapter' => 'Mysql', /* Possible Values: Mysql, Postgres, Sqlite */
14 | 'host' => 'your_ip_or_hostname',
15 | 'username' => 'your_db_username',
16 | 'password' => 'your_db_password',
17 | 'dbname' => 'your_database_schema',
18 | 'charset' => 'utf8',
19 | ],
20 | 'authentication' => [
21 | 'secret' => 'your secret key to SIGN token', // This will sign the token. (still insecure)
22 | 'encryption_key' => 'Your ultra secret key to ENCRYPT the token', // Secure token with an ultra password
23 | 'expiration_time' => 86400 * 7, // One week till token expires
24 | 'iss' => 'myproject', // Token issuer eg. www.myproject.com
25 | 'aud' => 'myproject', // Token audience eg. www.myproject.com
26 | ],
27 | ];
28 |
--------------------------------------------------------------------------------
/config/services.php:
--------------------------------------------------------------------------------
1 | set('modelsManager', function () {
21 | $modelsManager = new ModelsManager();
22 | return $modelsManager;
23 | });
24 |
25 | /**
26 | * Sets the view component
27 | */
28 | $di->setShared('view', function () use ($config) {
29 | $view = new View();
30 | $view->setViewsDir($config->application->viewsDir);
31 | return $view;
32 | });
33 |
34 | /**
35 | * The URL component is used to generate all kind of urls in the application
36 | */
37 | $di->setShared('url', function () use ($config) {
38 | $url = new UrlResolver();
39 | $url->setBaseUri($config->application->baseUri);
40 | return $url;
41 | });
42 |
43 | /**
44 | * Crypt service
45 | */
46 | $di->set('mycrypt', function () use ($config) {
47 | $crypt = new Crypt();
48 | $crypt->setKey($config->get('authentication')->encryption_key);
49 | return $crypt;
50 | }, true);
51 |
52 | /**
53 | * JWT service
54 | */
55 | $di->setShared('jwt', function () {
56 | return new JWT();
57 | });
58 |
59 | /**
60 | * tokenConfig
61 | */
62 | $di->setShared('tokenConfig', function () use ($config) {
63 | $tokenConfig = $config->authentication->toArray();
64 | return $tokenConfig;
65 | });
66 |
67 | /**
68 | * Database connection is created based in the parameters defined in the configuration file
69 | */
70 | $di->setShared('db', function () use ($config) {
71 | $dbConfig = $config->database->toArray();
72 | $adapter = $dbConfig['adapter'];
73 | unset($dbConfig['adapter']);
74 |
75 | $class = 'Phalcon\Db\Adapter\Pdo\\' . $adapter;
76 |
77 | $connection = new $class($dbConfig);
78 | $connection->setNestedTransactionsWithSavepoints(true);
79 |
80 | return $connection;
81 | });
82 |
83 | /**
84 | * Another Database connection is created based in the parameters defined in the configuration file
85 | */
86 | $di->setShared('db_log', function () use ($config) {
87 | $dbConfig = $config->log_database->toArray();
88 | $adapter = $dbConfig['adapter'];
89 | unset($dbConfig['adapter']);
90 |
91 | $class = 'Phalcon\Db\Adapter\Pdo\\' . $adapter;
92 |
93 | $connection = new $class($dbConfig);
94 | $connection->setNestedTransactionsWithSavepoints(true);
95 |
96 | return $connection;
97 | });
98 |
--------------------------------------------------------------------------------
/controllers/CitiesController.php:
--------------------------------------------------------------------------------
1 | $name,
14 | );
15 | $city = Cities::findFirst(
16 | array(
17 | $conditions,
18 | 'bind' => $parameters,
19 | )
20 | );
21 | if ($city) {
22 | $this->buildErrorResponse(409, 'common.THERE_IS_ALREADY_A_RECORD_WITH_THAT_NAME');
23 | }
24 | }
25 |
26 | private function checksIfCityToUpdateAlreadyExists($name, $id)
27 | {
28 | $name = trim($name);
29 | $conditions = 'name = :name: AND id != :id:';
30 | $parameters = array(
31 | 'name' => $name,
32 | 'id' => $id,
33 | );
34 | $city = Cities::findFirst(
35 | array(
36 | $conditions,
37 | 'bind' => $parameters,
38 | )
39 | );
40 | if ($city) {
41 | $this->buildErrorResponse(409, 'common.THERE_IS_ALREADY_A_RECORD_WITH_THAT_NAME');
42 | }
43 | }
44 |
45 | private function createCity($name, $country)
46 | {
47 | $city = new Cities();
48 | $city->name = trim($name);
49 | $city->country = trim($country);
50 | $this->tryToSaveData($city, 'common.COULD_NOT_BE_CREATED');
51 | return $city;
52 | }
53 |
54 | private function updateCity($city, $name, $country)
55 | {
56 | $city->name = trim($name);
57 | $city->country = trim($country);
58 | $this->tryToSaveData($city, 'common.COULD_NOT_BE_UPDATED');
59 | return $city;
60 | }
61 |
62 | /**
63 | * Public functions
64 | */
65 | public function index()
66 | {
67 | $this->initializeGet();
68 | $options = $this->buildOptions('name asc', $this->request->get('sort'), $this->request->get('order'), $this->request->get('limit'), $this->request->get('offset'));
69 | $filters = $this->buildFilters($this->request->get('filter'));
70 | $cities = $this->findElements('Cities', $filters['conditions'], $filters['parameters'], 'id, name, country', $options['order_by'], $options['offset'], $options['limit']);
71 | $total = $this->calculateTotalElements('Cities', $filters['conditions'], $filters['parameters']);
72 | $data = $this->buildListingObject($cities, $options['rows'], $total);
73 | $this->buildSuccessResponse(200, 'common.SUCCESSFUL_REQUEST', $data);
74 | }
75 |
76 | public function create()
77 | {
78 | $this->initializePost();
79 | $this->checkForEmptyData([$this->request->getPost('name'), $this->request->getPost('country')]);
80 | $this->checksIfCityAlreadyExists($this->request->getPost('name'));
81 | $city = $this->createCity($this->request->getPost('name'), $this->request->getPost('country'));
82 | $this->registerLog();
83 | $this->buildSuccessResponse(201, 'common.CREATED_SUCCESSFULLY', $city->toArray());
84 | }
85 |
86 | public function get($id)
87 | {
88 | $this->initializeGet();
89 | $city = $this->findElementById('Cities', $id);
90 | $this->buildSuccessResponse(200, 'common.SUCCESSFUL_REQUEST', $city->toArray());
91 | }
92 |
93 | public function update($id)
94 | {
95 | $this->initializePatch();
96 | $this->checkForEmptyData([$this->request->getPut('name'), $this->request->getPut('country')]);
97 | $this->checksIfCityToUpdateAlreadyExists($this->request->getPut('name'), $id);
98 | $city = $this->updateCity($this->findElementById('Cities', $id), $this->request->getPut('name'), $this->request->getPut('country'));
99 | $this->registerLog();
100 | $this->buildSuccessResponse(200, 'common.UPDATED_SUCCESSFULLY', $city->toArray());
101 | }
102 |
103 | public function delete($id)
104 | {
105 | $this->initializeDelete();
106 | if ($this->tryToDeleteData($this->findElementById('Cities', $id))) {
107 | $this->registerLog();
108 | $this->buildSuccessResponse(200, 'common.DELETED_SUCCESSFULLY');
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/controllers/ControllerBase.php:
--------------------------------------------------------------------------------
1 | decodeToken($this->getToken());
18 |
19 | // Gets URL route from request
20 | $url = $this->request->get();
21 |
22 | // Initiates log db transaction
23 | $this->db_log->begin();
24 | $newLog = new Logs();
25 | $newLog->username = $token_decoded->username_username; // gets username
26 | $newLog->route = $url['_url']; // gets route
27 | $newLog->date = $this->getNowDateTime();
28 | if (!$newLog->save()) {
29 | // rollback transaction
30 | $this->db_log->rollback();
31 | // Send errors
32 | $errors = array();
33 | foreach ($newLog->getMessages() as $message) {
34 | $errors[] = $message->getMessage();
35 | }
36 | $this->buildErrorResponse(400, 'common.COULD_NOT_BE_CREATED', $errors);
37 | }
38 | // Commit the transaction
39 | $this->db_log->commit();
40 | }
41 |
42 | /**
43 | * Try to save data in DB
44 | */
45 | public function tryToSaveData($element, $customMessage = 'common.THERE_HAS_BEEN_AN_ERROR')
46 | {
47 | if (!$element->save()) {
48 | // Send errors
49 | $errors = array();
50 | foreach ($element->getMessages() as $message) {
51 | $errors[] = $message->getMessage();
52 | }
53 | $this->buildErrorResponse(400, $customMessage, $errors);
54 | }
55 | return true;
56 | }
57 |
58 | /**
59 | * Try to delete data in DB
60 | */
61 | public function tryToDeleteData($element)
62 | {
63 | if (!$element->delete()) {
64 | // Send errors
65 | $errors = array();
66 | foreach ($element->getMessages() as $message) {
67 | $errors[] = $message->getMessage();
68 | }
69 | $this->buildErrorResponse(400, 'common.COULD_NOT_BE_DELETED', $errors);
70 | }
71 | return true;
72 | }
73 |
74 | /**
75 | * Build options for listings
76 | */
77 | public function buildOptions($defaultSort, $sort, $order, $limit, $offset)
78 | {
79 | $options = [];
80 | $rows = 5;
81 | $order_by = $defaultSort;
82 | $offset = 0;
83 | $limit = $offset + $rows;
84 |
85 | // Handles Sort querystring (order_by)
86 | if ($sort != null && $order != null) {
87 | $order_by = $sort . ' ' . $order;
88 | }
89 |
90 | // Gets rows_per_page
91 | if ($this->request->get('limit') != null) {
92 | $rows = $this->getQueryLimit($this->request->get('limit'));
93 | $limit = $rows;
94 | }
95 |
96 | // Calculate the offset and limit
97 | if ($this->request->get('offset') != null) {
98 | $offset = $this->request->get('offset');
99 | $limit = $rows;
100 | }
101 | $options = $this->array_push_assoc($options, 'rows', $rows);
102 | $options = $this->array_push_assoc($options, 'order_by', $order_by);
103 | $options = $this->array_push_assoc($options, 'offset', $offset);
104 | $options = $this->array_push_assoc($options, 'limit', $limit);
105 | return $options;
106 | }
107 |
108 | /**
109 | * Build filters for listings
110 | */
111 | public function buildFilters($filter)
112 | {
113 | $filters = [];
114 | $conditions = [];
115 | $parameters = [];
116 |
117 | // Filters simple (no left joins needed)
118 | if ($filter != null) {
119 | $filter = json_decode($filter, true);
120 | foreach ($filter as $key => $value) {
121 | array_push($conditions, $key . ' LIKE :' . $key . ':');
122 | $parameters = $this->array_push_assoc($parameters, $key, '%' . trim($value) . '%');
123 | }
124 | $conditions = implode(' AND ', $conditions);
125 | }
126 | $filters = $this->array_push_assoc($filters, 'conditions', $conditions);
127 | $filters = $this->array_push_assoc($filters, 'parameters', $parameters);
128 | return $filters;
129 | }
130 |
131 | /**
132 | * Build listing object
133 | */
134 | public function buildListingObject($elements, $rows, $total)
135 | {
136 | $data = [];
137 | $data = $this->array_push_assoc($data, 'rows_per_page', $rows);
138 | $data = $this->array_push_assoc($data, 'total_rows', $total);
139 | $data = $this->array_push_assoc($data, 'rows', $elements->toArray());
140 | return $data;
141 | }
142 |
143 | /**
144 | * Calculates total rows for an specified model
145 | */
146 | public function calculateTotalElements($model, $conditions, $parameters)
147 | {
148 | $total = $model::count(
149 | array(
150 | $conditions,
151 | 'bind' => $parameters,
152 | )
153 | );
154 | return $total;
155 | }
156 |
157 | /**
158 | * Find element by ID from an specified model
159 | */
160 | public function findElementById($model, $id)
161 | {
162 | $conditions = 'id = :id:';
163 | $parameters = array(
164 | 'id' => $id,
165 | );
166 | $element = $model::findFirst(
167 | array(
168 | $conditions,
169 | 'bind' => $parameters,
170 | )
171 | );
172 | if (!$element) {
173 | $this->buildErrorResponse(404, 'common.NOT_FOUND');
174 | }
175 | return $element;
176 | }
177 |
178 | /**
179 | * Find elements from an specified model
180 | */
181 | public function findElements($model, $conditions, $parameters, $columns, $order_by, $offset, $limit)
182 | {
183 | $elements = $model::find(
184 | array(
185 | $conditions,
186 | 'bind' => $parameters,
187 | 'columns' => $columns,
188 | 'order' => $order_by,
189 | 'offset' => $offset,
190 | 'limit' => $limit,
191 | )
192 | );
193 | if (!$elements) {
194 | $this->buildErrorResponse(404, 'common.NO_RECORDS');
195 | }
196 | return $elements;
197 | }
198 |
199 | /**
200 | * Check if there is missing data from the request
201 | */
202 | public function checkForEmptyData($array)
203 | {
204 | foreach ($array as $value) {
205 | if (empty($value)) {
206 | $this->buildErrorResponse(400, 'common.INCOMPLETE_DATA_RECEIVED');
207 | }
208 | }
209 | }
210 |
211 | /**
212 | * uset a properties from an array
213 | */
214 | public function unsetPropertyFromArray($array, $remove)
215 | {
216 | foreach ($remove as $value) {
217 | unset($array[$value]);
218 | }
219 | return $array;
220 | }
221 |
222 | /**
223 | * Generated NOW datetime based on a timezone
224 | */
225 | public function getNowDateTime()
226 | {
227 | $now = new DateTime();
228 | $now->setTimezone(new DateTimeZone('UTC'));
229 | $now = $now->format('Y-m-d H:i:s');
230 | return $now;
231 | }
232 |
233 | /**
234 | * Generated NOW datetime based on a timezone and added XX minutes
235 | */
236 | public function getNowDateTimePlusMinutes($minutes_to_add)
237 | {
238 | $now = new DateTime();
239 | $now->setTimezone(new DateTimeZone('UTC'));
240 | $now->add(new DateInterval('PT' . $minutes_to_add . 'M'));
241 | $now = $now->format('Y-m-d H:i:s');
242 | return $now;
243 | }
244 |
245 | /**
246 | * Converts ISO8601 date to DateTime UTC
247 | */
248 | public function iso8601_to_utc($date)
249 | {
250 | return date('Y-m-d H:i:s', strtotime($date));
251 | }
252 |
253 | /**
254 | * Converts DateTime UTC date to ISO8601
255 | */
256 | public function utc_to_iso8601($date)
257 | {
258 | if (!empty($date) && ($date != '0000-00-00') && ($date != '0000-00-00 00:00') && ($date != '0000-00-00 00:00:00')) {
259 | $datetime = new DateTime($date);
260 | return $datetime->format('Y-m-d\TH:i:s\Z');
261 | }
262 | return null;
263 | }
264 |
265 | /**
266 | * Array push associative.
267 | */
268 | public function array_push_assoc($array, $key, $value)
269 | {
270 | $array[$key] = $value;
271 | return $array;
272 | }
273 |
274 | /**
275 | * Generates limits for queries.
276 | */
277 | public function getQueryLimit($limit)
278 | {
279 | $setLimit = 5;
280 | if ($limit != '') {
281 | if ($limit > 150) {
282 | $setLimit = 150;
283 | }
284 | if ($limit <= 0) {
285 | $setLimit = 1;
286 | }
287 | if (($limit >= 1) && ($limit <= 150)) {
288 | $setLimit = $limit;
289 | }
290 | }
291 | return $setLimit;
292 | }
293 |
294 | /**
295 | * Verifies if is get request
296 | */
297 | public function initializeGet()
298 | {
299 | if (!$this->request->isGet()) {
300 | die();
301 | }
302 | }
303 |
304 | /**
305 | * Verifies if is post request
306 | */
307 | public function initializePost()
308 | {
309 | if (!$this->request->isPost()) {
310 | die();
311 | }
312 | }
313 |
314 | /**
315 | * Verifies if is patch request
316 | */
317 | public function initializePatch()
318 | {
319 | if (!$this->request->isPatch()) {
320 | die();
321 | }
322 | }
323 |
324 | /**
325 | * Verifies if is patch request
326 | */
327 | public function initializeDelete()
328 | {
329 | if (!$this->request->isDelete()) {
330 | die();
331 | }
332 | }
333 |
334 | /**
335 | * Encode token.
336 | */
337 | public function encodeToken($data)
338 | {
339 | // Encode token
340 | $token_encoded = $this->jwt->encode($data, $this->tokenConfig['secret']);
341 | $token_encoded = $this->mycrypt->encryptBase64($token_encoded);
342 | return $token_encoded;
343 | }
344 |
345 | /**
346 | * Decode token.
347 | */
348 | public function decodeToken($token)
349 | {
350 | // Decode token
351 | $token = $this->mycrypt->decryptBase64($token);
352 | $token = $this->jwt->decode($token, $this->tokenConfig['secret'], array('HS256'));
353 | return $token;
354 | }
355 |
356 | /**
357 | * Returns token from the request.
358 | * Uses token URL query field, or Authorization header
359 | */
360 | public function getToken()
361 | {
362 | $authHeader = $this->request->getHeader('Authorization');
363 | $authQuery = $this->request->getQuery('token');
364 | return $authQuery ? $authQuery : $this->parseBearerValue($authHeader);
365 | }
366 |
367 | protected function parseBearerValue($string)
368 | {
369 | if (strpos(trim($string), 'Bearer') !== 0) {
370 | return null;
371 | }
372 | return preg_replace('/.*\s/', '', $string);
373 | }
374 |
375 | /**
376 | * Builds success responses.
377 | */
378 | public function buildSuccessResponse($code, $messages, $data = '')
379 | {
380 | switch ($code) {
381 | case 200:
382 | $status = 'OK';
383 | break;
384 | case 201:
385 | $status = 'Created';
386 | break;
387 | case 202:
388 | break;
389 | }
390 | $generated = array(
391 | 'status' => $status,
392 | 'code' => $code,
393 | 'messages' => $messages,
394 | 'data' => $data,
395 | );
396 | $this->response->setStatusCode($code, $status)->sendHeaders();
397 | $this->response->setContentType('application/json', 'UTF-8');
398 | $this->response->setJsonContent($generated, JSON_NUMERIC_CHECK)->send();
399 | die();
400 | }
401 |
402 | /**
403 | * Builds error responses.
404 | */
405 | public function buildErrorResponse($code, $messages, $data = '')
406 | {
407 | switch ($code) {
408 | case 400:
409 | $status = 'Bad Request';
410 | break;
411 | case 401:
412 | $status = 'Unauthorized';
413 | break;
414 | case 403:
415 | $status = 'Forbidden';
416 | break;
417 | case 404:
418 | $status = 'Not Found';
419 | break;
420 | case 409:
421 | $status = 'Conflict';
422 | break;
423 | }
424 | $generated = array(
425 | 'status' => $status,
426 | 'code' => $code,
427 | 'messages' => $messages,
428 | 'data' => $data,
429 | );
430 | $this->response->setStatusCode($code, $status)->sendHeaders();
431 | $this->response->setContentType('application/json', 'UTF-8');
432 | $this->response->setJsonContent($generated, JSON_NUMERIC_CHECK)->send();
433 | die();
434 | }
435 | }
436 |
--------------------------------------------------------------------------------
/controllers/IndexController.php:
--------------------------------------------------------------------------------
1 | view->render('index');
11 | }
12 |
13 | /**
14 | * Private functions
15 | */
16 | private function checkIfHeadersExist($headers)
17 | {
18 | return (!isset($headers['Authorization']) || empty($headers['Authorization'])) ? $this->buildErrorResponse(403, 'common.HEADER_AUTHORIZATION_NOT_SENT') : true;
19 | }
20 |
21 | private function findUser($credentials)
22 | {
23 | $username = $credentials['username'];
24 |
25 | $conditions = 'username = :username:';
26 | $parameters = array(
27 | 'username' => $username,
28 | );
29 | $user = Users::findFirst(
30 | array(
31 | $conditions,
32 | 'bind' => $parameters,
33 | )
34 | );
35 | return (!$user) ? $this->buildErrorResponse(404, 'login.USER_IS_NOT_REGISTERED') : $user;
36 | }
37 |
38 | private function getUserPassword($credentials)
39 | {
40 | return $credentials['password'];
41 | }
42 |
43 | private function checkIfUserIsNotBlocked($user)
44 | {
45 | $block_expires = strtotime($user->block_expires);
46 | $now = strtotime($this->getNowDateTime());
47 | return ($block_expires > $now) ? $this->buildErrorResponse(403, 'login.USER_BLOCKED') : true;
48 | }
49 |
50 | private function checkIfUserIsAuthorized($user)
51 | {
52 | return ($user->authorised == 0) ? $this->buildErrorResponse(403, 'login.USER_UNAUTHORIZED') : true;
53 | }
54 |
55 | private function addOneLoginAttempt($user)
56 | {
57 | $user->login_attempts = $user->login_attempts + 1;
58 | $this->tryToSaveData($user);
59 | return $user->login_attempts;
60 | }
61 |
62 | private function addXMinutesBlockToUser($minutes, $user)
63 | {
64 | $user->block_expires = $this->getNowDateTimePlusMinutes($minutes);
65 | if ($this->tryToSaveData($user)) {
66 | $this->buildErrorResponse(400, 'login.TOO_MANY_FAILED_LOGIN_ATTEMPTS');
67 | }
68 | }
69 |
70 | private function checkPassword($password, $user)
71 | {
72 | if (!password_verify($password, $user->password)) {
73 | $login_attempts = $this->addOneLoginAttempt($user);
74 | ($login_attempts <= 4) ? $this->buildErrorResponse(400, 'login.WRONG_USER_PASSWORD') : $this->addXMinutesBlockToUser(120, $user);
75 | }
76 | }
77 |
78 | private function checkIfPasswordNeedsRehash($password, $user)
79 | {
80 | $options = [
81 | 'cost' => 10, // the default cost is 10, max is 12.
82 | ];
83 | if (password_needs_rehash($user->password, PASSWORD_DEFAULT, $options)) {
84 | $newHash = password_hash($password, PASSWORD_DEFAULT, $options);
85 | $user->password = $newHash;
86 | $this->tryToSaveData($user);
87 | }
88 | }
89 |
90 | private function buildUserData($user)
91 | {
92 | $user_data = array(
93 | 'id' => $user->id,
94 | 'username' => $user->username,
95 | 'email' => $user->email,
96 | 'firstname' => $user->firstname,
97 | 'lastname' => $user->lastname,
98 | );
99 | return $user_data;
100 | }
101 |
102 | private function buildTokenData($user)
103 | {
104 | // issue at time and expires (token)
105 | $iat = strtotime($this->getNowDateTime());
106 | $exp = strtotime('+' . $this->tokenConfig['expiration_time'] . ' seconds', $iat);
107 |
108 | $token_data = array(
109 | 'iss' => $this->tokenConfig['iss'],
110 | 'aud' => $this->tokenConfig['aud'],
111 | 'iat' => $iat,
112 | 'exp' => $exp,
113 | 'username_username' => $user->username,
114 | 'username_firstname' => $user->firstname,
115 | 'username_lastname' => $user->lastname,
116 | 'username_level' => $user->level,
117 | 'rand' => rand() . microtime(),
118 | );
119 | return $token_data;
120 | }
121 |
122 | private function resetLoginAttempts($user)
123 | {
124 | $user->login_attempts = 0;
125 | $this->tryToSaveData($user);
126 | }
127 |
128 | private function registerNewUserAccess($user)
129 | {
130 | $headers = $this->request->getHeaders();
131 | $newAccess = new UsersAccess();
132 | $newAccess->username = $user->username;
133 | $newAccess->ip = (isset($headers['Http-Client-Ip']) || !empty($headers['Http-Client-Ip'])) ? $headers['Http-Client-Ip'] : $this->request->getClientAddress();
134 | $newAccess->domain = (isset($headers['Http-Client-Domain']) || !empty($headers['Http-Client-Domain'])) ? $headers['Http-Client-Domain'] : gethostbyaddr($this->request->getClientAddress());
135 | $newAccess->country = (isset($headers['Http-Client-Country']) || !empty($headers['Http-Client-Country'])) ? $headers['Http-Client-Country'] : ($this->request->getServer('HTTP_CF_IPCOUNTRY') !== null) ? $this->request->getServer('HTTP_CF_IPCOUNTRY') : 'XX';
136 | $newAccess->browser = $this->request->getUserAgent();
137 | $newAccess->date = $this->getNowDateTime();
138 | $this->tryToSaveData($newAccess);
139 | }
140 |
141 | /**
142 | * Public functions
143 | */
144 | public function login()
145 | {
146 | $this->initializePost($this->request->getHeaders());
147 | if ($this->checkIfHeadersExist($this->request->getHeaders())) {
148 | $user = $this->findUser($this->request->getBasicAuth());
149 | $password = $this->getUserPassword($this->request->getBasicAuth());
150 | $this->checkIfUserIsNotBlocked($user);
151 | $this->checkIfUserIsAuthorized($user);
152 | $this->checkPassword($password, $user);
153 |
154 | // ALL OK, proceed to login
155 | $this->checkIfPasswordNeedsRehash($password, $user);
156 | $user_data = $this->buildUserData($user);
157 | $token = $this->encodeToken($this->buildTokenData($user));
158 |
159 | $data = array(
160 | 'token' => $token,
161 | 'user' => $user_data,
162 | );
163 |
164 | $this->resetLoginAttempts($user);
165 | $this->registerNewUserAccess($user);
166 | $this->buildSuccessResponse(200, 'common.SUCCESSFUL_REQUEST', $data);
167 | }
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/controllers/ProfileController.php:
--------------------------------------------------------------------------------
1 | $token->username_username,
13 | );
14 | $user = Users::findFirst(
15 | array(
16 | $conditions,
17 | 'bind' => $parameters,
18 | )
19 | );
20 | if (!$user) {
21 | $this->buildErrorResponse(404, 'profile.PROFILE_NOT_FOUND');
22 | }
23 | return $user;
24 | }
25 |
26 | private function updateProfile($user, $firstname, $lastname, $email, $phone, $mobile, $address, $birthday)
27 | {
28 | $user->firstname = trim($firstname);
29 | $user->lastname = trim($lastname);
30 | $user->email = trim($email);
31 | $user->phone = trim($phone);
32 | $user->mobile = trim($mobile);
33 | $user->address = trim($address);
34 | $user->birthday = trim($birthday);
35 | $this->tryToSaveData($user, 'profile.PROFILE_COULD_NOT_BE_UPDATED');
36 | return $user;
37 | }
38 |
39 | private function checkPassword($password, $user)
40 | {
41 | if (!password_verify($password, $user->password)) {
42 | $this->buildErrorResponse(400, 'change-password.WRONG_CURRENT_PASSWORD');
43 | }
44 | }
45 |
46 | private function setNewPassword($new_password, $user)
47 | {
48 | $user->password = password_hash($new_password, PASSWORD_BCRYPT);
49 | $this->tryToSaveData($user, 'change-password.PASSWORD_COULD_NOT_BE_UPDATED');
50 | }
51 |
52 | /**
53 | * Public functions
54 | */
55 | public function index()
56 | {
57 | $this->initializeGet();
58 | $user = $this->findUser($this->decodeToken($this->getToken()))->toArray();
59 | $user = $this->unsetPropertyFromArray($user, ['password', 'level', 'authorised', 'block_expires', 'login_attempts']);
60 | $this->buildSuccessResponse(200, 'common.SUCCESSFUL_REQUEST', $user);
61 | }
62 |
63 | public function update()
64 | {
65 | $this->initializePatch();
66 | $this->checkForEmptyData([$this->request->getPut('firstname'), $this->request->getPut('lastname')]);
67 | $user = $this->updateProfile($this->findUser($this->decodeToken($this->getToken())), $this->request->getPut('firstname'), $this->request->getPut('lastname'), $this->request->getPut('email'), $this->request->getPut('phone'), $this->request->getPut('mobile'), $this->request->getPut('address'), $this->request->getPut('birthday'));
68 | $user = $user->toArray();
69 | $user = $this->unsetPropertyFromArray($user, ['password', 'level', 'authorised', 'block_expires', 'login_attempts']);
70 | $this->registerLog();
71 | $this->buildSuccessResponse(200, 'profile.PROFILE_UPDATED', $user);
72 | }
73 |
74 | public function changePassword()
75 | {
76 | $this->initializePatch();
77 | $this->checkForEmptyData([$this->request->getPut('current_password'), $this->request->getPut('new_password')]);
78 | $user = $this->findUser($this->decodeToken($this->getToken()));
79 | $this->checkPassword($this->request->getPut('current_password'), $user);
80 | $this->setNewPassword($this->request->getPut('new_password'), $user);
81 | $this->registerLog();
82 | $this->buildSuccessResponse(200, 'change-password.PASSWORD_SUCCESSFULLY_UPDATED');
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/controllers/UsersController.php:
--------------------------------------------------------------------------------
1 | buildErrorResponse(409, 'common.COULD_NOT_BE_CREATED');
13 | }
14 | }
15 |
16 | private function checkIfUsernameAlreadyExists($username)
17 | {
18 | // checks if user already exists
19 | $conditions = 'username = :username:';
20 | $parameters = array(
21 | 'username' => trim($username),
22 | );
23 | $user = Users::findFirst(
24 | array(
25 | $conditions,
26 | 'bind' => $parameters,
27 | )
28 | );
29 | if ($user) {
30 | $this->buildErrorResponse(409, 'profile.ANOTHER_USER_ALREADY_REGISTERED_WITH_THIS_USERNAME');
31 | }
32 | }
33 |
34 | private function createUser()
35 | {
36 | $user = new Users();
37 | $user->email = trim($this->request->getPost('email'));
38 | $user->username = trim($this->request->getPost('username'));
39 | $user->firstname = trim($this->request->getPost('firstname'));
40 | $user->lastname = trim($this->request->getPost('lastname'));
41 | $user->level = trim($this->request->getPost('level'));
42 | $user->phone = trim($this->request->getPost('phone'));
43 | $user->mobile = trim($this->request->getPost('mobile'));
44 | $user->address = trim($this->request->getPost('address'));
45 | $user->city = trim($this->request->getPost('city'));
46 | $user->country = trim($this->request->getPost('country'));
47 | $user->birthday = trim($this->request->getPost('birthday'));
48 | $user->authorised = trim($this->request->getPost('authorised')) || 0;
49 | $user->password = password_hash($this->request->getPost('new_password'), PASSWORD_BCRYPT);
50 | $this->tryToSaveData($user, 'common.COULD_NOT_BE_CREATED');
51 | return $user;
52 | }
53 |
54 | private function findUserLastAccess($user)
55 | {
56 | $conditions = 'username = :username:';
57 | $parameters = array(
58 | 'username' => $user['username'],
59 | );
60 | $last_access = UsersAccess::find(
61 | array(
62 | $conditions,
63 | 'bind' => $parameters,
64 | 'columns' => 'date, ip, domain, browser',
65 | 'order' => 'id DESC',
66 | 'limit' => 10,
67 | )
68 | );
69 | if ($last_access) {
70 | $array = array();
71 | $user_last_access = $last_access->toArray();
72 | foreach ($user_last_access as $value_last_access) {
73 | $_user_last_access = array(
74 | 'date' => $this->utc_to_iso8601($value_last_access['date']),
75 | 'ip' => $value_last_access['ip'],
76 | 'domain' => $value_last_access['domain'],
77 | 'browser' => $value_last_access['browser'],
78 | );
79 | $array[] = $_user_last_access;
80 | }
81 | $user = empty($array) ? $this->array_push_assoc($user, 'last_access', '') : $this->array_push_assoc($user, 'last_access', $array);
82 | return $user;
83 | }
84 | }
85 |
86 | private function updateUser($user)
87 | {
88 | $user->email = trim($this->request->getPut('email'));
89 | $user->firstname = trim($this->request->getPut('firstname'));
90 | $user->lastname = trim($this->request->getPut('lastname'));
91 | $user->level = trim($this->request->getPut('level'));
92 | $user->phone = trim($this->request->getPut('phone'));
93 | $user->mobile = trim($this->request->getPut('mobile'));
94 | $user->address = trim($this->request->getPut('address'));
95 | $user->city = trim($this->request->getPut('city'));
96 | $user->country = trim($this->request->getPut('country'));
97 | $user->birthday = trim($this->request->getPut('birthday'));
98 | $user->authorised = trim($this->request->getPut('authorised')) || 0;
99 | $this->tryToSaveData($user, 'common.COULD_NOT_BE_UPDATED');
100 | return $user;
101 | }
102 |
103 | private function setNewPassword($new_password, $user)
104 | {
105 | $user->password = password_hash($new_password, PASSWORD_BCRYPT);
106 | $this->tryToSaveData($user, 'common.COULD_NOT_BE_UPDATED');
107 | }
108 |
109 | /**
110 | * Public functions
111 | */
112 | public function index()
113 | {
114 | $this->initializeGet();
115 | $options = $this->buildOptions('firstname asc, lastname asc', $this->request->get('sort'), $this->request->get('order'), $this->request->get('limit'), $this->request->get('offset'));
116 | $filters = $this->buildFilters($this->request->get('filter'));
117 | $cities = $this->findElements('Users', $filters['conditions'], $filters['parameters'], 'id, firstname, lastname, level, email, phone, mobile, address, country, city, birthday, authorised', $options['order_by'], $options['offset'], $options['limit']);
118 | $total = $this->calculateTotalElements('Users', $filters['conditions'], $filters['parameters']);
119 | $data = $this->buildListingObject($cities, $options['rows'], $total);
120 | $this->buildSuccessResponse(200, 'common.SUCCESSFUL_REQUEST', $data);
121 | }
122 |
123 | public function create()
124 | {
125 | $this->initializePost();
126 | $this->checkForEmptyData([$this->request->getPost('username'), $this->request->getPost('firstname'), $this->request->getPost('new_password'), $this->request->getPost('email')]);
127 | $this->checkForbiddenUsername($this->request->getPost('username'));
128 | $this->checkIfUsernameAlreadyExists($this->request->getPost('username'));
129 | $user = $this->createUser();
130 | $user = $user->toArray();
131 | $user = $this->unsetPropertyFromArray($user, ['password', 'block_expires', 'login_attempts']);
132 | $this->registerLog();
133 | $this->buildSuccessResponse(201, 'common.CREATED_SUCCESSFULLY', $user);
134 | }
135 |
136 | public function get($id)
137 | {
138 | $this->initializeGet();
139 | $user = $this->findElementById('Users', $id);
140 | $user = $user->toArray();
141 | $user = $this->unsetPropertyFromArray($user, ['password', 'block_expires', 'login_attempts']);
142 | $user = $this->findUserLastAccess($user);
143 | $this->buildSuccessResponse(200, 'common.SUCCESSFUL_REQUEST', $user);
144 | }
145 |
146 | public function update($id)
147 | {
148 | $this->initializePatch();
149 | $this->checkForEmptyData([$this->request->getPut('firstname'), $this->request->getPut('authorised')]);
150 | $user = $this->updateUser($this->findElementById('Users', $id));
151 | $user = $user->toArray();
152 | $user = $this->unsetPropertyFromArray($user, ['password', 'block_expires', 'login_attempts']);
153 | $this->registerLog();
154 | $this->buildSuccessResponse(200, 'common.UPDATED_SUCCESSFULLY', $user);
155 | }
156 |
157 | public function changePassword($id)
158 | {
159 | $this->initializePatch();
160 | $this->checkForEmptyData([$this->request->getPut('new_password')]);
161 | $user = $this->findElementById('Users', $id);
162 | $this->setNewPassword($this->request->getPut('new_password'), $user);
163 | $this->registerLog();
164 | $this->buildSuccessResponse(200, 'change-password.PASSWORD_SUCCESSFULLY_UPDATED');
165 | }
166 |
167 | public function delete($id)
168 | {
169 | $this->initializeDelete();
170 | if ($this->tryToDeleteData($this->findElementById('Users', $id))) {
171 | $this->registerLog();
172 | $this->buildSuccessResponse(200, 'common.DELETED_SUCCESSFULLY');
173 | }
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/deploy-production.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | PROJECT=myproject
3 | USER=server_username
4 | URL=my_production_server_url
5 | read -p "$(echo $'\n******************************') $(echo $'\n******* API PRODUCTION *******') $(echo $'\n******************************') $(echo $'\nPress Return to Deploy...')"; rsync -avzhe ssh --exclude-from=$HOME/$PROJECT/exclude-production.txt $HOME/$PROJECT/ $USER@$URL:/home/$USER/$URL; read -p "Press Return to Close..."
6 |
--------------------------------------------------------------------------------
/deploy-staging.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | PROJECT=myproject
3 | USER=server_username
4 | URL=my_staging_server_url
5 | read -p "$(echo $'\n*****************************') $(echo $'\n******** API STAGING ********') $(echo $'\n*****************************') $(echo $'\nPress Return to Deploy...')"; rsync -avzhe ssh --exclude-from=$HOME/$PROJECT/exclude-staging.txt $HOME/$PROJECT/ $USER@$URL:/home/$USER/$URL; read -p "Press Return to Close..."
6 |
--------------------------------------------------------------------------------
/exclude-production.txt:
--------------------------------------------------------------------------------
1 | .git/
2 | *.DS_Store
3 | .phalcon/
4 | .php/
5 | cache/
6 | .htaccess
7 | exclude-staging.txt
8 | exclude-production.txt
9 | deploy-staging.sh
10 | deploy-production.sh
11 | config/server.development.php
12 | config/server.staging.php
13 | public/.htaccess
14 | schemas/
15 |
--------------------------------------------------------------------------------
/exclude-staging.txt:
--------------------------------------------------------------------------------
1 | .git/
2 | *.DS_Store
3 | .phalcon/
4 | .php/
5 | cache/
6 | .htaccess
7 | exclude-staging.txt
8 | exclude-production.txt
9 | deploy-staging.sh
10 | deploy-production.sh
11 | config/server.development.php
12 | config/server.production.php
13 | public/.htaccess
14 | schemas/
15 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 | Mod-Rewrite is not enabled
Please enable rewrite module on your web server to continue
--------------------------------------------------------------------------------
/library/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davellanedam/phalcon-micro-rest-api-skeleton/3c1a9bb4264d3438920e8779dbc7884bf5283a4d/library/.gitkeep
--------------------------------------------------------------------------------
/middlewares/AccessMiddleware.php:
--------------------------------------------------------------------------------
1 | getActiveHandler();
18 | //get the controller for this handler
19 | $array = (array) $arrHandler[0];
20 | $nameController = implode('', $array);
21 | $controller = str_replace('Controller', '', $nameController);
22 | // check if controller is Index, if it´s Index, then checks if any of functions are called if so return allow
23 | if ($controller === 'Index') {
24 | $allowed = 1;
25 | return $allowed;
26 | }
27 |
28 | // gets user token
29 | $mytoken = $this->getToken();
30 |
31 | // Verifies Token exists and is not empty
32 | if (empty($mytoken) || $mytoken == '') {
33 | $this->buildErrorResponse(400, 'common.EMPTY_TOKEN_OR_NOT_RECEIVED');
34 | }
35 | // Verifies Token
36 | try {
37 | $token_decoded = $this->decodeToken($mytoken);
38 | // Verifies User role Access
39 | $allowed_access = $acl->isAllowed($token_decoded->username_level, $controller, $arrHandler[1]);
40 | return (!$allowed_access) ? $this->buildErrorResponse(403, 'common.YOUR_USER_ROLE_DOES_NOT_HAVE_THIS_FEATURE') : $allowed_access;
41 | } catch (Exception $e) {
42 | // BAD TOKEN
43 | $this->buildErrorResponse(401, 'common.BAD_TOKEN_GET_A_NEW_ONE');
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/migrations/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davellanedam/phalcon-micro-rest-api-skeleton/3c1a9bb4264d3438920e8779dbc7884bf5283a4d/migrations/.gitkeep
--------------------------------------------------------------------------------
/models/Cities.php:
--------------------------------------------------------------------------------
1 | setConnectionService('db');
30 | }
31 |
32 | /**
33 | * Returns table name mapped in the model.
34 | *
35 | * @return string
36 | */
37 | public function getSource()
38 | {
39 | return 'cities';
40 | }
41 |
42 | /**
43 | * Allows to query a set of records that match the specified conditions
44 | *
45 | * @param mixed $parameters
46 | * @return Cities[]
47 | */
48 | public static function find($parameters = null)
49 | {
50 | return parent::find($parameters);
51 | }
52 |
53 | /**
54 | * Allows to query the first record that match the specified conditions
55 | *
56 | * @param mixed $parameters
57 | * @return Cities
58 | */
59 | public static function findFirst($parameters = null)
60 | {
61 | return parent::findFirst($parameters);
62 | }
63 |
64 | /**
65 | * Independent Column Mapping.
66 | * Keys are the real names in the table and the values their names in the application
67 | *
68 | * @return array
69 | */
70 | public function columnMap()
71 | {
72 | return array(
73 | 'id' => 'id',
74 | 'name' => 'name',
75 | 'country' => 'country',
76 | );
77 | }
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/models/Logs.php:
--------------------------------------------------------------------------------
1 | setConnectionService('db_log'); // Connection service for log database
36 | }
37 |
38 | /**
39 | * Returns table name mapped in the model.
40 | *
41 | * @return string
42 | */
43 | public function getSource()
44 | {
45 | return 'logs';
46 | }
47 |
48 | /**
49 | * Allows to query a set of records that match the specified conditions
50 | *
51 | * @param mixed $parameters
52 | * @return Logs[]
53 | */
54 | public static function find($parameters = null)
55 | {
56 | return parent::find($parameters);
57 | }
58 |
59 | /**
60 | * Allows to query the first record that match the specified conditions
61 | *
62 | * @param mixed $parameters
63 | * @return Logs
64 | */
65 | public static function findFirst($parameters = null)
66 | {
67 | return parent::findFirst($parameters);
68 | }
69 |
70 | /**
71 | * Independent Column Mapping.
72 | * Keys are the real names in the table and the values their names in the application
73 | *
74 | * @return array
75 | */
76 | public function columnMap()
77 | {
78 | return array(
79 | 'id' => 'id',
80 | 'username' => 'username',
81 | 'route' => 'route',
82 | 'date' => 'date',
83 | );
84 | }
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/models/Users.php:
--------------------------------------------------------------------------------
1 | setConnectionService('db');
111 | }
112 |
113 | /**
114 | * Validations and business logic
115 | *
116 | * @return boolean
117 | */
118 | public function validation()
119 | {
120 | $validator = new Validation();
121 |
122 | $validator->add(
123 | 'email',
124 | new EmailValidator()
125 | );
126 |
127 | return $this->validate($validator);
128 | }
129 |
130 | /**
131 | * Returns table name mapped in the model.
132 | *
133 | * @return string
134 | */
135 | public function getSource()
136 | {
137 | return 'users';
138 | }
139 |
140 | /**
141 | * Allows to query a set of records that match the specified conditions
142 | *
143 | * @param mixed $parameters
144 | * @return Users[]
145 | */
146 | public static function find($parameters = null)
147 | {
148 | return parent::find($parameters);
149 | }
150 |
151 | /**
152 | * Allows to query the first record that match the specified conditions
153 | *
154 | * @param mixed $parameters
155 | * @return Users
156 | */
157 | public static function findFirst($parameters = null)
158 | {
159 | return parent::findFirst($parameters);
160 | }
161 |
162 | /**
163 | * Independent Column Mapping.
164 | * Keys are the real names in the table and the values their names in the application
165 | *
166 | * @return array
167 | */
168 | public function columnMap()
169 | {
170 | return array(
171 | 'id' => 'id',
172 | 'username' => 'username',
173 | 'password' => 'password',
174 | 'firstname' => 'firstname',
175 | 'lastname' => 'lastname',
176 | 'level' => 'level',
177 | 'email' => 'email',
178 | 'phone' => 'phone',
179 | 'mobile' => 'mobile',
180 | 'address' => 'address',
181 | 'country' => 'country',
182 | 'city' => 'city',
183 | 'birthday' => 'birthday',
184 | 'authorised' => 'authorised',
185 | 'block_expires' => 'block_expires',
186 | 'login_attempts' => 'login_attempts',
187 | );
188 | }
189 |
190 | }
191 |
--------------------------------------------------------------------------------
/models/UsersAccess.php:
--------------------------------------------------------------------------------
1 | setConnectionService('db');
54 | }
55 |
56 | /**
57 | * Returns table name mapped in the model.
58 | *
59 | * @return string
60 | */
61 | public function getSource()
62 | {
63 | return 'users_access';
64 | }
65 |
66 | /**
67 | * Allows to query a set of records that match the specified conditions
68 | *
69 | * @param mixed $parameters
70 | * @return UsersAccess[]
71 | */
72 | public static function find($parameters = null)
73 | {
74 | return parent::find($parameters);
75 | }
76 |
77 | /**
78 | * Allows to query the first record that match the specified conditions
79 | *
80 | * @param mixed $parameters
81 | * @return UsersAccess
82 | */
83 | public static function findFirst($parameters = null)
84 | {
85 | return parent::findFirst($parameters);
86 | }
87 |
88 | /**
89 | * Independent Column Mapping.
90 | * Keys are the real names in the table and the values their names in the application
91 | *
92 | * @return array
93 | */
94 | public function columnMap()
95 | {
96 | return array(
97 | 'id' => 'id',
98 | 'username' => 'username',
99 | 'ip' => 'ip',
100 | 'domain' => 'domain',
101 | 'country' => 'country',
102 | 'browser' => 'browser',
103 | 'date' => 'date',
104 | );
105 | }
106 |
107 | }
108 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 | ./tests/
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/public/.htaccess:
--------------------------------------------------------------------------------
1 | AddDefaultCharset UTF-8
2 |
3 | ############################################################
4 | # Possible values: development, staging, production #
5 | # Change value and upload ONCE to your server #
6 | # AVOID re-uploading when deployment, things will go crazy #
7 | ############################################################
8 | SetEnv APPLICATION_ENV "development"
9 |
10 | #CORS
11 | Header always set Access-Control-Allow-Origin: "*"
12 | Header always set Access-Control-Allow-Headers: "Origin, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Authorization, Accept, X-Accept-Charset, X-Accept, Client-Security-Token"
13 | Header always set Access-Control-Allow-Methods: "POST, GET, PUT, OPTIONS, PATCH, DELETE"
14 | Header always set Access-Control-Allow-Credentials: "true"
15 | Header always set Access-Control-Max-Age: "1000"
16 |
17 | #CORS
18 | RewriteCond %{REQUEST_METHOD} OPTIONS
19 | RewriteRule ^(.*)$ $1 [R=200,L]
20 |
21 | #IMPORTANT TO GET JWT FROM HEADERS
22 | RewriteCond %{HTTP:Authorization} ^(.*)
23 | RewriteRule .* - [e=HTTP_AUTHORIZATION:%1]
24 |
25 |
26 | RewriteEngine On
27 | RewriteCond %{REQUEST_FILENAME} !-f
28 | RewriteRule ^(.*)$ index.php?_url=/$1 [QSA,L]
29 |
30 |
31 |
32 | SetOutputFilter DEFLATE
33 |
34 |
35 |
36 | SetOutputFilter DEFLATE
37 |
38 |
39 |
40 | SetOutputFilter DEFLATE
41 |
42 |
43 |
44 | SetOutputFilter DEFLATE
45 |
46 |
47 |
48 | SetOutputFilter DEFLATE
49 |
50 |
51 |
52 | SetOutputFilter DEFLATE
53 |
54 |
55 |
56 | SetOutputFilter DEFLATE
57 |
58 |
59 |
60 | SetOutputFilter DEFLATE
61 |
62 |
63 |
64 | SetOutputFilter DEFLATE
65 |
66 |
67 |
68 | SetOutputFilter DEFLATE
69 |
70 |
71 |
72 | SetOutputFilter DEFLATE
73 |
74 |
75 |
76 | SetOutputFilter DEFLATE
77 |
78 |
79 |
80 | SetOutputFilter DEFLATE
81 |
82 |
--------------------------------------------------------------------------------
/public/index.php:
--------------------------------------------------------------------------------
1 | listen();
15 | }
16 |
17 | define('APP_PATH', realpath('..'));
18 |
19 | try {
20 |
21 | require __DIR__ . '/../vendor/autoload.php';
22 |
23 | /*
24 | * Read the configuration
25 | */
26 | $config = include __DIR__ . '/../config/config.php';
27 |
28 | /**
29 | * Include Autoloader.
30 | */
31 | include APP_PATH . '/config/loader.php';
32 |
33 | /**
34 | * Include Services.
35 | */
36 | include APP_PATH . '/config/services.php';
37 |
38 | /**
39 | * Include ACL.
40 | */
41 | include APP_PATH . '/config/acl.php';
42 |
43 | /*
44 | * Starting the application
45 | * Assign service locator to the application
46 | */
47 | $app = new Micro($di);
48 |
49 | /**
50 | * Include Application.
51 | */
52 | include APP_PATH . '/app.php';
53 |
54 | /*
55 | * Handle the request
56 | */
57 | $app->handle();
58 |
59 | } catch (\Exception $e) {
60 | if (APPLICATION_ENV === 'development') {
61 | print_r($e->getMessage() . '
');
62 | print_r('
' . $e->getTraceAsString() . '
');
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/schemas/myproject.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.6.35)
9 | # Database: myproject
10 | # Generation Time: 2017-09-05 02:02:50 +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 cities
24 | # ------------------------------------------------------------
25 |
26 | DROP TABLE IF EXISTS `cities`;
27 |
28 | CREATE TABLE `cities` (
29 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
30 | `name` varchar(250) DEFAULT NULL,
31 | `country` varchar(250) DEFAULT NULL,
32 | PRIMARY KEY (`id`),
33 | UNIQUE KEY `name` (`name`),
34 | KEY `country` (`country`)
35 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
36 |
37 | LOCK TABLES `cities` WRITE;
38 | /*!40000 ALTER TABLE `cities` DISABLE KEYS */;
39 |
40 | INSERT INTO `cities` (`id`, `name`, `country`)
41 | VALUES
42 | (1,'Bogotá','Colombia'),
43 | (2,'Medellín','Colombia'),
44 | (3,'Cali','Colombia'),
45 | (4,'Barranquilla','Colombia'),
46 | (5,'Cartagena','Colombia'),
47 | (6,'Bucaramanga','Colombia');
48 |
49 | /*!40000 ALTER TABLE `cities` ENABLE KEYS */;
50 | UNLOCK TABLES;
51 |
52 |
53 | # Dump of table users
54 | # ------------------------------------------------------------
55 |
56 | DROP TABLE IF EXISTS `users`;
57 |
58 | CREATE TABLE `users` (
59 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
60 | `username` varchar(45) NOT NULL,
61 | `password` varchar(255) NOT NULL,
62 | `firstname` varchar(255) DEFAULT NULL,
63 | `lastname` varchar(255) DEFAULT NULL,
64 | `level` varchar(45) NOT NULL,
65 | `email` varchar(150) DEFAULT NULL,
66 | `phone` varchar(150) DEFAULT NULL,
67 | `mobile` varchar(150) DEFAULT NULL,
68 | `address` text,
69 | `country` varchar(255) DEFAULT NULL,
70 | `city` varchar(255) DEFAULT NULL,
71 | `birthday` date DEFAULT NULL,
72 | `authorised` tinyint(1) DEFAULT '0',
73 | `block_expires` datetime DEFAULT NULL,
74 | `login_attempts` int(10) unsigned DEFAULT '0',
75 | PRIMARY KEY (`id`) USING BTREE,
76 | UNIQUE KEY `username_2` (`username`),
77 | KEY `email` (`email`(100)),
78 | KEY `username` (`username`) USING BTREE,
79 | KEY `level` (`level`)
80 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
81 |
82 | LOCK TABLES `users` WRITE;
83 | /*!40000 ALTER TABLE `users` DISABLE KEYS */;
84 |
85 | INSERT INTO `users` (`id`, `username`, `password`, `firstname`, `lastname`, `level`, `email`, `phone`, `mobile`, `address`, `country`, `city`, `birthday`, `authorised`, `block_expires`, `login_attempts`)
86 | VALUES
87 | (1,'admin','$2y$10$QS.T0VwA5/a78y2s6m2Yiulu2bhGPzH/W1ay7QslX830PO4RTCVj.','Super','Admin','Superuser','my@email.com','123123','123123123','My address','Colombia','Bucaramanga','1980-01-01',1,NULL,0);
88 |
89 | /*!40000 ALTER TABLE `users` ENABLE KEYS */;
90 | UNLOCK TABLES;
91 |
92 |
93 | # Dump of table users_access
94 | # ------------------------------------------------------------
95 |
96 | DROP TABLE IF EXISTS `users_access`;
97 |
98 | CREATE TABLE `users_access` (
99 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
100 | `username` varchar(45) DEFAULT NULL,
101 | `ip` varchar(250) DEFAULT NULL,
102 | `browser` text,
103 | `date` datetime DEFAULT NULL,
104 | `domain` varchar(255) DEFAULT NULL,
105 | `country` varchar(11) DEFAULT NULL,
106 | PRIMARY KEY (`id`)
107 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
108 |
109 |
110 |
111 |
112 | /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
113 | /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
114 | /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
115 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
116 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
117 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
118 |
--------------------------------------------------------------------------------
/schemas/myproject_log.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.6.35)
9 | # Database: myproject_log
10 | # Generation Time: 2017-09-05 02:02:58 +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 logs
24 | # ------------------------------------------------------------
25 |
26 | DROP TABLE IF EXISTS `logs`;
27 |
28 | CREATE TABLE `logs` (
29 | `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
30 | `username` varchar(45) DEFAULT NULL,
31 | `route` varchar(255) DEFAULT NULL,
32 | `date` datetime DEFAULT NULL,
33 | PRIMARY KEY (`id`)
34 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
35 |
36 |
37 |
38 |
39 | /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
40 | /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
41 | /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
42 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
43 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
44 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
45 |
--------------------------------------------------------------------------------
/tests/0IndexTest.php:
--------------------------------------------------------------------------------
1 | http = new GuzzleHttp\Client(['base_uri' => 'http://api.myproject.local/', 'http_errors' => false]);
12 | }
13 |
14 | public function tearDown()
15 | {
16 | $this->http = null;
17 | }
18 |
19 | protected function postLogin($credentials)
20 | {
21 | $response = $this->http->request('POST', 'authenticate', [
22 | 'headers' => [
23 | 'Authorization' => 'Basic ' . base64_encode($credentials),
24 | ]]);
25 | return $response;
26 | }
27 |
28 | protected function getJSON($response)
29 | {
30 | $contentType = $response->getHeaders()["Content-Type"][0];
31 | $this->assertEquals("application/json; charset=UTF-8", $contentType);
32 | $json = json_decode($response->getBody(), true);
33 | return $json;
34 | }
35 |
36 | public function testLogin()
37 | {
38 | $response = $this->postLogin('admin:admin1234');
39 | $this->assertEquals(200, $response->getStatusCode());
40 | $json = $this->getJSON($response);
41 | $this->assertEquals('common.SUCCESSFUL_REQUEST', $json['messages']);
42 | $this->assertEquals('1', $json['data']['user']['id']);
43 | $this->assertEquals('admin', $json['data']['user']['username']);
44 | }
45 |
46 | public function testLoginUserNotRegistered()
47 | {
48 | $response = $this->postLogin('admin1234:admin1234');
49 | $this->assertEquals(404, $response->getStatusCode());
50 | $json = $this->getJSON($response);
51 | $this->assertEquals('login.USER_IS_NOT_REGISTERED', $json['messages']);
52 | }
53 |
54 | public function testLoginWrongPassword()
55 | {
56 | $response = $this->postLogin('admin:admin12345');
57 | $this->assertEquals(400, $response->getStatusCode());
58 | $json = $this->getJSON($response);
59 | $this->assertEquals('login.WRONG_USER_PASSWORD', $json['messages']);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/tests/1CitiesTest.php:
--------------------------------------------------------------------------------
1 | http = new GuzzleHttp\Client(['base_uri' => 'http://api.myproject.local/', 'http_errors' => false]);
12 | $this->credentials = $this->getToken();
13 | }
14 |
15 | public function tearDown()
16 | {
17 | $this->http = null;
18 | }
19 |
20 | protected function getJSON($response)
21 | {
22 | $contentType = $response->getHeaders()["Content-Type"][0];
23 | $this->assertEquals("application/json; charset=UTF-8", $contentType);
24 | $json = json_decode($response->getBody(), true);
25 | return $json;
26 | }
27 |
28 | protected function getToken()
29 | {
30 | $credentials = base64_encode('admin:admin1234');
31 |
32 | $response = $this->http->request('POST', 'authenticate', [
33 | 'headers' => [
34 | 'Authorization' => 'Basic ' . $credentials,
35 | ]]);
36 |
37 | $this->assertEquals(200, $response->getStatusCode());
38 | $json = $this->getJSON($response);
39 | $this->assertEquals('common.SUCCESSFUL_REQUEST', $json['messages']);
40 | $this->assertEquals('1', $json['data']['user']['id']);
41 | $this->assertEquals('admin', $json['data']['user']['username']);
42 | return $json['data']['token'];
43 | }
44 |
45 | protected function buildGetRequest($endpoint)
46 | {
47 | $response = $this->http->request('GET', $endpoint, [
48 | 'headers' => [
49 | 'Authorization' => 'Bearer ' . $this->credentials,
50 | ]]);
51 | return $response;
52 | }
53 |
54 | protected function buildGetOrDeleteRequestById($method, $endpoint, $id)
55 | {
56 | $response = $this->http->request($method, $endpoint . $id, [
57 | 'headers' => [
58 | 'Authorization' => 'Bearer ' . $this->credentials,
59 | ]]);
60 | return $response;
61 | }
62 |
63 | protected function buildPostRequest($endpoint, $params)
64 | {
65 | $response = $this->http->request('POST', $endpoint, [
66 | 'headers' => [
67 | 'Authorization' => 'Bearer ' . $this->credentials,
68 | ],
69 | 'form_params' => $params]);
70 | return $response;
71 | }
72 |
73 | protected function buildPatchRequest($endpoint, $params, $id)
74 | {
75 | $response = $this->http->request('PATCH', $endpoint . $id, [
76 | 'headers' => [
77 | 'Authorization' => 'Bearer ' . $this->credentials,
78 | ],
79 | 'form_params' => $params]);
80 | return $response;
81 | }
82 |
83 | protected function deleteItem($id)
84 | {
85 | $response = $this->buildGetOrDeleteRequestById('DELETE', 'cities/delete/', $id);
86 | $this->assertEquals(200, $response->getStatusCode());
87 | $json = $this->getJSON($response);
88 | $this->assertEquals('common.DELETED_SUCCESSFULLY', $json['messages']);
89 | }
90 |
91 | protected function createItem()
92 | {
93 | $params = [
94 | 'name' => 'Girón',
95 | 'country' => 'Colombia',
96 | ];
97 | $response = $this->buildPostRequest('cities/create', $params);
98 | $this->assertEquals(201, $response->getStatusCode());
99 | $json = $this->getJSON($response);
100 | $this->assertEquals('common.CREATED_SUCCESSFULLY', $json['messages']);
101 | $id = $json['data']['id'];
102 | return $id;
103 | }
104 |
105 | public function testGetCities()
106 | {
107 | $response = $this->buildGetRequest('cities');
108 | $this->assertEquals(200, $response->getStatusCode());
109 | $json = $this->getJSON($response);
110 | $this->assertEquals('common.SUCCESSFUL_REQUEST', $json['messages']);
111 | }
112 |
113 | public function testCreateCity()
114 | {
115 | $id = $this->createItem();
116 | $this->deleteItem($id);
117 | }
118 |
119 | public function testCannotCreateDuplicatedCity()
120 | {
121 | $params = [
122 | 'name' => 'Bogotá',
123 | 'country' => 'Colombia',
124 | ];
125 | $response = $this->buildPostRequest('cities/create', $params);
126 | $this->assertEquals(409, $response->getStatusCode());
127 | $json = $this->getJSON($response);
128 | $this->assertEquals('common.THERE_IS_ALREADY_A_RECORD_WITH_THAT_NAME', $json['messages']);
129 | }
130 |
131 | public function testGetCityById()
132 | {
133 | $response = $this->buildGetOrDeleteRequestById('GET', 'cities/get/', 1);
134 | $this->assertEquals(200, $response->getStatusCode());
135 | $json = $this->getJSON($response);
136 | $this->assertEquals('Bogotá', $json['data']['name']);
137 | }
138 |
139 | public function testUpdateCity()
140 | {
141 | $id = $this->createItem();
142 | $params = [
143 | 'name' => 'Girón2',
144 | 'country' => 'Colombia2',
145 | ];
146 | $response = $this->buildPatchRequest('cities/update/', $params, $id);
147 | $this->assertEquals(200, $response->getStatusCode());
148 | $json = $this->getJSON($response);
149 | $this->assertEquals('Girón2', $json['data']['name']);
150 | $this->deleteItem($id);
151 | }
152 |
153 | public function testCannotUpdateCityThatAlreadyExists()
154 | {
155 | $id = $this->createItem();
156 | $params = [
157 | 'name' => 'Bogotá',
158 | 'country' => 'Colombia',
159 | ];
160 | $response = $this->buildPatchRequest('cities/update/', $params, $id);
161 | $this->assertEquals(409, $response->getStatusCode());
162 | $json = $this->getJSON($response);
163 | $this->assertEquals('common.THERE_IS_ALREADY_A_RECORD_WITH_THAT_NAME', $json['messages']);
164 | $this->deleteItem($id);
165 | }
166 |
167 | public function testDeleteCity()
168 | {
169 | $id = $this->createItem();
170 | $this->deleteItem($id);
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/tests/2UsersTest.php:
--------------------------------------------------------------------------------
1 | http = new GuzzleHttp\Client(['base_uri' => 'http://api.myproject.local/', 'http_errors' => false]);
12 | $this->credentials = $this->getToken();
13 | }
14 |
15 | public function tearDown()
16 | {
17 | $this->http = null;
18 | }
19 |
20 | protected function getJSON($response)
21 | {
22 | $contentType = $response->getHeaders()["Content-Type"][0];
23 | $this->assertEquals("application/json; charset=UTF-8", $contentType);
24 | $json = json_decode($response->getBody(), true);
25 | return $json;
26 | }
27 |
28 | protected function getToken()
29 | {
30 | $credentials = base64_encode('admin:admin1234');
31 |
32 | $response = $this->http->request('POST', 'authenticate', [
33 | 'headers' => [
34 | 'Authorization' => 'Basic ' . $credentials,
35 | ]]);
36 |
37 | $this->assertEquals(200, $response->getStatusCode());
38 | $json = $this->getJSON($response);
39 | $this->assertEquals('common.SUCCESSFUL_REQUEST', $json['messages']);
40 | $this->assertEquals('1', $json['data']['user']['id']);
41 | $this->assertEquals('admin', $json['data']['user']['username']);
42 | return $json['data']['token'];
43 | }
44 |
45 | protected function buildGetRequest($endpoint)
46 | {
47 | $response = $this->http->request('GET', $endpoint, [
48 | 'headers' => [
49 | 'Authorization' => 'Bearer ' . $this->credentials,
50 | ]]);
51 | return $response;
52 | }
53 |
54 | protected function buildGetOrDeleteRequestById($method, $endpoint, $id)
55 | {
56 | $response = $this->http->request($method, $endpoint . $id, [
57 | 'headers' => [
58 | 'Authorization' => 'Bearer ' . $this->credentials,
59 | ]]);
60 | return $response;
61 | }
62 |
63 | protected function buildPostRequest($endpoint, $params)
64 | {
65 | $response = $this->http->request('POST', $endpoint, [
66 | 'headers' => [
67 | 'Authorization' => 'Bearer ' . $this->credentials,
68 | ],
69 | 'form_params' => $params]);
70 | return $response;
71 | }
72 |
73 | protected function buildPatchRequest($endpoint, $params, $id)
74 | {
75 | $response = $this->http->request('PATCH', $endpoint . $id, [
76 | 'headers' => [
77 | 'Authorization' => 'Bearer ' . $this->credentials,
78 | ],
79 | 'form_params' => $params]);
80 | return $response;
81 | }
82 |
83 | protected function deleteItem($id)
84 | {
85 | $response = $this->buildGetOrDeleteRequestById('DELETE', 'users/delete/', $id);
86 | $this->assertEquals(200, $response->getStatusCode());
87 | $json = $this->getJSON($response);
88 | $this->assertEquals('common.DELETED_SUCCESSFULLY', $json['messages']);
89 | }
90 |
91 | protected function createItem()
92 | {
93 | $params = [
94 | 'email' => 'another@email.com',
95 | 'new_password' => 'test123',
96 | 'username' => 'admintest',
97 | 'firstname' => 'My name',
98 | 'lastname' => 'My last name',
99 | 'level' => 'Superuser',
100 | 'phone' => '12312312',
101 | 'mobile' => '31312312',
102 | 'address' => 'Calle 10',
103 | 'city' => 'Bogotá',
104 | 'country' => 'Colombia',
105 | 'birthday' => '1979-01-01',
106 | 'authorised' => '1',
107 | ];
108 | $response = $this->buildPostRequest('users/create', $params);
109 | $this->assertEquals(201, $response->getStatusCode());
110 | $json = $this->getJSON($response);
111 | $this->assertEquals('common.CREATED_SUCCESSFULLY', $json['messages']);
112 | $id = $json['data']['id'];
113 | return $id;
114 | }
115 |
116 | public function testGetUsers()
117 | {
118 | $response = $this->buildGetRequest('users');
119 | $this->assertEquals(200, $response->getStatusCode());
120 | $json = $this->getJSON($response);
121 | $this->assertEquals('common.SUCCESSFUL_REQUEST', $json['messages']);
122 | }
123 |
124 | public function testCreateUser()
125 | {
126 | $id = $this->createItem();
127 | $this->deleteItem($id);
128 | }
129 |
130 | public function testCannotCreateDuplicatedUser()
131 | {
132 | $id = $this->createItem();
133 | $params = [
134 | 'email' => 'another@email.com',
135 | 'new_password' => 'test123',
136 | 'username' => 'admintest',
137 | 'firstname' => 'My name',
138 | 'lastname' => 'My last name',
139 | 'level' => 'Superuser',
140 | 'phone' => '12312312',
141 | 'mobile' => '31312312',
142 | 'address' => 'Calle 10',
143 | 'city' => 'Bogotá',
144 | 'country' => 'Colombia',
145 | 'birthday' => '1979-01-01',
146 | 'authorised' => '1',
147 | ];
148 | $response = $this->buildPostRequest('users/create', $params);
149 | $this->assertEquals(409, $response->getStatusCode());
150 | $json = $this->getJSON($response);
151 | $this->assertEquals('profile.ANOTHER_USER_ALREADY_REGISTERED_WITH_THIS_USERNAME', $json['messages']);
152 | $this->deleteItem($id);
153 | }
154 |
155 | public function testGetUserById()
156 | {
157 | $response = $this->buildGetOrDeleteRequestById('GET', 'users/get/', 1);
158 | $this->assertEquals(200, $response->getStatusCode());
159 | $json = $this->getJSON($response);
160 | $this->assertEquals('1', $json['data']['id']);
161 | }
162 |
163 | public function testUpdateUser()
164 | {
165 | $id = $this->createItem();
166 | $params = [
167 | 'email' => 'onemore@email.com',
168 | 'new_password' => 'test123',
169 | 'username' => 'admintest',
170 | 'firstname' => 'Other name',
171 | 'lastname' => 'Other last name',
172 | 'level' => 'Superuser',
173 | 'phone' => '7654333',
174 | 'mobile' => '234568',
175 | 'address' => 'Calle 11',
176 | 'city' => 'Bogotá',
177 | 'country' => 'Colombia',
178 | 'birthday' => '1979-01-01',
179 | 'authorised' => '1',
180 | ];
181 | $response = $this->buildPatchRequest('users/update/', $params, $id);
182 | $this->assertEquals(200, $response->getStatusCode());
183 | $json = $this->getJSON($response);
184 | $this->assertEquals('onemore@email.com', $json['data']['email']);
185 | $this->deleteItem($id);
186 | }
187 |
188 | public function testDeleteUser()
189 | {
190 | $id = $this->createItem();
191 | $this->deleteItem($id);
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/tests/3ProfileTest.php:
--------------------------------------------------------------------------------
1 | http = new GuzzleHttp\Client(['base_uri' => 'http://api.myproject.local/', 'http_errors' => false]);
12 | $this->credentials = $this->getToken();
13 | }
14 |
15 | public function tearDown()
16 | {
17 | $this->http = null;
18 | }
19 |
20 | protected function getJSON($response)
21 | {
22 | $contentType = $response->getHeaders()["Content-Type"][0];
23 | $this->assertEquals("application/json; charset=UTF-8", $contentType);
24 | $json = json_decode($response->getBody(), true);
25 | return $json;
26 | }
27 |
28 | protected function getToken()
29 | {
30 | $credentials = base64_encode('admin:admin1234');
31 |
32 | $response = $this->http->request('POST', 'authenticate', [
33 | 'headers' => [
34 | 'Authorization' => 'Basic ' . $credentials,
35 | ]]);
36 |
37 | $this->assertEquals(200, $response->getStatusCode());
38 | $json = $this->getJSON($response);
39 | $this->assertEquals('common.SUCCESSFUL_REQUEST', $json['messages']);
40 | $this->assertEquals('1', $json['data']['user']['id']);
41 | $this->assertEquals('admin', $json['data']['user']['username']);
42 | return $json['data']['token'];
43 | }
44 |
45 | protected function buildGetRequest($endpoint)
46 | {
47 | $response = $this->http->request('GET', $endpoint, [
48 | 'headers' => [
49 | 'Authorization' => 'Bearer ' . $this->credentials,
50 | ]]);
51 | return $response;
52 | }
53 |
54 | protected function buildPatchRequest($endpoint, $params)
55 | {
56 | $response = $this->http->request('PATCH', $endpoint, [
57 | 'headers' => [
58 | 'Authorization' => 'Bearer ' . $this->credentials,
59 | ],
60 | 'form_params' => $params]);
61 | return $response;
62 | }
63 |
64 | public function testGetProfile()
65 | {
66 | $response = $this->buildGetRequest('profile');
67 | $this->assertEquals(200, $response->getStatusCode());
68 | $json = $this->getJSON($response);
69 | $this->assertEquals('1', $json['data']['id']);
70 | $this->assertEquals('admin', $json['data']['username']);
71 | }
72 |
73 | public function testUpdateProfile()
74 | {
75 | $params = [
76 | 'email' => 'asd@asd.com',
77 | 'firstname' => 'Other name',
78 | 'lastname' => 'Lastname',
79 | 'phone' => '12312312',
80 | 'mobile' => '31312312',
81 | 'address' => 'Calle 10',
82 | 'city' => 'Bogotá',
83 | 'country' => 'Colombia',
84 | 'birthday' => '1979-01-01',
85 | ];
86 | $response = $this->buildPatchRequest('profile/update', $params);
87 | $this->assertEquals(200, $response->getStatusCode());
88 | $json = $this->getJSON($response);
89 | $this->assertEquals('1', $json['data']['id']);
90 | $this->assertEquals('admin', $json['data']['username']);
91 | $this->assertEquals('Other name', $json['data']['firstname']);
92 | }
93 |
94 | public function testChangePassword()
95 | {
96 | $params = [
97 | 'current_password' => 'admin1234',
98 | 'new_password' => 'admin12345',
99 | ];
100 | $response = $this->buildPatchRequest('profile/change-password', $params);
101 | $this->assertEquals(200, $response->getStatusCode());
102 | $json = $this->getJSON($response);
103 | $this->assertEquals('change-password.PASSWORD_SUCCESSFULLY_UPDATED', $json['messages']);
104 |
105 | // restore default password
106 | $params = [
107 | 'current_password' => 'admin12345',
108 | 'new_password' => 'admin1234',
109 | ];
110 | $response = $this->buildPatchRequest('profile/change-password', $params);
111 | $this->assertEquals(200, $response->getStatusCode());
112 | $json = $this->getJSON($response);
113 | $this->assertEquals('change-password.PASSWORD_SUCCESSFULLY_UPDATED', $json['messages']);
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/views/404.phtml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | API
8 |
9 |
10 |
11 |
12 |
13 |
16 |
17 |
Sorry, requested page does not exists.
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/views/index.phtml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | API
8 |
9 |
10 |
11 |
12 |
13 |
16 |
17 |
This is our API
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------