dalle3430
02/08/2024, 9:11 AMPrenders
02/08/2024, 9:25 AMContentService
for creating the nodes and populating the data.
https://docs.umbraco.com/umbraco-cms/reference/management/services/contentservice/create-content-programmatically
How much effort you put into 'streamlining the workflow' will really depend on whether this a 'one off' initial import, of if you plan to use this as an import feature going forward.
If it's one hit, then focus on just getting the job done with a disposable API endpoint - ideally in a form where you can easily test.
If this this is an ongoing feature, then maybe consider creating a 'Content App' or similar that can allow the import to be managed via the backoffice.
https://docs.umbraco.com/umbraco-cms/extending/content-appsPrenders
02/08/2024, 9:35 AMProductImportService
that works with the Umbraco IContentService
.
Then try and get some unit tests around the mapping so you can test the given scenarios and possible fail points.Ravi
02/08/2024, 9:41 AMdalle3430
02/08/2024, 9:42 AMRavi
02/08/2024, 9:47 AMPrenders
02/08/2024, 9:53 AMProductImportService
then you can share the core logic across both.
The arguably trickier bit is having your scheduled import time variable. With my Product Owner hat on, I'd be asking if that is essential given that they will have the option for manual importing.
If it is essential, then messaging & background service tools like Hangfire as @Ravi suggested could be useful. Also one called Wolverine.
https://www.hangfire.io/
https://blog.jetbrains.com/dotnet/2023/05/09/dotnet-background-services/
https://wolverine.netlify.app/Prenders
02/08/2024, 9:54 AMdalle3430
02/08/2024, 9:57 AMPrenders
02/08/2024, 9:59 AMSiempreSteve
02/08/2024, 11:11 AMPrenders
02/08/2024, 5:54 PMdalle3430
02/08/2024, 6:20 PMDomitnator
02/08/2024, 8:25 PMdalle3430
02/14/2024, 2:25 PMvar container = _publishedContentQuery.Content(containerNodeId);
Is it then possible to access unpublished nodes in the following way?
var existingProduct = container.Children.FirstOrDefault(x => x.Value<string>("productSKU") == externalProduct.Sku);
What I'm essentially doing is iterating over each externalProduct in externalProducts (which is the result of the products I fetch from my API) and checking if there is a child node of my Products container node that has the same SKU as the externalProduct. In that case, the product already exists.
However, existingProduct is always null. I have a mild suspicion that it might be because the Product nodes are not published, but merely saved.
Is there anyway to perform such a query with unpublished nodes? And if that's not the problem, what do you think it might be?Anders Bjerner
02/14/2024, 4:31 PMIContent
in case you need to update or delete any products.Anders Bjerner
02/14/2024, 4:33 PMdalle3430
02/14/2024, 4:59 PMAnders Bjerner
02/14/2024, 5:07 PMGetPagedChildren
is 0, not 1 (or it's the other way around)Anders Bjerner
02/14/2024, 5:09 PMcsharp
Dictionary<int, IContent> existing = new();
foreach (IContent content in _contentService.GetPagedChildren(parent.Id, 0, int.MaxValue, out long _)) {
if (content.ContentType.Alias != feed.ContentTypeAlias) continue;
int jobId = content.GetValue<int>(settings.IdProperty.Alias);
if (jobId == 0) continue;
existing[jobId] = content;
}
Anders Bjerner
02/14/2024, 5:16 PMIContent
instances. Here I've used existing[jobId] = content;
, so if conflicts are handled somewhat at random.
Ideally there shouldn't be jobs/products/whatever with the same ID/SKU, but by experience, I can tell that this does happen. Mostly because someone messed up the source feed 😁
I'm sometimes using existing.Add(jobId, content);
instead, which will trigger an exception if two content items have the same ID. Then it might be easier to see when something is wrong.dalle3430
02/14/2024, 5:24 PMDomitnator
02/14/2024, 7:23 PMDomitnator
02/14/2024, 7:25 PMcsharp
public void UpdateProductGroup(IPublishedContent root, ExternalProductGroup externalProductGroup, List<ExternalProduct> externalProducts, List<Language> umbracoLanguages)
{
const string docTypeProduct = "Product";
const string docTypeProductGroup = "productGroup";
var productGroup = _contentService.
GetPagedDescendants(root.Id, 0, int.MaxValue, out long totalrecordsRoot)
.FirstOrDefault(s => s.GetValue<string>("productGroupSKU") == externalProductGroup.Code
&& s.ContentType.Alias == docTypeProductGroup);
List<string> languages = umbracoLanguages?.Select(s => s.IsoCode)?.ToList();
if (productGroup != null)
{
foreach (var item in externalProducts)
{
bool shouldUpdate = false;
var productPage = _contentService.
GetPagedChildren(productGroup.Id, 0, int.MaxValue, out long totalrecords)
.FirstOrDefault(s => s.GetValue<string>("productSKU") == item.Code);
IContent productContentPage = null;
if (productPage == null)
{
productContentPage = _contentService.Create(item.Name, productGroup.Id, docTypeProduct);
productContentPage.CreateDate = DateTime.Now;
}
else
{
productContentPage = _contentService.GetById(productPage.Id);
}
Domitnator
02/14/2024, 7:25 PMcsharp
foreach (var lang in languages)
{
if (productContentPage.GetCultureName(lang) != item.Name)
{
productContentPage.SetCultureName(item.Name, lang);
shouldUpdate = true;
}
if (String.IsNullOrEmpty(productContentPage.GetValue<string>("productDescription", culture: lang)) &&
!String.IsNullOrEmpty(item.Type_NL) && (lang.ToLower() == "nl-nl" || lang.ToLower() == "nl-be"))
{
productContentPage.SetValue("productDescription", item.Type_NL, lang);
shouldUpdate = true;
}
}
if (productContentPage.GetValue<string>("productSKU") != item.Code)
{
productContentPage.SetValue("productSKU", item.Code);
shouldUpdate = true;
}
Domitnator
02/14/2024, 7:25 PMcsharp
try
{
// Always save and publish newly pages in all cultures
if (productContentPage.Id == 0)
{
_contentService.SaveAndPublish(productContentPage, raiseEvents: false);
}
else
{
if (shouldUpdate)
{
_contentService.Save(productContentPage, raiseEvents: false);
// For existing ones check if the need to be published
foreach (var lang in languages)
{
if (productContentPage.IsCulturePublished(lang))
{
_contentService.SaveAndPublish(productContentPage, culture: lang, raiseEvents: false);
}
}
}
}
}
catch (Exception ex)
{
_logger?.Error<AddonImporter>(ex, $"An error occured while updating a product-group");
}
}
}
}
Domitnator
02/14/2024, 7:28 PMDomitnator
02/14/2024, 7:29 PMAnders Bjerner
02/14/2024, 7:45 PMcsharp
var productPage = _contentService.
GetPagedChildren(productGroup.Id, 0, 1, out long totalrecords)
.FirstOrDefault(s => s.GetValue<string>("productSKU") == item.Code);
Also, is the limit set to 1
? Does it find the correct product then?Domitnator
02/14/2024, 7:52 PMDomitnator
02/14/2024, 8:04 PMAnders Bjerner
02/14/2024, 8:12 PMAnders Bjerner
02/14/2024, 8:16 PMdalle3430
02/15/2024, 10:44 AMvar containerChildren = _contentService.GetPagedChildren(containerNodeId, 0, int.MaxValue, out long _);
And then when iterating over each externalProduct from the API call, I manage to find existing products like this:
var existingProduct = containerChildren.FirstOrDefault(x => x.GetValue<string>("productSKU") == externalProduct.Sku);
Anders Bjerner
02/15/2024, 11:06 AMdalle3430
02/15/2024, 1:39 PMA hub and casual space for you to interact with fellow community members and learn more about Umbraco!
Powered by