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

Improve document schedule #17535

Open
wants to merge 17 commits into
base: v15/dev
Choose a base branch
from
Open

Conversation

Migaroez
Copy link
Contributor

@Migaroez Migaroez commented Nov 14, 2024

Description

The Management API currently supports publishing OR scheduling of documents, but there is no way to cancel schedules or clear them.

This PR aims to rectify this shortcoming while still maintaining the API model/endpoint and the spirit of the endpoint.

Details

With this PR, it should be possible to

  • publish one or more cultures
  • schedule (publish and or unpublish) one or more cultures
  • update the schedule of one more cultures
  • clear the schedule for a given culture or cultures
  • combine any of the 4 actions above across multiple cultures (each action can not be combined within one culture)
  • read the schedules per culture when requesting the document

Notes

A few things came up while making the necessary changes, please keep these in mind when reviewing/testing this PR

  • The invariant culture string was mixed between "" and "*", where it makes sense (and there was no sign it would cause any issues) this has been aligned to "*" and placed in a constant for clarity
  • Regarding full truth vs partial truth: It is very hard, maybe even impossible to combine full context truth (document) vs sub context truth (per culture) in one model. To avoid breaking the endpoint signature and with the idea of allowing editors to only send the data they are working on (one or more cultures) to be updated in the future, this endpoint supports just that.

Testing

You can look for inspiration in the added integration tests, but please try to think outside the box.

@Migaroez Migaroez marked this pull request as ready for review November 18, 2024 13:15
@Migaroez Migaroez requested a review from kjac November 18, 2024 13:15
@@ -53,7 +71,9 @@ public async Task<IActionResult> ByKey(CancellationToken cancellationToken, Guid
return DocumentNotFound();
}

DocumentResponseModel model = await _documentPresentationFactory.CreateResponseModelAsync(content);
ContentScheduleCollection schedule = _contentService.GetContentScheduleByContentId(content.Id);
Copy link
Member

Choose a reason for hiding this comment

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

It's not optimal to call two services from the controller. I think we should make it a single service call and move the logic to the service layer. Furthermore, when using two services, we should wrap it in a single scope, to avoid funny things that can happen outside of a transaction

src/Umbraco.Core/Services/ContentService.cs Show resolved Hide resolved
@Migaroez Migaroez changed the title V15/task/improve document schedule Improve document schedule Nov 26, 2024
Comment on lines +47 to +90
async Task<Attempt<ContentPublishingResult, ContentPublishingOperationStatus>> PublishAsync(
Guid key,
ICollection<CulturePublishScheduleModel> culturesToPublishOrSchedule,
Guid userKey)
{
// todo remove default implementation when superseded method is removed in v17+
var culturesToPublishImmediately =
culturesToPublishOrSchedule.Where(culture => culture.Schedule is null).Select(c => c.Culture ?? Constants.System.InvariantCulture).ToHashSet();

ContentScheduleCollection schedules = StaticServiceProvider.Instance.GetRequiredService<IContentService>().GetContentScheduleByContentId(key);

foreach (CulturePublishScheduleModel cultureToSchedule in culturesToPublishOrSchedule.Where(c => c.Schedule is not null))
{
var culture = cultureToSchedule.Culture ?? Constants.System.InvariantCulture;

if (cultureToSchedule.Schedule!.PublishDate is null)
{
schedules.RemoveIfExists(culture, ContentScheduleAction.Release);
}
else
{
schedules.AddOrUpdate(culture, cultureToSchedule.Schedule!.PublishDate.Value.UtcDateTime,ContentScheduleAction.Release);
}

if (cultureToSchedule.Schedule!.UnpublishDate is null)
{
schedules.RemoveIfExists(culture, ContentScheduleAction.Expire);
}
else
{
schedules.AddOrUpdate(culture, cultureToSchedule.Schedule!.UnpublishDate.Value.UtcDateTime, ContentScheduleAction.Expire);
}
}

var cultureAndSchedule = new CultureAndScheduleModel
{
CulturesToPublishImmediately = culturesToPublishImmediately,
Schedules = schedules,
};

#pragma warning disable CS0618 // Type or member is obsolete
return await PublishAsync(key, cultureAndSchedule, userKey);
#pragma warning restore CS0618 // Type or member is obsolete
}
Copy link
Member

