-
Notifications
You must be signed in to change notification settings - Fork 33
Adding in Base Processor - exposing Umbraco context services #204
Adding in Base Processor - exposing Umbraco context services #204
Conversation
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.
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! |
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. |
Hey @jamiepollock, thanks for taking it on and making a start with this! 🎉 I'm not sure what to make of the |
@leekelleher Yea, I know what you mean. The whole reason is to have a testable in for UmbracoContext & ApplicationContext.
I've been back and forth with this over the weekend after I saw the first push failed I've been trying to get this in. In the end this was the best way I could find. I noticed the Umbraco core was doing something like this in their newer code for Umbraco Identity (https://github.com/umbraco/Umbraco-CMS/search?utf8=%E2%9C%93&q=SingletonUmbracoContextAccessor) and phasing out stuff like plugincontroller.
I'm not suggesting this is how we should go but I've certainly spent a good amount of time looking over a few options.
I realise adding it to the constructor is out as it'd need to be public to be testable and also then appear in the attribute setup.
It's been a fun one :)
… On 22 Jan 2017, at 18:05, Lee Kelleher ***@***.***> wrote:
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.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
|
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. |
@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. :) |
There was a problem hiding this 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>(); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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. :)
There was a problem hiding this comment.
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); } |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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. :)
Conflicts: src/Our.Umbraco.Ditto/Extensions/PublishedContentExtensions.cs
@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. |
@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. |
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? |
Sounds like a good idea. It was based, as you can imagine, on the default processor type but we can't specify an accessor on a per model basis. I'll add it in later on when I get back home.
… On 24 Jan 2017, at 11:19, Matt Brailsford ***@***.***> wrote:
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?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
|
@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.
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
@mattbrailsford @leekelleher Okay, chaps. I bit the bullet and added in a reference to This means All references have been updated! I think we're getting closer! :D |
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?
There was a problem hiding this 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>() |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 :)
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Chop chop chop...em
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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()); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 😏
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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 👍
There was a problem hiding this comment.
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. :)
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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! 💯
I think the main stickler here is the naming of the 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 :) |
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 💯 |
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. :) |
@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. 🤘 |
@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. |
As discussed on issue #200. I've done the work to add UmbracoContext and ApplicationContext in the base
DittoProcessorAttribute
. I used the singletonUmbracoContext.Current
andApplicationContext.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:
Let me know what you think!