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

WIP - Initial help/work to get this working for V8.0.0 #135

Conversation

warrenbuckley
Copy link
Member

Hiya @Jeavon @JimBobSquarePants @lars-erik
This is a WIP PR where I will try & find some time in the evening's or during lunchtime hacks, to get this package re-targeted for V8.0.0

Will post updates/notes as I go along...

@Jeavon
Copy link
Collaborator

Jeavon commented Jan 2, 2019

@warrenbuckley great stuff! I was discussing with @zpqrtbnk a week before the holidays about what changes would be needed to port this over. If there's anything I can do to support/test please let me know!

Do final fixes to get this to compile - still need to implement couple of things
@warrenbuckley
Copy link
Member Author

@Jeavon it will probably be test/tidy up my initial work - it currently compiles but I need to head off for today.

@warrenbuckley
Copy link
Member Author

Seems like we are close @Jeavon and I can add my first set of notes to my working document for common package upgrade notes for developers.

@warrenbuckley
Copy link
Member Author

Think the ZIP package won't work for now as think we (HQ) have not done much work with packages in V8 Core as of yet nor know whats planned for it.

@JimBobSquarePants
Copy link
Contributor

Man that Current stuff gives me the heebie-jeebies.

@warrenbuckley
Copy link
Member Author

@JimBobSquarePants feel free after I submit this to use the DI Container instead - but for quickness & to help easily port stuff then Current is the best/easiest way to go.

But please lets not get into a DI debate etc here. I just wanted to help port this over as quickly as possible so that it will continue to work on V8 when it gets released.

Seems perfectly valid to return a not implemented for the new AddFile method - due to the bool CanAddPhysical is false/not implemeted as well
@lars-erik
Copy link
Collaborator

I'm not sure if @zpqrtbnk got around to filesystems yet, but it's hopefully being tweaked more. I'm making a note to revisit this. In the mean time, Current could be centralized to a bastard injection ctor to make it as seamless as possible when time comes. IE. just delete a ctor. ;)

@warrenbuckley
Copy link
Member Author

I am not sure - will ask @zpqrtbnk when I am back at work officially on Monday to see if there is more tweaks to be done to IFileSystems

Like I said for now I will use Current as it compiles and this all works. If the three of you wanna tweak & improve it, after you merge it in then please do so :)

The only thing that is problematic is the ZIP based package & the post configuration view

<package id="Microsoft.AspNet.WebApi.Core" version="4.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.WebHost" version="4.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebPages" version="2.0.20710.0" targetFramework="net45" />
<package id="LightInject" version="5.1.2" targetFramework="net472" />
Copy link
Contributor

@JimBobSquarePants JimBobSquarePants Jan 6, 2019

Choose a reason for hiding this comment

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

Are these explicitly required? I'm seeing a lot of references here that I would expect to be implicit.

<package id="Microsoft.AspNet.WebApi.WebHost" version="4.0.30506.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebPages" version="2.0.20710.0" targetFramework="net45" />
<package id="LightInject" version="5.1.2" targetFramework="net472" />
<package id="LightInject.Annotation" version="1.1.0" targetFramework="net472" />
Copy link
Contributor

Choose a reason for hiding this comment

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

As above. Lot's of new explicit references.


