Interface CaldavObjectSyncDAO
- All Superinterfaces:
org.springframework.data.repository.CrudRepository<CaldavObjectSyncEntity,,Long> org.springframework.data.jpa.repository.JpaRepository<CaldavObjectSyncEntity,,Long> org.springframework.data.repository.ListCrudRepository<CaldavObjectSyncEntity,,Long> org.springframework.data.repository.ListPagingAndSortingRepository<CaldavObjectSyncEntity,,Long> org.springframework.data.repository.PagingAndSortingRepository<CaldavObjectSyncEntity,,Long> org.springframework.data.repository.query.QueryByExampleExecutor<CaldavObjectSyncEntity>,org.springframework.data.repository.Repository<CaldavObjectSyncEntity,Long>
Every listing here is paginated without exception: a calendar's object count is the user's, not the developer's, to decide, and a busy shared calendar is exactly the one whose full read would hurt.
-
Method Summary
Modifier and TypeMethodDescriptionlongcountByCalendarSyncId(long calendarSyncId) How many objects a pair maps.longcountByHomeAndOriginAndIcsUid(long serverId, org.exoplatform.caldav.model.SyncOrigin origin, String icsUid, String homePrefix) How many pairs of one origin on one account — whoever holds them — already map this iCalendar UID.longcountByOtherOwnerAndHomeAndOriginAndIcsUid(long userIdentityId, long serverId, org.exoplatform.caldav.model.SyncOrigin origin, String icsUid, String homePrefix) How many pairs of one origin on one account, held by a user other than this one, already map this iCalendar UID.intdeleteByCalendarSyncId(long calendarSyncId) Drops every mapping of a pair.booleanexistsByLocalEventId(long localEventId) Whether an eXo event is already mapped anywhere.org.springframework.data.domain.Page<CaldavObjectSyncEntity> findByCalendarSyncId(long calendarSyncId, org.springframework.data.domain.Pageable pageable) One page of a pair's object mappings.findByCalendarSyncIdAndIcsUid(long calendarSyncId, String icsUid) The mapping for one iCalendar object inside a pair.findByCalendarSyncIdAndLocalEventId(long calendarSyncId, long localEventId) The mapping for one eXo event inside a pair.org.springframework.data.domain.Page<CaldavObjectSyncEntity> findByLocalEventId(long localEventId, org.springframework.data.domain.Pageable pageable) Every mapping of one eXo event, across the collections of every user.findEventIdsByOwnerAndOriginAndIcsUid(long userIdentityId, long serverId, org.exoplatform.caldav.model.SyncOrigin origin, String icsUid) Which eXo events this user's pairs of one origin, on one server, hold that iCalendar UID for.findEventIdsByServerAndOriginAndIcsUid(long serverId, org.exoplatform.caldav.model.SyncOrigin origin, String icsUid) Which eXo events any pair of one origin, on one server, holds that iCalendar UID for — whoever owns the pair.findMappedLocalEventIds(Collection<Long> localEventIds) Which of these eXo events are already mapped.findMappedLocalEventIdsOfUser(long userIdentityId, Collection<Long> localEventIds) Which of these eXo events this user already holds a copy of.Methods inherited from interface org.springframework.data.repository.CrudRepository
count, delete, deleteAll, deleteAll, deleteAllById, deleteById, existsById, findById, saveMethods inherited from interface org.springframework.data.jpa.repository.JpaRepository
deleteAllByIdInBatch, deleteAllInBatch, deleteAllInBatch, deleteInBatch, findAll, findAll, flush, getById, getOne, getReferenceById, saveAllAndFlush, saveAndFlushMethods inherited from interface org.springframework.data.repository.ListCrudRepository
findAll, findAllById, saveAllMethods inherited from interface org.springframework.data.repository.ListPagingAndSortingRepository
findAllMethods inherited from interface org.springframework.data.repository.PagingAndSortingRepository
findAllMethods inherited from interface org.springframework.data.repository.query.QueryByExampleExecutor
count, exists, findAll, findBy, findOne
-
Method Details
-
findByCalendarSyncIdAndIcsUid
The mapping for one iCalendar object inside a pair. The identity lookup the engine performs before every write.- Parameters:
calendarSyncId- the pairicsUid- the iCalendar UID- Returns:
- the mapping, if the object is already known
-
findByCalendarSyncIdAndLocalEventId
Optional<CaldavObjectSyncEntity> findByCalendarSyncIdAndLocalEventId(long calendarSyncId, long localEventId) The mapping for one eXo event inside a pair.- Parameters:
calendarSyncId- the pairlocalEventId- the eXo event- Returns:
- the mapping, if the event has been pushed
-
findByLocalEventId
org.springframework.data.domain.Page<CaldavObjectSyncEntity> findByLocalEventId(long localEventId, org.springframework.data.domain.Pageable pageable) Every mapping of one eXo event, across the collections of every user.The query the propagation of an edit is driven from: the rows it answers are the set of people who already hold a copy of that meeting, which is the only set an edit is allowed to write to. Asked of the mapping table rather than of agenda's attendee list on purpose — an attendee without a copy must not acquire one from an edit, and a meeting destroyed in eXo has no attendee list left to ask.
Paginated like every other listing here: the row count is the number of attendees who connected a calendar account, which is the users' number to decide and not the developer's.
- Parameters:
localEventId- the eXo eventpageable- page and sort; required- Returns:
- one page of mappings
-
findByCalendarSyncId
org.springframework.data.domain.Page<CaldavObjectSyncEntity> findByCalendarSyncId(long calendarSyncId, org.springframework.data.domain.Pageable pageable) One page of a pair's object mappings.- Parameters:
calendarSyncId- the pairpageable- page and sort; required- Returns:
- one page of mappings
-
countByCalendarSyncId
long countByCalendarSyncId(long calendarSyncId) How many objects a pair maps.- Parameters:
calendarSyncId- the pair- Returns:
- the mapping count
-
existsByLocalEventId
boolean existsByLocalEventId(long localEventId) Whether an eXo event is already mapped anywhere. The backfill's idempotence check, asked once per event rather than per pair.- Parameters:
localEventId- the eXo event- Returns:
- true when a mapping exists
-
findMappedLocalEventIds
@Query("SELECT DISTINCT o.localEventId FROM CaldavObjectSyncEntity o WHERE o.localEventId IN :localEventIds") List<Long> findMappedLocalEventIds(@Param("localEventIds") Collection<Long> localEventIds) Which of these eXo events are already mapped. The same question asexistsByLocalEventId(long)asked once for a whole batch. The seeding pass reads a user's upcoming meetings on every sweep and, in the steady state, finds that all of them already have a copy — so asking one event at a time made the cost of doing nothing grow with how much had already been done, which is the wrong direction for work that repeats for ever.- Parameters:
localEventIds- the eXo events to ask about- Returns:
- the identifiers among them that carry a mapping
-
findMappedLocalEventIdsOfUser
@Query("SELECT DISTINCT o.localEventId FROM CaldavObjectSyncEntity o, CaldavCalendarSyncEntity p WHERE o.calendarSyncId = p.id AND p.userIdentityId = :userIdentityId AND o.localEventId IN :localEventIds") List<Long> findMappedLocalEventIdsOfUser(@Param("userIdentityId") long userIdentityId, @Param("localEventIds") Collection<Long> localEventIds) Which of these eXo events this user already holds a copy of. Scoped through the pair to its owner, and that scope is the whole point: a mapping says "somebody has a copy of this event", and every attendee of a meeting needs one of their own. Asking the unscoped question in a per-user pass meant the first attendee to be copied answered for all the others, who were then skipped and never got their copy.- Parameters:
userIdentityId- the user whose copies are asked aboutlocalEventIds- the eXo events to ask about- Returns:
- the identifiers this user already has a copy of
-
countByHomeAndOriginAndIcsUid
@Query("SELECT COUNT(o) FROM CaldavObjectSyncEntity o, CaldavCalendarSyncEntity p WHERE o.calendarSyncId = p.id AND p.serverId = :serverId AND p.origin = :origin AND o.icsUid = :icsUid AND p.remoteHref LIKE :homePrefix ESCAPE \'!\'") long countByHomeAndOriginAndIcsUid(@Param("serverId") long serverId, @Param("origin") org.exoplatform.caldav.model.SyncOrigin origin, @Param("icsUid") String icsUid, @Param("homePrefix") String homePrefix) How many pairs of one origin on one account — whoever holds them — already map this iCalendar UID.The ownership question the inbound half asks before importing an object: is this a copy eXo wrote? A MIRROR pair is an eXo copy by construction, whoever owns it, and the mapping table says so for every user at once — which is what the pair-scoped and then the user-scoped versions of this question could not see, and why two eXo users on one CalDAV account imported each other's copies as genuine remote events every sweep (EXO-90190).
Scoped to the account, not to the server registration. The first widening went one level too far: asked for every pair of the server, it answered "eXo's" about a UID that another user's mirror maps on a different account of the same server. That is reachable — an externally organised meeting keeps the organiser's UID as its remote identity, so once user A moves such an event onto a space calendar, A's mirror maps an externally issued UID; user C, invited by the same organiser and connected with their own account on the same server, then finds their own copy "owned" and never sees the meeting in eXo. Copies live in accounts: every pair this connector holds sits directly under its account's calendar home (the listing is depth one, and eXo mints its own collections there), so the home is the account's identity and the predicate is a prefix on the pair's href. The server predicate stays as the cheap first cut; the prefix is what decides.
No status predicate, on purpose. Disconnecting only pauses a pair and deletes neither its rows nor the copies on the server, so a paused mirror's rows still describe objects that are physically in the account and still eXo's; restricting the question to active pairs would import those as genuine remote events for whoever else reads the account — the defect back, for departed users. The rows go when the copies go, which is the cleanup's job, not this predicate's.
A count rather than the rows, for the reason every read here shares: the caller only ever asks whether the object is ours, and the row it would be handed belongs to another pair — of another user, now — so returning it would invite writing to a mapping this pair does not own.
The prefix is the caller's, wildcards already escaped with
!; the repository only says which character escapes.- Parameters:
serverId- the declared server registrationorigin- which side created the collections that count as owningicsUid- the iCalendar UID looked forhomePrefix- a LIKE pattern for the account's calendar home, ending in/%- Returns:
- how many mappings of that origin under that home carry the UID, zero when none do
-
countByOtherOwnerAndHomeAndOriginAndIcsUid
@Query("SELECT COUNT(o) FROM CaldavObjectSyncEntity o, CaldavCalendarSyncEntity p WHERE o.calendarSyncId = p.id AND p.userIdentityId <> :userIdentityId AND p.serverId = :serverId AND p.origin = :origin AND o.icsUid = :icsUid AND p.remoteHref LIKE :homePrefix ESCAPE \'!\'") long countByOtherOwnerAndHomeAndOriginAndIcsUid(@Param("userIdentityId") long userIdentityId, @Param("serverId") long serverId, @Param("origin") org.exoplatform.caldav.model.SyncOrigin origin, @Param("icsUid") String icsUid, @Param("homePrefix") String homePrefix) How many pairs of one origin on one account, held by a user other than this one, already map this iCalendar UID.The outbound half's lock (EXO-90190). Two pairs on one collection derive the same href from a UID, so a personal-calendar write of an object that another user's mirror maps would overwrite that user's copy in place. The push asks this before the PUT and refuses. Scoped to the account for the reason
countByHomeAndOriginAndIcsUid(long, org.exoplatform.caldav.model.SyncOrigin, java.lang.String, java.lang.String)gives — asked for the whole server, it refused a user's push of their own event into their own collection because another account of the server mirrored the same externally issued UID. A count and never a row, and no status predicate, for the reasons given there too.- Parameters:
userIdentityId- the user about to write, whose own pairs do not countserverId- the declared server registrationorigin- which side created the collections that count as owningicsUid- the iCalendar UID about to be writtenhomePrefix- a LIKE pattern for the account's calendar home, ending in/%, wildcards escaped with!- Returns:
- how many other users' mappings of that origin under that home carry the UID
-
findEventIdsByOwnerAndOriginAndIcsUid
@Query("SELECT DISTINCT o.localEventId FROM CaldavObjectSyncEntity o, CaldavCalendarSyncEntity p WHERE o.calendarSyncId = p.id AND p.userIdentityId = :userIdentityId AND p.serverId = :serverId AND p.origin = :origin AND o.icsUid = :icsUid AND o.localEventId IS NOT NULL") List<Long> findEventIdsByOwnerAndOriginAndIcsUid(@Param("userIdentityId") long userIdentityId, @Param("serverId") long serverId, @Param("origin") org.exoplatform.caldav.model.SyncOrigin origin, @Param("icsUid") String icsUid) Which eXo events this user's pairs of one origin, on one server, hold that iCalendar UID for.The question above answers "did eXo write this object?"; this one answers "and what did it write it for?" (EXO-89807). The inbound half needs the second because the owner's answer arrives on an object it is about to drop as one of eXo's own, and an answer can only be recorded against the event the copy stands for — which lives on a different pair from the one doing the reading, and so is out of reach of every pair-scoped lookup here.
Still scoped to the user, on purpose, while the ownership count is not (EXO-90190). The two questions look alike and must not be made alike. This one feeds the adoption of the owner's answer: the PARTSTAT read off the copy is recorded as the reading user's response to the event it names. Asked for the deployment, it would name the event behind another user's copy on a shared account, and the reading user's pass would record that other user's phone answer as its own. On a foreign copy this answers nothing, no answer is read, and that is right: the copy's owner reads their own answer on their own pass. Anyone widening this "for consistency" is reintroducing exactly that.
The event identifiers and nothing else, deliberately, for the reason the count above records: the caller must not be handed a mapping row belonging to another pair, because a row it holds is a row it can write to. An identifier is a fact it can only read.
- Parameters:
userIdentityId- the user whose pairs are asked aboutserverId- the declared server registrationorigin- which side created the collections that count as owningicsUid- the iCalendar UID looked for- Returns:
- the events those mappings name, empty when the UID is not ours
-
findEventIdsByServerAndOriginAndIcsUid
@Query("SELECT DISTINCT o.localEventId FROM CaldavObjectSyncEntity o, CaldavCalendarSyncEntity p WHERE o.calendarSyncId = p.id AND p.serverId = :serverId AND p.origin = :origin AND o.icsUid = :icsUid AND o.localEventId IS NOT NULL") List<Long> findEventIdsByServerAndOriginAndIcsUid(@Param("serverId") long serverId, @Param("origin") org.exoplatform.caldav.model.SyncOrigin origin, @Param("icsUid") String icsUid) Which eXo events any pair of one origin, on one server, holds that iCalendar UID for — whoever owns the pair.The third question of the family, and the one the other two cannot answer (EXO-90247). A server that performs its own scheduling delivers a copy eXo wrote for the organizer into another account of the same server, keeping its UID. Seen from the receiving account, that object is in nobody else's calendar home and on nobody else's pair, so
countByHomeAndOriginAndIcsUidsays "not ours" andfindEventIdsByOwnerAndOriginAndIcsUidsays "not this user's" — both correctly, and neither names the meeting.Why widening the scope is safe here and is not at
findEventIdsByOwnerAndOriginAndIcsUid(long, long, org.exoplatform.caldav.model.SyncOrigin, java.lang.String). That one feeds the adoption of an answer directly, so a row of another user's is an answer attributed to the wrong person — the EXO-90190 defect, and its javadoc says so. This one answers only which meeting an object stands for. Whether the reading user may answer for that meeting is a separate question asked of agenda afterwards, and whose PARTSTAT is read is decided by matching the reading user's own addresses against the object's attendee lines. Neither of those becomes any looser for this query being asked of the server. The two methods stay separate for exactly that reason: they are not the same question and must not be merged.A UID is a random UUID eXo mints per (series, user), so a match across accounts of one server is the same object and not a coincidence. The event identifiers and nothing else, for the reason the user-scoped sibling records: a row handed out is a row that can be written to; an identifier is a fact that can only be read.
- Parameters:
serverId- the declared server registrationorigin- which side created the collections that count as owningicsUid- the iCalendar UID looked for- Returns:
- the events those mappings name, empty when the UID is not ours
-
deleteByCalendarSyncId
@Modifying @Transactional @Query("DELETE FROM CaldavObjectSyncEntity o WHERE o.calendarSyncId = :calendarSyncId") int deleteByCalendarSyncId(@Param("calendarSyncId") long calendarSyncId) Drops every mapping of a pair. Called when a pair is unbound; the foreign key would cascade on a row delete, but a pair is often kept as a tombstone while its objects are not.- Parameters:
calendarSyncId- the pair- Returns:
- how many mappings were removed
-