Skip to content

Instantly share code, notes, and snippets.

@shaydoc
Last active May 13, 2020 06:35
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shaydoc/3d52670c7874f52f79ca14fbc9e29bdd to your computer and use it in GitHub Desktop.
Save shaydoc/3d52670c7874f52f79ca14fbc9e29bdd to your computer and use it in GitHub Desktop.
plugin sdk manifest
{
"manifest": {
"schemaVersion":0.0.1,
"id": "{{devportal-id}}",
"name": "tw-spaces-core",
"description": "This is a Teamwork core plugin package including the Checklist and Desk Demo modules",
"extendedDescription": "s3://tw-spaces-core.html",
"version": "0.2.5",
"author": "Spaces team",
"organisation": "teamwork",
"repository": "https://github.com/Teamwork/tw-spaces-core-widgets",
"marketplace":{
"coverImage":"plugin-cover.png",
"moduleImages":{
"checkList":['checklist-img1.png','checklist-img2.png','checklist-img3.png'],
"fullContact":['full-contact-img1.png','full-contact-img2.png','full-contact-img3.png']
}
},
"settings": {
"fullContact.apiKey": {
"displayName": 'The API key for FullContact',
"type": 'string',
"defaultValue": '',
"isPublic": false
},
"fullContact.authScheme": {
"displayName": "The Auth Scheme for FullContact",
"type": "string",
"defaultValue": "Bearer",
"isPublic": false
},
"fullContact.host": {
"displayName": "The endpoint address for FullContact",
"type": "string",
"defaultValue": "https://api.fullcontact.com",
"isPublic": false
},
"fullContant.httpMethod": {
"displayName": "The http method for calling FullContact",
"type": "string",
"defaultValue":"POST",
"isPublic": false
}
},
"modules": [{
"name": "Checklist",
"type": "custom-element",
"url": "js://Checklist",
"placements": {
spaces:['editor', 'inline-comments']
projects: ['notebooks','tasks-comments']
},
"configuration": {
"customElementName": "tw-spaces-core-checklist",
"description": "Checklist Component for Spaces Editor",
"extendedDescription": "s3://full-contact.html",
"keywords": "[]",
"editorConfiguration":{
"attributes": "[]",
"childNodeNames": "['tw-json']",
"shouldIncludeInPrint": true,
"isInlineElement": false,
"requiresProjects": false,
},
"icons": ["checklist-icon-sm", "checklist-icon-md", "checklist-icon-lg"]
}
},
{
"name": "FullContact",
"type": "custom-element",
"url": "js://FullContact",
"placements": {
"desk": ["ticket"]
},
"configuration": {
"customElementName": "tw-desk-full-contact",
"description": "Desk Full Contact for embedding on Tickets",
"keywords": "[]",
"extendedDescription": "s3://full-contact.html",
"editorConfiguration":{},
"icons": ["full-contact-icon-sm", "full-contact-demo-icon-md", "full-contact-icon-lg"]
}
}
}
]
}
}
@shaydoc
Copy link
Author

shaydoc commented Apr 7, 2020

perhaps we should also include a field for module icons

@gkubisa
Copy link

gkubisa commented Apr 7, 2020

extendedDescription sounds good. In the common case I'd expect it would be packaged in the plugin package (ZIP file). Once uploaded, the server would put it in S3 and rewrite the URL.

The icons would be handy too. I'd handle them in the same way as descriptions above.

@shaydoc
Copy link
Author

shaydoc commented Apr 7, 2020

we did also consider having a screeshots list, for maybe an image carousel like Chrome Store, should we include this ?

@gkubisa
Copy link

gkubisa commented Apr 8, 2020

I'd start with the minimum number of properties which we need now and add more only when necessary.

@gkubisa
Copy link

gkubisa commented Apr 8, 2020

Should extendedDescription be a plugin-level property?

@gkubisa
Copy link

gkubisa commented Apr 8, 2020

Regarding module settings... I think the SDK should not place any restrictions on it. It should just reserve the property name ("settings") for storing custom metadata for modules - just a blob of data. This means that the SDK itself would not recognise settings.icons, settings.extendedDescription, nor any other module settings. Module settings would only be used by apps at run-time to control some aspects of the modules, eg the name of the custom element to register, the name to display in an editor plugin menu, the priority of a paste handler, etc. The module settings can (and will) differ based on the module type and placements.