if (blockBlob != null)
{
return blockBlob.Properties.Length;
Copy link
Contributor

Choose a reason for hiding this comment

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

Properties can be null can it not?

@JimBobSquarePants
Copy link
Contributor

I do wonder whether this should be an independant package for V8?

If I were redesigning the IFileSystem interface I would make an explicit effort to design the shape of the API with other implementations (Azure, AWS etc) in mind, checking first to see if the API is both possible and efficient to manage.

CanAddPhysical seems an odd addition to me for example. That's an implementation detail not an API one imo.

https://github.com/JimBobSquarePants/UmbracoFileSystemProviders.Azure/pull/135/files#diff-255650a04f6557f1ccf449b4c809e544R153

https://github.com/umbraco/Umbraco-CMS/blob/8ce50ca357e610222ae3201f10965f0c39bf3932/src/Umbraco.Core/IO/IFileSystem.cs#L160

@JimBobSquarePants
Copy link
Contributor

This is all good except for the many "Umbraco implementors" that use this package it's going to be too complicated especially with many Umbraco Cloud installations etc... So the idea is to create 2 additional packages "Our.Umbraco.FileSystemProviders.Azure.MediaFileSystem" & "Our.Umbraco.FileSystemProviders.Azure.UmbracoFormsFileSystem", these will depend on "Our.Umbraco.FileSystemProviders.Azure" and essentially all they do is implement IComposer for the relevant FileSystem, these could also come with a configuration UI similar to the current installer.

What?? I don't understand why there should be any additional complexity at all. It all smells of some unfortunate architectural decisions in Umbraco. There's no way I want to suddenly maintain multiple projects.

V8 was supposed to make things simpler!!

Here's how it should work:

IFileSystem should be the basic crud implementation. IMediaFileSystem, Forms and anything else that needs to persist files should be loading the IFileSystem via constructor injection and utilizing that for all crud operations. There should be no complexity as IFileSystem should be designed in a manner to cover all basic required operations.

Our package should implement IFileSystem only. It shouldn't care about the media implementation or anything else higher up. If it suddenly needs to then there's something fundamentally wrong.

@@ -68,7 +69,7 @@ public override Stream Open()
// Add Accept-Ranges header to fix videos not playing on Safari
HttpContext.Current.Response.AppendHeader("Accept-Ranges", "bytes");

IFileSystem azureBlobFileSystem = FileSystemProviderManager.Current.GetUnderlyingFileSystemProvider("media");
IFileSystem azureBlobFileSystem = Current.MediaFileSystem.Unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks absolutely mental to me. Unwrap()? The IFileSystem should be injectable.

@zpqrtbnk
Copy link

zpqrtbnk commented Jan 9, 2019

@JimBobSquarePants This is an area which we've tried to simplify, but not sure we're there yet.

It goes like this. IMediaFileSystem is implemented by MediaFileSystem which requires an underlying IFileSystem - the one that by default is a PhysicalFileSystem rooted at ~/media but can be swapped, for instance, with the Azure Blob filesystem.

All this package should provide is, indeed, an IFileSystem implementation.

Now, how do we know which IFileSystem implementation should be injected into MediaFileSystem?

In v7 this is done in a config file, with a fair amount of pseudo DI. To clean things up, we've moved to configuration by code. Also, there are many different IFileSystem services (for medias, stylesheets, templates...) and we don't want to use named services. This is what we're trying to solve and the current solution may be mental-ish ;-)

The only other proper way I see... thinking of it... is to create an interface IMediaUnderlyingFileSystem and then, MediaFileSystem would require one of these. But then, your Azure fs need to declare it implements the interface.

Do you see the problem? Am I confused by too much thinking, and do you see a much simpler solution?

@lars-erik
Copy link
Collaborator

I don't think I ever understood why "we don't want named services"?

@zpqrtbnk
Copy link

zpqrtbnk commented Jan 9, 2019

@lars-erik how do you indicate you want to inject the IFileSystem implementation named "foo" in a non container-specific way?

@lars-erik
Copy link
Collaborator

@zpqrtbnk With the current implementation, the only way without more component metadata is to say so in a factory method. IE. container.Register<IAbstraction>(factory => new Concrete(factory.GetService<IDependency>("name"))).
To make it work with MS.DI, we'd have to employ some more tricks, so there's research needed. I think we could add name overloads to IRegister and do it with Lightinject while researching MS.DI.
Need more discussion, and can be done in umbraco/Umbraco-CMS#3955.

@lars-erik
Copy link
Collaborator

Discussion had off-site. I'll research naming with MS.DI while @zpqrtbnk will make it work with IRegister/IFactory/LI for now.

@JimBobSquarePants
Copy link
Contributor

JimBobSquarePants commented Jan 9, 2019

Also, there are many different IFileSystem services (for medias, stylesheets, templates...) and we don't want to use named services.

@zpqrtbnk I get why you don't want to do it via named services since that would require an explicit implementation.

There's a couple of ways that you could do this. Here's an example one way using factories with keys.
https://stackoverflow.com/a/44177920/427899

That's OK but I think we can maybe do better.

First define your types and interfaces

// Used to identify where the filesystem should be used.
public enum FileSystemScope
{
    Media,
    Templates,
    StyleSheets,
    // You can keep extending this ad infinitum.
}
IFileSystem
{
   FileSystemScope Scope { get; }
}
// By scoping this file system to a particular domain we can use different containers
// for different domains if need be. 
// Any underlying CRUD code would be simple enough and could be shared across 
// the implementations.
public class AzureMediaFileSystem : IFileSystem
{
    public FileSystemScope Scope { get; } = FileSystemScope.Media;
}

Then your consuming class...

// MediaFileSystem is a bad name btw. It's mixing the caller and callee names which is confusing.
public class MediaManager : IMediaManager
{
    private readonly IFileSystem fielsystem;

    public MediaManager(IEnumerable<FileSystem> filesystems)
    {
        // You could register some sort of factory that does this lookup for you if you liked
        // and pass that through the constructor instead.
        this.filesystem = filesystems.First(x => x.Scope == FileSystemScope.Media);
    }
}

Registration in Jamestopia would be performed by the availability of a method as follows.

// IUmbracoBuilder is an extensible interface + implementation that you can pass to your startup.
public static IUmbracoBuilder ReplaceMediaFileSystem<TFileSystem, TFileSystem2>(this IUmbracoBuilder builder)
    where TFileSystem : class, IFileSystem
    where TFileSystem2 : class, IFileSystem
{
    // I dunno if it's possible to ensure at compile time whether it's the IFileSystem is using the   
    // correct FileSystemScope but you'd soon get a warning anyway.
    // Services is IServiceCollection
    builder.Services.Remove(ServiceDescriptor.Singleton<IFileSystem, TFileSystem>());
    builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IFileSystem, TFileSystem2>());
    return builder;
}

