From 56fc5577a45efc6df5b10c0fa9c6119942dc5633 Mon Sep 17 00:00:00 2001 From: Meredith Date: Fri, 24 May 2019 11:18:36 -0700 Subject: [PATCH 1/6] add integration features to SentryAppDetailsModal --- .../modals/sentryAppDetailsModal.jsx | 118 ++++++++++++++---- 1 file changed, 94 insertions(+), 24 deletions(-) diff --git a/src/sentry/static/sentry/app/components/modals/sentryAppDetailsModal.jsx b/src/sentry/static/sentry/app/components/modals/sentryAppDetailsModal.jsx index fd3b39f61ac7..4371ea51f9f0 100644 --- a/src/sentry/static/sentry/app/components/modals/sentryAppDetailsModal.jsx +++ b/src/sentry/static/sentry/app/components/modals/sentryAppDetailsModal.jsx @@ -9,9 +9,31 @@ import PluginIcon from 'app/plugins/components/pluginIcon'; import SentryTypes from 'app/sentryTypes'; import space from 'app/styles/space'; import {t} from 'app/locale'; -import marked from 'app/utils/marked'; -export default class SentryAppDetailsModal extends React.Component { +import AsyncComponent from 'app/components/asyncComponent'; +import HookStore from 'app/stores/hookStore'; +import marked, {singleLineRenderer} from 'app/utils/marked'; +import InlineSvg from 'app/components/inlineSvg'; +import Tag from 'app/views/settings/components/tag'; + +const defaultFeatureGateComponents = { + IntegrationFeatures: p => + p.children({ + disabled: false, + disabledReason: null, + ungatedFeatures: p.features, + gatedFeatureGroups: [], + }), + FeatureList: p => ( + + ), +}; + +export default class SentryAppDetailsModal extends AsyncComponent { static propTypes = { sentryApp: SentryTypes.SentryApplication.isRequired, organization: SentryTypes.Organization.isRequired, @@ -20,10 +42,35 @@ export default class SentryAppDetailsModal extends React.Component { closeModal: PropTypes.func.isRequired, }; - render() { + getEndpoints() { + const {sentryApp} = this.props; + return [['featureData', `/sentry-apps/${sentryApp.slug}/features/`]]; + } + + featureTags(features) { + return features.map(feature => { + const feat = feature.featureGate.replace(/integrations/g, ''); + return {feat.replace(/-/g, ' ')}; + }); + } + + renderBody() { const {sentryApp, closeModal, onInstall, isInstalled, organization} = this.props; + const {featureData} = this.state; + // Prepare the features list + const features = (featureData || []).map(f => ({ + featureGate: f.featureGate, + description: ( + + ), + })); + + const featureListHooks = HookStore.get('integrations:feature-gates'); + featureListHooks.push(() => defaultFeatureGateComponents); const overview = sentryApp.overview || ''; + const {FeatureList, IntegrationFeatures} = featureListHooks[0](); + const featureProps = {organization, features}; return ( @@ -32,36 +79,43 @@ export default class SentryAppDetailsModal extends React.Component { {sentryApp.name} + {features.length && this.featureTags(features)} + {t('By %s', sentryApp.author)} -
- - - - {({hasAccess}) => - hasAccess && ( - - ) - } - -
+ + {({disabled, disabledReason}) => ( +
+ {disabled && } + + + + {({hasAccess}) => + hasAccess && ( + + ) + } + +
+ )} +
); } @@ -95,3 +149,19 @@ const Metadata = styled(Flex)` const Author = styled(Box)` color: ${p => p.theme.gray2}; `; + +const DisabledNotice = styled(({reason, ...p}) => ( + + + {reason} + +))` + color: ${p => p.theme.red}; + font-size: 0.9em; +`; + +const StyledTag = styled(Tag)` + &:not(:first-child) { + margin-left: ${space(0.5)}; + } +`; From 68fcb798ebdb62a724515e26eff1c9c5cdbbae8b Mon Sep 17 00:00:00 2001 From: Meredith Date: Wed, 5 Jun 2019 11:09:11 -0700 Subject: [PATCH 2/6] update wording --- src/sentry/models/integrationfeature.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sentry/models/integrationfeature.py b/src/sentry/models/integrationfeature.py index 0a3b9513f5ec..0a06fe2dc99a 100644 --- a/src/sentry/models/integrationfeature.py +++ b/src/sentry/models/integrationfeature.py @@ -33,15 +33,15 @@ def as_str(cls, feature): return 'integrations-event-hooks' @classmethod - def description(cls, feature): + def description(cls, feature, name): if feature == cls.API: - return "This integration can utilize the Sentry API (with the permissions granted) to pull data or update resources in Sentry!" + return "%s can **utilize the Sentry API** to pull data or update resources in Sentry (with permissions granted, of course)." % name elif feature == cls.ISSUE_LINK: - return "This integration can allow your organization to create or link Sentry issues to another service!" + return "Organizations can **create or link Sentry issues** to another service." elif feature == cls.STACKTRACE_LINK: - return "This integration allows your organization to open a line in Sentry's stack trace in another service!" + return "Organizations can **open a line to Sentry's stack trace** in another service." elif feature == cls.EVENT_HOOKS: - return "This integration allows your organization to forward events to another service!" + return "%s allows organizations to **forward events to another service**." % name class IntegrationFeature(Model): @@ -68,4 +68,4 @@ def description(self): if self.user_description: return self.user_description else: - return Feature.description(self.feature) + return Feature.description(self.feature, self.sentry_app.name) From e4de880a28fb64f9e6cc7aafa60ed62053ed81c1 Mon Sep 17 00:00:00 2001 From: Meredith Date: Fri, 14 Jun 2019 11:46:52 -0700 Subject: [PATCH 3/6] js tests --- .../modals/sentryAppDetailsModal.jsx | 2 +- .../sentryApplicationRow.jsx | 2 +- .../modals/sentryAppDetailsModal.spec.jsx | 7 ++++++ .../sentryAppInstallations.spec.jsx | 23 ++++++++++++++++++- .../sentry/models/test_integrationfeature.py | 6 ++--- 5 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/sentry/static/sentry/app/components/modals/sentryAppDetailsModal.jsx b/src/sentry/static/sentry/app/components/modals/sentryAppDetailsModal.jsx index 4371ea51f9f0..bdeaac8dd320 100644 --- a/src/sentry/static/sentry/app/components/modals/sentryAppDetailsModal.jsx +++ b/src/sentry/static/sentry/app/components/modals/sentryAppDetailsModal.jsx @@ -12,7 +12,7 @@ import {t} from 'app/locale'; import AsyncComponent from 'app/components/asyncComponent'; import HookStore from 'app/stores/hookStore'; -import marked, {singleLineRenderer} from 'app/utils/marked'; +import {singleLineRenderer} from 'app/utils/marked'; import InlineSvg from 'app/components/inlineSvg'; import Tag from 'app/views/settings/components/tag'; diff --git a/src/sentry/static/sentry/app/views/settings/organizationDeveloperSettings/sentryApplicationRow.jsx b/src/sentry/static/sentry/app/views/settings/organizationDeveloperSettings/sentryApplicationRow.jsx index 249b5afbe8ca..2a2937f96df5 100644 --- a/src/sentry/static/sentry/app/views/settings/organizationDeveloperSettings/sentryApplicationRow.jsx +++ b/src/sentry/static/sentry/app/views/settings/organizationDeveloperSettings/sentryApplicationRow.jsx @@ -130,7 +130,7 @@ export default class SentryApplicationRow extends React.PureComponent { {!isInstalled ? (