Skip to content
This repository has been archived by the owner on Dec 13, 2021. It is now read-only.

Adding in Base Processor - exposing Umbraco context services #204

Merged
merged 14 commits into from
Feb 10, 2017

Conversation

jamiepollock
Copy link

As discussed on issue #200. I've done the work to add UmbracoContext and ApplicationContext in the base DittoProcessorAttribute. I used the singleton UmbracoContext.Current and ApplicationContext.Current as mentioned here accessing ApplicationContext through UmbracoContext is something you'd want to avoid doing.

I've also reworked the DittoProcessor methods which called UmbracoContext.Current to use the new Umbraco property.

There are a couple of internal statics I didn't touch upon though:

  1. https://github.com/leekelleher/umbraco-ditto/blob/8387c47bc5f0a2c01ed5b3299506f91c28bd4234/src/Our.Umbraco.Ditto/ComponentModel/Processors/UmbracoDictionaryAttribute.cs#L61-L76
  2. https://github.com/leekelleher/umbraco-ditto/blob/8387c47bc5f0a2c01ed5b3299506f91c28bd4234/src/Our.Umbraco.Ditto/ComponentModel/Processors/UmbracoPickerAttribute.cs#L142-L154

Let me know what you think!

@jamiepollock
Copy link
Author

I realise there's going to need be a lot more work done in order to get this to work with the tests as UmbracoContext.Current & ApplicationContext.Current are both null. :/

Based on the idea of https://github.com/umbraco/Umbraco-CMS/blob/release-7.3.2/src/Umbraco.Web/IUmbracoContextAccessor.cs & https://github.com/umbraco/Umbraco-CMS/blob/release-7.3.2/src/Umbraco.Web/SingletonUmbracoContextAccessor.cs

I've implemented an "UmbracoApplicationContextAccessor which allows readonly access to the UmbracoContext & ApplicationContext. In most cases this will use the SingletonUmbracoApplicationContextAccessor but in the case of tests there is a publicly available static register method to setup an alternative which used a mocked version of this accessor.

Not happy about the accessor being passed through so many properties but it's the best way I can think of to ensure we're using exactly the same contexts from method to method.
@jamiepollock
Copy link
Author

Hey folks, struggling with this one. I've been back and forth with how this setup but in the end I took an idea from the core on UmbracoContext access (see notes here: bd4c79a).

Happy to rename things, the structure may not be how you guys would see the files being placed and the naming is a tad verbose. This was a bit of a brain racker!

@jamiepollock
Copy link
Author

jamiepollock commented Jan 22, 2017

I've managed to remove the static membershiphelper but removing the static dictionary helper might be a bit more challenging. I've left it in for now and it can be reviewed later on.

@leekelleher
Copy link
Collaborator

Hey @jamiepollock, thanks for taking it on and making a start with this! 🎉

I'm not sure what to make of the IUmbracoApplicationContextAccessor yet, (it's the first time I've heard about IUmbracoContextAccessor in Umbraco core too). I'll give it some thought.

@jamiepollock
Copy link
Author

jamiepollock commented Jan 22, 2017 via email

@mattbrailsford
Copy link
Collaborator

This is one of the biggest drawbacks to the "Context" pattern imo. Testing umbraco v5, which was uber context heavy, was a real pain as you end up spending ages mocking the rabbit hole.

RE the passing in attribute, you could make an internal accessor as I think we make internals visible to the testing library so it should be allowed to be set from the testing lib.

@jamiepollock
Copy link
Author

@mattbrailsford yea, I've not found it so bad. The goal with the accessor was to eliminate the need for any references to UmbracoContext in production code, as if I did put it in then it would cause tests to fail. Hence in the tests we can just setup a mocked up version and be done with it. In theory we could pass in an entirely different UmbracoContext & ApplicationContext constructed setup, somehow. not sure how that'd work mind or if it even should.

On top of this, the parameter nesting of passing the accessor result comes through comes about because we're using extension methods rather than a class we can DI something into.

I might be missing something though. I've tried going down the route of adding in internal properties this weekend and couldn't find the best place to populate those values in the production code which then wouldn't cause tests to fail. Hence coming back around to the accessor.

More than happy to take on edits, I might be close to the trees to see that wood. :)

Copy link
Collaborator

@mattbrailsford mattbrailsford left a comment

Choose a reason for hiding this comment

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

Just a few comments