For those reasons I'd document the plugin manifest with module settings as a blob. I'd then document module settings separately for specific module type and placement combinations.

@shaydoc
Copy link
Author

shaydoc commented Apr 8, 2020

extendedDescription I think belongs to the module itself cc @1001hz, I see that desk had this as instructions at their module level. I am also curious as to how we resolve the proxy behaviour at a package level as opposed to a module level also.

@gkubisa
Copy link

gkubisa commented Apr 8, 2020

I don't mind, if extendedDescription is in module settings, if Desk needs it this way. As I said above, I'd like the SDK to see module settings as a blob of data. Is that ok? We should later document specific module settings for all module types and placements.

extendedDescription at the package level would fall into the same category as package icons, screenshots, etc. They could be great for presenting the plugin in the developer portal, however, I'd postpone adding them until we know exactly what we need for that purpose.

I am also curious as to how we resolve the proxy behaviour at a package level as opposed to a module level also.

Good question - I'm also curious. ;-)

I imagine we could eventually develop a generic proxy service for plugins to take care of 3rd party authentication and cross-origin access - not sure if it's a good idea. Unless we have such a service, I'd be in favour of removing the proxy property. It could be added to Desk custom-element module settings for now, if necessary.

@shaydoc
Copy link
Author

shaydoc commented Apr 9, 2020

I am going to move the proxies settings back into the module settings section. However I have a feeling that this is definitely module config data, that would perhaps be instanced per installation, or per module, would be good to get clarity for that.
As in, will we we have to introduce a step now that does the following:

  1. Select the module from a "registry" e.g. "marketplace, or dev portal"
  2. Enable this module on my installation
  3. Define some Installation settings for this module e.g. ( api keys, url, urlToProxy request through)
  4. Then you are free to create an instance of this module in your app,

@shaydoc
Copy link
Author

shaydoc commented Apr 9, 2020

I have refined this a little and added installationConfiguration and editorConfiguration to the module
Thoughts ?

@gkubisa
Copy link

gkubisa commented Apr 9, 2020

I am going to move the proxies settings back into the module settings section. However I have a feeling that this is definitely module config data, that would perhaps be instanced per installation, or per module, would be good to get clarity for that.
As in, will we we have to introduce a step now that does the following:

  1. Select the module from a "registry" e.g. "marketplace, or dev portal"
  2. Enable this module on my installation
  3. Define some Installation settings for this module e.g. ( api keys, url, urlToProxy request through)
  4. Then you are free to create an instance of this module in your app,

From the user point of view modules are implementation details. Modules (from the same plugin) can also depend on one another. They can also be very low level and technical. I see no reason to allow end-users any direct control over modules, including disabling them.

In my opinion all end-user settings should be defined globally per plugin, rather than per module. All that an end-user would need wrt plugins is the ability to enable, disable and configure them. They don't need to know about modules at all. Modules can read plugin settings, so a user would configure module implicitly by configuring a plugin. If necessary, a plugin-level settings could even control, if specific modules are active, however, that would be plugin-specific and not a concern of the SDK. I hope it makes sense. :-)

@shaydoc
Copy link
Author

shaydoc commented Apr 9, 2020

The example for me here is say I have:

  1. Google Calendar Module
  2. MS Outlook Calendar Module
  3. Figma Module

I need to set api keys for both against my installation, in that way it makes sense to have these at a module setting level @gkubisa
This would likely be the case within tw-spaces-core-widgets repo if we added these modules to our plugin package.

@gkubisa
Copy link

gkubisa commented Apr 9, 2020

The example for me here is say I have:

  1. Google Calendar Module
  2. MS Outlook Calendar Module
  3. Figma Module

I need to set api keys for both against my installation, in that way it makes sense to have these at a module setting level @gkubisa
This would likely be the case within tw-spaces-core-widgets repo if we added these modules to our plugin package.

I would have each calendar integration in a separate plugin. :-) If they need to share code, they can do so through node modules. This way many plugins can be very small and simple, often containing only a single module. I think it would actually be easier for users, as most would use only a single calendar integration. So, if they use Google Calendar, they install the Google Calendar plugin and see only Goolge Calendar settings. It's also easier from the developer's point of view, as each plugin can be developed, tested, updated and published separately - plus we can naturally keep all user-modifiable setting at the plugin-level. ;-)

