Merge branch 'release/1.2.0' into production
This commit is contained in:
commit
a6c2299637
|
@ -1,2 +1,5 @@
|
||||||
node_modules
|
node_modules
|
||||||
coverage
|
coverage
|
||||||
|
*.tgz
|
||||||
|
*.xml
|
||||||
|
.loopbackrc
|
||||||
|
|
294
LICENSE
294
LICENSE
|
@ -1,4 +1,10 @@
|
||||||
Copyright (c) 2013 StrongLoop, Inc.
|
Copyright (c) 2013-2014 StrongLoop, Inc.
|
||||||
|
|
||||||
|
loopback-connector-mysql uses a 'dual license' model. Users may use
|
||||||
|
loopback-connector-mysql under the terms of the MIT license, or under the
|
||||||
|
StrongLoop License. The text of both is included below.
|
||||||
|
|
||||||
|
MIT license
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -17,3 +23,289 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
StrongLoop License
|
||||||
|
|
||||||
|
STRONGLOOP SUBSCRIPTION AGREEMENT
|
||||||
|
PLEASE READ THIS AGREEMENT CAREFULLY BEFORE YOU AGREE TO THESE TERMS. IF YOU
|
||||||
|
ARE ACTING ON BEHALF OF AN ENTITY, THEN YOU REPRESENT THAT YOU HAVE THE
|
||||||
|
AUTHORITY TO ENTER INTO THIS AGREEMENT ON BEHALF OF THAT ENTITY. IF YOU DO NOT
|
||||||
|
AGREE TO THESE TERMS, YOU SHOULD NOT AGREE TO THE TERMS OF THIS AGREEMENT OR
|
||||||
|
INSTALL OR USE THE SOFTWARE.
|
||||||
|
This StrongLoop Subscription Agreement ("Agreement") is made by and between
|
||||||
|
StrongLoop, Inc. ("StrongLoop") with its principal place of business at 107 S.
|
||||||
|
B St, Suite 220, San Mateo, CA 94401 and the person or entity entering into this
|
||||||
|
Agreement ("Customer"). The effective date ("Effective Date") of this Agreement
|
||||||
|
is the date Customer agrees to these terms or installs or uses the Software (as
|
||||||
|
defined below). This Agreement applies to Customer's use of the Software but it
|
||||||
|
shall be superseded by any signed agreement between you and StrongLoop
|
||||||
|
concerning the Software.
|
||||||
|
1. Subscriptions and Licenses.
|
||||||
|
1.1 Subscriptions. StrongLoop offers five different subscription levels to its
|
||||||
|
customers, each as more particularly described on StrongLoop's website located
|
||||||
|
at www.strongloop.com (the "StrongLoop Site"): (1) Free; (2) Developer; (3)
|
||||||
|
Professional; (4) Gold; and (5) Platinum. The actual subscription level
|
||||||
|
applicable to Customer (the "Subscription") will be specified in the purchase
|
||||||
|
order that Customer issues to StrongLoop. This Agreement applies to Customer
|
||||||
|
regardless of the level of the Subscription selected by Customer and whether or
|
||||||
|
not Customer upgrades or downgrades its Subscription. StrongLoop hereby agrees
|
||||||
|
to provide the services as described on the StrongLoop Site for each
|
||||||
|
Subscription level during the term for which Customer has purchased the
|
||||||
|
applicable Subscription, subject to Customer paying the fees applicable to the
|
||||||
|
Subscription level purchased, if any (the "Subscription Fees"). StrongLoop may
|
||||||
|
modify the services to be provided under any Subscription upon notice to
|
||||||
|
Customer.
|
||||||
|
1.2 License Grant. Subject to the terms and conditions of this Agreement,
|
||||||
|
StrongLoop grants to Customer, during the Subscription Term (as defined in
|
||||||
|
Section 7.1 (Term and Termination) of this Agreement, a limited, non-exclusive,
|
||||||
|
non-transferable right and license, to install and use the StrongLoop Suite
|
||||||
|
software (the "Software") and the documentation made available electronically as
|
||||||
|
part of the Software (the "Documentation"), either of which may be modified
|
||||||
|
during the Term (as defined in Section 7.1 below), solely for development,
|
||||||
|
production and commercial purposes so long as Customer is using the Software to
|
||||||
|
run only one process on a given operating system at a time. This Agreement,
|
||||||
|
including but not limited to the license and restrictions contained herein,
|
||||||
|
apply to Customer regardless of whether Customer accesses the Software via
|
||||||
|
download from the StrongLoop Site or through a third-party website or service,
|
||||||
|
even if Customer acquired the Software prior to agreeing to this Agreement.
|
||||||
|
1.3 License Restrictions. Customer shall not itself, or through any parent,
|
||||||
|
subsidiary, affiliate, agent or other third party:
|
||||||
|
1.3.1 sell, lease, license, distribute, sublicense or otherwise transfer
|
||||||
|
in whole or in part, any Software or the Documentation to a third party;
|
||||||
|
or
|
||||||
|
1.3.2 decompile, disassemble, translate, reverse engineer or otherwise
|
||||||
|
attempt to derive source code from the Software, in whole or in part, nor
|
||||||
|
shall Customer use any mechanical, electronic or other method to trace,
|
||||||
|
decompile, disassemble, or identify the source code of the Software or
|
||||||
|
encourage others to do so, except to the limited extent, if any, that
|
||||||
|
applicable law permits such acts notwithstanding any contractual
|
||||||
|
prohibitions, provided, however, before Customer exercises any rights that
|
||||||
|
Customer believes to be entitled to based on mandatory law, Customer shall
|
||||||
|
provide StrongLoop with thirty (30) days prior written notice and provide
|
||||||
|
all reasonably requested information to allow StrongLoop to assess
|
||||||
|
Customer's claim and, at StrongLoop's sole discretion, to provide
|
||||||
|
alternatives that reduce any adverse impact on StrongLoop's intellectual
|
||||||
|
property or other rights; or
|
||||||
|
1.3.3 allow access or permit use of the Software by any users other than
|
||||||
|
Customer's employees or authorized third-party contractors who are
|
||||||
|
providing services to Customer and agree in writing to abide by the terms
|
||||||
|
of this Agreement, provided further that Customer shall be liable for any
|
||||||
|
failure by such employees and third-party contractors to comply with the
|
||||||
|
terms of this Agreement and no usage restrictions, if any, shall be
|
||||||
|
exceeded; or
|
||||||
|
1.3.4 create, develop, license, install, use, or deploy any third party
|
||||||
|
software or services to circumvent or provide access, permissions or
|
||||||
|
rights which violate the license keys embedded within the Software; or
|
||||||
|
1.3.5 modify or create derivative works based upon the Software or
|
||||||
|
Documentation; or disclose the results of any benchmark test of the
|
||||||
|
Software to any third party without StrongLoop's prior written approval;
|
||||||
|
or
|
||||||
|
1.3.6 change any proprietary rights notices which appear in the Software
|
||||||
|
or Documentation; or
|
||||||
|
1.3.7 use the Software as part of a time sharing or service bureau
|
||||||
|
purposes or in any other resale capacity.
|
||||||
|
1.4 Third-Party Software. The Software may include individual certain software
|
||||||
|
that is owned by third parties, including individual open source software
|
||||||
|
components (the "Third-Party Software"), each of which has its own copyright and
|
||||||
|
its own applicable license conditions. Such third-party software is licensed to
|
||||||
|
Customer under the terms of the applicable third-party licenses and/or copyright
|
||||||
|
notices that can be found in the LICENSES file, the Documentation or other
|
||||||
|
materials accompanying the Software, except that Sections 5 (Warranty
|
||||||
|
Disclaimer) and 6 (Limitation of Liability) also govern Customer's use of the
|
||||||
|
third-party software. Customer agrees to comply with the terms and conditions
|
||||||
|
of the relevant third-party software licenses.
|
||||||
|
2. Support Services. StrongLoop has no obligation to provide any support for
|
||||||
|
the Software other than the support services specifically described on the
|
||||||
|
StrongLoop Site for the Subscription level procured by Customer. However,
|
||||||
|
StrongLoop has endeavored to establish a community of users of the Software who
|
||||||
|
have provided their own feedback, hints and advice regarding their experiences
|
||||||
|
in using the Software. You can find that community and user feedback on the
|
||||||
|
StrongLoop Site. The use of any information, content or other materials from,
|
||||||
|
contained in or on the StrongLoop Site are subject to the StrongLoop website
|
||||||
|
terms of use located here http://www.strongloop.com/terms-of-service.
|
||||||
|
3. Confidentiality. For purposes of this Agreement, "Confidential Information"
|
||||||
|
means any and all information or proprietary materials (in every form and media)
|
||||||
|
not generally known in the relevant trade or industry and which has been or is
|
||||||
|
hereafter disclosed or made available by StrongLoop to Customer in connection
|
||||||
|
with the transactions contemplated under this Agreement, including (i) all trade
|
||||||
|
secrets, (ii) existing or contemplated Software, services, designs, technology,
|
||||||
|
processes, technical data, engineering, techniques, methodologies and concepts
|
||||||
|
and any related information, and (iii) information relating to business plans,
|
||||||
|
sales or marketing methods and customer lists or requirements. For a period of
|
||||||
|
five (5) years from the date of disclosure of the applicable Confidential
|
||||||
|
Information, Customer shall (i) hold the Confidential Information in trust and
|
||||||
|
confidence and avoid the disclosure or release thereof to any other person or
|
||||||
|
entity by using the same degree of care as it uses to avoid unauthorized use,
|
||||||
|
disclosure, or dissemination of its own Confidential Information of a similar
|
||||||
|
nature, but not less than reasonable care, and (ii) not use the Confidential
|
||||||
|
Information for any purpose whatsoever except as expressly contemplated under
|
||||||
|
this Agreement; provided that, to the extent the Confidential Information
|
||||||
|
constitutes a trade secret under law, Customer agrees to protect such
|
||||||
|
information for so long as it qualifies as a trade secret under applicable law.
|
||||||
|
Customer shall disclose the Confidential Information only to those of its
|
||||||
|
employees and contractors having a need to know such Confidential Information
|
||||||
|
and shall take all reasonable precautions to ensure that such employees and
|
||||||
|
contractors comply with the provisions of this Section. The obligations of
|
||||||
|
Customer under this Section shall not apply to information that Customer can
|
||||||
|
demonstrate (i) was in its possession at the time of disclosure and without
|
||||||
|
restriction as to confidentiality, (ii) at the time of disclosure is generally
|
||||||
|
available to the public or after disclosure becomes generally available to the
|
||||||
|
public through no breach of agreement or other wrongful act by Customer, (iii)
|
||||||
|
has been received from a third party without restriction on disclosure and
|
||||||
|
without breach of agreement by Customer, or (iv) is independently developed by
|
||||||
|
Customer without regard to the Confidential Information. In addition, Customer
|
||||||
|
may disclose Confidential Information as required to comply with binding orders
|
||||||
|
of governmental entities that have jurisdiction over it; provided that Customer
|
||||||
|
gives StrongLoop reasonable written notice to allow StrongLoop to seek a
|
||||||
|
protective order or other appropriate remedy, discloses only such Confidential
|
||||||
|
Information as is required by the governmental entity, and uses commercially
|
||||||
|
reasonable efforts to obtain confidential treatment for any Confidential
|
||||||
|
Information disclosed. Notwithstanding the above, Customer agrees that
|
||||||
|
StrongLoop, its employees and agents shall be free to use and employ their
|
||||||
|
general skills, know-how, and expertise, and to use, disclose, and employ any
|
||||||
|
generalized ideas, concepts, know-how, methods, techniques or skills gained or
|
||||||
|
learned during the Term or thereafter.
|
||||||
|
4. Ownership. StrongLoop shall retain all intellectual property and proprietary
|
||||||
|
rights in the Software, Documentation, and related works, including but not
|
||||||
|
limited to any derivative work of the foregoing and StrongLoop's licensors shall
|
||||||
|
retain all intellectual property and proprietary rights in any Third-Party
|
||||||
|
Software that may be provided with or as a part of the Software. Customer shall
|
||||||
|
do nothing inconsistent with StrongLoop's or its licensors' title to the
|
||||||
|
Software and the intellectual property rights embodied therein, including, but
|
||||||
|
not limited to, transferring, loaning, selling, assigning, pledging, or
|
||||||
|
otherwise disposing, encumbering, or suffering a lien or encumbrance upon or
|
||||||
|
against any interest in the Software. The Software (including any Third-Party
|
||||||
|
Software) contain copyrighted material, trade secrets and other proprietary
|
||||||
|
material of StrongLoop and/or its licensors.
|
||||||
|
5. Warranty Disclaimer. THE SOFTWARE (INCLUDING ANY THIRD-PARTY SOFTWARE) AND
|
||||||
|
DOCUMENTATION MADE AVAILABLE TO CUSTOMER ARE PROVIDED "AS-IS" AND STRONGLOOP,
|
||||||
|
ON BEHALF OF ITSELF AND ITS LICENSORS, EXPRESSLY DISCLAIMS ALL WARRANTIES OF ANY
|
||||||
|
KIND, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, ANY IMPLIED WARRANTIES
|
||||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, TITLE,
|
||||||
|
PERFORMANCE, AND ACCURACY AND ANY IMPLIED WARRANTIES ARISING FROM STATUTE,
|
||||||
|
COURSE OF DEALING, COURSE OF PERFORMANCE, OR USAGE OF TRADE. STRONGLOOP DOES
|
||||||
|
NOT WARRANT THAT THE OPERATION OF THE SOFTWARE WILL BE UNINTERRUPTED OR
|
||||||
|
ERROR-FREE, THAT DEFECTS IN THE SOFTWARE WILL BE CORRECTED OR THAT THE SOFTWARE
|
||||||
|
WILL PROVIDE OR ENSURE ANY PARTICULAR RESULTS OR OUTCOME. NO ORAL OR WRITTEN
|
||||||
|
INFORMATION OR ADVICE GIVEN BY STRONGLOOP OR ITS AUTHORIZED REPRESENTATIVES
|
||||||
|
SHALL CREATE A WARRANTY OR IN ANY WAY INCREASE THE SCOPE OF THIS WARRANTY.
|
||||||
|
STRONGLOOP IS NOT OBLIGATED TO PROVIDE CUSTOMER WITH UPGRADES TO THE SOFTWARE,
|
||||||
|
BUT MAY ELECT TO DO SO IN ITS SOLE DISCRETION. SOME JURISDICTIONS DO NOT ALLOW
|
||||||
|
THE EXCLUSION OF IMPLIED WARRANTIES, SO THE ABOVE EXCLUSION MAY NOT APPLY TO
|
||||||
|
CUSTOMER.WITHOUT LIMITING THE GENERALITY OF THE FOREGOING DISCLAIMER, THE
|
||||||
|
SOFTWARE AND DOCUMENTATION ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE IN
|
||||||
|
THE PLANNING, CONSTRUCTION, MAINTENANCE, CONTROL, OR DIRECT OPERATION OF NUCLEAR
|
||||||
|
FACILITIES, AIRCRAFT NAVIGATION, CONTROL OR COMMUNICATION SYSTEMS, WEAPONS
|
||||||
|
SYSTEMS, OR DIRECT LIFE SUPPORT SYSTEMS.
|
||||||
|
6. Limitation of Liability.
|
||||||
|
6.1 Exclusion of Liability. IN NO EVENT WILL STRONGLOOP OR ITS LICENSORS
|
||||||
|
BE LIABLE UNDER THIS AGREEMENT FOR ANY INDIRECT, RELIANCE, PUNITIVE,
|
||||||
|
CONSEQUENTIAL, SPECIAL, EXEMPLARY, OR INCIDENTAL DAMAGES OF ANY KIND AND
|
||||||
|
HOWEVER CAUSED (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF
|
||||||
|
BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION AND
|
||||||
|
THE LIKE), EVEN IF STRONGLOOP HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||||
|
DAMAGES. CUSTOMER BEARS FULL RESPONSIBILITY FOR USE OF THE SOFTWARE AND
|
||||||
|
THE SUBSCRIPTION AND STRONGLOOP DOES NOT GUARANTEE THAT THE USE OF THE
|
||||||
|
SOFTWARE AND SUBSCRIPTION WILL ENSURE THAT CUSTOMER'S NETWORK WILL BE
|
||||||
|
AVAILABLE, SECURE, MONITORED OR PROTECTED AGAINST ANY DOWNTIME, DENIAL OF
|
||||||
|
SERVICE ATTACKS, SECUITY BREACHES, HACKERS AND THE LIKE. IN NO EVENT WILL
|
||||||
|
STRONGLOOP'S CUMULATIVE LIABILITY FOR ANY DAMAGES, LOSSES AND CAUSES OF
|
||||||
|
ACTION (WHETHER IN CONTRACT, TORT, INCLUDING NEGLIGENCE, OR OTHERWISE)
|
||||||
|
ARISING OUT OF OR RELATED TO THIS AGREEMENT EXCEED THE GREATER OF ONE
|
||||||
|
HUNDRED DOLLARS (US$100) OR THE TOTAL SUBSCRIPTION FEES PAID BY CUSTOMER
|
||||||
|
TO STRONGLOOP IN THE TWELVE (12) MONTHS PRECEDING THE DATE THE CLAIM
|
||||||
|
ARISES.
|
||||||
|
6.2 Limitation of Damages. IN NO EVENT WILL STRONGLOOP'S LICENSORS HAVE
|
||||||
|
ANY LIABILITY FOR ANY CLAIM ARISING IN CONNECTION WITH THIS AGREEMENT.
|
||||||
|
THE PROVISIONS OF THIS SECTION 6 ALLOCATE RISKS UNDER THIS AGREEMENT
|
||||||
|
BETWEEN CUSTOMER, STRONGLOOP AND STRONGLOOP'S SUPPLIERS. THE FOREGOING
|
||||||
|
LIMITATIONS, EXCLUSIONS AND DISCLAIMERS APPLY TO THE MAXIMUM EXTENT
|
||||||
|
PERMITTED BY APPLICABLE LAW, EVEN IF ANY REMEDY FAILS IN ITS ESSENTIAL
|
||||||
|
PURPOSE.
|
||||||
|
6.3 Failure of Essential Purpose. THE PARTIES AGREE THAT THESE
|
||||||
|
LIMITATIONS SHALL APPLY EVEN IF THIS AGREEMENT OR ANY LIMITED REMEDY
|
||||||
|
SPECIFIED HEREIN IS FOUND TO HAVE FAILED OF ITS ESSENTIAL PURPOSE.
|
||||||
|
6.4 Allocation of Risk. The sections on limitation of liability and
|
||||||
|
disclaimer of warranties allocate the risks in the Agreement between the
|
||||||
|
parties. This allocation is an essential element of the basis of the
|
||||||
|
bargain between the parties.
|
||||||
|
7. Term and Termination.
|
||||||
|
7.1 This Agreement shall commence on the Effective Date and continue for so long
|
||||||
|
as Customer has a valid Subscription and is current on the payment of any
|
||||||
|
Subscription Fees required to be paid for that Subscription (the "Subscription
|
||||||
|
Term"). Either party may terminate this Agreement immediately upon written
|
||||||
|
notice to the other party, and the Subscription and licenses granted hereunder
|
||||||
|
automatically terminate upon the termination of this Agreement. This Agreement
|
||||||
|
will terminate immediately without notice from StrongLoop if Customer fails to
|
||||||
|
comply with or otherwise breaches any provision of this Agreement.
|
||||||
|
7.2 All Sections other than Section 1.1 (Subscriptions) and 1.2 (Licenses) shall
|
||||||
|
survive the expiration or termination of this Agreement.
|
||||||
|
8. Subscription Fees and Payments. StrongLoop, Customer agrees to pay
|
||||||
|
StrongLoop the Subscription Fees as described on the StrongLoop Site for the
|
||||||
|
Subscription purchased unless a different amount has been agreed to in a
|
||||||
|
separate agreement between Customer and StrongLoop. In addition, Customer shall
|
||||||
|
pay all sales, use, value added, withholding, excise taxes and other tax, duty,
|
||||||
|
custom and similar fees levied upon the delivery or use of the Software and the
|
||||||
|
Subscriptions described in this Agreement. Fees shall be invoiced in full upon
|
||||||
|
StrongLoop's acceptance of Customer's purchase order for the Subscription. All
|
||||||
|
invoices shall be paid in US dollars and are due upon receipt and shall be paid
|
||||||
|
within thirty (30) days. Payments shall be made without right of set-off or
|
||||||
|
chargeback. If Customer does not pay the invoices when due, StrongLoop may
|
||||||
|
charge interest at one percent (1%) per month or the highest rate permitted by
|
||||||
|
law, whichever is lower, on the unpaid balance from the original due date. If
|
||||||
|
Customer fails to pay fees in accordance with this Section, StrongLoop may
|
||||||
|
suspend fulfilling its obligations under this Agreement (including but not
|
||||||
|
limited to suspending the services under the Subscription) until payment is
|
||||||
|
received by StrongLoop. If any applicable law requires Customer to withhold
|
||||||
|
amounts from any payments to StrongLoop under this Agreement, (a) Customer shall
|
||||||
|
effect such withholding, remit such amounts to the appropriate taxing
|
||||||
|
authorities and promptly furnish StrongLoop with tax receipts evidencing the
|
||||||
|
payments of such amounts and (b) the sum payable by Customer upon which the
|
||||||
|
deduction or withholding is based shall be increased to the extent necessary to
|
||||||
|
ensure that, after such deduction or withholding, StrongLoop receives and
|
||||||
|
retains, free from liability for such deduction or withholding, a net amount
|
||||||
|
equal to the amount StrongLoop would have received and retained absent the
|
||||||
|
required deduction or withholding.
|
||||||
|
9. General.
|
||||||
|
9.1 Compliance with Laws. Customer shall abide by all local, state, federal and
|
||||||
|
international laws, rules, regulations and orders applying to Customer's use of
|
||||||
|
the Software, including, without limitation, the laws and regulations of the
|
||||||
|
United States that may restrict the export and re-export of certain commodities
|
||||||
|
and technical data of United States origin, including the Software. Customer
|
||||||
|
agrees that it will not export or re-export the Software without the appropriate
|
||||||
|
United States or foreign government licenses.
|
||||||
|
9.2 Entire Agreement. This Agreement constitutes the entire agreement between
|
||||||
|
the parties concerning the subject matter hereof. This Agreement supersedes all
|
||||||
|
prior or contemporaneous discussions, proposals and agreements between the
|
||||||
|
parties relating to the subject matter hereof. No amendment, modification or
|
||||||
|
waiver of any provision of this Agreement shall be effective unless in writing
|
||||||
|
and signed by both parties. Any additional or different terms on any purchase
|
||||||
|
orders issued by Customer to StrongLoop shall not be binding on either party,
|
||||||
|
are hereby rejected by StrongLoop and void.
|
||||||
|
9.3 Severability. If any provision of this Agreement is held to be invalid or
|
||||||
|
unenforceable, the remaining portions shall remain in full force and effect and
|
||||||
|
such provision shall be enforced to the maximum extent possible so as to effect
|
||||||
|
the intent of the parties and shall be reformed to the extent necessary to make
|
||||||
|
such provision valid and enforceable.
|
||||||
|
9.4 Waiver. No waiver of rights by either party may be implied from any actions
|
||||||
|
or failures to enforce rights under this Agreement.
|
||||||
|
9.5 Force Majeure. Neither party shall be liable to the other for any delay or
|
||||||
|
failure to perform due to causes beyond its reasonable control (excluding
|
||||||
|
payment of monies due).
|
||||||
|
9.6 No Third Party Beneficiaries. Unless otherwise specifically stated, the
|
||||||
|
terms of this Agreement are intended to be and are solely for the benefit of
|
||||||
|
StrongLoop and Customer and do not create any right in favor of any third party.
|
||||||
|
9.7 Governing Law and Jurisdiction. This Agreement shall be governed by the
|
||||||
|
laws of the State of California, without reference to the principles of
|
||||||
|
conflicts of law. The provisions of the Uniform Computerized Information
|
||||||
|
Transaction Act and United Nations Convention on Contracts for the International
|
||||||
|
Sale of Goods shall not apply to this Agreement. The parties shall attempt to
|
||||||
|
resolve any dispute related to this Agreement informally, initially through
|
||||||
|
their respective management, and then by non-binding mediation in San Francisco
|
||||||
|
County, California. Any litigation related to this Agreement shall be brought
|
||||||
|
in the state or federal courts located in San Francisco County, California, and
|
||||||
|
only in those courts and each party irrevocably waives any objections to such
|
||||||
|
venue.
|
||||||
|
9.8 Notices. All notices must be in writing and shall be effective three (3)
|
||||||
|
days after the date sent to the other party's headquarters, Attention Chief
|
||||||
|
Financial Officer.
|
||||||
|
|
331
README.md
331
README.md
|
@ -1,6 +1,8 @@
|
||||||
## loopback-connector-mysql
|
## loopback-connector-mysql
|
||||||
|
|
||||||
`loopback-connector-mysql` is the MySQL connector module for [loopback-datasource-juggler](http://docs.strongloop.com/loopback-datasource-juggler/).
|
`loopback-connector-mysql` is the MySQL connector module for [loopback-datasource-juggler](https://github.com/strongloop/loopback-datasource-juggler/).
|
||||||
|
|
||||||
|
For complete documentation, see [StrongLoop Documentation | MySQL Connector](http://docs.strongloop.com/display/DOC/MySQL+connector).
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
@ -8,7 +10,7 @@
|
||||||
npm install loopback-connector-mysql --save
|
npm install loopback-connector-mysql --save
|
||||||
````
|
````
|
||||||
|
|
||||||
## Usage
|
## Basic use
|
||||||
|
|
||||||
To use it you need `loopback-datasource-juggler`.
|
To use it you need `loopback-datasource-juggler`.
|
||||||
|
|
||||||
|
@ -42,328 +44,3 @@ To use it you need `loopback-datasource-juggler`.
|
||||||
to `utf8_general_ci`. The `collation` value will also be used to derive the
|
to `utf8_general_ci`. The `collation` value will also be used to derive the
|
||||||
connection charset.
|
connection charset.
|
||||||
|
|
||||||
|
|
||||||
## Data type mappings
|
|
||||||
|
|
||||||
`loopback-connector-mysql` uses the following rules to map between JSON types and MySQL data types.
|
|
||||||
|
|
||||||
### JSON to MySQL types
|
|
||||||
|
|
||||||
- String/JSON: VARCHAR
|
|
||||||
- Text: TEXT
|
|
||||||
- Number: INT
|
|
||||||
- Date: DATETIME
|
|
||||||
- BOOLEAN: TINYINT(1)
|
|
||||||
- Point/GeoPoint: POINT
|
|
||||||
- Enum: ENUM
|
|
||||||
|
|
||||||
### MySQL to JSON types
|
|
||||||
|
|
||||||
- CHAR: String
|
|
||||||
- CHAR(1): Boolean
|
|
||||||
- VARCHAR/TINYTEXT/MEDIUMTEXT/LONGTEXT/TEXT/ENUM/SET: String
|
|
||||||
- TINYBLOB/MEDIUMBLOB/LONGBLOB/BLOB/BINARY/VARBINARY/BIT: Binary
|
|
||||||
- TINYINT/SMALLINT/INT/MEDIUMINT/YEAR/FLOAT/DOUBLE/NUMERIC/DECIMAL: Number
|
|
||||||
- DATE/TIMESTAMP/DATETIME: Date
|
|
||||||
|
|
||||||
## Using the `dataType` field/column option with MySQL
|
|
||||||
|
|
||||||
`loopback-connector-mysql` allows mapping of LoopBack model properties to MYSQL columns using the 'mysql' property of the
|
|
||||||
property definition. For example,
|
|
||||||
|
|
||||||
"locationId":{
|
|
||||||
"type":"String",
|
|
||||||
"required":true,
|
|
||||||
"length":20,
|
|
||||||
"mysql":
|
|
||||||
{
|
|
||||||
"columnName":"LOCATION_ID",
|
|
||||||
"dataType":"VARCHAR2",
|
|
||||||
"dataLength":20,
|
|
||||||
"nullable":"N"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
`loopback-connector-mysql` also supports using the `dataType` column/property attribute to specify what MySQL column
|
|
||||||
type is used for many loopback-datasource-juggler types.
|
|
||||||
|
|
||||||
The following type-dataType combinations are supported:
|
|
||||||
- Number
|
|
||||||
- integer
|
|
||||||
- tinyint
|
|
||||||
- smallint
|
|
||||||
- mediumint
|
|
||||||
- int
|
|
||||||
- bigint
|
|
||||||
|
|
||||||
Use the `limit` option to alter the display width.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
`{ count : { type: Number, dataType: 'smallInt' }}`
|
|
||||||
|
|
||||||
- floating point types
|
|
||||||
- float
|
|
||||||
- double
|
|
||||||
|
|
||||||
Use the `precision` and `scale` options to specify custom precision. Default is (16,8).
|
|
||||||
|
|
||||||
Example:
|
|
||||||
`{ average : { type: Number, dataType: 'float', precision: 20, scale: 4 }}`
|
|
||||||
|
|
||||||
- fixed-point exact value types
|
|
||||||
- decimal
|
|
||||||
- numeric
|
|
||||||
|
|
||||||
Use the `precision` and `scale` options to specify custom precision. Default is (9,2).
|
|
||||||
|
|
||||||
These aren't likely to function as true fixed-point.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
`{ stdDev : { type: Number, dataType: 'decimal', precision: 12, scale: 8 }}`
|
|
||||||
|
|
||||||
- String / DataSource.Text / DataSource.JSON
|
|
||||||
- varchar
|
|
||||||
- char
|
|
||||||
- text
|
|
||||||
- mediumtext
|
|
||||||
- tinytext
|
|
||||||
- longtext
|
|
||||||
|
|
||||||
Example:
|
|
||||||
`{ userName : { type: String, dataType: 'char', limit: 24 }}`
|
|
||||||
|
|
||||||
Example:
|
|
||||||
`{ biography : { type: String, dataType: 'longtext' }}`
|
|
||||||
|
|
||||||
- Date
|
|
||||||
- datetime
|
|
||||||
- timestamp
|
|
||||||
|
|
||||||
Example:
|
|
||||||
`{ startTime : { type: Date, dataType: 'timestamp' }}`
|
|
||||||
|
|
||||||
* Enum
|
|
||||||
Enums are special.
|
|
||||||
Create an Enum using Enum factory:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var MOOD = dataSource.EnumFactory('glad', 'sad', 'mad');
|
|
||||||
MOOD.SAD; // 'sad'
|
|
||||||
MOOD(2); // 'sad'
|
|
||||||
MOOD('SAD'); // 'sad'
|
|
||||||
MOOD('sad'); // 'sad'
|
|
||||||
```
|
|
||||||
|
|
||||||
- `{ mood: { type: MOOD }}`
|
|
||||||
- `{ choice: { type: dataSource.EnumFactory('yes', 'no', 'maybe'), null: false }}`
|
|
||||||
|
|
||||||
|
|
||||||
## Discovering Models
|
|
||||||
|
|
||||||
MySQL data sources allow you to discover model definition information from existing mysql databases. See the following APIs:
|
|
||||||
|
|
||||||
- [dataSource.discoverModelDefinitions([owner], fn)](https://github.com/strongloop/loopback#datasourcediscovermodeldefinitionsusername-fn)
|
|
||||||
- [dataSource.discoverSchema([owner], name, fn)](https://github.com/strongloop/loopback#datasourcediscoverschemaowner-name-fn)
|
|
||||||
|
|
||||||
### Asynchronous APIs for discovery
|
|
||||||
|
|
||||||
* MySQL.prototype.discoverModelDefinitions = function (options, cb)
|
|
||||||
- options:
|
|
||||||
- all: {Boolean} To include tables/views from all schemas/owners
|
|
||||||
- owner/schema: {String} The schema/owner name
|
|
||||||
- views: {Boolean} To include views
|
|
||||||
- cb:
|
|
||||||
- Get a list of table/view names, for example:
|
|
||||||
|
|
||||||
{type: 'table', name: 'INVENTORY', owner: 'STRONGLOOP' }
|
|
||||||
{type: 'table', name: 'LOCATION', owner: 'STRONGLOOP' }
|
|
||||||
{type: 'view', name: 'INVENTORY_VIEW', owner: 'STRONGLOOP' }
|
|
||||||
|
|
||||||
|
|
||||||
* MySQL.prototype.discoverModelProperties = function (table, options, cb)
|
|
||||||
- table: {String} The name of a table or view
|
|
||||||
- options:
|
|
||||||
- owner/schema: {String} The schema/owner name
|
|
||||||
- cb:
|
|
||||||
- Get a list of model property definitions, for example:
|
|
||||||
|
|
||||||
{ owner: 'STRONGLOOP',
|
|
||||||
tableName: 'PRODUCT',
|
|
||||||
columnName: 'ID',
|
|
||||||
dataType: 'VARCHAR2',
|
|
||||||
dataLength: 20,
|
|
||||||
nullable: 'N',
|
|
||||||
type: 'String' }
|
|
||||||
{ owner: 'STRONGLOOP',
|
|
||||||
tableName: 'PRODUCT',
|
|
||||||
columnName: 'NAME',
|
|
||||||
dataType: 'VARCHAR2',
|
|
||||||
dataLength: 64,
|
|
||||||
nullable: 'Y',
|
|
||||||
type: 'String' }
|
|
||||||
|
|
||||||
|
|
||||||
* MySQL.prototype.discoverPrimaryKeys= function(table, options, cb)
|
|
||||||
- table: {String} The name of a table or view
|
|
||||||
- options:
|
|
||||||
- owner/schema: {String} The schema/owner name
|
|
||||||
- cb:
|
|
||||||
- Get a list of primary key definitions, for example:
|
|
||||||
|
|
||||||
{ owner: 'STRONGLOOP',
|
|
||||||
tableName: 'INVENTORY',
|
|
||||||
columnName: 'PRODUCT_ID',
|
|
||||||
keySeq: 1,
|
|
||||||
pkName: 'ID_PK' }
|
|
||||||
{ owner: 'STRONGLOOP',
|
|
||||||
tableName: 'INVENTORY',
|
|
||||||
columnName: 'LOCATION_ID',
|
|
||||||
keySeq: 2,
|
|
||||||
pkName: 'ID_PK' }
|
|
||||||
|
|
||||||
* MySQL.prototype.discoverForeignKeys= function(table, options, cb)
|
|
||||||
- table: {String} The name of a table or view
|
|
||||||
- options:
|
|
||||||
- owner/schema: {String} The schema/owner name
|
|
||||||
- cb:
|
|
||||||
- Get a list of foreign key definitions, for example:
|
|
||||||
|
|
||||||
{ fkOwner: 'STRONGLOOP',
|
|
||||||
fkName: 'PRODUCT_FK',
|
|
||||||
fkTableName: 'INVENTORY',
|
|
||||||
fkColumnName: 'PRODUCT_ID',
|
|
||||||
keySeq: 1,
|
|
||||||
pkOwner: 'STRONGLOOP',
|
|
||||||
pkName: 'PRODUCT_PK',
|
|
||||||
pkTableName: 'PRODUCT',
|
|
||||||
pkColumnName: 'ID' }
|
|
||||||
|
|
||||||
|
|
||||||
* MySQL.prototype.discoverExportedForeignKeys= function(table, options, cb)
|
|
||||||
|
|
||||||
- table: {String} The name of a table or view
|
|
||||||
- options:
|
|
||||||
- owner/schema: {String} The schema/owner name
|
|
||||||
- cb:
|
|
||||||
- Get a list of foreign key definitions that reference the primary key of the given table, for example:
|
|
||||||
|
|
||||||
{ fkName: 'PRODUCT_FK',
|
|
||||||
fkOwner: 'STRONGLOOP',
|
|
||||||
fkTableName: 'INVENTORY',
|
|
||||||
fkColumnName: 'PRODUCT_ID',
|
|
||||||
keySeq: 1,
|
|
||||||
pkName: 'PRODUCT_PK',
|
|
||||||
pkOwner: 'STRONGLOOP',
|
|
||||||
pkTableName: 'PRODUCT',
|
|
||||||
pkColumnName: 'ID' }
|
|
||||||
|
|
||||||
### Discover/build/try the models
|
|
||||||
|
|
||||||
#### Build a LDL schema by discovery
|
|
||||||
|
|
||||||
Data sources backed by the MySQL connector can discover LDL models from the database using the `discoverSchema` API. For
|
|
||||||
example,
|
|
||||||
|
|
||||||
dataSource.discoverSchema('INVENTORY', {owner: 'STRONGLOOP'}, function (err, schema) {
|
|
||||||
...
|
|
||||||
}
|
|
||||||
|
|
||||||
Here is the sample result. Please note there are 'mysql' properties in addition to the regular LDL model options and
|
|
||||||
properties. The 'mysql' objects contain the MySQL specific mappings.
|
|
||||||
|
|
||||||
{
|
|
||||||
"name":"Inventory",
|
|
||||||
"options":{
|
|
||||||
"idInjection":false,
|
|
||||||
"mysql":{
|
|
||||||
"schema":"STRONGLOOP",
|
|
||||||
"table":"INVENTORY"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"properties":{
|
|
||||||
"productId":{
|
|
||||||
"type":"String",
|
|
||||||
"required":false,
|
|
||||||
"length":60,
|
|
||||||
"precision":null,
|
|
||||||
"scale":null,
|
|
||||||
"id":1,
|
|
||||||
"mysql":{
|
|
||||||
"columnName":"PRODUCT_ID",
|
|
||||||
"dataType":"varchar",
|
|
||||||
"dataLength":60,
|
|
||||||
"dataPrecision":null,
|
|
||||||
"dataScale":null,
|
|
||||||
"nullable":"NO"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"locationId":{
|
|
||||||
"type":"String",
|
|
||||||
"required":false,
|
|
||||||
"length":60,
|
|
||||||
"precision":null,
|
|
||||||
"scale":null,
|
|
||||||
"id":2,
|
|
||||||
"mysql":{
|
|
||||||
"columnName":"LOCATION_ID",
|
|
||||||
"dataType":"varchar",
|
|
||||||
"dataLength":60,
|
|
||||||
"dataPrecision":null,
|
|
||||||
"dataScale":null,
|
|
||||||
"nullable":"NO"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"available":{
|
|
||||||
"type":"Number",
|
|
||||||
"required":false,
|
|
||||||
"length":null,
|
|
||||||
"precision":10,
|
|
||||||
"scale":0,
|
|
||||||
"mysql":{
|
|
||||||
"columnName":"AVAILABLE",
|
|
||||||
"dataType":"int",
|
|
||||||
"dataLength":null,
|
|
||||||
"dataPrecision":10,
|
|
||||||
"dataScale":0,
|
|
||||||
"nullable":"YES"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"total":{
|
|
||||||
"type":"Number",
|
|
||||||
"required":false,
|
|
||||||
"length":null,
|
|
||||||
"precision":10,
|
|
||||||
"scale":0,
|
|
||||||
"mysql":{
|
|
||||||
"columnName":"TOTAL",
|
|
||||||
"dataType":"int",
|
|
||||||
"dataLength":null,
|
|
||||||
"dataPrecision":10,
|
|
||||||
"dataScale":0,
|
|
||||||
"nullable":"YES"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
We can also discover and build model classes in one shot. The following example uses `discoverAndBuildModels` to discover,
|
|
||||||
build and try the models:
|
|
||||||
|
|
||||||
dataSource.discoverAndBuildModels('INVENTORY', { owner: 'STRONGLOOP', visited: {}, associations: true},
|
|
||||||
function (err, models) {
|
|
||||||
// Show records from the models
|
|
||||||
for(var m in models) {
|
|
||||||
models[m].all(show);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Find one record for inventory
|
|
||||||
models.Inventory.findOne({}, function(err, inv) {
|
|
||||||
console.log("\nInventory: ", inv);
|
|
||||||
// Follow the foreign key to navigate to the product
|
|
||||||
inv.product(function(err, prod) {
|
|
||||||
console.log("\nProduct: ", prod);
|
|
||||||
console.log("\n ------------- ");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
12
docs.json
12
docs.json
|
@ -1,8 +1,14 @@
|
||||||
{
|
{
|
||||||
"content": [
|
"content": [
|
||||||
{"title": "LoopBack MySQL Connector API", "depth": 2},
|
{
|
||||||
|
"title": "LoopBack MySQL Connector API",
|
||||||
|
"depth": 2
|
||||||
|
},
|
||||||
"lib/mysql.js",
|
"lib/mysql.js",
|
||||||
{"title": "MySQL Discovery API", "depth": 2},
|
{
|
||||||
|
"title": "MySQL Discovery API",
|
||||||
|
"depth": 2
|
||||||
|
},
|
||||||
"lib/discovery.js"
|
"lib/discovery.js"
|
||||||
],
|
],
|
||||||
"codeSectionDepth": 3
|
"codeSectionDepth": 3
|
||||||
|
|
|
@ -5,19 +5,18 @@ var config = require('rc')('loopback', {dev: {mysql: {}}}).dev.mysql;
|
||||||
var ds = new DataSource(require('../'), config);
|
var ds = new DataSource(require('../'), config);
|
||||||
|
|
||||||
function show(err, models) {
|
function show(err, models) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
} else {
|
} else {
|
||||||
console.log(models);
|
console.log(models);
|
||||||
if (models) {
|
if (models) {
|
||||||
models.forEach(function (m) {
|
models.forEach(function (m) {
|
||||||
console.dir(m);
|
console.dir(m);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ds.discoverModelDefinitions({views: true, limit: 20}, show);
|
ds.discoverModelDefinitions({views: true, limit: 20}, show);
|
||||||
|
|
||||||
ds.discoverModelProperties('customer', show);
|
ds.discoverModelProperties('customer', show);
|
||||||
|
@ -25,16 +24,15 @@ ds.discoverModelProperties('customer', show);
|
||||||
ds.discoverModelProperties('location', {owner: 'strongloop'}, show);
|
ds.discoverModelProperties('location', {owner: 'strongloop'}, show);
|
||||||
|
|
||||||
ds.discoverPrimaryKeys('customer', show);
|
ds.discoverPrimaryKeys('customer', show);
|
||||||
ds.discoverForeignKeys('inventory', show);
|
ds.discoverForeignKeys('inventory', show);
|
||||||
|
|
||||||
ds.discoverExportedForeignKeys('location', show);
|
|
||||||
|
|
||||||
|
ds.discoverExportedForeignKeys('location', show);
|
||||||
|
|
||||||
ds.discoverAndBuildModels('weapon', {owner: 'strongloop', visited: {}, associations: true}, function (err, models) {
|
ds.discoverAndBuildModels('weapon', {owner: 'strongloop', visited: {}, associations: true}, function (err, models) {
|
||||||
|
|
||||||
for (var m in models) {
|
for (var m in models) {
|
||||||
models[m].all(show);
|
models[m].all(show);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
660
lib/discovery.js
660
lib/discovery.js
|
@ -1,355 +1,357 @@
|
||||||
module.exports = mixinDiscovery;
|
module.exports = mixinDiscovery;
|
||||||
|
|
||||||
function mixinDiscovery(MySQL) {
|
function mixinDiscovery(MySQL) {
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
|
|
||||||
function paginateSQL(sql, orderBy, options) {
|
function paginateSQL(sql, orderBy, options) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
var limit = '';
|
var limit = '';
|
||||||
if (options.offset || options.skip || options.limit) {
|
if (options.offset || options.skip || options.limit) {
|
||||||
limit = ' LIMIT ' + (options.offset || options.skip || 0); // Offset starts from 0
|
limit = ' LIMIT ' + (options.offset || options.skip || 0); // Offset starts from 0
|
||||||
if (options.limit) {
|
if (options.limit) {
|
||||||
limit = limit + ',' + options.limit;
|
limit = limit + ',' + options.limit;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if(!orderBy) {
|
|
||||||
sql += ' ORDER BY ' + orderBy;
|
|
||||||
}
|
|
||||||
return sql + limit;
|
|
||||||
}
|
}
|
||||||
|
if (!orderBy) {
|
||||||
/*!
|
sql += ' ORDER BY ' + orderBy;
|
||||||
* Build sql for listing tables
|
|
||||||
* @param options {all: for all owners, owner: for a given owner}
|
|
||||||
* @returns {string} The sql statement
|
|
||||||
*/
|
|
||||||
function queryTables(options) {
|
|
||||||
var sqlTables = null;
|
|
||||||
var owner = options.owner || options.schema;
|
|
||||||
|
|
||||||
if (options.all && !owner) {
|
|
||||||
sqlTables = paginateSQL('SELECT \'table\' AS "type", table_name AS "name", table_schema AS "owner"'
|
|
||||||
+ ' FROM information_schema.tables', 'table_schema, table_name', options);
|
|
||||||
} else if (owner) {
|
|
||||||
sqlTables = paginateSQL('SELECT \'table\' AS "type", table_name AS "name", table_schema AS "owner"'
|
|
||||||
+ ' FROM information_schema.tables WHERE table_schema=\'' + owner + '\'', 'table_schema, table_name', options);
|
|
||||||
} else {
|
|
||||||
sqlTables = paginateSQL('SELECT \'table\' AS "type", table_name AS "name",'
|
|
||||||
+ ' table_schema AS "owner" FROM information_schema.tables',
|
|
||||||
'table_name', options);
|
|
||||||
}
|
|
||||||
return sqlTables;
|
|
||||||
}
|
}
|
||||||
|
return sql + limit;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Build sql for listing views
|
* Build sql for listing tables
|
||||||
* @param options {all: for all owners, owner: for a given owner}
|
* @param options {all: for all owners, owner: for a given owner}
|
||||||
* @returns {string} The sql statement
|
* @returns {string} The sql statement
|
||||||
*/
|
*/
|
||||||
function queryViews(options) {
|
function queryTables(options) {
|
||||||
var sqlViews = null;
|
var sqlTables = null;
|
||||||
if (options.views) {
|
var owner = options.owner || options.schema;
|
||||||
|
|
||||||
var owner = options.owner || options.schema;
|
if (options.all && !owner) {
|
||||||
|
sqlTables = paginateSQL('SELECT \'table\' AS "type", table_name AS "name", table_schema AS "owner"'
|
||||||
if (options.all && !owner) {
|
+ ' FROM information_schema.tables', 'table_schema, table_name', options);
|
||||||
sqlViews = paginateSQL('SELECT \'view\' AS "type", table_name AS "name",'
|
} else if (owner) {
|
||||||
+ ' table_schema AS "owner" FROM information_schema.views',
|
sqlTables = paginateSQL('SELECT \'table\' AS "type", table_name AS "name", table_schema AS "owner"'
|
||||||
'table_schema, table_name', options);
|
+ ' FROM information_schema.tables WHERE table_schema=\'' + owner + '\'', 'table_schema, table_name', options);
|
||||||
} else if (owner) {
|
} else {
|
||||||
sqlViews = paginateSQL('SELECT \'view\' AS "type", table_name AS "name",'
|
sqlTables = paginateSQL('SELECT \'table\' AS "type", table_name AS "name",'
|
||||||
+ ' table_schema AS "owner" FROM information_schema.views WHERE table_schema=\'' + owner + '\'',
|
+ ' table_schema AS "owner" FROM information_schema.tables WHERE table_schema=SUBSTRING_INDEX(USER(),\'@\',1)',
|
||||||
'table_schema, table_name', options);
|
'table_name', options);
|
||||||
} else {
|
|
||||||
sqlViews = paginateSQL('SELECT \'view\' AS "type", table_name AS "name",'
|
|
||||||
+ ' table_schema AS "owner" FROM information_schema.views',
|
|
||||||
'table_name', options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return sqlViews;
|
|
||||||
}
|
}
|
||||||
|
return sqlTables;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/*!
|
||||||
* Discover model definitions
|
* Build sql for listing views
|
||||||
*
|
* @param options {all: for all owners, owner: for a given owner}
|
||||||
* @param {Object} options Options for discovery
|
* @returns {string} The sql statement
|
||||||
* @param {Function} [cb] The callback function
|
*/
|
||||||
*/
|
function queryViews(options) {
|
||||||
MySQL.prototype.discoverModelDefinitions = function (options, cb) {
|
var sqlViews = null;
|
||||||
if (!cb && typeof options === 'function') {
|
if (options.views) {
|
||||||
cb = options;
|
|
||||||
options = {};
|
var owner = options.owner || options.schema;
|
||||||
|
|
||||||
|
if (options.all && !owner) {
|
||||||
|
sqlViews = paginateSQL('SELECT \'view\' AS "type", table_name AS "name",'
|
||||||
|
+ ' table_schema AS "owner" FROM information_schema.views',
|
||||||
|
'table_schema, table_name', options);
|
||||||
|
} else if (owner) {
|
||||||
|
sqlViews = paginateSQL('SELECT \'view\' AS "type", table_name AS "name",'
|
||||||
|
+ ' table_schema AS "owner" FROM information_schema.views WHERE table_schema=\'' + owner + '\'',
|
||||||
|
'table_schema, table_name', options);
|
||||||
|
} else {
|
||||||
|
sqlViews = paginateSQL('SELECT \'view\' AS "type", table_name AS "name",'
|
||||||
|
+ ' table_schema AS "owner" FROM information_schema.views',
|
||||||
|
'table_name', options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sqlViews;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discover model definitions
|
||||||
|
*
|
||||||
|
* @param {Object} options Options for discovery
|
||||||
|
* @param {Function} [cb] The callback function
|
||||||
|
*/
|
||||||
|
MySQL.prototype.discoverModelDefinitions = function (options, cb) {
|
||||||
|
if (!cb && typeof options === 'function') {
|
||||||
|
cb = options;
|
||||||
|
options = {};
|
||||||
|
}
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
var calls = [function (callback) {
|
||||||
|
self.query(queryTables(options), callback);
|
||||||
|
}];
|
||||||
|
|
||||||
|
if (options.views) {
|
||||||
|
calls.push(function (callback) {
|
||||||
|
self.query(queryViews(options), callback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async.parallel(calls, function (err, data) {
|
||||||
|
if (err) {
|
||||||
|
cb(err, data);
|
||||||
|
} else {
|
||||||
|
var merged = [];
|
||||||
|
merged = merged.concat(data.shift());
|
||||||
|
if (data.length) {
|
||||||
|
merged = merged.concat(data.shift());
|
||||||
}
|
}
|
||||||
options = options || {};
|
cb(err, merged);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
var self = this;
|
/*!
|
||||||
var calls = [function (callback) {
|
* Normalize the arguments
|
||||||
self.query(queryTables(options), callback);
|
* @param table string, required
|
||||||
}];
|
* @param options object, optional
|
||||||
|
* @param cb function, optional
|
||||||
|
*/
|
||||||
|
function getArgs(table, options, cb) {
|
||||||
|
if ('string' !== typeof table || !table) {
|
||||||
|
throw new Error('table is a required string argument: ' + table);
|
||||||
|
}
|
||||||
|
options = options || {};
|
||||||
|
if (!cb && 'function' === typeof options) {
|
||||||
|
cb = options;
|
||||||
|
options = {};
|
||||||
|
}
|
||||||
|
if (typeof options !== 'object') {
|
||||||
|
throw new Error('options must be an object: ' + options);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
owner: options.owner || options.schema,
|
||||||
|
table: table,
|
||||||
|
options: options,
|
||||||
|
cb: cb
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (options.views) {
|
/*!
|
||||||
calls.push(function (callback) {
|
* Build the sql statement to query columns for a given table
|
||||||
self.query(queryViews(options), callback);
|
* @param owner
|
||||||
});
|
* @param table
|
||||||
}
|
* @returns {String} The sql statement
|
||||||
async.parallel(calls, function (err, data) {
|
*/
|
||||||
if (err) {
|
function queryColumns(owner, table) {
|
||||||
cb(err, data);
|
var sql = null;
|
||||||
} else {
|
if (owner) {
|
||||||
var merged = [];
|
sql = paginateSQL('SELECT table_schema AS "owner", table_name AS "tableName", column_name AS "columnName", data_type AS "dataType",'
|
||||||
merged = merged.concat(data.shift());
|
+ ' character_octet_length AS "dataLength", numeric_precision AS "dataPrecision", numeric_scale AS "dataScale", is_nullable AS "nullable"'
|
||||||
if (data.length) {
|
+ ' FROM information_schema.columns'
|
||||||
merged = merged.concat(data.shift());
|
+ ' WHERE table_schema=\'' + owner + '\''
|
||||||
}
|
+ (table ? ' AND table_name=\'' + table + '\'' : ''),
|
||||||
cb(err, merged);
|
'table_name, ordinal_position', {});
|
||||||
}
|
} else {
|
||||||
|
sql = paginateSQL('SELECT table_schema AS "owner", table_name AS "tableName", column_name AS "columnName", data_type AS "dataType",'
|
||||||
|
+ ' character_octet_length AS "dataLength", numeric_precision AS "dataPrecision", numeric_scale AS "dataScale", is_nullable AS "nullable"'
|
||||||
|
+ ' FROM information_schema.columns'
|
||||||
|
+ (table ? ' WHERE table_name=\'' + table + '\'' : ''),
|
||||||
|
'table_name, ordinal_position', {});
|
||||||
|
}
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discover model properties from a table
|
||||||
|
* @param {String} table The table name
|
||||||
|
* @param {Object} options The options for discovery
|
||||||
|
* @param {Function} [cb] The callback function
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
MySQL.prototype.discoverModelProperties = function (table, options, cb) {
|
||||||
|
var args = getArgs(table, options, cb);
|
||||||
|
var owner = args.owner;
|
||||||
|
table = args.table;
|
||||||
|
options = args.options;
|
||||||
|
cb = args.cb;
|
||||||
|
|
||||||
|
var sql = queryColumns(owner, table);
|
||||||
|
var callback = function (err, results) {
|
||||||
|
if (err) {
|
||||||
|
cb(err, results);
|
||||||
|
} else {
|
||||||
|
results.map(function (r) {
|
||||||
|
r.type = mysqlDataTypeToJSONType(r.dataType, r.dataLength);
|
||||||
});
|
});
|
||||||
|
cb(err, results);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
this.query(sql, callback);
|
||||||
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Normalize the arguments
|
* Build the sql statement for querying primary keys of a given table
|
||||||
* @param table string, required
|
* @param owner
|
||||||
* @param options object, optional
|
* @param table
|
||||||
* @param cb function, optional
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
function getArgs(table, options, cb) {
|
|
||||||
if ('string' !== typeof table || !table) {
|
|
||||||
throw new Error('table is a required string argument: ' + table);
|
|
||||||
}
|
|
||||||
options = options || {};
|
|
||||||
if (!cb && 'function' === typeof options) {
|
|
||||||
cb = options;
|
|
||||||
options = {};
|
|
||||||
}
|
|
||||||
if (typeof options !== 'object') {
|
|
||||||
throw new Error('options must be an object: ' + options);
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
owner: options.owner || options.schema,
|
|
||||||
table: table,
|
|
||||||
options: options,
|
|
||||||
cb: cb
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Build the sql statement to query columns for a given table
|
|
||||||
* @param owner
|
|
||||||
* @param table
|
|
||||||
* @returns {String} The sql statement
|
|
||||||
*/
|
|
||||||
function queryColumns(owner, table) {
|
|
||||||
var sql = null;
|
|
||||||
if (owner) {
|
|
||||||
sql = paginateSQL('SELECT table_schema AS "owner", table_name AS "tableName", column_name AS "columnName", data_type AS "dataType",'
|
|
||||||
+ ' character_octet_length AS "dataLength", numeric_precision AS "dataPrecision", numeric_scale AS "dataScale", is_nullable AS "nullable"'
|
|
||||||
+ ' FROM information_schema.columns'
|
|
||||||
+ ' WHERE table_schema=\'' + owner + '\''
|
|
||||||
+ (table ? ' AND table_name=\'' + table + '\'' : ''),
|
|
||||||
'table_name, ordinal_position', {});
|
|
||||||
} else {
|
|
||||||
sql = paginateSQL('SELECT table_schema AS "owner", table_name AS "tableName", column_name AS "columnName", data_type AS "dataType",'
|
|
||||||
+ ' character_octet_length AS "dataLength", numeric_precision AS "dataPrecision", numeric_scale AS "dataScale", is_nullable AS "nullable"'
|
|
||||||
+ ' FROM information_schema.columns'
|
|
||||||
+ (table ? ' WHERE table_name=\'' + table + '\'' : ''),
|
|
||||||
'table_name, ordinal_position', {});
|
|
||||||
}
|
|
||||||
return sql;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Discover model properties from a table
|
|
||||||
* @param {String} table The table name
|
|
||||||
* @param {Object} options The options for discovery
|
|
||||||
* @param {Function} [cb] The callback function
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
MySQL.prototype.discoverModelProperties = function (table, options, cb) {
|
|
||||||
var args = getArgs(table, options, cb);
|
|
||||||
var owner = args.owner;
|
|
||||||
table = args.table;
|
|
||||||
options = args.options;
|
|
||||||
cb = args.cb;
|
|
||||||
|
|
||||||
var sql = queryColumns(owner, table);
|
|
||||||
var callback = function (err, results) {
|
|
||||||
if (err) {
|
|
||||||
cb(err, results);
|
|
||||||
} else {
|
|
||||||
results.map(function (r) {
|
|
||||||
r.type = mysqlDataTypeToJSONType(r.dataType, r.dataLength);
|
|
||||||
});
|
|
||||||
cb(err, results);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
this.query(sql, callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Build the sql statement for querying primary keys of a given table
|
|
||||||
* @param owner
|
|
||||||
* @param table
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
// http://docs.oracle.com/javase/6/docs/api/java/sql/DatabaseMetaData.html#getPrimaryKeys(java.lang.String, java.lang.String, java.lang.String)
|
// http://docs.oracle.com/javase/6/docs/api/java/sql/DatabaseMetaData.html#getPrimaryKeys(java.lang.String, java.lang.String, java.lang.String)
|
||||||
function queryForPrimaryKeys(owner, table) {
|
function queryForPrimaryKeys(owner, table) {
|
||||||
var sql = 'SELECT table_schema AS "owner", '
|
var sql = 'SELECT table_schema AS "owner", '
|
||||||
+ 'table_name AS "tableName", column_name AS "columnName", ordinal_position AS "keySeq", constraint_name AS "pkName" FROM'
|
+ 'table_name AS "tableName", column_name AS "columnName", ordinal_position AS "keySeq", constraint_name AS "pkName" FROM'
|
||||||
+ ' information_schema.key_column_usage'
|
+ ' information_schema.key_column_usage'
|
||||||
+ ' WHERE constraint_name=\'PRIMARY\'';
|
+ ' WHERE constraint_name=\'PRIMARY\'';
|
||||||
|
|
||||||
if (owner) {
|
if (owner) {
|
||||||
sql += ' AND table_schema=\'' + owner + '\'';
|
sql += ' AND table_schema=\'' + owner + '\'';
|
||||||
}
|
|
||||||
if (table) {
|
|
||||||
sql += ' AND table_name=\'' + table + '\'';
|
|
||||||
}
|
|
||||||
sql += ' ORDER BY table_schema, constraint_name, table_name, ordinal_position';
|
|
||||||
return sql;
|
|
||||||
}
|
}
|
||||||
|
if (table) {
|
||||||
/**
|
sql += ' AND table_name=\'' + table + '\'';
|
||||||
* Discover primary keys for a given table
|
|
||||||
* @param {String} table The table name
|
|
||||||
* @param {Object} options The options for discovery
|
|
||||||
* @param {Function} [cb] The callback function
|
|
||||||
*/
|
|
||||||
MySQL.prototype.discoverPrimaryKeys = function (table, options, cb) {
|
|
||||||
var args = getArgs(table, options, cb);
|
|
||||||
var owner = args.owner;
|
|
||||||
table = args.table;
|
|
||||||
options = args.options;
|
|
||||||
cb = args.cb;
|
|
||||||
|
|
||||||
var sql = queryForPrimaryKeys(owner, table);
|
|
||||||
this.query(sql, cb);
|
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Build the sql statement for querying foreign keys of a given table
|
|
||||||
* @param owner
|
|
||||||
* @param table
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
function queryForeignKeys(owner, table) {
|
|
||||||
var sql =
|
|
||||||
'SELECT table_schema AS "fkOwner", constraint_name AS "fkName", table_name AS "fkTableName",'
|
|
||||||
+ ' column_name AS "fkColumnName", ordinal_position AS "keySeq",'
|
|
||||||
+ ' referenced_table_schema AS "pkOwner", \'PRIMARY\' AS "pkName", '
|
|
||||||
+ ' referenced_table_name AS "pkTableName", referenced_column_name AS "pkColumnName"'
|
|
||||||
+ ' FROM information_schema.key_column_usage'
|
|
||||||
+ ' WHERE'
|
|
||||||
+ ' constraint_name!=\'PRIMARY\' and POSITION_IN_UNIQUE_CONSTRAINT IS NOT NULL';
|
|
||||||
if (owner) {
|
|
||||||
sql += ' AND table_schema=\'' + owner + '\'';
|
|
||||||
}
|
|
||||||
if (table) {
|
|
||||||
sql += ' AND table_name=\'' + table + '\'';
|
|
||||||
}
|
|
||||||
return sql;
|
|
||||||
}
|
}
|
||||||
|
sql += ' ORDER BY table_schema, constraint_name, table_name, ordinal_position';
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Discover foreign keys for a given table
|
* Discover primary keys for a given table
|
||||||
* @param {String} table The table name
|
* @param {String} table The table name
|
||||||
* @param {Object} options The options for discovery
|
* @param {Object} options The options for discovery
|
||||||
* @param {Function} [cb] The callback function
|
* @param {Function} [cb] The callback function
|
||||||
*/
|
*/
|
||||||
MySQL.prototype.discoverForeignKeys = function (table, options, cb) {
|
MySQL.prototype.discoverPrimaryKeys = function (table, options, cb) {
|
||||||
var args = getArgs(table, options, cb);
|
var args = getArgs(table, options, cb);
|
||||||
var owner = args.owner;
|
var owner = args.owner;
|
||||||
table = args.table;
|
table = args.table;
|
||||||
options = args.options;
|
options = args.options;
|
||||||
cb = args.cb;
|
cb = args.cb;
|
||||||
|
|
||||||
var sql = queryForeignKeys(owner, table);
|
var sql = queryForPrimaryKeys(owner, table);
|
||||||
this.query(sql, cb);
|
this.query(sql, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Retrieves a description of the foreign key columns that reference the given table's primary key columns (the foreign keys exported by a table).
|
* Build the sql statement for querying foreign keys of a given table
|
||||||
* They are ordered by fkTableOwner, fkTableName, and keySeq.
|
* @param owner
|
||||||
* @param owner
|
* @param table
|
||||||
* @param table
|
* @returns {string}
|
||||||
* @returns {string}
|
*/
|
||||||
*/
|
function queryForeignKeys(owner, table) {
|
||||||
function queryExportedForeignKeys(owner, table) {
|
var sql =
|
||||||
var sql = 'SELECT a.constraint_name AS "fkName", a.table_schema AS "fkOwner", a.table_name AS "fkTableName",'
|
'SELECT table_schema AS "fkOwner", constraint_name AS "fkName", table_name AS "fkTableName",'
|
||||||
+ ' a.column_name AS "fkColumnName", a.ordinal_position AS "keySeq",'
|
+ ' column_name AS "fkColumnName", ordinal_position AS "keySeq",'
|
||||||
+ ' NULL AS "pkName", a.referenced_table_schema AS "pkOwner",'
|
+ ' referenced_table_schema AS "pkOwner", \'PRIMARY\' AS "pkName", '
|
||||||
+ ' a.referenced_table_name AS "pkTableName", a.referenced_column_name AS "pkColumnName"'
|
+ ' referenced_table_name AS "pkTableName", referenced_column_name AS "pkColumnName"'
|
||||||
+ ' FROM'
|
+ ' FROM information_schema.key_column_usage'
|
||||||
+ ' information_schema.key_column_usage a'
|
+ ' WHERE'
|
||||||
+ ' WHERE a.position_in_unique_constraint IS NOT NULL';
|
+ ' constraint_name!=\'PRIMARY\' and POSITION_IN_UNIQUE_CONSTRAINT IS NOT NULL';
|
||||||
if (owner) {
|
if (owner) {
|
||||||
sql += ' and a.referenced_table_schema=\'' + owner + '\'';
|
sql += ' AND table_schema=\'' + owner + '\'';
|
||||||
}
|
|
||||||
if (table) {
|
|
||||||
sql += ' and a.referenced_table_name=\'' + table + '\'';
|
|
||||||
}
|
|
||||||
sql += ' order by a.table_schema, a.table_name, a.ordinal_position';
|
|
||||||
|
|
||||||
return sql;
|
|
||||||
}
|
}
|
||||||
|
if (table) {
|
||||||
/**
|
sql += ' AND table_name=\'' + table + '\'';
|
||||||
* Discover foreign keys that reference to the primary key of this table
|
|
||||||
* @param {String} table The table name
|
|
||||||
* @param {Object} options The options for discovery
|
|
||||||
* @param {Function} [cb] The callback function
|
|
||||||
*/
|
|
||||||
MySQL.prototype.discoverExportedForeignKeys = function (table, options, cb) {
|
|
||||||
var args = getArgs(table, options, cb);
|
|
||||||
var owner = args.owner;
|
|
||||||
table = args.table;
|
|
||||||
options = args.options;
|
|
||||||
cb = args.cb;
|
|
||||||
|
|
||||||
var sql = queryExportedForeignKeys(owner, table);
|
|
||||||
this.query(sql, cb);
|
|
||||||
};
|
|
||||||
|
|
||||||
function mysqlDataTypeToJSONType(mysqlType, dataLength) {
|
|
||||||
var type = mysqlType.toUpperCase();
|
|
||||||
switch (type) {
|
|
||||||
case 'CHAR':
|
|
||||||
if (dataLength === 1) {
|
|
||||||
// Treat char(1) as boolean
|
|
||||||
return 'Boolean';
|
|
||||||
} else {
|
|
||||||
return 'String';
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'VARCHAR':
|
|
||||||
case 'TINYTEXT':
|
|
||||||
case 'MEDIUMTEXT':
|
|
||||||
case 'LONGTEXT':
|
|
||||||
case 'TEXT':
|
|
||||||
case 'ENUM':
|
|
||||||
case 'SET':
|
|
||||||
return 'String';
|
|
||||||
case 'TINYBLOB':
|
|
||||||
case 'MEDIUMBLOB':
|
|
||||||
case 'LONGBLOB':
|
|
||||||
case 'BLOB':
|
|
||||||
case 'BINARY':
|
|
||||||
case 'VARBINARY':
|
|
||||||
case 'BIT':
|
|
||||||
return 'Binary';
|
|
||||||
case 'TINYINT':
|
|
||||||
case 'SMALLINT':
|
|
||||||
case 'INT':
|
|
||||||
case 'MEDIUMINT':
|
|
||||||
case 'YEAR':
|
|
||||||
case 'FLOAT':
|
|
||||||
case 'DOUBLE':
|
|
||||||
return 'Number';
|
|
||||||
case 'DATE':
|
|
||||||
case 'TIMESTAMP':
|
|
||||||
case 'DATETIME':
|
|
||||||
return 'Date';
|
|
||||||
default:
|
|
||||||
return 'String';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discover foreign keys for a given table
|
||||||
|
* @param {String} table The table name
|
||||||
|
* @param {Object} options The options for discovery
|
||||||
|
* @param {Function} [cb] The callback function
|
||||||
|
*/
|
||||||
|
MySQL.prototype.discoverForeignKeys = function (table, options, cb) {
|
||||||
|
var args = getArgs(table, options, cb);
|
||||||
|
var owner = args.owner;
|
||||||
|
table = args.table;
|
||||||
|
options = args.options;
|
||||||
|
cb = args.cb;
|
||||||
|
|
||||||
|
var sql = queryForeignKeys(owner, table);
|
||||||
|
this.query(sql, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Retrieves a description of the foreign key columns that reference the given table's primary key columns (the foreign keys exported by a table).
|
||||||
|
* They are ordered by fkTableOwner, fkTableName, and keySeq.
|
||||||
|
* @param owner
|
||||||
|
* @param table
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function queryExportedForeignKeys(owner, table) {
|
||||||
|
var sql = 'SELECT a.constraint_name AS "fkName", a.table_schema AS "fkOwner", a.table_name AS "fkTableName",'
|
||||||
|
+ ' a.column_name AS "fkColumnName", a.ordinal_position AS "keySeq",'
|
||||||
|
+ ' NULL AS "pkName", a.referenced_table_schema AS "pkOwner",'
|
||||||
|
+ ' a.referenced_table_name AS "pkTableName", a.referenced_column_name AS "pkColumnName"'
|
||||||
|
+ ' FROM'
|
||||||
|
+ ' information_schema.key_column_usage a'
|
||||||
|
+ ' WHERE a.position_in_unique_constraint IS NOT NULL';
|
||||||
|
if (owner) {
|
||||||
|
sql += ' and a.referenced_table_schema=\'' + owner + '\'';
|
||||||
|
}
|
||||||
|
if (table) {
|
||||||
|
sql += ' and a.referenced_table_name=\'' + table + '\'';
|
||||||
|
}
|
||||||
|
sql += ' order by a.table_schema, a.table_name, a.ordinal_position';
|
||||||
|
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discover foreign keys that reference to the primary key of this table
|
||||||
|
* @param {String} table The table name
|
||||||
|
* @param {Object} options The options for discovery
|
||||||
|
* @param {Function} [cb] The callback function
|
||||||
|
*/
|
||||||
|
MySQL.prototype.discoverExportedForeignKeys = function (table, options, cb) {
|
||||||
|
var args = getArgs(table, options, cb);
|
||||||
|
var owner = args.owner;
|
||||||
|
table = args.table;
|
||||||
|
options = args.options;
|
||||||
|
cb = args.cb;
|
||||||
|
|
||||||
|
var sql = queryExportedForeignKeys(owner, table);
|
||||||
|
this.query(sql, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
function mysqlDataTypeToJSONType(mysqlType, dataLength) {
|
||||||
|
var type = mysqlType.toUpperCase();
|
||||||
|
switch (type) {
|
||||||
|
case 'CHAR':
|
||||||
|
if (dataLength === 1) {
|
||||||
|
// Treat char(1) as boolean
|
||||||
|
return 'Boolean';
|
||||||
|
} else {
|
||||||
|
return 'String';
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'VARCHAR':
|
||||||
|
case 'TINYTEXT':
|
||||||
|
case 'MEDIUMTEXT':
|
||||||
|
case 'LONGTEXT':
|
||||||
|
case 'TEXT':
|
||||||
|
case 'ENUM':
|
||||||
|
case 'SET':
|
||||||
|
return 'String';
|
||||||
|
case 'TINYBLOB':
|
||||||
|
case 'MEDIUMBLOB':
|
||||||
|
case 'LONGBLOB':
|
||||||
|
case 'BLOB':
|
||||||
|
case 'BINARY':
|
||||||
|
case 'VARBINARY':
|
||||||
|
case 'BIT':
|
||||||
|
return 'Binary';
|
||||||
|
case 'TINYINT':
|
||||||
|
case 'SMALLINT':
|
||||||
|
case 'INT':
|
||||||
|
case 'MEDIUMINT':
|
||||||
|
case 'YEAR':
|
||||||
|
case 'FLOAT':
|
||||||
|
case 'DOUBLE':
|
||||||
|
return 'Number';
|
||||||
|
case 'DATE':
|
||||||
|
case 'TIMESTAMP':
|
||||||
|
case 'DATETIME':
|
||||||
|
return 'Date';
|
||||||
|
case 'POINT':
|
||||||
|
return 'GeoPoint';
|
||||||
|
default:
|
||||||
|
return 'String';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,42 +1,42 @@
|
||||||
var EnumFactory = function() {
|
var EnumFactory = function () {
|
||||||
if(arguments.length > 0){
|
if (arguments.length > 0) {
|
||||||
var Enum = function Enum(arg){
|
var Enum = function Enum(arg) {
|
||||||
if(typeof arg === 'number' && arg % 1 == 0) {
|
if (typeof arg === 'number' && arg % 1 == 0) {
|
||||||
return Enum._values[arg];
|
return Enum._values[arg];
|
||||||
} else if(Enum[arg]){
|
} else if (Enum[arg]) {
|
||||||
return Enum[arg]
|
return Enum[arg]
|
||||||
} else if (Enum._values.indexOf(arg) !== -1 ) {
|
} else if (Enum._values.indexOf(arg) !== -1) {
|
||||||
return arg;
|
return arg;
|
||||||
} else if (arg === null) {
|
} else if (arg === null) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var dxList = [];
|
var dxList = [];
|
||||||
dxList.push(''); // Want empty value to be at index 0 to match MySQL Enum values and MySQL non-strict behavior.
|
dxList.push(''); // Want empty value to be at index 0 to match MySQL Enum values and MySQL non-strict behavior.
|
||||||
for(var arg in arguments){
|
for (var arg in arguments) {
|
||||||
arg = String(arguments[arg]);
|
arg = String(arguments[arg]);
|
||||||
Object.defineProperty(Enum, arg.toUpperCase(), {configurable: false, enumerable: true, value: arg, writable: false});
|
Object.defineProperty(Enum, arg.toUpperCase(), {configurable: false, enumerable: true, value: arg, writable: false});
|
||||||
dxList.push(arg);
|
dxList.push(arg);
|
||||||
}
|
|
||||||
Object.defineProperty(Enum, '_values', {configurable: false, enumerable: false, value: dxList, writable: false});
|
|
||||||
Object.defineProperty(Enum, '_string', {configurable: false, enumerable: false, value: stringified(Enum), writable: false});
|
|
||||||
Object.freeze(Enum);
|
|
||||||
return Enum;
|
|
||||||
} else {
|
|
||||||
throw "No arguments - could not create Enum.";
|
|
||||||
}
|
}
|
||||||
|
Object.defineProperty(Enum, '_values', {configurable: false, enumerable: false, value: dxList, writable: false});
|
||||||
|
Object.defineProperty(Enum, '_string', {configurable: false, enumerable: false, value: stringified(Enum), writable: false});
|
||||||
|
Object.freeze(Enum);
|
||||||
|
return Enum;
|
||||||
|
} else {
|
||||||
|
throw "No arguments - could not create Enum.";
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function stringified(anEnum) {
|
function stringified(anEnum) {
|
||||||
var s = [];
|
var s = [];
|
||||||
for(var i in anEnum._values){
|
for (var i in anEnum._values) {
|
||||||
if(anEnum._values[i] != ''){
|
if (anEnum._values[i] != '') {
|
||||||
s.push("'" + anEnum._values[i] + "'");
|
s.push("'" + anEnum._values[i] + "'");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return s.join(',');
|
}
|
||||||
|
return s.join(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.EnumFactory = EnumFactory;
|
exports.EnumFactory = EnumFactory;
|
||||||
|
|
1668
lib/mysql.js
1668
lib/mysql.js
File diff suppressed because it is too large
Load Diff
19
package.json
19
package.json
|
@ -1,27 +1,28 @@
|
||||||
{
|
{
|
||||||
"name": "loopback-connector-mysql",
|
"name": "loopback-connector-mysql",
|
||||||
"version": "1.1.1",
|
"version": "1.2.0",
|
||||||
"description": "MySQL connector for loopback-datasource-juggler",
|
"description": "MySQL connector for loopback-datasource-juggler",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "make test"
|
"test": "mocha"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"mysql": "~2.0.0-rc2",
|
"mysql": "~2.1.1",
|
||||||
"async": "~0.2.9"
|
"async": "~0.7.0",
|
||||||
},
|
"debug": "~0.8.0"
|
||||||
"devDependencies": {
|
|
||||||
"loopback-datasource-juggler": "1.x.x"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"loopback-datasource-juggler": "1.x.x",
|
"loopback-datasource-juggler": "1.x.x",
|
||||||
"should": "~1.3.0",
|
"should": "~1.3.0",
|
||||||
"mocha": "~1.14.0",
|
"mocha": "~1.18.0",
|
||||||
"rc": "~0.3.1"
|
"rc": "~0.3.1"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/strongloop/loopback-connector-mysql.git"
|
"url": "https://github.com/strongloop/loopback-connector-mysql.git"
|
||||||
},
|
},
|
||||||
"license": "MIT"
|
"license": {
|
||||||
|
"name": "Dual MIT/StrongLoop",
|
||||||
|
"url": "https://github.com/strongloop/loopback-connector-mysql/blob/master/LICENSE"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,105 +3,102 @@ var assert = require('assert');
|
||||||
|
|
||||||
var db, DummyModel, odb;
|
var db, DummyModel, odb;
|
||||||
|
|
||||||
describe('migrations', function() {
|
describe('migrations', function () {
|
||||||
|
|
||||||
before(function() {
|
before(function () {
|
||||||
require('./init.js');
|
require('./init.js');
|
||||||
|
|
||||||
odb = getDataSource({collation: 'utf8_general_ci', createDatabase: true});
|
|
||||||
db = odb;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should use utf8 charset', function(done) {
|
|
||||||
|
|
||||||
var test_set = /utf8/;
|
|
||||||
var test_collo = /utf8_general_ci/;
|
|
||||||
var test_set_str = 'utf8';
|
|
||||||
var test_set_collo = 'utf8_general_ci';
|
|
||||||
charsetTest(test_set, test_collo, test_set_str, test_set_collo, done);
|
|
||||||
|
|
||||||
|
odb = getDataSource({collation: 'utf8_general_ci', createDatabase: true});
|
||||||
|
db = odb;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use utf8 charset', function (done) {
|
||||||
|
|
||||||
|
var test_set = /utf8/;
|
||||||
|
var test_collo = /utf8_general_ci/;
|
||||||
|
var test_set_str = 'utf8';
|
||||||
|
var test_set_collo = 'utf8_general_ci';
|
||||||
|
charsetTest(test_set, test_collo, test_set_str, test_set_collo, done);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should disconnect first db', function (done) {
|
||||||
|
db.client.end(function () {
|
||||||
|
odb = getSchema();
|
||||||
|
done()
|
||||||
});
|
});
|
||||||
|
});
|
||||||
it('should disconnect first db', function(done) {
|
|
||||||
db.client.end(function(){
|
it('should use latin1 charset', function (done) {
|
||||||
odb = getSchema();
|
|
||||||
done()
|
var test_set = /latin1/;
|
||||||
});
|
var test_collo = /latin1_general_ci/;
|
||||||
});
|
var test_set_str = 'latin1';
|
||||||
|
var test_set_collo = 'latin1_general_ci';
|
||||||
it('should use latin1 charset', function(done) {
|
charsetTest(test_set, test_collo, test_set_str, test_set_collo, done);
|
||||||
|
|
||||||
var test_set = /latin1/;
|
});
|
||||||
var test_collo = /latin1_general_ci/;
|
|
||||||
var test_set_str = 'latin1';
|
it('should drop db and disconnect all', function (done) {
|
||||||
var test_set_collo = 'latin1_general_ci';
|
db.connector.query('DROP DATABASE IF EXISTS ' + db.settings.database, function (err) {
|
||||||
charsetTest(test_set, test_collo, test_set_str, test_set_collo, done);
|
db.client.end(function () {
|
||||||
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should drop db and disconnect all', function(done) {
|
|
||||||
db.connector.query('DROP DATABASE IF EXISTS ' + db.settings.database, function(err) {
|
|
||||||
db.client.end(function(){
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function charsetTest(test_set, test_collo, test_set_str, test_set_collo, done) {
|
||||||
|
|
||||||
|
query('DROP DATABASE IF EXISTS ' + odb.settings.database, function (err) {
|
||||||
|
assert.ok(!err);
|
||||||
|
odb.client.end(function () {
|
||||||
|
|
||||||
function charsetTest(test_set, test_collo, test_set_str, test_set_collo, done){
|
db = getSchema({collation: test_set_collo, createDatabase: true});
|
||||||
|
DummyModel = db.define('DummyModel', {string: String});
|
||||||
query('DROP DATABASE IF EXISTS ' + odb.settings.database, function(err) {
|
db.automigrate(function () {
|
||||||
assert.ok(!err);
|
var q = 'SELECT DEFAULT_COLLATION_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = ' + db.client.escape(db.settings.database) + ' LIMIT 1';
|
||||||
odb.client.end(function(){
|
db.connector.query(q, function (err, r) {
|
||||||
|
assert.ok(!err);
|
||||||
db = getSchema({collation: test_set_collo, createDatabase: true});
|
assert.ok(r[0].DEFAULT_COLLATION_NAME.match(test_collo));
|
||||||
DummyModel = db.define('DummyModel', {string: String});
|
db.connector.query('SHOW VARIABLES LIKE "character_set%"', function (err, r) {
|
||||||
db.automigrate(function(){
|
assert.ok(!err);
|
||||||
var q = 'SELECT DEFAULT_COLLATION_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = ' + db.client.escape(db.settings.database) + ' LIMIT 1';
|
var hit_all = 0;
|
||||||
db.connector.query(q, function(err, r) {
|
for (var result in r) {
|
||||||
assert.ok(!err);
|
hit_all += matchResult(r[result], 'character_set_connection', test_set);
|
||||||
assert.ok(r[0].DEFAULT_COLLATION_NAME.match(test_collo));
|
hit_all += matchResult(r[result], 'character_set_database', test_set);
|
||||||
db.connector.query('SHOW VARIABLES LIKE "character_set%"', function(err, r){
|
hit_all += matchResult(r[result], 'character_set_results', test_set);
|
||||||
assert.ok(!err);
|
hit_all += matchResult(r[result], 'character_set_client', test_set);
|
||||||
var hit_all = 0;
|
}
|
||||||
for (var result in r) {
|
assert.equal(hit_all, 4);
|
||||||
hit_all += matchResult(r[result], 'character_set_connection', test_set);
|
});
|
||||||
hit_all += matchResult(r[result], 'character_set_database', test_set);
|
db.connector.query('SHOW VARIABLES LIKE "collation%"', function (err, r) {
|
||||||
hit_all += matchResult(r[result], 'character_set_results', test_set);
|
assert.ok(!err);
|
||||||
hit_all += matchResult(r[result], 'character_set_client', test_set);
|
var hit_all = 0;
|
||||||
}
|
for (var result in r) {
|
||||||
assert.equal(hit_all, 4);
|
hit_all += matchResult(r[result], 'collation_connection', test_set);
|
||||||
});
|
hit_all += matchResult(r[result], 'collation_database', test_set);
|
||||||
db.connector.query('SHOW VARIABLES LIKE "collation%"', function(err, r){
|
}
|
||||||
assert.ok(!err);
|
assert.equal(hit_all, 2);
|
||||||
var hit_all = 0;
|
done();
|
||||||
for (var result in r) {
|
});
|
||||||
hit_all += matchResult(r[result], 'collation_connection', test_set);
|
|
||||||
hit_all += matchResult(r[result], 'collation_database', test_set);
|
|
||||||
}
|
|
||||||
assert.equal(hit_all, 2);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function matchResult(result, variable_name, match) {
|
function matchResult(result, variable_name, match) {
|
||||||
if(result.Variable_name == variable_name){
|
if (result.Variable_name == variable_name) {
|
||||||
assert.ok(result.Value.match(match));
|
assert.ok(result.Value.match(match));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
var query = function (sql, cb) {
|
var query = function (sql, cb) {
|
||||||
odb.connector.query(sql, cb);
|
odb.connector.query(sql, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,129 +3,129 @@ var assert = require('assert');
|
||||||
|
|
||||||
var db, EnumModel, ANIMAL_ENUM;
|
var db, EnumModel, ANIMAL_ENUM;
|
||||||
|
|
||||||
describe('MySQL specific datatypes', function() {
|
describe('MySQL specific datatypes', function () {
|
||||||
|
|
||||||
before(setup);
|
before(setup);
|
||||||
|
|
||||||
it('should run migration', function(done) {
|
it('should run migration', function (done) {
|
||||||
db.automigrate(function(){
|
db.automigrate(function () {
|
||||||
done();
|
done();
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
it('An enum should parse itself', function(done) {
|
|
||||||
assert.equal(ANIMAL_ENUM.CAT, ANIMAL_ENUM('cat'));
|
it('An enum should parse itself', function (done) {
|
||||||
assert.equal(ANIMAL_ENUM.CAT, ANIMAL_ENUM('CAT'));
|
assert.equal(ANIMAL_ENUM.CAT, ANIMAL_ENUM('cat'));
|
||||||
assert.equal(ANIMAL_ENUM.CAT, ANIMAL_ENUM(2));
|
assert.equal(ANIMAL_ENUM.CAT, ANIMAL_ENUM('CAT'));
|
||||||
assert.equal(ANIMAL_ENUM.CAT, 'cat');
|
assert.equal(ANIMAL_ENUM.CAT, ANIMAL_ENUM(2));
|
||||||
assert.equal(ANIMAL_ENUM(null), null);
|
assert.equal(ANIMAL_ENUM.CAT, 'cat');
|
||||||
assert.equal(ANIMAL_ENUM(''), '');
|
assert.equal(ANIMAL_ENUM(null), null);
|
||||||
assert.equal(ANIMAL_ENUM(0), '');
|
assert.equal(ANIMAL_ENUM(''), '');
|
||||||
|
assert.equal(ANIMAL_ENUM(0), '');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create a model instance with Enums', function (done) {
|
||||||
|
var em = EnumModel.create({animal: ANIMAL_ENUM.CAT, condition: 'sleepy', mood: 'happy'}, function (err, obj) {
|
||||||
|
assert.ok(!err);
|
||||||
|
assert.equal(obj.condition, 'sleepy');
|
||||||
|
EnumModel.findOne({where: {animal: ANIMAL_ENUM.CAT}}, function (err, found) {
|
||||||
|
assert.ok(!err);
|
||||||
|
assert.equal(found.mood, 'happy');
|
||||||
|
assert.equal(found.animal, ANIMAL_ENUM.CAT);
|
||||||
done();
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
it('should create a model instance with Enums', function(done) {
|
|
||||||
var em = EnumModel.create({animal: ANIMAL_ENUM.CAT, condition: 'sleepy', mood: 'happy'}, function(err, obj) {
|
it('should fail spectacularly with invalid enum values', function (done) {
|
||||||
assert.ok(!err);
|
var em = EnumModel.create({animal: 'horse', condition: 'sleepy', mood: 'happy'}, function (err, obj) {
|
||||||
assert.equal(obj.condition, 'sleepy');
|
assert.ok(!err);
|
||||||
EnumModel.findOne({where: {animal: ANIMAL_ENUM.CAT}}, function(err, found){
|
EnumModel.findById(obj.id, function (err, found) {
|
||||||
assert.ok(!err);
|
assert.ok(!err);
|
||||||
assert.equal(found.mood, 'happy');
|
assert.equal(found.animal, ''); // MySQL fun.
|
||||||
assert.equal(found.animal, ANIMAL_ENUM.CAT);
|
assert.equal(found.animal, 0);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should fail spectacularly with invalid enum values', function(done) {
|
|
||||||
var em = EnumModel.create({animal: 'horse', condition: 'sleepy', mood: 'happy'}, function(err, obj) {
|
|
||||||
assert.ok(!err);
|
|
||||||
EnumModel.findById(obj.id, function(err, found){
|
|
||||||
assert.ok(!err);
|
|
||||||
assert.equal(found.animal, ''); // MySQL fun.
|
|
||||||
assert.equal(found.animal, 0);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should disconnect when done', function(done) {
|
|
||||||
db.disconnect();
|
|
||||||
done()
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should disconnect when done', function (done) {
|
||||||
|
db.disconnect();
|
||||||
|
done()
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function setup(done) {
|
function setup(done) {
|
||||||
|
|
||||||
require('./init.js');
|
require('./init.js');
|
||||||
|
|
||||||
db = getSchema();
|
|
||||||
|
|
||||||
ANIMAL_ENUM = db.EnumFactory('dog', 'cat', 'mouse');
|
|
||||||
|
|
||||||
EnumModel = db.define('EnumModel', {
|
|
||||||
animal: { type: ANIMAL_ENUM, null: false },
|
|
||||||
condition: { type: db.EnumFactory('hungry', 'sleepy', 'thirsty') },
|
|
||||||
mood: { type: db.EnumFactory('angry', 'happy', 'sad') }
|
|
||||||
});
|
|
||||||
|
|
||||||
blankDatabase(db, done);
|
db = getSchema();
|
||||||
|
|
||||||
|
ANIMAL_ENUM = db.EnumFactory('dog', 'cat', 'mouse');
|
||||||
|
|
||||||
|
EnumModel = db.define('EnumModel', {
|
||||||
|
animal: { type: ANIMAL_ENUM, null: false },
|
||||||
|
condition: { type: db.EnumFactory('hungry', 'sleepy', 'thirsty') },
|
||||||
|
mood: { type: db.EnumFactory('angry', 'happy', 'sad') }
|
||||||
|
});
|
||||||
|
|
||||||
|
blankDatabase(db, done);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var query = function (sql, cb) {
|
var query = function (sql, cb) {
|
||||||
db.adapter.query(sql, cb);
|
db.adapter.query(sql, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
var blankDatabase = function (db, cb) {
|
var blankDatabase = function (db, cb) {
|
||||||
var dbn = db.settings.database;
|
var dbn = db.settings.database;
|
||||||
var cs = db.settings.charset;
|
var cs = db.settings.charset;
|
||||||
var co = db.settings.collation;
|
var co = db.settings.collation;
|
||||||
query('DROP DATABASE IF EXISTS ' + dbn, function(err) {
|
query('DROP DATABASE IF EXISTS ' + dbn, function (err) {
|
||||||
var q = 'CREATE DATABASE ' + dbn;
|
var q = 'CREATE DATABASE ' + dbn;
|
||||||
if(cs){
|
if (cs) {
|
||||||
q += ' CHARACTER SET ' + cs;
|
q += ' CHARACTER SET ' + cs;
|
||||||
}
|
}
|
||||||
if(co){
|
if (co) {
|
||||||
q += ' COLLATE ' + co;
|
q += ' COLLATE ' + co;
|
||||||
}
|
}
|
||||||
query(q, function(err) {
|
query(q, function (err) {
|
||||||
query('USE '+ dbn, cb);
|
query('USE ' + dbn, cb);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
getFields = function (model, cb) {
|
getFields = function (model, cb) {
|
||||||
query('SHOW FIELDS FROM ' + model, function(err, res) {
|
query('SHOW FIELDS FROM ' + model, function (err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
cb(err);
|
cb(err);
|
||||||
} else {
|
} else {
|
||||||
var fields = {};
|
var fields = {};
|
||||||
res.forEach(function(field){
|
res.forEach(function (field) {
|
||||||
fields[field.Field] = field;
|
fields[field.Field] = field;
|
||||||
});
|
});
|
||||||
cb(err, fields);
|
cb(err, fields);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getIndexes = function (model, cb) {
|
getIndexes = function (model, cb) {
|
||||||
query('SHOW INDEXES FROM ' + model, function(err, res) {
|
query('SHOW INDEXES FROM ' + model, function (err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
cb(err);
|
cb(err);
|
||||||
} else {
|
} else {
|
||||||
var indexes = {};
|
var indexes = {};
|
||||||
// Note: this will only show the first key of compound keys
|
// Note: this will only show the first key of compound keys
|
||||||
res.forEach(function(index) {
|
res.forEach(function (index) {
|
||||||
if (parseInt(index.Seq_in_index, 10) == 1) {
|
if (parseInt(index.Seq_in_index, 10) == 1) {
|
||||||
indexes[index.Key_name] = index
|
indexes[index.Key_name] = index
|
||||||
}
|
|
||||||
});
|
|
||||||
cb(err, indexes);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
cb(err, indexes);
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
describe('mysql imported features', function() {
|
describe('mysql imported features', function () {
|
||||||
|
|
||||||
before(function() {
|
before(function () {
|
||||||
require('./init.js');
|
require('./init.js');
|
||||||
});
|
});
|
||||||
|
|
||||||
require('loopback-datasource-juggler/test/common.batch.js');
|
require('loopback-datasource-juggler/test/common.batch.js');
|
||||||
require('loopback-datasource-juggler/test/include.test.js');
|
require('loopback-datasource-juggler/test/include.test.js');
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
34
test/init.js
34
test/init.js
|
@ -4,29 +4,29 @@ var DataSource = require('loopback-datasource-juggler').DataSource;
|
||||||
|
|
||||||
var config = require('rc')('loopback', {test: {mysql: {}}}).test.mysql;
|
var config = require('rc')('loopback', {test: {mysql: {}}}).test.mysql;
|
||||||
|
|
||||||
global.getConfig = function(options) {
|
global.getConfig = function (options) {
|
||||||
|
|
||||||
var dbConf = {
|
var dbConf = {
|
||||||
host: config.host || 'localhost',
|
host: config.host || 'localhost',
|
||||||
port: config.port || 3306,
|
port: config.port || 3306,
|
||||||
database: 'myapp_test',
|
database: 'myapp_test',
|
||||||
username: config.username,
|
username: config.username,
|
||||||
password: config.password,
|
password: config.password,
|
||||||
createDatabase: true
|
createDatabase: true
|
||||||
};
|
};
|
||||||
|
|
||||||
if (options) {
|
if (options) {
|
||||||
for (var el in options) {
|
for (var el in options) {
|
||||||
dbConf[el] = options[el];
|
dbConf[el] = options[el];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return dbConf;
|
return dbConf;
|
||||||
};
|
};
|
||||||
|
|
||||||
global.getDataSource = global.getSchema = function(options) {
|
global.getDataSource = global.getSchema = function (options) {
|
||||||
var db = new DataSource(require('../'), getConfig(options));
|
var db = new DataSource(require('../'), getConfig(options));
|
||||||
return db;
|
return db;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,428 +2,413 @@ var should = require('./init.js');
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var Schema = require('loopback-datasource-juggler').Schema;
|
var Schema = require('loopback-datasource-juggler').Schema;
|
||||||
|
|
||||||
var db, UserData, StringData, NumberData, DateData;
|
var db, UserData, StringData, NumberData, DateData;
|
||||||
|
|
||||||
describe('migrations', function() {
|
describe('migrations', function () {
|
||||||
|
|
||||||
before(setup);
|
|
||||||
|
|
||||||
it('should run migration', function(done) {
|
|
||||||
db.automigrate(function(){
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('UserData should have correct columns', function(done) {
|
|
||||||
getFields('UserData', function(err, fields) {
|
|
||||||
assert.deepEqual(fields, {
|
|
||||||
id: {
|
|
||||||
Field: 'id',
|
|
||||||
Type: 'int(11)',
|
|
||||||
Null: 'NO',
|
|
||||||
Key: 'PRI',
|
|
||||||
Default: null,
|
|
||||||
Extra: 'auto_increment' },
|
|
||||||
email: {
|
|
||||||
Field: 'email',
|
|
||||||
Type: 'varchar(255)',
|
|
||||||
Null: 'NO',
|
|
||||||
Key: 'MUL',
|
|
||||||
Default: null,
|
|
||||||
Extra: '' },
|
|
||||||
name: {
|
|
||||||
Field: 'name',
|
|
||||||
Type: 'varchar(255)',
|
|
||||||
Null: 'YES',
|
|
||||||
Key: '',
|
|
||||||
Default: null,
|
|
||||||
Extra: '' },
|
|
||||||
bio: {
|
|
||||||
Field: 'bio',
|
|
||||||
Type: 'text',
|
|
||||||
Null: 'YES',
|
|
||||||
Key: '',
|
|
||||||
Default: null,
|
|
||||||
Extra: '' },
|
|
||||||
birthDate: {
|
|
||||||
Field: 'birthDate',
|
|
||||||
Type: 'datetime',
|
|
||||||
Null: 'YES',
|
|
||||||
Key: '',
|
|
||||||
Default: null,
|
|
||||||
Extra: '' },
|
|
||||||
pendingPeriod: {
|
|
||||||
Field: 'pendingPeriod',
|
|
||||||
Type: 'int(11)',
|
|
||||||
Null: 'YES',
|
|
||||||
Key: '',
|
|
||||||
Default: null,
|
|
||||||
Extra: '' },
|
|
||||||
createdByAdmin: {
|
|
||||||
Field: 'createdByAdmin',
|
|
||||||
Type: 'tinyint(1)',
|
|
||||||
Null: 'YES',
|
|
||||||
Key: '',
|
|
||||||
Default: null,
|
|
||||||
Extra: '' }
|
|
||||||
});
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('UserData should have correct indexes', function(done) {
|
|
||||||
// Note: getIndexes truncates multi-key indexes to the first member. Hence index1 is correct.
|
|
||||||
getIndexes('UserData', function(err, fields) {
|
|
||||||
// console.log('....', fields);
|
|
||||||
assert.deepEqual(fields, { PRIMARY:
|
|
||||||
{ Table: 'UserData',
|
|
||||||
Non_unique: 0,
|
|
||||||
Key_name: 'PRIMARY',
|
|
||||||
Seq_in_index: 1,
|
|
||||||
Column_name: 'id',
|
|
||||||
Collation: 'A',
|
|
||||||
Cardinality: 0,
|
|
||||||
Sub_part: null,
|
|
||||||
Packed: null,
|
|
||||||
Null: '',
|
|
||||||
Index_type: 'BTREE',
|
|
||||||
Comment: '' },
|
|
||||||
email:
|
|
||||||
{ Table: 'UserData',
|
|
||||||
Non_unique: 1,
|
|
||||||
Key_name: 'email',
|
|
||||||
Seq_in_index: 1,
|
|
||||||
Column_name: 'email',
|
|
||||||
Collation: 'A',
|
|
||||||
Cardinality: null,
|
|
||||||
Sub_part: null,
|
|
||||||
Packed: null,
|
|
||||||
Null: '',
|
|
||||||
Index_type: 'BTREE',
|
|
||||||
Comment: '' },
|
|
||||||
index0:
|
|
||||||
{ Table: 'UserData',
|
|
||||||
Non_unique: 1,
|
|
||||||
Key_name: 'index0',
|
|
||||||
Seq_in_index: 1,
|
|
||||||
Column_name: 'email',
|
|
||||||
Collation: 'A',
|
|
||||||
Cardinality: null,
|
|
||||||
Sub_part: null,
|
|
||||||
Packed: null,
|
|
||||||
Null: '',
|
|
||||||
Index_type: 'BTREE',
|
|
||||||
Comment: '' }
|
|
||||||
});
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('StringData should have correct columns', function(done) {
|
|
||||||
getFields('StringData', function(err, fields) {
|
|
||||||
assert.deepEqual(fields, { id:
|
|
||||||
{ Field: 'id',
|
|
||||||
Type: 'int(11)',
|
|
||||||
Null: 'NO',
|
|
||||||
Key: 'PRI',
|
|
||||||
Default: null,
|
|
||||||
Extra: 'auto_increment' },
|
|
||||||
smallString:
|
|
||||||
{ Field: 'smallString',
|
|
||||||
Type: 'char(127)',
|
|
||||||
Null: 'NO',
|
|
||||||
Key: 'MUL',
|
|
||||||
Default: null,
|
|
||||||
Extra: '' },
|
|
||||||
mediumString:
|
|
||||||
{ Field: 'mediumString',
|
|
||||||
Type: 'varchar(255)',
|
|
||||||
Null: 'NO',
|
|
||||||
Key: '',
|
|
||||||
Default: null,
|
|
||||||
Extra: '' },
|
|
||||||
tinyText:
|
|
||||||
{ Field: 'tinyText',
|
|
||||||
Type: 'tinytext',
|
|
||||||
Null: 'YES',
|
|
||||||
Key: '',
|
|
||||||
Default: null,
|
|
||||||
Extra: '' },
|
|
||||||
giantJSON:
|
|
||||||
{ Field: 'giantJSON',
|
|
||||||
Type: 'longtext',
|
|
||||||
Null: 'YES',
|
|
||||||
Key: '',
|
|
||||||
Default: null,
|
|
||||||
Extra: '' },
|
|
||||||
text:
|
|
||||||
{ Field: 'text',
|
|
||||||
Type: 'varchar(1024)',
|
|
||||||
Null: 'YES',
|
|
||||||
Key: '',
|
|
||||||
Default: null,
|
|
||||||
Extra: '' }
|
|
||||||
});
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('NumberData should have correct columns', function(done) {
|
|
||||||
getFields('NumberData', function(err, fields) {
|
|
||||||
assert.deepEqual(fields, {
|
|
||||||
id:
|
|
||||||
{ Field: 'id',
|
|
||||||
Type: 'int(11)',
|
|
||||||
Null: 'NO',
|
|
||||||
Key: 'PRI',
|
|
||||||
Default: null,
|
|
||||||
Extra: 'auto_increment' },
|
|
||||||
number:
|
|
||||||
{ Field: 'number',
|
|
||||||
Type: 'decimal(10,3) unsigned',
|
|
||||||
Null: 'NO',
|
|
||||||
Key: 'MUL',
|
|
||||||
Default: null,
|
|
||||||
Extra: '' },
|
|
||||||
tinyInt:
|
|
||||||
{ Field: 'tinyInt',
|
|
||||||
Type: 'tinyint(2)',
|
|
||||||
Null: 'YES',
|
|
||||||
Key: '',
|
|
||||||
Default: null,
|
|
||||||
Extra: '' },
|
|
||||||
mediumInt:
|
|
||||||
{ Field: 'mediumInt',
|
|
||||||
Type: 'mediumint(8) unsigned',
|
|
||||||
Null: 'YES',
|
|
||||||
Key: '',
|
|
||||||
Default: null,
|
|
||||||
Extra: '' },
|
|
||||||
floater:
|
|
||||||
{ Field: 'floater',
|
|
||||||
Type: 'double(14,6)',
|
|
||||||
Null: 'YES',
|
|
||||||
Key: '',
|
|
||||||
Default: null,
|
|
||||||
Extra: '' }
|
|
||||||
});
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('DateData should have correct columns', function(done) {
|
before(setup);
|
||||||
getFields('DateData', function(err, fields) {
|
|
||||||
assert.deepEqual(fields, {
|
it('should run migration', function (done) {
|
||||||
id:
|
db.automigrate(function () {
|
||||||
{ Field: 'id',
|
done();
|
||||||
Type: 'int(11)',
|
});
|
||||||
Null: 'NO',
|
});
|
||||||
Key: 'PRI',
|
|
||||||
Default: null,
|
it('UserData should have correct columns', function (done) {
|
||||||
Extra: 'auto_increment' },
|
getFields('UserData', function (err, fields) {
|
||||||
dateTime:
|
assert.deepEqual(fields, {
|
||||||
{ Field: 'dateTime',
|
id: {
|
||||||
Type: 'datetime',
|
Field: 'id',
|
||||||
Null: 'YES',
|
Type: 'int(11)',
|
||||||
Key: '',
|
Null: 'NO',
|
||||||
Default: null,
|
Key: 'PRI',
|
||||||
Extra: '' },
|
Default: null,
|
||||||
timestamp:
|
Extra: 'auto_increment' },
|
||||||
{ Field: 'timestamp',
|
email: {
|
||||||
Type: 'timestamp',
|
Field: 'email',
|
||||||
Null: 'YES',
|
Type: 'varchar(255)',
|
||||||
Key: '',
|
Null: 'NO',
|
||||||
Default: null,
|
Key: 'MUL',
|
||||||
Extra: '' }
|
Default: null,
|
||||||
});
|
Extra: '' },
|
||||||
|
name: {
|
||||||
|
Field: 'name',
|
||||||
|
Type: 'varchar(255)',
|
||||||
|
Null: 'YES',
|
||||||
|
Key: '',
|
||||||
|
Default: null,
|
||||||
|
Extra: '' },
|
||||||
|
bio: {
|
||||||
|
Field: 'bio',
|
||||||
|
Type: 'text',
|
||||||
|
Null: 'YES',
|
||||||
|
Key: '',
|
||||||
|
Default: null,
|
||||||
|
Extra: '' },
|
||||||
|
birthDate: {
|
||||||
|
Field: 'birthDate',
|
||||||
|
Type: 'datetime',
|
||||||
|
Null: 'YES',
|
||||||
|
Key: '',
|
||||||
|
Default: null,
|
||||||
|
Extra: '' },
|
||||||
|
pendingPeriod: {
|
||||||
|
Field: 'pendingPeriod',
|
||||||
|
Type: 'int(11)',
|
||||||
|
Null: 'YES',
|
||||||
|
Key: '',
|
||||||
|
Default: null,
|
||||||
|
Extra: '' },
|
||||||
|
createdByAdmin: {
|
||||||
|
Field: 'createdByAdmin',
|
||||||
|
Type: 'tinyint(1)',
|
||||||
|
Null: 'YES',
|
||||||
|
Key: '',
|
||||||
|
Default: null,
|
||||||
|
Extra: '' }
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('UserData should have correct indexes', function (done) {
|
||||||
|
// Note: getIndexes truncates multi-key indexes to the first member. Hence index1 is correct.
|
||||||
|
getIndexes('UserData', function (err, fields) {
|
||||||
|
// console.log('....', fields);
|
||||||
|
assert.deepEqual(fields, { PRIMARY: { Table: 'UserData',
|
||||||
|
Non_unique: 0,
|
||||||
|
Key_name: 'PRIMARY',
|
||||||
|
Seq_in_index: 1,
|
||||||
|
Column_name: 'id',
|
||||||
|
Collation: 'A',
|
||||||
|
Cardinality: 0,
|
||||||
|
Sub_part: null,
|
||||||
|
Packed: null,
|
||||||
|
Null: '',
|
||||||
|
Index_type: 'BTREE',
|
||||||
|
Comment: '' },
|
||||||
|
email: { Table: 'UserData',
|
||||||
|
Non_unique: 1,
|
||||||
|
Key_name: 'email',
|
||||||
|
Seq_in_index: 1,
|
||||||
|
Column_name: 'email',
|
||||||
|
Collation: 'A',
|
||||||
|
Cardinality: null,
|
||||||
|
Sub_part: null,
|
||||||
|
Packed: null,
|
||||||
|
Null: '',
|
||||||
|
Index_type: 'BTREE',
|
||||||
|
Comment: '' },
|
||||||
|
index0: { Table: 'UserData',
|
||||||
|
Non_unique: 1,
|
||||||
|
Key_name: 'index0',
|
||||||
|
Seq_in_index: 1,
|
||||||
|
Column_name: 'email',
|
||||||
|
Collation: 'A',
|
||||||
|
Cardinality: null,
|
||||||
|
Sub_part: null,
|
||||||
|
Packed: null,
|
||||||
|
Null: '',
|
||||||
|
Index_type: 'BTREE',
|
||||||
|
Comment: '' }
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('StringData should have correct columns', function (done) {
|
||||||
|
getFields('StringData', function (err, fields) {
|
||||||
|
assert.deepEqual(fields, {
|
||||||
|
idString: { Field: "idString",
|
||||||
|
Type: 'varchar(255)',
|
||||||
|
Null: 'NO',
|
||||||
|
Key: 'PRI',
|
||||||
|
Default: null,
|
||||||
|
Extra: ''},
|
||||||
|
smallString: { Field: 'smallString',
|
||||||
|
Type: 'char(127)',
|
||||||
|
Null: 'NO',
|
||||||
|
Key: 'MUL',
|
||||||
|
Default: null,
|
||||||
|
Extra: '' },
|
||||||
|
mediumString: { Field: 'mediumString',
|
||||||
|
Type: 'varchar(255)',
|
||||||
|
Null: 'NO',
|
||||||
|
Key: '',
|
||||||
|
Default: null,
|
||||||
|
Extra: '' },
|
||||||
|
tinyText: { Field: 'tinyText',
|
||||||
|
Type: 'tinytext',
|
||||||
|
Null: 'YES',
|
||||||
|
Key: '',
|
||||||
|
Default: null,
|
||||||
|
Extra: '' },
|
||||||
|
giantJSON: { Field: 'giantJSON',
|
||||||
|
Type: 'longtext',
|
||||||
|
Null: 'YES',
|
||||||
|
Key: '',
|
||||||
|
Default: null,
|
||||||
|
Extra: '' },
|
||||||
|
text: { Field: 'text',
|
||||||
|
Type: 'varchar(1024)',
|
||||||
|
Null: 'YES',
|
||||||
|
Key: '',
|
||||||
|
Default: null,
|
||||||
|
Extra: '' }
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('NumberData should have correct columns', function (done) {
|
||||||
|
getFields('NumberData', function (err, fields) {
|
||||||
|
assert.deepEqual(fields, {
|
||||||
|
id: { Field: 'id',
|
||||||
|
Type: 'int(11)',
|
||||||
|
Null: 'NO',
|
||||||
|
Key: 'PRI',
|
||||||
|
Default: null,
|
||||||
|
Extra: 'auto_increment' },
|
||||||
|
number: { Field: 'number',
|
||||||
|
Type: 'decimal(10,3) unsigned',
|
||||||
|
Null: 'NO',
|
||||||
|
Key: 'MUL',
|
||||||
|
Default: null,
|
||||||
|
Extra: '' },
|
||||||
|
tinyInt: { Field: 'tinyInt',
|
||||||
|
Type: 'tinyint(2)',
|
||||||
|
Null: 'YES',
|
||||||
|
Key: '',
|
||||||
|
Default: null,
|
||||||
|
Extra: '' },
|
||||||
|
mediumInt: { Field: 'mediumInt',
|
||||||
|
Type: 'mediumint(8) unsigned',
|
||||||
|
Null: 'YES',
|
||||||
|
Key: '',
|
||||||
|
Default: null,
|
||||||
|
Extra: '' },
|
||||||
|
floater: { Field: 'floater',
|
||||||
|
Type: 'double(14,6)',
|
||||||
|
Null: 'YES',
|
||||||
|
Key: '',
|
||||||
|
Default: null,
|
||||||
|
Extra: '' }
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('DateData should have correct columns', function (done) {
|
||||||
|
getFields('DateData', function (err, fields) {
|
||||||
|
assert.deepEqual(fields, {
|
||||||
|
id: { Field: 'id',
|
||||||
|
Type: 'int(11)',
|
||||||
|
Null: 'NO',
|
||||||
|
Key: 'PRI',
|
||||||
|
Default: null,
|
||||||
|
Extra: 'auto_increment' },
|
||||||
|
dateTime: { Field: 'dateTime',
|
||||||
|
Type: 'datetime',
|
||||||
|
Null: 'YES',
|
||||||
|
Key: '',
|
||||||
|
Default: null,
|
||||||
|
Extra: '' },
|
||||||
|
timestamp: { Field: 'timestamp',
|
||||||
|
Type: 'timestamp',
|
||||||
|
Null: 'YES',
|
||||||
|
Key: '',
|
||||||
|
Default: null,
|
||||||
|
Extra: '' }
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should autoupdate', function (done) {
|
||||||
|
var userExists = function (cb) {
|
||||||
|
query('SELECT * FROM UserData', function (err, res) {
|
||||||
|
cb(!err && res[0].email == 'test@example.com');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
UserData.create({email: 'test@example.com'}, function (err, user) {
|
||||||
|
assert.ok(!err, 'Could not create user: ' + err);
|
||||||
|
userExists(function (yep) {
|
||||||
|
assert.ok(yep, 'User does not exist');
|
||||||
|
});
|
||||||
|
UserData.defineProperty('email', { type: String });
|
||||||
|
UserData.defineProperty('name', {type: String, dataType: 'char', limit: 50});
|
||||||
|
UserData.defineProperty('newProperty', {type: Number, unsigned: true, dataType: 'bigInt'});
|
||||||
|
// UserData.defineProperty('pendingPeriod', false); This will not work as expected.
|
||||||
|
db.autoupdate(function (err) {
|
||||||
|
getFields('UserData', function (err, fields) {
|
||||||
|
// change nullable for email
|
||||||
|
assert.equal(fields.email.Null, 'YES', 'Email does not allow null');
|
||||||
|
// change type of name
|
||||||
|
assert.equal(fields.name.Type, 'char(50)', 'Name is not char(50)');
|
||||||
|
// add new column
|
||||||
|
assert.ok(fields.newProperty, 'New column was not added');
|
||||||
|
if (fields.newProperty) {
|
||||||
|
assert.equal(fields.newProperty.Type, 'bigint(20) unsigned', 'New column type is not bigint(20) unsigned');
|
||||||
|
}
|
||||||
|
// drop column - will not happen.
|
||||||
|
// assert.ok(!fields.pendingPeriod, 'Did not drop column pendingPeriod');
|
||||||
|
// user still exists
|
||||||
|
userExists(function (yep) {
|
||||||
|
assert.ok(yep, 'User does not exist');
|
||||||
done();
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
it('should autoupgrade', function(done) {
|
|
||||||
var userExists = function(cb) {
|
|
||||||
query('SELECT * FROM UserData', function(err, res) {
|
|
||||||
cb(!err && res[0].email == 'test@example.com');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
UserData.create({email: 'test@example.com'}, function(err, user) {
|
it('should check actuality of dataSource', function (done) {
|
||||||
assert.ok(!err, 'Could not create user: ' + err);
|
// 'drop column'
|
||||||
userExists(function(yep) {
|
UserData.dataSource.isActual(function (err, ok) {
|
||||||
assert.ok(yep, 'User does not exist');
|
assert.ok(ok, 'dataSource is not actual (should be)');
|
||||||
});
|
UserData.defineProperty('essay', {type: Schema.Text});
|
||||||
UserData.defineProperty('email', { type: String });
|
// UserData.defineProperty('email', false); Can't undefine currently.
|
||||||
UserData.defineProperty('name', {type: String, dataType: 'char', limit: 50});
|
UserData.dataSource.isActual(function (err, ok) {
|
||||||
UserData.defineProperty('newProperty', {type: Number, unsigned: true, dataType: 'bigInt'});
|
assert.ok(!ok, 'dataSource is actual (shouldn\t be)');
|
||||||
// UserData.defineProperty('pendingPeriod', false); This will not work as expected.
|
|
||||||
db.autoupdate( function(err) {
|
|
||||||
getFields('UserData', function(err, fields) {
|
|
||||||
// change nullable for email
|
|
||||||
assert.equal(fields.email.Null, 'YES', 'Email does not allow null');
|
|
||||||
// change type of name
|
|
||||||
assert.equal(fields.name.Type, 'char(50)', 'Name is not char(50)');
|
|
||||||
// add new column
|
|
||||||
assert.ok(fields.newProperty, 'New column was not added');
|
|
||||||
if (fields.newProperty) {
|
|
||||||
assert.equal(fields.newProperty.Type, 'bigint(20) unsigned', 'New column type is not bigint(20) unsigned');
|
|
||||||
}
|
|
||||||
// drop column - will not happen.
|
|
||||||
// assert.ok(!fields.pendingPeriod, 'Did not drop column pendingPeriod');
|
|
||||||
// user still exists
|
|
||||||
userExists(function(yep) {
|
|
||||||
assert.ok(yep, 'User does not exist');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should check actuality of dataSource', function(done) {
|
|
||||||
// 'drop column'
|
|
||||||
UserData.dataSource.isActual(function(err, ok) {
|
|
||||||
assert.ok(ok, 'dataSource is not actual (should be)');
|
|
||||||
UserData.defineProperty('essay', {type: Schema.Text});
|
|
||||||
// UserData.defineProperty('email', false); Can't undefine currently.
|
|
||||||
UserData.dataSource.isActual(function(err, ok) {
|
|
||||||
assert.ok(!ok, 'dataSource is actual (shouldn\t be)');
|
|
||||||
done()
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow numbers with decimals', function(done) {
|
|
||||||
NumberData.create({number: 1.1234567, tinyInt: 123456, mediumInt: -1234567, floater: 123456789.1234567 }, function(err, obj) {
|
|
||||||
assert.ok(!err);
|
|
||||||
assert.ok(obj);
|
|
||||||
NumberData.findById(obj.id, function(err, found) {
|
|
||||||
assert.equal(found.number, 1.123);
|
|
||||||
assert.equal(found.tinyInt, 127);
|
|
||||||
assert.equal(found.mediumInt, 0);
|
|
||||||
assert.equal(found.floater, 99999999.999999);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow both kinds of date columns', function(done) {
|
|
||||||
DateData.create({
|
|
||||||
dateTime: new Date('Aug 9 1996 07:47:33 GMT'),
|
|
||||||
timestamp: new Date('Sep 22 2007 17:12:22 GMT')
|
|
||||||
}, function(err, obj){
|
|
||||||
assert.ok(!err);
|
|
||||||
assert.ok(obj);
|
|
||||||
DateData.findById(obj.id, function(err, found){
|
|
||||||
assert.equal(found.dateTime.toGMTString(), 'Fri, 09 Aug 1996 07:47:33 GMT');
|
|
||||||
assert.equal(found.timestamp.toGMTString(), 'Sat, 22 Sep 2007 17:12:22 GMT');
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should disconnect when done', function(done) {
|
|
||||||
db.disconnect();
|
|
||||||
done()
|
done()
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow numbers with decimals', function (done) {
|
||||||
|
NumberData.create({number: 1.1234567, tinyInt: 123456, mediumInt: -1234567, floater: 123456789.1234567 }, function (err, obj) {
|
||||||
|
assert.ok(!err);
|
||||||
|
assert.ok(obj);
|
||||||
|
NumberData.findById(obj.id, function (err, found) {
|
||||||
|
assert.equal(found.number, 1.123);
|
||||||
|
assert.equal(found.tinyInt, 127);
|
||||||
|
assert.equal(found.mediumInt, 0);
|
||||||
|
assert.equal(found.floater, 99999999.999999);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow both kinds of date columns', function (done) {
|
||||||
|
DateData.create({
|
||||||
|
dateTime: new Date('Aug 9 1996 07:47:33 GMT'),
|
||||||
|
timestamp: new Date('Sep 22 2007 17:12:22 GMT')
|
||||||
|
}, function (err, obj) {
|
||||||
|
assert.ok(!err);
|
||||||
|
assert.ok(obj);
|
||||||
|
DateData.findById(obj.id, function (err, found) {
|
||||||
|
assert.equal(found.dateTime.toGMTString(), 'Fri, 09 Aug 1996 07:47:33 GMT');
|
||||||
|
assert.equal(found.timestamp.toGMTString(), 'Sat, 22 Sep 2007 17:12:22 GMT');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should disconnect when done', function (done) {
|
||||||
|
db.disconnect();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function setup(done) {
|
function setup(done) {
|
||||||
|
|
||||||
require('./init.js');
|
require('./init.js');
|
||||||
|
|
||||||
db = getSchema();
|
|
||||||
|
|
||||||
UserData = db.define('UserData', {
|
|
||||||
email: { type: String, null: false, index: true },
|
|
||||||
name: String,
|
|
||||||
bio: Schema.Text,
|
|
||||||
birthDate: Date,
|
|
||||||
pendingPeriod: Number,
|
|
||||||
createdByAdmin: Boolean,
|
|
||||||
} , { indexes: {
|
|
||||||
index0: {
|
|
||||||
columns: 'email, createdByAdmin'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
StringData = db.define('StringData', {
|
db = getSchema();
|
||||||
smallString: {type: String, null: false, index: true, dataType: 'char', limit: 127},
|
|
||||||
mediumString: {type: String, null: false, dataType: 'varchar', limit: 255},
|
UserData = db.define('UserData', {
|
||||||
tinyText: {type: String, dataType: 'tinyText'},
|
email: { type: String, null: false, index: true },
|
||||||
giantJSON: {type: Schema.JSON, dataType: 'longText'},
|
name: String,
|
||||||
text: {type: Schema.Text, dataType: 'varchar', limit: 1024}
|
bio: Schema.Text,
|
||||||
});
|
birthDate: Date,
|
||||||
|
pendingPeriod: Number,
|
||||||
NumberData = db.define('NumberData', {
|
createdByAdmin: Boolean,
|
||||||
number: {type: Number, null: false, index: true, unsigned: true, dataType: 'decimal', precision: 10, scale: 3},
|
}, { indexes: {
|
||||||
tinyInt: {type: Number, dataType: 'tinyInt', display: 2},
|
index0: {
|
||||||
mediumInt: {type: Number, dataType: 'mediumInt', unsigned: true},
|
columns: 'email, createdByAdmin'
|
||||||
floater: {type: Number, dataType: 'double', precision: 14, scale: 6}
|
}
|
||||||
});
|
}
|
||||||
|
});
|
||||||
DateData = db.define('DateData', {
|
|
||||||
dateTime: {type: Date, dataType: 'datetime'},
|
StringData = db.define('StringData', {
|
||||||
timestamp: {type: Date, dataType: 'timestamp'}
|
idString: {type: String, id: true},
|
||||||
});
|
smallString: {type: String, null: false, index: true, dataType: 'char', limit: 127},
|
||||||
|
mediumString: {type: String, null: false, dataType: 'varchar', limit: 255},
|
||||||
|
tinyText: {type: String, dataType: 'tinyText'},
|
||||||
|
giantJSON: {type: Schema.JSON, dataType: 'longText'},
|
||||||
|
text: {type: Schema.Text, dataType: 'varchar', limit: 1024}
|
||||||
|
});
|
||||||
|
|
||||||
|
NumberData = db.define('NumberData', {
|
||||||
|
number: {type: Number, null: false, index: true, unsigned: true, dataType: 'decimal', precision: 10, scale: 3},
|
||||||
|
tinyInt: {type: Number, dataType: 'tinyInt', display: 2},
|
||||||
|
mediumInt: {type: Number, dataType: 'mediumInt', unsigned: true},
|
||||||
|
floater: {type: Number, dataType: 'double', precision: 14, scale: 6}
|
||||||
|
});
|
||||||
|
|
||||||
|
DateData = db.define('DateData', {
|
||||||
|
dateTime: {type: Date, dataType: 'datetime'},
|
||||||
|
timestamp: {type: Date, dataType: 'timestamp'}
|
||||||
|
});
|
||||||
|
|
||||||
|
blankDatabase(db, done);
|
||||||
|
|
||||||
blankDatabase(db, done);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var query = function (sql, cb) {
|
var query = function (sql, cb) {
|
||||||
db.adapter.query(sql, cb);
|
db.adapter.query(sql, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
var blankDatabase = function (db, cb) {
|
var blankDatabase = function (db, cb) {
|
||||||
var dbn = db.settings.database;
|
var dbn = db.settings.database;
|
||||||
var cs = db.settings.charset;
|
var cs = db.settings.charset;
|
||||||
var co = db.settings.collation;
|
var co = db.settings.collation;
|
||||||
query('DROP DATABASE IF EXISTS ' + dbn, function(err) {
|
query('DROP DATABASE IF EXISTS ' + dbn, function (err) {
|
||||||
var q = 'CREATE DATABASE ' + dbn;
|
var q = 'CREATE DATABASE ' + dbn;
|
||||||
if(cs){
|
if (cs) {
|
||||||
q += ' CHARACTER SET ' + cs;
|
q += ' CHARACTER SET ' + cs;
|
||||||
}
|
}
|
||||||
if(co){
|
if (co) {
|
||||||
q += ' COLLATE ' + co;
|
q += ' COLLATE ' + co;
|
||||||
}
|
}
|
||||||
query(q, function(err) {
|
query(q, function (err) {
|
||||||
query('USE '+ dbn, cb);
|
query('USE ' + dbn, cb);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
getFields = function (model, cb) {
|
getFields = function (model, cb) {
|
||||||
query('SHOW FIELDS FROM ' + model, function(err, res) {
|
query('SHOW FIELDS FROM ' + model, function (err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
cb(err);
|
cb(err);
|
||||||
} else {
|
} else {
|
||||||
var fields = {};
|
var fields = {};
|
||||||
res.forEach(function(field){
|
res.forEach(function (field) {
|
||||||
fields[field.Field] = field;
|
fields[field.Field] = field;
|
||||||
});
|
});
|
||||||
cb(err, fields);
|
cb(err, fields);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getIndexes = function (model, cb) {
|
getIndexes = function (model, cb) {
|
||||||
query('SHOW INDEXES FROM ' + model, function(err, res) {
|
query('SHOW INDEXES FROM ' + model, function (err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
cb(err);
|
cb(err);
|
||||||
} else {
|
} else {
|
||||||
var indexes = {};
|
var indexes = {};
|
||||||
// Note: this will only show the first key of compound keys
|
// Note: this will only show the first key of compound keys
|
||||||
res.forEach(function(index) {
|
res.forEach(function (index) {
|
||||||
if (parseInt(index.Seq_in_index, 10) == 1) {
|
if (parseInt(index.Seq_in_index, 10) == 1) {
|
||||||
indexes[index.Key_name] = index
|
indexes[index.Key_name] = index
|
||||||
}
|
|
||||||
});
|
|
||||||
cb(err, indexes);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
cb(err, indexes);
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
--globals getSchema
|
||||||
|
--timeout 15000
|
|
@ -3,28 +3,28 @@ require('should');
|
||||||
|
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var DataSource = require('loopback-datasource-juggler').DataSource;
|
var DataSource = require('loopback-datasource-juggler').DataSource;
|
||||||
var db;
|
var db, config;
|
||||||
|
|
||||||
before(function() {
|
before(function () {
|
||||||
var config = require('rc')('loopback', {dev: {mysql: {}}}).dev.mysql;
|
config = require('rc')('loopback', {dev: {mysql: {}}}).dev.mysql;
|
||||||
config.database = 'STRONGLOOP';
|
config.database = 'STRONGLOOP';
|
||||||
db = new DataSource(require('../'), config);
|
db = new DataSource(require('../'), config);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('discoverModels', function() {
|
describe('discoverModels', function () {
|
||||||
describe('Discover models including views', function() {
|
describe('Discover models including views', function () {
|
||||||
it('should return an array of tables and views', function(done) {
|
it('should return an array of tables and views', function (done) {
|
||||||
|
|
||||||
db.discoverModelDefinitions({
|
db.discoverModelDefinitions({
|
||||||
views : true,
|
views: true,
|
||||||
limit : 3
|
limit: 3
|
||||||
}, function(err, models) {
|
}, function (err, models) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
done(err);
|
done(err);
|
||||||
} else {
|
} else {
|
||||||
var views = false;
|
var views = false;
|
||||||
models.forEach(function(m) {
|
models.forEach(function (m) {
|
||||||
// console.dir(m);
|
// console.dir(m);
|
||||||
if (m.type === 'view') {
|
if (m.type === 'view') {
|
||||||
views = true;
|
views = true;
|
||||||
|
@ -37,19 +37,39 @@ describe('discoverModels', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Discover models excluding views', function() {
|
describe('Discover current user\'s tables', function () {
|
||||||
it('should return an array of only tables', function(done) {
|
it('should return an array of tables for the current user', function (done) {
|
||||||
|
|
||||||
db.discoverModelDefinitions({
|
db.discoverModelDefinitions({
|
||||||
views : false,
|
limit: 3
|
||||||
limit : 3
|
}, function (err, models) {
|
||||||
}, function(err, models) {
|
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
done(err);
|
done(err);
|
||||||
} else {
|
} else {
|
||||||
var views = false;
|
var views = false;
|
||||||
models.forEach(function(m) {
|
models.forEach(function (m) {
|
||||||
|
assert.equal(m.owner, config.username);
|
||||||
|
});
|
||||||
|
done(null, models);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Discover models excluding views', function () {
|
||||||
|
it('should return an array of only tables', function (done) {
|
||||||
|
|
||||||
|
db.discoverModelDefinitions({
|
||||||
|
views: false,
|
||||||
|
limit: 3
|
||||||
|
}, function (err, models) {
|
||||||
|
if (err) {
|
||||||
|
console.error(err);
|
||||||
|
done(err);
|
||||||
|
} else {
|
||||||
|
var views = false;
|
||||||
|
models.forEach(function (m) {
|
||||||
// console.dir(m);
|
// console.dir(m);
|
||||||
if (m.type === 'view') {
|
if (m.type === 'view') {
|
||||||
views = true;
|
views = true;
|
||||||
|
@ -64,19 +84,19 @@ describe('discoverModels', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Discover models including other users', function() {
|
describe('Discover models including other users', function () {
|
||||||
it('should return an array of all tables and views', function(done) {
|
it('should return an array of all tables and views', function (done) {
|
||||||
|
|
||||||
db.discoverModelDefinitions({
|
db.discoverModelDefinitions({
|
||||||
all : true,
|
all: true,
|
||||||
limit : 3
|
limit: 3
|
||||||
}, function(err, models) {
|
}, function (err, models) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
done(err);
|
done(err);
|
||||||
} else {
|
} else {
|
||||||
var others = false;
|
var others = false;
|
||||||
models.forEach(function(m) {
|
models.forEach(function (m) {
|
||||||
// console.dir(m);
|
// console.dir(m);
|
||||||
if (m.owner !== 'STRONGLOOP') {
|
if (m.owner !== 'STRONGLOOP') {
|
||||||
others = true;
|
others = true;
|
||||||
|
@ -89,15 +109,15 @@ describe('Discover models including other users', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Discover model properties', function() {
|
describe('Discover model properties', function () {
|
||||||
describe('Discover a named model', function() {
|
describe('Discover a named model', function () {
|
||||||
it('should return an array of columns for PRODUCT', function(done) {
|
it('should return an array of columns for PRODUCT', function (done) {
|
||||||
db.discoverModelProperties('PRODUCT', function(err, models) {
|
db.discoverModelProperties('PRODUCT', function (err, models) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
done(err);
|
done(err);
|
||||||
} else {
|
} else {
|
||||||
models.forEach(function(m) {
|
models.forEach(function (m) {
|
||||||
// console.dir(m);
|
// console.dir(m);
|
||||||
assert(m.tableName === 'PRODUCT');
|
assert(m.tableName === 'PRODUCT');
|
||||||
});
|
});
|
||||||
|
@ -110,88 +130,88 @@ describe('Discover model properties', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Discover model primary keys', function () {
|
describe('Discover model primary keys', function () {
|
||||||
it('should return an array of primary keys for PRODUCT', function (done) {
|
it('should return an array of primary keys for PRODUCT', function (done) {
|
||||||
db.discoverPrimaryKeys('PRODUCT',function (err, models) {
|
db.discoverPrimaryKeys('PRODUCT', function (err, models) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
done(err);
|
done(err);
|
||||||
} else {
|
} else {
|
||||||
models.forEach(function (m) {
|
models.forEach(function (m) {
|
||||||
// console.dir(m);
|
// console.dir(m);
|
||||||
assert(m.tableName === 'PRODUCT');
|
assert(m.tableName === 'PRODUCT');
|
||||||
});
|
|
||||||
done(null, models);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
done(null, models);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should return an array of primary keys for STRONGLOOP.PRODUCT', function (done) {
|
it('should return an array of primary keys for STRONGLOOP.PRODUCT', function (done) {
|
||||||
db.discoverPrimaryKeys('PRODUCT', {owner: 'STRONGLOOP'}, function (err, models) {
|
db.discoverPrimaryKeys('PRODUCT', {owner: 'STRONGLOOP'}, function (err, models) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
done(err);
|
done(err);
|
||||||
} else {
|
} else {
|
||||||
models.forEach(function (m) {
|
models.forEach(function (m) {
|
||||||
// console.dir(m);
|
// console.dir(m);
|
||||||
assert(m.tableName === 'PRODUCT');
|
assert(m.tableName === 'PRODUCT');
|
||||||
});
|
|
||||||
done(null, models);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
done(null, models);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Discover model foreign keys', function () {
|
describe('Discover model foreign keys', function () {
|
||||||
it('should return an array of foreign keys for INVENTORY', function (done) {
|
it('should return an array of foreign keys for INVENTORY', function (done) {
|
||||||
db.discoverForeignKeys('INVENTORY',function (err, models) {
|
db.discoverForeignKeys('INVENTORY', function (err, models) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
done(err);
|
done(err);
|
||||||
} else {
|
} else {
|
||||||
models.forEach(function (m) {
|
models.forEach(function (m) {
|
||||||
// console.dir(m);
|
// console.dir(m);
|
||||||
assert(m.fkTableName === 'INVENTORY');
|
assert(m.fkTableName === 'INVENTORY');
|
||||||
});
|
|
||||||
done(null, models);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
done(null, models);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
it('should return an array of foreign keys for STRONGLOOP.INVENTORY', function (done) {
|
});
|
||||||
db.discoverForeignKeys('INVENTORY', {owner: 'STRONGLOOP'}, function (err, models) {
|
it('should return an array of foreign keys for STRONGLOOP.INVENTORY', function (done) {
|
||||||
if (err) {
|
db.discoverForeignKeys('INVENTORY', {owner: 'STRONGLOOP'}, function (err, models) {
|
||||||
console.error(err);
|
if (err) {
|
||||||
done(err);
|
console.error(err);
|
||||||
} else {
|
done(err);
|
||||||
models.forEach(function (m) {
|
} else {
|
||||||
// console.dir(m);
|
models.forEach(function (m) {
|
||||||
assert(m.fkTableName === 'INVENTORY');
|
// console.dir(m);
|
||||||
});
|
assert(m.fkTableName === 'INVENTORY');
|
||||||
done(null, models);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
done(null, models);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Discover LDL schema from a table', function () {
|
describe('Discover LDL schema from a table', function () {
|
||||||
it('should return an LDL schema for INVENTORY', function (done) {
|
it('should return an LDL schema for INVENTORY', function (done) {
|
||||||
db.discoverSchema('INVENTORY', {owner: 'STRONGLOOP'}, function (err, schema) {
|
db.discoverSchema('INVENTORY', {owner: 'STRONGLOOP'}, function (err, schema) {
|
||||||
// console.log('%j', schema);
|
// console.log('%j', schema);
|
||||||
assert(schema.name === 'Inventory');
|
assert(schema.name === 'Inventory');
|
||||||
assert(schema.options.mysql.schema === 'STRONGLOOP');
|
assert(schema.options.mysql.schema === 'STRONGLOOP');
|
||||||
assert(schema.options.mysql.table === 'INVENTORY');
|
assert(schema.options.mysql.table === 'INVENTORY');
|
||||||
assert(schema.properties.productId);
|
assert(schema.properties.productId);
|
||||||
assert(schema.properties.productId.type === 'String');
|
assert(schema.properties.productId.type === 'String');
|
||||||
assert(schema.properties.productId.mysql.columnName === 'PRODUCT_ID');
|
assert(schema.properties.productId.mysql.columnName === 'PRODUCT_ID');
|
||||||
assert(schema.properties.locationId);
|
assert(schema.properties.locationId);
|
||||||
assert(schema.properties.locationId.type === 'String');
|
assert(schema.properties.locationId.type === 'String');
|
||||||
assert(schema.properties.locationId.mysql.columnName === 'LOCATION_ID');
|
assert(schema.properties.locationId.mysql.columnName === 'LOCATION_ID');
|
||||||
assert(schema.properties.available);
|
assert(schema.properties.available);
|
||||||
assert(schema.properties.available.type === 'Number');
|
assert(schema.properties.available.type === 'Number');
|
||||||
assert(schema.properties.total);
|
assert(schema.properties.total);
|
||||||
assert(schema.properties.total.type === 'Number');
|
assert(schema.properties.total.type === 'Number');
|
||||||
done(null, schema);
|
done(null, schema);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Discover and build models', function () {
|
describe('Discover and build models', function () {
|
||||||
|
|
Loading…
Reference in New Issue