diff --git a/.gitignore b/.gitignore index 00cbbdf..6af9c4d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,59 +1,12 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (http://nodejs.org/api/addons.html) -build/Release +# IDE +.vscode # Dependency directories node_modules/ -jspm_packages/ - -# Typescript v1 declaration files -typings/ - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity -# dotenv environment variables file -.env +# Builds +dist/ +# Realm Database +database/ +realm-object-server/ diff --git a/README.md b/README.md index 7afc6c8..c72c471 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,36 @@ -# users-api -Persistent RESTful API with basic CRUDL operations to manage the dataset provided in https://gist.githubusercontent.com/jasonmadigan/009c15b5dc4b4eccd32b/raw/34759c44e77d2f3515e20ed561cdd7a5e8345585/users.json +# Users RESTful API[ ![Codeship Status for josemigallas/users-api](https://app.codeship.com/projects/017055e0-4b1b-0135-9499-6a83b1829c88/status?branch=master)](https://app.codeship.com/projects/232759) + +> :warning: :alien: Due to [this Request issue](https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18074) transpilation is failing. It is necessary to fix the bug manually in node_modules/@types/request/typings.d.ts first time after doing an npm install. This is what's causing the failure at deployment on Heroku and testing with Codeship. + +This API allows all CRUDL operations over the dataset hosted in https://gist.githubusercontent.com/jasonmadigan/009c15b5dc4b4eccd32b/raw/34759c44e77d2f3515e20ed561cdd7a5e8345585/users.json. + +## Reference +You can find a complete reference to the API in [our awesome Apiary](http://docs.users226.apiary.io/#). + +## Usage +For normal production behaviour, first set ENV_NODE and then simply install dependencies and start it up by running the next command. Typescript transpilation is taken care of right after installation. +``` +$ export ENV_NODE="production" && npm install && npm start +``` +You should see this message: +``` +Users API listening on port 3000 in production mode +``` +In case you would like a fresh start, cleaning all generated files files run: +``` +$ npm run clean +``` + +## Test Suite +Alongside unit tests, the suite also performs some testing over the API routes. For this, it is needed to start the app manually in development mode. Open a terminal and run: +``` +$ export ENV_NODE="development" && npm install && npm start +``` +You should see this message: +``` +Users API listening on port 3000 in development mode +``` +Then, open a different terminal and simply run: +``` +$ npm test +``` diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..4468a53 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1443 @@ +{ + "name": "users-api", + "version": "0.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/express": { + "version": "4.0.36", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.0.36.tgz", + "integrity": "sha512-bT9q2eqH/E72AGBQKT50dh6AXzheTqigGZ1GwDiwmx7vfHff0bZOrvUWjvGpNWPNkRmX1vDF6wonG6rlpBHb1A==", + "dev": true, + "requires": { + "@types/express-serve-static-core": "4.0.49", + "@types/serve-static": "1.7.31" + } + }, + "@types/express-serve-static-core": { + "version": "4.0.49", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.0.49.tgz", + "integrity": "sha512-b7mVHoURu1xaP/V6xw1sYwyv9V0EZ7euyi+sdnbnTZxEkAh4/hzPsI6Eflq+ZzHQ/Tgl7l16Jz+0oz8F46MLnA==", + "dev": true, + "requires": { + "@types/node": "8.0.13" + } + }, + "@types/form-data": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz", + "integrity": "sha1-yayFsqX9GENbjIXZ7LUObWyJP/g=", + "dev": true, + "requires": { + "@types/node": "8.0.13" + } + }, + "@types/jasmine": { + "version": "2.5.53", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.5.53.tgz", + "integrity": "sha512-2YNL0jXYuN7w07mb1sMZQ6T6zOvGi83v8UbjhBZ8mhvI1VkQ2STU9XOrTFyvWswMyh5rW1evS+e7qltYJvTqPA==", + "dev": true + }, + "@types/mime": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.1.tgz", + "integrity": "sha512-rek8twk9C58gHYqIrUlJsx8NQMhlxqHzln9Z9ODqiNgv3/s+ZwIrfr+djqzsnVM12xe9hL98iJ20lj2RvCBv6A==", + "dev": true + }, + "@types/node": { + "version": "8.0.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.13.tgz", + "integrity": "sha512-Y3EAG7VA7NVNbZek/fjJtILnmTk/ZfpJuWZGDBqDZ1dVIxgJJJ82fXPW7pKnqyV9CD/9bcPOCi7eErUqGMHOrA==" + }, + "@types/realm": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/realm/-/realm-1.0.3.tgz", + "integrity": "sha1-iURUrP583P1oBWNt0VC4rFCEh9I=", + "dev": true + }, + "@types/request": { + "version": "0.0.47", + "resolved": "https://registry.npmjs.org/@types/request/-/request-0.0.47.tgz", + "integrity": "sha512-RAG3/f+dHwlC8R17loSB29zrKvEOQvTGUtgrNjKE8nXUkYxtV4IzEGO1ft+JMX0aa06pi1ed6b826e81eiwYdw==", + "dev": true, + "requires": { + "@types/form-data": "0.0.33", + "@types/node": "8.0.13" + } + }, + "@types/request-promise-native": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/request-promise-native/-/request-promise-native-1.0.6.tgz", + "integrity": "sha512-SbsRP/CnMW09mF8mIfOBgQzSJJKZkSbXIC3g1RSKgZSGNz5MK7dedxImr30ekjY71WzRRtQh4VwiAaHA3+Uu8w==", + "dev": true, + "requires": { + "@types/request": "0.0.47" + } + }, + "@types/serve-static": { + "version": "1.7.31", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.7.31.tgz", + "integrity": "sha1-FUVt6NmNa0z/Mb5savdJKuY/Uho=", + "dev": true, + "requires": { + "@types/express-serve-static-core": "4.0.49", + "@types/mime": "1.3.1" + } + }, + "abbrev": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz", + "integrity": "sha1-0FVMIlZjbi9W58LlrRg/hZQo2B8=" + }, + "accepts": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", + "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", + "requires": { + "mime-types": "2.1.15", + "negotiator": "0.6.1" + } + }, + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "aproba": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.2.tgz", + "integrity": "sha512-ZpYajIfO0j2cOFTO955KUMIKNmj6zhX8kVztMAxFsDaMwz+9Z9SV0uou2pC9HJqcfpffOsjnbrDMvkNy+9RXPw==" + }, + "are-we-there-yet": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", + "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.3.3" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + }, + "assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-sign2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=" + }, + "aws4": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "requires": { + "inherits": "2.0.3" + } + }, + "body-parser": { + "version": "1.17.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.17.2.tgz", + "integrity": "sha1-+IkqvI+eYn1Crtr7yma/WrmRBO4=", + "requires": { + "bytes": "2.4.0", + "content-type": "1.0.2", + "debug": "2.6.7", + "depd": "1.1.0", + "http-errors": "1.6.1", + "iconv-lite": "0.4.15", + "on-finished": "2.3.0", + "qs": "6.4.0", + "raw-body": "2.2.0", + "type-is": "1.6.15" + } + }, + "boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "requires": { + "hoek": "2.16.3" + } + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "bytes": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz", + "integrity": "sha1-fZcZb51br39pNeJZhVSe3SpsIzk=" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "combined-stream": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "requires": { + "delayed-stream": "1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", + "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.3", + "typedarray": "0.0.6" + } + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" + }, + "content-type": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz", + "integrity": "sha1-t9ETrueo3Se9IRM8TcJSnfFyHu0=" + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cryptiles": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "requires": { + "boom": "2.10.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "debug": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.7.tgz", + "integrity": "sha1-krrR9tBbu2u6Isyoi80OyJTChh4=", + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", + "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "depd": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz", + "integrity": "sha1-4b2Cxqq2ztlluXuIsX7T5SjKGMM=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", + "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=" + }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "requires": { + "iconv-lite": "0.4.15" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.0.tgz", + "integrity": "sha1-b2Ma7zNtbEY2K1F2QETOIWvjwFE=" + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "express": { + "version": "4.15.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.15.3.tgz", + "integrity": "sha1-urZdDwOqgMNYQIly/HAPkWlEtmI=", + "requires": { + "accepts": "1.3.3", + "array-flatten": "1.1.1", + "content-disposition": "0.5.2", + "content-type": "1.0.2", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.7", + "depd": "1.1.0", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "etag": "1.8.0", + "finalhandler": "1.0.3", + "fresh": "0.5.0", + "merge-descriptors": "1.0.1", + "methods": "1.1.2", + "on-finished": "2.3.0", + "parseurl": "1.3.1", + "path-to-regexp": "0.1.7", + "proxy-addr": "1.1.4", + "qs": "6.4.0", + "range-parser": "1.2.0", + "send": "0.15.3", + "serve-static": "1.12.3", + "setprototypeof": "1.0.3", + "statuses": "1.3.1", + "type-is": "1.6.15", + "utils-merge": "1.0.0", + "vary": "1.1.1" + } + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" + }, + "extract-zip": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.5.tgz", + "integrity": "sha1-maBnNbbqIOqbcF13ms/8yHz/BEA=", + "requires": { + "concat-stream": "1.6.0", + "debug": "2.2.0", + "mkdirp": "0.5.0", + "yauzl": "2.4.1" + }, + "dependencies": { + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "requires": { + "ms": "0.7.1" + } + }, + "ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" + } + } + }, + "extsprintf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz", + "integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA=" + }, + "fd-slicer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", + "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", + "requires": { + "pend": "1.2.0" + } + }, + "finalhandler": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.3.tgz", + "integrity": "sha1-70fneVDpmXgOhgIqVg4yF+DQzIk=", + "requires": { + "debug": "2.6.7", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "on-finished": "2.3.0", + "parseurl": "1.3.1", + "statuses": "1.3.1", + "unpipe": "1.0.0" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.15" + } + }, + "forwarded": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.0.tgz", + "integrity": "sha1-Ge+YdMSuHCl7zweP3mOgm2aoQ2M=" + }, + "fresh": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.0.tgz", + "integrity": "sha1-9HTKXmqSRtb9jglTz6m5yAWvp44=" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fstream": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "requires": { + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.0", + "rimraf": "2.6.1" + } + }, + "fstream-ignore": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", + "integrity": "sha1-nDHa40dnAY/h0kmyTa2mfQktoQU=", + "requires": { + "fstream": "1.0.11", + "inherits": "2.0.3", + "minimatch": "3.0.4" + } + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "1.1.2", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + }, + "har-schema": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", + "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=" + }, + "har-validator": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", + "requires": { + "ajv": "4.11.8", + "har-schema": "1.0.5" + } + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "hawk": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } + }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" + }, + "http-basic": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-2.5.1.tgz", + "integrity": "sha1-jORHvbW2xXf4pj4/p4BW7Eu02/s=", + "requires": { + "caseless": "0.11.0", + "concat-stream": "1.6.0", + "http-response-object": "1.1.0" + }, + "dependencies": { + "caseless": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", + "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=" + } + } + }, + "http-errors": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.1.tgz", + "integrity": "sha1-X4uO2YrKVFZWv1cplzh/kEpyIlc=", + "requires": { + "depd": "1.1.0", + "inherits": "2.0.3", + "setprototypeof": "1.0.3", + "statuses": "1.3.1" + } + }, + "http-response-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-1.1.0.tgz", + "integrity": "sha1-p8TnWq6C87tJBOT0P2FWc7TVGMM=" + }, + "http-signature": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.0", + "sshpk": "1.13.1" + } + }, + "iconv-lite": { + "version": "0.4.15", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz", + "integrity": "sha1-/iZaIYrGpXz+hUkn6dBMGYJe3es=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", + "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=" + }, + "ipaddr.js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.3.0.tgz", + "integrity": "sha1-HgOlL9rYOou7KyXL9JmLTP/NPew=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "jasmine": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.6.0.tgz", + "integrity": "sha1-ayLnCIPo5YnUVjRhU7TSBt2+IX8=", + "dev": true, + "requires": { + "exit": "0.1.2", + "glob": "7.1.2", + "jasmine-core": "2.6.4" + } + }, + "jasmine-core": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.6.4.tgz", + "integrity": "sha1-3skmzQqfoof7bbXHVfpIfnTOysU=", + "dev": true + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "requires": { + "jsonify": "0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" + }, + "jsprim": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", + "integrity": "sha1-o7h+QCmNjDgFUtjMdiigu5WiKRg=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.0.2", + "json-schema": "0.2.3", + "verror": "1.3.6" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", + "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=" + }, + "mime-db": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz", + "integrity": "sha1-gg9XIpa70g7CXtVeW13oaeVDbrE=" + }, + "mime-types": { + "version": "2.1.15", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", + "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=", + "requires": { + "mime-db": "1.27.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "1.1.8" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mkdirp": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", + "integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=", + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "nan": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz", + "integrity": "sha1-5P805slf37WuzAjeZZb0NgWn20U=" + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" + }, + "node-fetch": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.1.tgz", + "integrity": "sha512-j8XsFGCLw79vWXkZtMSmmLaOk9z5SQ9bV/tkbZVCqvgwzrjAGq66igobLofHtF63NvMTp2WjytpsNTGKa+XRIQ==", + "requires": { + "encoding": "0.1.12", + "is-stream": "1.1.0" + } + }, + "node-pre-gyp": { + "version": "0.6.36", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz", + "integrity": "sha1-22BBEst04NR3VU6bUFsXq936t4Y=", + "requires": { + "mkdirp": "0.5.1", + "nopt": "4.0.1", + "npmlog": "4.1.2", + "rc": "1.2.1", + "request": "2.81.0", + "rimraf": "2.6.1", + "semver": "5.3.0", + "tar": "2.2.1", + "tar-pack": "3.4.0" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + } + } + }, + "nopt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "requires": { + "abbrev": "1.1.0", + "osenv": "0.1.4" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "osenv": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", + "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=", + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "parseurl": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz", + "integrity": "sha1-yKuMkiO6NIiKpkopeyiFO+wY2lY=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, + "performance-now": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=" + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "2.0.6" + } + }, + "proxy-addr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.4.tgz", + "integrity": "sha1-J+VF9pYKRKYn2bREZ+NcG2tM4vM=", + "requires": { + "forwarded": "0.1.0", + "ipaddr.js": "1.3.0" + } + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "qs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=" + }, + "querystringify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-1.0.0.tgz", + "integrity": "sha1-YoYkIRLFtxL6ZU5SZlK/ahP/Bcs=" + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" + }, + "raw-body": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.2.0.tgz", + "integrity": "sha1-mUl2z2pQlqQRYoQEkvC9xdbn+5Y=", + "requires": { + "bytes": "2.4.0", + "iconv-lite": "0.4.15", + "unpipe": "1.0.0" + } + }, + "rc": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz", + "integrity": "sha1-LgPo5C7kULjLPc5lvhv4l04d/ZU=", + "requires": { + "deep-extend": "0.4.2", + "ini": "1.3.4", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "realm": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/realm/-/realm-1.10.0.tgz", + "integrity": "sha1-7A4XkBje2yB8hQqCnnjw6lf1ol0=", + "requires": { + "extract-zip": "1.6.5", + "ini": "1.3.4", + "nan": "2.6.2", + "node-fetch": "1.7.1", + "node-pre-gyp": "0.6.36", + "request": "2.81.0", + "sync-request": "3.0.1", + "url-parse": "1.1.9" + } + }, + "request": { + "version": "2.81.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.15", + "oauth-sign": "0.8.2", + "performance-now": "0.2.0", + "qs": "6.4.0", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.2", + "tunnel-agent": "0.6.0", + "uuid": "3.1.0" + } + }, + "request-promise-core": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", + "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", + "requires": { + "lodash": "4.17.4" + } + }, + "request-promise-native": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.4.tgz", + "integrity": "sha1-hpiOyO7kCORVefzoO/0Fs635oVU=", + "requires": { + "request-promise-core": "1.1.1", + "stealthy-require": "1.1.1", + "tough-cookie": "2.3.2" + } + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "rimraf": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", + "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" + }, + "send": { + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/send/-/send-0.15.3.tgz", + "integrity": "sha1-UBP5+ZAj31DRvZiSwZ4979HVMwk=", + "requires": { + "debug": "2.6.7", + "depd": "1.1.0", + "destroy": "1.0.4", + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "etag": "1.8.0", + "fresh": "0.5.0", + "http-errors": "1.6.1", + "mime": "1.3.4", + "ms": "2.0.0", + "on-finished": "2.3.0", + "range-parser": "1.2.0", + "statuses": "1.3.1" + } + }, + "serve-static": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.12.3.tgz", + "integrity": "sha1-n0uhni8wMMVH+K+ZEHg47DjVseI=", + "requires": { + "encodeurl": "1.0.1", + "escape-html": "1.0.3", + "parseurl": "1.3.1", + "send": "0.15.3" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "setprototypeof": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", + "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "requires": { + "hoek": "2.16.3" + } + }, + "sshpk": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + } + } + }, + "statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" + }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "requires": { + "safe-buffer": "5.1.1" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "sync-request": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-3.0.1.tgz", + "integrity": "sha1-yqEjWq+Im6UBB2oYNMQ2gwqC+3M=", + "requires": { + "concat-stream": "1.6.0", + "http-response-object": "1.1.0", + "then-request": "2.2.0" + } + }, + "tar": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" + } + }, + "tar-pack": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.0.tgz", + "integrity": "sha1-I74tf2cagzk3bL2wuP4/3r8xeYQ=", + "requires": { + "debug": "2.6.7", + "fstream": "1.0.11", + "fstream-ignore": "1.0.5", + "once": "1.4.0", + "readable-stream": "2.3.3", + "rimraf": "2.6.1", + "tar": "2.2.1", + "uid-number": "0.0.6" + } + }, + "then-request": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/then-request/-/then-request-2.2.0.tgz", + "integrity": "sha1-ZnizL6DKIY/laZgbvYhxtZQGDYE=", + "requires": { + "caseless": "0.11.0", + "concat-stream": "1.6.0", + "http-basic": "2.5.1", + "http-response-object": "1.1.0", + "promise": "7.3.1", + "qs": "6.4.0" + }, + "dependencies": { + "caseless": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", + "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=" + } + } + }, + "tough-cookie": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", + "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", + "requires": { + "punycode": "1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "5.1.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true + }, + "type-is": { + "version": "1.6.15", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", + "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", + "requires": { + "media-typer": "0.3.0", + "mime-types": "2.1.15" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "typescript": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.4.1.tgz", + "integrity": "sha1-w8yxbdqgsjFN4DHn5v7onlujRrw=" + }, + "uid-number": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", + "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "url-parse": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.1.9.tgz", + "integrity": "sha1-xn8dd11R8KGJEd17P/rSe7nlvRk=", + "requires": { + "querystringify": "1.0.0", + "requires-port": "1.0.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", + "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg=" + }, + "uuid": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" + }, + "vary": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.1.tgz", + "integrity": "sha1-Z1Neu2lMHVIldFeYRmUyP1h+jTc=" + }, + "verror": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", + "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=", + "requires": { + "extsprintf": "1.0.2" + } + }, + "wide-align": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", + "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", + "requires": { + "string-width": "1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "yauzl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", + "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", + "requires": { + "fd-slicer": "1.0.1" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..cd29d8a --- /dev/null +++ b/package.json @@ -0,0 +1,33 @@ +{ + "name": "users-api", + "version": "0.1.0", + "description": "This API allows all RESTL operations over the dataset hosted in https://gist.githubusercontent.com/jasonmadigan/009c15b5dc4b4eccd32b/raw/34759c44e77d2f3515e20ed561cdd7a5e8345585/users.json.", + "scripts": { + "start": "node dist/src/server.js", + "postinstall": "node_modules/typescript/bin/tsc -p . && cp src/repository/seed.json dist/src/repository/", + "test": "node_modules/.bin/jasmine", + "posttest": "rm -r database/test", + "clean": "rm -r node_modules database realm-object-server dist" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/josemigallas/users-api.git" + }, + "author": "Jose Miguel Gallas Olmedo", + "license": "ISC", + "dependencies": { + "@types/node": "^8.0.13", + "body-parser": "^1.17.2", + "express": "^4.15.3", + "realm": "^1.10.0", + "request-promise-native": "^1.0.4", + "typescript": "^2.4.1" + }, + "devDependencies": { + "@types/express": "^4.0.36", + "@types/jasmine": "^2.5.53", + "@types/realm": "^1.0.3", + "@types/request-promise-native": "^1.0.6", + "jasmine": "^2.6.0" + } +} diff --git a/spec/repository/realm-helper.spec.ts b/spec/repository/realm-helper.spec.ts new file mode 100644 index 0000000..b6ad105 --- /dev/null +++ b/spec/repository/realm-helper.spec.ts @@ -0,0 +1,394 @@ +import RealmHelper from "../../src/repository/realm-helper"; + +import User from "../../src/model/user"; +import UserDateFilter from "../../src/model/user-date-filter"; +import Location from "../../src/model/location"; +import Name from "../../src/model/name"; + +import UserSchema from "../../src/repository/schema/user-schema"; +import NameSchema from "../../src/repository/schema/name-schema"; +import LocationSchema from "../../src/repository/schema/location-schema"; +import PictureSchema from "../../src/repository/schema/picture-schema"; + +import TestUsers from "../support/test-users"; + +describe("RealmHelper", () => { + + const TEST_USER = JSON.parse(JSON.stringify(TestUsers[0])); + const TEST_CONFIG: Realm.Configuration = { + path: "database/test/users", + schema: [ + UserSchema, + NameSchema, + LocationSchema, + PictureSchema + ] + }; + + beforeAll(() => { + spyOnProperty(RealmHelper, "config", "get").and.returnValue(TEST_CONFIG); + }); + + beforeEach(() => { + const testRealm = RealmHelper.defaultRealm; + testRealm.write(() => { + testRealm.deleteAll(); + for (const user of TestUsers) { + testRealm.create(UserSchema.name, user); + } + }); + }); + + describe(".getUsers", () => { + + it("should return an array with all users", () => { + const users = RealmHelper.getUsers(); + + expect(users.length).toEqual(TestUsers.length); + }); + + }); + + describe(".getUserById", () => { + + it("should return a user by its username", () => { + const username1 = "josemigallas"; + const user1: User = RealmHelper.getUserByUsername(username1); + + const username2 = "josefa_la_jefa"; + const user2: User = RealmHelper.getUserByUsername(username2); + + expect(user1.username).toEqual(username1); + expect(user2.username).toEqual(username2); + }); + + }); + + describe(".addUser", () => { + + it("should create a new user in the DB", () => { + const usersBeforeAddition = RealmHelper.getUsers(); + expect(usersBeforeAddition.length).toEqual(TestUsers.length); + + const newUser: User = JSON.parse(JSON.stringify(TEST_USER)); + newUser.username = "spiderman1991"; + RealmHelper.addUser(newUser); + + const usersAfterAddition = RealmHelper.getUsers(); + expect(usersAfterAddition.length).toEqual(TestUsers.length + 1); + }); + + it("should throw if trying to add an existing user", () => { + const existingUser: User = TestUsers[0]; + const test = () => RealmHelper.addUser(existingUser); + + expect(test).toThrowError(); + }); + + }); + + describe(".deleteUser", () => { + + it("should delete an existing user in the DB", () => { + const usersBeforeDeletion = RealmHelper.getUsers(); + expect(usersBeforeDeletion.length).toEqual(TestUsers.length); + + const userToBeDeleted: User = TestUsers[0]; + RealmHelper.deleteUser(userToBeDeleted.username); + + const usersAfterDeletion = RealmHelper.getUsers(); + expect(usersAfterDeletion.length).toEqual(TestUsers.length - 1); + }); + + it("should not throw if trying to delete a nonexistent user", () => { + const nonExistentUsername: string = "TheThing67"; + const test = () => RealmHelper.deleteUser(nonExistentUsername); + + expect(test).not.toThrowError(); + }); + + }); + + describe(".updateUser", () => { + + it("should update a user of the DB", () => { + const userBeforeUpdating = RealmHelper.getUserByUsername(TEST_USER.username); + + const newEmail: string = "josemigallas@example.com"; + expect(userBeforeUpdating.email).not.toEqual(newEmail); + TEST_USER.email = newEmail; + + RealmHelper.updateUser(TEST_USER); + + const userAfterUpdating = RealmHelper.getUserByUsername(TEST_USER.username); + expect(userAfterUpdating.email).toEqual(newEmail); + }); + + it("should update a user of the DB even if some properties are undefined", () => { + const userBeforeUpdating = RealmHelper.getUserByUsername(TEST_USER.username); + + // Check value to update is different + const newEmail: string = "josemigallas@example.com"; + expect(userBeforeUpdating.email).not.toEqual(newEmail); + + // Check some value that won't be changed + const originalDob: number = userBeforeUpdating.dob; + + const userToUpdate: User = { + username: TEST_USER.username, + email: newEmail + }; + + RealmHelper.updateUser(userToUpdate); + + const userAfterUpdating = RealmHelper.getUserByUsername(TEST_USER.username); + expect(userAfterUpdating.email).toEqual(newEmail); + expect(userAfterUpdating.dob).toEqual(originalDob); + }); + + it("should throw if trying to update a nonexistent user", () => { + const nonExistentUsername: string = "TheThing67"; + expect(TEST_USER.username).not.toEqual(nonExistentUsername); + + TEST_USER.username = nonExistentUsername; + const test = () => RealmHelper.updateUser(TEST_USER); + + expect(test).toThrowError(); + }); + + }); + + describe(".getFilterQueryForUser", () => { + + it("should return a well formed query with object's properties", () => { + const filter: User = { + gender: "male", + salt: "123456789", + email: "test@example.com" + }; + + let expectedQuery: string = `gender = "${filter.gender}" AND `; + expectedQuery += `salt = "${filter.salt}" AND `; + expectedQuery += `email = "${filter.email}"`; + + expect(RealmHelper.getFilterQueryForUser(filter)).toEqual(expectedQuery); + }); + + it("should return a well formed query with object's inner objects", () => { + const location: Location = { + street: "123 Lie Av.", + city: "Nowhere", + state: "Solid", + zip: 12345 + }; + + const filter: User = { + location + }; + + let expectedQuery: string = `location.street = "${location.street}" AND `; + expectedQuery += `location.city = "${location.city}" AND `; + expectedQuery += `location.state = "${location.state}" AND `; + expectedQuery += `location.zip = "${location.zip}"`; + + expect(RealmHelper.getFilterQueryForUser(filter)).toEqual(expectedQuery); + }); + + it("should return a well formed query with object's properties and inner objects", () => { + const filter: User = { + gender: "male", + salt: "123456789", + email: "test@example.com", + location: { + street: "123 Lie Av.", + city: "Nowhere", + state: "Solid", + zip: 12345 + } + }; + let expectedQuery: string = `gender = "${filter.gender}" AND `; + expectedQuery += `salt = "${filter.salt}" AND `; + expectedQuery += `email = "${filter.email}" AND `; + expectedQuery += `location.street = "${filter.location.street}" AND `; + expectedQuery += `location.city = "${filter.location.city}" AND `; + expectedQuery += `location.state = "${filter.location.state}" AND `; + expectedQuery += `location.zip = "${filter.location.zip}"`; + + expect(RealmHelper.getFilterQueryForUser(filter)).toEqual(expectedQuery); + }); + + + it("should return a well formed query for max date filters", () => { + const maxDateFilter: UserDateFilter = { + dobMax: 999, + registeredMax: 999 + }; + + let expectedMaxDateQuery: string = `dob <= "${maxDateFilter.dobMax}" AND `; + expectedMaxDateQuery += `registered <= "${maxDateFilter.registeredMax}"`; + + expect(RealmHelper.getFilterQueryForUser(maxDateFilter)).toEqual(expectedMaxDateQuery); + }); + + it("should return a well formed query for min date filters", () => { + const minDateFilter: UserDateFilter = { + dobMin: 111, + registeredMin: 999 + }; + + let expectedMinDateQuery: string = `dob >= "${minDateFilter.dobMin}" AND `; + expectedMinDateQuery += `registered >= "${minDateFilter.registeredMin}"`; + + expect(RealmHelper.getFilterQueryForUser(minDateFilter)).toEqual(expectedMinDateQuery); + }); + + it("should return a well formed query for date interval filters", () => { + const intervalDateFilter: UserDateFilter = { + dobMin: 1, + dobMax: 2, + registeredMin: 10, + registeredMax: 20 + } + + let expectedIntervalDateQuery: string = `dob >= "${intervalDateFilter.dobMin}" AND `; + expectedIntervalDateQuery += `dob <= "${intervalDateFilter.dobMax}" AND `; + expectedIntervalDateQuery += `registered >= "${intervalDateFilter.registeredMin}" AND `; + expectedIntervalDateQuery += `registered <= "${intervalDateFilter.registeredMax}"`; + + expect(RealmHelper.getFilterQueryForUser(intervalDateFilter)).toEqual(expectedIntervalDateQuery); + }); + + }); + + describe(".findUsers", () => { + + it("should filter by location", () => { + const location: Location = { + street: "123 Lie Av." + }; + + const locationFilter: User = { + location + }; + + const filteredByLocation: User[] = RealmHelper.findUsers(locationFilter); + expect(filteredByLocation.length).toEqual(2); + }); + + it("should filter by gender", () => { + const genderFilter: User = { + gender: "male" + }; + + const filteredMales: User[] = RealmHelper.findUsers(genderFilter); + expect(filteredMales.length).toEqual(2); + + genderFilter.gender = "female"; + + const filteredFemales: User[] = RealmHelper.findUsers(genderFilter); + expect(filteredFemales.length).toEqual(1); + }); + + it("should filter by name", () => { + const name: Name = { + last: "Gallas" + }; + + const nameFilter: User = { + name + }; + + const filteredByLastName: User[] = RealmHelper.findUsers(nameFilter); + expect(filteredByLastName.length).toEqual(2); + + nameFilter.name.first = "Josemi"; + + const filteredByFirstAndLastName: User[] = RealmHelper.findUsers(nameFilter); + expect(filteredByFirstAndLastName.length).toEqual(1); + + nameFilter.name.title = "Lord"; + + const filteredByFirstLastAndTitle: User[] = RealmHelper.findUsers(nameFilter); + expect(filteredByFirstAndLastName.length).toEqual(1); + + nameFilter.name.title = "Mr"; + + const filteredByWrongTitle: User[] = RealmHelper.findUsers(nameFilter); + expect(filteredByWrongTitle.length).toEqual(0); + }); + + it("should filter by zip code", () => { + const zipFilter: User = { + location: { zip: 12345 } + }; + + let filteredByZip: User[] = RealmHelper.findUsers(zipFilter); + expect(filteredByZip.length).toEqual(2); + + zipFilter.location.zip = 99999; + + filteredByZip = RealmHelper.findUsers(zipFilter); + expect(filteredByZip.length).toEqual(1); + }); + + it("should filter by max date", () => { + const maxDateFilter: UserDateFilter = { + dobMax: 932871967 + }; + + let filteredByMaxDate: User[] = RealmHelper.findUsers(maxDateFilter); + expect(filteredByMaxDate.length).toEqual(0); + + maxDateFilter.dobMax = 932871968; + filteredByMaxDate = RealmHelper.findUsers(maxDateFilter); + expect(filteredByMaxDate.length).toEqual(1); + + maxDateFilter.dobMax = 932871969; + filteredByMaxDate = RealmHelper.findUsers(maxDateFilter); + expect(filteredByMaxDate.length).toEqual(2); + + maxDateFilter.dobMax = 932871970; + filteredByMaxDate = RealmHelper.findUsers(maxDateFilter); + expect(filteredByMaxDate.length).toEqual(3); + }); + + it("should filter by min date", () => { + const minDateFilter: UserDateFilter = { + dobMin: 932871968 + }; + + let filteredByMaxDate: User[] = RealmHelper.findUsers(minDateFilter); + expect(filteredByMaxDate.length).toEqual(3); + + minDateFilter.dobMin = 932871969; + filteredByMaxDate = RealmHelper.findUsers(minDateFilter); + expect(filteredByMaxDate.length).toEqual(2); + + minDateFilter.dobMin = 932871970; + filteredByMaxDate = RealmHelper.findUsers(minDateFilter); + expect(filteredByMaxDate.length).toEqual(1); + + minDateFilter.dobMin = 932871971; + filteredByMaxDate = RealmHelper.findUsers(minDateFilter); + expect(filteredByMaxDate.length).toEqual(0); + }); + + it("should filter by max date interval", () => { + const intervalDateFilter: UserDateFilter = { + dobMin: 932871968, + dobMax: 932871970 + }; + + let filteredByDateInterval: User[] = RealmHelper.findUsers(intervalDateFilter); + expect(filteredByDateInterval.length).toEqual(3); + + intervalDateFilter.dobMin = 932871968; + intervalDateFilter.dobMax = 932871969; + + filteredByDateInterval = RealmHelper.findUsers(intervalDateFilter); + expect(filteredByDateInterval.length).toEqual(2); + }); + + }); + +}) diff --git a/spec/routes/users.spec.ts b/spec/routes/users.spec.ts new file mode 100644 index 0000000..1e98207 --- /dev/null +++ b/spec/routes/users.spec.ts @@ -0,0 +1,535 @@ +import RealmHelper from "../../src/repository/realm-helper"; + +import User from "../../src/model/user"; +import UserDateFilter from "../../src/model/user-date-filter"; +import Location from "../../src/model/location"; +import Name from "../../src/model/name"; + +import UserSchema from "../../src/repository/schema/user-schema"; +import NameSchema from "../../src/repository/schema/name-schema"; +import LocationSchema from "../../src/repository/schema/location-schema"; +import PictureSchema from "../../src/repository/schema/picture-schema"; + +import TestUsers from "../support/test-users"; +import ApiTestClient from "../support/api-test-client"; + +describe("Route users", () => { + + const TEST_USER = JSON.parse(JSON.stringify(TestUsers[0])); + const TEST_CONFIG: Realm.Configuration = { + path: "database/test/users", + schema: [ + UserSchema, + NameSchema, + LocationSchema, + PictureSchema + ] + }; + + beforeAll(() => { + spyOnProperty(RealmHelper, "config", "get").and.returnValue(TEST_CONFIG); + }); + + beforeEach(() => { + const testRealm = RealmHelper.defaultRealm; + testRealm.write(() => { + testRealm.deleteAll(); + for (const user of TestUsers) { + testRealm.create(UserSchema.name, user); + } + }); + }); + + describe("GET /users", () => { + + it("returns an array with all users", done => { + ApiTestClient + .getUsers() + .then(users => { + expect(users).toEqual(jasmine.any(Array)); + expect(users.length).toEqual(TestUsers.length); + done(); + }) + .catch(err => { + fail(err); + done(); + }); + }); + + it("should filter by location", done => { + const locationFilter: User = { + location: { + street: "123 Lie Av." + } + }; + + ApiTestClient + .filterUsers(locationFilter) + .then(users => { + expect(users.length).toEqual(2); + done(); + }) + .catch(err => { + fail(err); + done(); + }); + }); + + it("should return 400 if trying to filter with wrong parameters", done => { + const wrongFilter: any = { + street: "123 Lie Av." + }; + + ApiTestClient + .filterUsers(wrongFilter) + .then(users => { + fail("It should have not filtered"); + done(); + }) + .catch(err => { + expect(err.statusCode).toEqual(400); + done(); + }); + }); + + it("should filter by gender", done => { + const genderFilter: User = { + gender: "male" + }; + + ApiTestClient + .filterUsers(genderFilter) + .then(users => { + expect(users.length).toEqual(2); + done(); + }) + .catch(err => { + fail(err); + done(); + }); + + genderFilter.gender = "female"; + + ApiTestClient + .filterUsers(genderFilter) + .then(users => { + expect(users.length).toEqual(1); + done(); + }) + .catch(err => { + fail(err); + done(); + }); + }); + + it("should filter by name", done => { + const nameFilter: User = { + name: { + last: "Gallas" + } + }; + + ApiTestClient + .filterUsers(nameFilter) + .then(users => { + expect(users.length).toEqual(2); + done(); + }) + .catch(err => { + fail(err); + done(); + }); + + nameFilter.name.first = "Josemi"; + + ApiTestClient + .filterUsers(nameFilter) + .then(users => { + expect(users.length).toEqual(1); + done(); + }) + .catch(err => { + fail(err); + done(); + }); + + nameFilter.name.title = "Lord"; + + ApiTestClient + .filterUsers(nameFilter) + .then(users => { + expect(users.length).toEqual(1); + done(); + }) + .catch(err => { + fail(err); + done(); + }); + + nameFilter.name.title = "Mr"; + + ApiTestClient + .filterUsers(nameFilter) + .then(users => { + expect(users.length).toEqual(0); + done(); + }) + .catch(err => { + fail(err); + done(); + }); + }); + + it("should filter by zip code", done => { + const zipFilter: User = { + location: { zip: 12345 } + }; + + ApiTestClient + .filterUsers(zipFilter) + .then(users => { + expect(users.length).toEqual(2); + done(); + }) + .catch(err => { + fail(err); + done(); + }); + + zipFilter.location.zip = 99999; + + ApiTestClient + .filterUsers(zipFilter) + .then(users => { + expect(users.length).toEqual(1); + done(); + }) + .catch(err => { + fail(err); + done(); + }); + }); + + it("should filter by max date", done => { + const maxDateFilter: UserDateFilter = { + dobMax: 932871967 + }; + + ApiTestClient + .filterUsers(maxDateFilter) + .then(users => { + expect(users.length).toEqual(0); + done(); + }) + .catch(err => { + fail(err); + done(); + }); + + maxDateFilter.dobMax = 932871968; + + ApiTestClient + .filterUsers(maxDateFilter) + .then(users => { + expect(users.length).toEqual(1); + done(); + }) + .catch(err => { + fail(err); + done(); + }); + + maxDateFilter.dobMax = 932871969; + + ApiTestClient + .filterUsers(maxDateFilter) + .then(users => { + expect(users.length).toEqual(2); + done(); + }) + .catch(err => { + fail(err); + done(); + }); + + maxDateFilter.dobMax = 932871970; + + ApiTestClient + .filterUsers(maxDateFilter) + .then(users => { + expect(users.length).toEqual(3); + done(); + }) + .catch(err => { + fail(err); + done(); + }); + }); + + it("should filter by min date", done => { + const minDateFilter: UserDateFilter = { + dobMin: 932871968 + }; + + ApiTestClient + .filterUsers(minDateFilter) + .then(users => { + expect(users.length).toEqual(3); + done(); + }) + .catch(err => { + fail(err); + done(); + }); + + minDateFilter.dobMin = 932871969; + + ApiTestClient + .filterUsers(minDateFilter) + .then(users => { + expect(users.length).toEqual(2); + done(); + }) + .catch(err => { + fail(err); + done(); + }); + + minDateFilter.dobMin = 932871970; + + ApiTestClient + .filterUsers(minDateFilter) + .then(users => { + expect(users.length).toEqual(1); + done(); + }) + .catch(err => { + fail(err); + done(); + }); + + minDateFilter.dobMin = 932871971; + + ApiTestClient + .filterUsers(minDateFilter) + .then(users => { + expect(users.length).toEqual(0); + done(); + }) + .catch(err => { + fail(err); + done(); + }); + }); + + it("should filter by max date interval", done => { + const intervalDateFilter: UserDateFilter = { + dobMin: 932871968, + dobMax: 932871970 + }; + + ApiTestClient + .filterUsers(intervalDateFilter) + .then(users => { + expect(users.length).toEqual(3); + done(); + }) + .catch(err => { + fail(err); + done(); + }); + + intervalDateFilter.dobMin = 932871968; + intervalDateFilter.dobMax = 932871969; + + ApiTestClient + .filterUsers(intervalDateFilter) + .then(users => { + expect(users.length).toEqual(2); + done(); + }) + .catch(err => { + fail(err); + done(); + }); + }); + + }); + + describe("GET /users/:username", () => { + + it("returns a user if it exists", done => { + ApiTestClient + .getUserByUsername("josemigallas") + .then(user => { + expect(user.username).toEqual("josemigallas"); + done(); + }) + .catch(err => { + fail(err); + done(); + }); + }); + + it("returns 404 if user does not exist", done => { + ApiTestClient + .getUserByUsername("whatever") + .then(user => { + fail("User should not exist"); + done(); + }) + .catch(err => { + expect(err.statusCode).toEqual(404); + done(); + }); + }); + }); + + describe("POST /users", () => { + + const newUser: User = JSON.parse(JSON.stringify(TEST_USER)); + newUser.username = "newUser"; + + it("returns 201 if the user is succesfully created", done => { + RealmHelper.deleteUser(newUser.username); + + ApiTestClient + .createUser(newUser) + .then(res => { + expect(res.statusCode).toEqual(201); + done(); + }) + .catch(err => { + fail(err); + done(); + }); + }); + + it("returns 409 if user exists already", done => { + RealmHelper.addUser(newUser); + + ApiTestClient + .createUser(newUser) + .then(res => { + fail("It should have thrown a 409 error"); + done(); + }) + .catch(err => { + expect(err.statusCode).toEqual(409); + done(); + }); + }); + }); + + describe("PUT /users", () => { + + it("returns 200 if the user is succesfully updated", done => { + const userToUpdate: User = JSON.parse(JSON.stringify(TEST_USER)); + const userBeforeUpdating: User = RealmHelper.getUserByUsername(userToUpdate.username); + expect(userBeforeUpdating.email).toEqual(userToUpdate.email); + + userToUpdate.email += "-updated"; + + ApiTestClient + .updateUser(userToUpdate) + .then(res => { + expect(res.statusCode).toEqual(200); + + const userAfterUpdating: User = RealmHelper.getUserByUsername(userToUpdate.username); + expect(userAfterUpdating.email).toEqual(userToUpdate.email); + + done(); + }) + .catch(err => { + fail(err); + done(); + }); + }); + + it("should update even if some user's properties are undefined", done => { + const userToUpdate: User = { + username: TEST_USER.username, + location: { + city: TEST_USER.location.city + } + }; + + const userBeforeUpdating: User = RealmHelper.getUserByUsername(userToUpdate.username); + expect(userBeforeUpdating.location.city).toEqual(userToUpdate.location.city); + + userToUpdate.location.city += " of tht South"; + + ApiTestClient + .updateUser(userToUpdate) + .then(res => { + expect(res.statusCode).toEqual(200); + + const userAfterUpdating: User = RealmHelper.getUserByUsername(userToUpdate.username); + expect(userAfterUpdating.location.city).toEqual(userToUpdate.location.city); + + done(); + }) + .catch(err => { + fail(err); + done(); + }); + }); + + it("returns 404 if user does not exist", done => { + const userToUpdate: User = { + username: "whatever" + }; + + const nonExistentUser: User = RealmHelper.getUserByUsername(userToUpdate.username); + expect(nonExistentUser).toBeFalsy(); + + ApiTestClient + .updateUser(userToUpdate) + .then(res => { + fail("It should have thrown a 404 error"); + done(); + }) + .catch(err => { + expect(err.statusCode).toEqual(404); + done(); + }); + }); + }); + + describe("DELETE /users/:username", () => { + + it("returns 200 if user has been successfully deleted", done => { + const user: User = RealmHelper.getUserByUsername("josemigallas"); + expect(user).toBeTruthy(); + + ApiTestClient + .deleteUser("josemigallas") + .then(res => { + expect(res.statusCode).toEqual(200); + + const user: User = RealmHelper.getUserByUsername("josemigallas"); + expect(user).toBeFalsy(); + + done(); + }) + .catch(err => { + fail(err); + done(); + }); + }); + + it("returns 404 if user does not exist", done => { + ApiTestClient + .deleteUser("whatever") + .then(user => { + fail("User should not exist"); + done(); + }) + .catch(err => { + expect(err.statusCode).toEqual(404); + done(); + }); + }); + }); + +}); diff --git a/spec/support/api-test-client.ts b/spec/support/api-test-client.ts new file mode 100644 index 0000000..2685f81 --- /dev/null +++ b/spec/support/api-test-client.ts @@ -0,0 +1,72 @@ +import * as request from "request-promise-native"; + +import User from "../../src/model/user"; +import UserDateFilter from "../../src/model/user-date-filter"; + +/** + * Utility class that wraps all calls to API when running integration tests. + */ +export default class ApiTestClient { + + private static readonly URL: string = "http://localhost:3000"; + + public static getUsers(): Promise { + return request.get(`${this.URL}/users`) + .then(JSON.parse); + } + + public static filterUsers(filter: UserDateFilter): Promise { + let url: string = `${this.URL}/users?`; + + for (const key in filter) { + if (typeof filter[key] !== "object") { + url += `${key}=${filter[key]}&`; + } else { + for (const subkey in filter[key]) { + url += `${key}[${subkey}]=${filter[key][subkey]}&`; + } + } + } + + url = url.slice(0, -1); + + return request.get(url).then(JSON.parse); + } + + public static getUserByUsername(username: string): Promise { + return request.get(`${this.URL}/users/${username}`) + .then(JSON.parse); + } + + public static createUser(user: User): Promise { + const options = { + uri: `${this.URL}/users`, + body: user, + json: true, + resolveWithFullResponse: true + }; + + return request.post(options); + } + + public static updateUser(user: User): Promise { + const options = { + uri: `${this.URL}/users`, + body: user, + json: true, + resolveWithFullResponse: true + }; + + return request.put(options); + } + + public static deleteUser(username: string): Promise { + const options = { + uri: `${this.URL}/users/${username}`, + resolveWithFullResponse: true + }; + + return request.delete(options); + } + +} diff --git a/spec/support/jasmine.json b/spec/support/jasmine.json new file mode 100644 index 0000000..fc246d6 --- /dev/null +++ b/spec/support/jasmine.json @@ -0,0 +1,11 @@ +{ + "spec_dir": "dist/spec", + "spec_files": [ + "**/*spec.js" + ], + "helpers": [ + "helpers/**/*.js" + ], + "stopSpecOnExpectationFailure": false, + "random": false +} \ No newline at end of file diff --git a/spec/support/test-users.ts b/spec/support/test-users.ts new file mode 100644 index 0000000..8e508b9 --- /dev/null +++ b/spec/support/test-users.ts @@ -0,0 +1,95 @@ +export default [ + { + gender: "male", + name: { + title: "Lord", + first: "Josemi", + last: "Gallas" + }, + location: { + street: "123 Lie Av.", + city: "Nowhere", + state: "Solid", + zip: 12345 + }, + email: "josemigallas@gmail.com", + username: "josemigallas", + password: "secret", + salt: "lypI10wj", + md5: "bbdd6140e188e3bf68ae7ae67345df65", + sha1: "4572d25c99aa65bbf0368168f65d9770b7cacfe6", + sha256: "ec0705aec7393e2269d4593f248e649400d4879b2209f11bb2e012628115a4eb", + registered: 1237176893, + dob: 932871968, + phone: "111-555-9999", + cell: "222-444-8888", + PPS: "3123123T", + picture: { + large: "https://avatars1.githubusercontent.com/u/11672286?v=3&s=460", + medium: "https://avatars1.githubusercontent.com/u/11672286?v=3&s=460", + thumbnail: "https://avatars1.githubusercontent.com/u/11672286?v=3&s=460" + } + }, + { + gender: "female", + name: { + title: "Lady", + first: "Josefina", + last: "Gallas" + }, + location: { + street: "123 Lie Av.", + city: "Nowhere", + state: "Solid", + zip: 12345 + }, + email: "josefa@gmail.com", + username: "josefa_la_jefa", + password: "secret", + salt: "lypI10wa", + md5: "bbdd6140f188e3bf68ae7ae67345df65", + sha1: "4572d25f99aa65bbf0368168f65d9770b7cacfe6", + sha256: "ec0703aec7393e2269d4593f248e649400d4879b2209f11bb2e012628115a4eb", + registered: 1237176894, + dob: 932871969, + phone: "111-444-9999", + cell: "222-555-8888", + PPS: "3123123M", + picture: { + large: "https://avatars1.githubusercontent.com/u/11672286?v=3&s=460", + medium: "https://avatars1.githubusercontent.com/u/11672286?v=3&s=460", + thumbnail: "https://avatars1.githubusercontent.com/u/11672286?v=3&s=460" + } + }, + { + gender: "male", + name: { + title: "Prince", + first: "Thor", + last: "Odinson" + }, + location: { + street: "Street", + city: "Asgard", + state: "Ragnarok", + zip: 99999 + }, + email: "thor@asgard.com", + username: "thor", + password: "secret", + salt: "lypt10wa", + md5: "bbdd3140f188e3bf68ae7ae67345df65", + sha1: "4571d25f99aa65bbf0368168f65d9770b7cacfe6", + sha256: "er0703aec7393e2269d4593f248e649400d4879b2209f11bb2e012628115a4eb", + registered: 1237176900, + dob: 932871970, + phone: "111-444-9999", + cell: "222-555-8888", + PPS: "3123253M", + picture: { + large: "https://avatars1.githubusercontent.com/u/11672286?v=3&s=460", + medium: "https://avatars1.githubusercontent.com/u/11672286?v=3&s=460", + thumbnail: "https://avatars1.githubusercontent.com/u/11672286?v=3&s=460" + } + } +]; diff --git a/src/model/location.ts b/src/model/location.ts new file mode 100644 index 0000000..8638a15 --- /dev/null +++ b/src/model/location.ts @@ -0,0 +1,8 @@ +export default interface Location { + + street?: string; + city?: string; + state?: string; + zip?: number; + +} diff --git a/src/model/name.ts b/src/model/name.ts new file mode 100644 index 0000000..240150e --- /dev/null +++ b/src/model/name.ts @@ -0,0 +1,7 @@ +export default interface Name { + + title?: string; + first?: string; + last?: string; + +} diff --git a/src/model/picture.ts b/src/model/picture.ts new file mode 100644 index 0000000..3b4fb3e --- /dev/null +++ b/src/model/picture.ts @@ -0,0 +1,7 @@ +export default interface Picture { + + large: string; + medium: string; + thumbnail: string; + +} diff --git a/src/model/user-date-filter.ts b/src/model/user-date-filter.ts new file mode 100644 index 0000000..15c1faa --- /dev/null +++ b/src/model/user-date-filter.ts @@ -0,0 +1,13 @@ +import User from "./user"; +import Name from "./name"; +import Location from "./location"; +import Picture from "./picture"; + +export default interface UserDateFilter extends User { + + registeredMax?: number, + registeredMin?: number, + dobMax?: number, + dobMin?: number + +} diff --git a/src/model/user.ts b/src/model/user.ts new file mode 100644 index 0000000..267a062 --- /dev/null +++ b/src/model/user.ts @@ -0,0 +1,24 @@ +import Name from "./name"; +import Location from "./location"; +import Picture from "./picture"; + +export default interface User { + + gender?: string, + name?: Name, + location?: Location, + email?: string, + username?: string, + password?: string, + salt?: string, + md5?: string, + sha1?: string, + sha256?: string, + registered?: number, + dob?: number, + phone?: string, + cell?: string, + PPS?: string, + picture?: Picture + +} diff --git a/src/repository/realm-helper.ts b/src/repository/realm-helper.ts new file mode 100644 index 0000000..f1ff66d --- /dev/null +++ b/src/repository/realm-helper.ts @@ -0,0 +1,151 @@ +import * as Realm from 'realm'; + +import LocationSchema from "./schema/location-schema"; +import NameSchema from "./schema/name-schema"; +import PictureSchema from "./schema/picture-schema"; +import UserSchema from "./schema/user-schema"; +import User from '../model/user'; + +/** + * Helper class that setup the Realm DB and serves as data access layer. + */ +export default class RealmHelper { + + private static _realm: Realm; + + private static _config: Realm.Configuration = { + path: "database/users", + schema: [ + UserSchema, + NameSchema, + LocationSchema, + PictureSchema + ] + }; + + public static get defaultRealm(): Realm { + if (!this._realm) { + this._realm = new Realm(this.config); + } + return new Realm(this.config); + } + + public static get config(): Realm.Configuration { + return this._config; + } + + public static init(development = false): void { + if (development) { + this._config.path = "database/test/users"; + } + + const realm = this.defaultRealm; + + if (!realm.empty) { + return; + } + + const users: User[] = require("./seed.json").users; + + realm.write(() => { + for (const user of users) { + realm.create(UserSchema.name, user); + } + }); + } + + static getUsers(): User[] { + const results: Realm.Results = this.defaultRealm + .objects(UserSchema.name); + + return Array.prototype.slice.call(results, 0, results.length); + } + + static getUserByUsername(username: string): User { + return this.defaultRealm + .objectForPrimaryKey(UserSchema.name, username); + } + + static findUsers(filter: User): User[] { + const query: string = this.getFilterQueryForUser(filter); + + const results: Realm.Results = this.defaultRealm + .objects(UserSchema.name) + .filtered(query); + + return Array.prototype.slice.call(results, 0, results.length); + } + + static getFilterQueryForUser(user: User): string { + let query: string = ""; + + // TODO refactor this kamehameha + for (const key in user) { + if (typeof user[key] !== "object") { + if (user[key]) { + if (key.match(".*Max")) { + query += `${key.slice(0, -3)} <= "${user[key]}" AND `; + } else if (key.match(".*Min")) { + query += `${key.slice(0, -3)} >= "${user[key]}" AND `; + } else { + query += `${key} = "${user[key]}" AND `; + } + } + } else { + for (const subkey in user[key]) { + if (user[key][subkey]) { + query += `${key}.${subkey} = "${user[key][subkey]}" AND `; + } + } + } + } + + query = query.slice(0, -5) + + return query; + } + + static addUser(user: User): User | void { + const realm: Realm = this.defaultRealm; + + realm.write(() => { + return realm.create(UserSchema.name, user, false); + }); + } + + static updateUser(newUser: User): User | void { + const user: User = this.getUserByUsername(newUser.username); + + if (!user) { + throw new Error(`User: ${newUser.username} does not exist`); + } + + const assignEach = (values, object) => { + for (const key in values) { + if (typeof values[key] !== "object" && key !== "username") { + object[key] = values[key]; + } else { + assignEach(values[key], object[key]); + } + } + } + + this.defaultRealm.write(() => { + assignEach(newUser, user); + }); + } + + static deleteUser(username: string): void { + const realm = this.defaultRealm; + const user: User = realm.objectForPrimaryKey(UserSchema.name, username); + + if (!user) { + return; + } + + realm.write(() => { + return realm.delete(user); + }) + } + +} diff --git a/src/repository/schema/location-schema.ts b/src/repository/schema/location-schema.ts new file mode 100644 index 0000000..b9f5781 --- /dev/null +++ b/src/repository/schema/location-schema.ts @@ -0,0 +1,13 @@ +import { ObjectSchema } from "realm"; + +const LocationSchema: ObjectSchema = { + name: "Location", + properties: { + street: "string", + city: "string", + state: "string", + zip: "int", + } +}; + +export default LocationSchema; diff --git a/src/repository/schema/name-schema.ts b/src/repository/schema/name-schema.ts new file mode 100644 index 0000000..1e9f9be --- /dev/null +++ b/src/repository/schema/name-schema.ts @@ -0,0 +1,12 @@ +import { ObjectSchema } from "realm"; + +const NameSchema: ObjectSchema = { + name: "Name", + properties: { + title: "string", + first: "string", + last: "string" + } +} + +export default NameSchema; diff --git a/src/repository/schema/picture-schema.ts b/src/repository/schema/picture-schema.ts new file mode 100644 index 0000000..be95027 --- /dev/null +++ b/src/repository/schema/picture-schema.ts @@ -0,0 +1,12 @@ +import { ObjectSchema } from "realm"; + +const PictureSchema: ObjectSchema = { + name: "Picture", + properties: { + large: "string", + medium: "string", + thumbnail: "string" + } +} + +export default PictureSchema; diff --git a/src/repository/schema/user-schema.ts b/src/repository/schema/user-schema.ts new file mode 100644 index 0000000..bba0dc8 --- /dev/null +++ b/src/repository/schema/user-schema.ts @@ -0,0 +1,30 @@ + +import { ObjectSchema } from "realm"; +import LocationSchema from "./location-schema"; +import NameSchema from "./name-schema"; +import PictureSchema from "./picture-schema"; + +const UserSchema: ObjectSchema = { + name: "User", + primaryKey: "username", + properties: { + gender: "string", + name: NameSchema.name, + location: LocationSchema.name, + email: "string", + username: "string", + password: "string", + salt: "string", + md5: "string", + sha1: "string", + sha256: "string", + registered: "int", + dob: "int", + phone: "string", + cell: "string", + PPS: "string", + picture: PictureSchema.name + } +} + +export default UserSchema; diff --git a/src/repository/seed.json b/src/repository/seed.json new file mode 100644 index 0000000..f54f14f --- /dev/null +++ b/src/repository/seed.json @@ -0,0 +1,3104 @@ +{ + "users": [ + { + "gender": "female", + "name": { + "title": "miss", + "first": "alison", + "last": "reid" + }, + "location": { + "street": "1097 the avenue", + "city": "Newbridge", + "state": "ohio", + "zip": 28782 + }, + "email": "alison.reid@example.com", + "username": "tinywolf709", + "password": "rockon", + "salt": "lypI10wj", + "md5": "bbdd6140e188e3bf68ae7ae67345df65", + "sha1": "4572d25c99aa65bbf0368168f65d9770b7cacfe6", + "sha256": "ec0705aec7393e2269d4593f248e649400d4879b2209f11bb2e012628115a4eb", + "registered": 1237176893, + "dob": 932871968, + "phone": "031-541-9181", + "cell": "081-647-4650", + "PPS": "3302243T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/60.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/60.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/60.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "miss", + "first": "allie", + "last": "willis" + }, + "location": { + "street": "7135 the crescent", + "city": "Leixlip", + "state": "colorado", + "zip": 37191 + }, + "email": "allie.willis@example.com", + "username": "crazybear293", + "password": "3232", + "salt": "UVMKO1Tj", + "md5": "b7441c556f250fe6ebb3367ba708d4b6", + "sha1": "fc79c95d01ca351efdf283331f39f2384db1dd78", + "sha256": "999afe92c680c6d74412ff438c8d0901028805caf66aeff536e0eed52e758d55", + "registered": 1216643814, + "dob": 253903290, + "phone": "041-379-5675", + "cell": "081-471-3648", + "PPS": "9408385T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/19.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/19.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/19.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "andy", + "last": "adams" + }, + "location": { + "street": "5857 woodlawn avenue", + "city": "Westport", + "state": "alaska", + "zip": 71280 + }, + "email": "andy.adams@example.com", + "username": "beautifulfish360", + "password": "123457", + "salt": "HsxzewdQ", + "md5": "5809e5fda81eed34bad9ca6eff414924", + "sha1": "6c95f0d9210e88028074d4baaeefc3d6c830a9a3", + "sha256": "f92fc585c017d093b03dba898162613380f137f934637c5bf9050fe68c103c54", + "registered": 1180746503, + "dob": 1028583070, + "phone": "041-252-0953", + "cell": "081-567-1935", + "PPS": "2470896T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/75.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/75.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/75.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "mrs", + "first": "florence", + "last": "fowler" + }, + "location": { + "street": "5047 church lane", + "city": "Arklow", + "state": "indiana", + "zip": 58360 + }, + "email": "florence.fowler@example.com", + "username": "redlion798", + "password": "legend", + "salt": "nxwLOZoF", + "md5": "96cf4434a0bc4a36abc62ff24364b1cd", + "sha1": "dc708250e44b7139a2d873af5077239bcb900933", + "sha256": "35b8f75a4b64d27d2ab3ef06a36bb350b89cc2f6bded184f00b6086bf7becace", + "registered": 1423031788, + "dob": 120739378, + "phone": "061-144-4167", + "cell": "081-835-9945", + "PPS": "8990114T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/33.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/33.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/33.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "ricky", + "last": "robertson" + }, + "location": { + "street": "1858 jones road", + "city": "Killarney", + "state": "virginia", + "zip": 67647 + }, + "email": "ricky.robertson@example.com", + "username": "silverswan291", + "password": "alive", + "salt": "dgB3H4Yt", + "md5": "3268a90b4cb3ad3c766f1547349dfc9e", + "sha1": "2bc652b6b88b9d35af93a1222e69c2c4ac66212d", + "sha256": "8c6d000d0d71a7100904669411ed8a049e5b33e84703ded1050dc23db7a4621c", + "registered": 1269540252, + "dob": 801739150, + "phone": "041-060-3589", + "cell": "081-733-6421", + "PPS": "0494654T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/76.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/76.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/76.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "ms", + "first": "barb", + "last": "reed" + }, + "location": { + "street": "6819 south street", + "city": "Passage West", + "state": "massachusetts", + "zip": 14086 + }, + "email": "barb.reed@example.com", + "username": "bluepeacock968", + "password": "bighead", + "salt": "q62v6sYW", + "md5": "846548fc6a8d2baff5dd9c4c380559b1", + "sha1": "b51dbebc23b10f72e2669fb3a43dfc574086cef3", + "sha256": "9e7b974eecb5f9d0f61ed7a94b3663a2d3eb24005ea17adbd3bccdc6d49bb29e", + "registered": 1093990654, + "dob": 702466955, + "phone": "011-434-6413", + "cell": "081-844-2224", + "PPS": "4170862T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/84.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/84.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/84.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "derrick", + "last": "long" + }, + "location": { + "street": "9156 jones road", + "city": "Cavan", + "state": "florida", + "zip": 26323 + }, + "email": "derrick.long@example.com", + "username": "redbird312", + "password": "hattrick", + "salt": "PlGn4MAW", + "md5": "efcdac37de5b6d8f60cd1ebc34451a73", + "sha1": "ff80299d9d8b979b16da3b8671982942483893d3", + "sha256": "b8045913fd185480a17db7f9e3f4fc6bb0d49ad174c30ec46cf54b0e9434bf7c", + "registered": 1400661287, + "dob": 828502632, + "phone": "041-661-5306", + "cell": "081-001-7123", + "PPS": "4874666T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/13.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/13.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/13.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "flenn", + "last": "flores" + }, + "location": { + "street": "6109 galway road", + "city": "Tipperary", + "state": "ohio", + "zip": 86581 + }, + "email": "flenn.flores@example.com", + "username": "blackfrog555", + "password": "annette", + "salt": "UajSXY0z", + "md5": "4da741206fad4ec92e56061f196b8969", + "sha1": "0855cde9aea1a62fa285f48fa52e4b65763e24a4", + "sha256": "7911f503aae626aeee8bc5d930e32ede8338dc83eb63b416778a46b6385871a8", + "registered": 1380508549, + "dob": 146490934, + "phone": "031-692-2673", + "cell": "081-818-1051", + "PPS": "1577239T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/26.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/26.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/26.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "brendan", + "last": "doyle" + }, + "location": { + "street": "7376 church street", + "city": "Greystones", + "state": "georgia", + "zip": 43608 + }, + "email": "brendan.doyle@example.com", + "username": "orangebutterfly881", + "password": "4711", + "salt": "9WmIGRUs", + "md5": "801891356465a7bb86d022558e9dc0b4", + "sha1": "3c61f142cdb4faf4b99a3339f17d50435a0bb762", + "sha256": "ca9ded269fb2def02567d478d9d3242e9db26921325ba00f92c4503569476aca", + "registered": 1037155862, + "dob": 988298406, + "phone": "071-359-1598", + "cell": "081-860-3402", + "PPS": "6443903T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/83.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/83.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/83.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "brad", + "last": "doyle" + }, + "location": { + "street": "4486 o'connell avenue", + "city": "Cashel", + "state": "pennsylvania", + "zip": 82940 + }, + "email": "brad.doyle@example.com", + "username": "smallduck162", + "password": "badgirl", + "salt": "X7ITc4QM", + "md5": "f37f3abdf6acb1b5e8725f88e3aa2c1a", + "sha1": "268fce7a95e4dd1987dded7d00c78857dbac6249", + "sha256": "e3f8d87f5e4b81b4157fe6a286a2ffa765aec75ec6b7c7ae332f6979abea9488", + "registered": 1063092194, + "dob": 732421954, + "phone": "051-574-8454", + "cell": "081-866-2008", + "PPS": "6168927T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/10.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/10.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/10.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "larry", + "last": "miller" + }, + "location": { + "street": "8623 new road", + "city": "Carlow", + "state": "wisconsin", + "zip": 26427 + }, + "email": "larry.miller@example.com", + "username": "bigmouse430", + "password": "latina", + "salt": "Bc1nHAfu", + "md5": "b9db4fc593aed45b8aea198b46f3d66a", + "sha1": "f96b340d99acaaccc49c66e2c8ed61f3203d61db", + "sha256": "d3042b5de9f7d031f556e227c457ceedb2bd667ecb5d42dc7e9fe622cb5a3f8c", + "registered": 1398652428, + "dob": 796713704, + "phone": "061-105-6160", + "cell": "081-050-5004", + "PPS": "5234473T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/17.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/17.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/17.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "sergio", + "last": "kelly" + }, + "location": { + "street": "8740 galway road", + "city": "Portlaoise", + "state": "north carolina", + "zip": 78477 + }, + "email": "sergio.kelly@example.com", + "username": "silvergorilla666", + "password": "ludwig", + "salt": "mPe49r58", + "md5": "ef297889f514ec2c7ab6361436176147", + "sha1": "a54fb5df33100c41dac12e558834138f92be6bc8", + "sha256": "74c93d6b8d290b5f80b70834ed9ac743c461ced42f2278dc952023f0d6f84487", + "registered": 995160178, + "dob": 176104870, + "phone": "011-361-3732", + "cell": "081-910-4872", + "PPS": "9501080T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/40.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/40.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/40.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "harold", + "last": "newman" + }, + "location": { + "street": "5345 park lane", + "city": "Drogheda", + "state": "pennsylvania", + "zip": 80045 + }, + "email": "harold.newman@example.com", + "username": "blueladybug338", + "password": "justus", + "salt": "hBlaJt4Y", + "md5": "94af19d792844fe67b26c446c68c0161", + "sha1": "87288d4290bef56367a3dd293b619124ff6e3b02", + "sha256": "8a92a4611e06fb670b43b72eb1257ec0e4e82dd59aaf0767f0d9c12466518313", + "registered": 1289817934, + "dob": 882928827, + "phone": "051-619-3115", + "cell": "081-078-3057", + "PPS": "4300944T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/4.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/4.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/4.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "dylan", + "last": "nichols" + }, + "location": { + "street": "8104 north road", + "city": "Wicklow", + "state": "new york", + "zip": 32191 + }, + "email": "dylan.nichols@example.com", + "username": "brownostrich663", + "password": "widget", + "salt": "TQMNUVea", + "md5": "d93c006c83b061d1999b07ee9a9662d4", + "sha1": "cb30b764687e2cfe39c38064b29a60352be104a6", + "sha256": "9b48045a34fa7ccad2b6fa80e46d38018f020d937d2d45f35d5d5629dba276e4", + "registered": 1094212980, + "dob": 760934993, + "phone": "011-390-0414", + "cell": "081-942-2394", + "PPS": "2053741T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/11.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/11.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/11.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "mrs", + "first": "lillian", + "last": "parker" + }, + "location": { + "street": "4496 galway road", + "city": "Maynooth", + "state": "maryland", + "zip": 17742 + }, + "email": "lillian.parker@example.com", + "username": "bluepeacock832", + "password": "ronaldo", + "salt": "87PNF0pI", + "md5": "62ed1d158390e235be0f6cf9d425e6d8", + "sha1": "5e2ed60ebeef247f16e1fc64f2266b4f114675fd", + "sha256": "52a99fada97b103174e2586ecddba52b9a47893740b372734b306af04ca97315", + "registered": 1314528189, + "dob": 105777872, + "phone": "021-709-9767", + "cell": "081-040-7940", + "PPS": "5244307T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/64.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/64.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/64.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "miss", + "first": "melissa", + "last": "ferguson" + }, + "location": { + "street": "8289 novara avenue", + "city": "Celbridge", + "state": "idaho", + "zip": 47618 + }, + "email": "melissa.ferguson@example.com", + "username": "beautifulmouse223", + "password": "checkers", + "salt": "3Q24WZra", + "md5": "9a2d11ae15b0d1a3afe2aaa5c2eeb740", + "sha1": "6bf257fa650ce4ef1c2491cf9fcabf633db87368", + "sha256": "6e1974774906eccdbcce136a6d6c005a547700e07b999c0704b5be8638ee71c7", + "registered": 1022289528, + "dob": 1038938379, + "phone": "031-433-3489", + "cell": "081-558-7936", + "PPS": "1140426T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/96.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/96.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/96.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "ben", + "last": "hoffman" + }, + "location": { + "street": "6066 westmoreland street", + "city": "Mallow", + "state": "iowa", + "zip": 74036 + }, + "email": "ben.hoffman@example.com", + "username": "heavybird100", + "password": "sneaky", + "salt": "p2H013yr", + "md5": "e854e1dcf1d3f4ab27b8b573287a0b9a", + "sha1": "b027010c92857d28011e1973076d952868b037c5", + "sha256": "ce1ce3fefe395e9de58d9eda89f7720ef5a36652f166a21b297b35162d9ecec7", + "registered": 921530361, + "dob": 567002032, + "phone": "021-074-3589", + "cell": "081-926-8542", + "PPS": "7331821T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/54.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/54.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/54.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "ms", + "first": "rose", + "last": "andrews" + }, + "location": { + "street": "5268 dame street", + "city": "Kinsealy-Drinan", + "state": "new hampshire", + "zip": 29779 + }, + "email": "rose.andrews@example.com", + "username": "blueleopard465", + "password": "close-up", + "salt": "rhuGMqea", + "md5": "310e3e8e43635b408e06df9381d2a1fb", + "sha1": "f573fb69ad006a544b3a692bc9ffadbca7ffa35b", + "sha256": "fe07ef6ead18a57f3855d3f0e12f9d962a62af7a4377d8dbc3cdf8c706f8f00e", + "registered": 1388815597, + "dob": 47706698, + "phone": "051-108-3171", + "cell": "081-855-8055", + "PPS": "8101606T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/10.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/10.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/10.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "miss", + "first": "katherine", + "last": "caldwell" + }, + "location": { + "street": "7110 mill lane", + "city": "Carrick-on-Suir", + "state": "tennessee", + "zip": 99671 + }, + "email": "katherine.caldwell@example.com", + "username": "heavyelephant663", + "password": "741852", + "salt": "Hzj9rBmG", + "md5": "f81fffdb88c0a384c5fc84f86b2c3a75", + "sha1": "02e8d3075d79be282924801438b8bb42a1f3547d", + "sha256": "cba2ce93065e5b16d76913778e30e38408630cec1798c3f26cc7d4b1816851ae", + "registered": 1161023187, + "dob": 701759530, + "phone": "041-390-7363", + "cell": "081-252-5416", + "PPS": "3391139T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/1.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/1.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/1.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "mrs", + "first": "sara", + "last": "bradley" + }, + "location": { + "street": "2411 the grove", + "city": "Athy", + "state": "west virginia", + "zip": 23314 + }, + "email": "sara.bradley@example.com", + "username": "smallwolf152", + "password": "novifarm", + "salt": "y6r7jDDg", + "md5": "3f13f627f280b41873d23dcb2d5ecf1f", + "sha1": "505ce490ee265818971d2cb9ef9f9de9e270157f", + "sha256": "1da0a8d08f6e5d071d218fb310af399bd945664a6f6b160d81d0860988749c62", + "registered": 1342488881, + "dob": 102285632, + "phone": "051-648-9238", + "cell": "081-757-4961", + "PPS": "0685377T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/78.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/78.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/78.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "mrs", + "first": "amy", + "last": "nguyen" + }, + "location": { + "street": "4418 cork street", + "city": "Ardee", + "state": "ohio", + "zip": 79026 + }, + "email": "amy.nguyen@example.com", + "username": "greenfish392", + "password": "oldone", + "salt": "Dpbm2FPs", + "md5": "557e9ba10f9b93f8a04a949100ed79f2", + "sha1": "9a5b87919464938673653782cd2606795626fd86", + "sha256": "4804cba9d869f2f66f8df53b445499716d3136bbf2c9c208b1cc722858aff391", + "registered": 1207722692, + "dob": 475372382, + "phone": "041-327-1618", + "cell": "081-396-7770", + "PPS": "7926025T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/63.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/63.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/63.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "miss", + "first": "katie", + "last": "wells" + }, + "location": { + "street": "8669 o'connell street", + "city": "Portarlington", + "state": "alabama", + "zip": 97369 + }, + "email": "katie.wells@example.com", + "username": "blackdog195", + "password": "brian", + "salt": "u9PbTnvW", + "md5": "1e80a42838a6f30991833ba937935b1e", + "sha1": "ddcfdfd41ad7007593757298ae83b69d18d5500b", + "sha256": "b72d0bb3dce46bc6b6974f964e9e84f62cfd7efe759ed1cbc3f8ce766d37dacd", + "registered": 1432239303, + "dob": 296118926, + "phone": "011-720-8302", + "cell": "081-695-8649", + "PPS": "0629067T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/44.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/44.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/44.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "miss", + "first": "amanda", + "last": "king" + }, + "location": { + "street": "4499 denny street", + "city": "Newbridge", + "state": "rhode island", + "zip": 44248 + }, + "email": "amanda.king@example.com", + "username": "ticklishpeacock244", + "password": "sparks", + "salt": "rRGU9aU1", + "md5": "822a28743be9a5fe093f9d85abdbac35", + "sha1": "a222c75acd207fa2b162b3915aed16e1ab3ef0a9", + "sha256": "3bcee62160ed1d2c593d9f87d11d2f3182f21cdce6b8b63773777e949b74b134", + "registered": 1424179514, + "dob": 946787308, + "phone": "051-786-6046", + "cell": "081-192-3649", + "PPS": "7661254T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/56.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/56.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/56.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "blake", + "last": "bennett" + }, + "location": { + "street": "9291 the crescent", + "city": "Kilkenny", + "state": "rhode island", + "zip": 14072 + }, + "email": "blake.bennett@example.com", + "username": "greencat850", + "password": "vixen", + "salt": "x1yMFyJ5", + "md5": "fe639a4b14647b9891476883724d0470", + "sha1": "20797d3cd285c240d4c3f305bdc55e363788155f", + "sha256": "c14144114aa9d88c099b0e327c894c4129c37f3354a62e123cb07295fbc57fb6", + "registered": 1418393662, + "dob": 444857398, + "phone": "011-491-5127", + "cell": "081-055-6538", + "PPS": "7501706T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/53.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/53.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/53.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "ms", + "first": "megan", + "last": "martin" + }, + "location": { + "street": "6930 denny street", + "city": "Killarney", + "state": "alaska", + "zip": 90587 + }, + "email": "megan.martin@example.com", + "username": "tinybutterfly537", + "password": "124578", + "salt": "Ld8KDNky", + "md5": "4e333efcf1aad4dfb5b1d95511cffecd", + "sha1": "efab0aadb084b4e2a7d7258002713168fced7526", + "sha256": "644931c31701dde5f87da283da8f3d02b1bdfb9d329969c1a9257b9988bffca7", + "registered": 1352779567, + "dob": 692236522, + "phone": "071-256-9104", + "cell": "081-343-0972", + "PPS": "2371266T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/35.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/35.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/35.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "fernando", + "last": "kelly" + }, + "location": { + "street": "9311 killarney road", + "city": "Thurles", + "state": "kansas", + "zip": 77076 + }, + "email": "fernando.kelly@example.com", + "username": "yellowostrich795", + "password": "food", + "salt": "cK4u6bDP", + "md5": "ebf55a2a262841bb5d0d4a43c3694922", + "sha1": "2d3946fb5de0bcd30e951b57e1430713debbbdd0", + "sha256": "f44ce92b3af25f54e1e79f989342869d250081cb5afa0168283612340a20b984", + "registered": 1127086331, + "dob": 1305620648, + "phone": "071-965-1877", + "cell": "081-436-2855", + "PPS": "6559180T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/17.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/17.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/17.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "ms", + "first": "kristin", + "last": "lambert" + }, + "location": { + "street": "7786 park lane", + "city": "Castlebar", + "state": "alabama", + "zip": 81610 + }, + "email": "kristin.lambert@example.com", + "username": "blackbutterfly604", + "password": "goldberg", + "salt": "gsLYi2oY", + "md5": "eea7df877a2edb5c300ba1bc7886214d", + "sha1": "108beb2c7ae7efe7ae3e813591ee379968100c8e", + "sha256": "ee5ea2562bddebd63b1b86cb2a3fb4918c7e8915f81ce1db89be82cd2b56d70e", + "registered": 1361928742, + "dob": 1402483298, + "phone": "051-258-7394", + "cell": "081-105-1493", + "PPS": "8575531TA", + "picture": { + "large": "https://randomuser.me/api/portraits/women/7.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/7.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/7.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "marcus", + "last": "henderson" + }, + "location": { + "street": "8806 high street", + "city": "Tullow", + "state": "north dakota", + "zip": 69346 + }, + "email": "marcus.henderson@example.com", + "username": "brownbear298", + "password": "officer", + "salt": "7lXDGYC4", + "md5": "25597a15df9da76ab90c2d0ec81d6df3", + "sha1": "2cc478c9b10d5d80c62706d7f8f59f8f095b6920", + "sha256": "f674b66aec07b6f9c3789ed503e61e60821c12651325796487b8ee145583b001", + "registered": 1259496924, + "dob": 1265964717, + "phone": "011-975-3040", + "cell": "081-431-0220", + "PPS": "3937797T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/16.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/16.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/16.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "mason", + "last": "allen" + }, + "location": { + "street": "6998 ormond quay", + "city": "Waterford", + "state": "new jersey", + "zip": 72094 + }, + "email": "mason.allen@example.com", + "username": "purplebear881", + "password": "baritone", + "salt": "Dt2842CQ", + "md5": "a876ac30fd59d17840d4387013ed9751", + "sha1": "87584f164e30c3150d654eccefd36c720715b61b", + "sha256": "6ead298f77a690c9c0965d7e01e2fe09cbc4d3b064d0fa8b04757495fdf8587a", + "registered": 1410735457, + "dob": 793824908, + "phone": "031-756-3934", + "cell": "081-347-2314", + "PPS": "2907183T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/13.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/13.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/13.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "henry", + "last": "shelton" + }, + "location": { + "street": "7296 galway road", + "city": "Mullingar", + "state": "north carolina", + "zip": 17084 + }, + "email": "henry.shelton@example.com", + "username": "brownswan157", + "password": "castro", + "salt": "zRmWeLmI", + "md5": "18d98e56d1c74ec026d4dac71e590616", + "sha1": "1b5beb138bac8e5300d1b0144d8e77b095fa7a87", + "sha256": "ba99a281ad6b13967a406ca92b3e21df63dd61a17720d1608ec7fa712f5f1c5d", + "registered": 1411510313, + "dob": 742007623, + "phone": "071-212-7843", + "cell": "081-608-9968", + "PPS": "4134203T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/48.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/48.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/48.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "ted", + "last": "chambers" + }, + "location": { + "street": "6629 dublin road", + "city": "Sallins", + "state": "vermont", + "zip": 81600 + }, + "email": "ted.chambers@example.com", + "username": "brownladybug582", + "password": "pooper", + "salt": "sORJ3gqN", + "md5": "4b2a75f11037ca2986fa432631df497d", + "sha1": "7faf8598d4ee1f09dbd56bcaddff0823d3760bea", + "sha256": "39751f31d0084bb7c56df4b1df2fbf2518fda9841372b9e4c6a8ebf402d7097a", + "registered": 1053858364, + "dob": 1229309582, + "phone": "021-109-5443", + "cell": "081-767-7552", + "PPS": "5897880T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/71.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/71.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/71.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "sean", + "last": "edwards" + }, + "location": { + "street": "3194 station road", + "city": "Arklow", + "state": "new jersey", + "zip": 41144 + }, + "email": "sean.edwards@example.com", + "username": "purplepeacock815", + "password": "xmas", + "salt": "pHRszydZ", + "md5": "f6705c58fb595e56b9cac4a4c7e98fe2", + "sha1": "3d5fe38150cc095f3021d93648d6b3deb6e25a15", + "sha256": "95fd1173a1595d060ff427b7bac2bf418e3655de8c26dd8085d7db039ffd2d61", + "registered": 1325856914, + "dob": 830744751, + "phone": "051-336-1396", + "cell": "081-995-0980", + "PPS": "5164784T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/22.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/22.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/22.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "miss", + "first": "alison", + "last": "hamilton" + }, + "location": { + "street": "2482 strand road", + "city": "Loughrea", + "state": "new hampshire", + "zip": 41353 + }, + "email": "alison.hamilton@example.com", + "username": "bluesnake225", + "password": "billbill", + "salt": "lwpNOCdL", + "md5": "87005efa416190aa9571e27741b7bc32", + "sha1": "5157beda1aeecf40db41b0a926f2ad044c9c3418", + "sha256": "1e8bc030fd11eb07ee6954a824bfde8ac3ec52087929d8bf91d37bb76f525d86", + "registered": 1070906026, + "dob": 199235472, + "phone": "041-405-2362", + "cell": "081-024-9253", + "PPS": "0505044T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/24.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/24.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/24.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "ms", + "first": "sheryl", + "last": "brooks" + }, + "location": { + "street": "6812 dublin road", + "city": "Gorey", + "state": "south dakota", + "zip": 11091 + }, + "email": "sheryl.brooks@example.com", + "username": "bigbear381", + "password": "bulls", + "salt": "QH8BtspJ", + "md5": "c346540ac9dc69575301f853125b40f4", + "sha1": "0cfc8c28711d872bd7be6632f7dcae17fa1f8c52", + "sha256": "206173935b05c9e8ee33ee35128837ed725114c938686ebd12b86b0781dd8c17", + "registered": 1417776517, + "dob": 219649055, + "phone": "011-426-3533", + "cell": "081-896-0371", + "PPS": "3704225T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/86.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/86.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/86.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "adam", + "last": "gordon" + }, + "location": { + "street": "4916 grange road", + "city": "Limerick", + "state": "montana", + "zip": 19103 + }, + "email": "adam.gordon@example.com", + "username": "organicrabbit668", + "password": "wally", + "salt": "CNChm60O", + "md5": "bf1c29801ec4f88a1411a831a33f69bb", + "sha1": "ad507bf46bfeae2249de5c7d9db668a287cec4a8", + "sha256": "c46bd3ea49b1bd1deae047cce94b618c3cc6207f52088b7513abdce0c8db0087", + "registered": 927745387, + "dob": 738521747, + "phone": "011-436-9673", + "cell": "081-123-5056", + "PPS": "8582385T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/78.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/78.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/78.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "ms", + "first": "alex", + "last": "brewer" + }, + "location": { + "street": "8042 rochestown road", + "city": "Mountmellick", + "state": "alaska", + "zip": 65449 + }, + "email": "alex.brewer@example.com", + "username": "silverbutterfly283", + "password": "never", + "salt": "hAETBFmu", + "md5": "b04782906c122c250bf0c1271519416f", + "sha1": "517a91e3d955d3b4cf3ac835e486b330115f9240", + "sha256": "7e52170215ce29c2cf65a79e5ecfc6870c7b3f4b91a78ddcb27cac4ed778c907", + "registered": 1019818284, + "dob": 881957062, + "phone": "071-047-6304", + "cell": "081-838-1980", + "PPS": "0373225T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/91.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/91.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/91.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "randall", + "last": "gibson" + }, + "location": { + "street": "6558 mill road", + "city": "Greystones", + "state": "tennessee", + "zip": 62456 + }, + "email": "randall.gibson@example.com", + "username": "tinyfrog703", + "password": "puddin", + "salt": "tHYtCt28", + "md5": "cb890ced49a8f006647004d5525acbe6", + "sha1": "ecf9c6223f47e5d8fc2f0f756a9cb1ee2295d224", + "sha256": "6348438b48bbcad5071d1510738a9aba4b9e12cf7cc5c91f8b4d4ac4ee19b85f", + "registered": 1382532481, + "dob": 484372270, + "phone": "011-772-7633", + "cell": "081-577-5680", + "PPS": "6424317T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/12.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/12.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/12.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "miss", + "first": "eliza", + "last": "cook" + }, + "location": { + "street": "9008 the avenue", + "city": "Passage West", + "state": "washington", + "zip": 32077 + }, + "email": "eliza.cook@example.com", + "username": "bluefrog171", + "password": "friend", + "salt": "xjIbOqXp", + "md5": "8b2d968b0290f7a0e420fec833565836", + "sha1": "1064efe148d0968a23e9205e58b491d9225cdaac", + "sha256": "debc1613df4b6e8220e01ed0301fef9cb41021126cb4bc67dfaa4e1b47d442db", + "registered": 1128366497, + "dob": 451050418, + "phone": "061-429-9734", + "cell": "081-496-3421", + "PPS": "7481280T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/62.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/62.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/62.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "mrs", + "first": "amy", + "last": "hansen" + }, + "location": { + "street": "2622 green lane", + "city": "Ratoath", + "state": "texas", + "zip": 79980 + }, + "email": "amy.hansen@example.com", + "username": "beautifulswan831", + "password": "just4fun", + "salt": "aHJok7DP", + "md5": "28288c0b51f25f7fced864b0b83aa7a3", + "sha1": "5ed0b687098d3db7aadd90a9f94ab95bfb8dd077", + "sha256": "508b2111bc3d4cdef5cecaae2a2b6296b08f2beb6f5247fe24a6cb8926b3a761", + "registered": 1078325849, + "dob": 91602186, + "phone": "051-528-7929", + "cell": "081-826-4886", + "PPS": "2518522T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/34.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/34.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/34.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "john", + "last": "wallace" + }, + "location": { + "street": "7348 the avenue", + "city": "Tuam", + "state": "illinois", + "zip": 52871 + }, + "email": "john.wallace@example.com", + "username": "yellowelephant285", + "password": "infantry", + "salt": "eF2hvNgI", + "md5": "4424816b186a17e53f6ab411294dd993", + "sha1": "78c5b05999b876a093d329a4235d0902fd89c23f", + "sha256": "3ef7321aa09b09eeba37225be12475c44330b94c3f55fe646398e986823df20f", + "registered": 1323610177, + "dob": 962253785, + "phone": "011-317-5827", + "cell": "081-087-0635", + "PPS": "5633289T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/24.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/24.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/24.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "mrs", + "first": "diane", + "last": "stephens" + }, + "location": { + "street": "7224 killarney road", + "city": "Oranmore", + "state": "wyoming", + "zip": 72613 + }, + "email": "diane.stephens@example.com", + "username": "blackwolf580", + "password": "rebelz", + "salt": "DdUmtcun", + "md5": "0afe3fe55ecbebac53bbf4f1d62df63e", + "sha1": "08d0e36658d09071c358f0e8177e9afd1ff7446c", + "sha256": "0676f6e31e9f120a305c09310d7a523ceefcb81e91a47584a602e8738300be54", + "registered": 1199909623, + "dob": 168510984, + "phone": "071-500-8027", + "cell": "081-419-2255", + "PPS": "5528892T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/34.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/34.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/34.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "ms", + "first": "alex", + "last": "mitchell" + }, + "location": { + "street": "8747 south street", + "city": "Bandon", + "state": "montana", + "zip": 78418 + }, + "email": "alex.mitchell@example.com", + "username": "tinyleopard502", + "password": "hillary", + "salt": "4jtHW9Un", + "md5": "1d8fa7be8921e4a54e1d191456e10e17", + "sha1": "e4e20cfa476e818753eec3240b2c403114686f3c", + "sha256": "68df408a5fcb5d02625a43cd6fb37816530ff3751f0fe3e12602cd6104674767", + "registered": 1053870068, + "dob": 1174845962, + "phone": "051-706-8637", + "cell": "081-730-0175", + "PPS": "2696660T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/53.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/53.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/53.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "danny", + "last": "neal" + }, + "location": { + "street": "2784 denny street", + "city": "Greystones", + "state": "alabama", + "zip": 65413 + }, + "email": "danny.neal@example.com", + "username": "lazymouse560", + "password": "gabby", + "salt": "AXApwcOh", + "md5": "382b839e09f8b193c72263f1c5b85fbc", + "sha1": "c890e2c4ee7698904a57c103fb9a6cf3ea7dc1dc", + "sha256": "3c9537a236491eba6e4d725fe47b32e20759014dfa3f8d9e961a9d4e1f8ee26e", + "registered": 1259469661, + "dob": 124143691, + "phone": "041-565-2430", + "cell": "081-090-4543", + "PPS": "9706129T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/33.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/33.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/33.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "francis", + "last": "ray" + }, + "location": { + "street": "1377 green lane", + "city": "Cobh", + "state": "maryland", + "zip": 23096 + }, + "email": "francis.ray@example.com", + "username": "silverswan570", + "password": "616161", + "salt": "mI3SVFWT", + "md5": "cfb5b23535a91063da04d82ba4968123", + "sha1": "6be02dcc74bd40b6bc54db40528b04d01293f86e", + "sha256": "d74a651d399eb06b35516ace1344881e4c6dbad1d6b71d745b6513305f3ee8cf", + "registered": 1041225244, + "dob": 65222509, + "phone": "021-143-9942", + "cell": "081-304-0890", + "PPS": "8746656T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/49.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/49.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/49.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "alvin", + "last": "white" + }, + "location": { + "street": "2685 park lane", + "city": "Kildare", + "state": "oklahoma", + "zip": 40271 + }, + "email": "alvin.white@example.com", + "username": "purplelion759", + "password": "doughnut", + "salt": "K3egenAH", + "md5": "8979a30396c9d5d68e37bd97184a22c8", + "sha1": "9e7df240f196fd6b371527ba1c5cd9fc872fed1e", + "sha256": "10a199ddf64bdf4ef1f8bebc38dd2aade60d47d951f16e39f4da41f33eafc508", + "registered": 1118254511, + "dob": 802274062, + "phone": "061-262-2775", + "cell": "081-039-7245", + "PPS": "8407323T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/46.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/46.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/46.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "walter", + "last": "smythe" + }, + "location": { + "street": "8485 boghall road", + "city": "Kildare", + "state": "rhode island", + "zip": 50269 + }, + "email": "walter.smythe@example.com", + "username": "heavybutterfly622", + "password": "sucking", + "salt": "9vxGlFWX", + "md5": "74605c416e65880e0c6e18507503f561", + "sha1": "d3542f1cd948dff7156b3b4a822fb2bc8472ef4d", + "sha256": "6007eb13dd433b4d023b5938a7e37455c0f925c590e29fcc355afbc1dd723c40", + "registered": 1328753996, + "dob": 765400665, + "phone": "031-245-1812", + "cell": "081-917-4018", + "PPS": "4491944T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/91.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/91.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/91.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "ms", + "first": "grace", + "last": "stephens" + }, + "location": { + "street": "7458 novara avenue", + "city": "Roscrea", + "state": "indiana", + "zip": 53189 + }, + "email": "grace.stephens@example.com", + "username": "silvertiger631", + "password": "biit", + "salt": "3mO2iLAW", + "md5": "b58a4ad10be6f231094c596ab44339e6", + "sha1": "259562b85ed29a2d46dcac7a0126cfacd4bee81d", + "sha256": "17fd7a317f9a969a4f4aeee7410cdf603f4a22e2847317c425000998eab02625", + "registered": 1356986307, + "dob": 478731014, + "phone": "041-711-1719", + "cell": "081-125-9893", + "PPS": "3592460T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/25.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/25.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/25.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "oscar", + "last": "stevens" + }, + "location": { + "street": "3990 church street", + "city": "Tullow", + "state": "vermont", + "zip": 74647 + }, + "email": "oscar.stevens@example.com", + "username": "smallrabbit490", + "password": "cattle", + "salt": "KEFyaoHN", + "md5": "a7a9fbf2ab060af617b2260d6c24b6a3", + "sha1": "d6940c985467b3d3fba6cd5272b381d759426901", + "sha256": "d7278f9fc4b189ad0341e8fe77aaecdf4a084f7ddbca6027dfc1595bbddda832", + "registered": 1352081427, + "dob": 1038822078, + "phone": "021-191-6032", + "cell": "081-867-6439", + "PPS": "6136422T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/83.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/83.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/83.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "ms", + "first": "susanne", + "last": "peters" + }, + "location": { + "street": "7904 north road", + "city": "Gorey", + "state": "south dakota", + "zip": 72594 + }, + "email": "susanne.peters@example.com", + "username": "redsnake958", + "password": "meowmeow", + "salt": "M3WQkqrW", + "md5": "ddfe5b3fb01f98f4a525eb46adcd3123", + "sha1": "8685c8c0e2c6234083996b4f740b47bb39cd14e1", + "sha256": "29c8f49a46fd8a050eb467d209ad968d1d8176f2775f3a210666a1242f4ef3b8", + "registered": 958570540, + "dob": 1259411616, + "phone": "041-594-8241", + "cell": "081-650-1535", + "PPS": "7897107T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/57.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/57.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/57.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "mrs", + "first": "alex", + "last": "weaver" + }, + "location": { + "street": "3730 high street", + "city": "Athlone", + "state": "minnesota", + "zip": 54022 + }, + "email": "alex.weaver@example.com", + "username": "bluebear966", + "password": "republic", + "salt": "Ydcc0rrt", + "md5": "faafa79fe47b7b14ceba8a18e67d7c16", + "sha1": "2d07e68c37b68e8078e048049cc6dafff7237576", + "sha256": "adc09a9cf0868d0efcbf3894c2a314b4d8c52c4419bbbb9368023ad9da29e3e2", + "registered": 1051376017, + "dob": 47355091, + "phone": "051-382-8814", + "cell": "081-452-4664", + "PPS": "3124689T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/67.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/67.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/67.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "ms", + "first": "tammy", + "last": "harris" + }, + "location": { + "street": "3708 church street", + "city": "Maynooth", + "state": "mississippi", + "zip": 72315 + }, + "email": "tammy.harris@example.com", + "username": "bigrabbit420", + "password": "internet", + "salt": "gGo06qBk", + "md5": "2d2ba3a312788a4409c683f39593dbd1", + "sha1": "2d37557e12e8162804872e0d36bff4ad508f1d52", + "sha256": "394ba21061c02abb3ffd1186da222d49effde5fa54d89d76845cd7e8e3370788", + "registered": 974575918, + "dob": 1087060878, + "phone": "071-286-5808", + "cell": "081-258-0978", + "PPS": "8512356T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/14.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/14.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/14.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "herman", + "last": "lopez" + }, + "location": { + "street": "8785 the avenue", + "city": "Waterford", + "state": "kansas", + "zip": 28773 + }, + "email": "herman.lopez@example.com", + "username": "crazymeercat412", + "password": "seeking", + "salt": "MuFyb5jt", + "md5": "37945210dd08ef807a8f2a978a311dfd", + "sha1": "afc7f1fb1623d9990061363a087495058f430286", + "sha256": "32d47ca93eadfc120d7d0bc3c73af6e03026b1d1d37a172d4037e56eb0264cf5", + "registered": 1032484755, + "dob": 300609411, + "phone": "051-079-4026", + "cell": "081-603-9493", + "PPS": "7793008T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/21.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/21.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/21.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "mrs", + "first": "allie", + "last": "simmons" + }, + "location": { + "street": "5136 richmond park", + "city": "Portmarnock", + "state": "montana", + "zip": 58150 + }, + "email": "allie.simmons@example.com", + "username": "organicpeacock979", + "password": "moon", + "salt": "XVxqvj7c", + "md5": "f930135b948b81825207a75344f7435d", + "sha1": "1d17348e3dcc187f354c9e97d2a084be9a6e5065", + "sha256": "b45dd8e7d8baf002ba49ab9fcca70862ec06b3cf544860dec7a66e5bd21f410a", + "registered": 955699938, + "dob": 369643881, + "phone": "031-811-0748", + "cell": "081-153-5864", + "PPS": "5937691T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/69.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/69.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/69.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "ms", + "first": "janet", + "last": "ward" + }, + "location": { + "street": "4281 church road", + "city": "Clane", + "state": "iowa", + "zip": 19551 + }, + "email": "janet.ward@example.com", + "username": "smallgorilla636", + "password": "asia", + "salt": "FFCTxch2", + "md5": "afabedb2ad6ee8eed0f3e28c4e1339e5", + "sha1": "59cf09eefe0b9e9fc121ad5c545250a7cb77661f", + "sha256": "6b1a0d4d88df3187712eacfc89720cf8edfcbd282c3c1a22fedb488b34de9612", + "registered": 1265612426, + "dob": 1403715278, + "phone": "071-278-4522", + "cell": "081-467-0687", + "PPS": "5304894TA", + "picture": { + "large": "https://randomuser.me/api/portraits/women/94.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/94.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/94.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "jose", + "last": "cook" + }, + "location": { + "street": "3830 park lane", + "city": "Castlebar", + "state": "north dakota", + "zip": 71061 + }, + "email": "jose.cook@example.com", + "username": "redgoose581", + "password": "paradise", + "salt": "iTZPuzmz", + "md5": "c57198dbcd0269bdc22782b0836faa5c", + "sha1": "ea65d6d71a4b3a259f98ec36d788651c1f3e72d2", + "sha256": "9d079b4c2f5fc6024640d5b31411b6634d88a1f705620c6c64c476fdb229f7ad", + "registered": 1301035702, + "dob": 1103940417, + "phone": "061-816-8533", + "cell": "081-336-8120", + "PPS": "4703921T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/93.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/93.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/93.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "miss", + "first": "rosalyn", + "last": "lopez" + }, + "location": { + "street": "9590 new road", + "city": "Donabate", + "state": "georgia", + "zip": 21062 + }, + "email": "rosalyn.lopez@example.com", + "username": "orangerabbit241", + "password": "q1w2e3r4", + "salt": "t8M5swk9", + "md5": "44888ce2caf0600415b6402e57830c89", + "sha1": "7f7bf92dcbc9f5d36f79965113a2bd487eddcd52", + "sha256": "a22052ff187eb8694f294921609cf643069c1ef2e314a95b2d00c15e661f97c8", + "registered": 1434588863, + "dob": 512267533, + "phone": "051-991-4356", + "cell": "081-658-9976", + "PPS": "5329940T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/68.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/68.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/68.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "joshua", + "last": "parker" + }, + "location": { + "street": "3664 church road", + "city": "Tuam", + "state": "west virginia", + "zip": 83238 + }, + "email": "joshua.parker@example.com", + "username": "bigbird310", + "password": "goodie", + "salt": "S2FyFzIX", + "md5": "cc8281369d4b36ca847d70af41994fa6", + "sha1": "906119b3a40745827fe09c71de0b20f0db641508", + "sha256": "d2078c363a507d64a8c5f6a2ea0a56d8841504e90d6dda927a123114a4cc8b10", + "registered": 1409903541, + "dob": 1260791035, + "phone": "071-612-8463", + "cell": "081-475-2911", + "PPS": "3269538T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/99.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/99.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/99.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "mrs", + "first": "emily", + "last": "simmmons" + }, + "location": { + "street": "8195 church street", + "city": "Trim", + "state": "pennsylvania", + "zip": 17207 + }, + "email": "emily.simmmons@example.com", + "username": "organicwolf260", + "password": "2323", + "salt": "3rLvIpXB", + "md5": "7fffd22d5576301983de7a1d0d22d2a1", + "sha1": "7b0b0de33c94309df9a9f6a202c8398acacf695a", + "sha256": "125c1a97e9792225c505ff2e04b74da24ef25ee82ea1ae06873a9cfe2f40a438", + "registered": 1088703475, + "dob": 1402603839, + "phone": "061-165-5228", + "cell": "081-863-6482", + "PPS": "8916347TA", + "picture": { + "large": "https://randomuser.me/api/portraits/women/62.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/62.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/62.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "kenneth", + "last": "hart" + }, + "location": { + "street": "8884 springfield road", + "city": "Midleton", + "state": "georgia", + "zip": 61074 + }, + "email": "kenneth.hart@example.com", + "username": "bluerabbit408", + "password": "airwolf", + "salt": "e9BT8EQu", + "md5": "557ca3502405035e3d1cc2046a6c6a38", + "sha1": "1ebdeb4a47ccb7aaaefd9940ab163803d31786e7", + "sha256": "276f30d29431e91a6c30e9f46c1a4758c96c06c06bd01f274e47ba94b3a39bae", + "registered": 937995399, + "dob": 131399048, + "phone": "061-760-0690", + "cell": "081-867-5516", + "PPS": "8540785T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/60.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/60.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/60.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "ms", + "first": "alison", + "last": "fox" + }, + "location": { + "street": "3865 henry street", + "city": "Passage West", + "state": "alabama", + "zip": 75180 + }, + "email": "alison.fox@example.com", + "username": "tinygorilla501", + "password": "qing", + "salt": "ieZgGz3U", + "md5": "c9aca130e6d1c75d6e8d422410b795e9", + "sha1": "9907d3ae8b8d615fc71564826e4c4adb572b9baa", + "sha256": "1e010f08a196eb6f55546f77192357126719eca90cfc9d3e84c6bf3281da7270", + "registered": 1210027039, + "dob": 686257757, + "phone": "021-396-9938", + "cell": "081-393-2548", + "PPS": "1033762T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/41.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/41.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/41.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "miss", + "first": "heather", + "last": "terry" + }, + "location": { + "street": "9884 denny street", + "city": "Balbriggan", + "state": "pennsylvania", + "zip": 75095 + }, + "email": "heather.terry@example.com", + "username": "lazypeacock596", + "password": "sneakers", + "salt": "iPE04bnH", + "md5": "2ba3bb2f0511caffa374ca91976b9c91", + "sha1": "259b346095a9d64899ceddc546e378736ce896ee", + "sha256": "2b01ccf79d1e823a64119c6bbe059fcdb7f3118456dc762512eb44cd4302bfa4", + "registered": 1358935984, + "dob": 1236662626, + "phone": "071-253-0657", + "cell": "081-718-4869", + "PPS": "2063640T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/32.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/32.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/32.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "ms", + "first": "susanna", + "last": "bell" + }, + "location": { + "street": "9056 pearse street", + "city": "Leixlip", + "state": "montana", + "zip": 26658 + }, + "email": "susanna.bell@example.com", + "username": "blackgoose808", + "password": "spike", + "salt": "Vkb3R1xO", + "md5": "cfb6c87661511d838f39e75beb9b61e8", + "sha1": "31fb4c5b2af746087790a50d2a12d1dd5340d6e3", + "sha256": "8b32432b68fa08d137b6b2b1ff8a857ddc960c93e166cf5c3f095a9d0aacac46", + "registered": 1338386522, + "dob": 163103035, + "phone": "051-824-1086", + "cell": "081-808-7551", + "PPS": "8899549T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/94.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/94.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/94.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "miss", + "first": "kristin", + "last": "fitzgerald" + }, + "location": { + "street": "5510 new street", + "city": "Cobh", + "state": "california", + "zip": 13293 + }, + "email": "kristin.fitzgerald@example.com", + "username": "silverbird814", + "password": "impreza", + "salt": "M8OYHAqF", + "md5": "788deed35b0a79f454106eb360f61781", + "sha1": "01f4cf8eba36dff8051bdde2fd73eaed5db6479d", + "sha256": "2274c7efaaafbdba03549e6c3a2d0799ee35bf3ead3ea56c217dcab6cba9ba09", + "registered": 1127461819, + "dob": 419377932, + "phone": "051-287-4681", + "cell": "081-453-9022", + "PPS": "3382895T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/79.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/79.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/79.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "gavin", + "last": "mitchell" + }, + "location": { + "street": "6719 church road", + "city": "Fermoy", + "state": "virginia", + "zip": 47047 + }, + "email": "gavin.mitchell@example.com", + "username": "tinyswan441", + "password": "helpme", + "salt": "2YtRhH9B", + "md5": "fef598555ac7dff700ed04ba8cf8ad47", + "sha1": "cb5d21248c64f5a6b20d2a5a835cd966fb4ef721", + "sha256": "79fa3ffb111f75a01b922fc42197f794d5983f6055d4530ba5ded2b9597440ff", + "registered": 1344160899, + "dob": 1416611729, + "phone": "021-115-8395", + "cell": "081-482-0914", + "PPS": "7416954TA", + "picture": { + "large": "https://randomuser.me/api/portraits/men/21.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/21.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/21.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "miss", + "first": "eva", + "last": "boyd" + }, + "location": { + "street": "7800 dame street", + "city": "Balbriggan", + "state": "delaware", + "zip": 59288 + }, + "email": "eva.boyd@example.com", + "username": "redostrich994", + "password": "burly", + "salt": "9wcISpzb", + "md5": "c91c04a5f8b783c6b1c1cbc4df771596", + "sha1": "721cf866459298ffa8638b80ffedad805f15a7f1", + "sha256": "50d54fb579c85f54a250993d6840eb3becb43db6144aefdffad23994518e8ce2", + "registered": 1087560220, + "dob": 159052991, + "phone": "011-582-7720", + "cell": "081-511-2220", + "PPS": "2821703T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/79.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/79.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/79.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "sam", + "last": "brown" + }, + "location": { + "street": "1237 westmoreland street", + "city": "Roscrea", + "state": "michigan", + "zip": 96011 + }, + "email": "sam.brown@example.com", + "username": "smallswan737", + "password": "elliot", + "salt": "iV91yFTT", + "md5": "ff97d0841e622d0df76c1877a02183d1", + "sha1": "e183342803062c048094ce2742482f260407f351", + "sha256": "af77f194a2fed0f6e3a032bb9c24ee61f7b7a51a81144cf285a84911e0b7ed3f", + "registered": 1386846530, + "dob": 1224644598, + "phone": "011-522-4132", + "cell": "081-037-2417", + "PPS": "2879917T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/25.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/25.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/25.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "louis", + "last": "stone" + }, + "location": { + "street": "4295 tara street", + "city": "Clonmel", + "state": "rhode island", + "zip": 73117 + }, + "email": "louis.stone@example.com", + "username": "lazykoala470", + "password": "wanda", + "salt": "jKJhPzxe", + "md5": "afc5ffa818e57dc607d14b61f9807d2b", + "sha1": "c4eb4a118ebd313d09ec19e6b66fcda3d357673c", + "sha256": "7b4cb7577a8f5c51026b5bfdf936af16bc0541ca72d9cc824e322d0675faa874", + "registered": 1220144114, + "dob": 1287392679, + "phone": "041-161-4669", + "cell": "081-358-4434", + "PPS": "7983752T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/41.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/41.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/41.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "josef", + "last": "hansen" + }, + "location": { + "street": "2272 the drive", + "city": "Thurles", + "state": "delaware", + "zip": 25912 + }, + "email": "josef.hansen@example.com", + "username": "tinyfish396", + "password": "allison", + "salt": "QroRz6DK", + "md5": "710df1bda1d154cd257a681e38d94c82", + "sha1": "b4db6a78ab119f7d6c94bd9d88fcd5c4b4730654", + "sha256": "805c02896d8fe3b7115433d0791cc9f6416999fc2a2d5edde825e1404b4976e1", + "registered": 1049978833, + "dob": 1305039610, + "phone": "051-334-5586", + "cell": "081-882-1153", + "PPS": "3133291T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/73.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/73.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/73.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "ms", + "first": "julia", + "last": "elliott" + }, + "location": { + "street": "6136 denny street", + "city": "Donabate", + "state": "georgia", + "zip": 52933 + }, + "email": "julia.elliott@example.com", + "username": "bluetiger240", + "password": "bird", + "salt": "8DK9RekZ", + "md5": "78cf3467c37cc104fefaef380fcf74f4", + "sha1": "95eccc3cbf1bfb7845a21e258f20f59bd44ed85c", + "sha256": "cded81250585a466df564bbb2348feb63e92935935efd44300efd30a2189b1bf", + "registered": 1111676436, + "dob": 464143597, + "phone": "071-845-9150", + "cell": "081-086-4203", + "PPS": "2305200T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/14.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/14.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/14.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "ms", + "first": "linda", + "last": "freeman" + }, + "location": { + "street": "9368 henry street", + "city": "Ballinasloe", + "state": "new mexico", + "zip": 19112 + }, + "email": "linda.freeman@example.com", + "username": "heavykoala323", + "password": "circle", + "salt": "FIEVKCB2", + "md5": "2c9e2e6a460f6af82e764a8dc8013ecf", + "sha1": "8badc394cd0d346617f0bb45f21e69622ed88cae", + "sha256": "36c068f4279d9e4e568f0489299f20a1534ef2b5a2a785d2d08e6d3bb423edb8", + "registered": 1190750844, + "dob": 661950245, + "phone": "021-970-5490", + "cell": "081-859-7104", + "PPS": "9196667T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/12.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/12.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/12.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "mrs", + "first": "vanessa", + "last": "bryant" + }, + "location": { + "street": "4409 park lane", + "city": "Clonmel", + "state": "south carolina", + "zip": 55242 + }, + "email": "vanessa.bryant@example.com", + "username": "greenpeacock236", + "password": "ministry", + "salt": "d4eE5JBJ", + "md5": "32f1f02be1abf3fb7b7f9f4ea24621bd", + "sha1": "99350ee6a8522a632bb2c60c970561e8e316bbbb", + "sha256": "2c529c875792d556d6f9c8bae64d5fe093ad6a26677b780d2145bd447a52271d", + "registered": 1061468057, + "dob": 155382107, + "phone": "061-047-9005", + "cell": "081-373-5984", + "PPS": "1611449T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/86.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/86.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/86.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "mrs", + "first": "judy", + "last": "cooper" + }, + "location": { + "street": "2591 richmond park", + "city": "Clonakilty", + "state": "wyoming", + "zip": 90961 + }, + "email": "judy.cooper@example.com", + "username": "crazysnake882", + "password": "tasha", + "salt": "hiay1DZX", + "md5": "b5ca09764791a2df1326c91cfb0a14e4", + "sha1": "da904235877faf0afcb84b67fede9ff557aee795", + "sha256": "3ffcb2ca2361b64dc0df095012e8205ee2fd2897362dc2ec17d604d4e36870a8", + "registered": 947139799, + "dob": 943617171, + "phone": "051-266-7402", + "cell": "081-388-9858", + "PPS": "3979187T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/33.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/33.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/33.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "isaac", + "last": "lawson" + }, + "location": { + "street": "7880 station road", + "city": "Tullow", + "state": "louisiana", + "zip": 52548 + }, + "email": "isaac.lawson@example.com", + "username": "bigbird633", + "password": "1515", + "salt": "nBux9NqZ", + "md5": "fb316654c481f8edec1f2762452dcde3", + "sha1": "328ff2935cc0d86100394371fa77b29ab3a52683", + "sha256": "bb71ba540eccb0ae6490e11c37805a4122b342c5cab48364f880a9c109bdd7a4", + "registered": 942113013, + "dob": 3601876, + "phone": "061-814-2080", + "cell": "081-667-2414", + "PPS": "0462526T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/82.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/82.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/82.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "miss", + "first": "sheryl", + "last": "mitchelle" + }, + "location": { + "street": "9616 boghall road", + "city": "Duleek", + "state": "georgia", + "zip": 26758 + }, + "email": "sheryl.mitchelle@example.com", + "username": "silverdog417", + "password": "mistress", + "salt": "IzTHuTQH", + "md5": "5bdf37891e0a38f85dca3d4c282a7dbf", + "sha1": "44cbdfb113c58b5e80b89543691a945fe190c226", + "sha256": "d147966c0660a9f07fa939bfccb3391c7dcb08603612a4a7f1f74117416f92cc", + "registered": 1302447975, + "dob": 252212933, + "phone": "071-301-9136", + "cell": "081-610-3474", + "PPS": "0000589T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/62.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/62.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/62.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "miss", + "first": "emma", + "last": "woods" + }, + "location": { + "street": "2861 dublin road", + "city": "Mallow", + "state": "missouri", + "zip": 74264 + }, + "email": "emma.woods@example.com", + "username": "orangeostrich701", + "password": "dentist", + "salt": "cnXyu4TL", + "md5": "c387fbc55fc25a5db4f3c6bfe748918c", + "sha1": "c9fb80098fe7cfa9a8194b2fed50416ac49201d5", + "sha256": "82044356ff5399d1fc5323e8960934e8672de7ca63adec8f9214280bb1b4f26c", + "registered": 1279457891, + "dob": 1017165058, + "phone": "041-708-1961", + "cell": "081-310-5138", + "PPS": "1815399T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/17.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/17.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/17.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "mrs", + "first": "kelly", + "last": "davis" + }, + "location": { + "street": "7490 mill lane", + "city": "Gorey", + "state": "louisiana", + "zip": 95042 + }, + "email": "kelly.davis@example.com", + "username": "silverbutterfly832", + "password": "gigi", + "salt": "N9e8SWQH", + "md5": "8625ff11eb8120b7c8e4917147afd43e", + "sha1": "71e2564d358507ce5130a2041cfbff472e320de8", + "sha256": "504d7c7875f701a6d8c8a2da8db2625dccfc410a388f96aa1991b15d4559ec07", + "registered": 1228516868, + "dob": 951772444, + "phone": "071-539-4428", + "cell": "081-281-2350", + "PPS": "8739098T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/89.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/89.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/89.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "miss", + "first": "amber", + "last": "caldwell" + }, + "location": { + "street": "5322 the avenue", + "city": "Dungarvan", + "state": "virginia", + "zip": 31036 + }, + "email": "amber.caldwell@example.com", + "username": "tinylion877", + "password": "holidays", + "salt": "BMBiBa1J", + "md5": "9cae328f78b54e4bb14a741c0bba1959", + "sha1": "30b131dd155d1f8099aec14fa8e44fb9be2ee955", + "sha256": "d079eb5d56c00bcccbdcdcf89cb9d542d7c71410522b0da3404eb61e86f1f214", + "registered": 1152898385, + "dob": 1290287850, + "phone": "041-035-1802", + "cell": "081-574-7201", + "PPS": "3881482T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/70.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/70.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/70.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "vincent", + "last": "burke" + }, + "location": { + "street": "9807 alexander road", + "city": "Carlow", + "state": "south dakota", + "zip": 71311 + }, + "email": "vincent.burke@example.com", + "username": "bigwolf759", + "password": "megapass", + "salt": "t9GfZ9Hl", + "md5": "5aff94407e2d3995995fdd5d6b86fbfc", + "sha1": "f51d93ae254cefdf2664f1874ccf432d6ef23b94", + "sha256": "a0efdf55bec6523e94fe862e998f60d48146e95660c0ee29d7e8f8a5108fe0b4", + "registered": 994610350, + "dob": 1041105417, + "phone": "071-831-7890", + "cell": "081-592-9672", + "PPS": "3308781T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/41.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/41.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/41.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "dave", + "last": "wilson" + }, + "location": { + "street": "6284 highfield road", + "city": "Duleek", + "state": "new mexico", + "zip": 40071 + }, + "email": "dave.wilson@example.com", + "username": "beautifulfish272", + "password": "dolores", + "salt": "Pz3POvtv", + "md5": "4acfb0c77438d497c8c4b3afe397d051", + "sha1": "6f4ad6c3f5e7ee0b45952dae37e15754ea8e8427", + "sha256": "761c794c7238ffa0c954fdc8213fd3bfbf5f63392db5b87595d9fbb3a74650e8", + "registered": 1068894599, + "dob": 1102639089, + "phone": "051-086-6568", + "cell": "081-650-0312", + "PPS": "8499104T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/92.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/92.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/92.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "guy", + "last": "doyle" + }, + "location": { + "street": "5622 springfield road", + "city": "Fermoy", + "state": "illinois", + "zip": 77970 + }, + "email": "guy.doyle@example.com", + "username": "beautifulwolf578", + "password": "quest1", + "salt": "cS2MB8BZ", + "md5": "bcc11061864160982e8c9d663f32ace8", + "sha1": "936f6189b394841b532a97f030e17a2e44b7e2a3", + "sha256": "2425f6c967d110a37a5c5c01906cdd722dea000e5bf5f792e4851056aac68e54", + "registered": 931948973, + "dob": 954757124, + "phone": "021-441-6977", + "cell": "081-134-1105", + "PPS": "6223994T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/41.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/41.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/41.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "ms", + "first": "olivia", + "last": "young" + }, + "location": { + "street": "1119 grove road", + "city": "Mountmellick", + "state": "rhode island", + "zip": 88061 + }, + "email": "olivia.young@example.com", + "username": "crazykoala938", + "password": "malibu", + "salt": "78TEnNQ1", + "md5": "9bebcc9d890f8c9e04c9e40fc1f41476", + "sha1": "36d6a69cabff0ad780a3dcceb4e94d44edb62fc6", + "sha256": "9e39c873967f52d67e8d052aad87daf4b63d5464a27de982b64abfe9b208efc8", + "registered": 1411100094, + "dob": 818810543, + "phone": "011-475-1126", + "cell": "081-725-2254", + "PPS": "4335321T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/20.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/20.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/20.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "ms", + "first": "susanne", + "last": "russell" + }, + "location": { + "street": "6896 grafton street", + "city": "Naas", + "state": "louisiana", + "zip": 25003 + }, + "email": "susanne.russell@example.com", + "username": "ticklishswan833", + "password": "reader", + "salt": "Qp38szSx", + "md5": "57f8e3404f1926bf3fa50c152f037a33", + "sha1": "43bf7f8fe85e46957cdcb33be61f19dfe9014317", + "sha256": "c12980f91c86dae1ba9d4d880e8d51645e59f95c6b3d1f28854891d6587b39b5", + "registered": 1345063087, + "dob": 481147180, + "phone": "061-032-9311", + "cell": "081-609-1066", + "PPS": "7348900T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/69.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/69.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/69.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "clarence", + "last": "hall" + }, + "location": { + "street": "6507 ormond quay", + "city": "Athlone", + "state": "massachusetts", + "zip": 29502 + }, + "email": "clarence.hall@example.com", + "username": "beautifulgoose810", + "password": "chippy", + "salt": "HC9uH2Ku", + "md5": "df7799560a7258a6dcecf6db7ce9a426", + "sha1": "1abe49c8461ae86db5e8a2863f9e783cf2e356bc", + "sha256": "d1580be088e1f4b24e2c8bfa43e408ac79370ba279075a6a74bb122d6efebb35", + "registered": 1296029367, + "dob": 834222424, + "phone": "011-637-4058", + "cell": "081-892-3364", + "PPS": "5035341T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/8.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/8.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/8.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "eduardo", + "last": "osullivan" + }, + "location": { + "street": "6916 o'connell avenue", + "city": "Sligo", + "state": "texas", + "zip": 28210 + }, + "email": "eduardo.osullivan@example.com", + "username": "organickoala912", + "password": "2003", + "salt": "kwa2dG49", + "md5": "2971d61020a8aeb6e1dd0a6421f88720", + "sha1": "ecbbe5f1a6e78d4a32f577ec5684782916c3b8b5", + "sha256": "ee61eec8bb44d11c3d81cae5286933d44d2bb999fc465750fd12e08d125f48f6", + "registered": 1099056885, + "dob": 1133918194, + "phone": "031-728-5571", + "cell": "081-076-2169", + "PPS": "3943592T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/62.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/62.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/62.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "steve", + "last": "jacobs" + }, + "location": { + "street": "5015 the green", + "city": "Thurles", + "state": "south dakota", + "zip": 27636 + }, + "email": "steve.jacobs@example.com", + "username": "bigbird137", + "password": "photos", + "salt": "wRzliJez", + "md5": "976089024489a4ad041316a585886021", + "sha1": "3b27c6ed8cfc8670e64a49746285060965006126", + "sha256": "43df2425464ea75807e60fa0b77094f4665e2bbb4a7d88f8e112812ec4b55cd5", + "registered": 1065938088, + "dob": 756912221, + "phone": "061-703-0944", + "cell": "081-292-6748", + "PPS": "9589212T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/39.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/39.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/39.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "nathaniel", + "last": "fuller" + }, + "location": { + "street": "1432 grange road", + "city": "Ballina", + "state": "illinois", + "zip": 45389 + }, + "email": "nathaniel.fuller@example.com", + "username": "organicmouse710", + "password": "latina", + "salt": "Y9BUZOTo", + "md5": "3ec604f1e394ac0e7238fc05f3773c2f", + "sha1": "2d35e57d144e564ca389d47ef481227b0b252368", + "sha256": "d3042b5de9f7d031f556e227c457ceedb2bd667ecb5d42dc7e9fe622cb5a3f8c", + "registered": 1308405666, + "dob": 447054038, + "phone": "021-947-8765", + "cell": "081-913-7740", + "PPS": "4204687T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/26.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/26.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/26.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "mrs", + "first": "judy", + "last": "hudson" + }, + "location": { + "street": "6417 high street", + "city": "Listowel", + "state": "texas", + "zip": 21878 + }, + "email": "judy.hudson@example.com", + "username": "bigpeacock145", + "password": "anne", + "salt": "mPytCTa5", + "md5": "450e05560faa97953716895fb8292deb", + "sha1": "a64cb35070b4fdac34a4778e08957b1c539b81ed", + "sha256": "9a0806f229f4ef175268815c8b7ac27b4e23ca2079fb33be5a3d763ca35f76cf", + "registered": 925646883, + "dob": 1391210366, + "phone": "071-573-1341", + "cell": "081-804-3983", + "PPS": "4916576TA", + "picture": { + "large": "https://randomuser.me/api/portraits/women/81.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/81.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/81.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "eddie", + "last": "harper" + }, + "location": { + "street": "8620 o'connell avenue", + "city": "Ratoath", + "state": "colorado", + "zip": 37121 + }, + "email": "eddie.harper@example.com", + "username": "blacktiger213", + "password": "engage", + "salt": "cidiXTIh", + "md5": "695cf46ea241c1cfd5ca33c687fe0e59", + "sha1": "59bdeeb68ec2361dc39879267b6364993bca048c", + "sha256": "8b750124d9a43b9a0601e03520006aa202f7cafa97eab8c8587520faf72c4192", + "registered": 1369071322, + "dob": 79411121, + "phone": "061-579-5562", + "cell": "081-883-8461", + "PPS": "5421036T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/93.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/93.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/93.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "lucas", + "last": "roberts" + }, + "location": { + "street": "6400 henry street", + "city": "Cobh", + "state": "new york", + "zip": 92798 + }, + "email": "lucas.roberts@example.com", + "username": "yellowduck651", + "password": "sierra", + "salt": "MePH8deK", + "md5": "241259a8a9a73dff5a6a4cb9b63bcc4d", + "sha1": "e8619d6ca214df73c79a3c36e79937fe5ac5497c", + "sha256": "55bb7072ea4807cc518f77999b99e5811a863025ffa4f1c1ecd38a659b279c9c", + "registered": 1116148735, + "dob": 1186016629, + "phone": "011-144-3224", + "cell": "081-656-0495", + "PPS": "4050748T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/24.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/24.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/24.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "willie", + "last": "mckinney" + }, + "location": { + "street": "7371 york road", + "city": "Ashbourne", + "state": "illinois", + "zip": 54471 + }, + "email": "willie.mckinney@example.com", + "username": "bigfish163", + "password": "000001", + "salt": "T1FB2GGP", + "md5": "00da97fe8e74e7551460772c6d229171", + "sha1": "387f24dc7acc079265d4ca03832a0f7cb04f1e13", + "sha256": "ee391f88848617eb54799ea6f0e33ce772f52e0759dc59a95d3ada143c2a1036", + "registered": 1198736666, + "dob": 883617236, + "phone": "061-272-5189", + "cell": "081-994-3954", + "PPS": "3545759T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/23.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/23.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/23.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "leroy", + "last": "hanson" + }, + "location": { + "street": "6154 patrick street", + "city": "Athlone", + "state": "idaho", + "zip": 97507 + }, + "email": "leroy.hanson@example.com", + "username": "crazyleopard813", + "password": "cooking", + "salt": "uLQFVTCV", + "md5": "13e95b66605214eee3168abd99042249", + "sha1": "cc0d2e4ed3ef9a42d4f645f73fa5c2e95b79abbf", + "sha256": "ca5aec0596b159178b9f63737767037dde53f7ae1ac0e77eb97b090bcebfcf0a", + "registered": 1247501358, + "dob": 1278974966, + "phone": "061-223-0451", + "cell": "081-554-2640", + "PPS": "4375407T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/85.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/85.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/85.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "edward", + "last": "clarke" + }, + "location": { + "street": "9181 o'connell avenue", + "city": "Listowel", + "state": "rhode island", + "zip": 46271 + }, + "email": "edward.clarke@example.com", + "username": "brownswan391", + "password": "pippin", + "salt": "4ebQLqHp", + "md5": "735a907d9cf7035be21a8eddb8a5e2c2", + "sha1": "7d3153084fd8295631699b86b0ccddb485d84862", + "sha256": "1db39a2272aaf7df4d2a32fe9bc994615cdd56668b3a03f25c915137e7d10cde", + "registered": 1250012271, + "dob": 860294858, + "phone": "021-372-7387", + "cell": "081-138-2581", + "PPS": "4805852T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/34.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/34.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/34.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "mrs", + "first": "susanna", + "last": "patterson" + }, + "location": { + "street": "9598 dame street", + "city": "Ennis", + "state": "minnesota", + "zip": 58054 + }, + "email": "susanna.patterson@example.com", + "username": "silverostrich910", + "password": "girfriend", + "salt": "u42e3K12", + "md5": "74156529a5d379de7543a39994bfbc2d", + "sha1": "3fd6bd7dfe50bf110ccbcb3f852ca92ca0b7c9a8", + "sha256": "bee4afce4fbfee891a843bfb60f22962aad0f27e0b8ad382ec9e026f0a3dd0bf", + "registered": 918907148, + "dob": 451595890, + "phone": "071-312-6679", + "cell": "081-417-0773", + "PPS": "0826730T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/3.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/3.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/3.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "miss", + "first": "susie", + "last": "wade" + }, + "location": { + "street": "3922 albert road", + "city": "Cork", + "state": "maryland", + "zip": 57935 + }, + "email": "susie.wade@example.com", + "username": "tinymouse123", + "password": "sarah1", + "salt": "80uaTpip", + "md5": "ebb026f7bfa8d853a86767c14f667297", + "sha1": "f2552d4b66922178ed6677e793d3b64fb61946a4", + "sha256": "fb51ed7fdcb5ebc42dddb52752ea4e31cadf89bdfc3866011c1fb90b6d86fb2f", + "registered": 1369433610, + "dob": 1141192121, + "phone": "051-242-8007", + "cell": "081-179-7145", + "PPS": "4878353T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/56.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/56.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/56.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "ms", + "first": "kayla", + "last": "bates" + }, + "location": { + "street": "3058 rochestown road", + "city": "Nenagh", + "state": "new york", + "zip": 78058 + }, + "email": "kayla.bates@example.com", + "username": "lazyelephant819", + "password": "thesims", + "salt": "8LduNFxn", + "md5": "e1ee6d78a0d9ad8c8ec144fdb9c6c365", + "sha1": "fe6397b6bbeb7e45b47f605d5ff78982c455a720", + "sha256": "cf448900b11e55f3afdc574acb35218d12c60f8ce3d3eaf6f224b4ef806c1451", + "registered": 1063092485, + "dob": 1236033715, + "phone": "041-219-9423", + "cell": "081-921-6762", + "PPS": "8959904T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/83.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/83.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/83.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "owen", + "last": "berry" + }, + "location": { + "street": "3821 north street", + "city": "Bray", + "state": "mississippi", + "zip": 55634 + }, + "email": "owen.berry@example.com", + "username": "tinyfrog705", + "password": "ticket", + "salt": "9fWugGiA", + "md5": "7166ce4a6bb339bd3c1633362f612706", + "sha1": "532b2d603f084e212c7bfba78e2e0c05ef786a51", + "sha256": "55a83f6486b9dd68969dce6ce3c9f3dcedd34fb0c1d600a4e4362efb1b28e44d", + "registered": 1067526067, + "dob": 704400434, + "phone": "041-290-9303", + "cell": "081-967-5709", + "PPS": "0752558T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/62.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/62.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/62.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "stephen", + "last": "henderson" + }, + "location": { + "street": "6033 grove road", + "city": "Ashbourne", + "state": "colorado", + "zip": 35830 + }, + "email": "stephen.henderson@example.com", + "username": "silverostrich705", + "password": "stone55", + "salt": "Bfn1Om2U", + "md5": "89e45b417b386308d781a528c4ca0f32", + "sha1": "400dcb942e7a7d930277bc43407690b700bc9208", + "sha256": "e93ff1f2188f75a831eb156626705b26fd362ed5fd2f3467ce085e6bda7767e1", + "registered": 1262993601, + "dob": 169593280, + "phone": "021-902-9879", + "cell": "081-090-8302", + "PPS": "7191152T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/36.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/36.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/36.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "dennis", + "last": "rhodes" + }, + "location": { + "street": "7125 henry street", + "city": "Wicklow", + "state": "missouri", + "zip": 98440 + }, + "email": "dennis.rhodes@example.com", + "username": "crazyrabbit754", + "password": "celeron", + "salt": "2Yv6ST1O", + "md5": "6d3ccfa94eccfe1f9df06c8a54f4ad3c", + "sha1": "16d664bf3c87323a6232ce55a01bf6315a7cec80", + "sha256": "a9d0882108e2cb7aaa968778d4c2f54ebd8e7f791039cdfb6dacc4794235a0ee", + "registered": 1001852164, + "dob": 786246371, + "phone": "011-667-8157", + "cell": "081-531-8657", + "PPS": "2067113T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/3.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/3.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/3.jpg" + } + }, + { + "gender": "female", + "name": { + "title": "mrs", + "first": "mandy", + "last": "simmmons" + }, + "location": { + "street": "3321 green lane", + "city": "Malahide", + "state": "minnesota", + "zip": 86703 + }, + "email": "mandy.simmmons@example.com", + "username": "organicbear719", + "password": "alice", + "salt": "ZqmYZzrm", + "md5": "c1746a6eb485e9218e836e64f229fa74", + "sha1": "78c8c275dc7194d96061dcf495437322f06f80c7", + "sha256": "46d375a5bc8702059d19b46846e4af831094ca98ca8dcd045cafc3b66c285342", + "registered": 1327561524, + "dob": 695282543, + "phone": "031-165-8296", + "cell": "081-970-1434", + "PPS": "6055678T", + "picture": { + "large": "https://randomuser.me/api/portraits/women/48.jpg", + "medium": "https://randomuser.me/api/portraits/med/women/48.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/women/48.jpg" + } + }, + { + "gender": "male", + "name": { + "title": "mr", + "first": "stanley", + "last": "beck" + }, + "location": { + "street": "3226 novara avenue", + "city": "Athenry", + "state": "nebraska", + "zip": 73994 + }, + "email": "stanley.beck@example.com", + "username": "lazyduck588", + "password": "buddha", + "salt": "vjWR3Yc1", + "md5": "7cbcfc6659a4334aef4ebeae6406d0b3", + "sha1": "6eb782953f39720b66abb1a70932372e422bc2d0", + "sha256": "e4d3e58a4b3b28c20ff8c3ba8af5ac0720ad352a32c8f5f348d00c02fa42e1ca", + "registered": 1193223661, + "dob": 431832704, + "phone": "021-768-8431", + "cell": "081-759-2850", + "PPS": "7466773T", + "picture": { + "large": "https://randomuser.me/api/portraits/men/63.jpg", + "medium": "https://randomuser.me/api/portraits/med/men/63.jpg", + "thumbnail": "https://randomuser.me/api/portraits/thumb/men/63.jpg" + } + } + ] +} \ No newline at end of file diff --git a/src/routes/index.ts b/src/routes/index.ts new file mode 100644 index 0000000..4e09b7a --- /dev/null +++ b/src/routes/index.ts @@ -0,0 +1,13 @@ +import { Router } from "express"; +import * as Realm from "realm"; +import RealmHelper from "../repository/realm-helper"; +import UserSchema from "../repository/schema/user-schema"; +import User from "../model/user"; + +const index: Router = Router(); + +index.get("/", (req, res) => { + res.send("Hello from Users API!"); +}); + +export default index; diff --git a/src/routes/users.ts b/src/routes/users.ts new file mode 100644 index 0000000..62f0723 --- /dev/null +++ b/src/routes/users.ts @@ -0,0 +1,84 @@ +import { Router } from "express"; +import RealmHelper from "../repository/realm-helper"; +import User from "../model/user"; +import UserDateFilter from "../model/user-date-filter"; + +const users: Router = Router(); + +/** + * Returns a list of all users, filtered by optional query arguments. + */ +users.get("/", (req, res) => { + const filter: UserDateFilter = req.query; + + if (req.url === "/") { + return res.send(RealmHelper.getUsers()); + } + + try { + res.send(RealmHelper.findUsers(filter)); + } catch (e) { + res.status(400).send(e.message); + } +}); + +/** + * Returns a single user by its username + */ +users.get("/:username", (req, res) => { + const user: User = RealmHelper.getUserByUsername(req.params.username); + + if (user) { + res.send(user) + } else { + res.sendStatus(404); + } +}); + +/** + * Adds a new user or throws an error if it exists already. User model are sent in the body of the request. + */ +users.post("/", (req, res) => { + const user: User = req.body; + + if (RealmHelper.getUserByUsername(user.username)) { + return res.status(409).send("User already exists"); + } + + RealmHelper.addUser(user); + res.sendStatus(201); +}); + +/** + * Updates an existing user or throws an error if it doesn't. User model are sent in the body of the request. + * Properties that won't be changed can be undefined but the entity must match the model. + */ +users.put("/", (req, res) => { + const user: User = req.body; + + try { + RealmHelper.updateUser(user); + return res.sendStatus(200); + } catch (e) { + if (e.message.indexOf("does not exist") !== -1) { + return res.status(404).send(e); + } + return res.status(500).send(e.message); + } +}); + +/** + * Deletes an existing user or throws an error if it doesn't. + */ +users.delete("/:username", (req, res) => { + const username: string = req.params.username; + + if (!RealmHelper.getUserByUsername(username)) { + return res.status(404).send(`User: ${username} does not exist`); + } + + RealmHelper.deleteUser(username); + res.status(200).send(`Successfuly delete: ${username}`); +}); + +export default users; diff --git a/src/server.ts b/src/server.ts new file mode 100644 index 0000000..c37be0b --- /dev/null +++ b/src/server.ts @@ -0,0 +1,23 @@ +import * as express from "express"; +import * as bodyParser from "body-parser"; + +// Server app definitions +const app: express.Express = express(); + +app.use(bodyParser.json()); + +// Routes definitions +import index from "./routes/index"; +import users from "./routes/users"; + +app.use("/", index); +app.use("/users", users); + +// Realm configuration +import RealmHelper from "./repository/realm-helper"; + +RealmHelper.init(process.env.ENV_NODE === "development"); + +app.listen(3000, () => { + console.log(`Users API listening on port 3000 in ${process.env.ENV_NODE} mode`); +}) \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..d84080e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "outDir": "./dist/", + "rootDir": ".", + "sourceMap": true, + "module": "commonjs", + "target": "es5" + }, + "include": [ + "./src/**/*", + "./spec/**/*" + ] +} \ No newline at end of file