Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MappingMongoJsonSchemaCreator does not resolve embedded documents subclasses/interfaces implementations attributes #3870

Closed
Pastissad opened this issue Oct 29, 2021 · 1 comment
Labels
type: enhancement A general enhancement

Comments

@Pastissad
Copy link

Pastissad commented Oct 29, 2021

I am trying to setup automatic Client-Side Field Level Encryption via a generated Json Schema using the new@Encrypted annotation.
Doc. reference : https://docs.spring.io/spring-data/mongodb/docs/3.3.x/reference/html/#mongo.jsonSchema
Example 90. Client-Side Field Level Encryption via Json Schema

I stumbled into a severe limitation that impacts the schema generation through MappingMongoJsonSchemaCreator : embedded documents using subclasses or interfaces implementations have their attributes partially ignored.

Here's a test that highlights the issue :

@Test
public void test_embedded_subclasses_ignored() {
    final MongoJsonSchema schema = MongoJsonSchemaCreator.create().createSchemaFor(Doc.class);
    final Document schemaDocument = schema.schemaDocument();
    final Document embeddedDocDocument = schemaDocument.get("properties", Document.class).get("embeddedDoc", Document.class)
                                                       .get("properties", Document.class);
    final Document embeddedDocInterfaceDocument = schemaDocument.get("properties", Document.class).get("embeddedDocInterface", Document.class)
                                                       .get("properties", Document.class);
    assertAll(() -> assertThat(embeddedDocDocument).isNotNull().containsKeys("a", "childBAttr", "childCAttr"),
              () -> assertThat(embeddedDocInterfaceDocument).isNotNull().containsKeys("interfaceAttr", "interfaceAttrB"));
}

@Data
static class Doc {
    String attr1;
    A embeddedDoc;
    AnInterface embeddedDocInterface;
}

interface AnInterface {
    String getInterfaceAttr();
}

@Data
static class InterfaceImpl implements AnInterface {
    @Encrypted
    String interfaceAttr;
    @Encrypted
    String interfaceAttrB;
}

@Data
static class B extends A {
    @Encrypted
    String childBAttr;
}

@Data
static class C extends A {
    @Encrypted
    String childCAttr;
}

@Data
static abstract class A {
    @Encrypted
    String a;
}

The resulting schema contains definitions for attributes attr1, embeddedDoc.a and embeddedDocInterface.interfaceAttr only.
I'd expect the schema to contain all a, childBAttr and childCAttr properties defined for 'embeddedDoc', and interfaceAttr + interfaceAttrB for the 'embeddedDocInterface'.

The issue originates here as it will only consider the base class or interface to resolve attributes :
MappingMongoJsonSchemaCreator#computePropertiesForEntity (L129)

...
List<JsonSchemaProperty> schemaProperties = new ArrayList<>();
for (MongoPersistentProperty nested : nestedProperties) {
...

Here's a potential solution I'm using in the meantime with a custom version of the class, getSubClasses() being any implementation for extracting the subclasses of a given class or interface :

...
List<JsonSchemaProperty> schemaProperties = new ArrayList<>();

final Set<? extends MongoPersistentEntity<?>> subclassesEntities = getSubClasses(entity.getType())
                                                                                      .stream().map(mappingContext::getRequiredPersistentEntity)
                                                                                      .collect(Collectors.toSet());

final Set<MongoPersistentProperty> nestedProperties =
      subclassesEntities.isEmpty() ? new HashSet<>(IteratorUtils.toList(entity.iterator())) :
      subclassesEntities.stream().flatMap(subClassEntity -> IteratorUtils.toList(subClassEntity.iterator()).stream())
                        .collect(Collectors.toSet()); 

for (MongoPersistentProperty nested : nestedProperties) {
...
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Oct 29, 2021
@christophstrobl
Copy link
Member

Thanks for reporting. The current behavior is intended because the SchemaCreator only operates upon the declared types. Nevertheless, we understand the issue and will investigate potential solutions.

@christophstrobl christophstrobl added type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged labels Nov 2, 2021
@christophstrobl christophstrobl linked a pull request Mar 7, 2022 that will close this issue
@mp911de mp911de changed the title MappingMongoJsonSchemaCreator does not resolve embedded documents subclasses/interfaces implementations attributes MappingMongoJsonSchemaCreator does not resolve embedded documents subclasses/interfaces implementations attributes Mar 18, 2022
@mp911de mp911de added this to the 3.4 M4 (2021.2.0) milestone Mar 18, 2022
mp911de added a commit that referenced this issue Mar 18, 2022
Refine API naming towards merge/property instead of combine/specify. Tweak documentation. Introduce Resolution.ofValue(…) for easier creation.

See #3870
Original pull request: #3986.
mp911de pushed a commit that referenced this issue Mar 18, 2022
This commit introduces MergedJsonSchema and MergedJsonSchemaProperty that can be used to merge properties of multiple objects into one as long as the additions do not conflict with another (eg. due to usage of different types).
To resolve previously mentioned errors it is required to provide a ConflictResolutionFunction.

Closes #3870
Original pull request: #3986.
mp911de added a commit that referenced this issue Mar 18, 2022
Refine API naming towards merge/property instead of combine/specify. Tweak documentation. Introduce Resolution.ofValue(…) for easier creation.

See #3870
Original pull request: #3986.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement A general enhancement
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants