feat: Activity grouping - #2330
Draft
JanCizmar wants to merge 67 commits into
Draft
Conversation
…-core # Conflicts: # backend/data/src/main/resources/db/changelog/schema.xml
The amount of time spent solving made up problems instead of working on real problems...
…-core # Conflicts: # backend/api/src/main/kotlin/io/tolgee/hateoas/InitialDataModel.kt # backend/app/src/main/resources/application.yaml # backend/app/src/test/kotlin/io/tolgee/api/v2/controllers/v2ImageUploadController/V2ImageUploadControllerTest.kt # backend/app/src/test/kotlin/io/tolgee/api/v2/controllers/v2ProjectsController/V2ProjectsControllerTest.kt # backend/app/src/test/kotlin/io/tolgee/api/v2/controllers/v2ScreenshotController/KeyScreenshotControllerTest.kt # backend/app/src/test/kotlin/io/tolgee/initialUserCreation/LegacyMigrationTest.kt # backend/data/src/main/kotlin/io/tolgee/activity/ActivityService.kt # backend/data/src/main/kotlin/io/tolgee/activity/views/ActivityViewByRevisionsProvider.kt # backend/data/src/main/kotlin/io/tolgee/activity/views/ProjectActivityViewByPageableProvider.kt # backend/data/src/main/kotlin/io/tolgee/batch/BatchJobActionService.kt # backend/data/src/main/kotlin/io/tolgee/batch/BatchJobConcurrentLauncher.kt # backend/data/src/main/kotlin/io/tolgee/batch/ProgressManager.kt # backend/data/src/main/kotlin/io/tolgee/batch/events/OnBatchJobStatusUpdated.kt # backend/data/src/main/kotlin/io/tolgee/dialects/postgres/CustomPostgreSQLDialect.kt # backend/data/src/main/kotlin/io/tolgee/dtos/ComputedPermissionDto.kt # backend/data/src/main/kotlin/io/tolgee/dtos/request/key/KeyScreenshotDto.kt # backend/data/src/main/kotlin/io/tolgee/model/DismissedAnnouncement.kt # backend/data/src/main/kotlin/io/tolgee/model/UserAccount.kt # backend/data/src/main/kotlin/io/tolgee/repository/LanguageRepository.kt # backend/data/src/main/kotlin/io/tolgee/repository/UserAccountRepository.kt # backend/data/src/main/kotlin/io/tolgee/service/LanguageService.kt # backend/data/src/main/kotlin/io/tolgee/service/project/ProjectService.kt # backend/data/src/main/kotlin/io/tolgee/service/security/UserAccountService.kt # backend/data/src/main/resources/db/changelog/schema.xml # backend/development/src/main/kotlin/io/tolgee/controllers/internal/e2eData/NotificationsE2eDataController.kt # backend/testing/src/main/kotlin/io/tolgee/fixtures/scopeAssert.kt # backend/testing/src/main/kotlin/io/tolgee/fixtures/statusExpectations.kt
…-core # Conflicts: # backend/api/src/main/kotlin/io/tolgee/api/v2/controllers/InitialDataController.kt # backend/data/src/main/kotlin/io/tolgee/repository/LanguageRepository.kt # backend/data/src/main/resources/db/changelog/schema.xml
…-core # Conflicts: # backend/data/src/main/kotlin/io/tolgee/model/UserAccount.kt
- revert PropertyModification to non-generic (main's PropertyModifications map is built around it; generic form crashed kapt) - keep instance-keyed ModifiedEntitiesType; adapt LanguageStatsListener and QaActivityListener to entity-instance keys - move user-notification cleanup into ProjectHardDeletingService - align permission service/dto signatures with main (suggestLanguageIds, non-null base permission handling, wrapper-returning repository) - rename branch NotificationsTestData to UserNotificationsTestData (main introduced its own NotificationsTestData) - triage new ActivityTypes into the notification listener ignore list - ktlint format
…ifications Main shipped its own in-app notification system (Notification entity, settings, email channel, top-bar popup) while this branch was dormant. Remove the branch's parallel UserNotification/NotificationPreferences stack — entities, services, dispatch/debounce, REST controllers, test data, webapp views and DB changesets. Activity grouping stays.
Contributor
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
- add group types for key soft-delete/restore/hard-delete, character limit, labels, tasks, glossaries, translation memories, suggestions, AI prompts, branches and QA issues - fix copy-pasted source activity types on webhook/content-delivery groups - merge activity-group links into the surviving revision when batch chunk revisions are merged (FK violation broke batch job finalization) - OpenAPI: keep base ProjectActivityModel generic; register per-activity-type schemas as standalone models and type modified entities as arrays - regenerate frontend API schema; fix React 18 children typings in group components Not grouped (intentionally): hidden types (AUTOMATION, TASK_KEY_UPDATE, HARD_DELETE_LANGUAGE), enum values never set in code (ORDER_TRANSLATION, TRANSLATION_MEMORY_COPY_FROM_PROJECT), activities recording no logged entities (TRANSLATION_MEMORY_ENTRY_*/\*_PROJECT*, BRANCH_DELETE)
- key modified-entity map by entity id once allocated (instance only as a fallback) — pure instance keying recorded the same row twice when Hibernate yielded multiple instances in one transaction (e.g. branch merge after entityManager.clear()), breaking inserts with duplicate PKs; set entityId on records created before id allocation so listeners see the real id - revert json-unit to 2.28.0 (main's version) and drop the custom number comparator — 2.38 compares BigDecimal scale strictly, breaking stats tests - add JOOQ to the liquibase runtime classpath (migration check loads entity classes referencing ActivityGroupType matchers) - add project_activity_title translation key used by the ActivityView - ktlint: line lengths, wildcard imports, jakarta @transient in Permission
…s, grouped activity UI - record branch_id on activity groups (split per branch at grouping time) and filter the /groups endpoint by branch like the regular activity feed - record baseLanguageId on activity revisions again (lost when main's ActivityRevisionInitializer replaced setProject) — fixes base/non-base translation matching and base-translation describing - generic group model provider: per-entity-class counts for every group type without a bespoke provider, plus a generic paged group-items endpoint returning raw modified entities - rebuild the grouped activity view: group cards with author, translated group-type labels, count chips and expandable per-type or generic item tables; translated labels for all 77 group types
Entity hashCode is id-based and mutates when the id gets assigned, stranding instance-keyed map entries in stale hash buckets — the same row was then recorded again under its id key, breaking activity inserts with duplicate PKs (project stats, history, language deletion, usage reporting tests). Key such entries by an EntityIdentity wrapper with identity hashCode and migrate them to the id key once the id is known. Also restore main's expected decimal formats in ProjectsControllerTest.
Replace the per-revision ActivityCompact list with the grouped activity cards (infinite scroll over /activity/groups, date separators kept). The date separator now spans all implicit columns instead of the removed 3-column grid. Dashboard e2e spec asserts group cards and expanded create-key items instead of compact rows and the detail dialog.
Buffer comes from the global polyfill on main; importing the 'buffer' package (not a direct dependency) broke avatar rendering in production builds — every avatar crashed into the global error dialog, which covered the page and failed the project-list E2E specs.
The branch made Permission.viewLanguages/translateLanguages/stateChangeLanguages lazy with a query-side id prefetch. Assemblers outside the fetching query hit LazyInitializationException, so listing projects with stats 500'd — the global error dialog then covered the page and broke the project-list E2E specs.
Value matchers built conditions against the whole modifications json instead of the matched property, and compared json = json which postgres has no operator for. Any group type with allowed/denied values (REVIEW, SET_TRANSLATION_STATE, batch clear...) made the whole groups listing fail with a bad-grammar error. Match on the property attribute, compare as jsonb, and treat JSON null as null. Also match soft-deleted languages in DELETE_LANGUAGE — the hard delete happens asynchronously later.
Record how the request was authenticated (UI / project API key / PAT) on each activity revision and aggregate the distinct origins and source activity types per group. The feed now shows an origin chip on groups touched over the API and attributes groups that emerged from a compound action — e.g. translations set while creating a key render as 'Set translations ... as part of: Created key'. Redesign the group cards along the way: semantic type icon (green for creating, red for deleting), avatar, translated label, count chip, relative time with an exact-datetime tooltip.
- IMPORT had no matcher, so its expanded list was always empty — match the keys and translations the import created - resolve the describing relations of generic group items, so a translation row shows its key name and language instead of a bare entity id - render modifications as old → new, formatting entity references by name, timestamps as dates and arrays as lists, instead of dumping field names - fall back to the deleted entity's previous name when it no longer has describing data
The dashboard no longer renders per-revision compact rows, so the specs that asserted them (labels, translation labels, suggestions, outdated) now assert group cards by type and expand them to check the affected items. Drop the unused E2ActivityChecker compound. Also show the entity's own text under the item title, so a suggestion row shows the suggested translation next to the key it belongs to.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this PR does
Groups related consecutive activity revisions into activity groups, so the project activity feed can show "created 50 keys" instead of 50 separate rows, and adds a grouped activity view to the webapp at
/projects/{id}/activity.How grouping works
ActivityGroupTypedeclares whichActivityTypes feed it and a matcher over the modified entities (entity class, revision type, changed props, allowed/denied values). One revision can land in several groups — e.g.COMPLEX_EDITsplits intoEDIT_KEY_NAME+SET_BASE_TRANSLATION+REVIEW.ActivityGroupermatches it against all group types and links it into the currently open group of that type (same project, author, matching string and branch). A group closes after 24 h of age or 2 h of inactivity.ActivityType. Not grouped (intentionally): hidden types (AUTOMATION,TASK_KEY_UPDATE,HARD_DELETE_LANGUAGE), enum values never set in code (ORDER_TRANSLATION,TRANSLATION_MEMORY_COPY_FROM_PROJECT), and activities that record no activity-logged entities (TRANSLATION_MEMORY_ENTRY_*,TRANSLATION_MEMORY_*_PROJECT*,BRANCH_DELETE).API
GET /v2/projects/{id}/activity/groups— paged groups with author, timestamp, type, per-type data (counts or a specialized payload), filterable by type/author/language andbranch(same semantics as the regular activity feed).GET /v2/projects/{id}/activity/group-items/generic/{groupId}— paged raw modified entities of any group.GET /v2/projects/{id}/activity/group-items/create-key/{groupId}— specialized items (key + base translation).ProjectActivity<Type>Model,ActivityGroup<Type>Model) are registered as standalone models; the base models stay generic.Frontend
Grouped activity view with group cards (author, translated group-type label, count chip, timestamp) expandable into per-type content (created keys with base translations, created project languages) or a generic modified-entities table.
Notable internal changes
ModifiedEntitiesTypeis keyed by entity id once allocated (instance only pre-allocation). Pure instance keying recorded the same row twice when Hibernate yielded two instances in one transaction (e.g. branch merge afterentityManager.clear()), causing duplicate-PK inserts.ActivityRevision.baseLanguageIdrecords the base language at change time (used by base/non-base translation matchers and the base-translation describer).activity_modified_entity.additional_description+ActivityAdditionalDescriberextension point (used to store base translation text with key activity).Testing
ActivityGroupsCreationTest— time-window grouping, complex-edit splittingProjectCreationGroupViewTest— group listing payloadsFollow-ups
activity_groups_type_*,project_activity_title, …) to the Tolgee platformbranch)