public static void RegisterUmbracoApplicationContextAccessor<TUmbracoApplicationContextAccessorType>()
where TUmbracoApplicationContextAccessorType : IUmbracoApplicationContextAccessor, new()
{
DittoProcessorRegistry.Instance.RegisterDefaultUmbracoApplicationContextAccessorType<TUmbracoApplicationContextAccessorType>();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not sure if DittoProcessorRegistery is the right place for this?

Copy link
Author

Choose a reason for hiding this comment

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

It could be moved to Ditto then, I guess, along with the private field?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Would seem logical, unless we want a DittoUmbracoApplicationContextAccessorRegistry? @leekelleher?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Actually, given there can be only 1 type, I'm wondering if this shouldn't just be a static variable on Ditto like ProcessorAttributeTargets and DefaultCacheBy etc. The Register methods are only really relevant if you have multiple types to register.

Copy link
Collaborator

Choose a reason for hiding this comment

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

doh! looks like we do have RegisterDefaultProcessorType though, hmmm. Lets keep it how you've got it, but just move it to Ditto including the private property. :)

Copy link
Author

Choose a reason for hiding this comment

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

Okay. I'll get on that a bit later. :)

Thanks for the feedback, @mattbrailsford!

/// </summary>
protected MembershipHelper Members
{
get { return new MembershipHelper(Umbraco); }
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm wondering if these should be stored on the processor context instead, and then these could maybe be convenience accessors?

Copy link
Author

Choose a reason for hiding this comment

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

Sure, so it'd be Context.Members, etc?

Copy link
Collaborator

Choose a reason for hiding this comment

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

You could keep these accessors, but maybe the main exposure point is the ProcessorContext, so you could do Context.Members or just Members (which internally just calls Context.Members). I dunno if this is a good or bad idea, so open to discussion? @leekelleher?

@@ -31,6 +31,11 @@ internal class DittoProcessorRegistry
private Type defaultProcessorType = typeof(UmbracoPropertyAttribute);

/// <summary>
/// The default umbraco application context accessor type for processors, (defaults to `SingletonUmbracoApplicationContextAccessor`).
/// </summary>
private Type defaultUmbracoApplicationContextAccessorType = typeof(SingletonUmbracoApplicationContextAccessor);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Thinking this should maybe go directly on Ditto?

Copy link
Author

Choose a reason for hiding this comment

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

Yea, I was wondering this too. Happy for it to be moved. :)

@jamiepollock
Copy link
Author

@mattbrailsford I've looked at implementing the code review changes. Only one I've not implemented is helpers into the Context. I wasn't quite sure how to achieve that at this point or if we're going that way.

@mattbrailsford
Copy link
Collaborator

@jamiepollock no worries. I'm not 100% on that suggestion so happy for it to stay as is and we can always move it later if needs be. It wouldn't change the API on the processors so won't be a big deal to move if we need to.

@mattbrailsford
Copy link
Collaborator

Sorry, one last thing. I think I'd drop the use of the word "Default" for the UmbracoApplicationAccessor stiff as there can be only one anyway, so it's not like it's a default of a collection (like the Processor one is). Do you think?

@jamiepollock
Copy link
Author

jamiepollock commented Jan 24, 2017 via email

@jamiepollock
Copy link
Author

@mattbrailsford sorted!

Removed the static class, UmbracoDictionaryHelper.

Tentatively added in UmbracoHelper instance as in Umbraco v7.3.2 this is the best way to get a dictionary value.

Also worked in some teardowns for Resolution.Reset() so tests work between this and the UmbracoPicker tests.

Finally, there's a slight change to the dictionary tests as UmbracoHelper.GetDictionaryValue doesn't do an ordinal key check for keys. Not sure how we can fix this within Ditto, it might be a feature Umbraco adds in later on.
@jamiepollock
Copy link
Author

I've added an instance of UmbracoHelper to UmbracoDictionaryAttribute.cs.

I feel this might be something we could add at Processor level as it has useful methods outside of wrapping up Services methods or ContentCache.

Conflicts:
	src/Our.Umbraco.Ditto/Extensions/PublishedContentExtensions.cs
	tests/Our.Umbraco.Ditto.Tests/Our.Umbraco.Ditto.Tests.csproj
@jamiepollock
Copy link
Author

@mattbrailsford @leekelleher Okay, chaps. I bit the bullet and added in a reference to UmbracoHelper along side all the other new additions. Much like Members (MembershipHelper) & UmbracoHelper referenced elsewhere this is simply called "Umbraco".

This means UmbracoContext has been renamed from Umbraco to UmbracoContext.

All references have been updated! I think we're getting closer! :D

leekelleher added a commit that referenced this pull request Feb 10, 2017
Added an XPath processor that can query the Umbraco content cache.
Included a supporting unit-test. We do need to review how the mocking objects/services are created, I believe PR #204 may cover this?
@leekelleher leekelleher mentioned this pull request Feb 10, 2017
@leekelleher leekelleher self-requested a review February 10, 2017 01:01
Copy link
Collaborator

@leekelleher leekelleher left a comment

Choose a reason for hiding this comment

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

All looking good.
(Apologies for not having the time to review earlier)

I've left a couple of comments about verbose naming... I'm not 100% sure how they should be named, we can discuss.

One thing I'd like to do is remove the Convert___FromInt methods from DittoProcessorAttribute - I don't think we need them any more. A processor can return an IPublishedContent, then either let Ditto (or another processor) handle it accordingly. (There's no need for those methods to explicitly call the .As() method.

/// Registers a global umbraco application context accessor.
/// </summary>
/// <typeparam name="TUmbracoApplicationContextAccessorType">The type of the accessor.</typeparam>
public static void RegisterUmbracoApplicationContextAccessor<TUmbracoApplicationContextAccessorType>()
Copy link
Collaborator

Choose a reason for hiding this comment

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

This feels like a mouthful, wondering if this can have a concise name?

Copy link
Author

Choose a reason for hiding this comment

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

Hey @leekelleher, I'd like to have a more concise name however it's an class that acts an accessor for both UmbracoContext and ApplicationContext. I'm at a loss as to what else to call it but I'm happy to hear any suggestions :)

Copy link

@JimBobSquarePants JimBobSquarePants Feb 10, 2017

Choose a reason for hiding this comment

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

@leekelleher UmbracoPickerAttribute is the last reference to those Convert___FromInt methods. Definitely happy to drop them as UmbracoPickerAttribute does everything

Copy link
Collaborator

Choose a reason for hiding this comment

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

@JimBobSquarePants I currently set them as deprecated, (142768e), but think I may remove them instead now.
@mattbrailsford Any objections?

Choose a reason for hiding this comment

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

Chop chop chop...em

Copy link
Collaborator

Choose a reason for hiding this comment

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

...and they're gone! 9678d43

@@ -11,6 +11,7 @@

namespace Our.Umbraco.Ditto
{
using global::Umbraco.Core;
Copy link
Collaborator

Choose a reason for hiding this comment

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

We can place the using namespaces outside the main namespace. Those left inside are older code. The global:: part felt unnecessary.

@@ -191,12 +192,15 @@ public static class PublishedContentExtensions
throw new ArgumentException(string.Format("The instance parameter does not implement Type '{0}'", type.Name), "instance");
}

// Get the accessor for UmbracoContext & ApplicationContext
var umbracoApplicationContextAccessor = (IUmbracoApplicationContextAccessor)Activator.CreateInstance(Ditto.GetUmbracoApplicationContextAccessorType());
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we have an extension method that can create new object instances - I'm lead to believe it has performance optimisations. I'll find out what the method is.

Copy link
Collaborator

Choose a reason for hiding this comment

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

The umbracoApplicationContextAccessor variable name feels too verbose... let's consider alternatives 😏

Choose a reason for hiding this comment

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

GetInstance(this Type type) though internally that calls Activator.CreateInstance() if no parameters are passed. We should use it for consistency though.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@JimBobSquarePants Cool, done - in comment fd44882 👍

}
else
{
return ConvertContent(content, type, culture, instance, processorContexts, onConverting, onConverted);
return ConvertContent(content, type, umbracoApplicationContextAccessor, culture, instance, processorContexts, onConverting, onConverted);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a reason why the umbracoApplicationContextAccessor is being passed in as a method parameter from this high up the chain? I'm assuming it's to do with the lazy-loaded properties, but let's review 👍

Copy link
Author

Choose a reason for hiding this comment

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

@leekelleher I wanted to find the highest up point in the chain where we used UmbracoContext.Current (where the CurrentCulture is being used) then use then pass the same context through to the extension methods to the place where we used to inject it into the processor attributes.

It didn't seem right to new the class in both cases but I see how passing this through might seem a little odd too. :)

Copy link
Collaborator

Choose a reason for hiding this comment

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

@jamiepollock I think it's fine... just wanted to flag it (probably me being pedantic late last night 😉)

@mattbrailsford You cool with this?

Copy link
Collaborator

Choose a reason for hiding this comment

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

👍

{
if (Resolution.IsFrozen)
{
Resolution.Reset();
Copy link
Collaborator

Choose a reason for hiding this comment

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

On a complete tangent, I had a problem with the frozen resolution on PR #209 ... and seeing this Resolution.Reset() might fix my issue... thanking you! 💯

@jamiepollock
Copy link
Author

I think the main stickler here is the naming of the UmbracoApplicationContextAccessor, As mentioned in this commit (bd4c79a) I took a cue from the core but that's probably not commonly used code as it was mostly put in for Umbraco Identity.

I don't think UmbracoContextAccessor is quite right as it also accesses ApplicationContext. In the current version of Umbraco we're using IUmbracoContextAccessor is internal but in newer versions it's public.

Happy to rename this to whatever you guys think is best :)

@leekelleher leekelleher changed the base branch from develop to feature/umbraco-context February 10, 2017 10:43
@leekelleher
Copy link
Collaborator

I've created a new feature branch called "feature/umbraco-context", I'll merge this in to there... then we can review/amend accordingly (then get it merged into "develop" branch).

@jamiepollock Thanks again for this, it's very much appreciated! #h5yr 💯

@leekelleher leekelleher merged commit 0a13fcf into umco:feature/umbraco-context Feb 10, 2017
@jamiepollock
Copy link
Author

Hey guys, is there anything I can do to help this along? Just wondering where we're at. I'm sure you're all super busy at the moment. :)

@leekelleher
Copy link
Collaborator

@jamiepollock It's the first time I've had chance to look at Ditto in ages. Reviewing stuff now and we'll see where it's at. 🤘

@jamiepollock
Copy link
Author

@leekelleher thats okay :) just want to make sure it's in the pipeline. I was happy with the code where I left off but time makes way for uncertainty.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants