Skip to content

Conversation

@dennisvang
Copy link
Contributor

@dennisvang dennisvang commented Dec 2, 2025

Possible implementation of factory reset using repository populator.

The basic idea is that we remove the relevant entries from the fixture history, update the list of resources accordingly, and then run the populator again.

TODO:

  • remove old reset files from src/main/resources/org/fairdatapoint/service/reset

@dennisvang dennisvang marked this pull request as ready for review December 8, 2025 14:57
Copy link
Contributor

@MarekSuchanek MarekSuchanek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏🏻 I like this approach... it should be then in docs that resetting works only with those "default" fixture files/resources (so it is clear that in some use cases you might want to override those and in some create separate files).

As a side effect, reset might apply new fixture resources that were not there previously 🤔 but I guess that does not matter as it would happen upon any restart anyway, right?


private void clearResourceDefinitions() {
log.debug("Clearing resource definitions");
resourceDefinitionChildMetadataRepository.deleteAll();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these added to make sure nothing is left out? Otherwise, cascading delete should work I believe...

Copy link
Contributor Author

@dennisvang dennisvang Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @MarekSuchanek thanks for the review! :)

You're right, the deleteAll() calls for metadataSchemaUsageRepository, resourceDefinitionChildMetadataRepository, andresourceDefinitionLinkRepository are redundant.
However, line 188 (resourceDefinitionChildRepository.deleteAll()) does seem to be required, for some reason.

Still, I like to be explicit here, and not rely on cascades.

Note

In my experience cascade deletes, in general, can lead to very nasty surprises.
One example of cascade deletes causing confusion is in #802, where it turned out that overriding the default Einstein user account in a test fixture caused the related objects to be deleted as well.

@dennisvang
Copy link
Contributor Author

dennisvang commented Dec 10, 2025

👏🏻 I like this approach... it should be then in docs that resetting works only with those "default" fixture files/resources (so it is clear that in some use cases you might want to override those and in some create separate files).

As a side effect, reset might apply new fixture resources that were not there previously 🤔 but I guess that does not matter as it would happen upon any restart anyway, right?

Hi @MarekSuchanek , yes you're right that the current approach only works with the default fixture files, and that could lead to problems later on... 🤔

One of the challenges is that we cannot know how people are going to name their fixture override files and which entities they will contain.

I do see a few options to mitigate that:

  1. As you mentioned above, we could simply document the limitation and leave it at that, or
  2. we could remove the fine-grained reset functionality and always clear everything, in which case we can simply clear the entire fixture history repository, or
  3. we could prescribe subfolders in the fixtures dir, corresponding to the ResetService.clear*() methods, and clear all files from those folders, or
  4. we could prescribe part of the fixture filename and use that to remove groups of files from the fixture history (this is explained in the post below).

To expand upon option 3., we could distribute our default fixtures over the following required subfolders, where the subfolder names match the names of the relevant org.fairdatapoint.entity subfolders:

  • fixtures/apikey (corresponding to clearApiKeys)
  • fixtures/membership (corresponding to clearMemberships)
  • fixtures/user (corresponding to clearUsers)
  • fixtures/search (corresponding to clearUsers)
  • fixtures/schema (corresponding to clearMetadataSchemasAndResourceDefinitions)
  • fixtures/resource (corresponding to clearMetadataSchemasAndResourceDefinitions)

and then adapt ResetService.removeFromFixtureHistory() and fixtureHistoryRepository to remove records by subfolder name instead of filename.

What do you think?

@dennisvang
Copy link
Contributor Author

dennisvang commented Dec 11, 2025

To expand upon option 4.:

It may be easier to introduce a naming convention for fixtures files, as in

<zero-padded-number>_<package-name>_<custom-description>.json

or as regex

^(?<order>[0-9]{4})_(?<package>[a-z]+)_(?<description>[a-zA-Z0-9\-]+)\.json$

where <package> matches the name of the relevant org.fairdatapoint.entity subfolders:

  • apikey
  • membership
  • user
  • schema
  • search
  • resource

with the restriction that the fixtures in each file must belong to the specified package.

EDIT: this idea is worked out in the following commits

…n pattern

matching a regex like ^(?<order>[0-9]{4})_(?<package>[a-z]+)_(?<description>[a-zA-Z0-9\-]+)\.json$
and remove the explicit filename tests, because this is now implicitly done during population before each test
@MarekSuchanek
Copy link
Contributor

Indeed the option 4 looks the best, although it still needs to be documented and naturally expects that the name will correspond with contents. But we should expect people adding/overriding the fixtures to be knowledgeable about such things. It is a great solution.

Wrt the TODOs in comments, I agree with moving the methods to BootstrapService (but probably with renaming reset to something like populate).

@dennisvang
Copy link
Contributor Author

dennisvang commented Dec 18, 2025

Not done yet:

Running a fresh FDP with development profile, if we reset metadata and resource definitions at the same time (or all at the same time):

image

we get the following error:

