cli-microsoft365 icon indicating copy to clipboard operation
cli-microsoft365 copied to clipboard

New command: `spo applicationcustomizer add`

Open martinlingstuyl opened this issue 2 years ago • 3 comments

Usage

spo applicationcustomizer add

Description

Add an application customizer to a site.

Options

Option Description
-t, --title <title> The title of the Application Customizer
-u, --webUrl [webUrl] The site to add the Application Customizer on. If not specified, the application customizer is deployed tenant wide.
-i, --clientSideComponentId <clientSideComponentId> The Client Side Component Id (GUID) of the application customizer.
--clientSideComponentProperties [clientSideComponentProperties] The Client Side Component properties of the application customizer.
--webTemplate [webTemplate] Optionally add a web template (e.g. STS#3, SITEPAGEPUBLISHING#0, etc) when deploying tenant wide as a filter for what kind of sites the application customizer is registered on.

Examples

Add an application customizer to the sales site.

m365 spo applicationcustomizer add --title "Some customizer" --clientSideComponentId  799883f5-7962-4384-a10a-105adaec6ffc --webUrl https://contoso.sharepoint.com/sites/sales

Deploy an application customizer to all communication sites

m365 spo applicationcustomizer add --title "Some customizer" --clientSideComponentId  799883f5-7962-4384-a10a-105adaec6ffc --webTemplate "SITEPAGEPUBLISHING#0"

Additional Info

Part of this functionality can be achieved by the spo customaction commands. However, I'm lazy... I don't want to search for what options I need for an application customizer.

We should reuse the functionality though. If no webUrl is passed, it should be registered in the TenantWideExtensions list of the app catalog. We could reuse the tenant appcatalog get command to retrieve the url of the app catalog.

Of course we would need a note in the docs saying you need to be a SharePoint admin to deploy tenant wide.

martinlingstuyl avatar Jul 01 '22 08:07 martinlingstuyl

Great suggestion for a convenience command. I'd suggest adding -i to --clientSideComponentId and -u to --webUrl.

waldekmastykarz avatar Aug 18 '22 18:08 waldekmastykarz

Updated it!

martinlingstuyl avatar Aug 18 '22 21:08 martinlingstuyl

It would be also helpful to have an example for the properties option, to make it clear what's the format that we're expecting.

waldekmastykarz avatar Aug 19 '22 06:08 waldekmastykarz

I could give this a go if I may 😄 !

MathijsVerbeeck avatar Oct 27 '22 06:10 MathijsVerbeeck

Nice @MathijsVerbeeck! 👏

martinlingstuyl avatar Oct 27 '22 19:10 martinlingstuyl

Hi @martinlingstuyl

I'm currently implementing this issue, and for a specific web it works perfectly fine.

I am however struggling with creating this to work tenant-wide with the current specs. Currently, what we accept is the ClientSideComponentId and that's it. The issue here is that, if the SPFx application in the app-catalog is not deployed to all sites in the organization, the list containing the tenant wide extensions will throw an error, because the component with the specified ClientSideComponentId is not found.

If however, we choose to make this solution available to all sites in the environment, a record will automatically be created in the tenant wide extensions list, so I don't really see a use for this, as we have the command app deploy for this, and we currently in this command do not specify from which solution the ClientSideComponentId originates, so it's impossible for us to identify the correct SPFx solution in the catalog which could deploy this app.

I'm not sure how we should go further on adding an Application Customizer tenant-wide... Do you have any suggestions by any chance?

MathijsVerbeeck avatar Nov 02 '22 23:11 MathijsVerbeeck

Hi @MathijsVerbeeck,

I'd certainly think this is useful. I've recently wanted to deploy apps myself and only later on add some tenant-wide configuration.

There's a hidden list on each app catalog that contains the manifests of the installed apps. You can check it out at /Lists/ComponentManifests. You'll find information like ClientSideComponentId there, so checking if an app is deployed to the app catalog, using only the ClientSideComponentId would be fairly easy. The listitems there also contain a SolutionId, which is helpful when finding the app in another list: the Apps for SharePoint list (<appcatalogurl>/AppCatalog). The SolutionId can be found in the AppProductID column there. Also: the 'Added to all sites' column that's visible in the list (when you browse it in the UI) has the internal name SkipFeatureDeployment.

Checking those two lists and relevant columns would help give you the information you need so you can throw an intelligible error message to the user of the CLI when the app has nog been added or has not been enabled on all sites.

Would that be an idea?

martinlingstuyl avatar Nov 03 '22 06:11 martinlingstuyl

Hi @martinlingstuyl

Makes sense. I'll implement it the way that you just suggested and will send you a message if I'm 'blocked' again or have a question.

MathijsVerbeeck avatar Nov 03 '22 06:11 MathijsVerbeeck

Awesome @MathijsVerbeeck.

I'm seeing other issues come up as well: Listing/removing these configurations, listing/adding/removing listview commandset extensions. Would be nice to have an entire set of this! 😁

martinlingstuyl avatar Nov 03 '22 06:11 martinlingstuyl

Perhaps it could be interesting to either make a helper function for adding the tenant-wide extension or making it a seperate command tenant extension add, tenant extension disable/set, .... But let's start with this one first so that we have a decent base!

MathijsVerbeeck avatar Nov 03 '22 06:11 MathijsVerbeeck

Ah, this will need some discussion indeed!

martinlingstuyl avatar Nov 03 '22 06:11 martinlingstuyl

I'd certainly think this is useful. I've recently wanted to deploy apps myself and only later on add some tenant-wide configuration.

@martinlingstuyl how did you went through this scenario? Have you extended the existing entry, or removed it and added a new one with the desired configuration? I think this should determine what this command should do?

As for tenant extension, it's too broad, because it would indicate something that's available m365-wide, while here, we're talking about something for the SharePoint tenant, so spo tenant would be clearer, before we do that though, let's clarify this what this command should do, for example, if this command is meant to add the customizer only to a specific site, we should consider renaming it to spo web applicationcustomizer add. Let's clarify the scenario and the specs before we continue.

waldekmastykarz avatar Nov 27 '22 12:11 waldekmastykarz

how did you went through this scenario? Have you extended the existing entry, or removed it and added a new one with the desired configuration? I think this should determine what this command should do?

I think I've done both things multiple times...

let's clarify this what this command should do, for example, if this command is meant to add the customizer only to a specific site, we should consider renaming it to spo web applicationcustomizer add. Let's clarify the scenario and the specs before we continue.

The current specs are written to show the command should be able to deploy tenant wide as well as site specific. But thinking through this I'm thinking it would be clearer if we split it up into two:

  • spo web applicationcustomizer add
  • spo tenant applicationcustomizer add

martinlingstuyl avatar Nov 27 '22 22:11 martinlingstuyl

For spo tenant applicationcustomizer add we'd still have to clarify its logic: should the command update the existing entry overwriting the properties and webtemplate or shall it fail with an error if an entry already exists?

It's a good idea to split the commands up into two as you suggested.

waldekmastykarz avatar Nov 28 '22 12:11 waldekmastykarz

Hi @waldekmastykarz, @MathijsVerbeeck, after some thought I think I'm leaning towards the following:

  1. Split the command specs into two (spo tenant applicationcustomizer set and spo web applicationcustomizer set. This will make the flow much clearer, as we discussed.

  2. Using the set verb instead of add. This might be clearer as we're setting configuration for an existing application. It will overwrite the entry if it already exists. Also its clearer because add might suggest that you could also deploy a package to the appcatalog with it. We're not actually adding an app. That should be done separately. For the spo web section it will get the existing customaction and update it, or create a new one. For the tenantwideextension it will get the existing item and update it, or create a new listitem.

  3. For the tenantwide deployment variant, we check the existence of the app and if there's in fact an applicationcustomizer that can be deployed tenantwide. The extra checks offer extra value here, I'd say.

  4. For the site deployment we do not check the existence of the app, as we would need to execute quite some http calls to check the tenantwide and site collection app catalogs.

Something like that. What are your thoughts on this. Does it make sense?

We could extend this with more commands in the future:

  • spo tenant applicationcustomizer list

  • spo tenant applicationcustomizer get

  • spo tenant applicationcustomizer remove

  • spo web applicationcustomizer list

  • spo web applicationcustomizer get

  • spo web applicationcustomizer remove

  • spo tenant commandsetcustomizer set

  • spo tenant commandsetcustomizer get

  • spo tenant commandsetcustomizer list

  • spo tenant commandsetcustomizer remove

  • spo web commandsetcustomizer list

  • spo web commandsetcustomizer set

  • spo web commandsetcustomizer get

  • spo web commandsetcustomizer remove

martinlingstuyl avatar Dec 11 '22 14:12 martinlingstuyl

If we want to combine add and set in one command, I suggest we should use the ensure verb which we use for this purpose in other places. That will keep it consistent and will more clearly set expectations. Other than that, the spec makes sense. 👍

waldekmastykarz avatar Dec 11 '22 18:12 waldekmastykarz

Ok, I'll update the specs and create some new issues.

martinlingstuyl avatar Dec 11 '22 19:12 martinlingstuyl

@martinlingstuyl A quick check - How would you like to check if a custom action already exists? As currently, the command customaction get only supports filtering on the id property or title property. Is it OK that I only check for the title option? I will also add a check that the clientSideComponentId matches the clientSideComponentId returned from the customaction get command.

MathijsVerbeeck avatar Dec 16 '22 15:12 MathijsVerbeeck

@martinlingstuyl A quick check - How would you like to check if a custom action already exists? As currently, the command customaction get only supports filtering on the id property or title property. Is it OK that I only check for the title option? I will also add a check that the clientSideComponentId matches the clientSideComponentId returned from the customaction get command.

Yes, I would implement it like that! @MathijsVerbeeck

martinlingstuyl avatar Dec 16 '22 18:12 martinlingstuyl

Thinking about it some more @MathijsVerbeeck... having second thoughts. What's important to check here is the clientSideComponentId. That id should only be registered once to a site in case of application customizers. Otherwise you get unpredictable results I believe. Titles can differ or be written with slight differences and you would not notice you are ensuring something else.

So I guess we'd better take the clientSideComponentId as the linking pin here. Of course this means that you cannot use customaction get. Instead you can use the customaction list I think. Or we should build the Rest request manually here so we can use $filter.

Thoughts?

martinlingstuyl avatar Dec 16 '22 22:12 martinlingstuyl

I do agree that filtering on clientSideComponentId may be better, as we can change the title.

Personall, to retrieve the customaction, I wouldn't use the customaction list command for this. I'd rather write the rest filter as this will limit the results and thus make the command quicker. Is this fine for you?

MathijsVerbeeck avatar Dec 16 '22 22:12 MathijsVerbeeck

That's okay @MathijsVerbeeck, it will query less data, which is better. We may implement an spo applicationcustomizer get command later where we allow to get the customizer by either Id, Title or clientComponentId. When we have that we can update this command to share code.

martinlingstuyl avatar Dec 17 '22 09:12 martinlingstuyl

By the way @MathijsVerbeeck, we haven't discussed it, but concerning scope: shall we just pick the Site scope for everything here? If people request it we could add a scope option later.

It is something to write down in the remarks though.

martinlingstuyl avatar Dec 17 '22 09:12 martinlingstuyl

@martinlingstuyl I'm fine using the scope 'Site' to start, we could indeed extend this later if requested.

Will try and finish the PR later today!

MathijsVerbeeck avatar Dec 17 '22 11:12 MathijsVerbeeck

Thinking about it some more @MathijsVerbeeck... having second thoughts. What's important to check here is the clientSideComponentId. That id should only be registered once to a site in case of application customizers. Otherwise you get unpredictable results I believe. Titles can differ or be written with slight differences and you would not notice you are ensuring something else.

@martinlingstuyl what if you have a configurable application customizer that can be registered/enabled multiple times with different parameters?

waldekmastykarz avatar Dec 17 '22 18:12 waldekmastykarz

Yeah, I was doubting if that is a thing. Couldn't find people doing that and have a vague memory that it causes problems running them multiple times. But it could be a case of assumptions being someone's mother..

Do you have experiences to the contrary @waldekmastykarz?

martinlingstuyl avatar Dec 17 '22 19:12 martinlingstuyl

I'm not building customer solutions these days so can't tell what are the odds of someone actually doing it, but it's technically possible, so we should at least consider it in our decision.

waldekmastykarz avatar Dec 18 '22 12:12 waldekmastykarz

Ok, what is your suggestion in that case @waldekmastykarz: compare on title just to be sure? I'm thinking: how useful is an ensure when you can create an unwanted duplicate by a small typo.

@nicodecleyre, @MathijsVerbeeck, @Jwaegebaert, you guys by any chance have experience with registering a single application customizers multiple times on the same site (with different props for example)

martinlingstuyl avatar Dec 18 '22 14:12 martinlingstuyl

@waldekmastykarz, an idea would be:

  • create a separate add command to be able to deploy multiple customizers of the same clientSideComponentId.
  • for the ensure command, do use the clientSideComponentId, as an ensure based on title does not really make sense.
  • throw an error on the ensure command when there happen to be multiple instances of the same customizer.
  • create a separate set command to be able to update properties of instances of the same customizer.

martinlingstuyl avatar Dec 18 '22 14:12 martinlingstuyl

@nicodecleyre, @MathijsVerbeeck, @Jwaegebaert, you guys by any chance have experience with registering a single application customizers multiple times on the same site (with different props for example)

I have never done this unfortunately.

MathijsVerbeeck avatar Dec 18 '22 14:12 MathijsVerbeeck