@deterministic-code/datasources 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ArrayReader.d.ts +12 -0
- package/dist/BatchCommandIBulkWriteStrategy.d.ts +33 -0
- package/dist/BuiltRepository.d.ts +16 -0
- package/dist/BulkCRUDRepository.d.ts +17 -0
- package/dist/BulkCommand.d.ts +5 -0
- package/dist/BulkWriteEnrichment.d.ts +4 -0
- package/dist/CRUDRepository.d.ts +40 -0
- package/dist/CRUDRepositoryBuilder.d.ts +19 -0
- package/dist/CRUDRepositoryOptions.d.ts +7 -0
- package/dist/CRUDRepositoryStatements.d.ts +17 -0
- package/dist/CascadeDeleteEnrichmentStrategy.d.ts +7 -0
- package/dist/CascadeDeleteUpdateEnrichmentStrategy.d.ts +8 -0
- package/dist/CommandMiddlewareHost.d.ts +22 -0
- package/dist/ConsoleCommandMiddleware.d.ts +14 -0
- package/dist/CreateEnrichmentStrategy.d.ts +14 -0
- package/dist/Datasource.d.ts +30 -0
- package/dist/DatasourceError.d.ts +3 -0
- package/dist/DatasourceFactory.d.ts +12 -0
- package/dist/DatasourceOptions.d.ts +1 -0
- package/dist/DatasourceUrl.d.ts +12 -0
- package/dist/DatasourceUrlParser.d.ts +4 -0
- package/dist/DefaultCreateEnrichmentStrategy.d.ts +7 -0
- package/dist/DefaultReadonlyEnrichmentStrategy.d.ts +19 -0
- package/dist/DefaultUpdateEnrichmentStrategy.d.ts +7 -0
- package/dist/DefaultWriteEnrichmentStrategy.d.ts +21 -0
- package/dist/DeleteEnrichmentStrategy.d.ts +11 -0
- package/dist/DriverLoader.d.ts +5 -0
- package/dist/Enrichment.d.ts +13 -0
- package/dist/EnrichmentPropertyType.d.ts +7 -0
- package/dist/ExpectConsoleMiddleware.d.ts +15 -0
- package/dist/FirstResultReader.d.ts +19 -0
- package/dist/HashsetOptimizedReadonlyEnrichmentStrategy.d.ts +9 -0
- package/dist/IBulkWriteStrategy.d.ts +12 -0
- package/dist/ICRUDRepository.d.ts +12 -0
- package/dist/ICRUDRepositoryFactory.d.ts +7 -0
- package/dist/ICommand.d.ts +20 -0
- package/dist/ICommandMiddleware.d.ts +12 -0
- package/dist/IConnection.d.ts +12 -0
- package/dist/IDatasource.d.ts +18 -0
- package/dist/IDatasourceFactory.d.ts +5 -0
- package/dist/IDriver.d.ts +19 -0
- package/dist/IEnriched.d.ts +6 -0
- package/dist/IReader.d.ts +8 -0
- package/dist/IReadonlyEnrichmentStrategy.d.ts +7 -0
- package/dist/IReadonlyRepository.d.ts +11 -0
- package/dist/IRepository.d.ts +4 -0
- package/dist/IRepositoryFactory.d.ts +7 -0
- package/dist/IRepositoryTree.d.ts +4 -0
- package/dist/IRepositoryTreeNode.d.ts +7 -0
- package/dist/ITransaction.d.ts +8 -0
- package/dist/IUnitOfWork.d.ts +5 -0
- package/dist/IWriteEnrichmentStrategy.d.ts +7 -0
- package/dist/Information.d.ts +5 -0
- package/dist/IsolationLevel.d.ts +7 -0
- package/dist/LazyWriteEnrichmentStrategy.d.ts +4 -0
- package/dist/NestedToObjectEnrichmentStrategy.d.ts +10 -0
- package/dist/Parameter.d.ts +7 -0
- package/dist/ParameterDirection.d.ts +7 -0
- package/dist/ProviderCatalog.d.ts +7 -0
- package/dist/ProviderNotImplementedError.d.ts +4 -0
- package/dist/ProviderNotInstalledError.d.ts +4 -0
- package/dist/ReadonlyRepository.d.ts +59 -0
- package/dist/ReadonlyRepositoryBuilder.d.ts +12 -0
- package/dist/ReadonlyRepositoryMappingOptions.d.ts +14 -0
- package/dist/ReadonlyRepositoryStatements.d.ts +8 -0
- package/dist/RepositoryAccess.d.ts +11 -0
- package/dist/RepositoryFactory.d.ts +23 -0
- package/dist/RepositoryNotRegisteredError.d.ts +4 -0
- package/dist/RepositoryTree.d.ts +6 -0
- package/dist/RepositoryTreeNode.d.ts +9 -0
- package/dist/Response.d.ts +7 -0
- package/dist/Row.d.ts +2 -0
- package/dist/SingleCommandIBulkWriteStrategy.d.ts +11 -0
- package/dist/StatementMappers.d.ts +16 -0
- package/dist/StoredEnrichment.d.ts +11 -0
- package/dist/StoredProcedureBulkWriteStrategy.d.ts +27 -0
- package/dist/StreamingReader.d.ts +16 -0
- package/dist/UnitOfWorkTransaction.d.ts +12 -0
- package/dist/UpdateEnrichmentStrategy.d.ts +18 -0
- package/dist/WriteMode.d.ts +1 -0
- package/dist/builders/Field.d.ts +5 -0
- package/dist/builders/Scalar.d.ts +1 -0
- package/dist/builders/index.d.ts +2 -0
- package/dist/collectAsync.d.ts +1 -0
- package/dist/converters/Cell.d.ts +2 -0
- package/dist/converters/ChangeCasingConverter.d.ts +7 -0
- package/dist/converters/Constructable.d.ts +1 -0
- package/dist/converters/MetaDataRowTypeConverter.d.ts +14 -0
- package/dist/converters/PluralizerConverter.d.ts +4 -0
- package/dist/converters/RegexConverter.d.ts +7 -0
- package/dist/converters/ReplaceConverter.d.ts +7 -0
- package/dist/converters/RowTypeConverter.d.ts +10 -0
- package/dist/converters/ToLowerCaseConverter.d.ts +4 -0
- package/dist/converters/ToUpperCaseConverter.d.ts +4 -0
- package/dist/converters/TypeConverter.d.ts +5 -0
- package/dist/createCRUDRepositoryOptions.d.ts +7 -0
- package/dist/createReadonlyRepositoryMappingOptions.d.ts +6 -0
- package/dist/dotEnv.d.ts +5 -0
- package/dist/index.d.ts +119 -0
- package/dist/mappers/ChangeCasingMapper.d.ts +9 -0
- package/dist/mappers/DictionaryMapper.d.ts +8 -0
- package/dist/mappers/FlatToNestedMapper.d.ts +20 -0
- package/dist/mappers/IFlatToNestedMapper.d.ts +6 -0
- package/dist/mappers/IMapper.d.ts +4 -0
- package/dist/mappers/IdentityMapper.d.ts +5 -0
- package/dist/mappers/PluralizerMapper.d.ts +6 -0
- package/dist/mappers/RowTypeMapper.d.ts +6 -0
- package/dist/mappers/TransformConverterMapper.d.ts +5 -0
- package/dist/mappers/TransformMapper.d.ts +6 -0
- package/dist/mappers/TypeMap.d.ts +8 -0
- package/dist/mappers/TypeMapGroup.d.ts +5 -0
- package/dist/mocks/index.d.ts +9 -0
- package/dist/mocks/mock-command.d.ts +28 -0
- package/dist/mocks/mock-connection.d.ts +22 -0
- package/dist/mocks/mock-datasource.d.ts +24 -0
- package/dist/mocks/mock-driver.d.ts +15 -0
- package/dist/mocks/mock-reader.d.ts +14 -0
- package/dist/mocks/mock-repository.d.ts +42 -0
- package/dist/mocks/mock-select-statement.d.ts +10 -0
- package/dist/mocks/mock-transaction.d.ts +16 -0
- package/dist/mocks/mock-where.d.ts +8 -0
- package/dist/parametersToRecord.d.ts +2 -0
- package/dist/readerToAsyncIterable.d.ts +2 -0
- package/dist/splitStatements.d.ts +1 -0
- package/dist/statementParameters.d.ts +1 -0
- package/dist/statementUsesParameter.d.ts +1 -0
- package/dist/statements/Aggregate.d.ts +6 -0
- package/dist/statements/ArrayExpression.d.ts +5 -0
- package/dist/statements/BulkDeleteStatement.d.ts +11 -0
- package/dist/statements/BulkDeleteStatementTextRenderer.d.ts +4 -0
- package/dist/statements/BulkInsertStatement.d.ts +12 -0
- package/dist/statements/BulkInsertStatementTextRenderer.d.ts +4 -0
- package/dist/statements/BulkUpdateStatement.d.ts +16 -0
- package/dist/statements/BulkUpdateStatementTextRenderer.d.ts +8 -0
- package/dist/statements/CheckConstraint.d.ts +7 -0
- package/dist/statements/Cmp.d.ts +9 -0
- package/dist/statements/Column.d.ts +9 -0
- package/dist/statements/ColumnDefault.d.ts +5 -0
- package/dist/statements/Condition.d.ts +3 -0
- package/dist/statements/ConditionRenderer.d.ts +12 -0
- package/dist/statements/CreateIndexStatement.d.ts +10 -0
- package/dist/statements/CreateIndexStatementTextRenderer.d.ts +5 -0
- package/dist/statements/CreateTableStatement.d.ts +11 -0
- package/dist/statements/CreateTableStatementTextRenderer.d.ts +18 -0
- package/dist/statements/DefaultStatementTextRenderer.d.ts +5 -0
- package/dist/statements/DeleteStatement.d.ts +9 -0
- package/dist/statements/DeleteStatementTextRenderer.d.ts +7 -0
- package/dist/statements/ExecuteStoredProcedureStatement.d.ts +9 -0
- package/dist/statements/ExecuteStoredProcedureStatementTextRenderer.d.ts +4 -0
- package/dist/statements/ExpressionEvaluator.d.ts +5 -0
- package/dist/statements/ExpressionRenderer.d.ts +8 -0
- package/dist/statements/ForeignKey.d.ts +19 -0
- package/dist/statements/IBulkDeleteStatement.d.ts +3 -0
- package/dist/statements/IBulkInsertStatement.d.ts +3 -0
- package/dist/statements/IBulkUpdateStatement.d.ts +3 -0
- package/dist/statements/ICondition.d.ts +4 -0
- package/dist/statements/IConditionVisitor.d.ts +6 -0
- package/dist/statements/ICreateIndexStatement.d.ts +3 -0
- package/dist/statements/ICreateTableStatement.d.ts +3 -0
- package/dist/statements/IDeleteStatement.d.ts +5 -0
- package/dist/statements/IExecuteStoredProcedureStatement.d.ts +3 -0
- package/dist/statements/IExpression.d.ts +2 -0
- package/dist/statements/IInsertStatement.d.ts +3 -0
- package/dist/statements/IReturnStatement.d.ts +3 -0
- package/dist/statements/ISelectStatement.d.ts +5 -0
- package/dist/statements/IStatement.d.ts +4 -0
- package/dist/statements/IStatementVisitor.d.ts +26 -0
- package/dist/statements/ITableConstraint.d.ts +5 -0
- package/dist/statements/IUpdateStatement.d.ts +5 -0
- package/dist/statements/IWhere.d.ts +6 -0
- package/dist/statements/Identifier.d.ts +5 -0
- package/dist/statements/InsertStatement.d.ts +9 -0
- package/dist/statements/InsertStatementTextRenderer.d.ts +4 -0
- package/dist/statements/Join.d.ts +14 -0
- package/dist/statements/Literal.d.ts +6 -0
- package/dist/statements/Native.d.ts +5 -0
- package/dist/statements/NotNull.d.ts +4 -0
- package/dist/statements/Operator.d.ts +107 -0
- package/dist/statements/OrderBy.d.ts +10 -0
- package/dist/statements/PrimaryKey.d.ts +7 -0
- package/dist/statements/ReturnStatement.d.ts +9 -0
- package/dist/statements/ReturnStatementTextRenderer.d.ts +4 -0
- package/dist/statements/SelectInsertedIdStatement.d.ts +8 -0
- package/dist/statements/SelectInsertedIdStatementTextRenderer.d.ts +4 -0
- package/dist/statements/SelectStatement.d.ts +20 -0
- package/dist/statements/SelectStatementOptions.d.ts +15 -0
- package/dist/statements/SelectStatementTextRenderer.d.ts +19 -0
- package/dist/statements/StatementEvaluator.d.ts +4 -0
- package/dist/statements/StatementParser.d.ts +4 -0
- package/dist/statements/StatementRenderer.d.ts +42 -0
- package/dist/statements/StatementTextRenderer.d.ts +14 -0
- package/dist/statements/Subquery.d.ts +6 -0
- package/dist/statements/UniqueConstraint.d.ts +7 -0
- package/dist/statements/UpdateStatement.d.ts +14 -0
- package/dist/statements/UpdateStatementTextRenderer.d.ts +14 -0
- package/dist/statements/Where.d.ts +16 -0
- package/dist/statements/WhereText.d.ts +8 -0
- package/dist/statements/index.d.ts +75 -0
- package/dist/stubs/StubCommand.d.ts +15 -0
- package/dist/stubs/StubConnection.d.ts +13 -0
- package/dist/stubs/StubDatasource.d.ts +20 -0
- package/dist/stubs/StubReader.d.ts +11 -0
- package/dist/stubs/StubRepository.d.ts +22 -0
- package/dist/stubs/StubTransaction.d.ts +10 -0
- package/dist/stubs/createStubDatasource.d.ts +2 -0
- package/dist/stubs/index.d.ts +7 -0
- package/dist/stubs/notImplemented.d.ts +1 -0
- package/package.json +24 -4
- package/src/ArrayReader.test.ts +23 -0
- package/src/ArrayReader.ts +32 -0
- package/src/BatchCommandIBulkWriteStrategy.ts +228 -0
- package/src/BuiltRepository.ts +17 -0
- package/src/BulkCRUDRepository.test.ts +183 -0
- package/src/BulkCRUDRepository.ts +84 -0
- package/src/BulkCommand.ts +6 -0
- package/src/BulkWriteEnrichment.ts +19 -0
- package/src/BulkWriteStrategy.test.ts +293 -0
- package/src/CRUDRepository.test.ts +420 -0
- package/src/CRUDRepository.ts +264 -0
- package/src/CRUDRepositoryBuilder.ts +53 -0
- package/src/CRUDRepositoryOptions.test.ts +76 -0
- package/src/CRUDRepositoryOptions.ts +8 -0
- package/src/CRUDRepositoryStatements.ts +43 -0
- package/src/CascadeDeleteEnrichmentStrategy.ts +51 -0
- package/src/CascadeDeleteUpdateEnrichmentStrategy.ts +28 -0
- package/src/Command.test.ts +105 -0
- package/src/CommandMiddlewareHost.test.ts +98 -0
- package/src/CommandMiddlewareHost.ts +113 -0
- package/src/Connection.test.ts +34 -0
- package/src/ConsoleCommandMiddleware.test.ts +149 -0
- package/src/ConsoleCommandMiddleware.ts +64 -0
- package/src/CreateEnrichmentStrategy.ts +82 -0
- package/src/Datasource.test.ts +63 -0
- package/src/Datasource.ts +152 -0
- package/src/DatasourceError.test.ts +10 -0
- package/src/DatasourceError.ts +6 -0
- package/src/DatasourceFactory.test.ts +159 -0
- package/src/DatasourceFactory.ts +86 -0
- package/src/DatasourceOptions.ts +1 -0
- package/src/DatasourceUrl.ts +37 -0
- package/src/DatasourceUrlParser.test.ts +173 -0
- package/src/DatasourceUrlParser.ts +188 -0
- package/src/DefaultCreateEnrichmentStrategy.ts +21 -0
- package/src/DefaultReadonlyEnrichmentStrategy.ts +164 -0
- package/src/DefaultUpdateEnrichmentStrategy.ts +40 -0
- package/src/DefaultWriteEnrichmentStrategy.ts +151 -0
- package/src/DeleteEnrichmentStrategy.ts +56 -0
- package/src/DriverLoader.ts +97 -0
- package/src/Enrichment.ts +15 -0
- package/src/EnrichmentPropertyType.ts +9 -0
- package/src/ExpectConsoleMiddleware.test.ts +79 -0
- package/src/ExpectConsoleMiddleware.ts +47 -0
- package/src/FirstResultReader.test.ts +43 -0
- package/src/FirstResultReader.ts +49 -0
- package/src/HashsetOptimizedReadonlyEnrichmentStrategy.ts +80 -0
- package/src/IBulkWriteStrategy.ts +13 -0
- package/src/ICRUDRepository.ts +13 -0
- package/src/ICRUDRepositoryFactory.ts +8 -0
- package/src/ICommand.ts +45 -0
- package/src/ICommandMiddleware.ts +20 -0
- package/src/IConnection.ts +13 -0
- package/src/IDatasource.ts +27 -0
- package/src/IDatasourceFactory.ts +6 -0
- package/src/IDriver.ts +24 -0
- package/src/IEnriched.ts +11 -0
- package/src/IReader.ts +8 -0
- package/src/IReadonlyEnrichmentStrategy.ts +19 -0
- package/src/IReadonlyRepository.ts +21 -0
- package/src/IRepository.ts +5 -0
- package/src/IRepositoryFactory.ts +8 -0
- package/src/IRepositoryTree.ts +5 -0
- package/src/IRepositoryTreeNode.ts +8 -0
- package/src/ITransaction.ts +9 -0
- package/src/IUnitOfWork.ts +5 -0
- package/src/IWriteEnrichmentStrategy.ts +8 -0
- package/src/Information.ts +5 -0
- package/src/IsolationLevel.test.ts +13 -0
- package/src/IsolationLevel.ts +8 -0
- package/src/LazyWriteEnrichmentStrategy.ts +5 -0
- package/src/NestedToObjectEnrichmentStrategy.ts +33 -0
- package/src/Parameter.ts +8 -0
- package/src/ParameterDirection.ts +9 -0
- package/src/ProviderCatalog.test.ts +110 -0
- package/src/ProviderCatalog.ts +21 -0
- package/src/ProviderNotImplementedError.test.ts +11 -0
- package/src/ProviderNotImplementedError.ts +9 -0
- package/src/ProviderNotInstalledError.test.ts +11 -0
- package/src/ProviderNotInstalledError.ts +9 -0
- package/src/ReadonlyRepository.test.ts +406 -0
- package/src/ReadonlyRepository.ts +276 -0
- package/src/ReadonlyRepositoryBuilder.ts +53 -0
- package/src/ReadonlyRepositoryMappingOptions.ts +15 -0
- package/src/ReadonlyRepositoryStatements.ts +13 -0
- package/src/RepositoryAccess.ts +14 -0
- package/src/RepositoryFactory.test.ts +94 -0
- package/src/RepositoryFactory.ts +115 -0
- package/src/RepositoryNotRegisteredError.ts +9 -0
- package/src/RepositoryTree.ts +6 -0
- package/src/RepositoryTreeNode.ts +11 -0
- package/src/Response.ts +15 -0
- package/src/Row.ts +3 -0
- package/src/SingleCommandIBulkWriteStrategy.ts +35 -0
- package/src/StatementMappers.test.ts +221 -0
- package/src/StatementMappers.ts +110 -0
- package/src/StoredEnrichment.ts +25 -0
- package/src/StoredProcedureBulkWriteStrategy.ts +164 -0
- package/src/StreamingReader.test.ts +126 -0
- package/src/StreamingReader.ts +64 -0
- package/src/Transaction.test.ts +37 -0
- package/src/UnitOfWorkTransaction.test.ts +43 -0
- package/src/UnitOfWorkTransaction.ts +50 -0
- package/src/UpdateEnrichmentStrategy.ts +97 -0
- package/src/UserViewCRUDRepository.test.ts +323 -0
- package/src/WriteMode.ts +1 -0
- package/src/builders/Field.ts +6 -0
- package/src/builders/Scalar.ts +1 -0
- package/src/builders/builders.test.ts +404 -0
- package/src/builders/index.ts +2 -0
- package/src/collectAsync.ts +7 -0
- package/src/converters/Cell.ts +10 -0
- package/src/converters/ChangeCasingConverter.test.ts +34 -0
- package/src/converters/ChangeCasingConverter.ts +59 -0
- package/src/converters/Constructable.ts +1 -0
- package/src/converters/MetaDataRowTypeConverter.ts +80 -0
- package/src/converters/PluralizerConverter.test.ts +43 -0
- package/src/converters/PluralizerConverter.ts +67 -0
- package/src/converters/RegexConverter.test.ts +11 -0
- package/src/converters/RegexConverter.ts +14 -0
- package/src/converters/ReplaceConverter.test.ts +9 -0
- package/src/converters/ReplaceConverter.ts +6 -0
- package/src/converters/RowTypeConverter.test.ts +79 -0
- package/src/converters/RowTypeConverter.ts +33 -0
- package/src/converters/ToLowerCaseConverter.test.ts +9 -0
- package/src/converters/ToLowerCaseConverter.ts +4 -0
- package/src/converters/ToUpperCaseConverter.test.ts +9 -0
- package/src/converters/ToUpperCaseConverter.ts +4 -0
- package/src/converters/TypeConverter.ts +6 -0
- package/src/createCRUDRepositoryOptions.ts +21 -0
- package/src/createReadonlyRepositoryMappingOptions.ts +26 -0
- package/src/dotEnv.test.ts +21 -0
- package/src/dotEnv.ts +16 -0
- package/src/edgeCases.test.ts +485 -0
- package/src/index.ts +222 -0
- package/src/mappers/ChangeCasingMapper.test.ts +32 -0
- package/src/mappers/ChangeCasingMapper.ts +15 -0
- package/src/mappers/DictionaryMapper.test.ts +69 -0
- package/src/mappers/DictionaryMapper.ts +16 -0
- package/src/mappers/FlatToNestedMapper.test.ts +269 -0
- package/src/mappers/FlatToNestedMapper.ts +127 -0
- package/src/mappers/IFlatToNestedMapper.ts +7 -0
- package/src/mappers/IMapper.ts +4 -0
- package/src/mappers/IdentityMapper.test.ts +12 -0
- package/src/mappers/IdentityMapper.ts +6 -0
- package/src/mappers/PluralizerMapper.test.ts +40 -0
- package/src/mappers/PluralizerMapper.ts +9 -0
- package/src/mappers/RowTypeMapper.test.ts +12 -0
- package/src/mappers/RowTypeMapper.ts +13 -0
- package/src/mappers/TransformConverterMapper.test.ts +181 -0
- package/src/mappers/TransformConverterMapper.ts +11 -0
- package/src/mappers/TransformMapper.ts +8 -0
- package/src/mappers/TypeMap.ts +10 -0
- package/src/mappers/TypeMapGroup.ts +6 -0
- package/src/mappers/transformMapper.test.ts +66 -0
- package/src/mocks/MockCommand.test.ts +75 -0
- package/src/mocks/MockConnection.test.ts +34 -0
- package/src/mocks/MockDatasource.test.ts +40 -0
- package/src/mocks/MockReader.test.ts +17 -0
- package/src/mocks/MockRepository.test.ts +32 -0
- package/src/mocks/MockSelectStatement.test.ts +11 -0
- package/src/mocks/MockTransaction.test.ts +17 -0
- package/src/mocks/MockWhere.test.ts +11 -0
- package/src/mocks/index.ts +9 -0
- package/src/mocks/mock-command.ts +66 -0
- package/src/mocks/mock-connection.ts +57 -0
- package/src/mocks/mock-datasource.ts +65 -0
- package/src/mocks/mock-driver.ts +27 -0
- package/src/mocks/mock-reader.ts +38 -0
- package/src/mocks/mock-repository.ts +91 -0
- package/src/mocks/mock-select-statement.ts +17 -0
- package/src/mocks/mock-transaction.ts +31 -0
- package/src/mocks/mock-where.ts +13 -0
- package/src/parametersToRecord.test.ts +19 -0
- package/src/parametersToRecord.ts +11 -0
- package/src/public-surface.test.ts +23 -0
- package/src/readWithWhereClauseAndCustomFlatToNestedObjectMappingOptions.test.ts +126 -0
- package/src/readerToAsyncIterable.test.ts +39 -0
- package/src/readerToAsyncIterable.ts +11 -0
- package/src/repository.test.ts +410 -0
- package/src/repository.type.test.ts +29 -0
- package/src/splitStatements.ts +99 -0
- package/src/statementParameters.ts +22 -0
- package/src/statementUsesParameter.ts +4 -0
- package/src/statements/Aggregate.ts +9 -0
- package/src/statements/ArrayExpression.ts +5 -0
- package/src/statements/BulkDeleteStatement.ts +18 -0
- package/src/statements/BulkDeleteStatementTextRenderer.ts +16 -0
- package/src/statements/BulkInsertStatement.ts +37 -0
- package/src/statements/BulkInsertStatementTextRenderer.ts +10 -0
- package/src/statements/BulkUpdateStatement.ts +24 -0
- package/src/statements/BulkUpdateStatementTextRenderer.ts +29 -0
- package/src/statements/CheckConstraint.ts +9 -0
- package/src/statements/Cmp.ts +12 -0
- package/src/statements/Column.ts +10 -0
- package/src/statements/ColumnDefault.ts +6 -0
- package/src/statements/Condition.ts +3 -0
- package/src/statements/ConditionRenderer.ts +17 -0
- package/src/statements/CreateIndexStatement.ts +18 -0
- package/src/statements/CreateIndexStatementTextRenderer.ts +11 -0
- package/src/statements/CreateTableStatement.ts +18 -0
- package/src/statements/CreateTableStatementTextRenderer.ts +92 -0
- package/src/statements/DefaultStatementTextRenderer.ts +9 -0
- package/src/statements/DeleteStatement.ts +15 -0
- package/src/statements/DeleteStatementTextRenderer.ts +12 -0
- package/src/statements/ExecuteStoredProcedureStatement.ts +15 -0
- package/src/statements/ExecuteStoredProcedureStatementTextRenderer.ts +15 -0
- package/src/statements/ExpressionEvaluator.ts +166 -0
- package/src/statements/ExpressionRenderer.ts +161 -0
- package/src/statements/ForeignKey.ts +23 -0
- package/src/statements/IBulkDeleteStatement.ts +3 -0
- package/src/statements/IBulkInsertStatement.ts +3 -0
- package/src/statements/IBulkUpdateStatement.ts +3 -0
- package/src/statements/ICondition.ts +5 -0
- package/src/statements/IConditionVisitor.ts +7 -0
- package/src/statements/ICreateIndexStatement.ts +3 -0
- package/src/statements/ICreateTableStatement.ts +3 -0
- package/src/statements/IDeleteStatement.ts +6 -0
- package/src/statements/IExecuteStoredProcedureStatement.ts +3 -0
- package/src/statements/IExpression.ts +1 -0
- package/src/statements/IInsertStatement.ts +3 -0
- package/src/statements/IReturnStatement.ts +3 -0
- package/src/statements/ISelectStatement.ts +6 -0
- package/src/statements/IStatement.ts +5 -0
- package/src/statements/IStatementVisitor.ts +27 -0
- package/src/statements/ITableConstraint.ts +6 -0
- package/src/statements/IUpdateStatement.ts +6 -0
- package/src/statements/IWhere.ts +7 -0
- package/src/statements/Identifier.ts +5 -0
- package/src/statements/InsertStatement.ts +15 -0
- package/src/statements/InsertStatementTextRenderer.ts +7 -0
- package/src/statements/Join.ts +26 -0
- package/src/statements/Literal.ts +6 -0
- package/src/statements/Native.ts +5 -0
- package/src/statements/NotNull.ts +3 -0
- package/src/statements/Operator.ts +234 -0
- package/src/statements/OrderBy.ts +19 -0
- package/src/statements/PrimaryKey.ts +9 -0
- package/src/statements/ReturnStatement.ts +15 -0
- package/src/statements/ReturnStatementTextRenderer.ts +15 -0
- package/src/statements/SelectInsertedIdStatement.test.ts +11 -0
- package/src/statements/SelectInsertedIdStatement.ts +11 -0
- package/src/statements/SelectInsertedIdStatementTextRenderer.ts +7 -0
- package/src/statements/SelectStatement.ts +34 -0
- package/src/statements/SelectStatementOptions.ts +16 -0
- package/src/statements/SelectStatementTextRenderer.ts +71 -0
- package/src/statements/StatementCompile.test.ts +231 -0
- package/src/statements/StatementEvaluator.ts +5 -0
- package/src/statements/StatementParser.test.ts +304 -0
- package/src/statements/StatementParser.ts +461 -0
- package/src/statements/StatementRenderer.ts +120 -0
- package/src/statements/StatementTextRenderer.ts +60 -0
- package/src/statements/Subquery.ts +6 -0
- package/src/statements/UniqueConstraint.ts +9 -0
- package/src/statements/UpdateStatement.ts +34 -0
- package/src/statements/UpdateStatementTextRenderer.ts +63 -0
- package/src/statements/Where.ts +250 -0
- package/src/statements/WhereText.ts +10 -0
- package/src/statements/index.ts +75 -0
- package/src/stubs/StubCommand.ts +30 -0
- package/src/stubs/StubConnection.ts +21 -0
- package/src/stubs/StubDatasource.test.ts +99 -0
- package/src/stubs/StubDatasource.ts +60 -0
- package/src/stubs/StubReader.ts +25 -0
- package/src/stubs/StubRepository.ts +42 -0
- package/src/stubs/StubTransaction.ts +17 -0
- package/src/stubs/createStubDatasource.ts +4 -0
- package/src/stubs/index.ts +7 -0
- package/src/stubs/notImplemented.ts +5 -0
- package/src/tests/contact/Address.ts +14 -0
- package/src/tests/contact/AddressType.ts +6 -0
- package/src/tests/contact/Base.ts +7 -0
- package/src/tests/contact/Contact.ts +12 -0
- package/src/tests/contact/ContactBase.ts +8 -0
- package/src/tests/contact/ContactChangeLog.ts +8 -0
- package/src/tests/contact/ContactGroup.ts +6 -0
- package/src/tests/contact/ContactGroupBase.ts +6 -0
- package/src/tests/contact/ContactGroupMember.ts +6 -0
- package/src/tests/contact/ContactSource.ts +6 -0
- package/src/tests/contact/ContactType.ts +6 -0
- package/src/tests/contact/ContactsService.test.ts +118 -0
- package/src/tests/contact/ContactsService.ts +32 -0
- package/src/tests/contact/ImportContactRequest.ts +4 -0
- package/src/tests/contact/ImportContactResult.ts +5 -0
- package/src/tests/contact/LegacyContact.ts +7 -0
- package/src/tests/contact/LegacyMigrationResult.ts +5 -0
- package/src/tests/contact/Phone.ts +9 -0
- package/src/tests/contact/PhoneType.ts +6 -0
- package/src/tests/contact/asContactsView.ts +63 -0
- package/src/tests/contact/contactColumns.ts +40 -0
- package/src/tests/contact/contactLookupIds.ts +32 -0
- package/src/tests/contact/contactLookups.ts +6 -0
- package/src/tests/contact/contactsParity.ts +63 -0
- package/src/tests/contact/contactsServiceOf.ts +38 -0
- package/src/tests/contact/contactsView.ts +31 -0
- package/src/tests/contact/expectContactsView.ts +23 -0
- package/src/tests/contact/extraContact.ts +61 -0
- package/src/tests/contact/logMapped.ts +5 -0
- package/src/tests/contact/seedContacts.ts +31 -0
- package/README.md +0 -3
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { MemoryDriver } from "@datasources/memory";
|
|
3
|
+
import { collectAsync } from "./collectAsync.ts";
|
|
4
|
+
import { BulkCRUDRepository } from "./BulkCRUDRepository.ts";
|
|
5
|
+
import { DefaultWriteEnrichmentStrategy } from "./DefaultWriteEnrichmentStrategy.ts";
|
|
6
|
+
import { DatasourceError } from "./DatasourceError.ts";
|
|
7
|
+
import type { IConnection } from "./IConnection.ts";
|
|
8
|
+
|
|
9
|
+
class Parent {
|
|
10
|
+
id = 0;
|
|
11
|
+
label = "";
|
|
12
|
+
children: Child[] = [];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
class Child {
|
|
16
|
+
id = 0;
|
|
17
|
+
parent_id = 0;
|
|
18
|
+
label = "";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const setup = async (): Promise<IConnection> => {
|
|
22
|
+
const connection = new MemoryDriver().connection();
|
|
23
|
+
await connection.open();
|
|
24
|
+
return connection;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const relate = (parentsRepo: BulkCRUDRepository<Parent>, childrenRepo: BulkCRUDRepository<Child>) => {
|
|
28
|
+
parentsRepo.addEnriched({ destinationProperty: "children", sourceProperty: "id", relatedKey: "parent_id", type: "list" }, childrenRepo);
|
|
29
|
+
return parentsRepo;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
describe("BulkCRUDRepository", () => {
|
|
33
|
+
it("creates parents and children", async () => {
|
|
34
|
+
const connection = await setup();
|
|
35
|
+
const parentsRepo = new BulkCRUDRepository(Parent, connection);
|
|
36
|
+
const childrenRepo = new BulkCRUDRepository(Child, connection);
|
|
37
|
+
const ids = await relate(parentsRepo, childrenRepo).createBulk([
|
|
38
|
+
Object.assign(new Parent(), { children: [Object.assign(new Child(), { label: "a" })] }),
|
|
39
|
+
Object.assign(new Parent(), {
|
|
40
|
+
children: [Object.assign(new Child(), { label: "b" }), Object.assign(new Child(), { label: "c" })],
|
|
41
|
+
}),
|
|
42
|
+
]);
|
|
43
|
+
const parents = await collectAsync(parentsRepo.read());
|
|
44
|
+
const children = await collectAsync(childrenRepo.read());
|
|
45
|
+
expect(parents).toHaveLength(2);
|
|
46
|
+
expect(parents.every((parent) => parent.id > 0)).toBe(true);
|
|
47
|
+
expect(children).toHaveLength(3);
|
|
48
|
+
expect(ids.map((row) => row.id).sort()).toEqual(parents.map((parent) => parent.id).sort());
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("createBulk writes each parent", async () => {
|
|
52
|
+
const parentsRepo = new BulkCRUDRepository(Parent, await setup());
|
|
53
|
+
const ids = await parentsRepo.createBulk([
|
|
54
|
+
Object.assign(new Parent(), { label: "a" }),
|
|
55
|
+
Object.assign(new Parent(), { label: "b" }),
|
|
56
|
+
]);
|
|
57
|
+
const parents = await collectAsync(parentsRepo.read());
|
|
58
|
+
expect(parents).toHaveLength(2);
|
|
59
|
+
expect(ids.map((row) => row.id).sort()).toEqual(parents.map((parent) => parent.id).sort());
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("createBulk returns composite ids", async () => {
|
|
63
|
+
class Coded {
|
|
64
|
+
code = "";
|
|
65
|
+
code_extra = "";
|
|
66
|
+
label = "";
|
|
67
|
+
}
|
|
68
|
+
const codesRepo = new BulkCRUDRepository<Coded>(await setup(), {
|
|
69
|
+
type: Coded,
|
|
70
|
+
ids: ["code", "code_extra"],
|
|
71
|
+
});
|
|
72
|
+
const ids = await codesRepo.createBulk([
|
|
73
|
+
Object.assign(new Coded(), { code: "a", code_extra: "ab", label: "one" }),
|
|
74
|
+
Object.assign(new Coded(), { code: "b", code_extra: "ba", label: "two" }),
|
|
75
|
+
]);
|
|
76
|
+
expect(ids).toEqual([
|
|
77
|
+
Object.assign(new Coded(), { code: "a", code_extra: "ab", label: "one" }),
|
|
78
|
+
Object.assign(new Coded(), { code: "b", code_extra: "ba", label: "two" }),
|
|
79
|
+
]);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("keeps the first write when a later bulk update throws", async () => {
|
|
83
|
+
const parentsRepo = new BulkCRUDRepository(Parent, await setup());
|
|
84
|
+
const first = await parentsRepo.create(Object.assign(new Parent(), { label: "a" }));
|
|
85
|
+
expect(first.id).toBeGreaterThan(0);
|
|
86
|
+
const second = await parentsRepo.create(Object.assign(new Parent(), { label: "b" }));
|
|
87
|
+
expect(second.id).toBeGreaterThan(0);
|
|
88
|
+
let seen = 0;
|
|
89
|
+
parentsRepo.addMiddleware({
|
|
90
|
+
beforeCommand() {
|
|
91
|
+
seen += 1;
|
|
92
|
+
if (seen === 2) throw new DatasourceError("stop");
|
|
93
|
+
},
|
|
94
|
+
afterCommand() {},
|
|
95
|
+
});
|
|
96
|
+
await expect(parentsRepo.updateBulk([
|
|
97
|
+
Object.assign(new Parent(), { id: first.id, label: "changed" }),
|
|
98
|
+
Object.assign(new Parent(), { id: first.id, label: "later" }),
|
|
99
|
+
])).rejects.toThrow(/stop/);
|
|
100
|
+
const stored = await collectAsync(parentsRepo.read());
|
|
101
|
+
expect(stored.find((parent) => parent.id === first.id)?.label).toBe("changed");
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("rolls back a bulk update when useTransaction is on", async () => {
|
|
105
|
+
const parentsRepo = new BulkCRUDRepository<Parent>(await setup(), {
|
|
106
|
+
type: Parent,
|
|
107
|
+
write: new DefaultWriteEnrichmentStrategy(undefined, undefined, undefined, true),
|
|
108
|
+
});
|
|
109
|
+
const first = await parentsRepo.create(Object.assign(new Parent(), { label: "a" }));
|
|
110
|
+
expect(first.id).toBeGreaterThan(0);
|
|
111
|
+
const second = await parentsRepo.create(Object.assign(new Parent(), { label: "b" }));
|
|
112
|
+
expect(second.id).toBeGreaterThan(0);
|
|
113
|
+
let seen = 0;
|
|
114
|
+
parentsRepo.addMiddleware({
|
|
115
|
+
beforeCommand() {
|
|
116
|
+
seen += 1;
|
|
117
|
+
if (seen === 2) throw new DatasourceError("stop");
|
|
118
|
+
},
|
|
119
|
+
afterCommand() {},
|
|
120
|
+
});
|
|
121
|
+
await expect(parentsRepo.updateBulk([
|
|
122
|
+
Object.assign(new Parent(), { id: first.id, label: "changed" }),
|
|
123
|
+
Object.assign(new Parent(), { id: first.id, label: "later" }),
|
|
124
|
+
])).rejects.toThrow(/stop/);
|
|
125
|
+
const stored = await collectAsync(parentsRepo.read());
|
|
126
|
+
expect(stored.find((parent) => parent.id === first.id)?.label).toBe("a");
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("update replace-syncs each parent", async () => {
|
|
130
|
+
const connection = await setup();
|
|
131
|
+
const parentsRepo = new BulkCRUDRepository(Parent, connection);
|
|
132
|
+
const childrenRepo = new BulkCRUDRepository(Child, connection);
|
|
133
|
+
const first = await parentsRepo.create(Object.assign(new Parent(), { label: "one" }));
|
|
134
|
+
expect(first.id).toBeGreaterThan(0);
|
|
135
|
+
const second = await parentsRepo.create(Object.assign(new Parent(), { label: "two" }));
|
|
136
|
+
expect(second.id).toBeGreaterThan(0);
|
|
137
|
+
const kept = await childrenRepo.create(Object.assign(new Child(), { parent_id: first.id, label: "a" }));
|
|
138
|
+
expect(kept.id).toBeGreaterThan(0);
|
|
139
|
+
const dropped = await childrenRepo.create(Object.assign(new Child(), { parent_id: second.id, label: "b" }));
|
|
140
|
+
expect(dropped.id).toBeGreaterThan(0);
|
|
141
|
+
const response = await relate(parentsRepo, childrenRepo).updateBulk([
|
|
142
|
+
Object.assign(new Parent(), {
|
|
143
|
+
id: first.id,
|
|
144
|
+
label: "one",
|
|
145
|
+
children: [Object.assign(new Child(), { id: kept.id, label: "aa" })],
|
|
146
|
+
}),
|
|
147
|
+
Object.assign(new Parent(), {
|
|
148
|
+
id: second.id,
|
|
149
|
+
label: "two",
|
|
150
|
+
children: [Object.assign(new Child(), { label: "c" })],
|
|
151
|
+
}),
|
|
152
|
+
]);
|
|
153
|
+
expect(response.rowsAffected).toBe(2);
|
|
154
|
+
const children = await collectAsync(childrenRepo.read());
|
|
155
|
+
expect(children).toHaveLength(2);
|
|
156
|
+
expect(children.find((child) => child.parent_id === first.id)?.label).toBe("aa");
|
|
157
|
+
expect(children.find((child) => child.parent_id === second.id)?.label).toBe("c");
|
|
158
|
+
expect(children.some((child) => child.label === "b")).toBe(false);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("delete cascades each parent", async () => {
|
|
162
|
+
const connection = await setup();
|
|
163
|
+
const parentsRepo = new BulkCRUDRepository(Parent, connection);
|
|
164
|
+
const childrenRepo = new BulkCRUDRepository(Child, connection);
|
|
165
|
+
const first = await parentsRepo.create(Object.assign(new Parent(), { label: "one" }));
|
|
166
|
+
expect(first.id).toBeGreaterThan(0);
|
|
167
|
+
const second = await parentsRepo.create(Object.assign(new Parent(), { label: "two" }));
|
|
168
|
+
expect(second.id).toBeGreaterThan(0);
|
|
169
|
+
const firstChild = await childrenRepo.create(Object.assign(new Child(), { parent_id: first.id, label: "a" }));
|
|
170
|
+
expect(firstChild.id).toBeGreaterThan(0);
|
|
171
|
+
const secondChild = await childrenRepo.create(Object.assign(new Child(), { parent_id: second.id, label: "b" }));
|
|
172
|
+
expect(secondChild.id).toBeGreaterThan(0);
|
|
173
|
+
const kept = await childrenRepo.create(Object.assign(new Child(), { label: "keep" }));
|
|
174
|
+
expect(kept.id).toBeGreaterThan(0);
|
|
175
|
+
const response = await relate(parentsRepo, childrenRepo).deleteBulk([
|
|
176
|
+
Object.assign(new Parent(), { id: first.id }),
|
|
177
|
+
Object.assign(new Parent(), { id: second.id }),
|
|
178
|
+
]);
|
|
179
|
+
expect(response.rowsAffected).toBe(2);
|
|
180
|
+
expect(await collectAsync(parentsRepo.read())).toEqual([]);
|
|
181
|
+
expect((await collectAsync(childrenRepo.read())).map((child) => child.label)).toEqual(["keep"]);
|
|
182
|
+
});
|
|
183
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { IConnection } from "./IConnection.ts";
|
|
2
|
+
import type { Constructable } from "./converters/Constructable.ts";
|
|
3
|
+
import type { Cells } from "./converters/Cell.ts";
|
|
4
|
+
import { CRUDRepository } from "./CRUDRepository.ts";
|
|
5
|
+
import type { CRUDRepositoryOptions } from "./CRUDRepositoryOptions.ts";
|
|
6
|
+
import { createCRUDRepositoryOptions } from "./createCRUDRepositoryOptions.ts";
|
|
7
|
+
import type { Response } from "./Response.ts";
|
|
8
|
+
import type { IWhere } from "./statements/IWhere.ts";
|
|
9
|
+
import type { IBulkWriteStrategy } from "./IBulkWriteStrategy.ts";
|
|
10
|
+
import { SingleCommandIBulkWriteStrategy } from "./SingleCommandIBulkWriteStrategy.ts";
|
|
11
|
+
import { UnitOfWorkTransaction } from "./UnitOfWorkTransaction.ts";
|
|
12
|
+
|
|
13
|
+
const isBulkStrategy = <T extends object>(value: object): value is IBulkWriteStrategy<T> =>
|
|
14
|
+
"create" in value &&
|
|
15
|
+
"update" in value &&
|
|
16
|
+
"delete" in value &&
|
|
17
|
+
!("persist" in value) &&
|
|
18
|
+
!("createCommand" in value) &&
|
|
19
|
+
!("statements" in value);
|
|
20
|
+
|
|
21
|
+
const transactionOf = <T extends object>(
|
|
22
|
+
options: IConnection | CRUDRepositoryOptions<T> | IBulkWriteStrategy<T> | undefined,
|
|
23
|
+
): boolean =>
|
|
24
|
+
options !== undefined && "write" in options ? options.write?.useTransaction ?? false : false;
|
|
25
|
+
|
|
26
|
+
const inTransaction = async <TResult>(
|
|
27
|
+
transactional: boolean,
|
|
28
|
+
connection: IConnection,
|
|
29
|
+
write: () => Promise<TResult>,
|
|
30
|
+
): Promise<TResult> => {
|
|
31
|
+
if (!transactional) return write();
|
|
32
|
+
const work = new UnitOfWorkTransaction(connection);
|
|
33
|
+
await work.begin();
|
|
34
|
+
try {
|
|
35
|
+
const result = await write();
|
|
36
|
+
await work.commit();
|
|
37
|
+
return result;
|
|
38
|
+
} catch (error) {
|
|
39
|
+
await work.abort();
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export class BulkCRUDRepository<T extends object = Cells> extends CRUDRepository<T> {
|
|
45
|
+
private readonly strategy: IBulkWriteStrategy<T>;
|
|
46
|
+
private readonly transactional: boolean;
|
|
47
|
+
|
|
48
|
+
constructor(type: Constructable<T>, connection: IConnection, bulk?: IBulkWriteStrategy<T>);
|
|
49
|
+
constructor(connection: IConnection, options: CRUDRepositoryOptions<T>, bulk?: IBulkWriteStrategy<T>);
|
|
50
|
+
constructor(
|
|
51
|
+
typeOrConnection: Constructable<T> | IConnection,
|
|
52
|
+
connectionOrOptions?: IConnection | CRUDRepositoryOptions<T> | IBulkWriteStrategy<T>,
|
|
53
|
+
bulk?: IBulkWriteStrategy<T>,
|
|
54
|
+
) {
|
|
55
|
+
const strategy = bulk ?? (isBulkStrategy<T>(connectionOrOptions ?? {}) ? connectionOrOptions : undefined);
|
|
56
|
+
const transactional = transactionOf(connectionOrOptions);
|
|
57
|
+
const parent = (
|
|
58
|
+
isBulkStrategy(connectionOrOptions ?? {}) ? undefined : connectionOrOptions
|
|
59
|
+
) as IConnection & CRUDRepositoryOptions<T>;
|
|
60
|
+
super(typeOrConnection as Constructable<T> & IConnection, parent);
|
|
61
|
+
this.strategy = strategy ?? new SingleCommandIBulkWriteStrategy<T>();
|
|
62
|
+
this.transactional = transactional;
|
|
63
|
+
this.strategy.repo = this;
|
|
64
|
+
this.strategy.attach(
|
|
65
|
+
this.command(),
|
|
66
|
+
typeof typeOrConnection === "function"
|
|
67
|
+
? createCRUDRepositoryOptions(typeOrConnection)
|
|
68
|
+
: parent,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
createBulk = async (items: T[]): Promise<T[]> => {
|
|
73
|
+
await inTransaction(this.transactional || items.length > 0, this.command().connection, () =>
|
|
74
|
+
this.strategy.create(items),
|
|
75
|
+
);
|
|
76
|
+
return items;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
updateBulk = (items: T[], byWhere?: IWhere): Promise<Response> =>
|
|
80
|
+
inTransaction(this.transactional, this.command().connection, () => this.strategy.update(items, byWhere));
|
|
81
|
+
|
|
82
|
+
deleteBulk = (items: T[], byWhere?: IWhere): Promise<Response> =>
|
|
83
|
+
inTransaction(this.transactional, this.command().connection, () => this.strategy.delete(items, byWhere));
|
|
84
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { CRUDRepository } from "./CRUDRepository.ts";
|
|
2
|
+
import type { CRUDRepositoryOptions } from "./CRUDRepositoryOptions.ts";
|
|
3
|
+
import { DefaultWriteEnrichmentStrategy } from "./DefaultWriteEnrichmentStrategy.ts";
|
|
4
|
+
import { enrichmentsOf } from "./ReadonlyRepository.ts";
|
|
5
|
+
import { RepositoryTree } from "./RepositoryTree.ts";
|
|
6
|
+
import { RepositoryTreeNode } from "./RepositoryTreeNode.ts";
|
|
7
|
+
import type { WriteMode } from "./WriteMode.ts";
|
|
8
|
+
|
|
9
|
+
export const afterBulkWrite = async <T extends object>(
|
|
10
|
+
repo: CRUDRepository<T>,
|
|
11
|
+
options: CRUDRepositoryOptions<T>,
|
|
12
|
+
items: T[],
|
|
13
|
+
mode: WriteMode,
|
|
14
|
+
): Promise<void> => {
|
|
15
|
+
if (items.length === 0 || !enrichmentsOf(repo)?.length) return;
|
|
16
|
+
const write = options.write ?? new DefaultWriteEnrichmentStrategy<T>();
|
|
17
|
+
const tree = new RepositoryTree(new RepositoryTreeNode(repo, ""));
|
|
18
|
+
await (write.persistBulk ?? write.persist)(tree, items, mode);
|
|
19
|
+
};
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { DatasourceError } from "./DatasourceError.ts";
|
|
3
|
+
import { BatchCommandIBulkWriteStrategy } from "./BatchCommandIBulkWriteStrategy.ts";
|
|
4
|
+
import { BulkCRUDRepository } from "./BulkCRUDRepository.ts";
|
|
5
|
+
import { MockCommand } from "./mocks/mock-command.ts";
|
|
6
|
+
import { MockConnection } from "./mocks/mock-connection.ts";
|
|
7
|
+
import { SingleCommandIBulkWriteStrategy } from "./SingleCommandIBulkWriteStrategy.ts";
|
|
8
|
+
import { StoredProcedureBulkWriteStrategy } from "./StoredProcedureBulkWriteStrategy.ts";
|
|
9
|
+
import type { IBulkWriteStrategy } from "./IBulkWriteStrategy.ts";
|
|
10
|
+
|
|
11
|
+
class Parent {
|
|
12
|
+
id: number | null = null;
|
|
13
|
+
label = "";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
class Scalar {
|
|
17
|
+
amount = 0;
|
|
18
|
+
blob: Uint8Array | null = null;
|
|
19
|
+
count = 0;
|
|
20
|
+
created: Date | null = null;
|
|
21
|
+
flag = false;
|
|
22
|
+
label: string | null = null;
|
|
23
|
+
price = 0;
|
|
24
|
+
token: string | null = null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const open = <T extends object>(type: new () => T, strategy: IBulkWriteStrategy<T>) => {
|
|
28
|
+
const connection = new MockConnection(undefined, "memory");
|
|
29
|
+
const command = new MockCommand(connection);
|
|
30
|
+
connection.command = command;
|
|
31
|
+
const repo = new BulkCRUDRepository<T>(connection, { type, entity: "parents" }, strategy);
|
|
32
|
+
return { repo, command };
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
describe("bulk write strategies", () => {
|
|
36
|
+
it("single commands writes one statement per row", async () => {
|
|
37
|
+
const { repo, command } = open(Parent, new SingleCommandIBulkWriteStrategy<Parent>());
|
|
38
|
+
command.response = { result: 9, rowsAffected: 1 };
|
|
39
|
+
await repo.createBulk([
|
|
40
|
+
Object.assign(new Parent(), { label: "a" }),
|
|
41
|
+
Object.assign(new Parent(), { label: "b" }),
|
|
42
|
+
]);
|
|
43
|
+
await repo.updateBulk([
|
|
44
|
+
Object.assign(new Parent(), { id: 1, label: "a" }),
|
|
45
|
+
Object.assign(new Parent(), { id: 2, label: "b" }),
|
|
46
|
+
]);
|
|
47
|
+
await repo.deleteBulk([
|
|
48
|
+
Object.assign(new Parent(), { id: 1, label: "a" }),
|
|
49
|
+
Object.assign(new Parent(), { id: 2, label: "b" }),
|
|
50
|
+
]);
|
|
51
|
+
expect(command.executeCalls).toHaveLength(6);
|
|
52
|
+
expect(command.executeCalls[0]?.text).toBe("INSERT INTO parents (label) VALUES ($label)");
|
|
53
|
+
expect(command.executeCalls[1]?.text).toBe("INSERT INTO parents (label) VALUES ($label)");
|
|
54
|
+
expect(command.executeCalls[2]?.text).toBe("UPDATE parents SET label = $label WHERE id = $id");
|
|
55
|
+
expect(command.executeCalls[3]?.text).toBe("UPDATE parents SET label = $label WHERE id = $id");
|
|
56
|
+
expect(command.executeCalls[4]?.text).toBe("DELETE FROM parents WHERE id = $id");
|
|
57
|
+
expect(command.executeCalls[5]?.text).toBe("DELETE FROM parents WHERE id = $id");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("batch commands joins statements", async () => {
|
|
61
|
+
const { repo, command } = open(Parent, new BatchCommandIBulkWriteStrategy<Parent>());
|
|
62
|
+
await repo.createBulk([
|
|
63
|
+
Object.assign(new Parent(), { label: "a" }),
|
|
64
|
+
Object.assign(new Parent(), { label: "b" }),
|
|
65
|
+
]);
|
|
66
|
+
expect(command.executeCalls).toHaveLength(1);
|
|
67
|
+
expect(command.executeCalls[0]?.text).toBe(
|
|
68
|
+
"INSERT INTO parents (label) VALUES ($label__0), ($label__1)",
|
|
69
|
+
);
|
|
70
|
+
expect(command.executeCalls[0]?.parameters?.[0]).toMatchObject({ name: "label__0", value: "a" });
|
|
71
|
+
expect(command.executeCalls[0]?.parameters?.[1]).toMatchObject({ name: "label__1", value: "b" });
|
|
72
|
+
|
|
73
|
+
await repo.updateBulk([
|
|
74
|
+
Object.assign(new Parent(), { id: 1, label: "a" }),
|
|
75
|
+
Object.assign(new Parent(), { id: 2, label: "b" }),
|
|
76
|
+
]);
|
|
77
|
+
expect(command.executeCalls[1]?.text).toBe(
|
|
78
|
+
"UPDATE parents SET label = data.label FROM (SELECT $label__0 AS label, $id__0 AS id UNION ALL SELECT $label__1 AS label, $id__1 AS id) AS data WHERE parents.id = data.id",
|
|
79
|
+
);
|
|
80
|
+
expect(command.executeCalls[1]?.parameters?.map((parameter) => parameter.name)).toEqual([
|
|
81
|
+
"label__0",
|
|
82
|
+
"id__0",
|
|
83
|
+
"label__1",
|
|
84
|
+
"id__1",
|
|
85
|
+
]);
|
|
86
|
+
|
|
87
|
+
await repo.deleteBulk([
|
|
88
|
+
Object.assign(new Parent(), { id: 1 }),
|
|
89
|
+
Object.assign(new Parent(), { id: 2 }),
|
|
90
|
+
]);
|
|
91
|
+
expect(command.executeCalls[2]?.text).toBe(
|
|
92
|
+
"DELETE FROM parents WHERE id IN ($id__0, $id__1)",
|
|
93
|
+
);
|
|
94
|
+
expect(command.executeCalls[2]?.parameters?.map((parameter) => parameter.name)).toEqual(["id__0", "id__1"]);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("leaves a shorter name inside a longer parameter", async () => {
|
|
98
|
+
class Coded {
|
|
99
|
+
id = 0;
|
|
100
|
+
code = "";
|
|
101
|
+
code_extra = "";
|
|
102
|
+
}
|
|
103
|
+
const { repo, command } = open(Coded, new BatchCommandIBulkWriteStrategy<Coded>());
|
|
104
|
+
await repo.createBulk([Object.assign(new Coded(), { code: "a", code_extra: "ab" })]);
|
|
105
|
+
expect(command.executeCalls[0]?.text).toContain("$code__0");
|
|
106
|
+
expect(command.executeCalls[0]?.text).toContain("$code_extra__0");
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("stored procedure commands sends one json call", async () => {
|
|
110
|
+
const { repo, command } = open(
|
|
111
|
+
Parent,
|
|
112
|
+
new StoredProcedureBulkWriteStrategy<Parent>("bulk_create_parent", "bulk_update_parent", "bulk_delete_parent"),
|
|
113
|
+
);
|
|
114
|
+
await repo.createBulk([
|
|
115
|
+
Object.assign(new Parent(), { label: "a" }),
|
|
116
|
+
Object.assign(new Parent(), { label: "b" }),
|
|
117
|
+
]);
|
|
118
|
+
expect(command.executeCalls).toHaveLength(1);
|
|
119
|
+
expect(command.executeCalls[0]?.text).toBe("CALL bulk_create_parent($payload, $ids)");
|
|
120
|
+
expect(command.executeCalls[0]?.parameters?.[0]).toMatchObject({
|
|
121
|
+
name: "payload",
|
|
122
|
+
value: '[{"label":"a"},{"label":"b"}]',
|
|
123
|
+
});
|
|
124
|
+
expect(command.executeCalls[0]?.parameters?.[1]).toMatchObject({ name: "ids", direction: "inputOutput" });
|
|
125
|
+
|
|
126
|
+
await repo.updateBulk([
|
|
127
|
+
Object.assign(new Parent(), { id: 1, label: "a" }),
|
|
128
|
+
Object.assign(new Parent(), { id: 2, label: "b" }),
|
|
129
|
+
]);
|
|
130
|
+
expect(command.executeCalls[1]?.text).toBe("CALL bulk_update_parent($payload)");
|
|
131
|
+
expect(command.executeCalls[1]?.parameters?.[0]?.value).toBe(
|
|
132
|
+
'[{"id":1,"label":"a"},{"id":2,"label":"b"}]',
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
await repo.deleteBulk([
|
|
136
|
+
Object.assign(new Parent(), { id: 1 }),
|
|
137
|
+
Object.assign(new Parent(), { id: 2 }),
|
|
138
|
+
]);
|
|
139
|
+
expect(command.executeCalls[2]?.text).toBe("CALL bulk_delete_parent($payload)");
|
|
140
|
+
expect(command.executeCalls[2]?.parameters?.[0]?.value).toBe(
|
|
141
|
+
'[{"id":1,"label":""},{"id":2,"label":""}]',
|
|
142
|
+
);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("payload encodes scalars", async () => {
|
|
146
|
+
const { repo, command } = open(
|
|
147
|
+
Scalar,
|
|
148
|
+
new StoredProcedureBulkWriteStrategy<Scalar>("create_scalar", "update_scalar", "delete_scalar"),
|
|
149
|
+
);
|
|
150
|
+
const created = new Date("2024-06-01T00:00:00.000Z");
|
|
151
|
+
await repo.createBulk([
|
|
152
|
+
Object.assign(new Scalar(), {
|
|
153
|
+
amount: 1.25,
|
|
154
|
+
blob: new Uint8Array([1, 2, 3]),
|
|
155
|
+
count: 2,
|
|
156
|
+
created,
|
|
157
|
+
flag: true,
|
|
158
|
+
price: 10.5,
|
|
159
|
+
token: "3f2504e0-4f89-11d3-9a0c-0305e82c3301",
|
|
160
|
+
}),
|
|
161
|
+
]);
|
|
162
|
+
expect(command.executeCalls[0]?.parameters?.[0]?.value).toBe(
|
|
163
|
+
`[{"amount":1.25,"blob":"AQID","count":2,"created":"${created.toISOString().replace(/\.(\d{3})Z$/, ".$10000Z")}","flag":true,"label":null,"price":10.5,"token":"3f2504e0-4f89-11d3-9a0c-0305e82c3301"}]`,
|
|
164
|
+
);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it("payload encodes the remaining scalars", async () => {
|
|
168
|
+
class Odd {
|
|
169
|
+
flag = false;
|
|
170
|
+
huge = 0;
|
|
171
|
+
note = "";
|
|
172
|
+
rank = 0n;
|
|
173
|
+
amy = 2;
|
|
174
|
+
zed = 1;
|
|
175
|
+
}
|
|
176
|
+
const { repo, command } = open(
|
|
177
|
+
Odd,
|
|
178
|
+
new StoredProcedureBulkWriteStrategy<Odd>("create_odd", "update_odd", "delete_odd"),
|
|
179
|
+
);
|
|
180
|
+
await repo.createBulk([
|
|
181
|
+
Object.assign(new Odd(), { flag: false, note: "a\u0001b", rank: 4n }),
|
|
182
|
+
]);
|
|
183
|
+
expect(command.executeCalls[0]?.parameters?.[0]?.value).toContain('"flag":false');
|
|
184
|
+
expect(command.executeCalls[0]?.parameters?.[0]?.value).toContain('"rank":4');
|
|
185
|
+
expect(command.executeCalls[0]?.parameters?.[0]?.value).toContain("\\u0001");
|
|
186
|
+
await expect(
|
|
187
|
+
repo.createBulk([Object.assign(new Odd(), { huge: Number.POSITIVE_INFINITY })]),
|
|
188
|
+
).rejects.toThrow(DatasourceError);
|
|
189
|
+
await expect(
|
|
190
|
+
repo.createBulk([Object.assign(new Odd(), { note: { bad: true } as never })]),
|
|
191
|
+
).rejects.toThrow(DatasourceError);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
class Child {
|
|
195
|
+
id: number | null = null;
|
|
196
|
+
parent_id: number | null = null;
|
|
197
|
+
label = "";
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
class Family {
|
|
201
|
+
id: number | null = null;
|
|
202
|
+
label = "";
|
|
203
|
+
children: Child[] = [];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const family = (
|
|
207
|
+
parents: IBulkWriteStrategy<Family>,
|
|
208
|
+
children: IBulkWriteStrategy<Child> = new BatchCommandIBulkWriteStrategy<Child>(),
|
|
209
|
+
) => {
|
|
210
|
+
const connection = new MockConnection(undefined, "memory");
|
|
211
|
+
const command = new MockCommand(connection);
|
|
212
|
+
connection.command = command;
|
|
213
|
+
const childrenRepo = new BulkCRUDRepository<Child>(connection, { type: Child, entity: "children" }, children);
|
|
214
|
+
const parentsRepo = new BulkCRUDRepository<Family>(connection, { type: Family, entity: "parents" }, parents);
|
|
215
|
+
parentsRepo.addEnriched(
|
|
216
|
+
{ destinationProperty: "children", sourceProperty: "id", relatedKey: "parent_id", type: "list" },
|
|
217
|
+
childrenRepo,
|
|
218
|
+
);
|
|
219
|
+
return { parentsRepo, command };
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
it("batch persists children in one command", async () => {
|
|
223
|
+
const { parentsRepo, command } = family(new BatchCommandIBulkWriteStrategy<Family>());
|
|
224
|
+
await parentsRepo.createBulk([
|
|
225
|
+
Object.assign(new Family(), { id: 1, label: "a", children: [Object.assign(new Child(), { id: 10, label: "x" })] }),
|
|
226
|
+
Object.assign(new Family(), { id: 2, label: "b", children: [Object.assign(new Child(), { id: 11, label: "y" })] }),
|
|
227
|
+
]);
|
|
228
|
+
expect(command.executeCalls).toHaveLength(2);
|
|
229
|
+
expect(command.executeCalls[0]?.text).toContain("INSERT INTO parents");
|
|
230
|
+
expect(command.executeCalls[1]?.text).toContain("INSERT INTO children");
|
|
231
|
+
expect(command.executeCalls[1]?.text).toContain("label__0");
|
|
232
|
+
expect(command.executeCalls[1]?.text).toContain("label__1");
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it("stored procedure persists children in one call", async () => {
|
|
236
|
+
const { parentsRepo, command } = family(
|
|
237
|
+
new StoredProcedureBulkWriteStrategy<Family>("bulk_create_parent", "bulk_update_parent", "bulk_delete_parent"),
|
|
238
|
+
new StoredProcedureBulkWriteStrategy<Child>("bulk_create_child", "bulk_update_child", "bulk_delete_child"),
|
|
239
|
+
);
|
|
240
|
+
await parentsRepo.createBulk([
|
|
241
|
+
Object.assign(new Family(), { id: 1, label: "a", children: [Object.assign(new Child(), { id: 10, label: "x" })] }),
|
|
242
|
+
Object.assign(new Family(), { id: 2, label: "b", children: [Object.assign(new Child(), { id: 11, label: "y" })] }),
|
|
243
|
+
]);
|
|
244
|
+
expect(command.executeCalls).toHaveLength(2);
|
|
245
|
+
expect(command.executeCalls[0]?.text).toBe("CALL bulk_create_parent($payload)");
|
|
246
|
+
expect(command.executeCalls[1]?.text).toBe("CALL bulk_create_child($payload)");
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it("stored procedure create writes generated ids onto items", async () => {
|
|
250
|
+
const { repo, command } = open(
|
|
251
|
+
Parent,
|
|
252
|
+
new StoredProcedureBulkWriteStrategy<Parent>("bulk_create_parent", "bulk_update_parent", "bulk_delete_parent"),
|
|
253
|
+
);
|
|
254
|
+
command.response = {
|
|
255
|
+
result: null,
|
|
256
|
+
rowsAffected: 2,
|
|
257
|
+
parameters: [
|
|
258
|
+
{ name: "payload", value: "[]" },
|
|
259
|
+
{ name: "ids", value: "[7,8]", direction: "inputOutput" },
|
|
260
|
+
],
|
|
261
|
+
};
|
|
262
|
+
const items = [Object.assign(new Parent(), { label: "a" }), Object.assign(new Parent(), { label: "b" })];
|
|
263
|
+
await repo.createBulk(items);
|
|
264
|
+
expect(items[0]?.id).toBe(7);
|
|
265
|
+
expect(items[1]?.id).toBe(8);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it("batch create requires a parent id when children are present", async () => {
|
|
269
|
+
const { parentsRepo } = family(new BatchCommandIBulkWriteStrategy<Family>());
|
|
270
|
+
await expect(
|
|
271
|
+
parentsRepo.createBulk([
|
|
272
|
+
Object.assign(new Family(), { label: "a", children: [Object.assign(new Child(), { label: "x" })] }),
|
|
273
|
+
]),
|
|
274
|
+
).rejects.toThrow(/id/);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it("empty list does not execute", async () => {
|
|
278
|
+
for (const strategy of [
|
|
279
|
+
new SingleCommandIBulkWriteStrategy<Parent>(),
|
|
280
|
+
new BatchCommandIBulkWriteStrategy<Parent>(),
|
|
281
|
+
new StoredProcedureBulkWriteStrategy<Parent>("create_parent", "update_parent", "delete_parent"),
|
|
282
|
+
]) {
|
|
283
|
+
const { repo, command } = open(Parent, strategy);
|
|
284
|
+
const created = await repo.createBulk([]);
|
|
285
|
+
const updated = await repo.updateBulk([]);
|
|
286
|
+
const deleted = await repo.deleteBulk([]);
|
|
287
|
+
expect(command.executeCalls).toEqual([]);
|
|
288
|
+
expect(created).toEqual([]);
|
|
289
|
+
expect(updated.rowsAffected).toBe(0);
|
|
290
|
+
expect(deleted.rowsAffected).toBe(0);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
});
|