It's a pretty simple approach really that you can build easily enough since you know the areas of scope. The same principle could be applied to other areas of the codebase I'm sure also.

@kjac
Copy link

kjac commented Jan 11, 2019

You guys are heroes ❤️ let me know if you need help testing or whatever.

@warrenbuckley
Copy link
Member Author

Any updates on this @zpqrtbnk @lars-erik or @JimBobSquarePants ?!

Can I carry working on this or you still discussing some changes for V8 core related to this?!

@lars-erik
Copy link
Collaborator

@warrenbuckley Things are happening. See #v8-di on community slack.

@warrenbuckley
Copy link
Member Author

Will go & have a nose and catch-up what’s been going on

@warrenbuckley
Copy link
Member Author

OK after the recent V8 core FileSystem changes that @lars-erik & @JimBobSquarePants have been in discussion with @zpqrtbnk that has been merged into the V8 core.

umbraco/Umbraco-CMS#4028
umbraco/Umbraco-CMS#4059

I have updated this PR again to make it work, currently it works with AppSetting keys.
You may wanna change the implementation I have done and do some tidying up and discuss if you want to keep/still use the installer project etc... /cc @Jeavon

@warrenbuckley
Copy link
Member Author

@lars-erik I have done the change you suggested & this currently all works.

However it feels I am getting deeper and deeper into the existing codebase, so I would love it if one of the three of you who are collaborators on this project, are able to do any further refactoring or changes that you would like to see, direct on this PR please.

Cheers,
Warren :)

@JimBobSquarePants
Copy link
Contributor

JimBobSquarePants commented Jan 15, 2019

However it feels I am getting deeper and deeper into the existing codebase, so I would love it if one of the three of you who are collaborators on this project, are able to do any further refactoring or changes that you would like to see, direct on this PR please.

Tbh mate I'd like to refactor the entire library from scratch no that we will have proper DI to utilize. If it's working now I'm happy to merge current progress in.

If I make a developV8 branch here could you retarget your PR at that?

Totally forgot I could change the base!

@JimBobSquarePants JimBobSquarePants changed the base branch from develop to develop-umbraco-version-8 January 15, 2019 23:31
@JimBobSquarePants
Copy link
Contributor

@warrenbuckley While I have your attention I do have some further questions regarding IFileSystem implementations that I'd really to get cleared up.

We currently have to use a virtual path provider in order to serve relative files. It's really shitty for us to have to do so because it shouldn't be a concern of ours - We should just return the values, Umbraco should serve them. Does Umbraco still save the paths in the DB? if so, can it not? Relative files should simply be the way it works. I want to delete our VPP.

Have any updates been made to ensure that the correct response headers are served? We currently have to do a hack, again in the virtual path provider, adding headers like Accept-Ranges to the output to correctly serve videos on Safari. I would really like to drop any header augmentation, leaving that to Umbraco. We have existing issues #78 and #108 that should not exist.

/// <summary>
/// The maximum number of days to cache blob items for in the browser
/// </summary>
public string MaxDays { get; set; }
Copy link
Contributor

Choose a reason for hiding this comment

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

This is because the Pseudo-DI that V7 uses when parsing the configuration originally passes the parameters as strings. Change them to the real deal anytime.

@warrenbuckley
Copy link
Member Author

@JimBobSquarePants to answer your couple of questions I don't have any definitive answers for you now. As far as I know, this will have to stay the same for now,

How shall we move this forward, are you happy to merge it in and then build/work upon the initial changes I have done so you can refactor & tweak as you please.

@JimBobSquarePants
Copy link
Contributor

@warrenbuckley Yeah, now I've changed the target we can merge this as. Thanks for making a start! 👍

@JimBobSquarePants JimBobSquarePants merged commit 65448c6 into umbraco-community:develop-umbraco-version-8 Jan 17, 2019
@warrenbuckley warrenbuckley deleted the develop8 branch January 18, 2019 08:13
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.

6 participants