diff --git a/packages/framework/dev.key b/packages/framework/dev.key deleted file mode 100644 index 5d49ae7e..00000000 --- a/packages/framework/dev.key +++ /dev/null @@ -1,29 +0,0 @@ ------BEGIN ENCRYPTED PRIVATE KEY----- -MIIE5DAcBgoqhkiG9w0BDAEBMA4ECL7L6rj6uEHGAgIIAASCBMLbucyfqAkgCbhP -xNSHYllPMAv/dsIjtnsBwepCXPGkCBCuOAw/2FaCHjN9hBqL5V7fkrKeaemhm2YE -ycPtlHJYPDf3kEkyMjdZ9rIY6kePGfQizs2uJPcXj4YPyQ4HsfVXpOicKfQrouf5 -Mze9bGzeMN065q3iP4dYUMwHAyZYteXCsanQNHlqvsWli0W+H8St8fdsXefZhnv1 -qVatKWdNdWQ9t5MuljgNU2Vv56sHKEYXI0yLxk2QUMk8KlJfnmt8foYUsnPUXHmc -gIjLKwwVkpdololnEHSNu0cEOUPowjgJru+uMpn7vdNl7TPEQ9jbEgdNg4JwoYzU -0nao8WzjaSp7kzvZz0VFwKnk5AjstGvvuAWckADdq23QElbn/mF7AG1m/TBpYxzF -gTt37UdndS/AcvVznWVVrRP5iTSIawdIwvqI4s7rqsoE0GCcak+RhchgAz2gWKkS -oODUo0JL6pPVbJ3l4ebbaO6c99nDVc8dViPtc1EkStJEJ2O4kI4xgLSCr4Y9ahKn -oAaoSkX7Xxq3aQm+BzqSpLjdGL8atsqR/YVOIHYIl3gThvP0NfZGx1xHyvO5mCdZ -kHxSA7tKWxauZ3eQ2clbnzeRsl4El0WMHy/5K1ovene4v7sunmoXVtghBC8hK6eh -zMO9orex2PNQ/VQC7HCvtytunOVx1lkSBoNo7hR70igg6rW9H7UyoAoBOwMpT1xa -J6V62nqruTKOqFNfur7aHJGpHGtDb5/ickHeYCyPTvmGp67u4wChzKReeg02oECe -d1E5FKAcIa8s9TVOB6Z+HvTRNQZu2PsI6TJnjQRowvY9DAHiWTlJZBBY/pko3hxX -TsIeybpvRdEHpDWv86/iqtw1hv9CUxS/8ZTWUgBo+osShHW79FeDASr9FC4/Zn76 -ZDERTgV4YWlW/klVWcG2lFo7jix+OPXAB+ZQavLhlN1xdWBcIz1AUWjAM4hdPylW -HCX4PB9CQIPl2E7F+Y2p6nMcMWSJVBi5UIH7E9LfaBguXSzMmTk2Fw5p1aOQ6wfN -goVAMVwi8ppAVs741PfHdZ295xMmK/1LCxz5DeAdD/tsA/SYfT753GotioDuC7im -EyJ5JyvTr5I6RFFBuqt3NlUb3Hp16wP3B2x9DZiB6jxr0l341/NHgsyeBXkuIy9j -ON2mvpBPCJhS8kgWo3G0UyyKnx64tcgpGuSvZhGwPz843B6AbYyE6pMRfSWRMkMS -YZYa+VNKhR4ixdj07ocFZEWLVjCH7kxkE8JZXKt8jKYmkWd0lS1QVjgaKlO6lRa3 -q6SPJkhW6pvqobvcqVNXwi1XuzpZeEbuh0B7OTekFTTxx5g9XeDl56M8SVQ1KEhT -Q1t7H2Nba18WCB7cf+6PN0F0K0Jz1Kq7ZWaqEI/grX1m4RQuvNF5807sB/QKMO/Z -Gz3NXvHg5xTJRd/567lxPGkor0cE7qD1EZfmJ2HrBYXQ91bhgA7LToBuMZo6ZRXH -QfsanjbP4FPLMiGdQigLjj3A35L/f4sQOOVac/sRaFnm7pzcxsMvyVU/YtvGcjYE -xaOOVnamg661Wo0wksXoDjeSz/JIyyKO3Gwp1FSm2wGLjjy/Ehmqcqy8rvHuf07w -AUukhVtTNn4= ------END ENCRYPTED PRIVATE KEY----- \ No newline at end of file diff --git a/packages/framework/dev.pem b/packages/framework/dev.pem deleted file mode 100644 index 01756b25..00000000 --- a/packages/framework/dev.pem +++ /dev/null @@ -1,57 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDKTCCAhGgAwIBAgIJAOWmjTS+OnTEMA0GCSqGSIb3DQEBCwUAMBcxFTATBgNV -BAMMDGludGVybWVkaWF0ZTAeFw0xNTA1MTgwOTAwNDBaFw0yMzA4MDQwOTAwNDBa -MBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC -AQoCggEBALlcwQJuzd+xH8QFgfJSn5tRlvhkldSX98cE7NiA602NBbnAVyUrkRXq -Ni75lgt0kwjYfA9z674m8WSVbgpLPintPCla9CYky1TH0keIs8Rz6cGWHryWEHiu -EDuljQynu2b3sAFuHu9nfWurbJwZnFakBKpdQ9m4EyOZCHC/jHYY7HacKSXg1Cki -we2ca0BWDrcqy8kLy0dZ5oC6IZG8O8drAK8f3f44CRYw59D3sOKBrKXaabpvyEcb -N7Wk2HDBVwHpUJo1reVwtbM8dhqQayYSD8oXnGpP3RQNu/e2rzlXRyq/BfcDY1JI -7TbC4t/7/N4EcPSpGsTcSOC9A7FpzvECAwEAAaN7MHkwCQYDVR0TBAIwADAsBglg -hkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0O -BBYEFCnwiEMMFZh7NhCr+qA8K0w4Q+AOMB8GA1UdIwQYMBaAFB0h1Evsaw2vfrmS -YuoCTmC4EE6ZMA0GCSqGSIb3DQEBCwUAA4IBAQAcFmHMaXRxyoNaeOowQ6iQWoZd -AUbvG7SHr7I6Pi2aqdqofsKWts7Ytm5WsS0M2nN+sW504houu0iCPeJJX8RQw2q4 -CCcNOs9IXk+2uMzlpocHpv+yYoUiD5DxgWh7eghQMLyMpf8FX3Gy4VazeuXznHOM -4gE4L417xkDzYOzqVTp0FTyAPUv6G2euhNCD6TMru9REcRhYul+K9kocjA5tt2KG -MH6y28LXbLyq4YJUxSUU9gY/xlnbbZS48KDqEcdYC9zjW9nQ0qS+XQuQuFIcwjJ5 -V4kAUYxDu6FoTpyQjgsrmBbZlKNxH7Nj4NDlcdJhp/zeSKHqWa5hSWjjKIxp ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDAjCCAeqgAwIBAgIJAOWmjTS+OnTDMA0GCSqGSIb3DQEBCwUAMBgxFjAUBgNV -BAMMDXJvb3RhdXRob3JpdHkwHhcNMTUwNTE4MDkwMDQwWhcNMjMwODA0MDkwMDQw -WjAXMRUwEwYDVQQDDAxpbnRlcm1lZGlhdGUwggEiMA0GCSqGSIb3DQEBAQUAA4IB -DwAwggEKAoIBAQDSrAO1CoPvUllgLOzDm5nG0skDF7vh1DUgAIDVGz0ecD0JFbQx -EF79pju/6MbtpTW2FYvRp11t/G7rGtX923ybOHY/1MNFQrdIvPlO1VV7IGKjoMwP -DNeb0fIGjHoE9QxaDxR8NX8xQbItpsw+TUtRfc9SLkR+jaYJfVRoM21BOncZbSHE -YKiZlEbpecB/+EtwVpgvl+8mPD5U07Fi4fp/lza3WXInXQPyiTVllIEJCt4PKmlu -MocNaJOW38bysL7i0PzDpVZtOxLHOTaW68yF3FckIHNCaA7k1ABEEEegjFMmIao7 -B9w7A0jvr4jZVvNmui5Djjn+oJxwEVVgyf8LAgMBAAGjUDBOMB0GA1UdDgQWBBQd -IdRL7GsNr365kmLqAk5guBBOmTAfBgNVHSMEGDAWgBRk81s9d0ZbiZhh44KckwPb -oTc0XzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBZQTK0plfdB5PC -cC5icut4EmrByJa1RbU7ayuEE70e7hla6KVmVjVdCBGltI4jBYwfhKbRItHiAJ/8 -x+XZKBG8DLPFuDb7lAa1ObhAYF7YThUFPQYaBhfzKcWrdmWDBFpvNv6E0Mm364dZ -e7Yxmbe5S4agkYPoxEzgEYmcUk9jbjdR6eTbs8laG169ljrECXfEU9RiAcqz5iSX -NLSewqB47hn3B9qgKcQn+PsgO2j7M+rfklhNgeGJeWmy7j6clSOuCsIjWHU0RLQ4 -0W3SB/rpEAJ7fgQbYUPTIUNALSOWi/o1tDX2mXPRjBoxqAv7I+vYk1lZPmSzkyRh -FKvRDxsW ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDAzCCAeugAwIBAgIJAJ0MomS4Ck+8MA0GCSqGSIb3DQEBCwUAMBgxFjAUBgNV -BAMMDXJvb3RhdXRob3JpdHkwHhcNMTUwNTE4MDkwMDQwWhcNMjMwODA0MDkwMDQw -WjAYMRYwFAYDVQQDDA1yb290YXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEAts1ijtBV92S2cOvpUMOSTp9c6A34nIGr0T5Nhz6XiqRVT+gv -dQgmkdKJQjbvR60y6jzltYFsI2MpGVXY8h/oAL81D/k7PDB2aREgyBfTPAhBHyGw -siR+2xYt5b/Zs99q5RdRqQNzNpLPJriIKvUsRyQWy1UiG2s7pRXQeA8qB0XtJdCj -kFIi+G2bDsaffspGeDOCqt7t+yqvRXfSES0c/l7DIHaiMbbp4//ZNML3RNgAjPz2 -hCezZ+wOYajOIyoSPK8IgICrhYFYxvgWxwbLDBEfC5B3jOQsySe10GoRAKZz1gBV -DmgReu81tYJmdgkc9zknnQtIFdA0ex+GvZlfWQIDAQABo1AwTjAdBgNVHQ4EFgQU -ZPNbPXdGW4mYYeOCnJMD26E3NF8wHwYDVR0jBBgwFoAUZPNbPXdGW4mYYeOCnJMD -26E3NF8wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEATzkZ97K777uZ -lQcduNX3ey4IbCiEzFA2zO5Blj+ilfIwNbZXNOgm/lqNvVGDYs6J1apJJe30vL3X -J+t2zsZWzzQzb9uIU37zYemt6m0fHrSrx/iy5lGNqt3HMfqEcOqSCOIK3PCTMz2/ -uyGe1iw33PVeWsm1JUybQ9IrU/huJjbgOHU4wab+8SJCM49ipArp68Fr6j4lcEaE -4rfRg1ZsvxiOyUB3qPn6wyL/JB8kOJ+QCBe498376eaem8AEFk0kQRh6hDaWtq/k -t6IIXQLjx+EBDVP/veK0UnVhKRP8YTOoV8ZiG1NcdlJmX/Uk7iAfevP7CkBfSN8W -r6AL284qtw== ------END CERTIFICATE----- \ No newline at end of file diff --git a/packages/jael/LICENSE b/packages/jael/LICENSE index 89074fd3..df5e0635 100644 --- a/packages/jael/LICENSE +++ b/packages/jael/LICENSE @@ -1,21 +1,29 @@ -MIT License +BSD 3-Clause License -Copyright (c) 2017 The Angel Framework +Copyright (c) 2021, dukefirehawk.com +All rights reserved. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -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 THE -SOFTWARE. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/jael/LSP_LICENSE b/packages/jael/LSP_LICENSE deleted file mode 100644 index 1c458fd5..00000000 --- a/packages/jael/LSP_LICENSE +++ /dev/null @@ -1,26 +0,0 @@ -Copyright 2017 dart_language_server authors - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -3. Neither the name of the copyright holder nor the names of its contributors - may be used to endorse or promote products derived from this software without - specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/jael/README.md b/packages/jael/README.md index d35c15f5..fdcdab34 100644 --- a/packages/jael/README.md +++ b/packages/jael/README.md @@ -1,10 +1,11 @@ -# jael -[![Pub](https://img.shields.io/pub/v/jael.svg)](https://pub.dartlang.org/packages/jael) -[![build status](https://travis-ci.org/angel-dart/jael.svg)](https://travis-ci.org/angel-dart/jael) +# JAEL3 + +![Pub Version (including pre-releases)](https://img.shields.io/pub/v/jael3?include_prereleases) A simple server-side HTML templating engine for Dart. Though its syntax is but a superset of HTML, it supports features such as: + * **Custom elements** * Loops * Conditionals @@ -13,22 +14,21 @@ Though its syntax is but a superset of HTML, it supports features such as: * `switch` syntax * Interpolation of any Dart expression -Jael is a good choice for applications of any scale, especially when the development team is small, -or the time invested in building an SPA would be too much. +Jael is a good choice for applications of any scale, especially when the development team is small, or the time invested in building an SPA would be too much. ## Documentation -Each of the [packages within this repository](#this-repository) contains -some sort of documentation. + +Each of the [packages within this repository](#this-repository) contains some sort of documentation. Documentation for Jael syntax and directives has been **moved** to the -[Angel framework wiki](https://docs.angel-dart.dev/packages/front-end/jael). +[Angel3 framework wiki](https://angel3-docs.dukefirehawk.com/packages/front-end/jael). ## This Repository + Within this repository are three packages: -* `package:jael` - Contains the Jael parser, AST, and HTML renderer. -* `package:jael_preprocessor` - Handles template inheritance, and facilitates the use of "compile-time" constructs. -* `package:build_jael` - Uses `package:build` to compile Jael templates, therefore allowing speedy incremental builds to HTML files. -* `package:angel_jael` - [Angel](https://angel-dart.github.io) support for Jael. Angel contains other +* `package:jael3` - Contains the Jael parser, AST, and HTML renderer. +* `package:jael3_preprocessor` - Handles template inheritance, and facilitates the use of "compile-time" constructs. +* `package:angel3_jael` - [Angel3](https://angel3-framework.web.app/) support for Jael. facilities to speed up application development, so something like Jael is right at home. diff --git a/packages/jael/angel_jael/example/main.dart b/packages/jael/angel_jael/example/main.dart index b78cf368..c55e7513 100644 --- a/packages/jael/angel_jael/example/main.dart +++ b/packages/jael/angel_jael/example/main.dart @@ -3,7 +3,6 @@ import 'package:angel3_framework/angel3_framework.dart'; import 'package:angel3_framework/http.dart'; import 'package:angel3_jael/angel3_jael.dart'; import 'package:file/local.dart'; -import 'package:jael3/jael3.dart'; import 'package:logging/logging.dart'; main() async { diff --git a/packages/jael/jael/README.md b/packages/jael/jael/README.md index ffc4b915..db9b256f 100644 --- a/packages/jael/jael/README.md +++ b/packages/jael/jael/README.md @@ -23,9 +23,9 @@ dependencies: The core `jael3` package exports classes for parsing Jael templates, an AST library, and a `Renderer` class that generates HTML on-the-fly. ```dart -import 'package:belatuk_code_buffer/code_buffer.dart'; -import 'package:belatuk_symbol_table/symbol_table.dart'; -import 'package:jael3/jael.dart' as jael; +import 'package:belatuk_code_buffer/belatuk_code_buffer.dart'; +import 'package:belatuk_symbol_table/belatuk_symbol_table.dart'; +import 'package:jael3/jael3.dart' as jael; void myFunction() { const template = ''' diff --git a/packages/jael/jael/pubspec.yaml b/packages/jael/jael/pubspec.yaml index a2769211..fafb7e8f 100644 --- a/packages/jael/jael/pubspec.yaml +++ b/packages/jael/jael/pubspec.yaml @@ -1,7 +1,8 @@ name: jael3 -version: 4.2.0 +version: 4.2.1 description: A simple server-side HTML templating engine for Dart. Comparable to Blade or Liquid. -homepage: https://github.com/dukefirehawk/angel/tree/master/packages/jael/jael +homepage: https://angel3-framework.web.app/ +repository: https://github.com/dukefirehawk/angel/tree/master/packages/jael/jael environment: sdk: '>=2.12.0 <3.0.0' dependencies: diff --git a/packages/orm/angel_orm/CHANGELOG.md b/packages/orm/angel_orm/CHANGELOG.md index 89acf8d1..2bbc30da 100644 --- a/packages/orm/angel_orm/CHANGELOG.md +++ b/packages/orm/angel_orm/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 4.0.3 + +* Removed debugging messages + ## 4.0.2 * Updated linter to `package:lints` diff --git a/packages/orm/angel_orm/example/main.dart b/packages/orm/angel_orm/example/main.dart index b296a5a3..e04ad2cc 100644 --- a/packages/orm/angel_orm/example/main.dart +++ b/packages/orm/angel_orm/example/main.dart @@ -1,8 +1,6 @@ import 'dart:async'; -import 'package:angel3_model/angel3_model.dart'; import 'package:angel3_orm/angel3_orm.dart'; -import 'package:angel3_orm/src/query.dart'; import 'package:angel3_serialize/angel3_serialize.dart'; import 'package:optional/optional.dart'; diff --git a/packages/orm/angel_orm/lib/src/query.dart b/packages/orm/angel_orm/lib/src/query.dart index def7900b..ae8e85a1 100644 --- a/packages/orm/angel_orm/lib/src/query.dart +++ b/packages/orm/angel_orm/lib/src/query.dart @@ -342,6 +342,8 @@ abstract class Query extends QueryBase { Future> delete(QueryExecutor executor) { var sql = compile({}, preamble: 'DELETE', withFields: false); + //_log.fine("Delete Query = $sql"); + if (_joins.isEmpty) { return executor .query(tableName, sql, substitutionValues, @@ -375,6 +377,8 @@ abstract class Query extends QueryBase { var sql = compile({}); sql = 'WITH $tableName as ($insertion RETURNING $returning) ' + sql; + //_log.fine("Insert Query = $sql"); + return executor.query(tableName, sql, substitutionValues).then((it) { // Return SQL execution results return it.isEmpty ? Optional.empty() : deserialize(it.first); @@ -400,6 +404,8 @@ abstract class Query extends QueryBase { var sql = compile({}); sql = 'WITH $tableName as ($updateSql RETURNING $returning) ' + sql; + //_log.fine("Update Query = $sql"); + return executor .query(tableName, sql, substitutionValues) .then((it) => deserializeList(it)); diff --git a/packages/orm/angel_orm/lib/src/query_base.dart b/packages/orm/angel_orm/lib/src/query_base.dart index 04d4d7c9..53ae04bb 100644 --- a/packages/orm/angel_orm/lib/src/query_base.dart +++ b/packages/orm/angel_orm/lib/src/query_base.dart @@ -60,8 +60,8 @@ abstract class QueryBase { Future> get(QueryExecutor executor) async { var sql = compile({}); - _log.fine('sql = $sql'); - _log.fine('substitutionValues = $substitutionValues'); + //_log.fine('sql = $sql'); + //_log.fine('substitutionValues = $substitutionValues'); return executor.query(tableName, sql, substitutionValues).then((it) { return deserializeList(it); diff --git a/packages/orm/angel_orm/pubspec.yaml b/packages/orm/angel_orm/pubspec.yaml index 14f24dfa..41663be8 100644 --- a/packages/orm/angel_orm/pubspec.yaml +++ b/packages/orm/angel_orm/pubspec.yaml @@ -1,5 +1,5 @@ name: angel3_orm -version: 4.0.2 +version: 4.0.3 description: Runtime support for Angel3 ORM. Includes base classes for queries. homepage: https://angel3-framework.web.app/ repository: https://github.com/dukefirehawk/angel/tree/master/packages/orm/angel_orm diff --git a/packages/orm/angel_orm_mysql/pubspec.yaml b/packages/orm/angel_orm_mysql/pubspec.yaml index a757e3ab..6371ee19 100644 --- a/packages/orm/angel_orm_mysql/pubspec.yaml +++ b/packages/orm/angel_orm_mysql/pubspec.yaml @@ -18,4 +18,9 @@ dev_dependencies: test: ^1.17.0 angel3_orm_test: ^3.0.0 lints: ^1.0.0 +dependency_overrides: + angel3_orm_test: + path: ../angel_orm_test + angel3_orm: + path: ../angel_orm diff --git a/packages/orm/angel_orm_postgres/CHANGELOG.md b/packages/orm/angel_orm_postgres/CHANGELOG.md index 27d426ed..e92c9ae4 100644 --- a/packages/orm/angel_orm_postgres/CHANGELOG.md +++ b/packages/orm/angel_orm_postgres/CHANGELOG.md @@ -1,5 +1,9 @@ # Change Log +## 3.2.0 + +* Added `package:postgres_pool` for connection pooling + ## 3.1.0 * Updated linter to `package:lints` diff --git a/packages/orm/angel_orm_postgres/README.md b/packages/orm/angel_orm_postgres/README.md index b79751f8..6d781b6d 100644 --- a/packages/orm/angel_orm_postgres/README.md +++ b/packages/orm/angel_orm_postgres/README.md @@ -5,6 +5,6 @@ [![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion) [![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dukefirehawk/angel/tree/master/packages/orm/angel_orm_postgres/LICENSE) -Postgresql support for Angel3 ORM. Supported version: 9, 10, 11 and 12. +Postgresql support for Angel3 ORM. Supported version: 10, 11, 12, 13 and 14 For documentation about the ORM, see [Developer Guide](https://angel3-docs.dukefirehawk.com/guides/orm) diff --git a/packages/orm/angel_orm_postgres/example/main.dart b/packages/orm/angel_orm_postgres/example/main.dart index 79709444..772f7ca4 100644 --- a/packages/orm/angel_orm_postgres/example/main.dart +++ b/packages/orm/angel_orm_postgres/example/main.dart @@ -1,12 +1,20 @@ import 'dart:io'; import 'package:angel3_orm_postgres/angel3_orm_postgres.dart'; -import 'package:postgres/postgres.dart'; +import 'package:postgres_pool/postgres_pool.dart'; void main() async { - var executor = PostgreSqlExecutorPool(Platform.numberOfProcessors, () { - return PostgreSQLConnection('localhost', 5432, 'orm_test', - username: 'test', password: 'test123'); - }); + var executor = PostgreSqlPoolExecutor(PgPool( + PgEndpoint( + host: 'localhost', + port: 5432, + database: 'orm_test', + username: Platform.environment['POSTGRES_USERNAME'] ?? 'test', + password: Platform.environment['POSTGRES_PASSWORD'] ?? 'test123', + ), + settings: PgPoolSettings() + ..maxConnectionAge = Duration(hours: 1) + ..concurrency = 5, + )); var rows = await executor.query('users', 'SELECT * FROM users', {}); print(rows); diff --git a/packages/orm/angel_orm_postgres/lib/angel3_orm_postgres.dart b/packages/orm/angel_orm_postgres/lib/angel3_orm_postgres.dart index 433fcf66..ecd387ef 100644 --- a/packages/orm/angel_orm_postgres/lib/angel3_orm_postgres.dart +++ b/packages/orm/angel_orm_postgres/lib/angel3_orm_postgres.dart @@ -1,171 +1,2 @@ -import 'dart:async'; -import 'dart:convert'; -import 'package:angel3_orm/angel3_orm.dart'; -import 'package:logging/logging.dart'; -import 'package:pool/pool.dart'; -import 'package:postgres/postgres.dart'; - -/// A [QueryExecutor] that queries a PostgreSQL database. -class PostgreSqlExecutor extends QueryExecutor { - final PostgreSQLExecutionContext _connection; - - /// An optional [Logger] to print information to. - late Logger logger; - - PostgreSqlExecutor(this._connection, {Logger? logger}) { - if (logger != null) { - this.logger = logger; - } else { - this.logger = Logger('PostgreSqlExecutor'); - } - } - - /// The underlying connection. - PostgreSQLExecutionContext get connection => _connection; - - /// Closes the connection. - Future close() { - if (_connection is PostgreSQLConnection) { - return (_connection as PostgreSQLConnection).close(); - } else { - return Future.value(); - } - } - - @override - Future> query( - String tableName, String query, Map substitutionValues, - [List? returningFields]) { - if (returningFields != null) { - var fields = returningFields.join(', '); - var returning = 'RETURNING $fields'; - query = '$query $returning'; - } - - logger.fine('Query: $query'); - logger.fine('Values: $substitutionValues'); - - // Convert List into String - var param = {}; - substitutionValues.forEach((key, value) { - if (value is List) { - param[key] = jsonEncode(value); - } else { - param[key] = value; - } - }); - - return _connection.query(query, substitutionValues: param); - } - - @override - Future transaction(FutureOr Function(QueryExecutor) f) async { - if (_connection is! PostgreSQLConnection) { - return await f(this); - } - - var conn = _connection as PostgreSQLConnection; - T? returnValue; - - var txResult = await conn.transaction((ctx) async { - try { - logger.fine('Entering transaction'); - var tx = PostgreSqlExecutor(ctx, logger: logger); - returnValue = await f(tx); - - return returnValue; - } catch (e) { - ctx.cancelTransaction(reason: e.toString()); - rethrow; - } finally { - logger.fine('Exiting transaction'); - } - }); - - if (txResult is PostgreSQLRollback) { - //if (txResult.reason == null) { - // throw StateError('The transaction was cancelled.'); - //} else { - throw StateError( - 'The transaction was cancelled with reason "${txResult.reason}".'); - //} - } else { - return returnValue!; - } - } -} - -/// A [QueryExecutor] that manages a pool of PostgreSQL connections. -class PostgreSqlExecutorPool extends QueryExecutor { - /// The maximum amount of concurrent connections. - final int size; - - /// Creates a new [PostgreSQLConnection], on demand. - /// - /// The created connection should **not** be open. - final PostgreSQLConnection Function() connectionFactory; - - /// An optional [Logger] to print information to. - late Logger logger; - - final List _connections = []; - int _index = 0; - final Pool _pool, _connMutex = Pool(1); - - PostgreSqlExecutorPool(this.size, this.connectionFactory, {Logger? logger}) - : _pool = Pool(size) { - if (logger != null) { - this.logger = logger; - } else { - this.logger = Logger('PostgreSqlExecutorPool'); - } - - assert(size > 0, 'Connection pool cannot be empty.'); - } - - /// Closes all connections. - Future close() async { - await _pool.close(); - await _connMutex.close(); - return Future.wait(_connections.map((c) => c.close())); - } - - Future _open() async { - if (_connections.isEmpty) { - _connections.addAll(await Future.wait(List.generate(size, (_) { - logger.fine('Spawning connections...'); - var conn = connectionFactory(); - return conn - .open() - .then((_) => PostgreSqlExecutor(conn, logger: logger)); - }))); - } - } - - Future _next() { - return _connMutex.withResource(() async { - await _open(); - if (_index >= size) _index = 0; - return _connections[_index++]; - }); - } - - @override - Future> query( - String tableName, String query, Map substitutionValues, - [List? returningFields]) { - return _pool.withResource(() async { - var executor = await _next(); - return executor.query( - tableName, query, substitutionValues, returningFields); - }); - } - - @override - Future transaction(FutureOr Function(QueryExecutor) f) { - return _pool.withResource(() async { - var executor = await _next(); - return executor.transaction(f); - }); - } -} +export 'src/orm_postgres.dart'; +export 'src/orm_postgres_pool.dart'; diff --git a/packages/orm/angel_orm_postgres/lib/src/orm_postgres.dart b/packages/orm/angel_orm_postgres/lib/src/orm_postgres.dart new file mode 100644 index 00000000..26707124 --- /dev/null +++ b/packages/orm/angel_orm_postgres/lib/src/orm_postgres.dart @@ -0,0 +1,169 @@ +import 'dart:async'; +import 'dart:convert'; +import 'package:angel3_orm/angel3_orm.dart'; +import 'package:logging/logging.dart'; +import 'package:pool/pool.dart'; +import 'package:postgres/postgres.dart'; + +/// A [QueryExecutor] that queries a PostgreSQL database. +class PostgreSqlExecutor extends QueryExecutor { + final PostgreSQLExecutionContext _connection; + + /// An optional [Logger] to print information to. + late Logger logger; + + PostgreSqlExecutor(this._connection, {Logger? logger}) { + this.logger = logger ?? Logger('PostgreSqlExecutor'); + } + + /// The underlying connection. + PostgreSQLExecutionContext get connection => _connection; + + /// Closes the connection. + Future close() { + if (_connection is PostgreSQLConnection) { + return (_connection as PostgreSQLConnection).close(); + } else { + return Future.value(); + } + } + + @override + Future query( + String tableName, String query, Map substitutionValues, + [List? returningFields]) { + if (returningFields != null && returningFields.isNotEmpty) { + var fields = returningFields.join(', '); + var returning = 'RETURNING $fields'; + query = '$query $returning'; + } + + //logger.fine('Query: $query'); + //logger.fine('Values: $substitutionValues'); + + // Convert List into String + var param = {}; + substitutionValues.forEach((key, value) { + if (value is List) { + param[key] = jsonEncode(value); + } else { + param[key] = value; + } + }); + + return _connection.query(query, substitutionValues: param); + } + + @override + Future transaction(FutureOr Function(QueryExecutor) f) async { + if (_connection is! PostgreSQLConnection) { + return await f(this); + } + + var conn = _connection as PostgreSQLConnection; + T? returnValue; + + var txResult = await conn.transaction((ctx) async { + try { + logger.fine('Entering transaction'); + var tx = PostgreSqlExecutor(ctx, logger: logger); + returnValue = await f(tx); + + return returnValue; + } catch (e) { + ctx.cancelTransaction(reason: e.toString()); + rethrow; + } finally { + logger.fine('Exiting transaction'); + } + }); + + if (txResult is PostgreSQLRollback) { + //if (txResult.reason == null) { + // throw StateError('The transaction was cancelled.'); + //} else { + throw StateError( + 'The transaction was cancelled with reason "${txResult.reason}".'); + //} + } else { + return returnValue!; + } + } +} + +/// A [QueryExecutor] that manages a pool of PostgreSQL connections. +class PostgreSqlExecutorPool extends QueryExecutor { + /// The maximum amount of concurrent connections. + final int size; + + /// Creates a new [PostgreSQLConnection], on demand. + /// + /// The created connection should **not** be open. + final PostgreSQLConnection Function() connectionFactory; + + /// An optional [Logger] to print information to. + late Logger logger; + + final List _connections = []; + int _index = 0; + final Pool _pool, _connMutex = Pool(1); + + PostgreSqlExecutorPool(this.size, this.connectionFactory, {Logger? logger}) + : _pool = Pool(size) { + if (logger != null) { + this.logger = logger; + } else { + this.logger = Logger('PostgreSqlExecutorPool'); + } + + assert(size > 0, 'Connection pool cannot be empty.'); + } + + /// Closes all connections. + Future close() async { + await _pool.close(); + await _connMutex.close(); + return Future.wait(_connections.map((c) => c.close())); + } + + Future _open() async { + if (_connections.isEmpty) { + _connections.addAll(await Future.wait(List.generate(size, (_) async { + logger.fine('Spawning connections...'); + var conn = connectionFactory(); + await conn.open(); + //return conn + // .open() + // .then((_) => PostgreSqlExecutor(conn, logger: logger)); + return PostgreSqlExecutor(conn, logger: logger); + }))); + } + } + + Future _next() { + return _connMutex.withResource(() async { + await _open(); + if (_index >= size) _index = 0; + return _connections[_index++]; + }); + } + + @override + Future query( + String tableName, String query, Map substitutionValues, + [List? returningFields]) { + return _pool.withResource(() async { + var executor = await _next(); + return executor.query( + tableName, query, substitutionValues, returningFields); + }); + } + + @override + Future transaction(FutureOr Function(QueryExecutor) f) { + return _pool.withResource(() async { + var executor = await _next(); + return executor.transaction(f); + }); + } +} diff --git a/packages/orm/angel_orm_postgres/lib/src/orm_postgres_pool.dart b/packages/orm/angel_orm_postgres/lib/src/orm_postgres_pool.dart new file mode 100644 index 00000000..3af1f3ff --- /dev/null +++ b/packages/orm/angel_orm_postgres/lib/src/orm_postgres_pool.dart @@ -0,0 +1,65 @@ +import 'dart:async'; +import 'dart:convert'; +import 'package:angel3_orm/angel3_orm.dart'; +import 'package:logging/logging.dart'; +import 'package:postgres_pool/postgres_pool.dart'; + +import '../angel3_orm_postgres.dart'; + +/// A [QueryExecutor] that uses `package:postgres_pool` for connetions pooling. +class PostgreSqlPoolExecutor extends QueryExecutor { + final PgPool _pool; + + /// An optional [Logger] to print information to. + late Logger logger; + + PostgreSqlPoolExecutor(this._pool, {Logger? logger}) { + this.logger = logger ?? Logger('PostgreSqlPoolExecutor'); + } + + /// The underlying connection pooling. + PgPool get pool => _pool; + + /// Closes all the connections in the pool. + Future close() { + return _pool.close(); + } + + /// Run query. + @override + Future query( + String tableName, String query, Map substitutionValues, + [List returningFields = const []]) { + if (returningFields.isNotEmpty) { + var fields = returningFields.join(', '); + var returning = 'RETURNING $fields'; + query = '$query $returning'; + } + + //logger.fine('Query: $query'); + //logger.fine('Values: $substitutionValues'); + + // Convert List into String + var param = {}; + substitutionValues.forEach((key, value) { + if (value is List) { + param[key] = jsonEncode(value); + } else { + param[key] = value; + } + }); + + return _pool.run((pgContext) async { + return await pgContext.query(query, substitutionValues: param); + }); + } + + /// Run query in a transaction. + @override + Future transaction(FutureOr Function(QueryExecutor) f) async { + return _pool.runTx((pgContext) async { + var exec = PostgreSqlExecutor(pgContext, logger: logger); + return await f(exec); + }); + } +} diff --git a/packages/orm/angel_orm_postgres/pubspec.yaml b/packages/orm/angel_orm_postgres/pubspec.yaml index 1a9d9f6b..c8faa2f5 100644 --- a/packages/orm/angel_orm_postgres/pubspec.yaml +++ b/packages/orm/angel_orm_postgres/pubspec.yaml @@ -1,5 +1,5 @@ name: angel3_orm_postgres -version: 3.1.0 +version: 3.2.0 description: PostgreSQL support for Angel3 ORM. Includes functionality for querying and transactions. homepage: https://angel3-framework.web.app/ repository: https://github.com/dukefirehawk/angel/tree/master/packages/orm/angel_orm_postgres @@ -10,8 +10,14 @@ dependencies: logging: ^1.0.1 pool: ^1.5.0 postgres: ^2.4.1 + postgres_pool: ^2.1.3 dev_dependencies: belatuk_pretty_logging: ^4.0.0 angel3_orm_test: ^3.0.0 test: ^1.17.5 lints: ^1.0.0 +#dependency_overrides: +# angel3_orm_test: +# path: ../angel_orm_test +# angel3_orm: +# path: ../angel_orm diff --git a/packages/orm/angel_orm_postgres/test/all_test.dart b/packages/orm/angel_orm_postgres/test/all_test.dart index 015799d8..0d1dc727 100644 --- a/packages/orm/angel_orm_postgres/test/all_test.dart +++ b/packages/orm/angel_orm_postgres/test/all_test.dart @@ -9,6 +9,9 @@ void main() { ..level = Level.ALL ..onRecord.listen(prettyLog); + //group('performance', + // () => performanceTests(pg(['performance']), close: closePg)); + group('postgresql', () { group('belongsTo', () => belongsToTests(pg(['author', 'book']), close: closePg)); diff --git a/packages/orm/angel_orm_postgres/test/common.dart b/packages/orm/angel_orm_postgres/test/common.dart index 061bccc7..3c3169f1 100644 --- a/packages/orm/angel_orm_postgres/test/common.dart +++ b/packages/orm/angel_orm_postgres/test/common.dart @@ -3,14 +3,28 @@ import 'dart:io'; import 'package:angel3_orm/angel3_orm.dart'; import 'package:angel3_orm_postgres/angel3_orm_postgres.dart'; import 'package:logging/logging.dart'; -import 'package:postgres/postgres.dart'; +import 'package:postgres_pool/postgres_pool.dart'; FutureOr Function() pg(Iterable schemas) { + // Use single connection return () => connectToPostgres(schemas); + + // Use connection pooling with 1 connection + //return () => connectToPostgresPool(schemas); + + // Use PostgreSqlExecutorPool (Not working) + //return () => connectToPostgresPool1(schemas); } -Future closePg(QueryExecutor executor) => - (executor as PostgreSqlExecutor).close(); +Future closePg(QueryExecutor executor) async { + if (executor is PostgreSqlExecutor) { + await executor.close(); + //} else if (executor is PostgreSqlExecutorPool) { + // await executor.close(); + } else if (executor is PostgreSqlPoolExecutor) { + await executor.close(); + } +} Future connectToPostgres(Iterable schemas) async { var conn = PostgreSQLConnection('127.0.0.1', 5432, 'orm_test', @@ -25,3 +39,47 @@ Future connectToPostgres(Iterable schemas) async { return PostgreSqlExecutor(conn, logger: Logger.root); } + +Future connectToPostgresPool1( + Iterable schemas) async { + PostgreSQLConnection connectionFactory() { + return PostgreSQLConnection('127.0.0.1', 5432, 'orm_test', + username: Platform.environment['POSTGRES_USERNAME'] ?? 'test', + password: Platform.environment['POSTGRES_PASSWORD'] ?? 'test123'); + } + + PostgreSQLConnection conn = connectionFactory(); + await conn.open(); + + // Run sql to create the tables + for (var s in schemas) { + await conn.execute(await File('test/migrations/$s.sql').readAsString()); + } + + return PostgreSqlExecutorPool(5, connectionFactory, logger: Logger.root); +} + +Future connectToPostgresPool( + Iterable schemas) async { + var _pool = PgPool( + PgEndpoint( + host: 'localhost', + port: 5432, + database: 'orm_test', + username: Platform.environment['POSTGRES_USERNAME'] ?? 'test', + password: Platform.environment['POSTGRES_PASSWORD'] ?? 'test123', + ), + settings: PgPoolSettings() + ..maxConnectionAge = Duration(hours: 1) + ..concurrency = 200, + ); + + // Run sql to create the tables in a transaction + //await _pool.runTx((conn) async { + // for (var s in schemas) { + // await conn.execute(await File('test/migrations/$s.sql').readAsString()); + // } + //}); + + return PostgreSqlPoolExecutor(_pool, logger: Logger.root); +} diff --git a/packages/orm/angel_orm_postgres/test/migrations/performance.sql b/packages/orm/angel_orm_postgres/test/migrations/performance.sql new file mode 100644 index 00000000..98eff6f9 --- /dev/null +++ b/packages/orm/angel_orm_postgres/test/migrations/performance.sql @@ -0,0 +1,11 @@ +CREATE TEMPORARY TABLE "world" ( + id serial NOT NULL, + randomNumber integer NOT NULL default 0, + PRIMARY KEY (id) +); + +CREATE TEMPORARY TABLE "fortune" ( + id serial NOT NULL, + message varchar(2048) NOT NULL, + PRIMARY KEY (id) +); \ No newline at end of file diff --git a/packages/orm/angel_orm_test/CHANGELOG.md b/packages/orm/angel_orm_test/CHANGELOG.md index 0fdb0445..9ca04bfa 100644 --- a/packages/orm/angel_orm_test/CHANGELOG.md +++ b/packages/orm/angel_orm_test/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## 3.0.3 + +* Added `performance_test` test cases +* Update `edge_case_test` test case to support connection pooling + ## 3.0.2 * Updated linter to `package:lints` diff --git a/packages/orm/angel_orm_test/lib/angel3_orm_test.dart b/packages/orm/angel_orm_test/lib/angel3_orm_test.dart index 2f1878ee..7abe259a 100644 --- a/packages/orm/angel_orm_test/lib/angel3_orm_test.dart +++ b/packages/orm/angel_orm_test/lib/angel3_orm_test.dart @@ -7,3 +7,4 @@ export 'src/has_map_test.dart'; export 'src/has_one_test.dart'; export 'src/many_to_many_test.dart'; export 'src/standalone_test.dart'; +export 'src/performance_test.dart'; diff --git a/packages/orm/angel_orm_test/lib/src/belongs_to_test.dart b/packages/orm/angel_orm_test/lib/src/belongs_to_test.dart index f55f442c..9adf39e8 100644 --- a/packages/orm/angel_orm_test/lib/src/belongs_to_test.dart +++ b/packages/orm/angel_orm_test/lib/src/belongs_to_test.dart @@ -42,12 +42,12 @@ void belongsToTests(FutureOr Function() createExecutor, expect(books, hasLength(1)); var book = books.first; - print(book.toJson()); + //print(book.toJson()); expect(book.id, deathlyHallows!.id); expect(book.name, deathlyHallows!.name); var author = book.author!; - print(AuthorSerializer.toMap(author)); + //print(AuthorSerializer.toMap(author)); expect(author.id, jkRowling!.id); expect(author.name, jkRowling!.name); }); @@ -55,17 +55,17 @@ void belongsToTests(FutureOr Function() createExecutor, test('select one', () async { var query = BookQuery(); query.where!.id.equals(int.parse(deathlyHallows!.id!)); - print(query.compile({})); + //print(query.compile({})); var bookOpt = await query.getOne(executor); expect(bookOpt.isPresent, true); bookOpt.ifPresent((book) { - print(book.toJson()); + //print(book.toJson()); expect(book.id, deathlyHallows!.id); expect(book.name, deathlyHallows!.name); var author = book.author!; - print(AuthorSerializer.toMap(author)); + //print(AuthorSerializer.toMap(author)); expect(author.id, jkRowling!.id); expect(author.name, jkRowling!.name); }); @@ -75,18 +75,18 @@ void belongsToTests(FutureOr Function() createExecutor, var query = BookQuery() ..where!.name.equals('Goblet of Fire') ..orWhere((w) => w.authorId.equals(int.parse(jkRowling!.id!))); - print(query.compile({})); + //print(query.compile({})); var books = await query.get(executor); expect(books, hasLength(1)); var book = books.first; - print(book.toJson()); + //print(book.toJson()); expect(book.id, deathlyHallows!.id); expect(book.name, deathlyHallows!.name); var author = book.author!; - print(AuthorSerializer.toMap(author)); + //print(AuthorSerializer.toMap(author)); expect(author.id, jkRowling!.id); expect(author.name, jkRowling!.name); }); @@ -99,18 +99,18 @@ void belongsToTests(FutureOr Function() createExecutor, query1 ..union(query2) ..unionAll(query3); - print(query1.compile({})); + //print(query1.compile({})); var books = await query1.get(executor); expect(books, hasLength(1)); var book = books.first; - print(book.toJson()); + //print(book.toJson()); expect(book.id, deathlyHallows!.id); expect(book.name, deathlyHallows!.name); var author = book.author!; - print(AuthorSerializer.toMap(author)); + //print(AuthorSerializer.toMap(author)); expect(author.id, jkRowling!.id); expect(author.name, jkRowling!.name); }); @@ -129,9 +129,9 @@ void belongsToTests(FutureOr Function() createExecutor, }); test('delete stream', () async { - printSeparator('Delete stream test'); + //printSeparator('Delete stream test'); var query = BookQuery()..where!.name.equals(deathlyHallows!.name!); - print(query.compile({}, preamble: 'DELETE', withFields: false)); + //print(query.compile({}, preamble: 'DELETE', withFields: false)); var books = await query.delete(executor); expect(books, hasLength(1)); @@ -149,7 +149,7 @@ void belongsToTests(FutureOr Function() createExecutor, var bookOpt = await (query.updateOne(executor)); expect(bookOpt.isPresent, true); bookOpt.ifPresent((book) { - print(book.toJson()); + //print(book.toJson()); expect(book.name, cloned.name); expect(book.author, isNotNull); expect(book.author!.name, jkRowling!.name); diff --git a/packages/orm/angel_orm_test/lib/src/edge_case_test.dart b/packages/orm/angel_orm_test/lib/src/edge_case_test.dart index c52a254f..357f9c3e 100644 --- a/packages/orm/angel_orm_test/lib/src/edge_case_test.dart +++ b/packages/orm/angel_orm_test/lib/src/edge_case_test.dart @@ -15,11 +15,11 @@ void edgeCaseTests(FutureOr Function() createExecutor, tearDown(() => close!(executor)); test('can create object with no id', () async { - var query = UnorthodoxQuery()..values.name = 'Hey'; + var query = UnorthodoxQuery()..values.name = 'World'; var modelOpt = await query.insert(executor); expect(modelOpt.isPresent, true); modelOpt.ifPresent((model) { - expect(model, Unorthodox(name: 'Hey')); + expect(model, Unorthodox(name: 'World')); }); }); @@ -27,12 +27,14 @@ void edgeCaseTests(FutureOr Function() createExecutor, Unorthodox? unorthodox; setUp(() async { + //if (unorthodox == null) { var query = UnorthodoxQuery()..values.name = 'Hey'; var unorthodoxOpt = await query.insert(executor); unorthodoxOpt.ifPresent((value) { unorthodox = value; }); + //} }); test('belongs to', () async { @@ -40,7 +42,7 @@ void edgeCaseTests(FutureOr Function() createExecutor, var modelOpt = await query.insert(executor); expect(modelOpt.isPresent, true); modelOpt.ifPresent((model) { - print(model.toJson()); + //print(model.toJson()); expect(model.id, isNotNull); // Postgres should set this. expect(model.unorthodox, unorthodox); }); @@ -73,7 +75,7 @@ void edgeCaseTests(FutureOr Function() createExecutor, var wjOpt = await query.getOne(executor); expect(wjOpt.isPresent, true); wjOpt.ifPresent((wj) { - print(wj.toJson()); + //print(wj.toJson()); expect(wj.song, girlBlue); }); }); @@ -96,7 +98,7 @@ void edgeCaseTests(FutureOr Function() createExecutor, var wjObj = await query.getOne(executor); expect(wjObj.isPresent, true); wjObj.ifPresent((wj) { - print(wj.toJson()); + //print(wj.toJson()); expect(wj.numbas, numbas); }); }); @@ -114,8 +116,8 @@ void edgeCaseTests(FutureOr Function() createExecutor, var fooOpt = await fooQuery.getOne(executor); expect(fooOpt.isPresent, true); fooOpt.ifPresent((foo) { - print(foo.toJson()); - print(weirdJoin!.toJson()); + //print(foo.toJson()); + //print(weirdJoin!.toJson()); expect(foo.weirdJoins![0].id, weirdJoin!.id); }); }); diff --git a/packages/orm/angel_orm_test/lib/src/has_many_test.dart b/packages/orm/angel_orm_test/lib/src/has_many_test.dart index 4d31a063..7cd47297 100644 --- a/packages/orm/angel_orm_test/lib/src/has_many_test.dart +++ b/packages/orm/angel_orm_test/lib/src/has_many_test.dart @@ -29,7 +29,7 @@ void hasManyTests(FutureOr Function() createExecutor, Fruit? apple, banana; void verify(Tree tree) { - print(tree.fruits!.map(FruitSerializer.toMap).toList()); + //print(tree.fruits!.map(FruitSerializer.toMap).toList()); expect(tree.fruits, hasLength(2)); expect(tree.fruits![0].commonName, apple!.commonName); expect(tree.fruits![1].commonName, banana!.commonName); diff --git a/packages/orm/angel_orm_test/lib/src/has_map_test.dart b/packages/orm/angel_orm_test/lib/src/has_map_test.dart index 97ff210b..03fe9155 100644 --- a/packages/orm/angel_orm_test/lib/src/has_map_test.dart +++ b/packages/orm/angel_orm_test/lib/src/has_map_test.dart @@ -22,10 +22,10 @@ void hasMapTests(FutureOr Function() createExecutor, var modelOpt = await (query.insert(executor)); expect(modelOpt.isPresent, true); modelOpt.ifPresent((model) { - print(model.toString()); + //print(model.toString()); var data = HasMap(value: {'foo': 'bar'}, list: ['1', 2, 3.0]); - print(data.toString()); + //print(data.toString()); expect(model, data); }); @@ -40,7 +40,7 @@ void hasMapTests(FutureOr Function() createExecutor, expect(modelOpt.isPresent, true); if (modelOpt.isPresent) { var model = modelOpt.value; - print(model.toJson()); + //print(model.toJson()); query = HasMapQuery()..values.copyFrom(model); var result = await query.updateOne(executor); expect(result.isPresent, true); @@ -83,7 +83,7 @@ void hasMapTests(FutureOr Function() createExecutor, query.where?.list.equals(['1', 2, 3.0]); - print(query.substitutionValues); + //print(query.substitutionValues); var result = await query.get(executor); expect(result, [initialValue]); diff --git a/packages/orm/angel_orm_test/lib/src/has_one_test.dart b/packages/orm/angel_orm_test/lib/src/has_one_test.dart index 627227ba..4901416e 100644 --- a/packages/orm/angel_orm_test/lib/src/has_one_test.dart +++ b/packages/orm/angel_orm_test/lib/src/has_one_test.dart @@ -18,12 +18,12 @@ void hasOneTests(FutureOr Function() createExecutor, tearDown(() => close!(executor)); test('sets to null if no child', () async { - print(LegQuery().compile({})); + //print(LegQuery().compile({})); var query = LegQuery()..where!.id.equals(int.parse(originalLeg!.id!)); var legOpt = await (query.getOne(executor)); expect(legOpt.isPresent, true); legOpt.ifPresent((leg) { - print(leg.toJson()); + //print(leg.toJson()); expect(leg.name, originalLeg?.name); expect(leg.id, originalLeg?.id); expect(leg.foot, isNull); @@ -82,7 +82,7 @@ void hasOneTests(FutureOr Function() createExecutor, expect(footOpt.isPresent, true); expect(legOpt.isPresent, true); legOpt.ifPresent((leg) { - print(leg.toJson()); + //print(leg.toJson()); expect(leg.name, 'Right'); expect(leg.foot, isNotNull); footOpt.ifPresent((foot) { @@ -102,7 +102,7 @@ void hasOneTests(FutureOr Function() createExecutor, expect(footOpt.isPresent, true); expect(legOpt.isPresent, true); legOpt.ifPresent((leg) { - print(leg.toJson()); + //print(leg.toJson()); expect(leg.name, originalLeg?.name); expect(leg.foot, isNotNull); footOpt.ifPresent((foot) { diff --git a/packages/orm/angel_orm_test/lib/src/many_to_many_test.dart b/packages/orm/angel_orm_test/lib/src/many_to_many_test.dart index 43b54316..5146eda5 100644 --- a/packages/orm/angel_orm_test/lib/src/many_to_many_test.dart +++ b/packages/orm/angel_orm_test/lib/src/many_to_many_test.dart @@ -21,7 +21,9 @@ void manyToManyTests(FutureOr Function() createExecutor, //var rows = await executor.query(null, query, {}); var rows = await executor.query('', query, {}); print('\n${rows.length} row(s):'); - rows.forEach((r) => print(' * $r')); + for (var r in rows) { + print(' * $r'); + } print('==================================================\n\n'); } diff --git a/packages/orm/angel_orm_test/lib/src/models/fortune.dart b/packages/orm/angel_orm_test/lib/src/models/fortune.dart new file mode 100644 index 00000000..3f0a612c --- /dev/null +++ b/packages/orm/angel_orm_test/lib/src/models/fortune.dart @@ -0,0 +1,16 @@ +import 'package:angel3_migration/angel3_migration.dart'; +//import 'package:angel3_model/angel3_model.dart'; +import 'package:angel3_serialize/angel3_serialize.dart'; +import 'package:angel3_orm/angel3_orm.dart'; +import 'package:optional/optional.dart'; + +part 'fortune.g.dart'; + +@serializable +@Orm(tableName: 'fortune') +abstract class _Fortune { + int? id; + + @Column(length: 2048) + String? message; +} diff --git a/packages/orm/angel_orm_test/lib/src/models/fortune.g.dart b/packages/orm/angel_orm_test/lib/src/models/fortune.g.dart new file mode 100644 index 00000000..c5131189 --- /dev/null +++ b/packages/orm/angel_orm_test/lib/src/models/fortune.g.dart @@ -0,0 +1,200 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'fortune.dart'; + +// ************************************************************************** +// MigrationGenerator +// ************************************************************************** + +class FortuneMigration extends Migration { + @override + void up(Schema schema) { + schema.create('fortune', (table) { + table.integer('id'); + table.varChar('message', length: 2048); + }); + } + + @override + void down(Schema schema) { + schema.drop('fortune'); + } +} + +// ************************************************************************** +// OrmGenerator +// ************************************************************************** + +class FortuneQuery extends Query { + FortuneQuery({Query? parent, Set? trampoline}) + : super(parent: parent) { + trampoline ??= {}; + trampoline.add(tableName); + _where = FortuneQueryWhere(this); + } + + @override + final FortuneQueryValues values = FortuneQueryValues(); + + FortuneQueryWhere? _where; + + @override + Map get casts { + return {}; + } + + @override + String get tableName { + return 'fortune'; + } + + @override + List get fields { + return const ['id', 'message']; + } + + @override + FortuneQueryWhere? get where { + return _where; + } + + @override + FortuneQueryWhere newWhereClause() { + return FortuneQueryWhere(this); + } + + static Fortune? parseRow(List row) { + if (row.every((x) => x == null)) { + return null; + } + var model = Fortune(id: (row[0] as int?), message: (row[1] as String?)); + return model; + } + + @override + Optional deserialize(List row) { + return Optional.ofNullable(parseRow(row)); + } +} + +class FortuneQueryWhere extends QueryWhere { + FortuneQueryWhere(FortuneQuery query) + : id = NumericSqlExpressionBuilder(query, 'id'), + message = StringSqlExpressionBuilder(query, 'message'); + + final NumericSqlExpressionBuilder id; + + final StringSqlExpressionBuilder message; + + @override + List get expressionBuilders { + return [id, message]; + } +} + +class FortuneQueryValues extends MapQueryValues { + @override + Map get casts { + return {}; + } + + int? get id { + return (values['id'] as int?); + } + + set id(int? value) => values['id'] = value; + String? get message { + return (values['message'] as String?); + } + + set message(String? value) => values['message'] = value; + void copyFrom(Fortune model) { + id = model.id; + message = model.message; + } +} + +// ************************************************************************** +// JsonModelGenerator +// ************************************************************************** + +@generatedSerializable +class Fortune extends _Fortune { + Fortune({this.id, this.message}); + + @override + int? id; + + @override + String? message; + + Fortune copyWith({int? id, String? message}) { + return Fortune(id: id ?? this.id, message: message ?? this.message); + } + + @override + bool operator ==(other) { + return other is _Fortune && other.id == id && other.message == message; + } + + @override + int get hashCode { + return hashObjects([id, message]); + } + + @override + String toString() { + return 'Fortune(id=$id, message=$message)'; + } + + Map toJson() { + return FortuneSerializer.toMap(this); + } +} + +// ************************************************************************** +// SerializerGenerator +// ************************************************************************** + +const FortuneSerializer fortuneSerializer = FortuneSerializer(); + +class FortuneEncoder extends Converter { + const FortuneEncoder(); + + @override + Map convert(Fortune model) => FortuneSerializer.toMap(model); +} + +class FortuneDecoder extends Converter { + const FortuneDecoder(); + + @override + Fortune convert(Map map) => FortuneSerializer.fromMap(map); +} + +class FortuneSerializer extends Codec { + const FortuneSerializer(); + + @override + FortuneEncoder get encoder => const FortuneEncoder(); + @override + FortuneDecoder get decoder => const FortuneDecoder(); + static Fortune fromMap(Map map) { + return Fortune(id: map['id'] as int?, message: map['message'] as String?); + } + + static Map toMap(_Fortune? model) { + if (model == null) { + return {}; + } + return {'id': model.id, 'message': model.message}; + } +} + +abstract class FortuneFields { + static const List allFields = [id, message]; + + static const String id = 'id'; + + static const String message = 'message'; +} diff --git a/packages/orm/angel_orm_test/lib/src/models/world.dart b/packages/orm/angel_orm_test/lib/src/models/world.dart new file mode 100644 index 00000000..b7505a4e --- /dev/null +++ b/packages/orm/angel_orm_test/lib/src/models/world.dart @@ -0,0 +1,16 @@ +import 'package:angel3_migration/angel3_migration.dart'; +//import 'package:angel3_model/angel3_model.dart'; +import 'package:angel3_serialize/angel3_serialize.dart'; +import 'package:angel3_orm/angel3_orm.dart'; +import 'package:optional/optional.dart'; + +part 'world.g.dart'; + +@serializable +@Orm(tableName: 'world') +abstract class _World { + int? id; + + @Column() + int? randomNumber; +} diff --git a/packages/orm/angel_orm_test/lib/src/models/world.g.dart b/packages/orm/angel_orm_test/lib/src/models/world.g.dart new file mode 100644 index 00000000..d6d442ee --- /dev/null +++ b/packages/orm/angel_orm_test/lib/src/models/world.g.dart @@ -0,0 +1,203 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'world.dart'; + +// ************************************************************************** +// MigrationGenerator +// ************************************************************************** + +class WorldMigration extends Migration { + @override + void up(Schema schema) { + schema.create('world', (table) { + table.integer('id'); + table.integer('randomNumber'); + }); + } + + @override + void down(Schema schema) { + schema.drop('world'); + } +} + +// ************************************************************************** +// OrmGenerator +// ************************************************************************** + +class WorldQuery extends Query { + WorldQuery({Query? parent, Set? trampoline}) : super(parent: parent) { + trampoline ??= {}; + trampoline.add(tableName); + _where = WorldQueryWhere(this); + } + + @override + final WorldQueryValues values = WorldQueryValues(); + + WorldQueryWhere? _where; + + @override + Map get casts { + return {}; + } + + @override + String get tableName { + return 'world'; + } + + @override + List get fields { + return const ['id', 'randomNumber']; + } + + @override + WorldQueryWhere? get where { + return _where; + } + + @override + WorldQueryWhere newWhereClause() { + return WorldQueryWhere(this); + } + + static World? parseRow(List row) { + if (row.every((x) => x == null)) { + return null; + } + var model = World(id: (row[0] as int?), randomNumber: (row[1] as int?)); + return model; + } + + @override + Optional deserialize(List row) { + return Optional.ofNullable(parseRow(row)); + } +} + +class WorldQueryWhere extends QueryWhere { + WorldQueryWhere(WorldQuery query) + : id = NumericSqlExpressionBuilder(query, 'id'), + randomNumber = NumericSqlExpressionBuilder(query, 'randomNumber'); + + final NumericSqlExpressionBuilder id; + + final NumericSqlExpressionBuilder randomNumber; + + @override + List get expressionBuilders { + return [id, randomNumber]; + } +} + +class WorldQueryValues extends MapQueryValues { + @override + Map get casts { + return {}; + } + + int? get id { + return (values['id'] as int?); + } + + set id(int? value) => values['id'] = value; + int? get randomNumber { + return (values['randomNumber'] as int?); + } + + set randomNumber(int? value) => values['randomNumber'] = value; + void copyFrom(World model) { + id = model.id; + randomNumber = model.randomNumber; + } +} + +// ************************************************************************** +// JsonModelGenerator +// ************************************************************************** + +@generatedSerializable +class World extends _World { + World({this.id, this.randomNumber}); + + @override + int? id; + + @override + int? randomNumber; + + World copyWith({int? id, int? randomNumber}) { + return World( + id: id ?? this.id, randomNumber: randomNumber ?? this.randomNumber); + } + + @override + bool operator ==(other) { + return other is _World && + other.id == id && + other.randomNumber == randomNumber; + } + + @override + int get hashCode { + return hashObjects([id, randomNumber]); + } + + @override + String toString() { + return 'World(id=$id, randomNumber=$randomNumber)'; + } + + Map toJson() { + return WorldSerializer.toMap(this); + } +} + +// ************************************************************************** +// SerializerGenerator +// ************************************************************************** + +const WorldSerializer worldSerializer = WorldSerializer(); + +class WorldEncoder extends Converter { + const WorldEncoder(); + + @override + Map convert(World model) => WorldSerializer.toMap(model); +} + +class WorldDecoder extends Converter { + const WorldDecoder(); + + @override + World convert(Map map) => WorldSerializer.fromMap(map); +} + +class WorldSerializer extends Codec { + const WorldSerializer(); + + @override + WorldEncoder get encoder => const WorldEncoder(); + @override + WorldDecoder get decoder => const WorldDecoder(); + static World fromMap(Map map) { + return World( + id: map['id'] as int?, randomNumber: map['randomNumber'] as int?); + } + + static Map toMap(_World? model) { + if (model == null) { + return {}; + } + return {'id': model.id, 'randomNumber': model.randomNumber}; + } +} + +abstract class WorldFields { + static const List allFields = [id, randomNumber]; + + static const String id = 'id'; + + static const String randomNumber = 'randomNumber'; +} diff --git a/packages/orm/angel_orm_test/lib/src/performance_test.dart b/packages/orm/angel_orm_test/lib/src/performance_test.dart new file mode 100644 index 00000000..98896a92 --- /dev/null +++ b/packages/orm/angel_orm_test/lib/src/performance_test.dart @@ -0,0 +1,91 @@ +import 'dart:async'; +import 'dart:math'; + +import 'package:angel3_orm/angel3_orm.dart'; +import 'package:test/test.dart'; + +import 'models/fortune.dart'; +import 'models/world.dart'; + +void performanceTests(FutureOr Function() createExecutor, + {FutureOr Function(QueryExecutor)? close}) { + QueryExecutor? executor; + + int _sampleSize = 1000; + + int concurrency = 200; + + // Generate a random number between 1 and 10000 + int _genRandomId() { + var rand = Random(); + return rand.nextInt(10000) + 1; + } + + setUp(() async { + print("Run setup"); + executor = await createExecutor(); + + /* + for (var i = 0; i < _sampleSize; i++) { + var query = WorldQuery(); + query.values.randomNumber = _genRandomId(); + var world = (await query.insert(executor!)).value; + } + + for (var i = 0; i < _sampleSize; i++) { + var query = FortuneQuery(); + query.values.message = "message ${_genRandomId()}"; + var fortune = (await query.insert(executor!)).value; + } + */ + }); + + tearDown(() => close!(executor!)); + + test('select all concurrency', () async { + var stopwatch = Stopwatch(); + stopwatch.start(); + + // Fire and forget + for (var i = 0; i < concurrency; i++) { + FortuneQuery().get(executor!); + } + + print("Loop time elapsed: ${stopwatch.elapsed.inMilliseconds}"); + + var result = await FortuneQuery().get(executor!); + + print("Final Time elapsed: ${stopwatch.elapsed.inMilliseconds}"); + stopwatch.stop(); + + expect(result, isNotNull); + }); + + test('select one concurrency', () async { + var stopwatch = Stopwatch(); + stopwatch.start(); + + var id = _genRandomId(); + var query = WorldQuery()..where?.id.equals(id); + var result = await query.get(executor!); + + print("Time elapsed: ${stopwatch.elapsed.inMilliseconds}"); + stopwatch.stop(); + + expect(result, isNotNull); + }); + + test('update concurrency', () async { + var stopwatch = Stopwatch(); + stopwatch.start(); + + var id = _genRandomId(); + var query = WorldQuery()..where?.id.equals(id); + var result = await query.get(executor!); + + print("Time elapsed: ${stopwatch.elapsed.inMilliseconds}"); + stopwatch.stop(); + + expect(result, isNotNull); + }); +} diff --git a/packages/orm/angel_orm_test/lib/src/standalone_test.dart b/packages/orm/angel_orm_test/lib/src/standalone_test.dart index f35357a5..f947ac9c 100644 --- a/packages/orm/angel_orm_test/lib/src/standalone_test.dart +++ b/packages/orm/angel_orm_test/lib/src/standalone_test.dart @@ -14,7 +14,7 @@ void standaloneTests(FutureOr Function() createExecutor, ?..familyFriendly.isTrue ..recalledAt.lessThanOrEqualTo(y2k, includeTime: false); var whereClause = query.where?.compile(tableName: 'cars'); - print('Where clause: $whereClause'); + //print('Where clause: $whereClause'); expect(whereClause, 'cars.family_friendly = TRUE AND cars.recalled_at <= \'2000-01-01\''); }); @@ -23,11 +23,11 @@ void standaloneTests(FutureOr Function() createExecutor, // 'id', 'created_at', 'updated_at', 'make', 'description', 'family_friendly', 'recalled_at' // var row = [0, 'Mazda', 'CX9', true, y2k, y2k, y2k]; var row = [0, y2k, y2k, 'Mazda', 'CX9', true, y2k]; - print(row); + //print(row); var carOpt = CarQuery().deserialize(row); expect(carOpt.isPresent, true); carOpt.ifPresent((car) { - print(car.toJson()); + //print(car.toJson()); expect(car.id, '0'); expect(car.make, 'Mazda'); expect(car.description, 'CX9'); @@ -42,17 +42,17 @@ void standaloneTests(FutureOr Function() createExecutor, }); group('queries', () { - late QueryExecutor executor; + QueryExecutor? executor; setUp(() async { executor = await createExecutor(); }); - tearDown(() => close!(executor)); + tearDown(() => close!(executor!)); group('selects', () { test('select all', () async { - var cars = await CarQuery().get(executor); + var cars = await CarQuery().get(executor!); expect(cars, []); }); @@ -63,19 +63,21 @@ void standaloneTests(FutureOr Function() createExecutor, var query = CarQuery(); query.values ..make = 'Ferrarię±' + ..createdAt = y2k + ..updatedAt = y2k ..description = 'Vroom vroom!' ..familyFriendly = false; - ferrari = (await query.insert(executor)).value; + ferrari = (await query.insert(executor!)).value; }); test('where clause is applied', () async { var query = CarQuery()..where!.familyFriendly.isTrue; - var cars = await query.get(executor); + var cars = await query.get(executor!); expect(cars, isEmpty); var sportsCars = CarQuery()..where!.familyFriendly.isFalse; - cars = await sportsCars.get(executor); - print(cars.map((c) => c.toJson())); + cars = await sportsCars.get(executor!); + //print(cars.map((c) => c.toJson())); var car = cars.first; expect(car.make, ferrari!.make); @@ -89,8 +91,8 @@ void standaloneTests(FutureOr Function() createExecutor, var query2 = CarQuery()..where?.familyFriendly.isTrue; var query3 = CarQuery()..where?.description.equals('Submarine'); var union = query1.union(query2).unionAll(query3); - print(union.compile({})); - var cars = await union.get(executor); + //print(union.compile({})); + var cars = await union.get(executor!); expect(cars, hasLength(1)); }); @@ -100,22 +102,22 @@ void standaloneTests(FutureOr Function() createExecutor, ..orWhere((where) => where ..familyFriendly.isTrue ..make.equals('Honda')); - print(query.compile({})); - var cars = await query.get(executor); + //print(query.compile({})); + var cars = await query.get(executor!); expect(cars, hasLength(1)); }); test('limit obeyed', () async { var query = CarQuery()..limit(0); - print(query.compile({})); - var cars = await query.get(executor); + //print(query.compile({})); + var cars = await query.get(executor!); expect(cars, isEmpty); }); test('get one', () async { var id = int.parse(ferrari!.id!); var query = CarQuery()..where!.id.equals(id); - var carOpt = await query.getOne(executor); + var carOpt = await query.getOne(executor!); expect(carOpt.isPresent, true); carOpt.ifPresent((car) { expect(car, ferrari); @@ -125,14 +127,14 @@ void standaloneTests(FutureOr Function() createExecutor, test('delete one', () async { var id = int.parse(ferrari!.id!); var query = CarQuery()..where!.id.equals(id); - var carOpt = await (query.deleteOne(executor)); + var carOpt = await (query.deleteOne(executor!)); expect(carOpt.isPresent, true); carOpt.ifPresent((car) { var car = carOpt.value; expect(car.toJson(), ferrari!.toJson()); }); - var cars = await CarQuery().get(executor); + var cars = await CarQuery().get(executor!); expect(cars, isEmpty); }); @@ -140,9 +142,9 @@ void standaloneTests(FutureOr Function() createExecutor, var query = CarQuery() ..where!.make.equals('Ferrarię±') ..orWhere((w) => w.familyFriendly.isTrue); - print(query.compile({}, preamble: 'DELETE FROM "cars"')); + //print(query.compile({}, preamble: 'DELETE FROM "cars"')); - var cars = await query.delete(executor); + var cars = await query.delete(executor!); expect(cars, hasLength(1)); expect(cars.first.toJson(), ferrari!.toJson()); }); @@ -151,7 +153,7 @@ void standaloneTests(FutureOr Function() createExecutor, var query = CarQuery() ..where!.id.equals(int.parse(ferrari!.id!)) ..values.make = 'Hyundai'; - var cars = await query.update(executor); + var cars = await query.update(executor!); expect(cars, hasLength(1)); expect(cars.first.make, 'Hyundai'); }); @@ -159,11 +161,11 @@ void standaloneTests(FutureOr Function() createExecutor, test('update car', () async { var cloned = ferrari!.copyWith(make: 'Angel'); var query = CarQuery()..values.copyFrom(cloned); - var carOpt = await (query.updateOne(executor)); + var carOpt = await (query.updateOne(executor!)); expect(carOpt.isPresent, true); carOpt.ifPresent((car) { var car = carOpt.value; - print(car.toJson()); + //print(car.toJson()); expect(car.toJson(), cloned.toJson()); }); }); @@ -181,7 +183,7 @@ void standaloneTests(FutureOr Function() createExecutor, ..recalledAt = recalledAt ..createdAt = now ..updatedAt = now; - var carOpt = await (query.insert(executor)); + var carOpt = await (query.insert(executor!)); expect(carOpt.isPresent, true); carOpt.ifPresent((car) { var car = carOpt.value; @@ -203,10 +205,10 @@ void standaloneTests(FutureOr Function() createExecutor, familyFriendly: true, recalledAt: recalledAt); var query = CarQuery()..values.copyFrom(beetle); - var carOpt = await (query.insert(executor)); + var carOpt = await (query.insert(executor!)); expect(carOpt.isPresent, true); carOpt.ifPresent((car) { - print(car.toJson()); + //print(car.toJson()); expect(car.make, beetle.make); expect(car.description, beetle.description); expect(car.familyFriendly, beetle.familyFriendly); diff --git a/packages/orm/angel_orm_test/pubspec.yaml b/packages/orm/angel_orm_test/pubspec.yaml index 932d6dec..58bcf062 100644 --- a/packages/orm/angel_orm_test/pubspec.yaml +++ b/packages/orm/angel_orm_test/pubspec.yaml @@ -1,5 +1,5 @@ name: angel3_orm_test -version: 3.0.2 +version: 3.0.3 description: Common tests for Angel3 ORM. Reference implmentation of the generated ORM files. homepage: https://angel3-framework.web.app/ repository: https://github.com/dukefirehawk/angel/tree/master/packages/orm/angel_orm_test diff --git a/packages/orm/angel_orm_test/scripts/create_tables.sql b/packages/orm/angel_orm_test/scripts/create_tables.sql new file mode 100644 index 00000000..bbe3a56f --- /dev/null +++ b/packages/orm/angel_orm_test/scripts/create_tables.sql @@ -0,0 +1,149 @@ +CREATE TABLE "authors" ( + id serial PRIMARY KEY, + name varchar(255) UNIQUE NOT NULL, + created_at timestamp, + updated_at timestamp +); + +CREATE TABLE "books" ( + id serial PRIMARY KEY, + author_id int NOT NULL, + partner_author_id int, + name varchar(255), + created_at timestamp, + updated_at timestamp +); + +CREATE TABLE "cars" ( + id serial PRIMARY KEY, + make varchar(255) NOT NULL, + description TEXT NOT NULL, + family_friendly BOOLEAN NOT NULL, + recalled_at timestamp, + created_at timestamp, + updated_at timestamp +); + +CREATE TABLE "numbers" ( + id serial PRIMARY KEY, + created_at timestamp, + updated_at timestamp +); + +CREATE TABLE "alphabets" ( + id serial PRIMARY KEY, + value TEXT, + numbers_id int, + created_at timestamp, + updated_at timestamp +); + +CREATE TABLE "feet" ( + id serial PRIMARY KEY, + leg_id int NOT NULL, + n_toes int NOT NULL, + created_at timestamp, + updated_at timestamp +); + +CREATE TABLE "fruits" ( + "id" serial, + "tree_id" int, + "common_name" varchar, + "created_at" timestamp, + "updated_at" timestamp, + PRIMARY KEY(id) +); + +CREATE TABLE "has_cars" ( + id serial PRIMARY KEY, + type int not null, + created_at timestamp, + updated_at timestamp +); + +CREATE TABLE "has_maps" ( + id serial PRIMARY KEY, + value jsonb not null, + list jsonb not null, + created_at timestamp, + updated_at timestamp +); + +CREATE TABLE "legs" ( + id serial PRIMARY KEY, + name varchar(255) NOT NULL, + created_at timestamp, + updated_at timestamp +); + +CREATE TABLE "roles" ( + "id" serial PRIMARY KEY, + "name" varchar(255), + "created_at" timestamp, + "updated_at" timestamp +); + +CREATE TABLE "trees" ( + "id" serial, + "rings" smallint UNIQUE, + "created_at" timestamp, + "updated_at" timestamp, + UNIQUE(rings), + PRIMARY KEY(id) +); + +CREATE TABLE "unorthodoxes" ( + "name" varchar(255), + PRIMARY KEY(name) +); + +CREATE TABLE "users" ( + "id" serial PRIMARY KEY, + "username" varchar(255), + "password" varchar(255), + "email" varchar(255), + "created_at" timestamp, + "updated_at" timestamp +); + +CREATE TABLE "role_users" ( + "id" serial PRIMARY KEY, + "user_id" int NOT NULL, + "role_id" int NOT NULL, + "created_at" timestamp, + "updated_at" timestamp +); + +CREATE TABLE "foos" ( + "bar" varchar(255), + PRIMARY KEY(bar) +); + +CREATE TABLE "weird_joins" ( + "id" serial, + "join_name" varchar(255) references unorthodoxes(name), + PRIMARY KEY(id) +); + +CREATE TABLE "songs" ( + "id" serial, + "weird_join_id" int references weird_joins(id), + "title" varchar(255), + created_at TIMESTAMP, + updated_at TIMESTAMP, + PRIMARY KEY(id) +); + +CREATE TABLE "numbas" ( + "i" int, + "parent" int references weird_joins(id), + created_at TIMESTAMP, + updated_at TIMESTAMP, + PRIMARY KEY(i) +); + +CREATE TABLE "foo_pivots" ( + "weird_join_id" int references weird_joins(id), + "foo_bar" varchar(255) references foos(bar) +); \ No newline at end of file diff --git a/tool/move_repos b/tool/archived/move_repos old mode 100755 new mode 100644 similarity index 100% rename from tool/move_repos rename to tool/archived/move_repos diff --git a/tool/pull_subproject b/tool/archived/pull_subproject old mode 100755 new mode 100644 similarity index 100% rename from tool/pull_subproject rename to tool/archived/pull_subproject diff --git a/tool/performance/AUTHORS.md b/tool/performance/AUTHORS.md new file mode 100644 index 00000000..15258b53 --- /dev/null +++ b/tool/performance/AUTHORS.md @@ -0,0 +1,6 @@ +Primary Authors +=============== + +* __[Thomas Hii](dukefirehawk.apps@gmail.com)__ + + The current main maintainer of the code base. diff --git a/tool/performance/LICENSE b/tool/performance/LICENSE new file mode 100644 index 00000000..df5e0635 --- /dev/null +++ b/tool/performance/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2021, dukefirehawk.com +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/tool/performance/analysis_options.yaml b/tool/performance/analysis_options.yaml new file mode 100644 index 00000000..ea2c9e94 --- /dev/null +++ b/tool/performance/analysis_options.yaml @@ -0,0 +1 @@ +include: package:lints/recommended.yaml \ No newline at end of file diff --git a/tool/performance/pubspec.yaml b/tool/performance/pubspec.yaml new file mode 100644 index 00000000..d4775884 --- /dev/null +++ b/tool/performance/pubspec.yaml @@ -0,0 +1,11 @@ +name: performance_tool +version: 1.0.0 +description: Angel3 performance testing tool +publish_to: none +environment: + sdk: '>=2.12.0 <3.0.0' +published_to: none +dependencies: + http: ^0.13.4 +dev_dependencies: + lints: ^1.0.0 \ No newline at end of file diff --git a/tool/performance/techempower/main.dart b/tool/performance/techempower/main.dart new file mode 100644 index 00000000..dc1e4b56 --- /dev/null +++ b/tool/performance/techempower/main.dart @@ -0,0 +1,104 @@ +import 'dart:isolate'; +import 'package:http/http.dart' as http; + +Future fortunes(var message) async { + var stopwatch = Stopwatch()..start(); + + var url = Uri.http('localhost:3000', '/fortunes'); + var response = await http.get(url); + print('Execution($message) Time: ${stopwatch.elapsed.inMilliseconds}ms'); + stopwatch.stop(); + + if (response.statusCode == 200) { + print('Execution($message): success'); + } else { + print('Execution($message): error'); + } +} + +Future plaintext(var message) async { + var stopwatch = Stopwatch()..start(); + + var url = Uri.http('localhost:3000', '/plaintext'); + var response = await http.get(url); + if (response.statusCode == 200) { + print('Execution($message): success'); + } else { + print('Execution($message): error'); + } + + print('Execution($message) Time: ${stopwatch.elapsed.inMilliseconds}ms'); + stopwatch.stop(); +} + +Future json(var message) async { + var stopwatch = Stopwatch()..start(); + + var url = Uri.http('localhost:3000', '/json'); + var response = await http.get(url); + print('Execution($message) Time: ${stopwatch.elapsed.inMilliseconds}ms'); + stopwatch.stop(); + + if (response.statusCode == 200) { + print('Execution($message): success'); + } else { + print('Execution($message): error'); + } +} + +Future dbUpdate(var message) async { + var stopwatch = Stopwatch()..start(); + + var url = Uri.http('localhost:3000', '/updates', {'queries': "5"}); + var response = await http.get(url); + print('Execution($message) Time: ${stopwatch.elapsed.inMilliseconds}ms'); + stopwatch.stop(); + + if (response.statusCode == 200) { + print('Execution($message): success'); + } else { + print('Execution($message): error'); + } +} + +Future dbSingleQuery(var message) async { + var stopwatch = Stopwatch()..start(); + + var url = Uri.http('localhost:3000', '/db'); + var response = await http.get(url); + print('Execution($message) Time: ${stopwatch.elapsed.inMilliseconds}ms'); + stopwatch.stop(); + + if (response.statusCode == 200) { + print('Execution($message): success'); + } else { + print('Execution($message): error'); + } +} + +Future dbMultipleQuery(var message) async { + var stopwatch = Stopwatch()..start(); + + var url = Uri.http('localhost:3000', '/query', {'queries': "5"}); + var response = await http.get(url); + print('Execution($message) Time: ${stopwatch.elapsed.inMilliseconds}ms'); + stopwatch.stop(); + + if (response.statusCode == 200) { + print('Execution($message): success'); + } else { + print('Execution($message): error'); + } +} + +void main() async { + var concurrency = 100; + + for (var i = 0; i < concurrency; i++) { + Isolate.spawn(dbUpdate, 'Instance_$i'); + } + + await Future.delayed(const Duration(seconds: 10)); + + //print("Exit"); +} diff --git a/tool/performance/tests/techempower.http b/tool/performance/tests/techempower.http new file mode 100644 index 00000000..91b285c3 --- /dev/null +++ b/tool/performance/tests/techempower.http @@ -0,0 +1,19 @@ +### JSON Test +GET http://localhost:3000/json HTTP/1.1 + +### Plaintext Test +GET http://localhost:3000/plaintext HTTP/1.1 + +### Fortunes Test +GET http://localhost:3000/fortunes HTTP/1.1 + +### Db Test +GET http://localhost:3000/db HTTP/1.1 + +### Query test +GET http://localhost:3000/query?queries=20 HTTP/1.1 + +### Update Test +GET http://localhost:3000/updates?queries=20 HTTP/1.1 + +