Another reason for having each calendar (or any other single well defined feature) in a separate plugin is that we might want to extend it later, for example so that it would work in more than one of our apps. This is naturally much easier to do when the scope of the plugin is smaller, eg only Google Calendar, instead of an arbitrary group of calendars (assuming we'd want to maintain feature parity between all (calendar) modules in a single plugin, which I think we should do, if we went that route).

Even though most plugins will be simple, often with only one module, I still think the concept of modules is very useful, as some features will require more than just a custom element. For example, you'd want a SingleTeamworkTask editor component module and an associated paste handler module in the same plugin, so that when you paste a link to a task, the paste handler rewrites the pasted content to create an instance of the SingleTeamworkTask component. Another example could be an editor component module and a sidebar component module, which aggregates the data from the editor components, eg extracts all CheckListItems from the content.

Regarding tw-spaces-core-widget, I think it's ok, if it contains multiple unrelated editor component modules as long as they don't require any user configuration. Then again, I wouldn't mind if it was split into multiple plugins. If they are all automatically enabled on all installations, the user wouldn't see a difference anyway.

@shaydoc
Copy link
Author

shaydoc commented Apr 10, 2020

I understand the idea here. And that makes sense that a plugin would typically contain a single module.

The only concern this may present is the bundle sizes when splitting our own vue based modules into to separate plugins. That means we would unnecessarily ship vue, vuex vuerouter as part of every bundle. In this way maybe it is preferable to keep them as a list of modules and therefore including module configuration for an installation. The only other issue splitting raises is additional script overhead to the document in spaces, instead of registering one lightweight script that supports maybe 50 or 60 good plugin modules built by ourselves, we would be adding alot of blocking script references at runtime ?

@gkubisa
Copy link

gkubisa commented Apr 10, 2020

That's a very good point about the bundle sizes. Could we provide an option in the (webpack) SDK to choose whether Vue and Vuex should be bundled, or reused from the host application?

The number of scripts shouldn't be a big problem I think... Caching would help a lot (except for the first load). They could be loaded in parallel with HTTP/2. We could also preload them in the background.

Having said that, bundling lots of modules is still an option for me, as long as we manage to keep it all simple and flexible for end users, plugin developers and SDK developers. :-) I think it is possible, however, I'd still keep all user settings at the plugin level. Apart from simplifying module definitions, a big reason is that the same setting might be applicable to multiple modules, or it might affect modules which the user shouldn't need to know about (eg a paste handler), so we'll need plugin-level settings anyway. What I'm after is basically to decouple settings from modules. Perhaps instead of supporting both plugin and module level user-modifiable settings we could have only plugin-level settings and enrich the definition format in the manifest to support sections/headings? Something like this:

manifest: {
  settings: [
    { type: 'heading', text: 'General' },
    { type: 'select', name: 'general.startOfWeek', label: 'Start of Week', options: ['Monday', 'Sunday'] },
    { type: 'select', name: 'general.display.type', label: 'Display Type', options: ['compact','full'] },
    { type: 'heading' , text: 'Google Calendar' },
    { type: 'input', name: 'google.apiKey', label: 'API Key', default: '' },
  ]
}

@gkubisa
Copy link

gkubisa commented Apr 10, 2020

One more thing... perhaps we could rename module.settings to module.config for clarity and to avoid confusion. plugin.settings would then be for user-modifiable settings and module.config would be for read-only module configuration.

@shaydoc
Copy link
Author

shaydoc commented Apr 20, 2020

OK, so I lifted out installationConfiguration and put it as settings at the plugin level. I have renamed module.settings to module.configuration, I lifted name up to the module level also. I guess I will introduce a manifest-version prop and set that to 0.0.1 @gkubisa

I think its worth introducing a screenshots attribute, an plugin cover image based upon this POC,
https://vue-zeit.shaydoc.now.sh/plugin-packages/tw-spaces-core

@gkubisa
Copy link

gkubisa commented Apr 20, 2020

I'm now happy with the structure and core properties. Thanks for your patience, Shay! :-)

Regarding the marketplace-related properties, I'd keep them all at the plugin level (like settings). Maybe they could be nested too, to keep it clean: manifest.marketplace.[...]? In there you could just add whatever you need on the page in the marketplace/dev-portal.

PS. "module.settings" -> "module.configuration" (or "module.config") for "FullContact". ;-)

@theflyingcodr
Copy link

