diff --git a/.xmake.cfg b/.xmake.cfg new file mode 100644 index 000000000..81923357f --- /dev/null +++ b/.xmake.cfg @@ -0,0 +1,6 @@ +[xmake] +xmake-version=0.9.2-MS8 +[buildplugin] +java-type=sapmachine +alternate-path=jpa +java-version=17.0.8 diff --git a/README.md b/README.md index daa3c9d4b..818678563 100644 --- a/README.md +++ b/README.md @@ -102,5 +102,5 @@ Detailed information including third-party components and their licensing/copyri |2.1.0| - Enhancement of annotation API
- Enhancement of API for server driven paging
- Optional support of IN operand
- Update to Olingo 5.0.0
- Rework $count implementation
- Fix problem with $count on collection properties|No| |2.1.1| - Fix for issue [#292](https://github.com/SAP/olingo-jpa-processor-v4/issues/292)|No| |2.1.3| - Fix for issue [#319](https://github.com/SAP/olingo-jpa-processor-v4/issues/319)
- Fix for issue [#325](https://github.com/SAP/olingo-jpa-processor-v4/issues/325)
- Fix for issue [#327](https://github.com/SAP/olingo-jpa-processor-v4/issues/327)
- Fix for issue [#331](https://github.com/SAP/olingo-jpa-processor-v4/issues/331)
- Fix of en issue with $orderby and _to one_ navigation properties |No| -|2.2.0| - Fix for issue [#378](https://github.com/SAP/olingo-jpa-processor-v4/issues/378)
- Fix for issue [#378](https://github.com/SAP/olingo-jpa-processor-v4/issues/378)
- Fix for issue [#361](https://github.com/SAP/olingo-jpa-processor-v4/issues/361)
- Fix for issue [#352](https://github.com/SAP/olingo-jpa-processor-v4/issues/352)
- Fix for issue [#307](https://github.com/SAP/olingo-jpa-processor-v4/issues/307)
- Enhancement according to [#314](https://github.com/SAP/olingo-jpa-processor-v4/issues/314)|No| +|2.2.0| - Fix for issue [#378](https://github.com/SAP/olingo-jpa-processor-v4/issues/378)
- Fix for issue [#361](https://github.com/SAP/olingo-jpa-processor-v4/issues/361)
- Fix for issue [#352](https://github.com/SAP/olingo-jpa-processor-v4/issues/352)
- Fix for issue [#307](https://github.com/SAP/olingo-jpa-processor-v4/issues/307)
- Enhancement according to [#314](https://github.com/SAP/olingo-jpa-processor-v4/issues/314)|No| |2.3.0| - Add support for handling multiple API version. Issue [#308](https://github.com/SAP/olingo-jpa-processor-v4/issues/308)
- Fix for issue [#359](https://github.com/SAP/olingo-jpa-processor-v4/issues/359)
- Fix for issue [#406](https://github.com/SAP/olingo-jpa-processor-v4/issues/406)
- Fix for issue [#397](https://github.com/SAP/olingo-jpa-processor-v4/issues/397)|No| diff --git a/jpa-archetype/odata-jpa-archetype-spring/pom.xml b/jpa-archetype/odata-jpa-archetype-spring/pom.xml index c62b63bc1..739ec7950 100644 --- a/jpa-archetype/odata-jpa-archetype-spring/pom.xml +++ b/jpa-archetype/odata-jpa-archetype-spring/pom.xml @@ -4,7 +4,7 @@ com.sap.olingo odata-jpa-archetype - 2.3.1-SNAPSHOT + 2.3.1-SNAPSHOT odata-jpa-archetype-spring Archetype - odata-jpa-archetype-spring diff --git a/jpa-archetype/pom.xml b/jpa-archetype/pom.xml index 244b22cba..294f593f9 100644 --- a/jpa-archetype/pom.xml +++ b/jpa-archetype/pom.xml @@ -10,10 +10,10 @@ UTF-8 - 17 + 17 - odata-jpa-archetype-spring + odata-jpa-archetype-spring \ No newline at end of file diff --git a/jpa-tutorial/Questions/HowToHandleApiVersions.adoc b/jpa-tutorial/Questions/HowToHandleApiVersions.adoc index 7388847d2..79101124f 100644 --- a/jpa-tutorial/Questions/HowToHandleApiVersions.adoc +++ b/jpa-tutorial/Questions/HowToHandleApiVersions.adoc @@ -103,7 +103,7 @@ public abstract class EclipseLinkJpaConfiguration extends JpaBaseConfiguration { ---- Then we need to create the version specific entity manager factories. -Please note that with each version of the entity manager factory we also have to create a transaction manager. +Please note that with each version of the entity manager factory we also have to create a transaction manager and must have an own persistence unit name. It is also important that one of the beans for the factory has the name `entityManagerFactory`. First the entity manager factory for the old version: @@ -136,6 +136,7 @@ public class JpaConfigurationV1 extends EclipseLinkJpaConfiguration { return basicSettings(factoryBuilder) .packages(Trip.class, PlanItem.class, OffsetDateTimeConverter.class) + .persistenceUnit("TrippinV1") .build(); } } @@ -176,6 +177,7 @@ public class JpaConfigurationV2 extends EclipseLinkJpaConfiguration { return basicSettings(factoryBuilder) .packages(Trip.class, PlanItem.class, OffsetDateTimeConverter.class) + .persistenceUnit("TrippinV2") .build(); } } diff --git a/jpa-tutorial/Questions/Preparation.adoc b/jpa-tutorial/Questions/Preparation.adoc index 38f0b45e1..4f39dd8c9 100644 --- a/jpa-tutorial/Questions/Preparation.adoc +++ b/jpa-tutorial/Questions/Preparation.adoc @@ -290,7 +290,7 @@ insert into "Person" values ('keithpinckney', 'Keith', 'Pinckney', null, 0, null insert into "Person" values ('marshallgaray', 'Marshall', 'Garay', null, 0, null); insert into "Person" values ('ryantheriault', 'Ryan', 'Theriault', null, 0, null); insert into "Person" values ('elainestewart', 'Elaine', 'Stewart', null, 0, null); -insert into "Person" values ('salliesampson', 'Sallie', 'Sampson', null, 1, null); +insert into "Person" values ('sallysampson', 'Sally', 'Sampson', null, 1, null); insert into "Person" values ('jonirosales', 'Joni', 'Rosales', null, 2, null); insert into "Person" values ('georginabarlow', 'Georgina', 'Barlow', null, 1, null); insert into "Person" values ('angelhuffman', 'Angel', 'Huffman', null, 1, null); @@ -326,7 +326,7 @@ insert into "Trip" values (11, 'keithpinckney', 'a88f675d-9199-4392-9656-b08e3b4 insert into "Trip" values (12, 'marshallgaray', 'a88f675d-9199-4392-9656-b08e3b46df8a', 'Study trip', 1550.3, 'This is a 2 weeks study trip', '2014-01-01T00:00:00Z', '2014-01-14T00:00:00Z'); insert into "Trip" values (13, 'ryantheriault', 'a88f675d-9199-4392-9656-b08e3b46df8a', 'Study trip', 1550.3, 'This is a 2 weeks study trip', '2014-01-01T00:00:00Z', '2014-01-14T00:00:00Z'); insert into "Trip" values (14, 'elainestewart', 'a88f675d-9199-4392-9656-b08e3b46df8a', 'Study trip', 1550.3, 'This is a 2 weeks study trip', '2014-01-01T00:00:00Z', '2014-01-14T00:00:00Z'); -insert into "Trip" values (15, 'salliesampson', 'a88f675d-9199-4392-9656-b08e3b46df8a', 'Study trip', 600, 'This is a 2 weeks study trip', '2014-01-01T00:00:00Z', '2014-01-14T00:00:00Z'); +insert into "Trip" values (15, 'sallysampson', 'a88f675d-9199-4392-9656-b08e3b46df8a', 'Study trip', 600, 'This is a 2 weeks study trip', '2014-01-01T00:00:00Z', '2014-01-14T00:00:00Z'); insert into "Trip" values (16, 'jonirosales', 'a88f675d-9199-4392-9656-b08e3b46df8a', 'Study trip', 2000, 'This is a 2 weeks study trip', '2014-01-01T00:00:00Z', '2014-01-14T00:00:00Z'); insert into "Trip" values (17, 'georginabarlow', 'a88f675d-9199-4392-9656-b08e3b46df8a', 'Study trip', 1150.3, 'This is a 2 weeks study trip', '2014-01-01T00:00:00Z', '2014-01-14T00:00:00Z'); insert into "Trip" values (18, 'angelhuffman', 'cb0b8acb-79cb-4127-8316-772bc4302824', 'DIY Trip', 1500.3, 'This is a DIY trip', '2011-02-11T00:00:00Z', '2011-02-14T00:00:00Z'); diff --git a/jpa-tutorial/QuickStart/QuickStart.adoc b/jpa-tutorial/QuickStart/QuickStart.adoc index e8b5157df..2ec74d9be 100644 --- a/jpa-tutorial/QuickStart/QuickStart.adoc +++ b/jpa-tutorial/QuickStart/QuickStart.adoc @@ -46,7 +46,7 @@ This should contain the information about archetype: com.sap.olingo odata-jpa-archetype-spring - 2.1.3 + 2.3.0 diff --git a/jpa/.dbeaver/credentials-config.json b/jpa/.dbeaver/credentials-config.json new file mode 100644 index 000000000..3156ea04c --- /dev/null +++ b/jpa/.dbeaver/credentials-config.json @@ -0,0 +1 @@ +:b6 94E˗JA8/gU{~=eVZ̘8a\;}q6 +2`kuN)ɪW_=/^;o|sI5':%0GfRt f}:\s:4n[\K$UYGrUSj3?EDeuΧ,>\@#7pZ;,H>WQyRϚ@Y`VqVڹlauz-\hD%_>Ci-lp-0?W`o؀gjwg_HV:%OI@$lm_J%fKx1tPC}c4hw;Eŋwg\VmD Z&`F@ׅlKE GiE`ttډ<,Oz)qznuoW4 )[+iD?FY2([]n'ڣ& \ No newline at end of file diff --git a/jpa/.dbeaver/data-sources.json b/jpa/.dbeaver/data-sources.json new file mode 100644 index 000000000..473b37c41 --- /dev/null +++ b/jpa/.dbeaver/data-sources.json @@ -0,0 +1,116 @@ +{ + "folders": {}, + "connections": { + "postgres-jdbc-18d652c2b95-113570e0ec869f1d": { + "provider": "postgresql", + "driver": "postgres-jdbc", + "name": "postgres", + "save-password": true, + "configuration": { + "host": "localhost", + "port": "5432", + "database": "postgres", + "url": "jdbc:postgresql://localhost:5432/postgres", + "configurationType": "MANUAL", + "home": "postgresql-x64-16", + "type": "dev", + "closeIdleConnection": false, + "auth-model": "native" + } + }, + "postgres-jdbc-18d7dab8ffc-6870c0ec5fa5f049": { + "provider": "postgresql", + "driver": "postgres-jdbc", + "name": "Olingo", + "save-password": true, + "configuration": { + "host": "localhost", + "port": "5432", + "database": "Olingo", + "url": "jdbc:postgresql://localhost:5432/Olingo", + "configurationType": "MANUAL", + "home": "postgresql-x64-16", + "type": "dev", + "closeIdleConnection": false, + "auth-model": "native" + } + }, + "sap_hana-18d83921104-7dd425d3aee15bf1": { + "provider": "hana", + "driver": "sap_hana", + "name": "localhost", + "save-password": true, + "configuration": { + "host": "localhost", + "port": "30015", + "url": "jdbc:sap://localhost:30015", + "configurationType": "MANUAL", + "type": "dev", + "closeIdleConnection": false, + "provider-properties": { + "edition": "GENERIC" + }, + "auth-model": "native" + } + }, + "postgres-jdbc-18dec2e5845-6df665f2f5a08171": { + "provider": "postgresql", + "driver": "postgres-jdbc", + "name": "compass", + "save-password": true, + "configuration": { + "host": "localhost", + "port": "5432", + "database": "compass", + "url": "jdbc:postgresql://localhost:5432/compass", + "configurationType": "MANUAL", + "home": "postgresql-x64-16", + "type": "dev", + "closeIdleConnection": true, + "provider-properties": { + "@dbeaver-show-non-default-db@": "false" + }, + "auth-model": "native" + } + }, + "mysql8-18f81c6ea39-153fa1c6870708de": { + "provider": "mysql", + "driver": "mysql8", + "name": "MySql-Olingo", + "save-password": true, + "configuration": { + "host": "localhost", + "port": "3306", + "database": "Olingo", + "url": "jdbc:mysql://localhost:3306/Olingo?allowPublicKeyRetrieval=true&useSSL=false", + "configurationType": "URL", + "home": "mysql_client", + "type": "dev", + "closeIdleConnection": true, + "properties": { + "rewriteBatchedStatements": "true", + "connectTimeout": "20000", + "enabledTLSProtocols": "TLSv1,TLSv1.1,TLSv1.2,TLSv1.3" + }, + "auth-model": "native", + "handlers": {} + } + } + }, + "connection-types": { + "dev": { + "name": "Development", + "color": "255,255,255", + "description": "Regular development database", + "auto-commit": true, + "confirm-execute": false, + "confirm-data-change": false, + "smart-commit": false, + "smart-commit-recover": false, + "auto-close-transactions": true, + "close-transactions-period": 1800, + "auto-close-connections": true, + "close-connections-period": 14400 + } + } +} \ No newline at end of file diff --git a/jpa/.dbeaver/project-metadata.json b/jpa/.dbeaver/project-metadata.json new file mode 100644 index 000000000..205c91a6d --- /dev/null +++ b/jpa/.dbeaver/project-metadata.json @@ -0,0 +1 @@ +{"resources":{"Scripts/Script-1.sql":{"default-schema":"OLINGO","default-datasource":"postgres-jdbc-18d7dab8ffc-6870c0ec5fa5f049","default-catalog":"Olingo"},"Scripts/Script-10.sql":{"default-schema":"OLINGO","default-datasource":"postgres-jdbc-18d7dab8ffc-6870c0ec5fa5f049","default-catalog":"Olingo"},"Scripts/Script-11.sql":{"default-schema":"OLINGO","default-datasource":"sap_hana-18d83921104-7dd425d3aee15bf1"},"Scripts/Script-12.sql":{"default-schema":"public","default-datasource":"postgres-jdbc-18dec2e5845-6df665f2f5a08171","default-catalog":"compass"},"Scripts/Script-13.sql":{"default-datasource":"sap_hana-18d83921104-7dd425d3aee15bf1","default-schema":"OLINGO"},"Scripts/Script-2.sql":{"default-schema":"OLINGO","default-datasource":"sap_hana-18d83921104-7dd425d3aee15bf1","default-catalog":"compass"},"Scripts/Script-3.sql":{"default-schema":"OLINGO","default-datasource":"sap_hana-18d83921104-7dd425d3aee15bf1","default-catalog":"Olingo"},"Scripts/Script-4.sql":{"default-schema":"OLINGO","default-datasource":"postgres-jdbc-18d7dab8ffc-6870c0ec5fa5f049","default-catalog":"Olingo"},"Scripts/Script-5.sql":{"default-schema":"OLINGO","default-datasource":"postgres-jdbc-18d7dab8ffc-6870c0ec5fa5f049","default-catalog":"Olingo"},"Scripts/Script-6.sql":{"default-schema":"public","default-datasource":"postgres-jdbc-18dec2e5845-6df665f2f5a08171","default-catalog":"compass"},"Scripts/Script-7.sql":{"default-schema":"public","default-datasource":"postgres-jdbc-18dec2e5845-6df665f2f5a08171","default-catalog":"compass"},"Scripts/Script-8.sql":{"default-schema":"public","default-datasource":"postgres-jdbc-18dec2e5845-6df665f2f5a08171","default-catalog":"compass"},"Scripts/Script-9.sql":{"default-schema":"public","default-datasource":"postgres-jdbc-18dec2e5845-6df665f2f5a08171","default-catalog":"compass"},"Scripts/Script.sql":{"default-schema":"OLINGO","default-datasource":"sap_hana-18d83921104-7dd425d3aee15bf1","default-catalog":"Olingo"}}} \ No newline at end of file diff --git a/jpa/.dbeaver/project-settings.json b/jpa/.dbeaver/project-settings.json new file mode 100644 index 000000000..0e2242aaf --- /dev/null +++ b/jpa/.dbeaver/project-settings.json @@ -0,0 +1 @@ +{"id":"1dc9dc18-395d-49be-a7b8-2ad9bb1a4481"} \ No newline at end of file diff --git a/jpa/.gitignore b/jpa/.gitignore index 465424a3d..f932957a7 100644 --- a/jpa/.gitignore +++ b/jpa/.gitignore @@ -11,4 +11,4 @@ target/ *.log /old.project /Scripts/ -/.dbeaver/ +/.dbeaver/ \ No newline at end of file diff --git a/jpa/.project b/jpa/.project index 942c0feae..cf9aeda0a 100644 --- a/jpa/.project +++ b/jpa/.project @@ -24,6 +24,7 @@ org.eclipse.jdt.core.javanature org.eclipse.m2e.core.maven2Nature + org.jkiss.dbeaver.DBeaverNature diff --git a/jpa/odata-jpa-coverage/pom.xml b/jpa/odata-jpa-coverage/pom.xml index 61d938d58..3d24b34fd 100644 --- a/jpa/odata-jpa-coverage/pom.xml +++ b/jpa/odata-jpa-coverage/pom.xml @@ -10,7 +10,6 @@ odata-jpa-coverage odata-jpa-coverage https://github.com/SAP/olingo-jpa-processor-v4 - com.sap.olingo diff --git a/jpa/odata-jpa-metadata/old.project b/jpa/odata-jpa-metadata/old.project new file mode 100644 index 000000000..ba05291b0 --- /dev/null +++ b/jpa/odata-jpa-metadata/old.project @@ -0,0 +1,47 @@ + + + jpa-metadata + + + + + + org.eclipse.wst.common.project.facet.core.builder + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.wst.validation.validationbuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jem.workbench.JavaEMFNature + org.eclipse.wst.common.modulecore.ModuleCoreNature + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + org.eclipse.wst.common.project.facet.core.nature + + + + 1634102235525 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + + diff --git a/jpa/odata-jpa-metadata/src/main/java/com/sap/olingo/jpa/metadata/core/edm/mapper/api/JPAEntityType.java b/jpa/odata-jpa-metadata/src/main/java/com/sap/olingo/jpa/metadata/core/edm/mapper/api/JPAEntityType.java index 42e89afef..06e9b87dc 100644 --- a/jpa/odata-jpa-metadata/src/main/java/com/sap/olingo/jpa/metadata/core/edm/mapper/api/JPAEntityType.java +++ b/jpa/odata-jpa-metadata/src/main/java/com/sap/olingo/jpa/metadata/core/edm/mapper/api/JPAEntityType.java @@ -61,6 +61,12 @@ public interface JPAEntityType extends JPAStructuredType, JPAAnnotatable { */ public boolean hasCompoundKey(); + /** + * True in case the entity type has an EmbeddedId + * @return + */ + public boolean hasEmbeddedKey(); + /** * @return a list of JPAPath to attributes marked with EdmSearchable * @throws ODataJPAModelException diff --git a/jpa/odata-jpa-metadata/src/main/java/com/sap/olingo/jpa/metadata/core/edm/mapper/impl/IntermediateEntityType.java b/jpa/odata-jpa-metadata/src/main/java/com/sap/olingo/jpa/metadata/core/edm/mapper/impl/IntermediateEntityType.java index e15381d83..54522ae49 100644 --- a/jpa/odata-jpa-metadata/src/main/java/com/sap/olingo/jpa/metadata/core/edm/mapper/impl/IntermediateEntityType.java +++ b/jpa/odata-jpa-metadata/src/main/java/com/sap/olingo/jpa/metadata/core/edm/mapper/impl/IntermediateEntityType.java @@ -295,6 +295,12 @@ public boolean hasCompoundKey() { || idType instanceof EmbeddableType; } + @Override + public boolean hasEmbeddedKey() { + return ((IdentifiableType) jpaManagedType).hasSingleIdAttribute() + && hasCompoundKey(); + } + @Override public boolean hasEtag() throws ODataJPAModelException { buildEdmTypeIfEmpty(); diff --git a/jpa/odata-jpa-metadata/src/test/java/com/sap/olingo/jpa/metadata/core/edm/mapper/impl/IntermediateEntityTypeTest.java b/jpa/odata-jpa-metadata/src/test/java/com/sap/olingo/jpa/metadata/core/edm/mapper/impl/IntermediateEntityTypeTest.java index 8c1cfe60b..1135f7386 100644 --- a/jpa/odata-jpa-metadata/src/test/java/com/sap/olingo/jpa/metadata/core/edm/mapper/impl/IntermediateEntityTypeTest.java +++ b/jpa/odata-jpa-metadata/src/test/java/com/sap/olingo/jpa/metadata/core/edm/mapper/impl/IntermediateEntityTypeTest.java @@ -677,6 +677,27 @@ void checkIdIsNotCompound() { assertFalse(et.hasCompoundKey()); } + @Test + void checkEmbeddedIdKeyIsEmbedded() { + final IntermediateEntityType et = new IntermediateEntityType<>( + new JPADefaultEdmNameBuilder(PUNIT_NAME), getEntityType(AdministrativeDivisionDescription.class), schema); + assertTrue(et.hasEmbeddedKey()); + } + + @Test + void checkMultipleKeyIsNotEmbedded() { + final IntermediateEntityType et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder( + PUNIT_NAME), getEntityType(AdministrativeDivision.class), schema); + assertFalse(et.hasEmbeddedKey()); + } + + @Test + void checkIdIsNotEmbedded() { + final IntermediateEntityType et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder( + PUNIT_NAME), getEntityType(BusinessPartner.class), schema); + assertFalse(et.hasEmbeddedKey()); + } + @Test void checkEntityWithMappedSuperClassContainsAllProperties() throws ODataJPAModelException { final IntermediateEntityType et = new IntermediateEntityType<>(new JPADefaultEdmNameBuilder(PUNIT_NAME), diff --git a/jpa/odata-jpa-processor/old.project b/jpa/odata-jpa-processor/old.project new file mode 100644 index 000000000..1fd67ec03 --- /dev/null +++ b/jpa/odata-jpa-processor/old.project @@ -0,0 +1,47 @@ + + + jpa-processor + + + + + + org.eclipse.wst.common.project.facet.core.builder + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.wst.validation.validationbuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jem.workbench.JavaEMFNature + org.eclipse.wst.common.modulecore.ModuleCoreNature + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + org.eclipse.wst.common.project.facet.core.nature + + + + 1634102235540 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + + \ No newline at end of file diff --git a/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/api/JPAODataQueryDirectives.java b/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/api/JPAODataQueryDirectives.java index 6b66f0c6e..8fc256798 100644 --- a/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/api/JPAODataQueryDirectives.java +++ b/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/api/JPAODataQueryDirectives.java @@ -10,19 +10,28 @@ public static JPAODataQueryDirectivesBuilder with(final Builder builder) { int getMaxValuesInInClause(); - static record JPAODataQueryDirectivesImpl(int maxValuesInInClause) implements JPAODataQueryDirectives { + UuidSortOrder getUuidSortOrder(); + + static record JPAODataQueryDirectivesImpl(int maxValuesInInClause, UuidSortOrder uuidSortOrder) implements + JPAODataQueryDirectives { @Override public int getMaxValuesInInClause() { return maxValuesInInClause; } + @Override + public UuidSortOrder getUuidSortOrder() { + return uuidSortOrder; + } + } static class JPAODataQueryDirectivesBuilderImpl implements JPAODataQueryDirectivesBuilder { private final Builder parent; private int maxValuesInInClause = 0; + private UuidSortOrder uuidSortOrder = UuidSortOrder.AS_STRING; JPAODataQueryDirectivesBuilderImpl(final Builder builder) { this.parent = builder; @@ -36,7 +45,20 @@ public JPAODataQueryDirectivesBuilder maxValuesInInClause(final int maxValues) { @Override public JPAODataServiceContextBuilder build() { - return parent.setQueryDirectives(new JPAODataQueryDirectivesImpl(maxValuesInInClause)); + return parent.setQueryDirectives(new JPAODataQueryDirectivesImpl(maxValuesInInClause, uuidSortOrder)); + } + + @Override + public JPAODataQueryDirectivesBuilder uuidSortOrder(final UuidSortOrder order) { + this.uuidSortOrder = order; + return this; } } + + public enum UuidSortOrder { + AS_STRING, + AS_BYTE_ARRAY, + AS_JAVA_UUID; + } + } diff --git a/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/api/JPAODataQueryDirectivesBuilder.java b/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/api/JPAODataQueryDirectivesBuilder.java index 674e3496a..95392f47b 100644 --- a/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/api/JPAODataQueryDirectivesBuilder.java +++ b/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/api/JPAODataQueryDirectivesBuilder.java @@ -1,9 +1,13 @@ package com.sap.olingo.jpa.processor.core.api; +import com.sap.olingo.jpa.processor.core.api.JPAODataQueryDirectives.UuidSortOrder; + public interface JPAODataQueryDirectivesBuilder { JPAODataQueryDirectivesBuilder maxValuesInInClause(int i); + JPAODataQueryDirectivesBuilder uuidSortOrder(UuidSortOrder order); + JPAODataServiceContextBuilder build(); } \ No newline at end of file diff --git a/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/api/JPAODataServiceContext.java b/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/api/JPAODataServiceContext.java index 7c1a27f9b..bb44bcba3 100644 --- a/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/api/JPAODataServiceContext.java +++ b/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/api/JPAODataServiceContext.java @@ -106,6 +106,7 @@ public JPAODataDatabaseOperations getOperationConverter() { return operationConverter; } + @Override public JPAODataPagingProvider getPagingProvider() { return pagingProvider; } diff --git a/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/api/example/JPAExampleCUDRequestHandler.java b/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/api/example/JPAExampleCUDRequestHandler.java index c9f4830c9..471929bbd 100644 --- a/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/api/example/JPAExampleCUDRequestHandler.java +++ b/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/api/example/JPAExampleCUDRequestHandler.java @@ -84,7 +84,8 @@ public Object createEntity(final JPARequestEntity requestEntity, final EntityMan em.persist(instance); } } else { - // POST on Link only // https://issues.oasis-open.org/browse/ODATA-1294 + // POST on Link only + // https://issues.oasis-open.org/browse/ODATA-1294 instance = findEntity(requestEntity, em); } processRelatedEntities(requestEntity.getRelatedEntities(), instance, requestEntity.getModifyUtil(), em); @@ -109,14 +110,16 @@ public JPAUpdateResult updateEntity(final JPARequestEntity requestEntity, final final HttpMethod method) throws ODataJPAProcessException { if (method == HttpMethod.PATCH || method == HttpMethod.DELETE) { - final Object instance = em.find(requestEntity.getEntityType().getTypeClass(), requestEntity.getModifyUtil() + Object instance = em.find(requestEntity.getEntityType().getTypeClass(), requestEntity.getModifyUtil() .createPrimaryKey(requestEntity.getEntityType(), requestEntity.getKeys(), requestEntity.getEntityType())); - if (instance == null) throw new JPAExampleModifyException(ENTITY_NOT_FOUND, HttpStatusCode.NOT_FOUND); - requestEntity.getModifyUtil().setAttributesDeep(requestEntity.getData(), instance, requestEntity.getEntityType()); + if (instance == null) { + instance = createOneEntity(requestEntity, null); + requestEntity.getModifyUtil().setPrimaryKey(requestEntity.getEntityType(), requestEntity.getKeys(), instance); - updateLinks(requestEntity, em, instance); - setAuditInformation(instance, requestEntity.getClaims(), false); - return new JPAUpdateResult(false, instance); + return new JPAUpdateResult(true, instance); + } else { + return updateFoundEntity(requestEntity, em, instance); + } } return super.updateEntity(requestEntity, em, method); } @@ -128,6 +131,16 @@ public void validateChanges(final EntityManager em) throws ODataJPAProcessExcept processBindingLinks(entity.getValue().getRelationLinks(), entity.getKey(), entity.getValue().getModifyUtil(), em); } + private JPAUpdateResult updateFoundEntity(final JPARequestEntity requestEntity, final EntityManager em, + final Object instance) throws ODataJPAProcessorException, ODataJPAInvocationTargetException { + requestEntity.getModifyUtil().setAttributesDeep(requestEntity.getData(), instance, requestEntity + .getEntityType()); + + updateLinks(requestEntity, em, instance); + setAuditInformation(instance, requestEntity.getClaims(), false); + return new JPAUpdateResult(false, instance); + } + private void checkAuthorizationsOneClaim(final JPAProtectionInfo protectionInfo, final Object value, final List> pairs) throws JPAExampleModifyException { boolean match = false; diff --git a/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/processor/JPAModifyUtil.java b/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/processor/JPAModifyUtil.java index cd5be773c..eca048a81 100644 --- a/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/processor/JPAModifyUtil.java +++ b/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/processor/JPAModifyUtil.java @@ -6,6 +6,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -86,6 +87,7 @@ Map buildSetterList(@Nonnull final Class type, * * @param et * @param jpaKeys + * @param st * @return * @throws ODataJPAProcessorException */ @@ -129,6 +131,40 @@ public Object createPrimaryKey(final JPAEntityType et, final Object instance) th } } + /** + * Fills the key properties in instance. This is helpful e.g. for Upserts.
+ * Setter for the key attributes are required. + * @param et Metadata describing the entity type + * @param jpaKeys Map of key properties + * @param instance Instance of the JPA entity to be filled + * @throws ODataJPAProcessorException + */ + public void setPrimaryKey(final JPAEntityType et, final Map jpaKeys, final Object instance) + throws ODataJPAProcessorException { + try { + if (et.hasEmbeddedKey()) { + final Object key = et.getKeyType().getConstructor().newInstance(); + for (final JPAAttribute keyElement : et.getKey()) { + setAttribute(key, keyElement, jpaKeys.get(keyElement.getInternalName())); + } + final var setter = Arrays.stream(instance.getClass().getMethods()) + .filter(method -> method.getParameterCount() == 1 + && method.getParameters()[0].getType() == et.getKeyType()) + .findAny(); + if (setter.isPresent()) { + setter.get().invoke(instance, key); + } + } else { + for (final var key : et.getKey()) { + setAttribute(instance, key, jpaKeys.get(key.getInternalName())); + } + } + } catch (NoSuchMethodException | SecurityException | IllegalAccessException + | IllegalArgumentException | InvocationTargetException | InstantiationException | ODataJPAModelException e) { + throw new ODataJPAProcessorException(e, HttpStatusCode.INTERNAL_SERVER_ERROR); + } + } + /** * Sets a link between a source and target instance. Prerequisite are * existing setter and getter on the level of the sourceInstance. In case of to n associations it is expected that the @@ -424,7 +460,8 @@ private void setEmbeddedAttributeDeep(final Object instance, final JPAStructured } @SuppressWarnings("unchecked") - private void setEmbeddedCollectionAttributeDeep(final Object instance, final JPAStructuredType st, final Method method, + private void setEmbeddedCollectionAttributeDeep(final Object instance, final JPAStructuredType st, + final Method method, final Object value, final Class[] parameters, final JPAAttribute attribute) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, ODataJPAModelException, ODataJPAProcessorException, ODataJPAInvocationTargetException { diff --git a/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/processor/JPANavigationRequestProcessor.java b/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/processor/JPANavigationRequestProcessor.java index 800363746..bc1f0ef8b 100644 --- a/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/processor/JPANavigationRequestProcessor.java +++ b/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/processor/JPANavigationRequestProcessor.java @@ -213,8 +213,8 @@ JPAETagValidationResult validateEntityTag(final JPAConvertibleResult result, fin return JPAETagValidationResult.SUCCESS; } - boolean isRootResultEmpty(JPAConvertibleResult result) { - if (result instanceof JPAExpandResult expandResult) { + private boolean isRootResultEmpty(final JPAConvertibleResult result) { + if (result instanceof final JPAExpandResult expandResult) { return expandResult.getResult(ROOT_RESULT_KEY).isEmpty(); } return false; diff --git a/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/properties/JPAAbstractProcessorAttributeImpl.java b/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/properties/JPAAbstractProcessorAttributeImpl.java index 21d269bb4..1d3868711 100644 --- a/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/properties/JPAAbstractProcessorAttributeImpl.java +++ b/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/properties/JPAAbstractProcessorAttributeImpl.java @@ -81,4 +81,9 @@ public boolean requiresJoin() { return !hops.isEmpty(); } + @Override + public JPAPath getJPAPath() { + return path; + } + } diff --git a/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/properties/JPAProcessorAttribute.java b/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/properties/JPAProcessorAttribute.java index 7948ea141..1b7b3330f 100644 --- a/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/properties/JPAProcessorAttribute.java +++ b/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/properties/JPAProcessorAttribute.java @@ -11,6 +11,7 @@ import jakarta.persistence.criteria.Order; import jakarta.persistence.criteria.Path; +import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPAPath; import com.sap.olingo.jpa.processor.core.exception.ODataJPAQueryException; public interface JPAProcessorAttribute { @@ -75,4 +76,6 @@ JPAProcessorAttribute setTarget(@Nonnull final From target, @Nonnull final */ Path getPath(); + JPAPath getJPAPath(); + } diff --git a/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/query/JPAExpandQueryResult.java b/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/query/JPAExpandQueryResult.java index b224b83c1..f7b1264e8 100644 --- a/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/query/JPAExpandQueryResult.java +++ b/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/query/JPAExpandQueryResult.java @@ -222,7 +222,7 @@ public Optional getKeyBoundary(final JPAODataRequestContextAcces && (requestContext.getUriInfo().getTopOption() != null || requestContext.getUriInfo().getSkipOption() != null)) { - final JPAKeyPair boundary = new JPAKeyPair(jpaEntityType.getKey()); + final JPAKeyPair boundary = new JPAKeyPair(jpaEntityType.getKey(), requestContext.getQueryDirectives()); for (final Tuple tuple : jpaResult.get(ROOT_RESULT_KEY)) { @SuppressWarnings("rawtypes") final Map key = createKey(tuple); diff --git a/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/query/JPAKeyPair.java b/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/query/JPAKeyPair.java index af392da2e..26751df70 100644 --- a/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/query/JPAKeyPair.java +++ b/jpa/odata-jpa-processor/src/main/java/com/sap/olingo/jpa/processor/core/query/JPAKeyPair.java @@ -1,11 +1,15 @@ package com.sap.olingo.jpa.processor.core.query; +import java.nio.ByteBuffer; import java.util.List; import java.util.Map; +import java.util.UUID; import jakarta.persistence.AttributeConverter; import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPAAttribute; +import com.sap.olingo.jpa.processor.core.api.JPAODataQueryDirectives; +import com.sap.olingo.jpa.processor.core.api.JPAODataQueryDirectives.UuidSortOrder; import com.sap.olingo.jpa.processor.core.exception.ODataJPAKeyPairException; /** @@ -25,10 +29,12 @@ public class JPAKeyPair { private Map min; private Map max; private final List keyDefinition; + private final UuidSortOrder uuidSortOrder; - public JPAKeyPair(final List keyDef) { + public JPAKeyPair(final List keyDef, final JPAODataQueryDirectives queryDirectives) { super(); this.keyDefinition = keyDef; + this.uuidSortOrder = queryDirectives.getUuidSortOrder(); } public Map getMin() { @@ -95,6 +101,13 @@ private int compareValues(final Comparable value, final Map value.toString().compareTo(uuid.toString()); + case AS_BYTE_ARRAY -> new ComparableByteArray(convertUUIDToBytes((UUID) value)) + .compareTo(convertUUIDToBytes(uuid)); + case AS_JAVA_UUID -> value.compareTo(uuid); + }; } return value.compareTo(minValue); } @@ -103,4 +116,11 @@ private int compareValues(final Comparable value, final Map createOrderByList(@Nonnull final Map> joinTables, LOGGER.trace("Determined $top/$skip or page: add primary key to Order By"); final var factory = new JPAOrderByPropertyFactory(); for (final var key : jpaEntity.getKey()) { - orderByAttributes.add(factory.createProperty(target, jpaEntity.getPath(key.getExternalName()), cb)); + if (!containsAttribute(orderByAttributes, key)) + orderByAttributes.add(factory.createProperty(target, jpaEntity.getPath(key.getExternalName()), cb)); } } @@ -132,6 +134,18 @@ List createOrderByList(@Nonnull final Map> joinTables, return result; } + private boolean containsAttribute(final List orderByAttributes, final JPAAttribute key) + throws ODataJPAModelException { + + var found = false; + for (final var attribute : orderByAttributes) { + found = attribute.getJPAPath().equals(jpaEntity.getPath(key.getExternalName())); + if (found) + break; + } + return found; + } + @Nonnull List createOrderByList(final Map> joinTables) { return Collections.emptyList(); diff --git a/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/api/example/JPAExampleCUDRequestHandlerTest.java b/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/api/example/JPAExampleCUDRequestHandlerTest.java index 34a3d2748..d7f57b4ac 100644 --- a/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/api/example/JPAExampleCUDRequestHandlerTest.java +++ b/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/api/example/JPAExampleCUDRequestHandlerTest.java @@ -443,22 +443,24 @@ void checkCreateEntityAutoIdNoJpaEntityType() throws ODataJPAProcessException, O } @Test - void checkUpdateEntityNotFound() throws ODataJPAProcessException, ODataJPAModelException { - final String id = "1"; + void checkUpdateEntityNotFoundUpsertsEntity() throws ODataJPAModelException, ODataJPAProcessException { + final String id = "100"; final Organization beforeImage = new Organization(id); final JPAEntityType et = helper.getJPAEntityType("Organizations"); beforeImage.setName1("Example Ltd"); doReturn(et).when(requestEntity).getEntityType(); doReturn(null).when(em).find(eq(et.getTypeClass()), any()); - data.put("name1", "Example SE"); keys.put("iD", id); - final ODataJPAProcessException exception = assertThrows(ODataJPAProcessException.class, () -> cut.updateEntity( - requestEntity, em, HttpMethod.PATCH)); + final var act = cut.updateEntity(requestEntity, em, HttpMethod.PATCH); + + assertNotNull(act); + assertTrue(act.wasCreate()); - assertEquals(404, exception.getStatusCode()); + final Organization org = (Organization) act.modifiedEntity(); + assertEquals("100", org.getID()); } @@ -768,16 +770,14 @@ void checkAuditFieldsSetOnUpdate() throws ODataJPAModelException, ODataJPAProces } @Test - void checkAuthorizationsCreateRejectsNotClaimsNotAllowed() throws ODataJPAModelException, - ODataJPAProcessException { + void checkAuthorizationsCreateRejectsNotClaimsNotAllowed() { final JPAExampleModifyException act = assertThrows(JPAExampleModifyException.class, () -> createPersonProtected(null)); assertEquals(HttpStatusCode.FORBIDDEN.getStatusCode(), act.getStatusCode()); } @Test - void checkAuthorizationsCreateRejectsAttributeNotPresent() throws ODataJPAModelException, - ODataJPAProcessException { + void checkAuthorizationsCreateRejectsAttributeNotPresent() { final JPAODataClaimProvider claims = mock(JPAODataClaimProvider.class); final JPAExampleModifyException act = assertThrows(JPAExampleModifyException.class, () -> createPersonProtected( claims)); @@ -785,8 +785,7 @@ void checkAuthorizationsCreateRejectsAttributeNotPresent() throws ODataJPAModelE } @Test - void checkAuthorizationsCreateRejectsAttributeNotMatch() throws ODataJPAModelException, - ODataJPAProcessException { + void checkAuthorizationsCreateRejectsAttributeNotMatch() { final JPAClaimsPair claim = new JPAClaimsPair<>("DOT01"); final JPAODataClaimProvider claims = mock(JPAODataClaimProvider.class); when(claims.get("BuildingNumber")).thenReturn(singletonList(claim)); @@ -796,8 +795,7 @@ void checkAuthorizationsCreateRejectsAttributeNotMatch() throws ODataJPAModelExc } @Test - void checkAuthorizationsCreateRejectedOnlyOneProvided() throws ODataJPAModelException, - ODataJPAProcessException { + void checkAuthorizationsCreateRejectedOnlyOneProvided() { final JPAClaimsPair claim = new JPAClaimsPair<>("MID*"); final JPAODataClaimProvider claims = mock(JPAODataClaimProvider.class); when(claims.get("BuildingNumber")).thenReturn(singletonList(claim)); @@ -828,8 +826,7 @@ void checkAuthorizationsCreateAllowedWithWildCardMix() throws ODataJPAModelExcep } @Test - void checkAuthorizationsCreateRejectsWildCardNotMatch() throws ODataJPAModelException, - ODataJPAProcessException { + void checkAuthorizationsCreateRejectsWildCardNotMatch() { final JPAClaimsPair claim = new JPAClaimsPair<>("D_D*"); final JPAExampleModifyException act = assertThrows(JPAExampleModifyException.class, () -> createPersonProtected(createPersonProtectedClaims(claim))); @@ -851,8 +848,7 @@ void checkAuthorizationsCreateAllowedInRangeWildcard() throws ODataJPAModelExcep } @Test - void checkAuthorizationsCreateRejectRangeWildcardMin() throws ODataJPAModelException, - ODataJPAProcessException { + void checkAuthorizationsCreateRejectRangeWildcardMin() { final JPAClaimsPair claim = new JPAClaimsPair<>("MI+0*", "MID99"); final JPAExampleModifyException act = assertThrows(JPAExampleModifyException.class, () -> createPersonProtected(createPersonProtectedClaims(claim))); @@ -860,8 +856,7 @@ void checkAuthorizationsCreateRejectRangeWildcardMin() throws ODataJPAModelExcep } @Test - void checkAuthorizationsCreateRejectRangeWildcardMax() throws ODataJPAModelException, - ODataJPAProcessException { + void checkAuthorizationsCreateRejectRangeWildcardMax() { final JPAClaimsPair claim = new JPAClaimsPair<>("MID00", "MI*99"); final JPAExampleModifyException act = assertThrows(JPAExampleModifyException.class, () -> createPersonProtected(createPersonProtectedClaims(claim))); @@ -890,8 +885,7 @@ void checkAuthorizationsCreateAllowedInRange() throws ODataJPAModelException, // } @Test - void checkAuthorizationsCreateRejectRangeToLow() throws ODataJPAModelException, - ODataJPAProcessException { + void checkAuthorizationsCreateRejectRangeToLow() throws ODataJPAModelException { buildOrganizationMockForAuthorizationTest(); data.put("id", 5); @@ -901,8 +895,7 @@ void checkAuthorizationsCreateRejectRangeToLow() throws ODataJPAModelException, } @Test - void checkAuthorizationsCreateRejectRangeToHigh() throws ODataJPAModelException, - ODataJPAProcessException { + void checkAuthorizationsCreateRejectRangeToHigh() throws ODataJPAModelException { buildOrganizationMockForAuthorizationTest(); data.put("id", 500); @@ -953,12 +946,8 @@ private void buildOrganizationMockForAuthorizationTest() throws ODataJPAModelExc when(path.getPath()).thenReturn(singletonList(idAttribute)); - when(et.getTypeClass()).thenAnswer(new Answer>() { - @Override - public Class answer(final InvocationOnMock invocation) throws Throwable { - return OrganizationWithAudit.class; - } - }); + doReturn(OrganizationWithAudit.class).when(et).getTypeClass(); + when(et.getKey()).thenReturn(singletonList(idAttribute)); when(et.getProtections()).thenReturn(singletonList(protectionInfo)); when(et.getAttribute("id")).thenReturn(Optional.of(idAttribute)); @@ -1008,8 +997,7 @@ private Object createAdminDiv() throws ODataJPAModelException, ODataJPAProcessEx data.put("countryCode", "DEU"); data.put("codePublisher", "Eurostat"); - final Object act = cut.createEntity(requestEntity, em); - return act; + return cut.createEntity(requestEntity, em); } private OrganizationWithAudit createOrganization() throws ODataJPAModelException, ODataJPAProcessException { @@ -1083,7 +1071,6 @@ private JPAUpdateResult updateSimplePrimitiveValue() throws ODataJPAModelExcepti data.put("name1", "Example SE"); keys.put("iD", id); - final JPAUpdateResult act = cut.updateEntity(requestEntity, em, HttpMethod.PATCH); - return act; + return cut.updateEntity(requestEntity, em, HttpMethod.PATCH); } } diff --git a/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/processor/JPAModifyUtilTest.java b/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/processor/JPAModifyUtilTest.java index 0f123eb2a..95f309f9a 100644 --- a/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/processor/JPAModifyUtilTest.java +++ b/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/processor/JPAModifyUtilTest.java @@ -5,6 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -20,8 +21,6 @@ import org.apache.olingo.commons.api.ex.ODataException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPAAssociationPath; import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPAAttribute; @@ -30,6 +29,8 @@ import com.sap.olingo.jpa.processor.core.exception.ODataJPAInvocationTargetException; import com.sap.olingo.jpa.processor.core.exception.ODataJPAProcessException; import com.sap.olingo.jpa.processor.core.exception.ODataJPAProcessorException; +import com.sap.olingo.jpa.processor.core.testmodel.AdministrativeDivision; +import com.sap.olingo.jpa.processor.core.testmodel.AdministrativeDivisionDescription; import com.sap.olingo.jpa.processor.core.testmodel.AdministrativeDivisionKey; import com.sap.olingo.jpa.processor.core.testmodel.BusinessPartner; import com.sap.olingo.jpa.processor.core.testmodel.BusinessPartnerRole; @@ -119,8 +120,7 @@ void testSetAttributesDeepMultipleAttribute() throws ODataJPAProcessException { } @Test - void testSetAttributeDeepIfAttributeNull() throws ODataJPAProcessException, - ODataJPAInvocationTargetException { + void testSetAttributeDeepIfAttributeNull() throws ODataJPAProcessException { partner.setType("2"); jpaAttributes.put("iD", "Willi"); jpaAttributes.put("type", null); @@ -130,8 +130,7 @@ void testSetAttributeDeepIfAttributeNull() throws ODataJPAProcessException, } @Test - void testDoNotSetAttributeDeepIfNotInMap() throws ODataJPAProcessException, - ODataJPAInvocationTargetException { + void testDoNotSetAttributeDeepIfNotInMap() throws ODataJPAProcessException { partner.setType("2"); jpaAttributes.put("iD", "Willi"); cut.setAttributesDeep(jpaAttributes, partner, org); @@ -140,8 +139,7 @@ void testDoNotSetAttributeDeepIfNotInMap() throws ODataJPAProcessException, } @Test - void testSetAttributesDeepShallIgnoreRequestEntities() throws ODataJPAProcessException, - ODataJPAInvocationTargetException { + void testSetAttributesDeepShallIgnoreRequestEntities() throws ODataJPAProcessException { try { final JPARequestEntity roles = mock(JPARequestEntity.class); jpaAttributes.put("iD", "Willi"); @@ -154,8 +152,7 @@ void testSetAttributesDeepShallIgnoreRequestEntities() throws ODataJPAProcessExc } @Test - void testSetAttributesDeepOneLevelViaGetter() throws ODataJPAProcessException, - ODataJPAInvocationTargetException, ODataJPAModelException { + void testSetAttributesDeepOneLevelViaGetter() throws ODataJPAProcessException { final Map embeddedAttributes = new HashMap<>(); jpaAttributes.put("iD", "Willi"); jpaAttributes.put("address", embeddedAttributes); @@ -183,8 +180,7 @@ void testSetAttributesDeepOneLevelViaGetterWithWrongRequestData() throws Throwab } @Test - void testDoNotSetAttributesDeepOneLevelIfNotProvided() throws ODataJPAProcessException, - ODataJPAInvocationTargetException { + void testDoNotSetAttributesDeepOneLevelIfNotProvided() throws ODataJPAProcessException { jpaAttributes.put("iD", "Willi"); jpaAttributes.put("address", null); cut.setAttributesDeep(jpaAttributes, partner, org); @@ -194,8 +190,7 @@ void testDoNotSetAttributesDeepOneLevelIfNotProvided() throws ODataJPAProcessExc } @Test - void testSetAttributesDeepOneLevelIfNull() throws ODataJPAProcessException, - ODataJPAInvocationTargetException { + void testSetAttributesDeepOneLevelIfNull() throws ODataJPAProcessException { final PostalAddressData address = new PostalAddressData(); address.setCityName("Test City"); @@ -209,8 +204,7 @@ void testSetAttributesDeepOneLevelIfNull() throws ODataJPAProcessException, } @Test - void testSetAttributesDeepOneLevelViaSetter() throws ODataJPAProcessException, - ODataJPAInvocationTargetException, ODataJPAModelException { + void testSetAttributesDeepOneLevelViaSetter() throws ODataJPAProcessException { final Map embeddedAttributes = new HashMap<>(); jpaAttributes.put("iD", "Willi"); jpaAttributes.put("communicationData", embeddedAttributes); @@ -223,7 +217,7 @@ void testSetAttributesDeepOneLevelViaSetter() throws ODataJPAProcessException, } @Test - void testSetAttributesDeepTwoLevel() throws ODataJPAProcessException, ODataJPAModelException { + void testSetAttributesDeepTwoLevel() throws ODataJPAProcessException { final Map embeddedAttributes = new HashMap<>(); final Map innerEmbeddedAttributes = new HashMap<>(); jpaAttributes.put("iD", "Willi"); @@ -242,12 +236,7 @@ void testSetAttributesDeepTwoLevel() throws ODataJPAProcessException, ODataJPAMo void testCreatePrimaryKeyOneStringKeyField() throws ODataJPAProcessException, ODataJPAModelException { final JPAEntityType et = createSingleKeyEntityType(); - when(et.getKeyType()).thenAnswer(new Answer>() { - @Override - public Class answer(final InvocationOnMock invocation) throws Throwable { - return String.class; - } - }); + doReturn(String.class).when(et).getKeyType(); jpaAttributes.put("iD", "Willi"); final String act = (String) cut.createPrimaryKey(et, jpaAttributes, org); @@ -258,12 +247,7 @@ public Class answer(final InvocationOnMock invocation) throws Throwable { void testCreatePrimaryKeyOneIntegerKeyField() throws ODataJPAProcessException, ODataJPAModelException { final JPAEntityType et = createSingleKeyEntityType(); - when(et.getKeyType()).thenAnswer(new Answer>() { - @Override - public Class answer(final InvocationOnMock invocation) throws Throwable { - return Integer.class; - } - }); + doReturn(Integer.class).when(et).getKeyType(); jpaAttributes.put("iD", Integer.valueOf(10)); final Integer act = (Integer) cut.createPrimaryKey(et, jpaAttributes, org); @@ -274,12 +258,7 @@ public Class answer(final InvocationOnMock invocation) throws Throwable { void testCreatePrimaryKeyOneBigIntegerKeyField() throws ODataJPAProcessException, ODataJPAModelException { final JPAEntityType et = createSingleKeyEntityType(); - when(et.getKeyType()).thenAnswer(new Answer>() { - @Override - public Class answer(final InvocationOnMock invocation) throws Throwable { - return BigInteger.class; - } - }); + doReturn(BigInteger.class).when(et).getKeyType(); jpaAttributes.put("iD", new BigInteger("10")); final BigInteger act = (BigInteger) cut.createPrimaryKey(et, jpaAttributes, org); @@ -287,15 +266,10 @@ public Class answer(final InvocationOnMock invocation) throws Throwable { } @Test - void testCreatePrimaryKeyMultipleField() throws ODataJPAProcessException, ODataJPAModelException { + void testCreatePrimaryKeyMultipleField() throws ODataJPAProcessException { final JPAEntityType et = mock(JPAEntityType.class); - when(et.getKeyType()).thenAnswer(new Answer>() { - @Override - public Class answer(final InvocationOnMock invocation) throws Throwable { - return AdministrativeDivisionKey.class; - } - }); + doReturn(AdministrativeDivisionKey.class).when(et).getKeyType(); jpaAttributes.put("codePublisher", "Test"); jpaAttributes.put("codeID", "10"); @@ -321,6 +295,52 @@ void testDeepLinkComplexNotExist() throws ODataJPAProcessorException, ODataJPAMo assertEquals(target, source.getAdministrativeInformation().getUpdated().getUser()); } + @Test + void testSetPrimitiveKeyString() throws ODataJPAModelException, ODataJPAProcessorException { + + final var et = helper.getJPAEntityType(Person.class); + final var act = new Person(); + jpaAttributes.put("iD", "10"); + + cut.setPrimaryKey(et, jpaAttributes, act); + assertEquals("10", act.getID()); + } + + @Test + void testSetCompoundKeyString() throws ODataJPAModelException, ODataJPAProcessorException { + + final var et = helper.getJPAEntityType(AdministrativeDivision.class); + final var act = new AdministrativeDivision(); + jpaAttributes.put("codePublisher", "Test"); + jpaAttributes.put("codeID", "10"); + jpaAttributes.put("divisionCode", "10.1"); + + cut.setPrimaryKey(et, jpaAttributes, act); + assertEquals("Test", act.getCodePublisher()); + assertEquals("10", act.getCodeID()); + assertEquals("10.1", act.getDivisionCode()); + } + + @Test + void testSetEmbeddedKeyString() throws ODataJPAModelException, ODataJPAProcessorException { + + final var et = helper.getJPAEntityType(AdministrativeDivisionDescription.class); + final var act = new AdministrativeDivisionDescription(); + jpaAttributes.put("codePublisher", "Test"); + jpaAttributes.put("codeID", "10"); + jpaAttributes.put("divisionCode", "10.1"); + jpaAttributes.put("language", "DE"); + + cut.setPrimaryKey(et, jpaAttributes, act); + + final var key = act.getKey(); + + assertEquals("Test", key.getCodePublisher()); + assertEquals("10", key.getCodeID()); + assertEquals("10.1", key.getDivisionCode()); + assertEquals("DE", key.getLanguage()); + } + @Test void testDirectLink() throws ODataJPAProcessorException, ODataJPAModelException { final Organization source = new Organization("100"); @@ -350,8 +370,7 @@ void testSetForeignKeyOneKey() throws ODataJPAModelException, ODataJPAProcessorE } @Test - void testSetForeignKeyThrowsExceptionOnMissingGetter() throws ODataJPAModelException, - ODataJPAProcessorException { + void testSetForeignKeyThrowsExceptionOnMissingGetter() throws ODataJPAModelException { final OrganizationWithoutGetter source = new OrganizationWithoutGetter("100"); final BusinessPartnerRole target = new BusinessPartnerRole(); target.setRoleCategory("A"); @@ -363,8 +382,7 @@ void testSetForeignKeyThrowsExceptionOnMissingGetter() throws ODataJPAModelExcep } @Test - void testSetForeignKeyThrowsExceptionOnMissingSetter() throws ODataJPAModelException, - ODataJPAProcessorException { + void testSetForeignKeyThrowsExceptionOnMissingSetter() throws ODataJPAModelException { final Organization source = new Organization("100"); final BusinessPartnerRoleWithoutSetter target = new BusinessPartnerRoleWithoutSetter(); final JPAAssociationPath path = helper.getJPAAssociationPath("Organizations", diff --git a/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/processor/JPAODataInternalRequestContextTest.java b/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/processor/JPAODataInternalRequestContextTest.java index cc3185065..78dac8e5e 100644 --- a/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/processor/JPAODataInternalRequestContextTest.java +++ b/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/processor/JPAODataInternalRequestContextTest.java @@ -50,6 +50,7 @@ import com.sap.olingo.jpa.processor.core.api.JPAODataPagingProvider; import com.sap.olingo.jpa.processor.core.api.JPAODataPathInformation; import com.sap.olingo.jpa.processor.core.api.JPAODataQueryDirectives; +import com.sap.olingo.jpa.processor.core.api.JPAODataQueryDirectives.UuidSortOrder; import com.sap.olingo.jpa.processor.core.api.JPAODataRequestContext; import com.sap.olingo.jpa.processor.core.api.JPAODataRequestContextAccess; import com.sap.olingo.jpa.processor.core.api.JPAODataSessionContextAccess; @@ -115,7 +116,7 @@ void setup() { dbProcessor = mock(JPAODataDatabaseProcessor.class); edmProvider = mock(JPAEdmProvider.class); operationConverter = mock(JPAODataDatabaseOperations.class); - queryDirectives = new JPAODataQueryDirectives.JPAODataQueryDirectivesImpl(0); + queryDirectives = new JPAODataQueryDirectives.JPAODataQueryDirectivesImpl(0, UuidSortOrder.AS_STRING); pagingProvider = mock(JPAODataPagingProvider.class); etagHelper = mock(JPAODataEtagHelper.class); pathInformation = new JPAODataPathInformation("", "", "", ""); diff --git a/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/query/JPAExpandQueryResultTest.java b/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/query/JPAExpandQueryResultTest.java index a05116803..e4097c7b9 100644 --- a/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/query/JPAExpandQueryResultTest.java +++ b/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/query/JPAExpandQueryResultTest.java @@ -31,6 +31,8 @@ import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPAEntityType; import com.sap.olingo.jpa.metadata.core.edm.mapper.exception.ODataJPAModelException; +import com.sap.olingo.jpa.processor.core.api.JPAODataQueryDirectives; +import com.sap.olingo.jpa.processor.core.api.JPAODataQueryDirectives.UuidSortOrder; import com.sap.olingo.jpa.processor.core.api.JPAODataRequestContextAccess; import com.sap.olingo.jpa.processor.core.exception.ODataJPAProcessException; import com.sap.olingo.jpa.processor.core.util.TestBase; @@ -49,6 +51,7 @@ class JPAExpandQueryResultTest extends TestBase { private final List tuples = new ArrayList<>(); private JPAEntityType et; private List hops; + private JPAODataQueryDirectives directives; @BeforeEach void setup() throws ODataException { @@ -61,11 +64,13 @@ void setup() throws ODataException { hops = Arrays.asList(hop0, hop1); et = helper.getJPAEntityType("Organizations"); uriInfo = mock(UriInfoResource.class); + directives = new JPAODataQueryDirectives.JPAODataQueryDirectivesImpl(0, UuidSortOrder.AS_JAVA_UUID); requestContext = mock(JPAODataRequestContextAccess.class); top = mock(TopOption.class); skip = mock(SkipOption.class); expand = mock(ExpandOption.class); when(requestContext.getUriInfo()).thenReturn(uriInfo); + when(requestContext.getQueryDirectives()).thenReturn(directives); queryResult.put("root", tuples); } diff --git a/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/query/JPAExpandQueryTest.java b/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/query/JPAExpandQueryTest.java index d13c37128..7655ae930 100644 --- a/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/query/JPAExpandQueryTest.java +++ b/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/query/JPAExpandQueryTest.java @@ -35,6 +35,8 @@ import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPAAttribute; import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPAEntityType; import com.sap.olingo.jpa.metadata.core.edm.mapper.exception.ODataJPAModelException; +import com.sap.olingo.jpa.processor.core.api.JPAODataQueryDirectives; +import com.sap.olingo.jpa.processor.core.api.JPAODataQueryDirectives.UuidSortOrder; import com.sap.olingo.jpa.processor.core.api.JPAODataRequestContextAccess; import com.sap.olingo.jpa.processor.core.api.JPAServiceDebugger; import com.sap.olingo.jpa.processor.core.database.JPADefaultDatabaseProcessor; @@ -57,16 +59,18 @@ abstract class JPAExpandQueryTest extends TestBase { protected Optional adminBoundary; @SuppressWarnings("rawtypes") protected Map simpleKey; + private JPAODataQueryDirectives directives; @BeforeEach void setup() throws ODataException { createHeaders(); helper = new TestHelper(emf, PUNIT_NAME); em = emf.createEntityManager(); + directives = new JPAODataQueryDirectives.JPAODataQueryDirectivesImpl(0, UuidSortOrder.AS_JAVA_UUID); requestContext = mock(JPAODataRequestContextAccess.class); - organizationPair = new JPAKeyPair(helper.getJPAEntityType("Organizations").getKey()); + organizationPair = new JPAKeyPair(helper.getJPAEntityType("Organizations").getKey(), directives); organizationBoundary = Optional.of(new JPAKeyBoundary(1, organizationPair)); - adminPair = new JPAKeyPair(helper.getJPAEntityType("AdministrativeDivisions").getKey()); + adminPair = new JPAKeyPair(helper.getJPAEntityType("AdministrativeDivisions").getKey(), directives); adminBoundary = Optional.of(new JPAKeyBoundary(1, adminPair)); final JPAServiceDebugger debugger = mock(JPAServiceDebugger.class); diff --git a/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/query/JPAKeyPairTest.java b/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/query/JPAKeyPairTest.java index 81bd9140c..a84dcf07e 100644 --- a/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/query/JPAKeyPairTest.java +++ b/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/query/JPAKeyPairTest.java @@ -6,8 +6,8 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.Arrays; @@ -21,12 +21,11 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPAAttribute; +import com.sap.olingo.jpa.processor.core.api.JPAODataQueryDirectives; +import com.sap.olingo.jpa.processor.core.api.JPAODataQueryDirectives.UuidSortOrder; import com.sap.olingo.jpa.processor.core.exception.ODataJPAKeyPairException; -import com.sap.olingo.jpa.processor.core.exception.ODataJPAQueryException; import com.sap.olingo.jpa.processor.core.testmodel.UUIDToBinaryConverter; import com.sap.olingo.jpa.processor.core.testmodel.UUIDToStringConverter; @@ -40,9 +39,11 @@ class JPAKeyPairTest { private Map key2; private Map key3; private List keyDef; + private JPAODataQueryDirectives directives; @BeforeEach void setup() { + directives = new JPAODataQueryDirectives.JPAODataQueryDirectivesImpl(0, UuidSortOrder.AS_STRING); attribute1 = mock(JPAAttribute.class); attribute2 = mock(JPAAttribute.class); attribute3 = mock(JPAAttribute.class); @@ -51,7 +52,7 @@ void setup() { key3 = new HashMap<>(3); keyDef = new ArrayList<>(3); keyDef.add(attribute1); - cut = new JPAKeyPair(keyDef); + cut = new JPAKeyPair(keyDef, directives); key1.put(attribute1, Integer.valueOf(10)); key2.put(attribute1, Integer.valueOf(100)); } @@ -79,7 +80,7 @@ void testCreatePairWithOneValues() throws ODataJPAKeyPairException { } @Test - void testCreatePairWithTwoValues() throws ODataJPAQueryException, ODataJPAKeyPairException { + void testCreatePairWithTwoValues() throws ODataJPAKeyPairException { cut.setValue(key1); cut.setValue(key2); assertEquals(10, cut.getMin().get(attribute1)); @@ -88,7 +89,7 @@ void testCreatePairWithTwoValues() throws ODataJPAQueryException, ODataJPAKeyPai } @Test - void testCreatePairWithTwoValuesSecondLower() throws ODataJPAQueryException, ODataJPAKeyPairException { + void testCreatePairWithTwoValuesSecondLower() throws ODataJPAKeyPairException { cut.setValue(key2); cut.setValue(key1); assertEquals(10, cut.getMin().get(attribute1)); @@ -97,7 +98,7 @@ void testCreatePairWithTwoValuesSecondLower() throws ODataJPAQueryException, ODa } @Test - void testCreatePairWithThirdValuesHigher() throws ODataJPAQueryException, ODataJPAKeyPairException { + void testCreatePairWithThirdValuesHigher() throws ODataJPAKeyPairException { key3.put(attribute1, Integer.valueOf(101)); cut.setValue(key2); cut.setValue(key1); @@ -107,7 +108,7 @@ void testCreatePairWithThirdValuesHigher() throws ODataJPAQueryException, ODataJ } @Test - void testCreatePairWithThirdValuesLower() throws ODataJPAQueryException, ODataJPAKeyPairException { + void testCreatePairWithThirdValuesLower() throws ODataJPAKeyPairException { key3.put(attribute1, Integer.valueOf(9)); cut.setValue(key2); cut.setValue(key1); @@ -117,7 +118,7 @@ void testCreatePairWithThirdValuesLower() throws ODataJPAQueryException, ODataJP } @Test - void testCreatePairWithThirdValuesBetween() throws ODataJPAQueryException, ODataJPAKeyPairException { + void testCreatePairWithThirdValuesBetween() throws ODataJPAKeyPairException { key3.put(attribute1, Integer.valueOf(50)); cut.setValue(key2); cut.setValue(key1); @@ -207,20 +208,10 @@ void testCreatePairWithThreeCompoundLastKeySmallest() throws ODataJPAKeyPairExce @Test void testCreatePairConversionString() { final JPAAttribute attributeUUID = mock(JPAAttribute.class); - when(attributeUUID.getConverter()).thenAnswer(new Answer() { - @Override - public UUIDToStringConverter answer(final InvocationOnMock invocation) throws Throwable { - return new UUIDToStringConverter(); - } - }); - when(attributeUUID.getRawConverter()).thenAnswer(new Answer() { - @Override - public UUIDToStringConverter answer(final InvocationOnMock invocation) throws Throwable { - return new UUIDToStringConverter(); - } - }); - - cut = new JPAKeyPair(Collections.singletonList(attributeUUID)); + doReturn(new UUIDToStringConverter()).when(attributeUUID).getConverter(); + doReturn(new UUIDToStringConverter()).when(attributeUUID).getRawConverter(); + + cut = new JPAKeyPair(Collections.singletonList(attributeUUID), directives); Arrays.asList("400d7044-1e84-4e63-b2d9-0f58f4ca5bd0", "52a4eb6d-ab9d-4bc8-8405-5255d9607441", "59ce6d1c-0770-48ae-b9ea-47c4ce9994c1", "9768b78c-e010-4e62-bada-8a138be7334d", @@ -235,27 +226,11 @@ public UUIDToStringConverter answer(final InvocationOnMock invocation) throws Th @Test void testCreatePairConversionByteArray() { final JPAAttribute attributeUUID = mock(JPAAttribute.class); - when(attributeUUID.getDbType()).thenAnswer(new Answer>() { - @Override - public Class answer(final InvocationOnMock invocation) throws Throwable { - return byte[].class; - } - }); - when(attributeUUID.getConverter()).thenAnswer(new Answer() { - @Override - public UUIDToBinaryConverter answer(final InvocationOnMock invocation) throws Throwable { - return new UUIDToBinaryConverter(); - } - }); - - when(attributeUUID.getRawConverter()).thenAnswer(new Answer() { - @Override - public UUIDToBinaryConverter answer(final InvocationOnMock invocation) throws Throwable { - return new UUIDToBinaryConverter(); - } - }); - - cut = new JPAKeyPair(Collections.singletonList(attributeUUID)); + doReturn(byte[].class).when(attributeUUID).getDbType(); + doReturn(new UUIDToBinaryConverter()).when(attributeUUID).getConverter(); + doReturn(new UUIDToBinaryConverter()).when(attributeUUID).getRawConverter(); + + cut = new JPAKeyPair(Collections.singletonList(attributeUUID), directives); Arrays.asList("400d7044-1e84-4e63-b2d9-0f58f4ca5bd0", "52a4eb6d-ab9d-4bc8-8405-5255d9607441", "59ce6d1c-0770-48ae-b9ea-47c4ce9994c1", "9768b78c-e010-4e62-bada-8a138be7334d", @@ -270,33 +245,74 @@ public UUIDToBinaryConverter answer(final InvocationOnMock invocation) throws Th @Test void testCreatePairConversionTargetNotComparable() throws ODataJPAKeyPairException { final JPAAttribute attribute = mock(JPAAttribute.class); - when(attribute.getDbType()).thenAnswer(new Answer>() { - @Override - public Class answer(final InvocationOnMock invocation) throws Throwable { - return NotComparable.class; - } - }); - when(attribute.getConverter()).thenAnswer(new Answer() { - @Override - public NotComparableConverter answer(final InvocationOnMock invocation) throws Throwable { - return new NotComparableConverter(); - } - }); - - when(attribute.getRawConverter()).thenAnswer(new Answer() { - @Override - public NotComparableConverter answer(final InvocationOnMock invocation) throws Throwable { - return new NotComparableConverter(); - } - }); - - cut = new JPAKeyPair(Collections.singletonList(attribute)); + doReturn(NotComparable.class).when(attribute).getDbType(); + doReturn(new NotComparableConverter()).when(attribute).getConverter(); + doReturn(new NotComparableConverter()).when(attribute).getRawConverter(); + + cut = new JPAKeyPair(Collections.singletonList(attribute), directives); final Map key = new HashMap<>(3); key.put(attribute, "Hallo"); cut.setValue(key); assertThrows(ODataJPAKeyPairException.class, () -> cut.setValue(key)); } + @Test + void testCreatePairUuidSortedAsString() { + + final JPAAttribute attributeUUID = mock(JPAAttribute.class); + + cut = new JPAKeyPair(Collections.singletonList(attributeUUID), directives); + + Arrays.asList("400d7044-1e84-4e63-b2d9-0f58f4ca5bd0", "52a4eb6d-ab9d-4bc8-8405-5255d9607441", + "59ce6d1c-0770-48ae-b9ea-47c4ce9994c1", "9768b78c-e010-4e62-bada-8a138be7334d", + "e5406bb9-7166-4c0a-928c-f6deed6325bc").forEach(u -> addUUID(attributeUUID, u)); + + assertTrue(cut.hasUpperBoundary()); + assertEquals("400d7044-1e84-4e63-b2d9-0f58f4ca5bd0", cut.getMinElement(attributeUUID).toString()); + assertEquals("400d7044-1e84-4e63-b2d9-0f58f4ca5bd0", cut.getMin().get(attributeUUID).toString()); + assertEquals("e5406bb9-7166-4c0a-928c-f6deed6325bc", cut.getMaxElement(attributeUUID).toString()); + assertEquals("e5406bb9-7166-4c0a-928c-f6deed6325bc", cut.getMax().get(attributeUUID).toString()); + + } + + @Test + void testCreatePairUuidSortedAsByteArray() { + directives = new JPAODataQueryDirectives.JPAODataQueryDirectivesImpl(0, UuidSortOrder.AS_BYTE_ARRAY); + cut = new JPAKeyPair(keyDef, directives); + final JPAAttribute attributeUUID = mock(JPAAttribute.class); + + cut = new JPAKeyPair(Collections.singletonList(attributeUUID), directives); + + Arrays.asList("400d7044-1e84-4e63-b2d9-0f58f4ca5bd0", "52a4eb6d-ab9d-4bc8-8405-5255d9607441", + "59ce6d1c-0770-48ae-b9ea-47c4ce9994c1", "9768b78c-e010-4e62-bada-8a138be7334d", + "e5406bb9-7166-4c0a-928c-f6deed6325bc").forEach(u -> addUUID(attributeUUID, u)); + + assertTrue(cut.hasUpperBoundary()); + assertEquals("400d7044-1e84-4e63-b2d9-0f58f4ca5bd0", cut.getMinElement(attributeUUID).toString()); + assertEquals("400d7044-1e84-4e63-b2d9-0f58f4ca5bd0", cut.getMin().get(attributeUUID).toString()); + assertEquals("9768b78c-e010-4e62-bada-8a138be7334d", cut.getMaxElement(attributeUUID).toString()); + assertEquals("9768b78c-e010-4e62-bada-8a138be7334d", cut.getMax().get(attributeUUID).toString()); + } + + @Test + void testCreatePairUuidSortedAsUuid() { + directives = new JPAODataQueryDirectives.JPAODataQueryDirectivesImpl(0, UuidSortOrder.AS_JAVA_UUID); + cut = new JPAKeyPair(keyDef, directives); + final JPAAttribute attributeUUID = mock(JPAAttribute.class); + + cut = new JPAKeyPair(Collections.singletonList(attributeUUID), directives); + + Arrays.asList("400d7044-1e84-4e63-b2d9-0f58f4ca5bd0", "52a4eb6d-ab9d-4bc8-8405-5255d9607441", + "59ce6d1c-0770-48ae-b9ea-47c4ce9994c1", "9768b78c-e010-4e62-bada-8a138be7334d", + "e5406bb9-7166-4c0a-928c-f6deed6325bc").forEach(u -> addUUID(attributeUUID, u)); + + assertTrue(cut.hasUpperBoundary()); + assertEquals("9768b78c-e010-4e62-bada-8a138be7334d", cut.getMinElement(attributeUUID).toString()); + assertEquals("9768b78c-e010-4e62-bada-8a138be7334d", cut.getMin().get(attributeUUID).toString()); + assertEquals("59ce6d1c-0770-48ae-b9ea-47c4ce9994c1", cut.getMaxElement(attributeUUID).toString()); + assertEquals("59ce6d1c-0770-48ae-b9ea-47c4ce9994c1", cut.getMax().get(attributeUUID).toString()); + } + private void addUUID(final JPAAttribute attributeUUID, final String uuid) { final UUID id = UUID.fromString(uuid); try { diff --git a/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/query/JPAOrderByBuilderTest.java b/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/query/JPAOrderByBuilderTest.java index 08031855c..210d2e72b 100644 --- a/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/query/JPAOrderByBuilderTest.java +++ b/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/query/JPAOrderByBuilderTest.java @@ -171,11 +171,12 @@ void testOrderByPropertyAndTop() throws ODataException { when(uriResource.getOrderByOption()).thenReturn(orderBy); when(uriResource.getTopOption()).thenReturn(top); - final List act = cut.createOrderByList(joinTables, orderByAttributes, uriInfo); + final List act = cut.createOrderByList(joinTables, orderByAttributes, uriResource); assertFalse(act.isEmpty()); assertEquals(String.class, act.get(0).getExpression().getJavaType()); assertEquals("DivisionCode", act.get(0).getExpression().getAlias()); + assertEquals(4, act.size()); } @Test @@ -201,7 +202,7 @@ void testOrderByPropertyAndPage() throws ODataException { assertFalse(act.isEmpty()); assertEquals(String.class, act.get(0).getExpression().getJavaType()); assertEquals("DivisionCode", act.get(0).getExpression().getAlias()); - assertEquals(5, act.size()); + assertEquals(4, act.size()); } private List createOrderByClause(final Boolean isDescending) { diff --git a/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/util/JPAEntityTypeDouble.java b/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/util/JPAEntityTypeDouble.java index 5c11934dc..aadb5eeba 100644 --- a/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/util/JPAEntityTypeDouble.java +++ b/jpa/odata-jpa-processor/src/test/java/com/sap/olingo/jpa/processor/core/util/JPAEntityTypeDouble.java @@ -11,12 +11,12 @@ import org.apache.olingo.server.api.uri.UriResourceProperty; import com.sap.olingo.jpa.metadata.core.edm.annotation.EdmQueryExtensionProvider; -import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPAEtagValidator; import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPAAssociationAttribute; import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPAAssociationPath; import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPAAttribute; import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPACollectionAttribute; import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPAEntityType; +import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPAEtagValidator; import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPAPath; import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPAProtectionInfo; import com.sap.olingo.jpa.metadata.core.edm.mapper.api.JPAQueryExtension; @@ -41,10 +41,10 @@ public JPAAssociationPath getAssociationPath(final String externalName) throws O return base.getAssociationPath(externalName); } + @SuppressWarnings("unchecked") @Override public List getAssociationPathList() throws ODataJPAModelException { - fail(); - return null; + return (List) failWithNull(); } @Override @@ -104,8 +104,7 @@ public JPAPath getPath(final String externalName) throws ODataJPAModelException @Override public JPAPath getPath(final String externalName, final boolean respectIgnore) throws ODataJPAModelException { - fail(); - return null; + return (JPAPath) failWithNull(); } @Override @@ -120,8 +119,7 @@ public Class getTypeClass() { @Override public boolean isAbstract() { - fail(); - return false; + return failWithFalse(); } @Override @@ -141,26 +139,24 @@ public String getInternalName() { @Override public String getContentType() throws ODataJPAModelException { - fail(); - return null; + return (String) failWithNull(); } @Override public JPAPath getContentTypeAttributePath() throws ODataJPAModelException { - fail(); - return null; + return (JPAPath) failWithNull(); } + @SuppressWarnings("unchecked") @Override public List getKey() throws ODataJPAModelException { - fail(); - return null; + return (List) failWithNull(); } + @SuppressWarnings("unchecked") @Override public List getKeyPath() throws ODataJPAModelException { - fail(); - return null; + return (List) failWithNull(); } @Override @@ -210,14 +206,17 @@ public List getProtections() throws ODataJPAModelException { @Override public JPAPath getEtagPath() throws ODataJPAModelException { - fail(); - return null; + return (JPAPath) failWithNull(); } @Override public boolean hasCompoundKey() { - fail(); - return false; + return failWithFalse(); + } + + @Override + public boolean hasEmbeddedKey() { + return failWithFalse(); } @Override @@ -247,4 +246,14 @@ public JPAEtagValidator getEtagValidator() { fail(); return JPAEtagValidator.WEAK; } + + private Object failWithNull() { + fail(); + return null; + } + + private boolean failWithFalse() { + fail(); + return false; + } } diff --git a/jpa/odata-jpa-test/src/main/resources/META-INF/persistence.xml b/jpa/odata-jpa-test/src/main/resources/META-INF/persistence.xml index 5fec96a33..dda3e0e62 100644 --- a/jpa/odata-jpa-test/src/main/resources/META-INF/persistence.xml +++ b/jpa/odata-jpa-test/src/main/resources/META-INF/persistence.xml @@ -99,13 +99,14 @@ - - + + + @@ -148,9 +149,9 @@ - - - + + + diff --git a/jpa/pom.xml b/jpa/pom.xml index 953e10441..415826997 100644 --- a/jpa/pom.xml +++ b/jpa/pom.xml @@ -19,19 +19,19 @@ 1.7.1 4.3.0 3.8.4 - 10.21.0 + 11.0.0 6.1.0 4.0.1 3.1.0 ${project.version} 6.2.0 - 4.0.3 + 4.0.4 6.4.0.Final 4.0.0 10.16.1.1 5.11.3 1.11.2 - 5.13.0 + 5.14.2 0.8.12 4.1.115.Final 2.0.16