Repository navigation
Merge-up: Merge v17/dev into main - #24156
iOvergaard wants to merge 3 commits into
Conversation
…composition (closes #24117) (#24126) * Refresh composing content types when a property is added. Adding a property to a content type used as a composition left every type composed of (or inheriting from) it serving a published content type built before the property existed, so the value read as null on the front end and in Preview until the application restarted. ComposeContentTypeChanges only propagated to deriving types for changes in hasPropertyMainImpact, which never included a property addition. The deriving types therefore got no change entry at all, and neither the published content type cache nor the converted content cache was ever cleared for them. Propagation now also runs for a property addition, emitting RefreshOther for the deriving types - enough to clear both caches, without the cmsContentNu rebuild a pure addition does not need. The traversal is also made transitive (GetComposedOfTransitive), so a change reaches types further down an inheritance or composition chain rather than only the direct consumers. Reloading the published caches now clears IPublishedContentTypeCache as well, so "Reload Memory Cache" can recover a site left in this state instead of the rebuilt content being projected through the same stale definitions. * fix(core): propagate an alias change to deriving types, and address review A content type's published content type resolves its composition aliases recursively through its compositions, so renaming a content type left every type deriving from it reporting the old alias - breaking IsComposedOf on the front end and CompositionSchemaIds in the Delivery API - until another refresh or a restart. hasAliasChanged now propagates too; deriving types take RefreshOther, since the stored blob is keyed by property alias rather than content type alias and so needs no rebuild. GetComposedOfTransitive built its reverse edges by rescanning every content type for each type it found, making a base-type save O(types x descendants). It now builds the reverse lookup once, as GetPropertyAliasesReservedByDescendants does, so the traversal is O(types + composition edges). Also: name the target in both ContentCacheRefresher obsolete messages so the migration path is unambiguous when two of them are present, and assert in ReloadPublishedCacheTests that a cache hit re-serves the same instance, so the reference comparison after the reload cannot pass vacuously. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Include a few more test scenarios for chained inheritance * For batched updates: Only load the content types once, and ensure that multiple saves (in)directly affecting the same content type only yield a single change --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: kjac <kja@umbraco.dk>
…r lookup and always apply the minimum response time (#24152) * fix(api): report an unconfigured application URL before the password reset user lookup ApplicationUrlNotConfigured was only produced once the user had been found, so the reset endpoint answered a registered email differently from an unknown one on sites where the application URL could not be resolved. Add IForgotPasswordUriProvider.CanCreateForgotPasswordUriAsync for failures that don't depend on the user, and check it before the lookup. The default implementation succeeds, so existing providers are unaffected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(api): always apply the minimum response time on password reset endpoints The filter checked TimeSpan.Microseconds, which is only the 0-999 µs component of the remaining wait, so the delay was skipped whenever that component happened to be zero. Compare the whole TimeSpan instead, and measure with TimeProvider so the behaviour can be tested deterministically. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Added docs for EnsureMinimumResponseTimeFilter. --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Andy Butland <abutland73@gmail.com>
Resolves #24126 against main: - ComposeContentTypeChanges keeps main's granular change types for the saved content type, and takes #24126's transitive propagation (property additions and alias changes reach every deriving type) in place of the one-level GetComposedOf propagation. - ContentCacheRefresher resolves IPublishedContentTypeCache lazily: a new constructor taking it would be ambiguous for the container next to the obsolete constructor taking both publish status services. - #24126's tests are adapted to 18: no synchronous batch Save, the original constructor, and main's granular flags for the saved content type.
|
Claude finished @iOvergaard's task in 1m 12s —— View job PR ReviewTarget: Merge-up of
Resolution check
No Critical, Important or Suggestion findings, so there are no inline comments. Labels: I added ApprovedThis looks good to be merged as-is, but please do a manual sanity check and testing before merging. (Use a merge commit, as noted in the description.) |
|



Description
Merges
v17/devintomain. It brings two PRs:1.
ContentTypeServiceBase.ComposeContentTypeChangesmainhas reworked change detection into granular change types (PropertyAdded,AliasChanged, …). The resolution keeps that model for the saved content type. It replaces the old one-levelGetComposedOfpropagation with #24126's transitive one (GetDirectReferencingTypes/GetComposedOfTransitive, with the lookup built once per batch):RefreshMainand share the rebuild requirement, as before.RefreshOther, as in Content Types: Refresh composing types when a property is added to a composition (closes #24117) #24126.Question: should deriving types get the granular
PropertyAddedinstead of a plainRefreshOther? Nothing in core checksPropertyAddedspecifically, so for core they behave the same, and I kept #24126's choice.2.
ContentCacheRefresher#24126 adds a constructor taking
IPublishedContentTypeCache. On 18 that constructor has the same length as the obsolete one taking bothIPublishStatusManagementServiceandIDocumentPublishStatusManagementService. Both can be satisfied by the container, so it throws "constructors are ambiguous" at startup. I tried this: 187 integration tests failed.[ActivatorUtilitiesConstructor]doesn't help, because the container resolves the refresher by type.So on
mainthe constructors are unchanged, and the cache is resolved lazily throughStaticServiceProviderthe first time a "refresh all" needs it, with aTODO (V19). Onv19/dev, which has a single constructor, it will be injected normally when this goes up.3. Tests adapted to 18
DocumentHybridCacheDocumentTypeTests.Batch_Save_Reports_A_Single_Change_For_A_Type_Deriving_From_Several_Saved_Typesused the synchronous batchSave, which doesn't exist on 18. It now saves each composition withUpdateAsyncand classifies both in oneComposeContentTypeChangescall, the same pattern as the existing alias-rename test in that file. The assertion is unchanged.ContentCacheRefresherIdKeyMapTestsgoes back tomain's constructor.ContentTypeEditingServiceTests, the saved content type now expectsmain's granular flags (PropertyAdded,PropertyVariationChanged,PropertyRemoved | RawDataUnaffected) instead of v17'sRefreshOther/RefreshMain. The deriving types' expectations are unchanged and pass as written.How to test
All of the following pass:
ContentCacheRefresher*,EnsureMinimumResponseTimeFilterTests,UriProviderTests.PublishedContentTypeCacheTests,ReloadPublishedCacheTests,ContentTypeEditingServiceTests,DocumentHybridCacheDocumentTypeTests,UserServiceCrudTests,ContentTypeServiceTests.Merge with a merge commit (not squash), so
v17/devstays an ancestor ofmain.🤖 Generated with Claude Code