Can we add a plugin level settings page for user configurable settings (the settings pane on a plugin), this could be an area to select a project or add some other property. This should be an object something like pluginConfig:[{"name":"projectID", "displayName":"Enter ProjectID","type":"int", "defaultValue":0}]

@theflyingcodr
Copy link

Can we change this

 "placements": [
        { "app": "spaces", "location": "editor" }
      ],

to a map instead? This means each app could only have one entry and an array for each location the plugin is supported. Would also make it a bit faster to access the locations for a specific app

"placements":{
"spaces":["editor","comments","inlinecomments"]
}

@gkubisa
Copy link

gkubisa commented Apr 20, 2020

As discussed in Chat, the id should be a random string (App ID) obtained from the developer portal.

@shaydoc
Copy link
Author

shaydoc commented Apr 20, 2020

thanks, this should conclude everything for this version

@shaydoc
Copy link
Author

shaydoc commented Apr 20, 2020

Can we add a plugin level settings page for user configurable settings (the settings pane on a plugin), this could be an area to select a project or add some other property. This should be an object something like pluginConfig:[{"name":"projectID", "displayName":"Enter ProjectID","type":"int", "defaultValue":0}]

My only feeling here, is that feels like plugin instance settings, and would vary from instance to instance, is it necessary if the plugin can self contain this type of config without the need to declare it ?

@gkubisa
Copy link

gkubisa commented Apr 21, 2020

I think this is just a bad example and the intention is to change the format of the end-user-modifiable plugin-level settings, as we discussed during the meeting.

manifest: {
  settings: [
    {
      name: 'fullContact.apiKey',
      displayName: 'The API key for FullContact',
      type: 'string',
      defaultValue: '',
      isPublic: false
    }
  ]
}

PS. I'm not sure about the best way to handle the proxy settings. Perhaps we could discuss that at our next meeting?

@shaydoc
Copy link
Author

shaydoc commented Apr 21, 2020

Yep, can discuss at the next meeting, OK, but maybe still a map structure?

  manifest: {
     settings: {
           'fullContact.apiKey': {
                displayName: 'The API key for FullContact',
                type: 'string',
                defaultValue: '',
                isPublic: false
           },
           'fullContact.authScheme': {
                displayName: 'The Auth Scheme  for FullContact',
                type: 'string',
                defaultValue: 'Bearer',
                isPublic: false
           },
           'fullContact.host': {
               displayName: 'The endpoint address for FullContact',
               type: 'string',
               defaultValue: 'https://api.fullcontact.com',
               isPublic: false
            },
           'fullContant.httpMethod':{
                displayName: 'The http method for calling FullContact',
                type: 'string',
                defaultValue:'POST',
                isPublic: false
           }
     }
 }

@gkubisa
Copy link

gkubisa commented Apr 21, 2020

I don't really mind, if it's an object or an array. Having said that, I think an array gives us a bit more flexibility in terms of defining the settings page, like allowing control over the order of the settings and insertion of auxiliary items like horizontal lines, headers, etc. See also my previous comment.

@shaydoc
Copy link
Author

shaydoc commented Apr 21, 2020

Is it likely the plugins will be shared and reused across applications ?
Should we have an attribute on the top level to describe what application this plugin is for do we rely on the module placements element ?

@gkubisa
Copy link

gkubisa commented Apr 21, 2020

Is it likely the plugins will be shared and reused across applications ?

It's really hard to say... I suppose things like Chat, time logging and some other widgets launched from the header could be distributed as plugins.

Should we have an attribute on the top level to describe what application this plugin is for do we rely on the module placements element ?

This information can already be obtained from module placements, however, I think it would be reasonable to have "supported apps" at the plugin-level too, perhaps under the "marketplace" key.

@ready4god2513
Copy link

Can we change this

 "placements": [
        { "app": "spaces", "location": "editor" }
      ],

to a map instead? This means each app could only have one entry and an array for each location the plugin is supported. Would also make it a bit faster to access the locations for a specific app

"placements":{
"spaces":["editor","comments","inlinecomments"]
}

I'd like to echo this. It also allows us to get rid of marketplace/supportedApplications. If the key spaces is provided then it's available for spaces, otherwise not. There is a bit of duplication in having both. Additionally when they are keys rather than values it's more clear to describe.

@gkubisa
Copy link

gkubisa commented May 13, 2020

We have already stopped using this gist and moved the information to this Spaces page.

PS. I see the placements property was already updated as you suggested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment