diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4ad0f13
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,17 @@
+# IntelliJ Idea project settings folder
+.idea
+
+# NPM package manager temporary files and folders
+node_modules
+npm-debug.log
+package-lock.json
+
+# ModelGen output folders
+migrations
+models
+models-webservice
+resolvers
+schemas
+test
+validations
+patches
\ No newline at end of file
diff --git a/Dockerfile.graphql_server b/Dockerfile.graphql_server
new file mode 100644
index 0000000..ac4fff3
--- /dev/null
+++ b/Dockerfile.graphql_server
@@ -0,0 +1,15 @@
+FROM node:11.12.0-stretch-slim
+
+# Create app directory
+WORKDIR /usr/ScienceDbStarterPack/graphql-server
+
+# Copy generated code into the skeleton GraphQL-Server
+COPY . .
+
+# Clone the skeleton project and install dependencies
+RUN chmod u+x ./migrateDbAndStartServer.sh && \
+ rm .git* && \
+ mv ./config/config_postgres_docker.json ./config/config.json && \
+ npm install
+
+EXPOSE 3000
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..f288702
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/README.md b/README.md
index 6aa2032..97ec8f8 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,9 @@
# GraphQL backend
Skeleton NodeJS project for a graphQL server.
-This package integrates the code generated by [code-generator](https://github.com/vsuaste/express_graphql_model_gen).
+This package integrates the code generated by [code-generator](https://github.com/ScienceDb/graphql-server-model-codegen).
-[Code-generator](https://github.com/vsuaste/express_graphql_model_gen) will generate four folders with the models' information:
+[Code-generator](https://github.com/ScienceDb/graphql-server-model-codegen) will generate four folders with the models' information:
* models
* schemas
* resolvers
@@ -12,18 +12,26 @@ This package integrates the code generated by [code-generator](https://github.co
After getting ready the generated code for the models, proceed with the server set up.
## Set up
+
Clone the repository and run:
```
$ npm install
$ node_modules/.bin/sequelize db:migrate
-$ node server.js
+$ npm start
```
``` $ node_modules/.bin/sequelize db:migrate ``` command will create the tables specified in the ```migrations``` folder.
With credential as in ``` config/config.json``` file.
+### ENVIRONMENT VARIABLES
+
+* `PORT` - The port where the app is listening, default value is `3000`
+* `ALLOW_ORIGIN` - In development mode we need to specify the header `Access-Control-Allow-Origin` so the SPA application can communicate with the server, default value `http://localhost:8080`.
+
+## NOTE
+A data base should be already configured locally as in `config/config.json`
## Example of use
-If you followed the example for generating the code described [here](https://github.com/vsuaste/express_graphql_model_gen), you can try the next queries and mutations. Otherwise, just adapt the same queries and mutations for your own models generated.
+If you followed the example for generating the code described [here](https://github.com/ScienceDb/graphql-server-model-codegen), you can try the next queries and mutations. Otherwise, just adapt the same queries and mutations for your own models generated.
We will add the next 4 people to our table ``people``.
@@ -65,7 +73,7 @@ curl -XPOST http://localhost:3000/graphql -H 'Content-Type: application/graphql'
We'll search people with 'science' as substring of their email and as result we'll get only their name and last name.
```
-curl -XPOST http://localhost:3000/graphql -H 'Content-Type: application/graphql' -d '{ searchPerson(input:{field:email, value:{value:"%science%"}, operator:like}){ firstName lastName}}'
+curl -XPOST http://localhost:3000/graphql -H 'Content-Type: application/graphql' -d '{ people(search:{field:email, value:{value:"%science%"}, operator:like}){ firstName lastName}}'
```
The result will be:
diff --git a/acl_rules.js b/acl_rules.js
index ad76120..ce28b79 100644
--- a/acl_rules.js
+++ b/acl_rules.js
@@ -1,8 +1,8 @@
module.exports = {
aclRules: [{
- roles: 'administrator',
+ roles: 'admin',
allows: [{
- resources: '*',
+ resources: ['users', 'roles'],
permissions: '*'
}]
},
diff --git a/config/config.json b/config/config.json
index 5d202e1..b0b9c84 100644
--- a/config/config.json
+++ b/config/config.json
@@ -4,20 +4,23 @@
"password": "test_code_gen",
"database": "test_code_gen",
"host": "127.0.0.1",
- "dialect": "postgres"
+ "dialect": "postgres",
+ "operatorsAliases": false
},
"test": {
- "username": "test_code_gen",
- "password": "test_code_gen",
- "database": "test_code_gen",
- "host": "127.0.0.1",
- "dialect": "postgres"
+ "username": "sciencedb",
+ "password": "sciencedb",
+ "database": "sciencedb_test",
+ "host": "postgres",
+ "dialect": "postgres",
+ "operatorsAliases": false
},
"production": {
- "username": "test_code_gen",
- "password": "test_code_gen",
- "database": "test_code_gen",
- "host": "127.0.0.1",
- "dialect": "postgres"
+ "username": "sciencedb",
+ "password": "sciencedb",
+ "database": "sciencedb_production",
+ "host": "postgres",
+ "dialect": "postgres",
+ "operatorsAliases": false
}
}
diff --git a/config/config_postgres_docker.json b/config/config_postgres_docker.json
new file mode 100644
index 0000000..d120ba8
--- /dev/null
+++ b/config/config_postgres_docker.json
@@ -0,0 +1,26 @@
+{
+ "development": {
+ "username": "sciencedb",
+ "password": "sciencedb",
+ "database": "sciencedb_development",
+ "host": "sdb_postgres",
+ "dialect": "postgres",
+ "operatorsAliases": false
+ },
+ "test": {
+ "username": "sciencedb",
+ "password": "sciencedb",
+ "database": "sciencedb_test",
+ "host": "sdb_postgres",
+ "dialect": "postgres",
+ "operatorsAliases": false
+ },
+ "production": {
+ "username": "sciencedb",
+ "password": "sciencedb",
+ "database": "sciencedb_production",
+ "host": "sdb_postgres",
+ "dialect": "postgres",
+ "operatorsAliases": false
+ }
+}
diff --git a/config/globals.js b/config/globals.js
new file mode 100644
index 0000000..4fcb596
--- /dev/null
+++ b/config/globals.js
@@ -0,0 +1,10 @@
+module.exports = {
+ LIMIT_RECORDS : process.env.LIMIT_RECORDS || 10000,
+ PORT : process.env.PORT || 3000,
+ ALLOW_ORIGIN: process.env.ALLOW_ORIGIN || "http://localhost:8080",
+ REQUIRE_SIGN_IN: process.env.REQUIRE_SIGN_IN || "true",
+ MAIL_SERVICE: "gmail",
+ MAIL_HOST: "smtp.gmail.com",
+ MAIL_ACCOUNT: "sci.db.service@gmail.com",
+ MAIL_PASSWORD: "SciDbServiceQAZ"
+}
diff --git a/connection.js b/connection.js
index 054bcdd..99b2624 100644
--- a/connection.js
+++ b/connection.js
@@ -1,23 +1,23 @@
-Sequelize = require('sequelize');
+const env = process.env.NODE_ENV || 'development';
+const path = require('path')
+const config = require(path.join(__dirname, 'config', 'config.json'))[env];
+const Sequelize = require('sequelize');
+
const Op = Sequelize.Op;
-const operatorsAliases = {
+config.operatorsAliases = {
$eq: Op.eq,
$and: Op.and,
$or: Op.or,
$like: Op.like,
$between: Op.between,
- $in: Op.in
+ $in: Op.in,
+ $gt: Op.gt,
+ $gte: Op.gte,
+ $lt: Op.lt,
+ $lte: Op.lte,
+ $ne: Op.ne
};
-sequelize = new Sequelize(
- 'test_code_gen',
- 'test_code_gen',
- 'test_code_gen',
- {
- dialect: 'postgres',
- host: '127.0.0.1'
- },
- {operatorsAliases}
-);
+sequelize = new Sequelize(config);
module.exports = sequelize;
diff --git a/migrateDbAndStartServer.sh b/migrateDbAndStartServer.sh
new file mode 100755
index 0000000..5d58e45
--- /dev/null
+++ b/migrateDbAndStartServer.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+# Wait until the relational database-server up and running
+waited=0
+until node ./utils/testSequelizeDbServerAvailable.js
+do
+ if [ $waited == 240 ]; then
+ echo -e '\nERROR: Time out reached while waiting for relational database server to be available.\n'
+ exit 1
+ fi
+ sleep 2
+ waited=$(expr $waited + 2)
+done
+
+# Run the migrations
+if ! ./node_modules/.bin/sequelize db:migrate; then
+ echo -e '\nERROR: Migrating the relational database(s) caused an error.\n'
+ exit 1
+fi
+
+# Run seeders if needed
+if [ -d ./seeders ]; then
+ if ! ./node_modules/.bin/sequelize db:seed:all; then
+ echo -e '\nERROR: Seeding the relational database(s) caused an error.\n'
+ exit 1
+ fi
+fi
+
+# Start GraphQL-server
+npm start 2> /usr/src/app/error_server.log # acl
diff --git a/models_index.js b/models_index.js
new file mode 100644
index 0000000..fbb8d51
--- /dev/null
+++ b/models_index.js
@@ -0,0 +1,64 @@
+const fs = require('fs');
+const path = require('path');
+const Sequelize = require('sequelize');
+sequelize = require('./connection');
+
+var models = {};
+module.exports = models;
+
+// **********************************************************************************
+// IMPORT SEQUEILIZE MODELS
+
+//grabs all the models in your models folder, adds them to the models object
+fs.readdirSync("./models")
+ .filter(function(file) {
+ return (file.indexOf('.') !== 0) && (file !== 'index.js') && (file.slice(-3) === '.js');
+ })
+ .forEach(function(file) {
+ console.log(file);
+ let model_file = require(path.join(__dirname,'models', file));
+ let model = model_file.init(sequelize, Sequelize);
+
+
+ let validator_patch = path.join('./validations', file);
+ if(fs.existsSync(validator_patch)){
+ model = require(`./${validator_patch}`).validator_patch(model);
+ }
+
+ let patches_patch = path.join('./patches', file);
+ if(fs.existsSync(patches_patch)){
+ model = require(`./${patches_patch}`).logic_patch(model);
+ }
+
+ if(models[model.name])
+ throw Error(`Duplicated model name ${model.name}`);
+
+ models[model.name] = model;
+
+ });
+//Important: creates associations based on associations defined in associate function in the model files
+Object.keys(models).forEach(function(modelName) {
+ if (models[modelName].associate) {
+ models[modelName].associate(models);
+ }
+});
+//update tables with association (temporary, just for testing purposes)
+//this part is suppose to be done in the migration file
+//sequelize.sync({force: true});
+
+
+// **********************************************************************************
+// IMPORT WEBSERVICES
+
+fs.readdirSync("./models-webservice")
+ .filter(function(file) {
+ return (file.indexOf('.') !== 0) && (file !== 'index.js') && (file.slice(-3) === '.js');
+ })
+ .forEach(function(file) {
+ let model = require(`./${path.join("./models-webservice", file)}`);
+
+ if(models[model.name])
+ throw Error(`Duplicated model name ${model.name}`);
+
+ models[model.name] = model;
+ });
diff --git a/package-lock.json b/package-lock.json
deleted file mode 100644
index 0ff79f5..0000000
--- a/package-lock.json
+++ /dev/null
@@ -1,2227 +0,0 @@
-{
- "name": "second-server-graphql",
- "version": "1.0.0",
- "lockfileVersion": 1,
- "requires": true,
- "dependencies": {
- "@types/geojson": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-1.0.6.tgz",
- "integrity": "sha512-Xqg/lIZMrUd0VRmSRbCAewtwGZiAk3mEUDvV4op1tGl+LvyPcb/MIOSxTl9z+9+J+R4/vpjiCAT4xeKzH9ji1w=="
- },
- "@types/node": {
- "version": "9.4.6",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-9.4.6.tgz",
- "integrity": "sha512-CTUtLb6WqCCgp6P59QintjHWqzf4VL1uPA27bipLAPxFqrtK1gEYllePzTICGqQ8rYsCbpnsNypXjjDzGAAjEQ=="
- },
- "abbrev": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
- "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
- },
- "accepts": {
- "version": "1.3.5",
- "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
- "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
- "requires": {
- "mime-types": "2.1.18",
- "negotiator": "0.6.1"
- }
- },
- "acl": {
- "version": "0.4.11",
- "resolved": "https://registry.npmjs.org/acl/-/acl-0.4.11.tgz",
- "integrity": "sha1-ACzHZuvyXNqP5TK1bzZRvr2yWzo=",
- "requires": {
- "async": "2.6.0",
- "bluebird": "3.5.1",
- "lodash": "4.17.5",
- "mongodb": "2.2.35",
- "redis": "2.8.0"
- }
- },
- "adler-32": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.2.0.tgz",
- "integrity": "sha1-aj5r8KY5ALoVZSgIyxXGgT0aXyU=",
- "requires": {
- "exit-on-epipe": "1.0.1",
- "printj": "1.1.2"
- }
- },
- "ansi-regex": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
- "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
- },
- "array-flatten": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
- "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
- },
- "assertion-error": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
- "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw=="
- },
- "async": {
- "version": "2.6.0",
- "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz",
- "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==",
- "requires": {
- "lodash": "4.17.5"
- }
- },
- "asynckit": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
- "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
- },
- "babel-runtime": {
- "version": "6.26.0",
- "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
- "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
- "requires": {
- "core-js": "2.5.5",
- "regenerator-runtime": "0.11.1"
- }
- },
- "balanced-match": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
- "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
- },
- "base64url": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz",
- "integrity": "sha1-6sFuA+oUOO/5Qj1puqNiYu0fcLs="
- },
- "bcryptjs": {
- "version": "2.4.3",
- "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
- "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms="
- },
- "bluebird": {
- "version": "3.5.1",
- "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
- "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA=="
- },
- "body-parser": {
- "version": "1.18.2",
- "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz",
- "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=",
- "requires": {
- "bytes": "3.0.0",
- "content-type": "1.0.4",
- "debug": "2.6.9",
- "depd": "1.1.2",
- "http-errors": "1.6.2",
- "iconv-lite": "0.4.19",
- "on-finished": "2.3.0",
- "qs": "6.5.1",
- "raw-body": "2.3.2",
- "type-is": "1.6.16"
- }
- },
- "brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "requires": {
- "balanced-match": "1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "browser-stdout": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
- "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
- "dev": true
- },
- "bson": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.6.tgz",
- "integrity": "sha512-D8zmlb46xfuK2gGvKmUjIklQEouN2nQ0LEHHeZ/NoHM2LDiMk2EYzZ5Ntw/Urk+bgMDosOZxaRzXxvhI5TcAVQ=="
- },
- "buffer-equal-constant-time": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
- "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
- },
- "buffer-shims": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz",
- "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E="
- },
- "buffer-writer": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-1.0.1.tgz",
- "integrity": "sha1-Iqk2kB4wKa/NdUfrRIfOtpejvwg="
- },
- "builtin-modules": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
- "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8="
- },
- "busboy": {
- "version": "0.2.14",
- "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
- "integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=",
- "requires": {
- "dicer": "0.2.5",
- "readable-stream": "1.1.14"
- },
- "dependencies": {
- "isarray": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
- "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
- },
- "readable-stream": {
- "version": "1.1.14",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
- "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
- "requires": {
- "core-util-is": "1.0.2",
- "inherits": "2.0.3",
- "isarray": "0.0.1",
- "string_decoder": "0.10.31"
- }
- },
- "string_decoder": {
- "version": "0.10.31",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
- "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
- }
- }
- },
- "bytes": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
- "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
- },
- "camelcase": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
- "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0="
- },
- "cfb": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.0.7.tgz",
- "integrity": "sha512-KjjZFR+a/e8RDdDTr4PwR0P/HIFRI3sxArFQttml0pFkhIO4TnvS/1+dqtGXPqe5/0MHp2IzjFx1JTzmohHT+w==",
- "requires": {
- "commander": "2.15.1",
- "printj": "1.1.2"
- }
- },
- "chai": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz",
- "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=",
- "requires": {
- "assertion-error": "1.1.0",
- "check-error": "1.0.2",
- "deep-eql": "3.0.1",
- "get-func-name": "2.0.0",
- "pathval": "1.1.0",
- "type-detect": "4.0.8"
- }
- },
- "charenc": {
- "version": "0.0.2",
- "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
- "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc="
- },
- "check-error": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
- "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII="
- },
- "cli-color": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-1.2.0.tgz",
- "integrity": "sha1-OlrnT9drYmevZm5p4q+70B3vNNE=",
- "requires": {
- "ansi-regex": "2.1.1",
- "d": "1.0.0",
- "es5-ext": "0.10.42",
- "es6-iterator": "2.0.3",
- "memoizee": "0.4.12",
- "timers-ext": "0.1.5"
- }
- },
- "cliui": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
- "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
- "requires": {
- "string-width": "1.0.2",
- "strip-ansi": "3.0.1",
- "wrap-ansi": "2.1.0"
- },
- "dependencies": {
- "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"
- }
- }
- }
- },
- "cls-bluebird": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/cls-bluebird/-/cls-bluebird-2.1.0.tgz",
- "integrity": "sha1-N+8eCAqP+1XC9BZPU28ZGeeWiu4=",
- "requires": {
- "is-bluebird": "1.0.2",
- "shimmer": "1.2.0"
- }
- },
- "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="
- },
- "codepage": {
- "version": "1.12.2",
- "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.12.2.tgz",
- "integrity": "sha512-FAN+oPs/ocaPLFvIt4vEOHgWA6UJ6t+fVbbVBoXDpTpC+4JYasomYZEEjR/Miph3qQrVnIShRwwmwu4P35JW1w==",
- "requires": {
- "commander": "2.14.1",
- "exit-on-epipe": "1.0.1"
- },
- "dependencies": {
- "commander": {
- "version": "2.14.1",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.14.1.tgz",
- "integrity": "sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw=="
- }
- }
- },
- "combined-stream": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz",
- "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
- "requires": {
- "delayed-stream": "1.0.0"
- }
- },
- "commander": {
- "version": "2.15.1",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
- "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag=="
- },
- "component-emitter": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
- "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
- },
- "concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
- },
- "config-chain": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.11.tgz",
- "integrity": "sha1-q6CXR9++TD5w52am5BWG4YWfxvI=",
- "requires": {
- "ini": "1.3.5",
- "proto-list": "1.2.4"
- }
- },
- "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.4",
- "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
- "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
- },
- "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="
- },
- "cookiejar": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.1.tgz",
- "integrity": "sha1-Qa1XsbVVlR7BcUEqgZQrHoIA00o="
- },
- "core-js": {
- "version": "2.5.5",
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.5.tgz",
- "integrity": "sha1-sU3ek2xkDAV5prUMq8wTLdYSfjs="
- },
- "core-util-is": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
- "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
- },
- "cors": {
- "version": "2.8.4",
- "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.4.tgz",
- "integrity": "sha1-K9OB8usgECAQXNUOpZ2mMJBpRoY=",
- "requires": {
- "object-assign": "4.1.1",
- "vary": "1.1.2"
- }
- },
- "crc-32": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.0.tgz",
- "integrity": "sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA==",
- "requires": {
- "exit-on-epipe": "1.0.1",
- "printj": "1.1.2"
- }
- },
- "cross-spawn": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
- "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
- "requires": {
- "lru-cache": "4.1.2",
- "shebang-command": "1.2.0",
- "which": "1.3.0"
- },
- "dependencies": {
- "lru-cache": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.2.tgz",
- "integrity": "sha512-wgeVXhrDwAWnIF/yZARsFnMBtdFXOg1b8RIrhilp+0iDYN4mdQcNZElDZ0e4B64BhaxeQ5zN7PMyvu7we1kPeQ==",
- "requires": {
- "pseudomap": "1.0.2",
- "yallist": "2.1.2"
- }
- }
- }
- },
- "crypt": {
- "version": "0.0.2",
- "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
- "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs="
- },
- "csv-parse": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-2.2.0.tgz",
- "integrity": "sha512-nNXh61kEIUbTXPWPZbrKlkkylh7BDxffDUWQPWIho+Rog4XWRV8bTR8ZVo8qngzAwbhlvtKFcsaf2hGDV1iF8Q=="
- },
- "d": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz",
- "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=",
- "requires": {
- "es5-ext": "0.10.42"
- }
- },
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "requires": {
- "ms": "2.0.0"
- }
- },
- "decamelize": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
- "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
- },
- "deep-eql": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
- "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
- "requires": {
- "type-detect": "4.0.8"
- }
- },
- "deepmerge": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.1.0.tgz",
- "integrity": "sha512-Q89Z26KAfA3lpPGhbF6XMfYAm3jIV3avViy6KOJ2JLzFbeWHOvPQUu5aSJIWXap3gDZC2y1eF5HXEPI2wGqgvw=="
- },
- "delayed-stream": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
- "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
- },
- "depd": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
- "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
- },
- "destroy": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
- "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
- },
- "dicer": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz",
- "integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=",
- "requires": {
- "readable-stream": "1.1.14",
- "streamsearch": "0.1.2"
- },
- "dependencies": {
- "isarray": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
- "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
- },
- "readable-stream": {
- "version": "1.1.14",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
- "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
- "requires": {
- "core-util-is": "1.0.2",
- "inherits": "2.0.3",
- "isarray": "0.0.1",
- "string_decoder": "0.10.31"
- }
- },
- "string_decoder": {
- "version": "0.10.31",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
- "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
- }
- }
- },
- "diff": {
- "version": "3.5.0",
- "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
- "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
- "dev": true
- },
- "dottie": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.0.tgz",
- "integrity": "sha1-2hkZgci41xPKARXViYzzl8Lw3dA="
- },
- "double-ended-queue": {
- "version": "2.1.0-0",
- "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz",
- "integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw="
- },
- "ecdsa-sig-formatter": {
- "version": "1.0.9",
- "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.9.tgz",
- "integrity": "sha1-S8kmJ07Dtau1AW5+HWCSGsJisqE=",
- "requires": {
- "base64url": "2.0.0",
- "safe-buffer": "5.1.1"
- }
- },
- "editorconfig": {
- "version": "0.13.3",
- "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.13.3.tgz",
- "integrity": "sha512-WkjsUNVCu+ITKDj73QDvi0trvpdDWdkDyHybDGSXPfekLCqwmpD7CP7iPbvBgosNuLcI96XTDwNa75JyFl7tEQ==",
- "requires": {
- "bluebird": "3.5.1",
- "commander": "2.15.1",
- "lru-cache": "3.2.0",
- "semver": "5.5.0",
- "sigmund": "1.0.1"
- },
- "dependencies": {
- "semver": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
- "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA=="
- }
- }
- },
- "ee-first": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
- "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
- },
- "encodeurl": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
- "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
- },
- "error-ex": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz",
- "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=",
- "requires": {
- "is-arrayish": "0.2.1"
- }
- },
- "es5-ext": {
- "version": "0.10.42",
- "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.42.tgz",
- "integrity": "sha512-AJxO1rmPe1bDEfSR6TJ/FgMFYuTBhR5R57KW58iCkYACMyFbrkqVyzXSurYoScDGvgyMpk7uRF/lPUPPTmsRSA==",
- "requires": {
- "es6-iterator": "2.0.3",
- "es6-symbol": "3.1.1",
- "next-tick": "1.0.0"
- }
- },
- "es6-iterator": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
- "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
- "requires": {
- "d": "1.0.0",
- "es5-ext": "0.10.42",
- "es6-symbol": "3.1.1"
- }
- },
- "es6-promise": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
- "integrity": "sha1-7FYjOGgDKQkgcXDDlEjiREndH8Q="
- },
- "es6-symbol": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz",
- "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=",
- "requires": {
- "d": "1.0.0",
- "es5-ext": "0.10.42"
- }
- },
- "es6-weak-map": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz",
- "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=",
- "requires": {
- "d": "1.0.0",
- "es5-ext": "0.10.42",
- "es6-iterator": "2.0.3",
- "es6-symbol": "3.1.1"
- }
- },
- "escape-html": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
- "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "etag": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
- "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
- },
- "event-emitter": {
- "version": "0.3.5",
- "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
- "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=",
- "requires": {
- "d": "1.0.0",
- "es5-ext": "0.10.42"
- }
- },
- "execa": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz",
- "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=",
- "requires": {
- "cross-spawn": "5.1.0",
- "get-stream": "3.0.0",
- "is-stream": "1.1.0",
- "npm-run-path": "2.0.2",
- "p-finally": "1.0.0",
- "signal-exit": "3.0.2",
- "strip-eof": "1.0.0"
- }
- },
- "exit-on-epipe": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz",
- "integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw=="
- },
- "express": {
- "version": "4.16.2",
- "resolved": "https://registry.npmjs.org/express/-/express-4.16.2.tgz",
- "integrity": "sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w=",
- "requires": {
- "accepts": "1.3.5",
- "array-flatten": "1.1.1",
- "body-parser": "1.18.2",
- "content-disposition": "0.5.2",
- "content-type": "1.0.4",
- "cookie": "0.3.1",
- "cookie-signature": "1.0.6",
- "debug": "2.6.9",
- "depd": "1.1.2",
- "encodeurl": "1.0.2",
- "escape-html": "1.0.3",
- "etag": "1.8.1",
- "finalhandler": "1.1.0",
- "fresh": "0.5.2",
- "merge-descriptors": "1.0.1",
- "methods": "1.1.2",
- "on-finished": "2.3.0",
- "parseurl": "1.3.2",
- "path-to-regexp": "0.1.7",
- "proxy-addr": "2.0.3",
- "qs": "6.5.1",
- "range-parser": "1.2.0",
- "safe-buffer": "5.1.1",
- "send": "0.16.1",
- "serve-static": "1.13.1",
- "setprototypeof": "1.1.0",
- "statuses": "1.3.1",
- "type-is": "1.6.16",
- "utils-merge": "1.0.1",
- "vary": "1.1.2"
- }
- },
- "express-fileupload": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-0.4.0.tgz",
- "integrity": "sha512-jPv3aCdTIdQrGAUXQ1e1hU0Vnl+0jE9IbzEsI7VRIevQybrUrIMUgvwNwBThnsetandW8+9ICgflAkhKwLUuLw==",
- "requires": {
- "busboy": "0.2.14",
- "fs-extra": "4.0.3",
- "md5": "2.2.1",
- "streamifier": "0.1.1"
- },
- "dependencies": {
- "fs-extra": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz",
- "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==",
- "requires": {
- "graceful-fs": "4.1.11",
- "jsonfile": "4.0.0",
- "universalify": "0.1.1"
- }
- }
- }
- },
- "express-graphql": {
- "version": "0.6.12",
- "resolved": "https://registry.npmjs.org/express-graphql/-/express-graphql-0.6.12.tgz",
- "integrity": "sha512-ouLWV0hRw4hnaLtXzzwhdC79ewxKbY2PRvm05mPc/zOH5W5WVCHDQ1SmNxEPBQdUeeSNh29aIqW9zEQkA3kMuA==",
- "requires": {
- "accepts": "1.3.5",
- "content-type": "1.0.4",
- "http-errors": "1.6.2",
- "raw-body": "2.3.2"
- }
- },
- "extend": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
- "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ="
- },
- "faker": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/faker/-/faker-4.1.0.tgz",
- "integrity": "sha1-HkW7vsxndLPBlfrSg1EJxtdIzD8="
- },
- "finalhandler": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz",
- "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=",
- "requires": {
- "debug": "2.6.9",
- "encodeurl": "1.0.2",
- "escape-html": "1.0.3",
- "on-finished": "2.3.0",
- "parseurl": "1.3.2",
- "statuses": "1.3.1",
- "unpipe": "1.0.0"
- }
- },
- "find-up": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
- "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
- "requires": {
- "locate-path": "2.0.0"
- }
- },
- "form-data": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
- "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
- "requires": {
- "asynckit": "0.4.0",
- "combined-stream": "1.0.6",
- "mime-types": "2.1.18"
- }
- },
- "formidable": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz",
- "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg=="
- },
- "forwarded": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
- "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
- },
- "frac": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz",
- "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA=="
- },
- "fresh": {
- "version": "0.5.2",
- "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
- "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
- },
- "fs-extra": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz",
- "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==",
- "requires": {
- "graceful-fs": "4.1.11",
- "jsonfile": "4.0.0",
- "universalify": "0.1.1"
- }
- },
- "fs.realpath": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
- },
- "generic-pool": {
- "version": "3.4.2",
- "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.4.2.tgz",
- "integrity": "sha512-H7cUpwCQSiJmAHM4c/aFu6fUfrhWXW1ncyh8ftxEPMu6AiYkHw9K8br720TGPZJbk5eOH2bynjZD1yPvdDAmag=="
- },
- "get-caller-file": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz",
- "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U="
- },
- "get-func-name": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
- "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE="
- },
- "get-stream": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
- "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
- },
- "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="
- },
- "graphql": {
- "version": "0.13.1",
- "resolved": "https://registry.npmjs.org/graphql/-/graphql-0.13.1.tgz",
- "integrity": "sha512-awNp3LTrQ7dJDSX3p3PBuxNDC+WFSOrWeV6+l4Xeh2PQJVOFyQ9SZPonXRz2WZc7aIxLZsf2nDZuuuc0qyEq/A==",
- "requires": {
- "iterall": "1.2.2"
- }
- },
- "growl": {
- "version": "1.10.5",
- "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
- "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
- "dev": true
- },
- "has-flag": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
- "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
- "dev": true
- },
- "he": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
- "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
- "dev": true
- },
- "hosted-git-info": {
- "version": "2.6.0",
- "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.6.0.tgz",
- "integrity": "sha512-lIbgIIQA3lz5XaB6vxakj6sDHADJiZadYEJB+FgA+C4nubM1NwcuvUr9EJPmnH1skZqpqUzWborWo8EIUi0Sdw=="
- },
- "http-errors": {
- "version": "1.6.2",
- "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz",
- "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=",
- "requires": {
- "depd": "1.1.1",
- "inherits": "2.0.3",
- "setprototypeof": "1.0.3",
- "statuses": "1.3.1"
- },
- "dependencies": {
- "depd": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz",
- "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k="
- },
- "setprototypeof": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz",
- "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ="
- }
- }
- },
- "iconv-lite": {
- "version": "0.4.19",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
- "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ=="
- },
- "inflection": {
- "version": "1.12.0",
- "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz",
- "integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY="
- },
- "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.5",
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
- "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
- },
- "invert-kv": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
- "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY="
- },
- "ipaddr.js": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz",
- "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs="
- },
- "is-arrayish": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
- "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
- },
- "is-bluebird": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-bluebird/-/is-bluebird-1.0.2.tgz",
- "integrity": "sha1-CWQ5Bg9KpBGr7hkUOoTWpVNG1uI="
- },
- "is-buffer": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
- },
- "is-builtin-module": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
- "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
- "requires": {
- "builtin-modules": "1.1.1"
- }
- },
- "is-extglob": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="
- },
- "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-glob": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz",
- "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=",
- "requires": {
- "is-extglob": "2.1.1"
- }
- },
- "is-promise": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
- "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o="
- },
- "is-stream": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
- "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
- },
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
- },
- "isexe": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
- },
- "iterall": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.2.2.tgz",
- "integrity": "sha512-yynBb1g+RFUPY64fTrFv7nsjRrENBQJaX2UL+2Szc9REFrSNm1rpSXHGzhmAy7a9uv3vlvgBlXnf9RqmPH1/DA=="
- },
- "js-beautify": {
- "version": "1.7.5",
- "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.7.5.tgz",
- "integrity": "sha512-9OhfAqGOrD7hoQBLJMTA+BKuKmoEtTJXzZ7WDF/9gvjtey1koVLuZqIY6c51aPDjbNdNtIXAkiWKVhziawE9Og==",
- "requires": {
- "config-chain": "1.1.11",
- "editorconfig": "0.13.3",
- "mkdirp": "0.5.1",
- "nopt": "3.0.6"
- }
- },
- "js-string-escape": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz",
- "integrity": "sha1-4mJbrbwNZ8dTPp7cEGjFh65BN+8="
- },
- "jsonfile": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
- "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
- "requires": {
- "graceful-fs": "4.1.11"
- }
- },
- "jsonwebtoken": {
- "version": "8.2.0",
- "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.2.0.tgz",
- "integrity": "sha512-1Wxh8ADP3cNyPl8tZ95WtraHXCAyXupgc0AhMHjU9er98BV+UcKsO7OJUjfhIu0Uba9A40n1oSx8dbJYrm+EoQ==",
- "requires": {
- "jws": "3.1.4",
- "lodash.includes": "4.3.0",
- "lodash.isboolean": "3.0.3",
- "lodash.isinteger": "4.0.4",
- "lodash.isnumber": "3.0.3",
- "lodash.isplainobject": "4.0.6",
- "lodash.isstring": "4.0.1",
- "lodash.once": "4.1.1",
- "ms": "2.1.1",
- "xtend": "4.0.1"
- },
- "dependencies": {
- "ms": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
- "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
- }
- }
- },
- "jwa": {
- "version": "1.1.5",
- "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.5.tgz",
- "integrity": "sha1-oFUs4CIHQs1S4VN3SjKQXDDnVuU=",
- "requires": {
- "base64url": "2.0.0",
- "buffer-equal-constant-time": "1.0.1",
- "ecdsa-sig-formatter": "1.0.9",
- "safe-buffer": "5.1.1"
- }
- },
- "jws": {
- "version": "3.1.4",
- "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.4.tgz",
- "integrity": "sha1-+ei5M46KhHJ31kRLFGT2GIDgUKI=",
- "requires": {
- "base64url": "2.0.0",
- "jwa": "1.1.5",
- "safe-buffer": "5.1.1"
- }
- },
- "lcid": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
- "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
- "requires": {
- "invert-kv": "1.0.0"
- }
- },
- "load-json-file": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
- "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
- "requires": {
- "graceful-fs": "4.1.11",
- "parse-json": "2.2.0",
- "pify": "2.3.0",
- "strip-bom": "3.0.0"
- }
- },
- "locate-path": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
- "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
- "requires": {
- "p-locate": "2.0.0",
- "path-exists": "3.0.0"
- }
- },
- "lodash": {
- "version": "4.17.5",
- "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz",
- "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw=="
- },
- "lodash.includes": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
- "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8="
- },
- "lodash.isboolean": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
- "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY="
- },
- "lodash.isinteger": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
- "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M="
- },
- "lodash.isnumber": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
- "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w="
- },
- "lodash.isplainobject": {
- "version": "4.0.6",
- "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
- "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
- },
- "lodash.isstring": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
- "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="
- },
- "lodash.once": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
- "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w="
- },
- "lru-cache": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-3.2.0.tgz",
- "integrity": "sha1-cXibO39Tmb7IVl3aOKow0qCX7+4=",
- "requires": {
- "pseudomap": "1.0.2"
- }
- },
- "lru-queue": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz",
- "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=",
- "requires": {
- "es5-ext": "0.10.42"
- }
- },
- "md5": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz",
- "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=",
- "requires": {
- "charenc": "0.0.2",
- "crypt": "0.0.2",
- "is-buffer": "1.1.6"
- }
- },
- "media-typer": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
- "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
- },
- "mem": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz",
- "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=",
- "requires": {
- "mimic-fn": "1.2.0"
- }
- },
- "memoizee": {
- "version": "0.4.12",
- "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.12.tgz",
- "integrity": "sha512-sprBu6nwxBWBvBOh5v2jcsGqiGLlL2xr2dLub3vR8dnE8YB17omwtm/0NSHl8jjNbcsJd5GMWJAnTSVe/O0Wfg==",
- "requires": {
- "d": "1.0.0",
- "es5-ext": "0.10.42",
- "es6-weak-map": "2.0.2",
- "event-emitter": "0.3.5",
- "is-promise": "2.1.0",
- "lru-queue": "0.1.0",
- "next-tick": "1.0.0",
- "timers-ext": "0.1.5"
- }
- },
- "merge-descriptors": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
- "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
- },
- "merge-graphql-schemas": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/merge-graphql-schemas/-/merge-graphql-schemas-1.5.1.tgz",
- "integrity": "sha512-J5+FVjI7IaMXGNNLYotSGGbHmHb4Vd2KjxB45QsO5B6tSc81bJcilbtdx30fkZv7jIs7u0F8jcf7C0KfcSDBFA==",
- "requires": {
- "deepmerge": "2.1.0",
- "glob": "7.1.2",
- "is-glob": "4.0.0"
- }
- },
- "methods": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
- "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
- },
- "mime": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
- "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
- },
- "mime-db": {
- "version": "1.33.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
- "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ=="
- },
- "mime-types": {
- "version": "2.1.18",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
- "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
- "requires": {
- "mime-db": "1.33.0"
- }
- },
- "mimic-fn": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
- "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="
- },
- "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.11"
- }
- },
- "minimist": {
- "version": "0.0.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
- "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
- },
- "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"
- }
- },
- "mocha": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz",
- "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==",
- "dev": true,
- "requires": {
- "browser-stdout": "1.3.1",
- "commander": "2.15.1",
- "debug": "3.1.0",
- "diff": "3.5.0",
- "escape-string-regexp": "1.0.5",
- "glob": "7.1.2",
- "growl": "1.10.5",
- "he": "1.1.1",
- "minimatch": "3.0.4",
- "mkdirp": "0.5.1",
- "supports-color": "5.4.0"
- },
- "dependencies": {
- "debug": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
- "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- }
- }
- },
- "moment": {
- "version": "2.21.0",
- "resolved": "https://registry.npmjs.org/moment/-/moment-2.21.0.tgz",
- "integrity": "sha512-TCZ36BjURTeFTM/CwRcViQlfkMvL1/vFISuNLO5GkcVm1+QHfbSiNqZuWeMFjj1/3+uAjXswgRk30j1kkLYJBQ=="
- },
- "moment-timezone": {
- "version": "0.5.14",
- "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.14.tgz",
- "integrity": "sha1-TrOP+VOLgBCLpGekWPPtQmjM/LE=",
- "requires": {
- "moment": "2.21.0"
- }
- },
- "mongodb": {
- "version": "2.2.35",
- "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.2.35.tgz",
- "integrity": "sha512-3HGLucDg/8EeYMin3k+nFWChTA85hcYDCw1lPsWR6yV9A6RgKb24BkLiZ9ySZR+S0nfBjWoIUS7cyV6ceGx5Gg==",
- "requires": {
- "es6-promise": "3.2.1",
- "mongodb-core": "2.1.19",
- "readable-stream": "2.2.7"
- }
- },
- "mongodb-core": {
- "version": "2.1.19",
- "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.19.tgz",
- "integrity": "sha512-Jt4AtWUkpuW03kRdYGxga4O65O1UHlFfvvInslEfLlGi+zDMxbBe3J2NVmN9qPJ957Mn6Iz0UpMtV80cmxCVxw==",
- "requires": {
- "bson": "1.0.6",
- "require_optional": "1.0.1"
- }
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
- },
- "negotiator": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
- "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
- },
- "next-tick": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
- "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
- },
- "nopt": {
- "version": "3.0.6",
- "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
- "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
- "requires": {
- "abbrev": "1.1.1"
- }
- },
- "normalize-package-data": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
- "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
- "requires": {
- "hosted-git-info": "2.6.0",
- "is-builtin-module": "1.0.0",
- "semver": "4.3.2",
- "validate-npm-package-license": "3.0.3"
- }
- },
- "npm-run-path": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
- "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
- "requires": {
- "path-key": "2.0.1"
- }
- },
- "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="
- },
- "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-locale": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz",
- "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==",
- "requires": {
- "execa": "0.7.0",
- "lcid": "1.0.0",
- "mem": "1.1.0"
- }
- },
- "p-finally": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
- "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
- },
- "p-limit": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz",
- "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==",
- "requires": {
- "p-try": "1.0.0"
- }
- },
- "p-locate": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
- "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
- "requires": {
- "p-limit": "1.2.0"
- }
- },
- "p-try": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
- "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M="
- },
- "packet-reader": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-0.3.1.tgz",
- "integrity": "sha1-zWLmCvjX/qinBexP+ZCHHEaHHyc="
- },
- "parse-json": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
- "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
- "requires": {
- "error-ex": "1.3.1"
- }
- },
- "parseurl": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
- "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
- },
- "path-exists": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
- "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
- },
- "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-key": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
- "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
- },
- "path-parse": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz",
- "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME="
- },
- "path-to-regexp": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
- "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
- },
- "path-type": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
- "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
- "requires": {
- "pify": "2.3.0"
- }
- },
- "pathval": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz",
- "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA="
- },
- "pg": {
- "version": "7.4.1",
- "resolved": "https://registry.npmjs.org/pg/-/pg-7.4.1.tgz",
- "integrity": "sha512-Pi5qYuXro5PAD9xXx8h7bFtmHgAQEG6/SCNyi7gS3rvb/ZQYDmxKchfB0zYtiSJNWq9iXTsYsHjrM+21eBcN1A==",
- "requires": {
- "buffer-writer": "1.0.1",
- "js-string-escape": "1.0.1",
- "packet-reader": "0.3.1",
- "pg-connection-string": "0.1.3",
- "pg-pool": "2.0.3",
- "pg-types": "1.12.1",
- "pgpass": "1.0.2",
- "semver": "4.3.2"
- }
- },
- "pg-connection-string": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-0.1.3.tgz",
- "integrity": "sha1-2hhHsglA5C7hSSvq9l1J2RskXfc="
- },
- "pg-hstore": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/pg-hstore/-/pg-hstore-2.3.2.tgz",
- "integrity": "sha1-9+8FPnubiSrphq8vfL6GQy388k8=",
- "requires": {
- "underscore": "1.8.3"
- }
- },
- "pg-pool": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-2.0.3.tgz",
- "integrity": "sha1-wCIDLIlJ8xKk+R+2QJzgQHa+Mlc="
- },
- "pg-types": {
- "version": "1.12.1",
- "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-1.12.1.tgz",
- "integrity": "sha1-1kCH45A7WP+q0nnnWVxSIIoUw9I=",
- "requires": {
- "postgres-array": "1.0.2",
- "postgres-bytea": "1.0.0",
- "postgres-date": "1.0.3",
- "postgres-interval": "1.1.1"
- }
- },
- "pgpass": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.2.tgz",
- "integrity": "sha1-Knu0G2BltnkH6R2hsHwYR8h3swY=",
- "requires": {
- "split": "1.0.1"
- }
- },
- "pify": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
- },
- "postgres-array": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-1.0.2.tgz",
- "integrity": "sha1-jgsy6wO/d6XAp4UeBEHBaaJWojg="
- },
- "postgres-bytea": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
- "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU="
- },
- "postgres-date": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.3.tgz",
- "integrity": "sha1-4tiXAu/bJY/52c7g/pG9BpdSV6g="
- },
- "postgres-interval": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.1.1.tgz",
- "integrity": "sha512-OkuCi9t/3CZmeQreutGgx/OVNv9MKHGIT5jH8KldQ4NLYXkvmT9nDVxEuCENlNwhlGPE374oA/xMqn05G49pHA==",
- "requires": {
- "xtend": "4.0.1"
- }
- },
- "printj": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz",
- "integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ=="
- },
- "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="
- },
- "proto-list": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
- "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk="
- },
- "proxy-addr": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz",
- "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==",
- "requires": {
- "forwarded": "0.1.2",
- "ipaddr.js": "1.6.0"
- }
- },
- "pseudomap": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
- "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
- },
- "qs": {
- "version": "6.5.1",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
- "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A=="
- },
- "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.3.2",
- "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz",
- "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=",
- "requires": {
- "bytes": "3.0.0",
- "http-errors": "1.6.2",
- "iconv-lite": "0.4.19",
- "unpipe": "1.0.0"
- }
- },
- "read-pkg": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
- "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
- "requires": {
- "load-json-file": "2.0.0",
- "normalize-package-data": "2.4.0",
- "path-type": "2.0.0"
- }
- },
- "read-pkg-up": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
- "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
- "requires": {
- "find-up": "2.1.0",
- "read-pkg": "2.0.0"
- }
- },
- "readable-stream": {
- "version": "2.2.7",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz",
- "integrity": "sha1-BwV6y+JGeyIELTb5jFrVBwVOlbE=",
- "requires": {
- "buffer-shims": "1.0.0",
- "core-util-is": "1.0.2",
- "inherits": "2.0.3",
- "isarray": "1.0.0",
- "process-nextick-args": "1.0.7",
- "string_decoder": "1.0.3",
- "util-deprecate": "1.0.2"
- }
- },
- "redis": {
- "version": "2.8.0",
- "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz",
- "integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==",
- "requires": {
- "double-ended-queue": "2.1.0-0",
- "redis-commands": "1.3.5",
- "redis-parser": "2.6.0"
- }
- },
- "redis-commands": {
- "version": "1.3.5",
- "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.3.5.tgz",
- "integrity": "sha512-foGF8u6MXGFF++1TZVC6icGXuMYPftKXt1FBT2vrfU9ZATNtZJ8duRC5d1lEfE8hyVe3jhelHGB91oB7I6qLsA=="
- },
- "redis-parser": {
- "version": "2.6.0",
- "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz",
- "integrity": "sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs="
- },
- "regenerator-runtime": {
- "version": "0.11.1",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
- "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
- },
- "require-directory": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
- "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
- },
- "require-main-filename": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
- "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE="
- },
- "require_optional": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz",
- "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==",
- "requires": {
- "resolve-from": "2.0.0",
- "semver": "5.5.0"
- },
- "dependencies": {
- "semver": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
- "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA=="
- }
- }
- },
- "resolve": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.0.tgz",
- "integrity": "sha512-QdgZ5bjR1WAlpLaO5yHepFvC+o3rCr6wpfE2tpJNMkXdulf2jKomQBdNRQITF3ZKHNlT71syG98yQP03gasgnA==",
- "requires": {
- "path-parse": "1.0.5"
- }
- },
- "resolve-from": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz",
- "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c="
- },
- "retry-as-promised": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-2.3.2.tgz",
- "integrity": "sha1-zZdO5P2bX+A8vzGHHuSCIcB3N7c=",
- "requires": {
- "bluebird": "3.5.1",
- "debug": "2.6.9"
- }
- },
- "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": "4.3.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.2.tgz",
- "integrity": "sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c="
- },
- "send": {
- "version": "0.16.1",
- "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz",
- "integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==",
- "requires": {
- "debug": "2.6.9",
- "depd": "1.1.2",
- "destroy": "1.0.4",
- "encodeurl": "1.0.2",
- "escape-html": "1.0.3",
- "etag": "1.8.1",
- "fresh": "0.5.2",
- "http-errors": "1.6.2",
- "mime": "1.4.1",
- "ms": "2.0.0",
- "on-finished": "2.3.0",
- "range-parser": "1.2.0",
- "statuses": "1.3.1"
- }
- },
- "sequelize": {
- "version": "4.35.2",
- "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-4.35.2.tgz",
- "integrity": "sha512-uK1vpgq4OkRdUG1pEGfeCkudQaoDqfX/hNICm7tqsDYHjADEU/E/Bn9+ib2JQkKhIcX4X4Sp1/88/lsofsXPzA==",
- "requires": {
- "bluebird": "3.5.1",
- "cls-bluebird": "2.1.0",
- "debug": "3.1.0",
- "depd": "1.1.2",
- "dottie": "2.0.0",
- "generic-pool": "3.4.2",
- "inflection": "1.12.0",
- "lodash": "4.17.5",
- "moment": "2.21.0",
- "moment-timezone": "0.5.14",
- "retry-as-promised": "2.3.2",
- "semver": "5.5.0",
- "terraformer-wkt-parser": "1.1.2",
- "toposort-class": "1.0.1",
- "uuid": "3.2.1",
- "validator": "9.4.1",
- "wkx": "0.4.4"
- },
- "dependencies": {
- "debug": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
- "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
- "requires": {
- "ms": "2.0.0"
- }
- },
- "semver": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
- "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA=="
- }
- }
- },
- "sequelize-cli": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/sequelize-cli/-/sequelize-cli-4.0.0.tgz",
- "integrity": "sha1-TWQd+1iwNwq0QPc34bC/c38V7KU=",
- "requires": {
- "bluebird": "3.5.1",
- "cli-color": "1.2.0",
- "fs-extra": "5.0.0",
- "js-beautify": "1.7.5",
- "lodash": "4.17.5",
- "resolve": "1.7.0",
- "umzug": "2.1.0",
- "yargs": "8.0.2"
- }
- },
- "serve-static": {
- "version": "1.13.1",
- "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz",
- "integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==",
- "requires": {
- "encodeurl": "1.0.2",
- "escape-html": "1.0.3",
- "parseurl": "1.3.2",
- "send": "0.16.1"
- }
- },
- "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.1.0",
- "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
- "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
- },
- "shebang-command": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
- "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
- "requires": {
- "shebang-regex": "1.0.0"
- }
- },
- "shebang-regex": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
- "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
- },
- "shimmer": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.0.tgz",
- "integrity": "sha512-xTCx2vohXC2EWWDqY/zb4+5Mu28D+HYNSOuFzsyRDRvI/e1ICb69afwaUwfjr+25ZXldbOLyp+iDUZHq8UnTag=="
- },
- "sigmund": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
- "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA="
- },
- "signal-exit": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
- "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
- },
- "spdx-correct": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz",
- "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==",
- "requires": {
- "spdx-expression-parse": "3.0.0",
- "spdx-license-ids": "3.0.0"
- }
- },
- "spdx-exceptions": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz",
- "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg=="
- },
- "spdx-expression-parse": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
- "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
- "requires": {
- "spdx-exceptions": "2.1.0",
- "spdx-license-ids": "3.0.0"
- }
- },
- "spdx-license-ids": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz",
- "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA=="
- },
- "split": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz",
- "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==",
- "requires": {
- "through": "2.3.8"
- }
- },
- "ssf": {
- "version": "0.10.2",
- "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.10.2.tgz",
- "integrity": "sha512-rDhAPm9WyIsY8eZEKyE8Qsotb3j/wBdvMWBUsOhJdfhKGLfQidRjiBUV0y/MkyCLiXQ38FG6LWW/VYUtqlIDZQ==",
- "requires": {
- "frac": "1.1.2"
- }
- },
- "statuses": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz",
- "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4="
- },
- "streamifier": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/streamifier/-/streamifier-0.1.1.tgz",
- "integrity": "sha1-l+mNj6TRBdYqJpHR3AfoINuN/E8="
- },
- "streamsearch": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
- "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo="
- },
- "string-width": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
- "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
- "requires": {
- "is-fullwidth-code-point": "2.0.0",
- "strip-ansi": "4.0.0"
- },
- "dependencies": {
- "ansi-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
- "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
- },
- "is-fullwidth-code-point": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
- "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
- },
- "strip-ansi": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
- "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
- "requires": {
- "ansi-regex": "3.0.0"
- }
- }
- }
- },
- "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"
- }
- },
- "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-bom": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
- "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM="
- },
- "strip-eof": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
- "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="
- },
- "superagent": {
- "version": "3.8.2",
- "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.2.tgz",
- "integrity": "sha512-gVH4QfYHcY3P0f/BZzavLreHW3T1v7hG9B+hpMQotGQqurOvhv87GcMCd6LWySmBuf+BDR44TQd0aISjVHLeNQ==",
- "requires": {
- "component-emitter": "1.2.1",
- "cookiejar": "2.1.1",
- "debug": "3.1.0",
- "extend": "3.0.1",
- "form-data": "2.3.2",
- "formidable": "1.2.1",
- "methods": "1.1.2",
- "mime": "1.4.1",
- "qs": "6.5.1",
- "readable-stream": "2.2.7"
- },
- "dependencies": {
- "debug": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
- "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
- "requires": {
- "ms": "2.0.0"
- }
- }
- }
- },
- "supertest": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/supertest/-/supertest-3.1.0.tgz",
- "integrity": "sha512-O44AMnmJqx294uJQjfUmEyYOg7d9mylNFsMw/Wkz4evKd1njyPrtCN+U6ZIC7sKtfEVQhfTqFFijlXx8KP/Czw==",
- "requires": {
- "methods": "1.1.2",
- "superagent": "3.8.2"
- }
- },
- "supports-color": {
- "version": "5.4.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
- "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
- "dev": true,
- "requires": {
- "has-flag": "3.0.0"
- }
- },
- "terraformer": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/terraformer/-/terraformer-1.0.8.tgz",
- "integrity": "sha1-UeCtiXRvzyFh3G9lqnDkI3fItZM=",
- "requires": {
- "@types/geojson": "1.0.6"
- }
- },
- "terraformer-wkt-parser": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/terraformer-wkt-parser/-/terraformer-wkt-parser-1.1.2.tgz",
- "integrity": "sha1-M2oMj8gglKWv+DKI9prt7NNpvww=",
- "requires": {
- "terraformer": "1.0.8"
- }
- },
- "through": {
- "version": "2.3.8",
- "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
- "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
- },
- "timers-ext": {
- "version": "0.1.5",
- "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.5.tgz",
- "integrity": "sha512-tsEStd7kmACHENhsUPaxb8Jf8/+GZZxyNFQbZD07HQOyooOa6At1rQqjffgvg7n+dxscQa9cjjMdWhJtsP2sxg==",
- "requires": {
- "es5-ext": "0.10.42",
- "next-tick": "1.0.0"
- }
- },
- "toposort-class": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz",
- "integrity": "sha1-f/0feMi+KMO6Rc1OGj9e4ZO9mYg="
- },
- "type-detect": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
- "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g=="
- },
- "type-is": {
- "version": "1.6.16",
- "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
- "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==",
- "requires": {
- "media-typer": "0.3.0",
- "mime-types": "2.1.18"
- }
- },
- "umzug": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/umzug/-/umzug-2.1.0.tgz",
- "integrity": "sha512-BgT+ekpItEWaG+3JjLLj6yVTxw2wIH8Cr6JyKYIzukWAx9nzGhC6BGHb/IRMjpobMM1qtIrReATwLUjKpU2iOQ==",
- "requires": {
- "babel-runtime": "6.26.0",
- "bluebird": "3.5.1",
- "lodash": "4.17.5",
- "resolve": "1.7.0"
- }
- },
- "underscore": {
- "version": "1.8.3",
- "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz",
- "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI="
- },
- "universalify": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz",
- "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc="
- },
- "unpipe": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
- "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
- },
- "util-deprecate": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
- },
- "utils-merge": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
- "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
- },
- "uuid": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz",
- "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA=="
- },
- "validate-npm-package-license": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz",
- "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==",
- "requires": {
- "spdx-correct": "3.0.0",
- "spdx-expression-parse": "3.0.0"
- }
- },
- "validator": {
- "version": "9.4.1",
- "resolved": "https://registry.npmjs.org/validator/-/validator-9.4.1.tgz",
- "integrity": "sha512-YV5KjzvRmSyJ1ee/Dm5UED0G+1L4GZnLN3w6/T+zZm8scVua4sOhYKWTUrKa0H/tMiJyO9QLHMPN+9mB/aMunA=="
- },
- "vary": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
- "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
- },
- "which": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz",
- "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==",
- "requires": {
- "isexe": "2.0.0"
- }
- },
- "which-module": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
- "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
- },
- "wkx": {
- "version": "0.4.4",
- "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.4.4.tgz",
- "integrity": "sha512-eVVHka2jRaAp9QanKhLpxWs3AGDV0b8cijlavxBnn4ryXzq5N/3Xe3nkQsI0XMRA16RURwviCWuOCj4mXCmrxw==",
- "requires": {
- "@types/node": "9.4.6"
- }
- },
- "wrap-ansi": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
- "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
- "requires": {
- "string-width": "1.0.2",
- "strip-ansi": "3.0.1"
- },
- "dependencies": {
- "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"
- }
- }
- }
- },
- "wrappy": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
- },
- "xlsx": {
- "version": "0.12.11",
- "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.12.11.tgz",
- "integrity": "sha1-VIBteKe7ApmKuj1ZxJmVpvC+AB4=",
- "requires": {
- "adler-32": "1.2.0",
- "cfb": "1.0.7",
- "codepage": "1.12.2",
- "commander": "2.14.1",
- "crc-32": "1.2.0",
- "exit-on-epipe": "1.0.1",
- "ssf": "0.10.2"
- },
- "dependencies": {
- "commander": {
- "version": "2.14.1",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.14.1.tgz",
- "integrity": "sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw=="
- }
- }
- },
- "xtend": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
- "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
- },
- "y18n": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
- "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE="
- },
- "yallist": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
- "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
- },
- "yargs": {
- "version": "8.0.2",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz",
- "integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=",
- "requires": {
- "camelcase": "4.1.0",
- "cliui": "3.2.0",
- "decamelize": "1.2.0",
- "get-caller-file": "1.0.2",
- "os-locale": "2.1.0",
- "read-pkg-up": "2.0.0",
- "require-directory": "2.1.1",
- "require-main-filename": "1.0.1",
- "set-blocking": "2.0.0",
- "string-width": "2.1.1",
- "which-module": "2.0.0",
- "y18n": "3.2.1",
- "yargs-parser": "7.0.0"
- }
- },
- "yargs-parser": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz",
- "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=",
- "requires": {
- "camelcase": "4.1.0"
- }
- }
- }
-}
diff --git a/package.json b/package.json
index a1488db..8857665 100644
--- a/package.json
+++ b/package.json
@@ -4,12 +4,17 @@
"description": "Schema as string",
"main": "index.js",
"scripts": {
+ "start": "node server.js",
"test": "mocha test/test.js --exit"
},
"author": "vsuaste",
"license": "ISC",
"dependencies": {
"acl": "^0.4.11",
+ "adm-zip": "^0.4.13",
+ "ajv": "^6.10.0",
+ "awaitify-stream": "^1.0.2",
+ "bcrypt": "^3.0.3",
"bcryptjs": "^2.4.3",
"chai": "^4.1.2",
"cors": "^2.8.4",
@@ -17,19 +22,30 @@
"express": "^4.16.2",
"express-fileupload": "^0.4.0",
"express-graphql": "^0.6.12",
+ "express-jwt": "^5.3.1",
"faker": "^4.1.0",
"graphql": "^0.13.1",
+ "graphql-iso-date": "^3.6.1",
"jsonwebtoken": "^8.2.0",
+ "linked-list": "^2.0.0",
"lodash": "^4.17.5",
+ "mathjs": "^5.2.0",
"merge-graphql-schemas": "^1.5.1",
+ "mysql2": "^1.6.4",
+ "nodemailer": "^5.1.1",
+ "nodemailer-smtp-transport": "^2.7.4",
"pg": "^7.4.1",
"pg-hstore": "^2.3.2",
"sequelize": "^4.35.2",
"sequelize-cli": "^4.0.0",
"supertest": "^3.1.0",
+ "sync-request": "^6.0.0",
+ "uuidv4": "^2.0.0",
"xlsx": "^0.12.11"
},
"devDependencies": {
- "mocha": "^5.2.0"
+ "mocha": "^5.2.0",
+ "axios": "^0.18.0",
+ "form-data": "^2.3.2-rc1"
}
}
diff --git a/server.js b/server.js
index 3d1706c..0620fdb 100644
--- a/server.js
+++ b/server.js
@@ -1,61 +1,171 @@
var express = require('express');
var path = require('path');
var graphqlHTTP = require('express-graphql');
+ var jwt = require('express-jwt');
const fileUpload = require('express-fileupload');
- var {buildSchema} = require('graphql');
+ const auth = require('./utils/login');
+ const bodyParser = require('body-parser');
+ const globals = require('./config/globals');
+ const JOIN = require('./utils/join-models');
+ const simpleExport = require('./utils/simple-export');
+ const {GraphQLDateTime, GraphQLDate, GraphQLTime } = require('graphql-iso-date');
+
+ var {
+ buildSchema
+ } = require('graphql');
var mergeSchema = require('./utils/merge-schemas');
var acl = null;
var cors = require('cors');
+
+ /* Server */
+ const APP_PORT = globals.PORT;
+ const app = express();
+
+ app.use((req, res, next)=> {
+
+ // Website you wish to allow to connect
+ res.setHeader('Access-Control-Allow-Origin', globals.ALLOW_ORIGIN);
+ //res.setHeader('Access-Control-Expose-Headers', 'Access-Control-Allow-Origin');
+
+ // Request methods you wish to allow
+ //res.setHeader('Access-Control-Allow-Methods',
+ // 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
+
+ // Request headers you wish to allow
+ //res.setHeader('Access-Control-Allow-Headers',
+ // 'X-Requested-With,content-type,authorization,Authorization,accept,Accept');
+ next();
+ });
+
+ // Force users to sign in to get access to anything else than '/login'
+ console.log("REQUIRE: ",globals.REQUIRE_SIGN_IN);
+ if(globals.REQUIRE_SIGN_IN === "true"){
+ app.use(jwt({ secret: 'something-secret'}).unless({path: ['/login']}));
+ }
+
+
/* Temporary solution: acl rules set */
- if(process.argv.length > 2 && process.argv[2]=='acl')
- {
+ if (process.argv.length > 2 && process.argv[2] == 'acl') {
var node_acl = require('acl');
- var {aclRules} = require('./acl_rules');
+ var {
+ aclRules
+ } = require('./acl_rules');
var acl = new node_acl(new node_acl.memoryBackend());
/* set authorization rules from file acl_rules.js */
acl.allow(aclRules);
console.log("Authoization rules set!");
- /*For testing purposes*/
- acl.addUserRoles(1, 'guest');
- acl.addUserRoles(2, 'administrator');
-}else{
- console.log("Open server, no authorization rules");
-}
+ } else {
+ console.log("Open server, no authorization rules");
+ }
/* Schema */
-var merged_schema = mergeSchema( path.join(__dirname, './schemas'));
-var Schema = buildSchema(merged_schema);
+ console.log('Merging Schema');
+ var merged_schema = mergeSchema(path.join(__dirname, './schemas'));
+ console.log(merged_schema);
+ var Schema = buildSchema(merged_schema);
+ /*set scalar types for dates */
+ Object.assign(Schema._typeMap.DateTime, GraphQLDateTime);
+ Object.assign(Schema._typeMap.Date, GraphQLDate);
+ Object.assign(Schema._typeMap.Time, GraphQLTime);
-/* Resolvers*/
-var resolvers = require('./resolvers/index');
+ /* Resolvers*/
+ var resolvers = require('./resolvers/index');
+
+
+
+
+app.use(bodyParser.urlencoded({ extended: false }));
+app.use(bodyParser.json());
+
+app.use('/login', cors(), (req, res)=>{
+
+ auth.login(req.body).then( (token) =>{
+ res.json({token: token});
+ }).catch((err) =>{
+ console.log(err);
+ res.status(500).send({error: "Wrong email or password. Please check your credentials."})
+ });
+
+});
- /* Server */
- const APP_PORT = 3000;
- const app = express();
- //app.use((req, res, next)=> {
- // Website you wish to allow to connect
- //res.setHeader('Access-Control-Allow-Origin', '*');
- //res.setHeader('Access-Control-Expose-Headers', 'Access-Control-Allow-Origin');
- // Request methods you wish to allow
- //res.setHeader('Access-Control-Allow-Methods',
- // 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
+app.use('/join', cors(), (req, res) => {
- // Request headers you wish to allow
- //res.setHeader('Access-Control-Allow-Headers',
- // 'X-Requested-With,content-type,authorization,Authorization,accept,Accept');
- // next();
- //});
+ // check if the Content-Type is in JSON so that bodyParser can be applied automatically
+ if (!req.is('application/json'))
+ return res.status(415).send({error: "JSON Content-Type expected"});
-app.use(fileUpload());
- /*request is passed as context by default */
- app.use('/graphql', cors(),graphqlHTTP((req)=> ({
+ let context = {
+ request: req,
+ acl: acl
+ };
+
+ // select the output format
+ let params = req.body;
+ let joinModels = {};
+
+ if(params.outputFormat === 'TEST'){
+ joinModels = new JOIN.JoinModels(context);
+ }else if(params.outputFormat === 'CSV'){
+ joinModels = new JOIN.JoinModelsCSV(context);
+ }else if(params.outputFormat === 'JSON'){
+ joinModels = new JOIN.JoinModelsJSON(context);
+ }else{
+ return res.status(415).send({error: "outputFormat = TEST/CSV/JSON is required"});
+ }
+
+ // start data transmission
+ joinModels.run(req.body, res).then(() => {
+ res.end();
+ }).catch(error => {
+ let formattedError = {
+ message: error.message,
+ details: error.originalError && error.originalError.errors ? error.originalError.errors : "",
+ path: error.path
+ };
+ res.status(500).send(formattedError);
+ });
+});
+
+
+
+app.use('/export', cors(), (req, res) =>{
+
+ let context = {
+ request: req,
+ acl : acl
+ }
+
+ let body_info = req.query;
+
+ simpleExport(context, body_info ,res).then( () =>{
+ res.end();
+ }).catch( error => {
+ let formattedError = {
+ message: error.message,
+ details: error.originalError && error.originalError.errors ? error.originalError.errors : "",
+ path: error.path
+ };
+ res.status(500).send(formattedError);
+ });
+
+
+});
+
+
+
+
+
+
+ app.use(fileUpload());
+ /*request is passed as context by default */
+ app.use('/graphql', cors(), graphqlHTTP((req) => ({
schema: Schema,
rootValue: resolvers,
pretty: true,
@@ -63,12 +173,28 @@ app.use(fileUpload());
context: {
request: req,
acl: acl
+ },
+ formatError(error){
+ return {
+ message: error.message,
+ details: error.originalError && error.originalError.errors ? error.originalError.errors : "",
+ path: error.path
+ };
}
})));
+ // Error handling
+ app.use(function (err, req, res, next) {
+ if (err.name === 'UnauthorizedError') { // Send the error rather than to show it on the console
+ res.status(401).send(err);
+ }
+ else {
+ next(err);
+ }
+ });
-var server = app.listen(APP_PORT, ()=>{
+ var server = app.listen(APP_PORT, () => {
console.log(`App listening on port ${APP_PORT}`);
});
-module.exports = server;
+ module.exports = server;
diff --git a/test/test-data.js b/test/test-data.js
deleted file mode 100644
index 47ac566..0000000
--- a/test/test-data.js
+++ /dev/null
@@ -1,60 +0,0 @@
-
-module.exports.people = [
- {
- "firstName": "Leonardo",
- "lastName": "Da Vinci",
- "email": "leonardo.vinci@art.com"
- },
- {
- "firstName": "Thomas",
- "lastName": "Edison",
- "email": "thomas.edison@science.com"
- },
- {
- "firstName": "Vicent",
- "lastName": "van Gogh",
- "email": "vicent.vanGogh@art.com"
- },
- {
- "firstName": "Albert",
- "lastName": "Einstein",
- "email": "albert.einstein@science.com"
- },
- {
- "firstName": "Ludwig",
- "lastName": "Beethoven",
- "email": "ludwig.beethoven@art.com"
- }
- ];
-
-module.exports.searchPeopleByEmail = [
- {
- "firstName": "Leonardo",
- "lastName": "Da Vinci",
- },
- {
- "firstName": "Vicent",
- "lastName": "van Gogh",
- },
- {
- "firstName": "Ludwig",
- "lastName": "Beethoven",
- }
-];
-
-module.exports.readOneBook = {
- "title": "Science of music"
- };
-
-
-module.exports.addDog = {
- "name": "toto",
- "breed": "chihuahua"
- };
-
-module.exports.deleteDog = "Item succesfully deleted";
-
-module.exports.updateBook = {
- "title": "Paintings II",
- "genre": "Art"
-};
diff --git a/test/test.js b/test/test.js
deleted file mode 100644
index 58ca736..0000000
--- a/test/test.js
+++ /dev/null
@@ -1,97 +0,0 @@
-var server = require('../server');
-const request = require('supertest');
-const test = require('./test-data');
-
-const chai = require('chai');
-const expect = chai.expect;
-
-describe('Testing Queries Server GraphQL', ()=>{
- it('Read all - people', (done)=>{
- request(server).post('/graphql')
- .send({query: '{people{ firstName lastName email}}'})
- .expect(200).end((err,res)=>{
- if(err){
- console.log(err);
- done(err);
- }
- expect(res.body.data.people).to.deep.equal(test.people);
- done();
- });
-
- });
-
- it('Search with filter - people by email', (done)=>{
- request(server).post('/graphql')
- .send({query: '{ searchPerson(input:{field:email, value:{value:"%art%"}, operator:like}){ firstName lastName}}'})
- .expect(200).end((err,res)=>{
- if(err){
- console.log(err);
- done(err);
- }
- expect(res.body.data.searchPerson).to.deep.equal(test.searchPeopleByEmail);
- done();
- });
-
- });
-
- it('Read one by Id - books', (done)=>{
- request(server).post('/graphql')
- .send({query: '{readOneBook(id:3){title}}'})
- .expect(200).end((err,res)=>{
- if(err){
- console.log(err);
- done(err);
- }
- expect(res.body.data.readOneBook).to.deep.equal(test.readOneBook);
- done();
- });
- });
-});
-
-describe('Testing Mutations Server GraphQL', ()=>{
- it('Create one - dogs', (done)=>{
- request(server).post('/graphql')
- .send({query: 'mutation{addDog(name:"toto", breed:"chihuahua"){name breed}}'})
- .expect(200).end((err,res)=>{
- if(err){
- console.log(err);
- done(err);
- }
- expect(res.body.data.addDog).to.deep.equal(test.addDog);
- done();
- });
- });
-
- it('Delete one - dogs', (done)=>{
- request(server).post('/graphql')
- .send({query: 'mutation{deleteDog(id:6)}'})
- .expect(200).end((err,res)=>{
- if(err){
- console.log(err);
- done(err);
- }
- expect(res.body.data.deleteDog).to.deep.equal(test.deleteDog);
- done();
- });
- });
-
- it('Update one - books', (done)=>{
- request(server).post('/graphql')
- .send({query: 'mutation{updateBook(id:2, title:"Paintings II"){title genre}}'})
- .expect(200).end((err,res)=>{
- if(err){
- console.log(err);
- done(err);
- }
- expect(res.body.data.updateBook).to.deep.equal(test.updateBook);
- done();
- });
- });
-
-
-});
-
-
-
-
-server.close();
diff --git a/utils/check-authorization.js b/utils/check-authorization.js
index 5fe8dbd..ae31364 100644
--- a/utils/check-authorization.js
+++ b/utils/check-authorization.js
@@ -2,27 +2,37 @@ const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const secret = 'something-secret';
-module.exports = function( context, resource, permission ) {
+
+/**
+ * @function - Given a context this function check if the user(implicit in context)
+ * is allowed to perform 'permission' action to the 'resource' model.
+ *
+ * @param {object} context context object contains the request info and the acl rules.
+ * @param {string} resource resource to which the user wants to perform an action (i.e. a model).
+ * @param {string} permission action that the user wants to perform to resourse (i.e. read, edit, create).
+ * @return {promise} it will resolve true if within the context the user is allowed to perform 'permission' action to the 'resource' model.
+ */
+module.exports = function( context, resource, permission ) {
//if there's not authorization rules set
- if (context.acl == null) return true;
+ if (context.acl == null) //return true;
+ {
+ return Promise.resolve(true);
+ }
- let token = context.request.headers["authorization"];
+ let token_bearer = context.request.headers["authorization"];
+ let token = token_bearer.replace("Bearer ","");
+ console.log("TOKEN",typeof token, token);
try{
//Identify user from context
let decoded = jwt.verify(token, secret);
- //check for permissions of that specific user
- let allowed = context.acl.isAllowed(decoded.id, resource, permission);
- console.log(typeof decoded.id, ' * ' ,decoded.id);
- if(allowed){
- return true;
- }else{
- //no permission for user
- console.log("Permission dennied...");
- return false;
- }
+
+ //check for permissions from specific roles
+ return context.acl.areAnyRolesAllowed(decoded.roles, resource, permission);
}catch(err){
//invalid token
console.log("invalid token...");
- return false;
+ console.log(err);
+ throw new Error(err);
+ //return false;
}
}
diff --git a/utils/email.js b/utils/email.js
new file mode 100644
index 0000000..ebd9d51
--- /dev/null
+++ b/utils/email.js
@@ -0,0 +1,31 @@
+const NodeMailer = require('nodemailer');
+const SmtpTransport = require('nodemailer-smtp-transport');
+const Globals = require('../config/globals');
+const path = require('path');
+
+module.exports = {
+ sendEmail: function (dst_email, subj, message, att){
+ console.log(`${dst_email}, ${message}, ${Globals.MAIL_ACCOUNT}, ${Globals.MAIL_PASSWORD}`);
+
+ let transporter = NodeMailer.createTransport(SmtpTransport({
+ service: Globals.MAIL_SERVICE,
+ host: Globals.MAIL_HOST,
+ auth: {
+ type: "login",
+ user: Globals.MAIL_ACCOUNT,
+ pass: Globals.MAIL_PASSWORD
+ }
+ }));
+
+ let mailOptions = {
+ from: Globals.MAIL_ACCOUNT,
+ to: dst_email,
+ subject: subj,
+ text: message,
+ attachments: att
+ };
+
+
+ return transporter.sendMail(mailOptions);
+ }
+};
\ No newline at end of file
diff --git a/utils/errors.js b/utils/errors.js
new file mode 100644
index 0000000..f0eae8a
--- /dev/null
+++ b/utils/errors.js
@@ -0,0 +1,18 @@
+
+class customArrayError extends Error {
+ constructor(errors_array, message){
+ super();
+ this.message = message;
+ this.errors = errors_array;
+ }
+}
+
+handleError = function(error){
+ if(error.name === "SequelizeValidationError"){
+ throw new customArrayError(error.errors, "Validation error");
+ }else{
+ throw new Error(error)
+ }
+}
+
+module.exports = { handleError}
diff --git a/utils/file-tools.js b/utils/file-tools.js
index 931f00f..b34c313 100644
--- a/utils/file-tools.js
+++ b/utils/file-tools.js
@@ -1,7 +1,19 @@
const XLSX = require('xlsx');
const Promise = require('bluebird');
-const csv_parse = Promise.promisify(require('csv-parse'));
+const promise_csv_parse = Promise.promisify(require('csv-parse'));
+const csv_parse = require('csv-parse');
+const fs = require('fs');
+const awaitifyStream = require('awaitify-stream');
+const validatorUtil = require('./validatorUtil');
+const admZip = require('adm-zip');
+
+/**
+ * replaceNullStringsWithLiteralNulls - Replace null entries of columns with literal null types
+ *
+ * @param {array} arrOfObjs Each item correponds to a column represented as object.
+ * @return {array} Each item corresponds to a column and all items have either a valid entry or null type.
+ */
replaceNullStringsWithLiteralNulls = function(arrOfObjs) {
console.log(typeof arrOfObjs, arrOfObjs);
return arrOfObjs.map(function(csvRow) {
@@ -14,17 +26,33 @@ replaceNullStringsWithLiteralNulls = function(arrOfObjs) {
});
}
+
+/**
+ * parseCsv - parse csv file (string)
+ *
+ * @param {string} csvStr Csv file converted to string.
+ * @param {string} delim Set the field delimiter in the csv file. One or multiple character.
+ * @param {array|boolean|function} cols Columns as in csv-parser options.(true if auto-discovered in the first CSV line).
+ * @return {array} Each item correponds to a column represented as object and filtered with replaceNullStringsWithLiteralNulls function.
+ */
exports.parseCsv = function(csvStr, delim, cols) {
if (!delim) delim = ","
if (typeof cols === 'undefined') cols = true
return replaceNullStringsWithLiteralNulls(
- csv_parse(csvStr, {
+ promise_csv_parse(csvStr, {
delimiter: delim,
columns: cols
})
)
}
+
+/**
+ * parseXlsx - description
+ *
+ * @param {string} bstr Xlsx file converted to string
+ * @return {array} Each item correponds to a column represented as object and filtered with replaceNullStringsWithLiteralNulls function.
+ */
exports.parseXlsx = function(bstr) {
var workbook = XLSX.read(bstr, {
type: "binary"
@@ -34,4 +62,167 @@ exports.parseXlsx = function(bstr) {
XLSX.utils.sheet_to_json(
workbook.Sheets[sheet_name_list[0]])
);
-}
+};
+
+/**
+ * Function that will delete a file if it exists and is insensitive to the
+ * case when a file not exist.
+ *
+ * @param {String} path - A path to the file
+ */
+exports.deleteIfExists = function(path) {
+ console.log(`Removing ${path}`);
+ fs.unlink(path, function(err) {
+ // file may be already deleted
+ });
+};
+
+/**
+ * Function deletes properties that contain string values "NULL" or "null".
+ *
+ * @param {Object} pojo - A plain old JavaScript object.
+ *
+ * @return {Object} A modified clone of the argument pojo in which all String
+ * "NULL" or "null" values are deleted.
+ */
+exports.replacePojoNullValueWithLiteralNull = function(pojo) {
+ if (pojo === null || pojo === undefined) {
+ return null
+ }
+ let res = Object.assign({}, pojo);
+ Object.keys(res).forEach((k) => {
+ if (typeof res[k] === "string" && res[k].match(/\s*null\s*/i)) {
+ delete res[k];
+ }
+ });
+ return res
+};
+
+
+/**
+ * Parse by streaming a csv file and create the records in the correspondant table
+ * @function
+ * @param {string} csvFilePath - The path where the csv file is stored.
+ * @param {object} model - Sequelize model, record will be created through this model.
+ * @param {string} delim - Set the field delimiter in the csv file. One or multiple character.
+ * @param {array|boolean|function} cols - Columns as in csv-parser options.(true if auto-discovered in the first CSV line).
+ */
+exports.parseCsvStream = async function(csvFilePath, model, delim, cols) {
+
+ if (!delim) delim = ",";
+ if (typeof cols === 'undefined') cols = true;
+ console.log("TYPEOF", typeof model);
+ // Wrap all database actions within a transaction:
+ let transaction = await model.sequelize.transaction();
+
+ let addedFilePath = csvFilePath.substr(0, csvFilePath.lastIndexOf(".")) +
+ ".json";
+ let addedZipFilePath = csvFilePath.substr(0, csvFilePath.lastIndexOf(".")) +
+ ".zip";
+
+ console.log(addedFilePath);
+ console.log(addedZipFilePath);
+
+ try {
+ // Pipe a file read-stream through a CSV-Reader and handle records asynchronously:
+ let csvStream = awaitifyStream.createReader(
+ fs.createReadStream(csvFilePath).pipe(
+ csv_parse({
+ delimiter: delim,
+ columns: cols,
+ cast: true
+ })
+ )
+ );
+
+ // Create an output file stream
+ let addedRecords = awaitifyStream.createWriter(
+ fs.createWriteStream(addedFilePath)
+ );
+
+ let record;
+ let errors = [];
+
+ while (null !== (record = await csvStream.readAsync())) {
+
+ console.log(record);
+ record = exports.replacePojoNullValueWithLiteralNull(record);
+ console.log(record);
+
+
+ try {
+ let result = await validatorUtil.ifHasValidatorFunctionInvoke(
+ 'validateForCreate', model, record);
+ console.log(result);
+ await model.create(record, {
+ transaction: transaction
+ }).then(created => {
+
+ // this is async, here we just push new line into the parallel thread
+ // synchronization goes at endAsync;
+ addedRecords.writeAsync(`${JSON.stringify(created)}\n`);
+
+ }).catch(error => {
+ console.log(
+ `Caught sequelize error during CSV batch upload: ${JSON.stringify(error)}`
+ );
+ error.record = record;
+ errors.push(error);
+ })
+ } catch (error) {
+ console.log(
+ `Validation error during CSV batch upload: ${JSON.stringify(error)}`
+ );
+ error['record'] = record;
+ errors.push(error);
+
+ }
+
+ }
+
+ // close the addedRecords file so it can be sent afterwards
+ await addedRecords.endAsync();
+
+ if (errors.length > 0) {
+ let message =
+ "Some records could not be submitted. No database changes has been applied.\n";
+ message += "Please see the next list for details:\n";
+
+ errors.forEach(function(error) {
+ valErrMessages = error.errors.reduce((acc, val) => {
+ return acc.concat(val.dataPath).concat(" ").concat(val.message)
+ .concat(" ")
+ })
+ message +=
+ `record ${JSON.stringify(error.record)} ${error.message}: ${valErrMessages}; \n`;
+ });
+
+ throw new Error(message.slice(0, message.length - 1));
+ }
+
+ await transaction.commit();
+
+ // zip comitted data and return a corresponding file path
+ let zipper = new admZip();
+ zipper.addLocalFile(addedFilePath);
+ await zipper.writeZip(addedZipFilePath);
+
+ console.log(addedZipFilePath);
+
+ // At this moment the parseCsvStream caller is responsible in deleting the
+ // addedZipFilePath
+ return addedZipFilePath;
+
+ } catch (error) {
+
+ await transaction.rollback();
+
+ exports.deleteIfExists(addedFilePath);
+ exports.deleteIfExists(addedZipFilePath);
+
+ throw error;
+
+ } finally {
+ exports.deleteIfExists(addedFilePath);
+ }
+};
diff --git a/utils/graphql-sequelize-types.js b/utils/graphql-sequelize-types.js
new file mode 100644
index 0000000..d10ab02
--- /dev/null
+++ b/utils/graphql-sequelize-types.js
@@ -0,0 +1,14 @@
+/*
+Data types dictionary from graphql-type to sequelize-type
+*/
+
+module.exports = {
+
+ "Int" : 'INTEGER',
+ "String": 'TEXT',
+ "Float": 'FLOAT',
+ "Boolean": 'BOOLEAN',
+ "Date": "DATEONLY",
+ "Time": "TIME",
+ "DateTime": "DATE"
+}
diff --git a/utils/graphql_schema.js b/utils/graphql_schema.js
new file mode 100644
index 0000000..2837eb4
--- /dev/null
+++ b/utils/graphql_schema.js
@@ -0,0 +1,33 @@
+const path = require('path');
+
+const { buildSchema } = require('graphql');
+const mergeSchema = require(path.join(__dirname,'../','utils','merge-schemas'));
+
+module.exports.schema = buildSchema(mergeSchema(path.join(__dirname,'../','./schemas')));
+
+/**
+ * Each model deefined in it's *.json file can have a description field. However, we also add
+ * internal annotations within this filed. It is possible to search for fields of the given
+ * model with respect to the presence of an annotation inside of it's description.
+ * @function getModelFieldByAnnotation
+ * @param {string} model_name - The name of the data model to be explored.
+ * @param {string} annotation - An annotation name, for example "@original-field"
+ * @return {Array} - an array of model field names that contain a given annotation in it's
+ * description
+ */
+module.exports.getModelFieldByAnnotation = function(model_name, annotation){
+ let schema = module.exports.schema;
+
+ let model_type = schema.getType(model_name);
+ if(! schema.getType(model_name))
+ throw new Error(`Model ${model_name} not exist`);
+
+ let fields = model_type._fields;
+
+ return Object.keys(fields).filter( a => {
+ let desc = fields[a].description;
+ if(desc)
+ return desc.toString().includes(annotation);
+ return false;
+ })
+};
\ No newline at end of file
diff --git a/utils/helper.js b/utils/helper.js
new file mode 100644
index 0000000..984b5cd
--- /dev/null
+++ b/utils/helper.js
@@ -0,0 +1,243 @@
+const objectAssign = require('object-assign');
+const math = require('mathjs');
+
+
+ /**
+ * paginate - Creates pagination argument as needed in sequelize cotaining limit and offset accordingly to the current
+ * page implicit in the request info.
+ *
+ * @param {object} req Request info.
+ * @return {object} Pagination argument.
+ */
+ paginate = function(req) {
+ selectOpts = {}
+ if (req.query.per_page){ selectOpts['limit'] = req.query.per_page}
+ else{ selectOpts['limit'] = 20}
+ if (req.query.page) {
+ os = (req.query.page - 1) * selectOpts['limit']
+ selectOpts['offset'] = os
+ }
+ return selectOpts
+ }
+
+
+
+ /**
+ * requestedUrl - Recover baseUrl from the request.
+ *
+ * @param {object} req Request info.
+ * @return {string} baseUrl from request.
+ */
+ requestedUrl = function(req) {
+ //console.log(req.port)
+ //console.log(req.headers.host)
+ //let port = req.port|| 2000;
+ return req.protocol + '://' + req.headers.host +
+ //(port == 80 || port == 443 ? '' : ':' + port) +
+ req.baseUrl;
+ }
+
+
+
+ /**
+ * prevNextPageUrl - Creates request string for previous or next page int the vue-table data object.
+ *
+ * @param {object} req Request info.
+ * @param {boolean} isPrevious True if previous page is requestes and false if next page is requested.
+ * @return {string} String request for previous or next page int the vue-table data object.
+ */
+ prevNextPageUrl = function(req, isPrevious) {
+ //console.log("Requested URL", req);
+ let baseUrl = requestedUrl(req).replace(/\?.*$/, '')
+ let query = ["query="+req.query.query]
+ i = isPrevious ? -1 : 1
+ // page
+ p = req.query.page == '1' ? null : (req.query.page + i)
+ query = query.concat(['page=' + p])
+ // per_page
+ query = query.concat(['per_page=' + (req.query.per_page || 20)])
+ // filter
+ if (req.query.filter) query = query.concat(['filter=' + req.query.filter])
+ // sort
+ if (req.query.sort) query = query.concat(['sort=' + req.query.sort])
+ // Append query to base URL
+ if (query.length > 0) baseUrl += "?" + query.join("&")
+ return baseUrl
+ }
+
+
+ /**
+ * sort - Creates sort argument as needed in sequelize and accordingly to the order implicit in the resquest info.
+ *
+ * @param {object} req Request info.
+ * @return {object} Sort argument object as needed in the schema to retrieve filtered records from a given model.
+ */
+ sort = function(req) {
+ let sortOpts = {}
+ if (req.query.sort) {
+ sortOpts = {
+ order: [req.query.sort.split('|')]
+ }
+ }
+ return sortOpts
+ }
+
+
+ /**
+ * search - Creates search argument as needed in sequelize and accordingly to the filter string implicit in the resquest info.
+ *
+ * @param {object} req Request info. This info will contain the substring that will be used to filter records.
+ * @param {array} strAttributes Name of model's attributes
+ * @return {object} Search argument object as needed in the schema to retrieve filtered records from a given model.
+ */
+ search = function(req, strAttributes) {
+ let selectOpts = {}
+ if (req.query.filter) {
+ let fieldClauses = []
+ strAttributes.forEach(function(x) {
+ let fieldWhereClause = {}
+ if (x !== "id") {
+ fieldWhereClause[x] = {
+ $like: "%" + req.query.filter + "%"
+ }
+ fieldClauses = fieldClauses.concat([fieldWhereClause])
+ } else {
+ if (/^\d+$/.test(req.query.filter)) {
+ fieldWhereClause[x] = req.query.filter
+ fieldClauses = fieldClauses.concat([fieldWhereClause])
+ }
+ }
+ })
+ selectOpts['where'] = {
+ $or: fieldClauses
+ }
+ }
+ return selectOpts
+ }
+
+
+// includeAssociations = function (req) {
+// return req.query.excludeAssociations ? {} : {
+// include: [{
+// all: true
+// }]
+// }
+// }
+
+/**
+ * searchPaginate - Creates one object mergin search, sort, and paginate arguments
+ *
+ * @param {object} req Request info.
+ * @param {array} strAttributes Name of model's attributes.
+ * @return {object} General argument for filtering models in sequelize.
+ */
+searchPaginate = function(req, strAttributes) {
+ return objectAssign(
+ search(req, strAttributes),
+ sort(req),
+ paginate(req)
+ //,includeAssociations(req)
+ );
+}
+
+/**
+ * vueTable - Creates object needed to display a vue-table in a vuejs SPA
+ *
+ * @param {object} req Request info.
+ * @param {object} model Sequelize model which records are intended to be displayed in the vue-table.
+ * @param {array} strAttributes Name of model's attributes.
+ * @return {object} Info for displaying vue-table in a vuejs SPA, including info for automatic pagination.
+ */
+module.exports.vueTable = function(req, model, strAttributes) {
+ let searchOptions = search(req, strAttributes)
+ let searchSortPagIncl = searchPaginate( req, strAttributes )
+ let queries = []
+ queries.push(model.count(searchOptions))
+ queries.push(model.findAll(searchSortPagIncl))
+ return Promise.all(queries).then(
+ function(res) {
+ let searchRes = res[0]
+ let paginatedSearchRes = res[1]
+ let lastPage = math.ceil(searchRes / req.query.per_page)
+ return {
+ data: paginatedSearchRes,
+ total: searchRes,
+ per_page: req.query.per_page,
+ current_page: req.query.page,
+ 'from': (req.query.page - 1) * req.query.per_page + 1,
+ 'to': math.min(searchRes, req.query.page * req.query.per_page),
+ last_page: lastPage,
+ prev_page_url: (req.query.page == 1) ? null : prevNextPageUrl(
+ req, true),
+ next_page_url: (req.query.page == lastPage) ? null : prevNextPageUrl(
+ req, false)
+ }
+ })
+ }
+
+
+ /**
+ * modelAttributes - Return info about each column in the model's table
+ *
+ * @param {Object} model Sequelize model from which the info will be given.
+ * @return {Array} Array of objects, each object contains info for each attribute in the model
+ */
+ modelAttributes = function(model) {
+ return model.sequelize.query(
+ "SELECT column_name, data_type, is_nullable, column_default " +
+ "FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '" +
+ model.tableName + "'", {
+ type: model.sequelize.QueryTypes.SELECT
+ }
+ )
+ }
+
+ //attributes to discard
+ discardModelAttributes = ['createdAt', 'updatedAt']
+
+
+ /**
+ * filterModelAttributesForCsv - Filter attributes from a given model
+f *
+ * @param {Object} model Sequelize model from which the attributes will be filtered
+ * @param {Array} discardAttrs Array of attributes to discard
+ * @return {Array} Filtered attributes
+ */
+ filterModelAttributesForCsv = function(model, discardAttrs) {
+ discardAttrs = discardAttrs || discardModelAttributes
+ modelPrimaryKey = model.primaryKeyField
+ if (modelPrimaryKey)
+ discardAttrs = discardAttrs.concat([modelPrimaryKey])
+ return modelAttributes(model).then(function(x) {
+ return x.filter(function(i) {
+ return discardAttrs.indexOf(i.column_name) < 0
+ })
+ })
+ }
+
+
+ /**
+ * csvTableTemplate - Returns template of model, i.e. header of each column an its type
+ *
+ * @param {Object} model Sequelize model from which the template will be returned.
+ * @param {Array} discardAttrs Attributes to discard from the template
+ * @return {Array} Array of strings, one for header and one for the attribute't type.
+ */
+ module.exports.csvTableTemplate = function(model, discardAttrs) {
+ return filterModelAttributesForCsv(model,
+ discardAttrs).then(function(x) {
+ csvHeader = []
+ csvExmplRow = []
+ x.forEach(function(i) {
+ csvStr = i.data_type
+ if (i.is_nullable.toLowerCase() === 'false' || i.is_nullable.toLowerCase() ===
+ 'no' || i.is_nullable === 0)
+ csvStr += ",required"
+ if (i.column_default)
+ csvStr += ",default:" + i.column_default
+ csvHeader = csvHeader.concat([i.column_name])
+ csvExmplRow = csvExmplRow.concat([csvStr])
+ })
+ return [csvHeader.join(','), csvExmplRow.join(',')]
+ })
+ }
diff --git a/utils/helpers-acl.js b/utils/helpers-acl.js
new file mode 100644
index 0000000..fa930a9
--- /dev/null
+++ b/utils/helpers-acl.js
@@ -0,0 +1,22 @@
+const secret = 'something-secret';
+const jwt = require('jsonwebtoken');
+
+//TODO: Use this routines through all the code to have them in one place
+
+module.exports = {
+ getTokenFromContext: function (context) {
+ let token_bearer = context.request.headers["authorization"];
+ let token = token_bearer.replace("Bearer ","");
+ let decoded = jwt.verify(token, secret);
+ return decoded;
+ },
+
+ //INFO: can be useful for tests
+ /*generateDummyToken: function (email) {
+ return jsonwebtoken.sign({
+ id: 1,
+ email: email,
+ roles: "admin"
+ }, 'something-secret', { expiresIn: '1h' });
+ }*/
+};
\ No newline at end of file
diff --git a/utils/join-models.js b/utils/join-models.js
new file mode 100644
index 0000000..8c2cd8d
--- /dev/null
+++ b/utils/join-models.js
@@ -0,0 +1,599 @@
+
+// TODO: JOIN: do not print anything if there is no data found, print that there is no data found
+
+
+//TODO: Web service generates a class object that is incompatible with
+//TODO: sequelize object. All types of models shell have the same interface to
+//TODO: proceed with generic JOIN. That's clearly a new issue.
+
+
+const _ = require('lodash');
+const path = require('path');
+const models = require(path.join(__dirname, '..', 'models_index.js'));
+const resolvers = require(path.join(__dirname, '..', 'resolvers', 'index.js'));
+const inflection = require('inflection');
+const checkAuthorization = require('./check-authorization');
+const schema = require('./graphql_schema');
+let LinkedList = require('linked-list');
+
+
+
+/**
+ *
+ * INPUT FORMAT DESCRIPTION
+ *
+ * The "modelAdjacencies" input parameter is an ordered array of JSON objects that describe a JOIN chain.
+ * Below goes an example of the currently supported parameter set:
+ *
+ * {
+ *
+ * outputFormat = CSV // (REQUIRED) For the moment can be TEST, CSV or JSON only, see
+ * // the corresponding module exports
+ *
+ *
+ * modelAdjacencies = // (REQUIRED)
+ * [
+ * {
+ * "name" : "individual", // Name of the model as it appears in the corresponding index.js
+ *
+ *
+ * "association_name" : "transcript_counts",
+ * // (REQUIRED) There can be more than one association between two models,
+ * // the way to differ between these associations is an "as_name"
+ * // used by sequelize. This name is used by codegen to create resolvers and
+ * // is used here to find them.
+ *
+ *
+ *
+ * "attributes" : [ // (OPTIONAL - not implemented) The resolvers does not give possibility
+ * // to filter out unnecessary columns of the table. However, is is easy
+ * // to implement this functionality inside a "constructRow" function. This way
+ * // it can be possible to create different cut-offs of the database at the
+ * // presentation level and resolve the data analysis problem at a low cost.
+ * "name",
+ * "createdAt"
+ * ],
+ *
+ * <, "search" : {...}, "order" : {...}>// (OPTIONAL) Can be specified to filter records at the head of the
+ * // JOIN chain.
+ *
+ *
+ * },
+ * {
+ * "name" : "transcript_count",
+ * // The last element of the association chain does not require an "assoc"
+ * // structure, it has no sense here and will be ignored if present.
+ *
+ * "search" : { // (OPTIONAL) In the case when as_type of the previous element is "hasMany" or
+ * // "belongsToMany", there can be more than one "transcript_count" record
+ * "field" : "name", // associated with the same "individual".
+ * "value" : { // The "transcript_count" records can be filtered and ordered
+ * "value" : "%A%" // correspondingly to these "search" and "order" parameters. In the case of
+ * }, // "hasOne" or "belongsToOne" as_type of the "individual", the "search" and
+ * "operator" : "like" // "order" parameters will be ignored.
+ * },
+ *
+ * order: [{field: name, order: DESC}] // (OPTIONAL) Ordering of the associated "transcript_count" records.
+ * }
+ * ]
+ * }
+ *
+ * PERMISSIONS
+ *
+ * The user role should have a "batch_download" permission for all models defined in the
+ * "modelAdjacencies" input parameter.
+ *
+ */
+
+class JoinModels {
+
+ /**
+ * Internal class parameters
+ */
+ constructor(context){
+
+ // a linked list to be initialized from the input adjacency array
+ // this list will always keep the current model and the next model that is more useful than
+ // a plain array
+ this.list = new LinkedList;
+
+ // request context
+ this.context = context;
+ }
+
+ /**
+ * joinModels - is a function called by express server directly. This function execution can take
+ * a long time so it should not be blocking and should not produce long call stack chains
+
+ * @param modelAdjacencies a number of parameters in JSON format that define a JOIN request (see below)
+ * @param httpWritableStream a writable http stream
+ * @returns {Promise} generally a void function
+ */
+
+ async run(params, httpWritableStream) {
+
+ let modelAdjacencies = params.modelAdjacencies;
+ if ( ! modelAdjacencies || modelAdjacencies.length === 0)
+ throw Error(`modelAdjacencies array is undefined`);
+
+ // a linked list to be initialized from the input adjacency array
+ // this list will always keep the current model and the next model that is more useful than
+ // a plain array
+ //let list = new LinkedList;
+
+ for(let model_adj of modelAdjacencies) {
+ let item = new LinkedList.Item();
+ item.model_adj = model_adj;
+ this.list.append(item);
+ }
+
+ // iterate over the list and add some useful information to it's elements
+ let cur = this.list.head;
+ do{
+ // check for permission to make batch_download for all models listed
+ if( ! await checkAuthorization(this.context, cur.model_adj.name, 'batch_download'))
+ return new Error(`You don't have authorization to perform batch download for the ${cur.model_adj.name} model`);
+
+ // required on this step input data validation
+ if( ! cur.model_adj.name ) throw Error(`Model name is not defined in ${JSON.stringify(cur.model_adj)}`);
+ if( ! models[cur.model_adj.name] ) throw Error(`Model with name ${cur.model_adj.name} not exist`);
+
+ // store raw names of the model attributes if not given at input
+ let all_attributes = schema.getModelFieldByAnnotation(cur.model_adj.name,'@original-field');
+ if( ! cur.model_adj.attributes ) {
+ cur.model_adj.attributes = all_attributes;
+ }else{
+ // check that specified attributes are correct
+ for (let attribute_name of cur.model_adj.attributes)
+ if( ! all_attributes.includes(attribute_name))
+ throw Error(`Requested attribute '${attribute_name}' is not defined in the model '${cur.model_adj.name}'`);
+ }
+
+ // current data (is initialized by the 'func_find' call)
+ cur.model_adj.data = null;
+
+ // in the case when SELECT query that is formed by func_find method can return
+ // a collection of database records, those can be filtered and ordered accordingly
+ // to "find_params" structure. This structure also includes "offset" initialized to 0
+ cur.model_adj.search_params = this.defineSearchParams(cur);
+
+ // function that searches by criteria and offset instances of the given model_adj
+ cur.model_adj.func_find = this.defineFindFunction(cur);
+
+ }while(null !== (cur = cur.next));
+
+
+ // http send stream header
+ let timestamp = new Date().getTime();
+ httpWritableStream.writeHead(200, {'Content-Type': 'application/force-download',
+ 'Content-disposition': `attachment; filename = ${timestamp}.json`});
+
+
+ /*
+ This is the main introspection loop. On each iteration the user would receive
+ a new data raw. Accordingly to implementation of the constructRow function there
+ is a possibility to generate different output formats, hide unnecessary columns, etc.
+ This functionality is out of the scope of the current class and the constructRow
+ implementation has to be overloaded to output real data. See the child classes to get
+ more information.
+ */
+ while(true){
+
+ // entering into the iterations from the head element
+ cur = this.list.head;
+
+ try {
+
+ while(true){
+ let rollback = false;
+
+ // query the database (see defineFindFunction for details)
+ cur = await cur.model_adj.func_find(cur, this.context);
+
+ // no data found for the cur element of association chain => print, rollback or exit
+ if(cur.model_adj.data === null){
+
+ // cur element was visited for the first time: augment offset and print the line
+ if(cur.model_adj.search_params.pagination.offset === 0){
+ cur = this.augmentOffsetFlushTrailing(cur);
+ break;
+
+ // cur element was visited before and has no data
+ }else{
+
+ // head has no more data, terminate
+ if(cur.prev === null){
+ return;
+
+ // cur has no data and was already printed
+ // goto prev, augment it's offset and try again
+ }else{
+ cur = cur.prev;
+ cur = this.augmentOffsetFlushTrailing(cur);
+ rollback = true;
+ }
+ }
+ }
+
+ // the last element was reached and it has data != null
+ if(cur.next === null){
+ cur = this.augmentOffsetFlushTrailing(cur);
+ break;
+ }
+
+ // if it's not a rollback run - explore the next element
+ if(!rollback)
+ cur = cur.next;
+ }
+
+ /*
+ Send joined data raw to the end-user accordingly to the constructRow implementation.
+
+ It should be stressed, that after the first element with data == null, all subsequent elements
+ have no valid data. Also, as long as offsets are used to check if a given element was
+ already visited (printed) or not, the offsets should not be modified within constructRow function,
+ and it's interpretation is not direct.
+ */
+
+ let row_string = this.constructRow(this.list.head);
+ await httpWritableStream.write(row_string);
+
+ }catch(err){
+ /*
+ We can't throw an error to Express server at this stage because the response Content-Type
+ was already sent. So we can try to attach it to the end of file.
+ */
+ console.log(err);
+ await httpWritableStream.write(`{error : ${err.message}}\n`);
+ return;
+ }
+ }
+ };
+
+
+ /**
+ * defineFindFunction - Function use offset to retrieve corresponding data for the current list element according
+ * to the current offset. This function will renew the cur.model_adj.data and augment
+ * the cur.model_adj.offset field. If there is no data for the current offset, the
+ * cur.model_adj.data will be set to null.
+ *
+ * It is assumed, that cur->prev element has already initialized it's data field. If cur->prev is null, it means that
+ * we are working with the list head. If after calling this function, the cur.model_adj.data is null,
+ * it means that there is nothing mode to do, and the JOIN process has successfully completed.
+ *
+ * @param {object} cur is a current element of the linked-list
+ * @return {function} returns a function used inside generic algorithm
+ */
+
+ defineFindFunction(cur){
+
+
+ if(cur.prev === null){
+
+ // cur is the head element of the list
+ return async function(cur, context){
+
+ // for head getter function has to be estimated just once
+ if( ! cur.model_adj.func_getter)
+ cur.model_adj.func_getter = resolvers[inflection.pluralize(cur.model_adj.name)];
+
+ // get record from database for the given offset
+ // an output is an array that have one or zero elements
+ cur.model_adj.data = await cur.model_adj.func_getter(cur.model_adj.search_params, context);
+
+ if( cur.model_adj.data.length === 0 ) {
+ cur.model_adj.data = null;
+ }else{
+ cur.model_adj.data = cur.model_adj.data[0];
+ }
+
+ return cur;
+ }
+
+ } else {
+
+ /*
+ Here an explicit check is applied to detect for the association getter function in the cur.prev data model.
+ At the same time this is a validator (see the "else" option).
+ */
+
+ let model_prev = models[cur.prev.model_adj.name];
+
+ const as_name = cur.prev.model_adj.association_name;
+ if( ! as_name ) throw Error('"assoc" structure is required, see the docs');
+
+ //<%- nameLc -%>.prototype.<%=associations_one[i].name%>
+ let func_toOneGetter = model_prev.prototype[as_name];
+
+ //<%- nameLc -%>.prototype.<%=associations_temp[i].name%>Filter
+ let func_toManyGetter = model_prev.prototype[`${as_name}Filter`];
+
+ if(typeof func_toOneGetter === "function"){
+
+ // there is just one cur element can be found from the cur.prev that
+ // corresponds to the hasOne or belongsTo of the prev->cur association type
+ return async function(cur, context){
+ const as_name = cur.prev.model_adj.association_name;
+
+ if(cur.model_adj.search_params.pagination.offset > 0){
+ cur.model_adj.data = null;
+ }else{
+ cur.model_adj.data = await cur.prev.model_adj.data[as_name]({},context);
+ if(!cur.model_adj.data)
+ cur.model_adj.data = null;
+ }
+
+ return cur;
+ }
+ } else if(typeof func_toManyGetter === "function"){
+
+ return async function(cur, context){
+
+ // get record from database for the current offset (it comes inside cur.model_adj.search_params data structure)
+ // an output is an array that would have one (because limit is always 1) or zero elements (if nothing was found)
+ cur.model_adj.data = await cur.prev.model_adj.data[`${inflection.pluralize(cur.model_adj.name)}Filter`](cur.model_adj.search_params, context);
+
+ // set data to null explicitly or remove an array wrapper (anyway there is just one element)
+ if( cur.model_adj.data.length === 0 ){
+ cur.model_adj.data = null;
+ }else{
+ cur.model_adj.data = cur.model_adj.data[0];
+ }
+
+ return cur;
+ }
+ } else{
+ /*
+ If you get this error, it means that there is no explicit link between cur.prev and cur elements.
+ For example, assume that model A belongsTo model B. However, the madel B does not have a corresponding
+ hasMany or hasOne association with A. If you try to make a JOIN in the order B -> A, you will get
+ this "No association" exception. However, if you JOIN these models in the order A -> B, the corresponding
+ association resolver will be found.
+
+ Currently all associations are inverse, so this error should never happen.
+ */
+
+ throw Error(`No association from ${cur.prev.model_adj.name} to ${cur.model_adj.name} was detected,` +
+ `please check for ${as_name} or ${as_name}Filter functions`);
+ }
+
+ }
+
+ };
+
+
+
+
+
+ /**
+ * defineSearchParams - a helper function fills up a serach_params data structure. It's 'search' and 'order'
+ * elements would never change during the given transmission session. However the pagination
+ * parameter is important. The limit shell always be 1, and the offset is internal parameter of
+ * the current algorithm. It is prohibited to alter offset values from the outside world.
+ *
+ * @param {object} cur is a current element of the linked-list
+ * @return {object} returns a structure of search params
+ */
+ defineSearchParams(cur){
+ let search_params = {};
+
+ search_params.pagination = {
+ offset : 0,
+ limit : 1
+ };
+
+ if( cur.model_adj.search )
+ search_params.search = cur.model_adj.search;
+
+ if( cur.model_adj.order )
+ search_params.order = cur.model_adj.order;
+
+ return search_params;
+ };
+
+
+
+
+ /**
+ * augmentOffsetFlushTrailing - Helper function is used to augment offset of the "cur" element.
+ * In this case offsets and data of the all trailing elements became invalid
+ * and shell be flushed.
+ *
+ * @param {object} cur is a current element of the linked-list
+ * @return {object} returns a cur element with offset augmented that points on to the cleaned up tail
+ */
+ augmentOffsetFlushTrailing(cur){
+ cur.model_adj.search_params.pagination.offset++;
+ let next = cur.next;
+ while(next !== null){
+ next.model_adj.search_params.pagination.offset = 0;
+ next.model_adj.data = null;
+ next = next.next;
+ }
+ return cur;
+ };
+
+
+ /**
+ * constructRow - basic implementation of the constructRow function that prints model names and id's
+ *of the found elements. It is useful for testing purposes.
+ *...
+ *individual[id:458] ->transcript_count[id:6]
+ *individual[id:459] ->transcript_count[id:2]
+ *individual[id:460]
+ *individual[id:461]
+ *
+ * @param {object} head head of the linked list
+ * @return {string} joined line
+ */
+ constructRow(head){
+ let str = "";
+
+ let cur = head;
+ do{
+ str = str.concat(`${cur.model_adj.name}[`);
+ str = str.concat(`id:${cur.model_adj.data.id}] `);
+
+ if(cur.next !== null && cur.next.model_adj.data === null)
+ break;
+
+ if(cur.next !== null)
+ str = str.concat("->");
+
+ }while(null !== (cur = cur.next));
+
+ str = str.concat("\n");
+
+ return str;
+ }
+
+
+};
+
+
+
+// *********************************************************************************************************************
+
+
+
+class JoinModelsJSON extends JoinModels{
+
+ /**
+ * Internal class parameters
+ */
+ constructor(context){
+ super(context);
+ }
+
+ /**
+ * constructRow - create text string for the joined line in JSON format
+ *
+ * Example output for filtered columns:
+ * ...
+ * {"transcript_count.id":8,"individual.id":463}
+ * {"transcript_count.id":9,"individual.id":null}
+ * {"transcript_count.id":10,"individual.id":462}
+ * ...
+ *
+ * @param {object} head head of the linked list
+ * @return {string} a text string that will be sent to the service client
+ */
+
+ constructRow(head){
+
+ let cur = head;
+ let joined_data = {};
+ do{
+
+ let data = {};
+
+ if(cur.model_adj.data === null){
+ for (let attr of cur.model_adj.attributes) {
+ data[cur.model_adj.name + "." + attr] = null;
+ }
+ }else{
+ data = _.pick(cur.model_adj.data, cur.model_adj.attributes);
+
+ for(let old_key of Object.keys(data)){
+ let new_key = cur.model_adj.name + "." + old_key;
+ Object.defineProperty(data, new_key,
+ Object.getOwnPropertyDescriptor(data, old_key));
+ delete data[old_key];
+ }
+ }
+
+ Object.assign(joined_data, data);
+
+ }while(null !== (cur = cur.next));
+
+ return JSON.stringify(joined_data) + "\n";
+ };
+};
+
+
+class JoinModelsCSV extends JoinModels{
+ /**
+ * Internal class parameters
+ */
+ constructor(context){
+ super(context);
+
+ // in CSV format first line is the title
+ this.csv_header = true;
+ }
+
+
+ /**
+ * constructRow - create text string for the joined line in CSV format
+ *
+ * Example output for filtered columns:
+ *
+ * transcript_count.id,individual.id
+ * 8,463
+ * 9,null
+ * 10,462
+ * ...
+ *
+ * @param {object} head head of the linked list
+ * @return {string} a text string that will be sent to the service client
+ */
+ constructRow(head){
+
+ let cur = head;
+
+ let str = "";
+ let header = "";
+
+ do{
+
+ for (let attr of cur.model_adj.attributes){
+
+ if(this.csv_header)
+ header += cur.model_adj.name + "." + attr + ",";
+
+ if(cur.model_adj.data === null || cur.model_adj.data[attr] === null){
+ str += `"NULL",`;
+ }else{
+ str += `"${cur.model_adj.data[attr]}"` + ",";
+ }
+ }
+
+ }while(null !== (cur = cur.next));
+
+ if(this.csv_header){
+ this.csv_header = false;
+ header = header.replace(/.$/,"\n");
+ header += str;
+ str = header;
+ }
+
+ str = str.replace(/.$/,"\n");
+
+ return str;
+ };
+}
+
+
+
+module.exports.JoinModels = JoinModels;
+module.exports.JoinModelsJSON = JoinModelsJSON;
+module.exports.JoinModelsCSV = JoinModelsCSV;
+
+/**********************************************************
+
+CURL tests (copy-paste to console):
+
+curl -d '{"outputFormat" : "JSON", "modelAdjacencies" : [ { "name" : "individual", "association_name" : "transcript_counts" }, { "name" : "transcript_count"} ]}' -H "Content-Type: application/json" http://localhost:3000/join
+
+curl -d '{"outputFormat" : "JSON", "modelAdjacencies" : [ { "name" : "transcript_count", "association_name" : "individual" }, { "name" : "individual"} ]}' -H "Content-Type: application/json" http://localhost:3000/join
+
+curl -d '{"outputFormat" : "JSON", "modelAdjacencies" : [ { "name" : "transcript_count", "association_name" : "individual", "attributes" : ["id"] }, { "name" : "individual", "attributes" : ["id"]} ]}' -H "Content-Type: application/json" http://localhost:3000/join
+
+
+curl -d '{"outputFormat" : "JSON", "modelAdjacencies" : [ { "name" : "individual", "attributes" : ["id", "name"], "search" : { "field" : "name", "value" : {"value" : "A"}, "operator" : "like" }, "order" : [{"field" : "name", "order" : "ASC" }] } ]}' -H "Content-Type: application/json" http://localhost:3000/join
+
+curl -d '{"outputFormat" : "JSON", "modelAdjacencies" : [ { "name" : "transcript_count", "association_name" : "aminoacidsequence", "attributes" : [ "id", "gene"] } , {"name": "aminoacidsequence"} ]}' -H "Content-Type: application/json" http://localhost:3000/join
+
+curl -d '{"outputFormat" : "JSON", "modelAdjacencies" : [ { "name": "aminoacidsequence", "attributes" : [ "id"], "association_name" : "transcript_counts"}, { "name" : "transcript_count" } ]}' -H "Content-Type: application/json" http://localhost:3000/join
+
+*/
diff --git a/utils/login.js b/utils/login.js
new file mode 100644
index 0000000..7b2d6d4
--- /dev/null
+++ b/utils/login.js
@@ -0,0 +1,39 @@
+const bcrypt = require('bcrypt')
+const jsonwebtoken = require('jsonwebtoken')
+const path = require('path')
+const user = require(path.join(__dirname, '..', 'models_index.js')).user
+
+module.exports = {
+
+ /**
+ * login - Search for email in users table and returns a webtoken if the password is valid.
+ *
+ * @param {String} email User's email
+ * @param {String} password User's password
+ * @return {String} Webtoken with user's data encoded
+ */
+ login: async function({ email, password }) {
+
+ const user_data = await user.findOne({ where: { email } })
+ console.log(user_data);
+ if (!user_data) {
+ throw new Error('No user with that email')
+ }
+
+ const valid = (password==user_data.password); //await bcrypt.compare(password, user_data.password)
+
+ if (!valid) {
+ throw new Error('Incorrect password')
+ }
+ const roles = await user_data.getRoles();
+ const name_roles = roles.map( x =>{ return x.name })
+ console.log("ROLES: ", name_roles);
+ // return json web token
+ return jsonwebtoken.sign({
+ id: user_data.id,
+ email: user_data.email,
+ roles: name_roles
+ }, 'something-secret', { expiresIn: '1h' })
+ }
+
+}
diff --git a/utils/merge-schemas.js b/utils/merge-schemas.js
index c0ccf7b..dc2092b 100644
--- a/utils/merge-schemas.js
+++ b/utils/merge-schemas.js
@@ -1,6 +1,13 @@
var { fileLoader, mergeTypes } = require('merge-graphql-schemas');
+
+/**
+ * @function - Merge graphql schemas stored in a same directory
+ *
+ * @param {string} schemas_folder path to directory where all graphql schemas are stored.
+ * @return {string} Merged graphql schema.
+ */
module.exports = function( schemas_folder ) {
const typesArray = fileLoader( schemas_folder);
let merged = mergeTypes(typesArray);
diff --git a/utils/search-argument.js b/utils/search-argument.js
index bb69e50..d52ab95 100644
--- a/utils/search-argument.js
+++ b/utils/search-argument.js
@@ -1,16 +1,33 @@
-/*
- Class to parse search argument for any model
-*/
-module.exports = class searchArg{
+/**
+ * search Class to parse search argument for any model and translate it so sequelize model will accept it
+ */
+module.exports = class search{
- constructor({field, value, operator, searchArg}){
+
+ /**
+ * constructor - Creates an instace with the given arguments
+ *
+ * @param {string} field field to filter.
+ * @param {object} value value contains type(i.e. array, string) and actual value to match in the filter.
+ * @param {string} operator operator used to perform the filter.
+ * @param {object} search recursive search instance.
+ * @return {object} instace of search class.
+ */
+ constructor({field, value, operator, search}){
this.field = field;
this.value = this.constructor.parseValue(value);
this.operator = operator;
- this.searchArg = searchArg
+ this.search = search
}
+
+ /**
+ * @static parseValue - Creates the proper type(either array or string) of the value that user wants to filter.
+ *
+ * @param {object} val value object to parse.
+ * @return {(array|string|number)} Parsed value
+ */
static parseValue(val){
if(val!==undefined)
{
@@ -23,32 +40,38 @@ module.exports = class searchArg{
}
}
+
+ /**
+ * toSequelize - Convert recursive search instance to search object that sequelize will accept as input.
+ *
+ * @return {object} Translated search instance into sequelize object format.
+ */
toSequelize(){
- let searchArgsInSequelize = {};
+ let searchsInSequelize = {};
- if(this.searchArg === undefined && this.field === undefined)
+ if(this.search === undefined && this.field === undefined)
{
- searchArgsInSequelize['$'+this.operator] = this.value;
+ searchsInSequelize['$'+this.operator] = this.value;
- }else if(this.searchArg === undefined)
+ }else if(this.search === undefined)
{
- searchArgsInSequelize[this.field] = {
+ searchsInSequelize[this.field] = {
['$'+this.operator] : this.value
};
}else if(this.field === undefined){
- searchArgsInSequelize['$'+this.operator] = this.searchArg.map(sa => {
- let new_sa = new searchArg(sa);
+ searchsInSequelize['$'+this.operator] = this.search.map(sa => {
+ let new_sa = new search(sa);
return new_sa.toSequelize();
});
}else{
- searchArgsInSequelize[this.field] = {
- ['$'+this.operator] : this.searchArg.map(sa => {
- let new_sa = new searchArg(sa);
+ searchsInSequelize[this.field] = {
+ ['$'+this.operator] : this.search.map(sa => {
+ let new_sa = new search(sa);
return new_sa.toSequelize();
})
}
}
- return searchArgsInSequelize;
+ return searchsInSequelize;
}
};
diff --git a/utils/simple-export.js b/utils/simple-export.js
new file mode 100644
index 0000000..ce575e7
--- /dev/null
+++ b/utils/simple-export.js
@@ -0,0 +1,95 @@
+const path = require('path');
+const resolvers = require(path.join(__dirname, '..', 'resolvers', 'index.js'));
+const inflection = require('inflection');
+const schema = require('./graphql_schema');
+
+getAttributes = function( model_name ){
+ return schema.getModelFieldByAnnotation(model_name, '@original-field');
+}
+
+crateHeaderCSV = function(attributes){
+ let str_header = "";
+ attributes.forEach( att =>{
+ str_header+= att+",";
+ } )
+ str_header= str_header.replace(/.$/,"\n");
+
+ return str_header;
+}
+
+asyncForEach = async function(array, callback) {
+ for (let index = 0; index < array.length; index++) {
+ await callback(array[index], index, array);
+ }
+}
+
+jsonToCSV = function(row_data, attributes){
+ let str_csv = "";
+ attributes.forEach( att => {
+ if(row_data[att]===null || row_data[att] === undefined){
+ str_csv+='NULL.';
+ }else {
+ str_csv+= row_data[att]+",";
+ }
+ })
+
+ str_csv= str_csv.replace(/.$/,"\n");
+ return str_csv;
+}
+
+// wait ms milliseconds
+function wait(ms) {
+ return new Promise(r => setTimeout(r, ms));
+}
+
+
+module.exports = async function(context, body_info, writableStream ){
+ //get resolver name for model
+ let model_name = body_info.model;
+ let getter_resolver = inflection.pluralize(model_name.slice(0,1).toLowerCase() + model_name.slice(1, model_name.length));
+
+ //get count resolver
+ let count_resolver = 'count'+inflection.pluralize(model_name.slice(0,1).toUpperCase() + model_name.slice(1, model_name.length));
+ let total_records = await resolvers[count_resolver]({}, context);
+ console.log("TOTAL NUMBER OF RECORDS TO STREAM: ", total_records);
+
+ //pagination
+ let batch_step = {
+ limit: 1,
+ offset: 0
+ }
+
+ // http send stream header
+ let timestamp = new Date().getTime();
+ writableStream.writeHead(200, {'Content-Type': 'application/force-download',
+ 'Content-disposition': `attachment; filename = ${timestamp}.csv`});
+
+ //get attributes names
+ let attributes = getAttributes(model_name);
+
+ //write csv header
+ let csv_header = crateHeaderCSV(attributes);
+ await writableStream.write(csv_header);
+
+ while(batch_step.offset < total_records){
+
+ try{
+ data = await resolvers[getter_resolver]({pagination: batch_step},context);
+
+ await asyncForEach(data, async (record) =>{
+ let row = jsonToCSV(record.dataValues, attributes);
+ await writableStream.write(row);
+ })
+ batch_step.offset = batch_step.offset + batch_step.limit;
+ }catch(err){
+ /*
+ We can't throw an error to Express server at this stage because the response Content-Type
+ was already sent. So we can try to attach it to the end of file.
+ */
+ console.log(err);
+ await writableStream.write(`{error : ${err.message}}\n`);
+ return;
+ }
+
+ }
+}
diff --git a/utils/testSequelizeDbServerAvailable.js b/utils/testSequelizeDbServerAvailable.js
new file mode 100644
index 0000000..6ae5e63
--- /dev/null
+++ b/utils/testSequelizeDbServerAvailable.js
@@ -0,0 +1,15 @@
+#!/usr/bin/env node
+
+const path = require('path')
+const Sequelize = require(path.join(__dirname, '..', 'connection.js'))
+
+async function checkConnection() {
+ try {
+ await Sequelize.authenticate()
+ return process.exit(0)
+ } catch (exception) {
+ return process.exit(1)
+ }
+}
+
+checkConnection()
diff --git a/utils/validatorUtil.js b/utils/validatorUtil.js
new file mode 100644
index 0000000..7e8b19e
--- /dev/null
+++ b/utils/validatorUtil.js
@@ -0,0 +1,110 @@
+const gd = require('graphql-iso-date')
+const Ajv = require('ajv')
+
+/**
+ * ifHasValidatorFunctionInvoke - checks if data model has a validator function with
+ * the given name, and apply that function
+ *
+ * @param {string} validatorFunction Name of the validator function
+ * @param {object} dataModel The empty data model object
+ * @param {object} data JSON data to be inserted into the dataModel
+ * @return {Promise} The result of invoking the respective validator, or
+ * undefined if no validator was found to be registered
+ *
+ */
+
+module.exports.ifHasValidatorFunctionInvoke = async function( validatorFunction, dataModel, data) {
+ if (typeof dataModel.prototype[validatorFunction] === "function") {
+ try{
+ return await dataModel.prototype[validatorFunction](data);
+ }catch( err) {
+ throw err;
+ }
+ }
+};
+
+/**
+ * Adds AJV asynchronous keywords to the argument AJV instance that define ISO
+ * Date, ISO Time, and ISO DateTime strings or the respective GraphQL instances
+ * (see package 'graphql-iso-date'). Use in a schema as in the following
+ * example: let schema = { '$async': true, properties: { startDate: { isoDate:
+ * true } } }
+ *
+ * @param {object} ajv - An instance of AJV (see package 'ajv' for details.
+ *
+ * @return {object} the modified instance of ajv. As Javascript uses references
+ * this return value can be ignored, as long as the original argument is kept
+ * and used.
+ */
+module.exports.addDateTimeAjvKeywords = function(ajv) {
+ ajv.addKeyword('isoDate', {
+ async: true,
+ compile: function(schema, parentSchema) {
+ return async function(data) {
+ try {
+ gd.GraphQLDate.serialize(data);
+ return true
+ } catch (e) {
+ return new Promise(function(resolve, reject) {
+ return reject(new Ajv.ValidationError([{
+ keyword: 'isoDate',
+ message: 'Must be a GraphQLDate instance or a ISO Date formatted string (e.g. "1900-12-31").',
+ params: {
+ 'keyword': 'isoDate'
+ }
+ }]))
+ })
+ }
+ }
+ },
+ errors: true
+ })
+
+ ajv.addKeyword('isoTime', {
+ async: true,
+ compile: function(schema, parentSchema) {
+ return async function(data) {
+ try {
+ gd.GraphQLTime.serialize(data);
+ return true
+ } catch (e) {
+ return new Promise(function(resolve, reject) {
+ return reject(new Ajv.ValidationError([{
+ keyword: 'isoTime',
+ message: 'Must be a GraphQLTime instance or a ISO Time formatted string (e.g. "13:56:45Z" or "13.56.45.1982Z").',
+ params: {
+ 'keyword': 'isoTime'
+ }
+ }]))
+ })
+ }
+ }
+ },
+ errors: true
+ })
+
+ ajv.addKeyword('isoDateTime', {
+ async: true,
+ compile: function(schema, parentSchema) {
+ return async function(data) {
+ try {
+ gd.GraphQLTime.serialize(data);
+ return true
+ } catch (e) {
+ return new Promise(function(resolve, reject) {
+ return reject(new Ajv.ValidationError([{
+ keyword: 'isoDateTime',
+ message: 'Must be a GraphQLDateTime instance or a ISO Date-Time formatted string (e.g. "1900-12-31T23:59:59Z" or "1900-12-31T23:59:59.1982Z").',
+ params: {
+ 'keyword': 'isoDateTime'
+ }
+ }]))
+ })
+ }
+ }
+ },
+ errors: true
+ })
+
+ return ajv
+}