diff --git a/plugins/Amplitude/v1/configValidation.json b/plugins/Amplitude/v1/configValidation.json new file mode 100644 index 00000000..b1a5380b --- /dev/null +++ b/plugins/Amplitude/v1/configValidation.json @@ -0,0 +1,13 @@ +{ + "steps": [ + { + "displayName": "Authenticate", + "dataStream": { + "name": "validateAuth" + }, + "required": true, + "error": "Could not authenticate with Amplitude. Check that your API key and secret key are correct, and that the correct region (US/EU) is selected.", + "success": "Connected to Amplitude successfully." + } + ] +} diff --git a/plugins/Amplitude/v1/custom_types.json b/plugins/Amplitude/v1/custom_types.json new file mode 100644 index 00000000..02086400 --- /dev/null +++ b/plugins/Amplitude/v1/custom_types.json @@ -0,0 +1,16 @@ +[ + { + "name": "Amplitude Event Type", + "sourceType": "Amplitude Event Type", + "icon": "bolt", + "singular": "Event Type", + "plural": "Event Types" + }, + { + "name": "Amplitude Cohort", + "sourceType": "Amplitude Cohort", + "icon": "users", + "singular": "Cohort", + "plural": "Cohorts" + } +] diff --git a/plugins/Amplitude/v1/dataStreams/activeUsersHistory.json b/plugins/Amplitude/v1/dataStreams/activeUsersHistory.json new file mode 100644 index 00000000..739f4355 --- /dev/null +++ b/plugins/Amplitude/v1/dataStreams/activeUsersHistory.json @@ -0,0 +1,55 @@ +{ + "name": "activeUsersHistory", + "displayName": "Active Users History", + "description": "Daily active or new user counts over time", + "tags": ["Analytics", "Users"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "api/2/users", + "getArgs": [ + { "key": "start", "value": "{{timeframe.start.slice(0,10).replace(/-/g,'')}}" }, + { "key": "end", "value": "{{timeframe.end.slice(0,10).replace(/-/g,'')}}" }, + { "key": "i", "value": "1" }, + { "key": "m", "value": "{{metric}}" } + ], + "postRequestScript": "activeUsersHistory.js" + }, + "matches": "none", + "ui": [ + { + "type": "choiceChips", + "name": "metric", + "label": "Metric", + "options": [ + { "value": "active", "label": "Active Users" }, + { "value": "new", "label": "New Users" } + ], + "default": "active" + } + ], + "metadata": [ + { + "name": "date", + "displayName": "Date", + "shape": "date", + "role": "timestamp" + }, + { + "name": "users", + "displayName": "Users", + "shape": "number", + "role": "value" + } + ], + "timeframes": [ + "last7days", + "last30days", + "thisMonth", + "lastMonth", + "thisQuarter", + "lastQuarter", + "thisYear", + "lastYear" + ] +} diff --git a/plugins/Amplitude/v1/dataStreams/cohortEventHistory.json b/plugins/Amplitude/v1/dataStreams/cohortEventHistory.json new file mode 100644 index 00000000..ff6d8139 --- /dev/null +++ b/plugins/Amplitude/v1/dataStreams/cohortEventHistory.json @@ -0,0 +1,58 @@ +{ + "name": "cohortEventHistory", + "displayName": "Cohort Event History", + "description": "Daily count of a chosen event fired by a cohort's members", + "tags": ["Analytics", "Events", "Cohorts"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "api/2/events/segmentation", + "getArgs": [ + { "key": "e", "value": "{{JSON.stringify({event_type: eventType && eventType[0] && eventType[0].value})}}" }, + { "key": "s", "value": "{{JSON.stringify([{prop: 'userdata_cohort', op: 'is', values: [object.rawId]}])}}" }, + { "key": "start", "value": "{{timeframe.start.slice(0,10).replace(/-/g,'')}}" }, + { "key": "end", "value": "{{timeframe.end.slice(0,10).replace(/-/g,'')}}" }, + { "key": "i", "value": "1" }, + { "key": "m", "value": "totals" } + ], + "postRequestScript": "cohortEventHistory.js" + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Amplitude Cohort"] } }, + "ui": [ + { + "type": "autocomplete", + "name": "eventType", + "label": "Event", + "data": { + "source": "dataStream", + "dataStreamName": "eventTypes", + "dataSourceConfig": {} + }, + "validation": { "required": true } + } + ], + "metadata": [ + { + "name": "date", + "displayName": "Date", + "shape": "date", + "role": "timestamp" + }, + { + "name": "eventCount", + "displayName": "Event Count", + "shape": "number", + "role": "value" + } + ], + "timeframes": [ + "last7days", + "last30days", + "thisMonth", + "lastMonth", + "thisQuarter", + "lastQuarter", + "thisYear", + "lastYear" + ] +} diff --git a/plugins/Amplitude/v1/dataStreams/cohorts.json b/plugins/Amplitude/v1/dataStreams/cohorts.json new file mode 100644 index 00000000..44d245eb --- /dev/null +++ b/plugins/Amplitude/v1/dataStreams/cohorts.json @@ -0,0 +1,63 @@ +{ + "name": "cohorts", + "displayName": "Cohorts", + "description": "Behavioral cohorts defined in the project", + "tags": ["Analytics"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "api/3/cohorts", + "pathToData": "cohorts" + }, + "matches": "none", + "metadata": [ + { + "name": "id", + "displayName": "ID", + "visible": false + }, + { + "name": "name", + "displayName": "Name", + "role": "label" + }, + { + "name": "description", + "displayName": "Description" + }, + { + "name": "size", + "displayName": "Size", + "shape": "number" + }, + { + "name": "type", + "displayName": "Cohort Type", + "visible": false + }, + { + "name": "published", + "displayName": "Published", + "shape": "boolean" + }, + { + "name": "archived", + "displayName": "Archived", + "shape": "boolean" + }, + { + "name": "lastComputed", + "displayName": "Last Computed", + "shape": "date" + }, + { + "name": "createdAt", + "displayName": "Created", + "shape": "date" + } + ], + "timeframes": false, + "visibility": { + "type": "hidden" + } +} diff --git a/plugins/Amplitude/v1/dataStreams/eventCountHistory.json b/plugins/Amplitude/v1/dataStreams/eventCountHistory.json new file mode 100644 index 00000000..2d670929 --- /dev/null +++ b/plugins/Amplitude/v1/dataStreams/eventCountHistory.json @@ -0,0 +1,71 @@ +{ + "name": "eventCountHistory", + "displayName": "Event Count History", + "description": "Daily count of a specific event's occurrences", + "tags": ["Analytics", "Events"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "api/2/events/segmentation", + "getArgs": [ + { "key": "e", "value": "{{JSON.stringify(groupByProperty && groupByProperty[0] ? {event_type: object.rawId, group_by: [{type: 'event', value: groupByProperty[0].value}]} : {event_type: object.rawId})}}" }, + { "key": "start", "value": "{{timeframe.start.slice(0,10).replace(/-/g,'')}}" }, + { "key": "end", "value": "{{timeframe.end.slice(0,10).replace(/-/g,'')}}" }, + { "key": "i", "value": "1" }, + { "key": "m", "value": "{{metric}}" } + ], + "postRequestScript": "eventCountHistory.js" + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Amplitude Event Type"] } }, + "ui": [ + { + "type": "choiceChips", + "name": "metric", + "label": "Metric", + "options": [ + { "value": "totals", "label": "Total Events" }, + { "value": "uniques", "label": "Unique Users" } + ], + "default": "totals" + }, + { + "type": "autocomplete", + "name": "groupByProperty", + "label": "Group by property (optional)", + "data": { + "source": "dataStream", + "dataStreamName": "eventProperties", + "dataSourceConfig": {} + } + } + ], + "metadata": [ + { + "name": "date", + "displayName": "Date", + "shape": "date", + "role": "timestamp" + }, + { + "name": "eventCount", + "displayName": "Event Count", + "shape": "number", + "role": "value" + }, + { + "name": "groupValue", + "displayName": "Property Value", + "shape": "string" + } + ], + "timeframes": [ + "last7days", + "last30days", + "thisMonth", + "lastMonth", + "thisQuarter", + "lastQuarter", + "thisYear", + "lastYear" + ] +} diff --git a/plugins/Amplitude/v1/dataStreams/eventProperties.json b/plugins/Amplitude/v1/dataStreams/eventProperties.json new file mode 100644 index 00000000..65524a92 --- /dev/null +++ b/plugins/Amplitude/v1/dataStreams/eventProperties.json @@ -0,0 +1,41 @@ +{ + "name": "eventProperties", + "displayName": "Event Properties", + "description": "Event properties defined in the project's taxonomy schema", + "tags": ["Analytics"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "api/2/taxonomy/event-property", + "pathToData": "data", + "expandInnerObjects": true + }, + "matches": "none", + "metadata": [ + { + "name": "event_property", + "displayName": "Event Property", + "visible": false, + "role": "value" + }, + { + "name": "name", + "displayName": "Name", + "computed": true, + "valueExpression": "{{ $['event_property'] }}", + "role": "label" + }, + { + "name": "description", + "displayName": "Description" + }, + { + "name": "type", + "displayName": "Type" + } + ], + "timeframes": false, + "visibility": { + "type": "hidden" + } +} diff --git a/plugins/Amplitude/v1/dataStreams/eventTypes.json b/plugins/Amplitude/v1/dataStreams/eventTypes.json new file mode 100644 index 00000000..372c6ba1 --- /dev/null +++ b/plugins/Amplitude/v1/dataStreams/eventTypes.json @@ -0,0 +1,51 @@ +{ + "name": "eventTypes", + "displayName": "Event Types", + "description": "Event types defined in the project's taxonomy schema", + "tags": ["Analytics"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "api/2/taxonomy/event", + "pathToData": "data", + "expandInnerObjects": true + }, + "matches": "none", + "metadata": [ + { + "name": "event_type", + "displayName": "Event Type", + "visible": false, + "role": "value" + }, + { + "name": "display_name", + "displayName": "Display Name", + "visible": false + }, + { + "name": "name", + "displayName": "Name", + "computed": true, + "valueExpression": "{{ $['display_name']?.__isNone ? $['event_type'] : $['display_name'] }}", + "role": "label" + }, + { + "name": "description", + "displayName": "Description" + }, + { + "name": "category.name", + "displayName": "Category" + }, + { + "name": "is_active", + "displayName": "Active", + "shape": "boolean" + } + ], + "timeframes": false, + "visibility": { + "type": "hidden" + } +} diff --git a/plugins/Amplitude/v1/dataStreams/realtimeActiveUsers.json b/plugins/Amplitude/v1/dataStreams/realtimeActiveUsers.json new file mode 100644 index 00000000..70409f03 --- /dev/null +++ b/plugins/Amplitude/v1/dataStreams/realtimeActiveUsers.json @@ -0,0 +1,28 @@ +{ + "name": "realtimeActiveUsers", + "displayName": "Real-time Active Users", + "description": "Current real-time active user count", + "tags": ["Analytics", "Users"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "api/2/realtime", + "postRequestScript": "realtimeActiveUsers.js" + }, + "matches": "none", + "metadata": [ + { + "name": "activeUsers", + "displayName": "Real-time Active Users", + "shape": "number", + "role": "value" + }, + { + "name": "asOf", + "displayName": "As of", + "shape": "string", + "visible": true + } + ], + "timeframes": false +} diff --git a/plugins/Amplitude/v1/dataStreams/scripts/activeUsersHistory.js b/plugins/Amplitude/v1/dataStreams/scripts/activeUsersHistory.js new file mode 100644 index 00000000..d4a113bf --- /dev/null +++ b/plugins/Amplitude/v1/dataStreams/scripts/activeUsersHistory.js @@ -0,0 +1,7 @@ +// dataStreams/scripts/activeUsersHistory.js +// Amplitude returns two parallel arrays (dates + values) rather than one row +// per date, so a post-request script is required to zip them together. +const xValues = (data.data && data.data.xValues) || []; +const series = (data.data && data.data.series && data.data.series[0]) || []; + +result = xValues.map((date, i) => ({ date, users: series[i] })); diff --git a/plugins/Amplitude/v1/dataStreams/scripts/cohortEventHistory.js b/plugins/Amplitude/v1/dataStreams/scripts/cohortEventHistory.js new file mode 100644 index 00000000..ce539ee7 --- /dev/null +++ b/plugins/Amplitude/v1/dataStreams/scripts/cohortEventHistory.js @@ -0,0 +1,7 @@ +// dataStreams/scripts/cohortEventHistory.js +// Amplitude returns two parallel arrays (dates + values) rather than one row +// per date, so a post-request script is required to zip them together. +const xValues = (data.data && data.data.xValues) || []; +const series = (data.data && data.data.series && data.data.series[0]) || []; + +result = xValues.map((date, i) => ({ date, eventCount: series[i] })); diff --git a/plugins/Amplitude/v1/dataStreams/scripts/eventCountHistory.js b/plugins/Amplitude/v1/dataStreams/scripts/eventCountHistory.js new file mode 100644 index 00000000..133db821 --- /dev/null +++ b/plugins/Amplitude/v1/dataStreams/scripts/eventCountHistory.js @@ -0,0 +1,46 @@ +// dataStreams/scripts/eventCountHistory.js +// Amplitude returns two/three parallel arrays (dates + one series per group +// + group labels) rather than one row per date, so a post-request script is +// required to zip them together. +// +// Ungrouped (no groupByProperty set): data.series has exactly one array +// (one row per date, groupValue omitted) - this is today's existing, +// unchanged behavior. +// +// Grouped (groupByProperty set, via an embedded `group_by` on the `e` param): +// data.series has one array per distinct property value, and +// data.seriesLabels holds the label for each series - as an +// [eventIndex, label] tuple (since group_by is embedded per-event), not a +// bare string. Zip each series against xValues and tag every row with its +// group's label. +// +// Whether the request was grouped is read from context.config.groupByProperty +// (the tile's own selection), not from series.length - a grouped property +// that only has one distinct value in range still returns exactly one +// series, and series.length alone can't tell that apart from "ungrouped". +const xValues = (data.data && data.data.xValues) || []; +const series = (data.data && data.data.series) || []; +const seriesLabels = (data.data && data.data.seriesLabels) || []; + +const groupByProperty = context.config && context.config.groupByProperty; +const isGrouped = Boolean(groupByProperty && groupByProperty[0] && groupByProperty[0].value); + +if (!isGrouped) { + // Ungrouped - identical output shape to before this change. + const values = series[0] || []; + result = xValues.map((date, i) => ({ + date, + eventCount: values[i], + groupValue: null, + })); +} else { + result = series.flatMap((values, seriesIndex) => { + const label = seriesLabels[seriesIndex]; + const groupValue = Array.isArray(label) ? label[1] : label; + return xValues.map((date, i) => ({ + date, + eventCount: values[i], + groupValue, + })); + }); +} diff --git a/plugins/Amplitude/v1/dataStreams/scripts/realtimeActiveUsers.js b/plugins/Amplitude/v1/dataStreams/scripts/realtimeActiveUsers.js new file mode 100644 index 00000000..ce0a8d9d --- /dev/null +++ b/plugins/Amplitude/v1/dataStreams/scripts/realtimeActiveUsers.js @@ -0,0 +1,17 @@ +// dataStreams/scripts/realtimeActiveUsers.js +// api/2/realtime returns rolling ~2 days of data at 5-minute granularity, +// with xValues as time-of-day labels and series[0] as "today". Later buckets +// for today may be null since the day isn't over yet - find the last non-null +// value to get the current active user count. +const xValues = (data.data && data.data.xValues) || []; +const todaySeries = (data.data && data.data.series && data.data.series[0]) || []; + +let idx = -1; +for (let i = todaySeries.length - 1; i >= 0; i--) { + if (todaySeries[i] !== null && todaySeries[i] !== undefined) { + idx = i; + break; + } +} + +result = idx >= 0 ? [{ asOf: xValues[idx], activeUsers: todaySeries[idx] }] : [{ asOf: null, activeUsers: 0 }]; diff --git a/plugins/Amplitude/v1/dataStreams/userActivity.json b/plugins/Amplitude/v1/dataStreams/userActivity.json new file mode 100644 index 00000000..0d8d3fc6 --- /dev/null +++ b/plugins/Amplitude/v1/dataStreams/userActivity.json @@ -0,0 +1,70 @@ +{ + "name": "userActivity", + "displayName": "User Activity", + "description": "Recent event activity for a specific user", + "tags": ["Analytics", "Users"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "api/2/useractivity", + "getArgs": [ + { "key": "user", "value": "{{amplitudeId}}" }, + { "key": "limit", "value": "100" } + ], + "pathToData": "events" + }, + "matches": "none", + "ui": [ + { + "name": "amplitudeId", + "label": "Amplitude ID", + "type": "text", + "placeholder": "123456789", + "help": "The numeric Amplitude ID of the user to look up (not their email or User ID). Find it using the **User Search** tile first, then copy the **Amplitude ID** value from the matching row here.", + "validation": { + "required": true + } + } + ], + "metadata": [ + { + "name": "event_time", + "displayName": "Time", + "shape": "date", + "role": "timestamp" + }, + { + "name": "event_type", + "displayName": "Event", + "role": "label" + }, + { + "name": "device_id", + "displayName": "Device ID" + }, + { + "name": "platform", + "displayName": "Platform" + }, + { + "name": "city", + "displayName": "City" + }, + { + "name": "country", + "displayName": "Country" + }, + { + "name": "event_properties", + "displayName": "Event Properties", + "shape": "json" + }, + { + "name": "user_properties", + "displayName": "User Properties", + "shape": "json" + }, + { "pattern": ".*" } + ], + "timeframes": false +} diff --git a/plugins/Amplitude/v1/dataStreams/userSearch.json b/plugins/Amplitude/v1/dataStreams/userSearch.json new file mode 100644 index 00000000..0b1afa48 --- /dev/null +++ b/plugins/Amplitude/v1/dataStreams/userSearch.json @@ -0,0 +1,40 @@ +{ + "name": "userSearch", + "displayName": "User Search", + "description": "Matches user, device, or Amplitude IDs against a search string", + "tags": ["Analytics", "Users"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "api/2/usersearch", + "getArgs": [{ "key": "user", "value": "{{identifier}}" }], + "pathToData": "matches" + }, + "matches": "none", + "ui": [ + { + "name": "identifier", + "label": "User ID / Device ID / Email prefix", + "type": "text", + "placeholder": "user@example.com", + "help": "Search for a user by their User ID, Device ID, or an email/ID prefix. Copy the **Amplitude ID** from a matching row into the **User Activity** tile's parameter to see that user's event timeline.", + "validation": { + "required": true + } + } + ], + "metadata": [ + { + "name": "user_id", + "displayName": "User ID", + "role": "label" + }, + { + "name": "amplitude_id", + "displayName": "Amplitude ID", + "shape": "number", + "role": "value" + } + ], + "timeframes": false +} diff --git a/plugins/Amplitude/v1/dataStreams/validateAuth.json b/plugins/Amplitude/v1/dataStreams/validateAuth.json new file mode 100644 index 00000000..0b9a5da2 --- /dev/null +++ b/plugins/Amplitude/v1/dataStreams/validateAuth.json @@ -0,0 +1,17 @@ +{ + "name": "validateAuth", + "displayName": "Authentication Validation", + "description": "Returns the project's taxonomy event categories", + "tags": ["Utility"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "api/2/taxonomy/category" + }, + "matches": "none", + "metadata": [{ "pattern": ".*" }], + "timeframes": false, + "visibility": { + "type": "hidden" + } +} diff --git a/plugins/Amplitude/v1/defaultContent/cohortOverview.dash.json b/plugins/Amplitude/v1/defaultContent/cohortOverview.dash.json new file mode 100644 index 00000000..b50a4329 --- /dev/null +++ b/plugins/Amplitude/v1/defaultContent/cohortOverview.dash.json @@ -0,0 +1,101 @@ +{ + "name": "Cohort", + "schemaVersion": "1.5", + "timeframe": "last30days", + "variables": ["{{variables.[Amplitude Cohort]}}"], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "1cc9ddc5-eecc-40bc-804d-eb0e211128ef", + "x": 0, + "y": 0, + "w": 1, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Cohort Details", + "description": "", + "timeframe": "none", + "variables": ["{{variables.[Amplitude Cohort]}}"], + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "datastream-properties", + "name": "properties", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Amplitude Cohorts]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Amplitude Cohort]}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": true, + "columnOrder": [ + "name", + "description", + "size", + "published", + "archived", + "lastComputed", + "createdAt" + ], + "hiddenColumns": ["id", "link", "links", "label", "sourceId", "sourceType", "cohortType"] + } + } + } + } + }, + { + "i": "2d787a1a-f55b-4702-b138-136b64cbb70f", + "x": 1, + "y": 0, + "w": 3, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Cohort Event History", + "description": "", + "variables": ["{{variables.[Amplitude Cohort]}}"], + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[cohortEventHistory]}}", + "name": "cohortEventHistory", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Amplitude Cohorts]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Amplitude Cohort]}}" + }, + "visualisation": { + "type": "data-stream-line-graph", + "config": { + "data-stream-line-graph": { + "xAxisColumn": "date", + "yAxisColumn": ["eventCount"], + "seriesColumn": "none", + "showLegend": false, + "legendPosition": "bottom", + "yAxisLabel": "Events", + "showYAxisLabel": true, + "showTrendLine": false + } + } + } + } + } + ] + } +} diff --git a/plugins/Amplitude/v1/defaultContent/eventTypeOverview.dash.json b/plugins/Amplitude/v1/defaultContent/eventTypeOverview.dash.json new file mode 100644 index 00000000..18511793 --- /dev/null +++ b/plugins/Amplitude/v1/defaultContent/eventTypeOverview.dash.json @@ -0,0 +1,154 @@ +{ + "name": "Event Type", + "schemaVersion": "1.5", + "timeframe": "last30days", + "variables": ["{{variables.[Amplitude Event Type]}}"], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "4b94c2a0-5067-485d-985b-22feff45dea5", + "x": 0, + "y": 0, + "w": 1, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Event Type Details", + "description": "", + "timeframe": "none", + "variables": ["{{variables.[Amplitude Event Type]}}"], + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "datastream-properties", + "name": "properties", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Amplitude Event Types]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Amplitude Event Type]}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": true, + "columnOrder": ["name", "description", "category", "is_active"], + "hiddenColumns": ["id", "link", "links", "label", "sourceId", "sourceType"] + } + } + } + } + }, + { + "i": "7cf3f9d6-6b24-471d-b20e-2372d587dfc5", + "x": 1, + "y": 0, + "w": 3, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Event Count History", + "description": "", + "variables": ["{{variables.[Amplitude Event Type]}}"], + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[eventCountHistory]}}", + "name": "eventCountHistory", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "metric": "totals" + } + }, + "scope": { + "scope": "{{scopes.[Amplitude Event Types]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Amplitude Event Type]}}" + }, + "visualisation": { + "type": "data-stream-line-graph", + "config": { + "data-stream-line-graph": { + "xAxisColumn": "date", + "yAxisColumn": ["eventCount"], + "seriesColumn": "none", + "showLegend": false, + "legendPosition": "bottom", + "yAxisLabel": "Events", + "showYAxisLabel": true, + "showTrendLine": false + } + } + } + } + }, + { + "i": "48ad4c07-009b-4a9a-95ea-336fefdcd761", + "x": 0, + "y": 3, + "w": 4, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Event Breakdown by Property", + "description": "", + "variables": ["{{variables.[Amplitude Event Type]}}"], + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[eventCountHistory]}}", + "name": "eventCountHistory", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "metric": "totals" + }, + "group": { + "by": [["groupValue", "uniqueValues"]], + "aggregate": [{ "type": "sum", "names": ["eventCount"] }] + }, + "sort": { "by": [["eventCount_sum", "desc"]], "top": 10 } + }, + "scope": { + "scope": "{{scopes.[Amplitude Event Types]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Amplitude Event Type]}}" + }, + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisData": "groupValue_uniqueValues", + "yAxisData": ["eventCount_sum"], + "xAxisGroup": "none", + "xAxisLabel": "", + "yAxisLabel": "", + "showXAxisLabel": true, + "showYAxisLabel": true, + "showLegend": false, + "legendPosition": "bottom", + "showGrid": true, + "horizontalLayout": "vertical", + "displayMode": "actual", + "showTotals": false, + "showValue": false, + "grouping": false, + "range": { "type": "auto" } + } + } + } + } + } + ] + } +} diff --git a/plugins/Amplitude/v1/defaultContent/manifest.json b/plugins/Amplitude/v1/defaultContent/manifest.json new file mode 100644 index 00000000..0e35e788 --- /dev/null +++ b/plugins/Amplitude/v1/defaultContent/manifest.json @@ -0,0 +1,7 @@ +{ + "items": [ + { "name": "overview", "type": "dashboard" }, + { "name": "eventTypeOverview", "type": "dashboard" }, + { "name": "cohortOverview", "type": "dashboard" } + ] +} diff --git a/plugins/Amplitude/v1/defaultContent/overview.dash.json b/plugins/Amplitude/v1/defaultContent/overview.dash.json new file mode 100644 index 00000000..e5e2ea4a --- /dev/null +++ b/plugins/Amplitude/v1/defaultContent/overview.dash.json @@ -0,0 +1,237 @@ +{ + "name": "Overview", + "schemaVersion": "1.5", + "timeframe": "last30days", + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "34c5570e-cbc0-43b4-881b-ccd4ad562177", + "x": 0, + "y": 0, + "w": 3, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Active Users Trend", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[activeUsersHistory]}}", + "name": "activeUsersHistory", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "metric": "active" + } + }, + "visualisation": { + "type": "data-stream-line-graph", + "config": { + "data-stream-line-graph": { + "xAxisColumn": "date", + "yAxisColumn": ["users"], + "seriesColumn": "none", + "showLegend": false, + "legendPosition": "bottom", + "yAxisLabel": "Active users", + "showYAxisLabel": true, + "showTrendLine": false + } + } + } + } + }, + { + "i": "35a44773-e55c-4242-8746-5c807f0b8830", + "x": 3, + "y": 0, + "w": 1, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Real-time Active Users", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[realtimeActiveUsers]}}", + "name": "realtimeActiveUsers", + "pluginConfigId": "{{configId}}" + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "activeUsers", + "comparisonColumn": "none" + } + } + } + } + }, + { + "i": "00663308-6093-4c0d-b53c-0bccb9d6cabf", + "x": 0, + "y": 3, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Event Types", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[eventTypes]}}", + "name": "eventTypes", + "pluginConfigId": "{{configId}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": ["name", "description", "category.name", "is_active"] + } + } + } + } + }, + { + "i": "49be72d4-6ec6-4e06-90e5-8681f5bc9373", + "x": 2, + "y": 3, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Cohorts", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[cohorts]}}", + "name": "cohorts", + "pluginConfigId": "{{configId}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": ["name", "description", "size", "published"], + "hiddenColumns": ["archived", "lastComputed", "createdAt"] + } + } + } + } + }, + { + "i": "fdacd88d-155e-4367-83a3-7f24613a5886", + "x": 0, + "y": 6, + "w": 4, + "h": 1, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/text", + "title": "", + "description": "", + "visualisation": { + "config": { + "content": "User Lookup — search below to find a user's Amplitude ID, then paste it into the User Activity tile to see their event timeline", + "fontSize": 16, + "align": "left", + "autoSize": true + } + } + } + }, + { + "i": "23a3535f-a6c8-41c4-a7ae-53c3f982b7ef", + "x": 0, + "y": 7, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "1. User Search", + "description": "Search for a user, then copy their Amplitude ID into the User Activity tile", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[userSearch]}}", + "name": "userSearch", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "identifier": "" + } + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": ["user_id", "amplitude_id"] + } + } + } + } + }, + { + "i": "3ee601a5-5009-4421-a48a-a733739164c0", + "x": 2, + "y": 7, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "2. User Activity", + "description": "Paste an Amplitude ID from the User Search tile to see that user's recent events", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[userActivity]}}", + "name": "userActivity", + "pluginConfigId": "{{configId}}", + "dataSourceConfig": { + "amplitudeId": "" + } + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": ["event_time", "event_type", "platform", "city", "country", "device_id"], + "hiddenColumns": ["event_properties", "user_properties"] + } + } + } + } + } + ] + } +} diff --git a/plugins/Amplitude/v1/defaultContent/scopes.json b/plugins/Amplitude/v1/defaultContent/scopes.json new file mode 100644 index 00000000..26bb3e49 --- /dev/null +++ b/plugins/Amplitude/v1/defaultContent/scopes.json @@ -0,0 +1,26 @@ +[ + { + "name": "Amplitude Event Types", + "matches": { + "sourceType": { "type": "oneOf", "values": ["Amplitude Event Type"] } + }, + "variable": { + "name": "Amplitude Event Type", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + }, + { + "name": "Amplitude Cohorts", + "matches": { + "sourceType": { "type": "oneOf", "values": ["Amplitude Cohort"] } + }, + "variable": { + "name": "Amplitude Cohort", + "allowMultipleSelection": false, + "default": "none", + "type": "object" + } + } +] diff --git a/plugins/Amplitude/v1/docs/README.md b/plugins/Amplitude/v1/docs/README.md new file mode 100644 index 00000000..879656e4 --- /dev/null +++ b/plugins/Amplitude/v1/docs/README.md @@ -0,0 +1,45 @@ +# Amplitude + +Monitor your [Amplitude](https://amplitude.com) product analytics project in SquaredUp. This plugin imports your project's event types and behavioral cohorts into the SquaredUp graph, and provides data streams for active user trends, event volume trends, cohort-filtered event activity, and individual user activity lookups. + +## What this plugin monitors + +- **Event Types** and **Cohorts** are imported as objects you can scope dashboards to, search for, and drill into. +- An **Overview** dashboard shows account-wide **active/new user trends**, **real-time active users**, a table of tracked **event types**, and a table of **cohorts**, plus a **user lookup** tile. +- An **Event Type** perspective shows the **event count trend** for that specific event. +- A **Cohort** perspective shows the cohort's **current size and status**, plus an **event trend filtered to that cohort's members**. +- A two-step **user lookup** workflow: search for a user by User ID, Device ID, or email prefix in the **User Search** tile, copy the **Amplitude ID** it returns, then paste it into the **User Activity** tile to see that user's recent event activity. These are two separate tiles because SquaredUp's dashboard framework has no way to automatically pass a search result into another tile's parameter — this is a manual copy/paste step by design, not a bug. + +## Prerequisites — getting your API key and secret key + +Amplitude API credentials are scoped to a single project, so each SquaredUp plugin instance monitors one Amplitude project. + +1. Sign in to Amplitude and open the project you want to monitor. +2. Go to **Settings → Projects**, select the project, then open its **General** settings tab. +3. Copy the **API Key** and **Secret Key** shown there. +4. Note whether your organization uses Amplitude's **US** or **EU** data center — this is usually visible in the URL you sign in at (`analytics.amplitude.com` for US, `analytics.eu.amplitude.com` for EU) or in your organization's data residency settings. + +No additional scopes need to be granted — the API key/secret key pair has access to all the read-only analytics endpoints this plugin uses. + +## Configuration fields + +| Field | What it is | Where to find it | Required | +| -------------- | ------------------------------------------------------------ | -------------------------------------------------- | -------- | +| **Region** | The data residency region your Amplitude project uses (US or EU). | The domain you sign in at, or your org's data residency settings. | Yes | +| **API Key** | Your Amplitude project's API key, used as the Basic Auth username. | Settings → Projects → [project] → General. | Yes | +| **Secret Key** | Your Amplitude project's secret key, used as the Basic Auth password. | Settings → Projects → [project] → General. | Yes | + +## What gets indexed + +| Object type | Represents | +| ------------------------ | ------------------------------------------------------------------------ | +| **Amplitude Event Type** | An event defined in the project's taxonomy schema. | +| **Amplitude Cohort** | A behavioral cohort (user segment) defined in the project. | + +## Known limitations + +- **One project per plugin instance.** Amplitude API keys are project-scoped, so monitoring multiple projects means adding this plugin multiple times, once per project. +- **Rate limits.** Amplitude enforces a concurrency limit of 5 requests across the Dashboard REST/Cohort APIs and a cost-based hourly limit. Very frequent refreshes of many tiles at once may be throttled (HTTP 429). +- **Funnel and retention analysis are not included.** These require multi-step chart configuration that doesn't map cleanly to a generic tile — this version covers active users, event trends, cohorts, and user lookups only. +- **User lookup is a manual two-step process.** There is no "browse all users" endpoint in Amplitude, and Amplitude's user-activity endpoint only accepts a numeric Amplitude ID (not an email or User ID directly) — so you search first in the User Search tile, then copy the Amplitude ID it returns into the User Activity tile yourself. SquaredUp's dashboard framework has no mechanism to bind a search result automatically into another tile's parameter. +- **Hidden taxonomy items are excluded.** Events or properties marked as hidden/visibility-restricted in Amplitude's taxonomy don't appear in the imported Event Types. diff --git a/plugins/Amplitude/v1/icon.svg b/plugins/Amplitude/v1/icon.svg new file mode 100644 index 00000000..7ed2e719 --- /dev/null +++ b/plugins/Amplitude/v1/icon.svg @@ -0,0 +1,18 @@ + + + + + + + diff --git a/plugins/Amplitude/v1/indexDefinitions/default.json b/plugins/Amplitude/v1/indexDefinitions/default.json new file mode 100644 index 00000000..acd33e6c --- /dev/null +++ b/plugins/Amplitude/v1/indexDefinitions/default.json @@ -0,0 +1,46 @@ +{ + "steps": [ + { + "name": "eventTypes", + "dataStream": { + "name": "eventTypes" + }, + "timeframe": "none", + "objectMapping": { + "id": "event_type", + "name": "name", + "type": { + "value": "Amplitude Event Type" + }, + "properties": [ + "description", + { "category": "category.name" }, + "is_active" + ] + } + }, + { + "name": "cohorts", + "dataStream": { + "name": "cohorts" + }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "name", + "type": { + "value": "Amplitude Cohort" + }, + "properties": [ + "description", + "size", + { "cohortType": "type" }, + "published", + "archived", + "lastComputed", + "createdAt" + ] + } + } + ] +} diff --git a/plugins/Amplitude/v1/metadata.json b/plugins/Amplitude/v1/metadata.json new file mode 100644 index 00000000..12af1f99 --- /dev/null +++ b/plugins/Amplitude/v1/metadata.json @@ -0,0 +1,50 @@ +{ + "name": "amplitude", + "displayName": "Amplitude", + "version": "1.1.0", + "author": { + "name": "conor.richey", + "type": "community" + }, + "description": "Monitor your Amplitude project in SquaredUp - track active users, event trends, cohorts, and look up individual user activity.", + "category": "Analytics", + "type": "hybrid", + "schemaVersion": "2.1", + "importNotSupported": false, + "restrictedToPlatforms": [], + "keywords": [ + "amplitude", + "analytics", + "product analytics", + "events", + "cohorts", + "active users", + "user activity" + ], + "objectTypes": [ + "Amplitude Event Type", + "Amplitude Cohort" + ], + "links": [ + { + "category": "documentation", + "url": "https://github.com/squaredup/plugins/blob/main/plugins/Amplitude/v1/docs/README.md", + "label": "Help adding this plugin" + }, + { + "category": "source", + "url": "https://github.com/squaredup/plugins/tree/main/plugins/Amplitude", + "label": "Repository" + } + ], + "base": { + "plugin": "WebAPI", + "majorVersion": "1", + "config": { + "baseUrl": "{{region === 'eu' ? 'https://analytics.eu.amplitude.com' : 'https://amplitude.com'}}", + "authMode": "basic", + "basicAuthUsername": "{{apiKey}}", + "basicAuthPassword": "{{secretKey}}" + } + } +} diff --git a/plugins/Amplitude/v1/ui.json b/plugins/Amplitude/v1/ui.json new file mode 100644 index 00000000..18ba7b1b --- /dev/null +++ b/plugins/Amplitude/v1/ui.json @@ -0,0 +1,42 @@ +[ + { + "type": "radio", + "name": "region", + "label": "Region", + "help": "The data residency region for your Amplitude project. Choose EU if your project uses Amplitude's EU data center, otherwise US.", + "defaultValue": "us", + "options": [ + { + "value": "us", + "label": "US (amplitude.com)" + }, + { + "value": "eu", + "label": "EU (analytics.eu.amplitude.com)" + } + ], + "validation": { + "required": true + } + }, + { + "type": "password", + "name": "apiKey", + "label": "API Key", + "placeholder": "1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p", + "help": "Your Amplitude project's API key. Find it at **Settings → Projects → [your project] → General**.", + "validation": { + "required": true + } + }, + { + "type": "password", + "name": "secretKey", + "label": "Secret Key", + "placeholder": "0z9y8x7w6v5u4t3s2r1q0p9o8n7m6l5k", + "help": "Your Amplitude project's secret key, found alongside the API key at **Settings → Projects → [your project] → General**.", + "validation": { + "required": true + } + } +]