...
2025-12-18 15:31:47,909 86794 [http-nio-8080-exec-1] INFO  org.fairdatapoint.service.reset.ResetService - Resetting to factory defaults
2025-12-18 15:31:47,909 86794 [http-nio-8080-exec-1] DEBUG org.fairdatapoint.service.reset.ResetService - Clearing membership permissions
2025-12-18 15:31:47,927 86812 [http-nio-8080-exec-1] DEBUG org.fairdatapoint.service.reset.ResetService - Clearing memberships
2025-12-18 15:31:47,952 86837 [http-nio-8080-exec-1] DEBUG org.fairdatapoint.service.reset.ResetService - Clearing ACL cache
2025-12-18 15:31:47,952 86837 [http-nio-8080-exec-1] DEBUG org.fairdatapoint.service.reset.ResetService - Clearing metadata
2025-12-18 15:31:48,015 86900 [http-nio-8080-exec-1] DEBUG org.fairdatapoint.service.reset.ResetService - Creating default metadata
2025-12-18 15:31:48,017 86902 [http-nio-8080-exec-1] DEBUG org.fairdatapoint.service.reset.ResetService - Clearing metadata schemas and resource definitions
2025-12-18 15:31:48,113 86998 [http-nio-8080-exec-1] WARN  org.hibernate.engine.jdbc.spi.SqlExceptionHelper - SQL Error: 0, SQLState: 23502
2025-12-18 15:31:48,114 86999 [http-nio-8080-exec-1] ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper - ERROR: null value in column "target_resource_definition_id" of relation "resource_definition_child" violates not-null constraint
  Detail: Failing row contains (b8648597-8fbd-4b89-9e30-5eab82675e42, 77aaad6a-0136-4c6e-88b9-07ffccd0ee4c, null, https://w3id.org/fdp/fdp-o#metadataCatalog, Catalogs, http://www.w3.org/ns/dcat#themeTaxonomy, 0, 2025-12-18 15:30:27.037585+01, 2025-12-18 15:31:48.100645+01).
2025-12-18 15:31:48,124 87009 [http-nio-8080-exec-1] DEBUG org.springframework.web.servlet.DispatcherServlet - Failed to complete request: org.springframework.dao.DataIntegrityViolationException: could not execute statement [ERROR: null value in column "target_resource_definition_id" of relation "resource_definition_child" violates not-null constraint
  Detail: Failing row contains (b8648597-8fbd-4b89-9e30-5eab82675e42, 77aaad6a-0136-4c6e-88b9-07ffccd0ee4c, null, https://w3id.org/fdp/fdp-o#metadataCatalog, Catalogs, http://www.w3.org/ns/dcat#themeTaxonomy, 0, 2025-12-18 15:30:27.037585+01, 2025-12-18 15:31:48.100645+01).] [update resource_definition_child set order_priority=?,relation_uri=?,source_resource_definition_id=?,tags_uri=?,target_resource_definition_id=?,title=?,updated_at=? where uuid=?]; SQL [update resource_definition_child set order_priority=?,relation_uri=?,source_resource_definition_id=?,tags_uri=?,target_resource_definition_id=?,title=?,updated_at=? where uuid=?]; constraint [target_resource_definition_id" of relation "resource_definition_child]
2025-12-18 15:31:48,126 87011 [http-nio-8080-exec-1] ERROR org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/].[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.dao.DataIntegrityViolationException: could not execute statement [ERROR: null value in column "target_resource_definition_id" of relation "resource_definition_child" violates not-null constraint
  Detail: Failing row contains (b8648597-8fbd-4b89-9e30-5eab82675e42, 77aaad6a-0136-4c6e-88b9-07ffccd0ee4c, null, https://w3id.org/fdp/fdp-o#metadataCatalog, Catalogs, http://www.w3.org/ns/dcat#themeTaxonomy, 0, 2025-12-18 15:30:27.037585+01, 2025-12-18 15:31:48.100645+01).] [update resource_definition_child set order_priority=?,relation_uri=?,source_resource_definition_id=?,tags_uri=?,target_resource_definition_id=?,title=?,updated_at=? where uuid=?]; SQL [update resource_definition_child set order_priority=?,relation_uri=?,source_resource_definition_id=?,tags_uri=?,target_resource_definition_id=?,title=?,updated_at=? where uuid=?]; constraint [target_resource_definition_id" of relation "resource_definition_child]] with root cause
org.postgresql.util.PSQLException: ERROR: null value in column "target_resource_definition_id" of relation "resource_definition_child" violates not-null constraint
  Detail: Failing row contains (b8648597-8fbd-4b89-9e30-5eab82675e42, 77aaad6a-0136-4c6e-88b9-07ffccd0ee4c, null, https://w3id.org/fdp/fdp-o#metadataCatalog, Catalogs, http://www.w3.org/ns/dcat#themeTaxonomy, 0, 2025-12-18 15:30:27.037585+01, 2025-12-18 15:31:48.100645+01).
...

However, the issue is not reproduced by testResetToFactoryDefaultsAll(), not even if we exclude the test-fixtures and if we don't clear the repositories at the start of the test. EDIT: I forgot that BaseIntegrationTest is not transactional, contrary to DataJpaTest. The issue is reproduced if we annotate the test with @transactional.

Note that reset does work if we don't clear the metadata, but clearMetadata() does not even modify the relational db, only the triple store...

This reproduces the DataIntegrityViolationException 'null value in column target_resource_definition_id of relation resource_definition_child violates not-null constraint'
@dennisvang
Copy link
Contributor Author

DispatcherServlet - Failed to complete request: org.springframework.dao.InvalidDataAccessApiUsageException: 
  org.hibernate.TransientObjectException: 
    persistent instance references an unsaved transient instance of 'org.fairdatapoint.entity.resource.ResourceDefinition'
    (save the transient instance before flushing)

coming from

membershipRepository.findByUuid(KnownUUIDs.MEMBERSHIP_OWNER_UUID)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants