{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-} {-| Module : Data.Connect.Descriptor Description : Defines a strictly typed Atlassian Connect add-on descriptor. Copyright : (c) Robert Massioli, 2014 License : APACHE-2 Maintainer : rmassaioli@atlassian.com Stability : experimental This module provides the data types to let you write your own typesafe Atlassian Connect descriptor and it comes with Aeson bindings so that you can easily convert into json: the format that the Atlassian Connect framework expects. Atlassian Connect is a framework for writing Add-on's that can run inside the Atlassian Cloud products. You can find more information from the Atlassian Connect documentation <https://developer.atlassian.com/static/connect/docs/guides/introduction.html>. The plugin descriptor is defined by the 'Plugin' class. The end result of using this Haskell Module should be for you to end up with a valid 'Plugin'. To turn your plugin into JSON that the Atlassian marketplace can accept just use the 'encode' function from the Aeson library. For example, here in an example Atlassian Connect Descriptor: > pluginToJsonString :: Plugin -> ByteString > pluginToJsonString = encode > > exampleDescriptor :: Plugin > exampleDescriptor = (pluginDescriptor (PluginKey "my-example-connect") baseURL (Authentication Jwt)) > { pluginName = Just . Name $ "My Example Connect Addon" > , pluginDescription = Just "This is an example connect descriptor." > , vendor = Just $ Vendor (Name "Awesome Devs") (toURI "http://awesome-devs.com") > , lifecycle = Just defaultLifecycle > , modules = Just exampleModules > , enableLicensing = Just False > , links = HM.fromList > [ ("documentation", toURI "http://awesome-devs.com/docs") > , ("source", toURI "http://bitbucket.org/awesome-devs/connect-addon") > ] > , scopes = Just [Read, Admin] > } > > exampleModules :: Modules > exampleModules = Modules exampleJIRAModules emptyConfluenceModules > > exampleJIRAModules :: JIRAModules > exampleJIRAModules = emptyJIRAModules > { jmWebPanels = Just > [ WebPanel > { wpKey = "test-web-panel" > , wpName = simpleText "Test Web Panel" > , wpTooltip = Just $ simpleText "This is a test web panel..." > , wpLocation = "some-location-in-jira" > , wpUrl = "/panel/location/for" > , wpConditions = [staticJiraCondition UserIsAdminJiraCondition] > , wpWeight = Nothing > , wpLayout = Nothing > , wpParams = noParams > } > ] > , jmGeneralPages = Just > [ JIRAPage > { jiraPageKey = "test-general-page" > , jiraPageName = simpleText "Test General Page" > , jiraPageLocation = Just "some-other-location-in-jira" > , jiraPageWeight = Just 1234 > , jiraPageUrl = "/panel/general-page" > , jiraPageIcon = Just IconDetails > { iconUrl = "/static/path/to/icon.png" > , iconWidth = Just 20 > , iconHeight = Just 40 > } > , jiraPageConditions = [staticJiraCondition UserHasIssueHistoryJiraCondition] > , jiraPageParams = noParams > } > ] > , jmWebhooks = Just > [ Webhook > { webhookEvent = JiraIssueDeleted > , webhookUrl = "/webhook/handle-deletion" > } > ] > } You can use this library to make your own. This library will experience change whenever the Atlassian Connect descriptor changes. There are likely to be many breaking changes but we will keep track of them using the standard Haskell version structure. -} module Data.Connect.Descriptor ( -- * Atlassian Connect Add-on Descriptor Plugin(..) , pluginDescriptor -- * Basic Types , Key(..) , PluginKey(..) , Timeout(..) , Vendor(..) , Authentication(..) , AuthType(..) , IconDetails(..) , Name(..) , I18nText(..) , simpleText , URLBean(..) , toUrl , Length(..) , Weight , ModuleParams , noParams -- * Migrations , ApiMigrations(..) -- * Lifecycle , Lifecycle(..) , emptyLifecycle , defaultLifecycle -- * Add-on Modules , Modules(..) , JIRAModules(..) , emptyJIRAModules , ConfluenceModules(..) , emptyConfluenceModules -- ** Web Sections, Items and Panels , JIRAWebSection(..) , WebItem(..) , WebItemContext(..) , WebPanel(..) , WebPanelLayout(..) -- ** JIRA Pages , JIRAPage(..) -- ** JIRA Tab Panels , JIRAGenericTabPanel(..) , JIRAProjectAdminTabPanel(..) -- ** JIRA Specific Modules , JIRASearchRequestView(..) , JIRAIssueContent(..) , JIRAIssueContentTarget(..) , JIRAIssueField(..) , JiraIssueFieldExtraction(..) , JiraIssueFieldProperty(..) , JiraIssueFieldTemplate(..) , JiraIssueFieldType(..) , JiraIssueFieldPropertyType(..) , JIRAIssueGlance(..) , JIRAIssueGlanceContent(..) , JIRAIssueGlanceTarget(..) , JIRAReport(..) , JIRAReportCategory(..) , Target(..) , JIRAWorkflowPostFunction(..) , DialogOptions(..) , InlineDialogOptions(..) , JIRAEntityProperties(..) , EntityType(..) , KeyConfiguration(..) , Extraction(..) , ExtractionType(..) -- ** Webhooks , Webhook(..) , WebhookEvent(..) -- * Module Conditions , Condition(..) , ConditionType(..) , ConditionSource(..) , remoteCondition , JIRACondition(..) , staticJiraCondition , ConfluenceCondition(..) , staticConfluenceCondition , invertCondition -- * Scopes (Permissions) , ProductScope(..) ) where import qualified Data.Aeson as DA import Data.Connect.AesonHelpers import Data.Connect.BaseTypes import Data.Connect.Conditions import Data.Connect.Lifecycle import Data.Connect.Modules import Data.Connect.Scopes import Data.Connect.Webhooks import qualified Data.HashMap.Strict as HM import Data.Text import GHC.Generics import Network.URI -- | A 'Plugin' is the end result of an Atlassian Connect descriptor. It is what you should provide to the Atlassian -- Marketplace in order to register your plugin and it is what your Atlassian Cloud customers will download to install -- your Add-on via the Atlassian UPM (Universal Plugin Manager). Only a very small number of fields are strictly required -- to generate a valid Atlassian Connect plugin descriptor. Everything that is optional is marked by a maybe type. -- -- Even though we provide documentation here you shoucd check the Atlassian Connect Descriptor documentation if you want -- to get accurate information on the contents of a plugin: <https://developer.atlassian.com/static/connect/docs/modules/> data Plugin = Plugin { Plugin -> PluginKey pluginKey :: PluginKey -- ^ Plugin keys are required. The important detail about this key is that it should -- be unique across the Atlassian Marketplace. For example, a good key might be -- @com.yourcompanyorpersonalname.youraddonname@ because it would be unique in the -- marketplace. , Plugin -> URI pluginBaseUrl :: URI -- ^ Every plugin must specify a base url where all other relative URI's in the plugin will -- call to in production. This is the url that the Atlassian Marketplace will query for your -- descriptor and the url that your customers will come in on. This is especially important -- for load balanced applications and will also likely be different in your staging and production -- environments. , Plugin -> Authentication authentication :: Authentication -- ^ The authentication type that you plugin requires. See 'Authentication' for more details. , Plugin -> Maybe (Name Plugin) pluginName :: Maybe (Name Plugin) -- ^ While your add-on does not require a name it is strongly recommended -- as this is the human readable name that will appear in the UPM amongst other places. , Plugin -> Maybe Text pluginDescription :: Maybe Text -- ^ You should give your add-on a description. This description will appear in multiple -- locations on the Atlassian Marketplace and UPM and will be used to explain what your -- add-on does. , Plugin -> Maybe Vendor vendor :: Maybe Vendor -- ^ You are the 'Vendor'. Put your details here! , Plugin -> Maybe Lifecycle lifecycle :: Maybe Lifecycle -- ^ Atlassian Connect addon's have a lifecycle. Register your handlers for -- the 'Lifecycle' events here so that you can tell, for example, when your -- addon is installed or enabled. , Plugin -> Maybe Modules modules :: Maybe Modules -- ^ The modules that your Atlassian Connect add-on provides to the Cloud application. Look at the 'Modules' documentaiton for more information. , Plugin -> Maybe Text apiVersion :: Maybe Text -- ^ Required if you wish to provide new versions of your addon to a subset of beta customers. , Plugin -> Maybe Bool enableLicensing :: Maybe Bool -- ^ If you are giving away a free add-on then you can set this to false, otherwise set it to true. , Plugin -> HashMap Text URI links :: HM.HashMap Text URI -- ^ A collection of custom links that you wish to publish with your add-on. Like documentation or bug-tracking links. , Plugin -> Maybe [ProductScope] scopes :: Maybe [ProductScope] -- ^ The scopes that your add-on requires. See 'ProductScope' for more information. , Plugin -> Maybe ApiMigrations apiMigrations :: Maybe ApiMigrations -- ^ The Migrations that this app has opted into. } deriving (Int -> Plugin -> ShowS [Plugin] -> ShowS Plugin -> String forall a. (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a showList :: [Plugin] -> ShowS $cshowList :: [Plugin] -> ShowS show :: Plugin -> String $cshow :: Plugin -> String showsPrec :: Int -> Plugin -> ShowS $cshowsPrec :: Int -> Plugin -> ShowS Show, forall x. Rep Plugin x -> Plugin forall x. Plugin -> Rep Plugin x forall a. (forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a $cto :: forall x. Rep Plugin x -> Plugin $cfrom :: forall x. Plugin -> Rep Plugin x Generic) instance DA.ToJSON (Name Plugin) instance DA.ToJSON Plugin where toJSON :: Plugin -> Value toJSON = forall a. (Generic a, GToJSON' Value Zero (Rep a)) => Options -> a -> Value DA.genericToJSON Options baseOptions { fieldLabelModifier :: ShowS DA.fieldLabelModifier = String -> ShowS stripFieldNamePrefix String "plugin" } data ApiMigrations = ApiMigrations { ApiMigrations -> Bool migrationGdpr :: Bool , ApiMigrations -> Bool migrationSignedInstall :: Bool } deriving (Int -> ApiMigrations -> ShowS [ApiMigrations] -> ShowS ApiMigrations -> String forall a. (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a showList :: [ApiMigrations] -> ShowS $cshowList :: [ApiMigrations] -> ShowS show :: ApiMigrations -> String $cshow :: ApiMigrations -> String showsPrec :: Int -> ApiMigrations -> ShowS $cshowsPrec :: Int -> ApiMigrations -> ShowS Show, forall x. Rep ApiMigrations x -> ApiMigrations forall x. ApiMigrations -> Rep ApiMigrations x forall a. (forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a $cto :: forall x. Rep ApiMigrations x -> ApiMigrations $cfrom :: forall x. ApiMigrations -> Rep ApiMigrations x Generic) instance DA.ToJSON ApiMigrations where toJSON :: ApiMigrations -> Value toJSON ApiMigrations am = [Pair] -> Value DA.object [ (Key "gdpr" :: DA.Key) forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv DA..= ApiMigrations -> Bool migrationGdpr ApiMigrations am , (Key "signed-install" :: DA.Key) forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv DA..= ApiMigrations -> Bool migrationSignedInstall ApiMigrations am ] -- | A helper method to generate a bare-bones Atlassian Connect add-on by providing only the absolutely required fields. -- You can then use Haskell record syntax to update the plugin with more details. For example: -- -- > (pluginDescriptor (PluginKey . pack $ "com.company.mycoolplugin") (fromJust . parseURI $ "http://mycoolplugin.company.com") (Authentication Jwt)) -- > { pluginName = Just . Name . pack $ "My Cool Plugin" -- > , pluginDescription = Just . pack $ "Chil and be cool, you have a plugin descriptor." -- > } pluginDescriptor :: PluginKey -- ^ The key for your add-on. -> URI -- ^ The base url for your add-on. -> Authentication -- ^ The authentication that your add-on requires. -> Plugin -- ^ A bare-bones Atlassian Connect descriptor. pluginDescriptor :: PluginKey -> URI -> Authentication -> Plugin pluginDescriptor PluginKey key' URI url' Authentication auth = Plugin { pluginKey :: PluginKey pluginKey = PluginKey key' , pluginBaseUrl :: URI pluginBaseUrl = URI url' , authentication :: Authentication authentication = Authentication auth , pluginName :: Maybe (Name Plugin) pluginName = forall a. Maybe a Nothing , pluginDescription :: Maybe Text pluginDescription = forall a. Maybe a Nothing , vendor :: Maybe Vendor vendor = forall a. Maybe a Nothing , apiVersion :: Maybe Text apiVersion = forall a. Maybe a Nothing , modules :: Maybe Modules modules = forall a. Maybe a Nothing , enableLicensing :: Maybe Bool enableLicensing = forall a. Maybe a Nothing , lifecycle :: Maybe Lifecycle lifecycle = forall a. Maybe a Nothing , links :: HashMap Text URI links = forall k v. HashMap k v HM.empty , scopes :: Maybe [ProductScope] scopes = forall a. Maybe a Nothing , apiMigrations :: Maybe ApiMigrations apiMigrations = forall a. Maybe a Nothing }