diff --git a/src/sentry/api/bases/sentryapps.py b/src/sentry/api/bases/sentryapps.py
index 85f395bfa547..3e5bcc4af5f2 100644
--- a/src/sentry/api/bases/sentryapps.py
+++ b/src/sentry/api/bases/sentryapps.py
@@ -131,9 +131,10 @@ def has_object_permission(self, request, view, sentry_app):
if is_active_superuser(request):
return True
- # User must be in the Org who owns the app.
- if sentry_app.owner not in request.user.get_orgs():
- raise Http404
+ # if app is unpublished, user must be in the Org who owns the app.
+ if not sentry_app.is_published:
+ if sentry_app.owner not in request.user.get_orgs():
+ raise Http404
return ensure_scoped_permission(
request,
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)
diff --git a/src/sentry/static/sentry/app/components/modals/sentryAppDetailsModal.jsx b/src/sentry/static/sentry/app/components/modals/sentryAppDetailsModal.jsx
index fd3b39f61ac7..2956175cbd78 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 => (
+