diff --git a/api/chalicelib/core/integration_github.py b/api/chalicelib/core/integration_github.py index a05c946f4..2de8cc518 100644 --- a/api/chalicelib/core/integration_github.py +++ b/api/chalicelib/core/integration_github.py @@ -1,8 +1,9 @@ +import schemas from chalicelib.core import integration_base from chalicelib.core.integration_github_issue import GithubIntegrationIssue from chalicelib.utils import pg_client, helper -PROVIDER = "GITHUB" +PROVIDER = schemas.IntegrationType.github class GitHubIntegration(integration_base.BaseIntegration): diff --git a/api/chalicelib/core/integration_jira_cloud.py b/api/chalicelib/core/integration_jira_cloud.py index 7d8c956cf..469723e4e 100644 --- a/api/chalicelib/core/integration_jira_cloud.py +++ b/api/chalicelib/core/integration_jira_cloud.py @@ -1,8 +1,9 @@ +import schemas from chalicelib.core import integration_base from chalicelib.core.integration_jira_cloud_issue import JIRACloudIntegrationIssue from chalicelib.utils import pg_client, helper -PROVIDER = "JIRA" +PROVIDER = schemas.IntegrationType.jira def obfuscate_string(string): diff --git a/api/chalicelib/core/integrations_global.py b/api/chalicelib/core/integrations_global.py new file mode 100644 index 000000000..5b00a28bd --- /dev/null +++ b/api/chalicelib/core/integrations_global.py @@ -0,0 +1,61 @@ +import schemas +from chalicelib.utils import pg_client + + +def get_global_integrations_status(tenant_id, user_id, project_id): + with pg_client.PostgresClient() as cur: + cur.execute( + cur.mogrify(f"""\ + SELECT EXISTS((SELECT 1 + FROM public.oauth_authentication + WHERE user_id = %(user_id)s + AND provider = 'github')) AS {schemas.IntegrationType.github}, + EXISTS((SELECT 1 + FROM public.jira_cloud + WHERE user_id = %(user_id)s)) AS {schemas.IntegrationType.jira}, + EXISTS((SELECT 1 + FROM public.integrations + WHERE project_id=%(project_id)s + AND provider='bugsnag')) AS {schemas.IntegrationType.bugsnag}, + EXISTS((SELECT 1 + FROM public.integrations + WHERE project_id=%(project_id)s + AND provider='cloudwatch')) AS {schemas.IntegrationType.cloudwatch}, + EXISTS((SELECT 1 + FROM public.integrations + WHERE project_id=%(project_id)s + AND provider='datadog')) AS {schemas.IntegrationType.datadog}, + EXISTS((SELECT 1 + FROM public.integrations + WHERE project_id=%(project_id)s + AND provider='newrelic')) AS {schemas.IntegrationType.newrelic}, + EXISTS((SELECT 1 + FROM public.integrations + WHERE project_id=%(project_id)s + AND provider='rollbar')) AS {schemas.IntegrationType.rollbar}, + EXISTS((SELECT 1 + FROM public.integrations + WHERE project_id=%(project_id)s + AND provider='sentry')) AS {schemas.IntegrationType.sentry}, + EXISTS((SELECT 1 + FROM public.integrations + WHERE project_id=%(project_id)s + AND provider='stackdriver')) AS {schemas.IntegrationType.stackdriver}, + EXISTS((SELECT 1 + FROM public.integrations + WHERE project_id=%(project_id)s + AND provider='sumologic')) AS {schemas.IntegrationType.sumologic}, + EXISTS((SELECT 1 + FROM public.integrations + WHERE project_id=%(project_id)s + AND provider='elasticsearch')) AS {schemas.IntegrationType.elasticsearch}, + EXISTS((SELECT 1 + FROM public.webhooks + WHERE type='slack')) AS {schemas.IntegrationType.slack};""", + {"user_id": user_id, "tenant_id": tenant_id, "project_id": project_id}) + ) + current_integrations = cur.fetchone() + result = [] + for k in current_integrations.keys(): + result.append({"name": k, "integrated": current_integrations[k]}) + return result diff --git a/api/routers/core.py b/api/routers/core.py index 2c3ff5b90..f9629d8bb 100644 --- a/api/routers/core.py +++ b/api/routers/core.py @@ -12,7 +12,7 @@ from chalicelib.core import log_tool_rollbar, sourcemaps, events, sessions_assig log_tool_cloudwatch, log_tool_sentry, log_tool_sumologic, log_tools, errors, sessions, \ log_tool_newrelic, announcements, log_tool_bugsnag, weekly_report, integration_jira_cloud, integration_github, \ assist, heatmaps, mobile, signup, tenants, errors_favorite_viewed, boarding, notifications, webhook, users, \ - custom_metrics, saved_search + custom_metrics, saved_search, integrations_global from chalicelib.core.collaboration_slack import Slack from chalicelib.utils import email_helper, helper, captcha from chalicelib.utils.TimeUTC import TimeUTC @@ -180,6 +180,14 @@ def session_top_filter_values(projectId: int, context: schemas.CurrentContext = return {'data': sessions_metas.get_top_key_values(projectId)} +@app.get('/{projectId}/integrations', tags=["integrations"]) +def get_integrations_status(projectId: int, context: schemas.CurrentContext = Depends(OR_context)): + data = integrations_global.get_global_integrations_status(tenant_id=context.tenant_id, + user_id=context.user_id, + project_id=projectId) + return {"data": data} + + @app.post('/{projectId}/integrations/{integration}/notify/{integrationId}/{source}/{sourceId}', tags=["integrations"]) @app.put('/{projectId}/integrations/{integration}/notify/{integrationId}/{source}/{sourceId}', tags=["integrations"]) def integration_notify(projectId: int, integration: str, integrationId: int, source: str, sourceId: str, diff --git a/api/schemas.py b/api/schemas.py index 314a0f7d2..81d2bffab 100644 --- a/api/schemas.py +++ b/api/schemas.py @@ -1024,7 +1024,7 @@ class LiveFilterType(str, Enum): user_UUID = "USERUUID" tracker_version = "TRACKERVERSION" user_browser_version = "USERBROWSERVERSION" - user_device_type = "USERDEVICETYPE", + user_device_type = "USERDEVICETYPE" class LiveSessionSearchFilterSchema(BaseModel): @@ -1068,3 +1068,18 @@ class LiveSessionsSearchPayloadSchema(_PaginatedSchema): class Config: alias_generator = attribute_to_camel_case + + +class IntegrationType(str, Enum): + github = "GITHUB" + jira = "JIRA" + slack = "SLACK" + sentry = "SENTRY" + bugsnag = "BUGSNAG" + rollbar = "ROLLBAR" + elasticsearch = "ELASTICSEARCH" + datadog = "DATADOG" + sumologic = "SUMOLOGIC" + stackdriver = "STACKDRIVER" + cloudwatch = "CLOUDWATCH" + newrelic = "NEWRELIC" diff --git a/ee/api/chalicelib/core/integrations_global.py b/ee/api/chalicelib/core/integrations_global.py new file mode 100644 index 000000000..b923fc5ab --- /dev/null +++ b/ee/api/chalicelib/core/integrations_global.py @@ -0,0 +1,61 @@ +import schemas +from chalicelib.utils import pg_client + + +def get_global_integrations_status(tenant_id, user_id, project_id): + with pg_client.PostgresClient() as cur: + cur.execute( + cur.mogrify(f"""\ + SELECT EXISTS((SELECT 1 + FROM public.oauth_authentication + WHERE user_id = %(user_id)s + AND provider = 'github')) AS {schemas.IntegrationType.github}, + EXISTS((SELECT 1 + FROM public.jira_cloud + WHERE user_id = %(user_id)s)) AS {schemas.IntegrationType.jira}, + EXISTS((SELECT 1 + FROM public.integrations + WHERE project_id=%(project_id)s + AND provider='bugsnag')) AS {schemas.IntegrationType.bugsnag}, + EXISTS((SELECT 1 + FROM public.integrations + WHERE project_id=%(project_id)s + AND provider='cloudwatch')) AS {schemas.IntegrationType.cloudwatch}, + EXISTS((SELECT 1 + FROM public.integrations + WHERE project_id=%(project_id)s + AND provider='datadog')) AS {schemas.IntegrationType.datadog}, + EXISTS((SELECT 1 + FROM public.integrations + WHERE project_id=%(project_id)s + AND provider='newrelic')) AS {schemas.IntegrationType.newrelic}, + EXISTS((SELECT 1 + FROM public.integrations + WHERE project_id=%(project_id)s + AND provider='rollbar')) AS {schemas.IntegrationType.rollbar}, + EXISTS((SELECT 1 + FROM public.integrations + WHERE project_id=%(project_id)s + AND provider='sentry')) AS {schemas.IntegrationType.sentry}, + EXISTS((SELECT 1 + FROM public.integrations + WHERE project_id=%(project_id)s + AND provider='stackdriver')) AS {schemas.IntegrationType.stackdriver}, + EXISTS((SELECT 1 + FROM public.integrations + WHERE project_id=%(project_id)s + AND provider='sumologic')) AS {schemas.IntegrationType.sumologic}, + EXISTS((SELECT 1 + FROM public.integrations + WHERE project_id=%(project_id)s + AND provider='elasticsearch')) AS {schemas.IntegrationType.elasticsearch}, + EXISTS((SELECT 1 + FROM public.webhooks + WHERE type='slack' AND tenant_id=%(tenant_id)s)) AS {schemas.IntegrationType.slack};""", + {"user_id": user_id, "tenant_id": tenant_id, "project_id": project_id}) + ) + current_integrations = cur.fetchone() + result = [] + for k in current_integrations.keys(): + result.append({"name": k, "integrated": current_integrations[k]}) + return result diff --git a/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql b/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql index e236ce90e..dfae901c5 100644 --- a/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql +++ b/ee/scripts/helm/db/init_dbs/postgresql/init_schema.sql @@ -659,24 +659,24 @@ $$ ); CREATE UNIQUE INDEX IF NOT EXISTS autocomplete_unique_project_id_md5value_type_idx ON autocomplete (project_id, md5(value), type); - CREATE index IF NOT EXISTS autocomplete_project_id_idx ON autocomplete (project_id); + CREATE INDEX IF NOT EXISTS autocomplete_project_id_idx ON autocomplete (project_id); CREATE INDEX IF NOT EXISTS autocomplete_type_idx ON public.autocomplete (type); - CREATE INDEX autocomplete_value_clickonly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'CLICK'; - CREATE INDEX autocomplete_value_customonly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'CUSTOM'; - CREATE INDEX autocomplete_value_graphqlonly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'GRAPHQL'; - CREATE INDEX autocomplete_value_inputonly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'INPUT'; - CREATE INDEX autocomplete_value_locationonly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'LOCATION'; - CREATE INDEX autocomplete_value_referreronly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'REFERRER'; - CREATE INDEX autocomplete_value_requestonly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'REQUEST'; - CREATE INDEX autocomplete_value_revidonly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'REVID'; - CREATE INDEX autocomplete_value_stateactiononly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'STATEACTION'; - CREATE INDEX autocomplete_value_useranonymousidonly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'USERANONYMOUSID'; - CREATE INDEX autocomplete_value_userbrowseronly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'USERBROWSER'; - CREATE INDEX autocomplete_value_usercountryonly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'USERCOUNTRY'; - CREATE INDEX autocomplete_value_userdeviceonly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'USERDEVICE'; - CREATE INDEX autocomplete_value_useridonly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'USERID'; - CREATE INDEX autocomplete_value_userosonly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'USEROS'; + CREATE INDEX IF NOT EXISTS autocomplete_value_clickonly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'CLICK'; + CREATE INDEX IF NOT EXISTS autocomplete_value_customonly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'CUSTOM'; + CREATE INDEX IF NOT EXISTS autocomplete_value_graphqlonly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'GRAPHQL'; + CREATE INDEX IF NOT EXISTS autocomplete_value_inputonly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'INPUT'; + CREATE INDEX IF NOT EXISTS autocomplete_value_locationonly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'LOCATION'; + CREATE INDEX IF NOT EXISTS autocomplete_value_referreronly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'REFERRER'; + CREATE INDEX IF NOT EXISTS autocomplete_value_requestonly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'REQUEST'; + CREATE INDEX IF NOT EXISTS autocomplete_value_revidonly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'REVID'; + CREATE INDEX IF NOT EXISTS autocomplete_value_stateactiononly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'STATEACTION'; + CREATE INDEX IF NOT EXISTS autocomplete_value_useranonymousidonly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'USERANONYMOUSID'; + CREATE INDEX IF NOT EXISTS autocomplete_value_userbrowseronly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'USERBROWSER'; + CREATE INDEX IF NOT EXISTS autocomplete_value_usercountryonly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'USERCOUNTRY'; + CREATE INDEX IF NOT EXISTS autocomplete_value_userdeviceonly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'USERDEVICE'; + CREATE INDEX IF NOT EXISTS autocomplete_value_useridonly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'USERID'; + CREATE INDEX IF NOT EXISTS autocomplete_value_userosonly_gin_idx ON public.autocomplete USING GIN (value gin_trgm_ops) WHERE type = 'USEROS'; BEGIN IF NOT EXISTS(SELECT *