diff --git a/docs/README.md b/docs/README.md index 41c2f0e8..ad71755f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,4 +6,4 @@ This plugin supports a two-way integration between Mattermost and Jira. For a st This plugin supports Jira Core and Jira Software products for Server, Data Center, and Cloud platforms. It has been tested with versions 7 and 8. -**Support for multiple Jira instances is supported from Jira 3.0 for Mattermost Enterprise Edition E20 and configured using [Administrator Slash Commands](https://mattermost.gitbook.io/plugin-jira/administrator-guide/administrator-slash-commands).** +**Support for multiple Jira instances is available in Jira plugin v3.0 and later. The feature is configured using [Administrator Slash Commands](https://mattermost.gitbook.io/plugin-jira/administrator-guide/administrator-slash-commands). Note that versions v3.0.0 and v3.0.1 of the plugin require an E20 Enterprise license to use this feature. The feature is now available for Mattermost E20, Professional, and Enterprise licenses.** diff --git a/docs/development/environment.md b/docs/development/environment.md index 9943493c..a60bc47c 100644 --- a/docs/development/environment.md +++ b/docs/development/environment.md @@ -10,4 +10,6 @@ This plugin supports both Jira Server (self-hosted) and Jira Cloud instances. Th To test your changes against a local instance of Jira Server, you need [Docker](https://docs.docker.com/install) installed, then you can use the `docker-compose.yml` file in this repository to create a Jira instance. Simply run `docker-compose up` in the directory of the repository, and a new Jira server should start up and be available at http://localhost:8080. It can take a few minutes to start up due to Jira Server's startup processes. If the container fails to start with `exit code 137`, you may need to increase the amount of RAM you are allowing docker to use. +If you are contributing to a feature that requires multiple Jira instances to be installed, please enable [ServiceSettings.EnableDeveloper](https://docs.mattermost.com/configure/configuration-settings.html#enable-developer-mode) in your server's config in order to circumvent the Enterprise license requirement. + To test your changes against a Jira Cloud instance, we recommend starting a 14-day trial, if you don't have a Jira project to test against. More information can be found here: https://www.atlassian.com/software/jira/try. diff --git a/go.sum b/go.sum index 34a809a9..2b5d615d 100644 --- a/go.sum +++ b/go.sum @@ -127,6 +127,7 @@ github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwj github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/blevesearch/bleve v1.0.14/go.mod h1:e/LJTr+E7EaoVdkQZTfoz7dt4KoDNvDbLb8MSKuNTLQ= github.com/blevesearch/blevex v1.0.0/go.mod h1:2rNVqoG2BZI8t1/P1awgTKnGlx5MP9ZbtEciQaNhswc= @@ -877,6 +878,7 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.0.0 h1:UVQPSSmc3qtTi+zPPkCXvZX9VvW/xT/NsRvKfwY81a8= diff --git a/readme.md b/readme.md index 0416a3b7..24437e5a 100644 --- a/readme.md +++ b/readme.md @@ -2,7 +2,7 @@ This plugin supports a two-way integration between Mattermost and Jira. Jira Core and Jira Software products, for Server, Data Center, and Cloud platforms are supported. It has been tested with versions 7 and 8. -From v3.0 of this plugin, support for multiple Jira instances is offered for Mattermost Enterprise Edition E20. +For versions v3.0 and later of this plugin, support for multiple Jira instances is offered for Mattermost E20, Professionsal, and Enterprise Edition. Note that for versions v3.0.0 and v3.0.1 of this plugin, an E20 license is required to set up multiple Jira instances. Visit our [Jira Plugin Documentation](https://mattermost.gitbook.io/jira-plugin/) for guidance on installation, configuration, and usage. @@ -20,4 +20,4 @@ This repository is licensed under the Apache 2.0 License, except for the [server ## Development -This plugin contains both a server and web app portion. Read our documentation about the [Developer Workflow](https://developers.mattermost.com/extend/plugins/developer-workflow/) and [Developer Setup](https://developers.mattermost.com/extend/plugins/developer-setup/) for more information about developing and extending plugins. +Read our [development docs](https://mattermost.gitbook.io/plugin-jira/development/environment) for this project, as well as the [Developer Workflow](https://developers.mattermost.com/extend/plugins/developer-workflow/) and [Developer Setup](https://developers.mattermost.com/extend/plugins/developer-setup/) documentation for more information about developing and extending plugins. diff --git a/server/command.go b/server/command.go index aab29bf6..45cf8072 100644 --- a/server/command.go +++ b/server/command.go @@ -801,7 +801,7 @@ func executeInstanceInstallCloud(p *Plugin, c *plugin.Context, header *model.Com instances, _ := p.instanceStore.LoadInstances() if !p.enterpriseChecker.HasEnterpriseFeatures() { if instances != nil && len(instances.IDs()) > 0 { - return p.responsef(header, "You need a valid Mattermost Enterprise E20 License to install multiple Jira instances") + return p.responsef(header, licenseErrorString) } } diff --git a/server/command_test.go b/server/command_test.go index 65a9d3a8..68c95689 100644 --- a/server/command_test.go +++ b/server/command_test.go @@ -348,12 +348,11 @@ func TestPlugin_ExecuteCommand_Installation(t *testing.T) { isSendEphemeralPostCalled := false // add valid license - trueValue := true var license model.License - license.Features = &model.Features{} - license.Features.EnterprisePlugins = &trueValue + license.SkuShortName = "professional" api.On("GetLicense").Return(&license) + api.On("GetConfig").Return(&model.Config{}) api.On("RegisterCommand", mock.Anything).Return(nil) api.On("GetBundlePath").Return("", nil) api.On("SendEphemeralPost", mock.AnythingOfType("string"), mock.AnythingOfType("*model.Post")).Run(func(args mock.Arguments) { diff --git a/server/enterprise/LICENSE b/server/enterprise/LICENSE index c698c020..91f5c896 100644 --- a/server/enterprise/LICENSE +++ b/server/enterprise/LICENSE @@ -5,19 +5,19 @@ With regard to the Mattermost Software: This software and associated documentation files (the "Software") may only be used in production, if you (and any entity that you represent) have agreed to, -and are in compliance with, the Mattermost Terms of Service, available at -https://mattermost.com/enterprise-edition-terms/ (the “EE Terms”), or other -agreement governing the use of the Software, as agreed by you and Mattermost, -and otherwise have a valid Mattermost Enterprise E20 subscription for the -correct number of user seats. Subject to the foregoing sentence, you are free +and are in compliance with all of the following: (a) the Mattermost Terms of Service, available at +https://mattermost.com/enterprise-edition-terms/ (the “EE Terms”), (b) any other +agreement(s) governing the use of the Software, as agreed upon by you and Mattermost, +and (c) you otherwise have a valid license or Subscription for the +correct number of Registered Authorized Users of the Software. Subject to the foregoing, you are free to modify this Software and publish patches to the Software. You agree that Mattermost and/or its licensors (as applicable) retain all right, title and interest in and to all such modifications and/or patches, and all such modifications and/or patches may only be used, copied, modified, displayed, -distributed, or otherwise exploited with a valid Mattermost Enterprise E20 -Edition subscription for the correct number of user seats. Notwithstanding +distributed, or otherwise exploited with a valid license or Subscription for the correct number of +Registered Authorized Users of the Software. Notwithstanding the foregoing, you may copy and modify the Software for development and testing -purposes, without requiring a subscription. You agree that Mattermost and/or +purposes, without requiring a valid license or Subscription. You agree that Mattermost and/or its licensors (as applicable) retain all right, title and interest in and to all such modifications. You are not granted any other rights beyond what is expressly stated herein. Subject to the foregoing, it is forbidden to copy, diff --git a/server/enterprise/license.go b/server/enterprise/license.go index 7412db1f..16f2f551 100644 --- a/server/enterprise/license.go +++ b/server/enterprise/license.go @@ -1,6 +1,7 @@ package enterprise import ( + pluginapi "github.com/mattermost/mattermost-plugin-api" "github.com/mattermost/mattermost-server/v5/model" ) @@ -14,6 +15,7 @@ type enterpriseChecker struct { type PluginAPI interface { GetLicense() *model.License + GetConfig() *model.Config } func NewEnterpriseChecker(api PluginAPI) Checker { @@ -22,23 +24,19 @@ func NewEnterpriseChecker(api PluginAPI) Checker { } } +const ( + e20 = "E20" + professional = "professional" + enterprise = "enterprise" +) + func (e *enterpriseChecker) HasEnterpriseFeatures() bool { + config := e.api.GetConfig() license := e.api.GetLicense() - if license == nil { - return false - } - - if license.Features == nil { - return false - } - - if license.Features.EnterprisePlugins == nil { - return false - } - if !*license.Features.EnterprisePlugins { - return false + if license != nil && (license.SkuShortName == e20 || license.SkuShortName == enterprise || license.SkuShortName == professional) { + return true } - return true + return pluginapi.IsE20LicensedOrDevelopment(config, license) } diff --git a/server/instances.go b/server/instances.go index 3bc82651..cbb7ce62 100644 --- a/server/instances.go +++ b/server/instances.go @@ -14,6 +14,8 @@ import ( "github.com/mattermost/mattermost-plugin-jira/server/utils/types" ) +const licenseErrorString = "You need a valid Mattermost E20, Professional, or Enterprise License to install multiple Jira instances." + type Instances struct { *types.ValueSet // of *InstanceCommon, not Instance } @@ -134,7 +136,7 @@ func (p *Plugin) InstallInstance(instance Instance) error { func(instances *Instances) error { if !p.enterpriseChecker.HasEnterpriseFeatures() { if instances != nil && len(instances.IDs()) > 0 { - return errors.Errorf("You need a valid Mattermost Enterprise E20 License to install multiple Jira instances") + return errors.Errorf(licenseErrorString) } } diff --git a/server/instances_test.go b/server/instances_test.go index 6a1bf0a1..fb424fc9 100644 --- a/server/instances_test.go +++ b/server/instances_test.go @@ -21,58 +21,65 @@ func TestInstallInstance(t *testing.T) { license *model.License numInstances int expectError bool + devEnabled bool }{ - "0 preinstalled valid license": { + "0 preinstalled, valid license": { numInstances: 0, expectError: false, license: &model.License{ - Features: &model.Features{ - EnterprisePlugins: &trueValue, - }, + SkuShortName: "professional", }, }, - "0 preinstalled nil license": { + "0 preinstalled, nil license": { numInstances: 0, expectError: false, license: nil, }, - "0 preinstalled nil Features": { - numInstances: 0, + "1 preinstalled, professional license": { + numInstances: 1, expectError: false, - license: &model.License{}, + license: &model.License{ + SkuShortName: "professional", + }, }, - "0 preinstalled nil Features EnterprisePlugins": { - numInstances: 0, + "1 preinstalled, E10 license": { + numInstances: 1, + expectError: true, + license: &model.License{ + SkuShortName: "E10", + }, + }, + "1 preinstalled, E20 license": { + numInstances: 1, expectError: false, license: &model.License{ - Features: &model.Features{}, + SkuShortName: "E20", }, }, - "1 preinstalled valid license": { + "1 preinstalled, enterprise license": { numInstances: 1, expectError: false, license: &model.License{ - Features: &model.Features{ - EnterprisePlugins: &trueValue, - }, + SkuShortName: "enterprise", }, }, - "1 preinstalled nil license": { + "1 preinstalled, cloud starter license. should have error": { numInstances: 1, expectError: true, - license: nil, + license: &model.License{ + SkuShortName: "starter", + }, }, - "1 preinstalled nil Features": { + "1 preinstalled, dev mode": { numInstances: 1, - expectError: true, - license: &model.License{}, + expectError: false, + license: nil, + devEnabled: true, }, - "1 preinstalled nil Features EnterprisePlugins": { + "1 preinstalled nil license": { numInstances: 1, expectError: true, - license: &model.License{ - Features: &model.Features{}, - }, + license: nil, }, } { t.Run(name, func(t *testing.T) { @@ -82,8 +89,15 @@ func TestInstallInstance(t *testing.T) { p.enterpriseChecker = enterprise.NewEnterpriseChecker(api) p.instanceStore = p.getMockInstanceStoreKV(tc.numInstances) + conf := &model.Config{} + if tc.devEnabled { + conf.ServiceSettings.EnableDeveloper = &trueValue + conf.ServiceSettings.EnableTesting = &trueValue + } + api.On("KVGet", mock.Anything).Return(mock.Anything, nil) api.On("GetLicense").Return(tc.license) + api.On("GetConfig").Return(conf) api.On("UnregisterCommand", mock.Anything, mock.Anything).Return(nil) api.On("RegisterCommand", mock.Anything, mock.Anything).Return(nil) api.On("PublishWebSocketEvent", mock.Anything, mock.Anything, mock.Anything) @@ -103,7 +117,7 @@ func TestInstallInstance(t *testing.T) { err = p.InstallInstance(testInstance0) if tc.expectError { assert.NotNil(t, err) - expected := "You need a valid Mattermost Enterprise E20 License to install multiple Jira instances" + expected := "You need a valid Mattermost E20, Professional, or Enterprise License to install multiple Jira instances." assert.Equal(t, expected, err.Error()) } else { assert.Nil(t, err)