Choose a reason for hiding this comment

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

Would it be better to forward to the actual implementation?

Suggested change
async Task<Attempt<ContentPublishingResult, ContentPublishingOperationStatus>> PublishAsync(
Guid key,
ICollection<CulturePublishScheduleModel> culturesToPublishOrSchedule,
Guid userKey)
{
// todo remove default implementation when superseded method is removed in v17+
var culturesToPublishImmediately =
culturesToPublishOrSchedule.Where(culture => culture.Schedule is null).Select(c => c.Culture ?? Constants.System.InvariantCulture).ToHashSet();
ContentScheduleCollection schedules = StaticServiceProvider.Instance.GetRequiredService<IContentService>().GetContentScheduleByContentId(key);
foreach (CulturePublishScheduleModel cultureToSchedule in culturesToPublishOrSchedule.Where(c => c.Schedule is not null))
{
var culture = cultureToSchedule.Culture ?? Constants.System.InvariantCulture;
if (cultureToSchedule.Schedule!.PublishDate is null)
{
schedules.RemoveIfExists(culture, ContentScheduleAction.Release);
}
else
{
schedules.AddOrUpdate(culture, cultureToSchedule.Schedule!.PublishDate.Value.UtcDateTime,ContentScheduleAction.Release);
}
if (cultureToSchedule.Schedule!.UnpublishDate is null)
{
schedules.RemoveIfExists(culture, ContentScheduleAction.Expire);
}
else
{
schedules.AddOrUpdate(culture, cultureToSchedule.Schedule!.UnpublishDate.Value.UtcDateTime, ContentScheduleAction.Expire);
}
}
var cultureAndSchedule = new CultureAndScheduleModel
{
CulturesToPublishImmediately = culturesToPublishImmediately,
Schedules = schedules,
};
#pragma warning disable CS0618 // Type or member is obsolete
return await PublishAsync(key, cultureAndSchedule, userKey);
#pragma warning restore CS0618 // Type or member is obsolete
}
Task<Attempt<ContentPublishingResult, ContentPublishingOperationStatus>> PublishAsync(
Guid key,
ICollection<CulturePublishScheduleModel> culturesToPublishOrSchedule,
Guid userKey) => StaticServiceProvider.Instance.GetRequiredService<ContentPublishingService>()
.PublishAsync(key, culturesToPublishOrSchedule, userKey);

Comment on lines +529 to +539
ContentScheduleCollection GetContentScheduleByContentId(Guid contentId)
{
// Todo clean up default implementation in v17
Attempt<int> idAttempt = StaticServiceProvider.Instance.GetRequiredService<IIdKeyMap>()
.GetIdForKey(contentId, UmbracoObjectTypes.Document);
if (idAttempt.Success is false)
{
throw new ArgumentException("Invalid contentId");
}
return GetContentScheduleByContentId(idAttempt.Result);
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
ContentScheduleCollection GetContentScheduleByContentId(Guid contentId)
{
// Todo clean up default implementation in v17
Attempt<int> idAttempt = StaticServiceProvider.Instance.GetRequiredService<IIdKeyMap>()
.GetIdForKey(contentId, UmbracoObjectTypes.Document);
if (idAttempt.Success is false)
{
throw new ArgumentException("Invalid contentId");
}
return GetContentScheduleByContentId(idAttempt.Result);
}
ContentScheduleCollection GetContentScheduleByContentId(Guid contentId) => StaticServiceProvider.Instance
.GetRequiredService<ContentService>().GetContentScheduleByContentId(contentId);

Maybe?

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.

2 participants