diff --git a/packages/gooddata-sdk/src/gooddata_sdk/__init__.py b/packages/gooddata-sdk/src/gooddata_sdk/__init__.py index 91f87c918..e3cc5d4a1 100644 --- a/packages/gooddata-sdk/src/gooddata_sdk/__init__.py +++ b/packages/gooddata-sdk/src/gooddata_sdk/__init__.py @@ -133,6 +133,9 @@ from gooddata_sdk.catalog.organization.layout.export_template import ( CatalogDeclarativeExportTemplate, ) +from gooddata_sdk.catalog.organization.layout.ip_allowlist_policy import ( + CatalogDeclarativeIpAllowlistPolicy, +) from gooddata_sdk.catalog.organization.layout.notification_channel import ( CatalogDeclarativeNotificationChannel, CatalogWebhook, diff --git a/packages/gooddata-sdk/src/gooddata_sdk/catalog/organization/layout/ip_allowlist_policy.py b/packages/gooddata-sdk/src/gooddata_sdk/catalog/organization/layout/ip_allowlist_policy.py new file mode 100644 index 000000000..74f0407d5 --- /dev/null +++ b/packages/gooddata-sdk/src/gooddata_sdk/catalog/organization/layout/ip_allowlist_policy.py @@ -0,0 +1,22 @@ +# (C) 2025 GoodData Corporation +from __future__ import annotations + +import builtins + +from attrs import define, field +from gooddata_api_client.model.declarative_ip_allowlist_policy import DeclarativeIpAllowlistPolicy + +from gooddata_sdk.catalog.base import Base +from gooddata_sdk.catalog.identifier import CatalogDeclarativeUserGroupIdentifier, CatalogUserIdentifier + + +@define(kw_only=True) +class CatalogDeclarativeIpAllowlistPolicy(Base): + id: str + allowed_sources: list[str] = field(factory=list) + user_groups: list[CatalogDeclarativeUserGroupIdentifier] = field(factory=list) + users: list[CatalogUserIdentifier] = field(factory=list) + + @staticmethod + def client_class() -> builtins.type[DeclarativeIpAllowlistPolicy]: + return DeclarativeIpAllowlistPolicy diff --git a/packages/gooddata-sdk/src/gooddata_sdk/catalog/organization/service.py b/packages/gooddata-sdk/src/gooddata_sdk/catalog/organization/service.py index c05de1350..b79d3c9a6 100644 --- a/packages/gooddata-sdk/src/gooddata_sdk/catalog/organization/service.py +++ b/packages/gooddata-sdk/src/gooddata_sdk/catalog/organization/service.py @@ -31,6 +31,7 @@ ) from gooddata_sdk.catalog.organization.entity_model.setting import CatalogOrganizationSetting from gooddata_sdk.catalog.organization.layout.identity_provider import CatalogDeclarativeIdentityProvider +from gooddata_sdk.catalog.organization.layout.ip_allowlist_policy import CatalogDeclarativeIpAllowlistPolicy from gooddata_sdk.catalog.organization.layout.notification_channel import CatalogDeclarativeNotificationChannel from gooddata_sdk.client import GoodDataApiClient from gooddata_sdk.utils import load_all_entities, load_all_entities_dict @@ -742,3 +743,35 @@ def switch_active_identity_provider(self, identity_provider_id: str) -> None: ) except Exception as e: raise ValueError(f"Error switching active identity provider: {str(e)}") + + def get_declarative_ip_allowlist_policies(self) -> list[CatalogDeclarativeIpAllowlistPolicy]: + """Get all declarative IP allowlist policies for the current organization. + + IP allowlist policies are returned as part of the full organization layout. + + Returns: + list[CatalogDeclarativeIpAllowlistPolicy]: + List of declarative IP allowlist policies. + """ + org_layout = self._layout_api.get_organization_layout(_check_return_type=False) + policies = getattr(org_layout, "ip_allowlist_policies", None) or [] + return [CatalogDeclarativeIpAllowlistPolicy.from_api(policy) for policy in policies] + + def put_declarative_ip_allowlist_policies( + self, ip_allowlist_policies: list[CatalogDeclarativeIpAllowlistPolicy] + ) -> None: + """Put declarative IP allowlist policies for the current organization. + + Reads the full organization layout, replaces the ip_allowlist_policies field, + and writes the full layout back. All other organization settings are preserved. + + Args: + ip_allowlist_policies (list[CatalogDeclarativeIpAllowlistPolicy]): + List of declarative IP allowlist policies to set. + + Returns: + None + """ + org_layout = self._layout_api.get_organization_layout(_check_return_type=False) + org_layout.ip_allowlist_policies = [policy.to_api() for policy in ip_allowlist_policies] + self._layout_api.set_organization_layout(org_layout, _check_return_type=False) diff --git a/packages/gooddata-sdk/tests/catalog/fixtures/organization/layout_ip_allowlist_policies.yaml b/packages/gooddata-sdk/tests/catalog/fixtures/organization/layout_ip_allowlist_policies.yaml new file mode 100644 index 000000000..7c5cbac33 --- /dev/null +++ b/packages/gooddata-sdk/tests/catalog/fixtures/organization/layout_ip_allowlist_policies.yaml @@ -0,0 +1,21487 @@ +interactions: + - request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - br, gzip, deflate + X-GDC-VALIDATE-RELATIONS: + - 'true' + X-Requested-With: + - XMLHttpRequest + method: GET + uri: http://localhost:3000/api/v1/layout/organization + response: + body: + string: + agents: + - aiKnowledge: true + availableToAll: true + enabled: true + id: default-ai-assistant + name: Default GoodData AI Assistant + skillsMode: all + customGeoCollections: [] + dataSources: + - alternativeDataSourceId: ds-put-abc-id + authenticationType: USERNAME_PASSWORD + id: demo-test-ds + name: demo-test-ds + permissions: + - assignee: + id: demo2 + type: user + name: MANAGE + - assignee: + id: demoGroup + type: userGroup + name: USE + schema: demo + type: POSTGRESQL + url: jdbc:postgresql://postgres:5432/tiger?sslmode=prefer + username: postgres + exportTemplates: [] + identityProviders: + - id: defaultIdp + idpType: DEX_IDP + ipAllowlistPolicies: [] + jwks: [] + notificationChannels: [] + organization: + allowedOrigins: [] + colorPalettes: [] + cspDirectives: [] + earlyAccess: enableAlerting + earlyAccessValues: + - enableAlerting + - enableSmtp + - enablePreAggregationDatasets + - enableScheduling + - enableCompositeGrain + - enableUserManagement + - enableRawExports + - enableFlexibleDashboardLayout + hostname: localhost + id: default + identityProvider: + id: defaultIdp + type: identityProvider + name: Default Organization + permissions: + - assignee: + id: adminGroup + type: userGroup + name: MANAGE + settings: [] + themes: [] + userGroups: + - id: adminGroup + permissions: [] + - id: adminQA1Group + parents: + - id: adminGroup + type: userGroup + permissions: [] + - id: demoGroup + name: demo group + permissions: [] + - id: visitorsGroup + name: visitors + parents: + - id: demoGroup + type: userGroup + permissions: [] + users: + - id: admin + permissions: [] + settings: [] + userGroups: + - id: adminGroup + type: userGroup + - authId: NORMALIZED_AUTH_ID + email: demo@example.com + firstname: Demo + id: demo + lastname: User + permissions: [] + settings: [] + userGroups: + - id: adminGroup + type: userGroup + - authId: NORMALIZED_AUTH_ID + id: demo2 + permissions: [] + settings: [] + userGroups: + - id: demoGroup + type: userGroup + workspaceDataFilters: + - columnName: wdf__region + id: wdf__region + title: Customer region + workspace: + id: demo + type: workspace + workspaceDataFilterSettings: + - filterValues: + - West + id: region_west + title: Region West + workspace: + id: demo_west + type: workspace + - columnName: wdf__state + id: wdf__state + title: Customer state + workspace: + id: demo_west + type: workspace + workspaceDataFilterSettings: + - filterValues: + - California + id: region_west_california + title: Region West California + workspace: + id: demo_west_california + type: workspace + workspaces: + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: + - assignee: + id: demo2 + type: user + name: MANAGE + - assignee: + id: demoGroup + type: userGroup + name: ANALYZE + id: demo + model: + analytics: + analyticalDashboardExtensions: [] + analyticalDashboards: + - content: + filterContextRef: + identifier: + id: campaign_name_filter + type: filterContext + layout: + sections: + - header: + description: The first insight shows a breakdown of + spend by category and campaign. The second shows + revenue per $ spend, for each campaign, to demonstrate, + how campaigns are successful. + title: Spend breakdown and Revenue + items: + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: campaign_spend + type: visualizationObject + properties: {} + title: Campaign Spend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: revenue_per_usd_vs_spend_by_campaign + type: visualizationObject + properties: {} + title: Revenue per $ vs Spend by Campaign + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: campaign + permissions: + - assigneeRule: + type: allWorkspaceUsers + name: VIEW + title: Campaign + - content: + filterContextRef: + identifier: + id: campaign_name_filter + type: filterContext + layout: + sections: + - items: + - size: + xl: + gridWidth: 12 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: top_10_products + type: visualizationObject + properties: {} + title: DHO simple + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + plugins: + - plugin: + identifier: + id: dashboard_plugin_1 + type: dashboardPlugin + version: '2' + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: dashboard_plugin + title: Dashboard plugin + - content: + filterContextRef: + identifier: + id: region_filter + type: filterContext + layout: + sections: + - items: + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: top_10_products + type: visualizationObject + properties: {} + title: Top 10 Products + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: revenue_trend + type: visualizationObject + properties: {} + title: Revenue Trend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: customers_trend + type: visualizationObject + properties: {} + title: Customers Trend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_categories_pie_chart + type: visualizationObject + properties: {} + title: Product Categories Pie Chart + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_breakdown + type: visualizationObject + properties: {} + title: Product Breakdown + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_saleability + type: visualizationObject + properties: {} + title: Product Saleability + type: insight + - size: + xl: + gridWidth: 12 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: percent_revenue_per_product_by_customer_and_category + type: visualizationObject + properties: {} + title: '% Revenue per Product by Customer and + Category' + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: product_and_category + title: Product & Category + attributeHierarchies: [] + dashboardPlugins: + - content: + url: https://www.example.com + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: Testing record dashboard_plugin_1 + id: dashboard_plugin_1 + title: dashboard_plugin_1 + - content: + url: https://www.example.com + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: Testing record dashboard_plugin_2 + id: dashboard_plugin_2 + title: dashboard_plugin_2 + exportDefinitions: [] + filterContexts: + - content: + filters: + - dateFilter: + from: '0' + granularity: GDC.time.month + to: '0' + type: relative + - attributeFilter: + attributeElements: + uris: [] + displayForm: + identifier: + id: campaign_name + type: label + filterElementsBy: [] + localIdentifier: 14b0807447ef4bc28f43e4fc5c337d1d + negativeSelection: true + version: '2' + description: '' + id: campaign_name_filter + title: filterContext + - content: + filters: + - attributeFilter: + attributeElements: + uris: [] + displayForm: + identifier: + id: region + type: label + filterElementsBy: [] + localIdentifier: 2d5ef8df82444f6ba27b45f0990ee6af + negativeSelection: true + version: '2' + description: '' + id: region_filter + title: filterContext + memoryItems: [] + metrics: + - content: + format: '#,##0' + maql: SELECT COUNT({attribute/customer_id},{attribute/order_line_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_active_customers + title: '# of Active Customers' + - content: + format: '#,##0' + maql: SELECT COUNT({attribute/order_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_orders + title: '# of Orders' + - content: + format: '#,##0' + maql: 'SELECT {metric/amount_of_active_customers} WHERE (SELECT + {metric/revenue} BY {attribute/customer_id}) > 10000 ' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_top_customers + title: '# of Top Customers' + - content: + format: '#,##0.00' + maql: SELECT {metric/amount_of_orders} WHERE NOT ({label/order_status} + IN ("Returned", "Canceled")) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: amount_of_valid_orders + title: '# of Valid Orders' + - content: + format: $#,##0 + maql: SELECT SUM({fact/spend}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: campaign_spend + title: Campaign Spend + - content: + format: $#,##0 + maql: SELECT SUM({fact/price}*{fact/quantity}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: order_amount + title: Order Amount + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / {metric/total_revenue} + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue + title: '% Revenue' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10}\ + \ BY {attribute/customer_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_customers + title: '% Revenue from Top 10 Customers' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10_percent}\ + \ BY {attribute/customer_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_percent_customers + title: '% Revenue from Top 10% Customers' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10_percent}\ + \ BY {attribute/product_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_percent_products + title: '% Revenue from Top 10% Products' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10}\ + \ BY {attribute/product_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_products + title: '% Revenue from Top 10 Products' + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / (SELECT {metric/revenue} BY + {attribute/products.category}, ALL OTHER) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_in_category + title: '% Revenue in Category' + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / (SELECT {metric/revenue} BY + ALL {attribute/product_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_per_product + title: '% Revenue per Product' + - content: + format: $#,##0 + maql: SELECT {metric/order_amount} WHERE NOT ({label/order_status} + IN ("Returned", "Canceled")) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: revenue + title: Revenue + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Clothing") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-clothing + title: Revenue (Clothing) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ( "Electronics") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-electronic + title: Revenue (Electronic) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Home") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-home + title: Revenue (Home) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Outdoor") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-outdoor + title: Revenue (Outdoor) + - content: + format: $#,##0.0 + maql: SELECT AVG(SELECT {metric/revenue} BY {attribute/customer_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_customer + title: Revenue per Customer + - content: + format: $#,##0.0 + maql: SELECT {metric/revenue} / {metric/campaign_spend} + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_dollar_spent + title: Revenue per Dollar Spent + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE TOP(10) OF ({metric/revenue}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_top_10 + title: Revenue / Top 10 + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE TOP(10%) OF ({metric/revenue}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_top_10_percent + title: Revenue / Top 10% + - content: + format: $#,##0 + maql: SELECT {metric/revenue} BY ALL OTHER + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: total_revenue + title: Total Revenue + - content: + format: $#,##0 + maql: SELECT {metric/total_revenue} WITHOUT PARENT FILTER + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: total_revenue-no_filters + title: Total Revenue (No Filters) + parameters: [] + visualizationObjects: + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: campaign_spend + type: metric + localIdentifier: d319bcb2d8c04442a684e3b3cd063381 + title: Campaign Spend + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: campaign_channels.category + type: label + localIdentifier: 291c085e7df8420db84117ca49f59c49 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: campaign_name + type: label + localIdentifier: d9dd143d647d4d148405a60ec2cf59bc + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: type + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_channels.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_name + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:treemap + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: campaign_spend + title: Campaign Spend + - content: + buckets: + - items: + - measure: + alias: Active Customers + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: amount_of_active_customers + type: metric + localIdentifier: 2ba0b87b59ca41a4b1530e81a5c1d081 + title: '# of Active Customers' + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_per_customer + type: metric + localIdentifier: ec0606894b9f4897b7beaf1550608928 + title: Revenue per Customer + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 0de7d7f08af7480aa636857a26be72b6 + localIdentifier: view + filters: + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -12 + granularity: GDC.time.month + to: -1 + properties: + controls: + colorMapping: + - color: + type: guid + value: '20' + id: 2ba0b87b59ca41a4b1530e81a5c1d081 + - color: + type: guid + value: '4' + id: ec0606894b9f4897b7beaf1550608928 + dualAxis: true + legend: + position: bottom + primaryChartType: column + secondaryChartType: line + secondary_yaxis: + measures: + - ec0606894b9f4897b7beaf1550608928 + xaxis: + name: + visible: false + rotation: auto + version: '2' + visualizationUrl: local:combo2 + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: customers_trend + title: Customers Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: percent_revenue_per_product + type: metric + localIdentifier: 08d8346c1ce7438994b251991c0fbf65 + title: '% Revenue per Product' + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: b2350c06688b4da9b3833ebcce65527f + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: customer_name + type: label + localIdentifier: 7a4045fd00ac44579f52406df679435f + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 6a003ffd14994237ba64c4a02c488429 + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 75ea396d0c8b48098e31dccf8b5801d3 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: {} + sorts: + - attributeSortItem: + attributeIdentifier: 7a4045fd00ac44579f52406df679435f + direction: asc + version: '2' + visualizationUrl: local:table + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_per_product_by_customer_and_category + title: '% Revenue per Product by Customer and Category' + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: amount_of_active_customers + type: metric + localIdentifier: 1a14cdc1293c46e89a2e25d3e741d235 + title: '# of Active Customers' + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: c1feca1864244ec2ace7a9b9d7fda231 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: region + type: label + localIdentifier: 530cddbd7ca04d039e73462d81ed44d5 + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: region + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -11 + granularity: GDC.time.month + to: 0 + properties: + controls: + legend: + position: bottom + stackMeasuresToPercent: true + version: '2' + visualizationUrl: local:area + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percentage_of_customers_by_region + title: Percentage of Customers by Region + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 590d332ef686468b8878ae41b23341c6 + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: b166c71091864312a14c7ae8ff886ffe + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: e920a50e0bbb49788df0aac53634c1cd + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:treemap + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_breakdown + title: Product Breakdown + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + computeRatio: true + filters: [] + item: + identifier: + id: revenue + type: metric + format: '#,##0.00%' + localIdentifier: 162b857af49d45769bc12604a5c192b9 + title: '% Revenue' + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: fe513cef1c6244a5ac21c5f49c56b108 + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + dataLabels: + visible: auto + legend: + position: bottom + version: '2' + visualizationUrl: local:donut + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_categories_pie_chart + title: Product Categories Pie Chart + - content: + buckets: + - items: + - measure: + alias: Previous Period + definition: + popMeasureDefinition: + measureIdentifier: c82e025fa2db4afea9a600a424591dbe + popAttribute: + identifier: + id: date.year + type: attribute + localIdentifier: c82e025fa2db4afea9a600a424591dbe_pop + - measure: + alias: This Period + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: c82e025fa2db4afea9a600a424591dbe + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: c804ef5ba7944a5a9f360c86a9e95e9a + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -11 + granularity: GDC.time.month + to: 0 + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + stackMeasures: false + xaxis: + name: + visible: false + yaxis: + name: + visible: false + version: '2' + visualizationUrl: local:column + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_revenue_comparison-over_previous_period + title: Product Revenue Comparison (over previous period) + - content: + buckets: + - items: + - measure: + alias: Number of Orders + definition: + measureDefinition: + filters: [] + item: + identifier: + id: amount_of_orders + type: metric + localIdentifier: aeb5d51a162d4b59aba3bd6ddebcc780 + title: '# of Orders' + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 94b3edd3a73c4a48a4d13bbe9442cc98 + title: Revenue + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: d2a991bdd123448eb2be73d79f1180c4 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: + controls: + dataLabels: + visible: auto + grid: + enabled: true + version: '2' + visualizationUrl: local:scatter + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_saleability + title: Product Saleability + - content: + buckets: + - items: + - measure: + alias: Items Sold + definition: + measureDefinition: + aggregation: sum + filters: [] + item: + identifier: + id: quantity + type: fact + format: '#,##0.00' + localIdentifier: 29486504dd0e4a36a18b0b2f792d3a46 + title: Sum of Quantity + - measure: + definition: + measureDefinition: + aggregation: avg + filters: [] + item: + identifier: + id: price + type: fact + format: '#,##0.00' + localIdentifier: aa6391acccf1452f8011201aef9af492 + title: Avg Price + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: percent_revenue_in_category + type: metric + localIdentifier: 2cd39539d8da46c9883e63caa3ba7cc0 + title: '% Revenue in Category' + - measure: + alias: Total Revenue + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 9a0f08331c094c7facf2a0b4f418de0a + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 06bc6b3b9949466494e4f594c11f1bff + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 192668bfb6a74e9ab7b5d1ce7cb68ea3 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: {} + sorts: + - attributeSortItem: + attributeIdentifier: 06bc6b3b9949466494e4f594c11f1bff + direction: asc + version: '2' + visualizationUrl: local:table + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_and_quantity_by_product_and_category + title: Revenue and Quantity by Product and Category + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 7df6c34387744d69b23ec92e1a5cf543 + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 4bb4fc1986c546de9ad976e6ec23fed4 + localIdentifier: trend + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 34bddcb1cd024902a82396216b0fa9d8 + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + granularity: GDC.time.year + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:line + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_by_category_trend + title: Revenue by Category Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 4ae3401bdbba4938afe983df4ba04e1c + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 1c8ba72dbfc84ddd913bf81dc355c427 + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: {} + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_by_product + title: Revenue by Product + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: campaign_spend + type: metric + localIdentifier: 13a50d811e474ac6808d8da7f4673b35 + title: Campaign Spend + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_per_dollar_spent + type: metric + localIdentifier: a0f15e82e6334280a44dbedc7d086e7c + title: Revenue per Dollar Spent + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: campaign_name + type: label + localIdentifier: 1d9fa968bafb423eb29c938dfb1207ff + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_name + type: label + notIn: + values: [] + properties: + controls: + xaxis: + min: '0' + yaxis: + min: '0' + version: '2' + visualizationUrl: local:scatter + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_usd_vs_spend_by_campaign + title: Revenue per $ vs Spend by Campaign + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 60c854969a9c4c278ab596d99c222e92 + title: Revenue + localIdentifier: measures + - items: + - measure: + alias: Number of Orders + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: amount_of_orders + type: metric + localIdentifier: c2fa7ef48cc54af99f8c280eb451e051 + title: '# of Orders' + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 413ac374b65648fa96826ca01d47bdda + localIdentifier: view + filters: + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -3 + granularity: GDC.time.quarter + to: 0 + properties: + controls: + dualAxis: true + legend: + position: bottom + primaryChartType: column + secondaryChartType: line + secondary_yaxis: + measures: + - c2fa7ef48cc54af99f8c280eb451e051 + xaxis: + name: + visible: false + rotation: auto + version: '2' + visualizationUrl: local:combo2 + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_trend + title: Revenue Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_top_10 + type: metric + localIdentifier: 3f127ccfe57a40399e23f9ae2a4ad810 + title: Revenue / Top 10 + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: customer_name + type: label + localIdentifier: f4e39e24f11e4827a191c30d65c89d2c + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: state + type: label + localIdentifier: bbccd430176d428caed54c99afc9589e + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: state + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: top_10_customers + title: Top 10 Customers + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_top_10 + type: metric + localIdentifier: 77dc71bbac92412bac5f94284a5919df + title: Revenue / Top 10 + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 781952e728204dcf923142910cc22ae2 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: fe513cef1c6244a5ac21c5f49c56b108 + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: top_10_products + title: Top 10 Products + ldm: + datasets: + - aggregatedFacts: [] + attributes: + - description: Campaign channel id + id: campaign_channel_id + isNullable: false + labels: [] + sourceColumn: campaign_channel_id + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Campaign channel id + - description: Category + id: campaign_channels.category + isNullable: true + labels: [] + sourceColumn: category + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Category + - description: Type + id: type + isNullable: true + labels: [] + sourceColumn: type + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Type + dataSourceTableId: + dataSourceId: demo-test-ds + id: campaign_channels + path: + - demo + - campaign_channels + type: dataSource + description: Campaign channels + facts: + - description: Budget + id: budget + isNullable: true + sourceColumn: budget + sourceColumnDataType: NUMERIC + tags: + - Campaign channels + title: Budget + - description: Spend + id: spend + isNullable: true + sourceColumn: spend + sourceColumnDataType: NUMERIC + tags: + - Campaign channels + title: Spend + grain: + - id: campaign_channel_id + type: attribute + id: campaign_channels + references: + - identifier: + id: campaigns + type: dataset + multivalue: false + sources: + - column: campaign_id + dataType: INT + isNullable: true + target: + id: campaign_id + type: attribute + tags: + - Campaign channels + title: Campaign channels + - aggregatedFacts: + - description: Budget Agg + id: budget_agg + sourceColumn: budget + sourceColumnDataType: INT + sourceFactReference: + operation: SUM + reference: + id: budget + type: fact + tags: + - Campaign channels per category + attributes: [] + description: Campaign channels per categories + facts: [] + grain: [] + id: campaign_channels_per_category + precedence: 1 + references: + - identifier: + id: campaign_channels + type: dataset + multivalue: false + sources: + - column: campaign_channel_id + dataType: STRING + isNullable: true + target: + id: campaign_channel_id + type: attribute + sql: + dataSourceId: demo-test-ds + statement: SELECT category, SUM(budget) FROM campaign_channels + GROUP BY category + tags: + - Campaign channels per category + title: Campaign channels per category + - aggregatedFacts: [] + attributes: + - description: Campaign id + id: campaign_id + isNullable: false + labels: [] + sourceColumn: campaign_id + sourceColumnDataType: INT + tags: + - Campaigns + title: Campaign id + - description: Campaign name + id: campaign_name + isNullable: true + labels: [] + sourceColumn: campaign_name + sourceColumnDataType: STRING + tags: + - Campaigns + title: Campaign name + dataSourceTableId: + dataSourceId: demo-test-ds + id: campaigns + path: + - demo + - campaigns + type: dataSource + description: Campaigns + facts: [] + grain: + - id: campaign_id + type: attribute + id: campaigns + references: [] + tags: + - Campaigns + title: Campaigns + - aggregatedFacts: [] + attributes: + - description: Customer id + id: customer_id + isNullable: false + labels: [] + sourceColumn: customer_id + sourceColumnDataType: INT + tags: + - Customers + title: Customer id + - description: Customer name + id: customer_name + isNullable: true + labels: [] + sourceColumn: customer_name + sourceColumnDataType: STRING + tags: + - Customers + title: Customer name + - description: Region + id: region + isNullable: true + labels: [] + sourceColumn: region + sourceColumnDataType: STRING + tags: + - Customers + title: Region + - description: State + id: state + isNullable: true + labels: + - description: Location + id: geo__state__location + isNullable: true + sourceColumn: geo__state__location + sourceColumnDataType: STRING + tags: + - Customers + title: Location + valueType: GEO + sourceColumn: state + sourceColumnDataType: STRING + tags: + - Customers + title: State + dataSourceTableId: + dataSourceId: demo-test-ds + id: customers + path: + - demo + - customers + type: dataSource + description: Customers + facts: [] + grain: + - id: customer_id + type: attribute + id: customers + references: [] + tags: + - Customers + title: Customers + - aggregatedFacts: [] + attributes: + - description: Order id + id: order_id + isNullable: true + labels: [] + sourceColumn: order_id + sourceColumnDataType: STRING + tags: + - Order lines + title: Order id + - description: Order line id + id: order_line_id + isNullable: false + labels: [] + sourceColumn: order_line_id + sourceColumnDataType: STRING + tags: + - Order lines + title: Order line id + - description: Order status + id: order_status + isNullable: true + labels: [] + sourceColumn: order_status + sourceColumnDataType: STRING + tags: + - Order lines + title: Order status + dataSourceTableId: + dataSourceId: demo-test-ds + id: order_lines + path: + - demo + - order_lines + type: dataSource + description: Order lines + facts: + - description: Price + id: price + isNullable: true + sourceColumn: price + sourceColumnDataType: NUMERIC + tags: + - Order lines + title: Price + - description: Quantity + id: quantity + isNullable: true + sourceColumn: quantity + sourceColumnDataType: NUMERIC + tags: + - Order lines + title: Quantity + grain: + - id: order_line_id + type: attribute + id: order_lines + references: + - identifier: + id: campaigns + type: dataset + multivalue: false + sources: + - column: campaign_id + dataType: INT + isNullable: true + target: + id: campaign_id + type: attribute + - identifier: + id: customers + type: dataset + multivalue: false + sources: + - column: customer_id + dataType: INT + isNullable: true + target: + id: customer_id + type: attribute + - identifier: + id: date + type: dataset + multivalue: false + sources: + - column: date + dataType: DATE + isNullable: true + target: + id: date + type: date + - identifier: + id: products + type: dataset + multivalue: false + sources: + - column: product_id + dataType: INT + isNullable: true + target: + id: product_id + type: attribute + tags: + - Order lines + title: Order lines + workspaceDataFilterColumns: + - dataType: STRING + name: wdf__region + - dataType: STRING + name: wdf__state + workspaceDataFilterReferences: + - filterColumn: wdf__region + filterColumnDataType: STRING + filterId: + id: wdf__region + type: workspaceDataFilter + - aggregatedFacts: [] + attributes: + - description: Product id + id: product_id + isNullable: false + labels: [] + sourceColumn: product_id + sourceColumnDataType: INT + tags: + - Products + title: Product id + - description: Product name + id: product_name + isNullable: true + labels: [] + sourceColumn: product_name + sourceColumnDataType: STRING + tags: + - Products + title: Product name + - description: Category + id: products.category + isNullable: true + labels: [] + sourceColumn: category + sourceColumnDataType: STRING + tags: + - Products + title: Category + dataSourceTableId: + dataSourceId: demo-test-ds + id: products + path: + - demo + - products + type: dataSource + description: Products + facts: [] + grain: + - id: product_id + type: attribute + id: products + references: [] + tags: + - Products + title: Products + dateInstances: + - description: '' + granularities: + - DAY + - WEEK + - MONTH + - QUARTER + - YEAR + granularitiesFormatting: + titleBase: '' + titlePattern: '%titleBase - %granularityTitle' + id: date + tags: + - Date + title: Date + name: Demo + permissions: + - assignee: + id: demo2 + type: user + name: ANALYZE + - assignee: + id: demoGroup + type: userGroup + name: VIEW + settings: [] + userDataFilters: [] + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: [] + id: demo_west + model: + analytics: + analyticalDashboardExtensions: + - id: campaign + permissions: + - assigneeRule: + type: allWorkspaceUsers + name: VIEW + analyticalDashboards: [] + attributeHierarchies: [] + dashboardPlugins: [] + exportDefinitions: [] + filterContexts: [] + memoryItems: [] + metrics: [] + parameters: [] + visualizationObjects: [] + ldm: + datasetExtensions: + - id: order_lines + workspaceDataFilterReferences: + - filterColumn: wdf__state + filterColumnDataType: STRING + filterId: + id: wdf__state + type: workspaceDataFilter + datasets: [] + dateInstances: [] + name: Demo West + parent: + id: demo + type: workspace + permissions: [] + settings: [] + userDataFilters: [] + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: [] + id: demo_west_california + model: + analytics: + analyticalDashboardExtensions: [] + analyticalDashboards: [] + attributeHierarchies: [] + dashboardPlugins: [] + exportDefinitions: [] + filterContexts: [] + memoryItems: [] + metrics: [] + parameters: [] + visualizationObjects: [] + ldm: + datasets: [] + dateInstances: [] + name: Demo West California + parent: + id: demo_west + type: workspace + permissions: [] + settings: [] + userDataFilters: [] + headers: + Content-Type: + - application/json + DATE: &id001 + - PLACEHOLDER + Expires: + - '0' + Pragma: + - no-cache + X-Content-Type-Options: + - nosniff + X-GDC-TRACE-ID: *id001 + status: + code: 200 + message: OK + - request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - br, gzip, deflate + X-GDC-VALIDATE-RELATIONS: + - 'true' + X-Requested-With: + - XMLHttpRequest + method: GET + uri: http://localhost:3000/api/v1/layout/organization + response: + body: + string: + agents: + - aiKnowledge: true + availableToAll: true + enabled: true + id: default-ai-assistant + name: Default GoodData AI Assistant + skillsMode: all + customGeoCollections: [] + dataSources: + - alternativeDataSourceId: ds-put-abc-id + authenticationType: USERNAME_PASSWORD + id: demo-test-ds + name: demo-test-ds + permissions: + - assignee: + id: demo2 + type: user + name: MANAGE + - assignee: + id: demoGroup + type: userGroup + name: USE + schema: demo + type: POSTGRESQL + url: jdbc:postgresql://postgres:5432/tiger?sslmode=prefer + username: postgres + exportTemplates: [] + identityProviders: + - id: defaultIdp + idpType: DEX_IDP + ipAllowlistPolicies: [] + jwks: [] + notificationChannels: [] + organization: + allowedOrigins: [] + colorPalettes: [] + cspDirectives: [] + earlyAccess: enableAlerting + earlyAccessValues: + - enableAlerting + - enableSmtp + - enablePreAggregationDatasets + - enableScheduling + - enableCompositeGrain + - enableUserManagement + - enableRawExports + - enableFlexibleDashboardLayout + hostname: localhost + id: default + identityProvider: + id: defaultIdp + type: identityProvider + name: Default Organization + permissions: + - assignee: + id: adminGroup + type: userGroup + name: MANAGE + settings: [] + themes: [] + userGroups: + - id: adminGroup + permissions: [] + - id: adminQA1Group + parents: + - id: adminGroup + type: userGroup + permissions: [] + - id: demoGroup + name: demo group + permissions: [] + - id: visitorsGroup + name: visitors + parents: + - id: demoGroup + type: userGroup + permissions: [] + users: + - id: admin + permissions: [] + settings: [] + userGroups: + - id: adminGroup + type: userGroup + - authId: NORMALIZED_AUTH_ID + email: demo@example.com + firstname: Demo + id: demo + lastname: User + permissions: [] + settings: [] + userGroups: + - id: adminGroup + type: userGroup + - authId: NORMALIZED_AUTH_ID + id: demo2 + permissions: [] + settings: [] + userGroups: + - id: demoGroup + type: userGroup + workspaceDataFilters: + - columnName: wdf__region + id: wdf__region + title: Customer region + workspace: + id: demo + type: workspace + workspaceDataFilterSettings: + - filterValues: + - West + id: region_west + title: Region West + workspace: + id: demo_west + type: workspace + - columnName: wdf__state + id: wdf__state + title: Customer state + workspace: + id: demo_west + type: workspace + workspaceDataFilterSettings: + - filterValues: + - California + id: region_west_california + title: Region West California + workspace: + id: demo_west_california + type: workspace + workspaces: + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: + - assignee: + id: demo2 + type: user + name: MANAGE + - assignee: + id: demoGroup + type: userGroup + name: ANALYZE + id: demo + model: + analytics: + analyticalDashboardExtensions: [] + analyticalDashboards: + - content: + filterContextRef: + identifier: + id: campaign_name_filter + type: filterContext + layout: + sections: + - header: + description: The first insight shows a breakdown of + spend by category and campaign. The second shows + revenue per $ spend, for each campaign, to demonstrate, + how campaigns are successful. + title: Spend breakdown and Revenue + items: + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: campaign_spend + type: visualizationObject + properties: {} + title: Campaign Spend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: revenue_per_usd_vs_spend_by_campaign + type: visualizationObject + properties: {} + title: Revenue per $ vs Spend by Campaign + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: campaign + permissions: + - assigneeRule: + type: allWorkspaceUsers + name: VIEW + title: Campaign + - content: + filterContextRef: + identifier: + id: campaign_name_filter + type: filterContext + layout: + sections: + - items: + - size: + xl: + gridWidth: 12 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: top_10_products + type: visualizationObject + properties: {} + title: DHO simple + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + plugins: + - plugin: + identifier: + id: dashboard_plugin_1 + type: dashboardPlugin + version: '2' + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: dashboard_plugin + title: Dashboard plugin + - content: + filterContextRef: + identifier: + id: region_filter + type: filterContext + layout: + sections: + - items: + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: top_10_products + type: visualizationObject + properties: {} + title: Top 10 Products + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: revenue_trend + type: visualizationObject + properties: {} + title: Revenue Trend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: customers_trend + type: visualizationObject + properties: {} + title: Customers Trend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_categories_pie_chart + type: visualizationObject + properties: {} + title: Product Categories Pie Chart + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_breakdown + type: visualizationObject + properties: {} + title: Product Breakdown + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_saleability + type: visualizationObject + properties: {} + title: Product Saleability + type: insight + - size: + xl: + gridWidth: 12 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: percent_revenue_per_product_by_customer_and_category + type: visualizationObject + properties: {} + title: '% Revenue per Product by Customer and + Category' + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: product_and_category + title: Product & Category + attributeHierarchies: [] + dashboardPlugins: + - content: + url: https://www.example.com + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: Testing record dashboard_plugin_1 + id: dashboard_plugin_1 + title: dashboard_plugin_1 + - content: + url: https://www.example.com + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: Testing record dashboard_plugin_2 + id: dashboard_plugin_2 + title: dashboard_plugin_2 + exportDefinitions: [] + filterContexts: + - content: + filters: + - dateFilter: + from: '0' + granularity: GDC.time.month + to: '0' + type: relative + - attributeFilter: + attributeElements: + uris: [] + displayForm: + identifier: + id: campaign_name + type: label + filterElementsBy: [] + localIdentifier: 14b0807447ef4bc28f43e4fc5c337d1d + negativeSelection: true + version: '2' + description: '' + id: campaign_name_filter + title: filterContext + - content: + filters: + - attributeFilter: + attributeElements: + uris: [] + displayForm: + identifier: + id: region + type: label + filterElementsBy: [] + localIdentifier: 2d5ef8df82444f6ba27b45f0990ee6af + negativeSelection: true + version: '2' + description: '' + id: region_filter + title: filterContext + memoryItems: [] + metrics: + - content: + format: '#,##0' + maql: SELECT COUNT({attribute/customer_id},{attribute/order_line_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_active_customers + title: '# of Active Customers' + - content: + format: '#,##0' + maql: SELECT COUNT({attribute/order_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_orders + title: '# of Orders' + - content: + format: '#,##0' + maql: 'SELECT {metric/amount_of_active_customers} WHERE (SELECT + {metric/revenue} BY {attribute/customer_id}) > 10000 ' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_top_customers + title: '# of Top Customers' + - content: + format: '#,##0.00' + maql: SELECT {metric/amount_of_orders} WHERE NOT ({label/order_status} + IN ("Returned", "Canceled")) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: amount_of_valid_orders + title: '# of Valid Orders' + - content: + format: $#,##0 + maql: SELECT SUM({fact/spend}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: campaign_spend + title: Campaign Spend + - content: + format: $#,##0 + maql: SELECT SUM({fact/price}*{fact/quantity}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: order_amount + title: Order Amount + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / {metric/total_revenue} + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue + title: '% Revenue' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10}\ + \ BY {attribute/customer_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_customers + title: '% Revenue from Top 10 Customers' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10_percent}\ + \ BY {attribute/customer_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_percent_customers + title: '% Revenue from Top 10% Customers' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10_percent}\ + \ BY {attribute/product_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_percent_products + title: '% Revenue from Top 10% Products' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10}\ + \ BY {attribute/product_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_products + title: '% Revenue from Top 10 Products' + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / (SELECT {metric/revenue} BY + {attribute/products.category}, ALL OTHER) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_in_category + title: '% Revenue in Category' + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / (SELECT {metric/revenue} BY + ALL {attribute/product_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_per_product + title: '% Revenue per Product' + - content: + format: $#,##0 + maql: SELECT {metric/order_amount} WHERE NOT ({label/order_status} + IN ("Returned", "Canceled")) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: revenue + title: Revenue + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Clothing") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-clothing + title: Revenue (Clothing) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ( "Electronics") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-electronic + title: Revenue (Electronic) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Home") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-home + title: Revenue (Home) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Outdoor") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-outdoor + title: Revenue (Outdoor) + - content: + format: $#,##0.0 + maql: SELECT AVG(SELECT {metric/revenue} BY {attribute/customer_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_customer + title: Revenue per Customer + - content: + format: $#,##0.0 + maql: SELECT {metric/revenue} / {metric/campaign_spend} + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_dollar_spent + title: Revenue per Dollar Spent + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE TOP(10) OF ({metric/revenue}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_top_10 + title: Revenue / Top 10 + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE TOP(10%) OF ({metric/revenue}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_top_10_percent + title: Revenue / Top 10% + - content: + format: $#,##0 + maql: SELECT {metric/revenue} BY ALL OTHER + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: total_revenue + title: Total Revenue + - content: + format: $#,##0 + maql: SELECT {metric/total_revenue} WITHOUT PARENT FILTER + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: total_revenue-no_filters + title: Total Revenue (No Filters) + parameters: [] + visualizationObjects: + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: campaign_spend + type: metric + localIdentifier: d319bcb2d8c04442a684e3b3cd063381 + title: Campaign Spend + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: campaign_channels.category + type: label + localIdentifier: 291c085e7df8420db84117ca49f59c49 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: campaign_name + type: label + localIdentifier: d9dd143d647d4d148405a60ec2cf59bc + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: type + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_channels.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_name + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:treemap + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: campaign_spend + title: Campaign Spend + - content: + buckets: + - items: + - measure: + alias: Active Customers + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: amount_of_active_customers + type: metric + localIdentifier: 2ba0b87b59ca41a4b1530e81a5c1d081 + title: '# of Active Customers' + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_per_customer + type: metric + localIdentifier: ec0606894b9f4897b7beaf1550608928 + title: Revenue per Customer + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 0de7d7f08af7480aa636857a26be72b6 + localIdentifier: view + filters: + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -12 + granularity: GDC.time.month + to: -1 + properties: + controls: + colorMapping: + - color: + type: guid + value: '20' + id: 2ba0b87b59ca41a4b1530e81a5c1d081 + - color: + type: guid + value: '4' + id: ec0606894b9f4897b7beaf1550608928 + dualAxis: true + legend: + position: bottom + primaryChartType: column + secondaryChartType: line + secondary_yaxis: + measures: + - ec0606894b9f4897b7beaf1550608928 + xaxis: + name: + visible: false + rotation: auto + version: '2' + visualizationUrl: local:combo2 + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: customers_trend + title: Customers Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: percent_revenue_per_product + type: metric + localIdentifier: 08d8346c1ce7438994b251991c0fbf65 + title: '% Revenue per Product' + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: b2350c06688b4da9b3833ebcce65527f + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: customer_name + type: label + localIdentifier: 7a4045fd00ac44579f52406df679435f + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 6a003ffd14994237ba64c4a02c488429 + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 75ea396d0c8b48098e31dccf8b5801d3 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: {} + sorts: + - attributeSortItem: + attributeIdentifier: 7a4045fd00ac44579f52406df679435f + direction: asc + version: '2' + visualizationUrl: local:table + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_per_product_by_customer_and_category + title: '% Revenue per Product by Customer and Category' + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: amount_of_active_customers + type: metric + localIdentifier: 1a14cdc1293c46e89a2e25d3e741d235 + title: '# of Active Customers' + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: c1feca1864244ec2ace7a9b9d7fda231 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: region + type: label + localIdentifier: 530cddbd7ca04d039e73462d81ed44d5 + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: region + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -11 + granularity: GDC.time.month + to: 0 + properties: + controls: + legend: + position: bottom + stackMeasuresToPercent: true + version: '2' + visualizationUrl: local:area + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percentage_of_customers_by_region + title: Percentage of Customers by Region + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 590d332ef686468b8878ae41b23341c6 + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: b166c71091864312a14c7ae8ff886ffe + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: e920a50e0bbb49788df0aac53634c1cd + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:treemap + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_breakdown + title: Product Breakdown + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + computeRatio: true + filters: [] + item: + identifier: + id: revenue + type: metric + format: '#,##0.00%' + localIdentifier: 162b857af49d45769bc12604a5c192b9 + title: '% Revenue' + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: fe513cef1c6244a5ac21c5f49c56b108 + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + dataLabels: + visible: auto + legend: + position: bottom + version: '2' + visualizationUrl: local:donut + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_categories_pie_chart + title: Product Categories Pie Chart + - content: + buckets: + - items: + - measure: + alias: Previous Period + definition: + popMeasureDefinition: + measureIdentifier: c82e025fa2db4afea9a600a424591dbe + popAttribute: + identifier: + id: date.year + type: attribute + localIdentifier: c82e025fa2db4afea9a600a424591dbe_pop + - measure: + alias: This Period + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: c82e025fa2db4afea9a600a424591dbe + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: c804ef5ba7944a5a9f360c86a9e95e9a + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -11 + granularity: GDC.time.month + to: 0 + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + stackMeasures: false + xaxis: + name: + visible: false + yaxis: + name: + visible: false + version: '2' + visualizationUrl: local:column + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_revenue_comparison-over_previous_period + title: Product Revenue Comparison (over previous period) + - content: + buckets: + - items: + - measure: + alias: Number of Orders + definition: + measureDefinition: + filters: [] + item: + identifier: + id: amount_of_orders + type: metric + localIdentifier: aeb5d51a162d4b59aba3bd6ddebcc780 + title: '# of Orders' + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 94b3edd3a73c4a48a4d13bbe9442cc98 + title: Revenue + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: d2a991bdd123448eb2be73d79f1180c4 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: + controls: + dataLabels: + visible: auto + grid: + enabled: true + version: '2' + visualizationUrl: local:scatter + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_saleability + title: Product Saleability + - content: + buckets: + - items: + - measure: + alias: Items Sold + definition: + measureDefinition: + aggregation: sum + filters: [] + item: + identifier: + id: quantity + type: fact + format: '#,##0.00' + localIdentifier: 29486504dd0e4a36a18b0b2f792d3a46 + title: Sum of Quantity + - measure: + definition: + measureDefinition: + aggregation: avg + filters: [] + item: + identifier: + id: price + type: fact + format: '#,##0.00' + localIdentifier: aa6391acccf1452f8011201aef9af492 + title: Avg Price + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: percent_revenue_in_category + type: metric + localIdentifier: 2cd39539d8da46c9883e63caa3ba7cc0 + title: '% Revenue in Category' + - measure: + alias: Total Revenue + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 9a0f08331c094c7facf2a0b4f418de0a + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 06bc6b3b9949466494e4f594c11f1bff + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 192668bfb6a74e9ab7b5d1ce7cb68ea3 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: {} + sorts: + - attributeSortItem: + attributeIdentifier: 06bc6b3b9949466494e4f594c11f1bff + direction: asc + version: '2' + visualizationUrl: local:table + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_and_quantity_by_product_and_category + title: Revenue and Quantity by Product and Category + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 7df6c34387744d69b23ec92e1a5cf543 + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 4bb4fc1986c546de9ad976e6ec23fed4 + localIdentifier: trend + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 34bddcb1cd024902a82396216b0fa9d8 + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + granularity: GDC.time.year + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:line + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_by_category_trend + title: Revenue by Category Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 4ae3401bdbba4938afe983df4ba04e1c + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 1c8ba72dbfc84ddd913bf81dc355c427 + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: {} + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_by_product + title: Revenue by Product + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: campaign_spend + type: metric + localIdentifier: 13a50d811e474ac6808d8da7f4673b35 + title: Campaign Spend + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_per_dollar_spent + type: metric + localIdentifier: a0f15e82e6334280a44dbedc7d086e7c + title: Revenue per Dollar Spent + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: campaign_name + type: label + localIdentifier: 1d9fa968bafb423eb29c938dfb1207ff + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_name + type: label + notIn: + values: [] + properties: + controls: + xaxis: + min: '0' + yaxis: + min: '0' + version: '2' + visualizationUrl: local:scatter + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_usd_vs_spend_by_campaign + title: Revenue per $ vs Spend by Campaign + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 60c854969a9c4c278ab596d99c222e92 + title: Revenue + localIdentifier: measures + - items: + - measure: + alias: Number of Orders + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: amount_of_orders + type: metric + localIdentifier: c2fa7ef48cc54af99f8c280eb451e051 + title: '# of Orders' + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 413ac374b65648fa96826ca01d47bdda + localIdentifier: view + filters: + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -3 + granularity: GDC.time.quarter + to: 0 + properties: + controls: + dualAxis: true + legend: + position: bottom + primaryChartType: column + secondaryChartType: line + secondary_yaxis: + measures: + - c2fa7ef48cc54af99f8c280eb451e051 + xaxis: + name: + visible: false + rotation: auto + version: '2' + visualizationUrl: local:combo2 + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_trend + title: Revenue Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_top_10 + type: metric + localIdentifier: 3f127ccfe57a40399e23f9ae2a4ad810 + title: Revenue / Top 10 + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: customer_name + type: label + localIdentifier: f4e39e24f11e4827a191c30d65c89d2c + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: state + type: label + localIdentifier: bbccd430176d428caed54c99afc9589e + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: state + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: top_10_customers + title: Top 10 Customers + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_top_10 + type: metric + localIdentifier: 77dc71bbac92412bac5f94284a5919df + title: Revenue / Top 10 + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 781952e728204dcf923142910cc22ae2 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: fe513cef1c6244a5ac21c5f49c56b108 + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: top_10_products + title: Top 10 Products + ldm: + datasets: + - aggregatedFacts: [] + attributes: + - description: Campaign channel id + id: campaign_channel_id + isNullable: false + labels: [] + sourceColumn: campaign_channel_id + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Campaign channel id + - description: Category + id: campaign_channels.category + isNullable: true + labels: [] + sourceColumn: category + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Category + - description: Type + id: type + isNullable: true + labels: [] + sourceColumn: type + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Type + dataSourceTableId: + dataSourceId: demo-test-ds + id: campaign_channels + path: + - demo + - campaign_channels + type: dataSource + description: Campaign channels + facts: + - description: Budget + id: budget + isNullable: true + sourceColumn: budget + sourceColumnDataType: NUMERIC + tags: + - Campaign channels + title: Budget + - description: Spend + id: spend + isNullable: true + sourceColumn: spend + sourceColumnDataType: NUMERIC + tags: + - Campaign channels + title: Spend + grain: + - id: campaign_channel_id + type: attribute + id: campaign_channels + references: + - identifier: + id: campaigns + type: dataset + multivalue: false + sources: + - column: campaign_id + dataType: INT + isNullable: true + target: + id: campaign_id + type: attribute + tags: + - Campaign channels + title: Campaign channels + - aggregatedFacts: + - description: Budget Agg + id: budget_agg + sourceColumn: budget + sourceColumnDataType: INT + sourceFactReference: + operation: SUM + reference: + id: budget + type: fact + tags: + - Campaign channels per category + attributes: [] + description: Campaign channels per categories + facts: [] + grain: [] + id: campaign_channels_per_category + precedence: 1 + references: + - identifier: + id: campaign_channels + type: dataset + multivalue: false + sources: + - column: campaign_channel_id + dataType: STRING + isNullable: true + target: + id: campaign_channel_id + type: attribute + sql: + dataSourceId: demo-test-ds + statement: SELECT category, SUM(budget) FROM campaign_channels + GROUP BY category + tags: + - Campaign channels per category + title: Campaign channels per category + - aggregatedFacts: [] + attributes: + - description: Campaign id + id: campaign_id + isNullable: false + labels: [] + sourceColumn: campaign_id + sourceColumnDataType: INT + tags: + - Campaigns + title: Campaign id + - description: Campaign name + id: campaign_name + isNullable: true + labels: [] + sourceColumn: campaign_name + sourceColumnDataType: STRING + tags: + - Campaigns + title: Campaign name + dataSourceTableId: + dataSourceId: demo-test-ds + id: campaigns + path: + - demo + - campaigns + type: dataSource + description: Campaigns + facts: [] + grain: + - id: campaign_id + type: attribute + id: campaigns + references: [] + tags: + - Campaigns + title: Campaigns + - aggregatedFacts: [] + attributes: + - description: Customer id + id: customer_id + isNullable: false + labels: [] + sourceColumn: customer_id + sourceColumnDataType: INT + tags: + - Customers + title: Customer id + - description: Customer name + id: customer_name + isNullable: true + labels: [] + sourceColumn: customer_name + sourceColumnDataType: STRING + tags: + - Customers + title: Customer name + - description: Region + id: region + isNullable: true + labels: [] + sourceColumn: region + sourceColumnDataType: STRING + tags: + - Customers + title: Region + - description: State + id: state + isNullable: true + labels: + - description: Location + id: geo__state__location + isNullable: true + sourceColumn: geo__state__location + sourceColumnDataType: STRING + tags: + - Customers + title: Location + valueType: GEO + sourceColumn: state + sourceColumnDataType: STRING + tags: + - Customers + title: State + dataSourceTableId: + dataSourceId: demo-test-ds + id: customers + path: + - demo + - customers + type: dataSource + description: Customers + facts: [] + grain: + - id: customer_id + type: attribute + id: customers + references: [] + tags: + - Customers + title: Customers + - aggregatedFacts: [] + attributes: + - description: Order id + id: order_id + isNullable: true + labels: [] + sourceColumn: order_id + sourceColumnDataType: STRING + tags: + - Order lines + title: Order id + - description: Order line id + id: order_line_id + isNullable: false + labels: [] + sourceColumn: order_line_id + sourceColumnDataType: STRING + tags: + - Order lines + title: Order line id + - description: Order status + id: order_status + isNullable: true + labels: [] + sourceColumn: order_status + sourceColumnDataType: STRING + tags: + - Order lines + title: Order status + dataSourceTableId: + dataSourceId: demo-test-ds + id: order_lines + path: + - demo + - order_lines + type: dataSource + description: Order lines + facts: + - description: Price + id: price + isNullable: true + sourceColumn: price + sourceColumnDataType: NUMERIC + tags: + - Order lines + title: Price + - description: Quantity + id: quantity + isNullable: true + sourceColumn: quantity + sourceColumnDataType: NUMERIC + tags: + - Order lines + title: Quantity + grain: + - id: order_line_id + type: attribute + id: order_lines + references: + - identifier: + id: campaigns + type: dataset + multivalue: false + sources: + - column: campaign_id + dataType: INT + isNullable: true + target: + id: campaign_id + type: attribute + - identifier: + id: customers + type: dataset + multivalue: false + sources: + - column: customer_id + dataType: INT + isNullable: true + target: + id: customer_id + type: attribute + - identifier: + id: date + type: dataset + multivalue: false + sources: + - column: date + dataType: DATE + isNullable: true + target: + id: date + type: date + - identifier: + id: products + type: dataset + multivalue: false + sources: + - column: product_id + dataType: INT + isNullable: true + target: + id: product_id + type: attribute + tags: + - Order lines + title: Order lines + workspaceDataFilterColumns: + - dataType: STRING + name: wdf__region + - dataType: STRING + name: wdf__state + workspaceDataFilterReferences: + - filterColumn: wdf__region + filterColumnDataType: STRING + filterId: + id: wdf__region + type: workspaceDataFilter + - aggregatedFacts: [] + attributes: + - description: Product id + id: product_id + isNullable: false + labels: [] + sourceColumn: product_id + sourceColumnDataType: INT + tags: + - Products + title: Product id + - description: Product name + id: product_name + isNullable: true + labels: [] + sourceColumn: product_name + sourceColumnDataType: STRING + tags: + - Products + title: Product name + - description: Category + id: products.category + isNullable: true + labels: [] + sourceColumn: category + sourceColumnDataType: STRING + tags: + - Products + title: Category + dataSourceTableId: + dataSourceId: demo-test-ds + id: products + path: + - demo + - products + type: dataSource + description: Products + facts: [] + grain: + - id: product_id + type: attribute + id: products + references: [] + tags: + - Products + title: Products + dateInstances: + - description: '' + granularities: + - DAY + - WEEK + - MONTH + - QUARTER + - YEAR + granularitiesFormatting: + titleBase: '' + titlePattern: '%titleBase - %granularityTitle' + id: date + tags: + - Date + title: Date + name: Demo + permissions: + - assignee: + id: demo2 + type: user + name: ANALYZE + - assignee: + id: demoGroup + type: userGroup + name: VIEW + settings: [] + userDataFilters: [] + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: [] + id: demo_west + model: + analytics: + analyticalDashboardExtensions: + - id: campaign + permissions: + - assigneeRule: + type: allWorkspaceUsers + name: VIEW + analyticalDashboards: [] + attributeHierarchies: [] + dashboardPlugins: [] + exportDefinitions: [] + filterContexts: [] + memoryItems: [] + metrics: [] + parameters: [] + visualizationObjects: [] + ldm: + datasetExtensions: + - id: order_lines + workspaceDataFilterReferences: + - filterColumn: wdf__state + filterColumnDataType: STRING + filterId: + id: wdf__state + type: workspaceDataFilter + datasets: [] + dateInstances: [] + name: Demo West + parent: + id: demo + type: workspace + permissions: [] + settings: [] + userDataFilters: [] + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: [] + id: demo_west_california + model: + analytics: + analyticalDashboardExtensions: [] + analyticalDashboards: [] + attributeHierarchies: [] + dashboardPlugins: [] + exportDefinitions: [] + filterContexts: [] + memoryItems: [] + metrics: [] + parameters: [] + visualizationObjects: [] + ldm: + datasets: [] + dateInstances: [] + name: Demo West California + parent: + id: demo_west + type: workspace + permissions: [] + settings: [] + userDataFilters: [] + headers: + Content-Type: + - application/json + DATE: *id001 + Expires: + - '0' + Pragma: + - no-cache + X-Content-Type-Options: + - nosniff + X-GDC-TRACE-ID: *id001 + status: + code: 200 + message: OK + - request: + body: + agents: + - aiKnowledge: true + availableToAll: true + enabled: true + id: default-ai-assistant + name: Default GoodData AI Assistant + skillsMode: all + customGeoCollections: [] + dataSources: + - alternativeDataSourceId: ds-put-abc-id + authenticationType: USERNAME_PASSWORD + id: demo-test-ds + name: demo-test-ds + permissions: + - assignee: + id: demo2 + type: user + name: MANAGE + - assignee: + id: demoGroup + type: userGroup + name: USE + schema: demo + type: POSTGRESQL + url: jdbc:postgresql://postgres:5432/tiger?sslmode=prefer + username: postgres + exportTemplates: [] + identityProviders: + - id: defaultIdp + idpType: DEX_IDP + ipAllowlistPolicies: + - allowedSources: + - 203.0.113.10/32 + - 198.51.100.0/24 + id: admin-vpn-only + userGroups: + - id: adminGroup + type: userGroup + users: + - id: demo + type: user + jwks: [] + notificationChannels: [] + organization: + allowedOrigins: [] + colorPalettes: [] + cspDirectives: [] + earlyAccess: enableAlerting + earlyAccessValues: + - enableAlerting + - enableSmtp + - enablePreAggregationDatasets + - enableScheduling + - enableCompositeGrain + - enableUserManagement + - enableRawExports + - enableFlexibleDashboardLayout + hostname: localhost + id: default + identityProvider: + id: defaultIdp + type: identityProvider + name: Default Organization + permissions: + - assignee: + id: adminGroup + type: userGroup + name: MANAGE + settings: [] + themes: [] + userGroups: + - id: adminGroup + permissions: [] + - id: adminQA1Group + parents: + - id: adminGroup + type: userGroup + permissions: [] + - id: demoGroup + name: demo group + permissions: [] + - id: visitorsGroup + name: visitors + parents: + - id: demoGroup + type: userGroup + permissions: [] + users: + - id: admin + permissions: [] + settings: [] + userGroups: + - id: adminGroup + type: userGroup + - authId: NORMALIZED_AUTH_ID + email: demo@example.com + firstname: Demo + id: demo + lastname: User + permissions: [] + settings: [] + userGroups: + - id: adminGroup + type: userGroup + - authId: NORMALIZED_AUTH_ID + id: demo2 + permissions: [] + settings: [] + userGroups: + - id: demoGroup + type: userGroup + workspaceDataFilters: + - columnName: wdf__region + id: wdf__region + title: Customer region + workspace: + id: demo + type: workspace + workspaceDataFilterSettings: + - filterValues: + - West + id: region_west + title: Region West + workspace: + id: demo_west + type: workspace + - columnName: wdf__state + id: wdf__state + title: Customer state + workspace: + id: demo_west + type: workspace + workspaceDataFilterSettings: + - filterValues: + - California + id: region_west_california + title: Region West California + workspace: + id: demo_west_california + type: workspace + workspaces: + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: + - assignee: + id: demo2 + type: user + name: MANAGE + - assignee: + id: demoGroup + type: userGroup + name: ANALYZE + id: demo + model: + analytics: + analyticalDashboardExtensions: [] + analyticalDashboards: + - content: + filterContextRef: + identifier: + id: campaign_name_filter + type: filterContext + layout: + sections: + - header: + description: The first insight shows a breakdown of + spend by category and campaign. The second shows revenue + per $ spend, for each campaign, to demonstrate, how + campaigns are successful. + title: Spend breakdown and Revenue + items: + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: campaign_spend + type: visualizationObject + properties: {} + title: Campaign Spend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: revenue_per_usd_vs_spend_by_campaign + type: visualizationObject + properties: {} + title: Revenue per $ vs Spend by Campaign + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: campaign + permissions: + - assigneeRule: + type: allWorkspaceUsers + name: VIEW + title: Campaign + - content: + filterContextRef: + identifier: + id: campaign_name_filter + type: filterContext + layout: + sections: + - items: + - size: + xl: + gridWidth: 12 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: top_10_products + type: visualizationObject + properties: {} + title: DHO simple + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + plugins: + - plugin: + identifier: + id: dashboard_plugin_1 + type: dashboardPlugin + version: '2' + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: dashboard_plugin + title: Dashboard plugin + - content: + filterContextRef: + identifier: + id: region_filter + type: filterContext + layout: + sections: + - items: + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: top_10_products + type: visualizationObject + properties: {} + title: Top 10 Products + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: revenue_trend + type: visualizationObject + properties: {} + title: Revenue Trend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: customers_trend + type: visualizationObject + properties: {} + title: Customers Trend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_categories_pie_chart + type: visualizationObject + properties: {} + title: Product Categories Pie Chart + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_breakdown + type: visualizationObject + properties: {} + title: Product Breakdown + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_saleability + type: visualizationObject + properties: {} + title: Product Saleability + type: insight + - size: + xl: + gridWidth: 12 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: percent_revenue_per_product_by_customer_and_category + type: visualizationObject + properties: {} + title: '% Revenue per Product by Customer and Category' + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: product_and_category + title: Product & Category + attributeHierarchies: [] + dashboardPlugins: + - content: + url: https://www.example.com + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: Testing record dashboard_plugin_1 + id: dashboard_plugin_1 + title: dashboard_plugin_1 + - content: + url: https://www.example.com + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: Testing record dashboard_plugin_2 + id: dashboard_plugin_2 + title: dashboard_plugin_2 + exportDefinitions: [] + filterContexts: + - content: + filters: + - dateFilter: + from: '0' + granularity: GDC.time.month + to: '0' + type: relative + - attributeFilter: + attributeElements: + uris: [] + displayForm: + identifier: + id: campaign_name + type: label + filterElementsBy: [] + localIdentifier: 14b0807447ef4bc28f43e4fc5c337d1d + negativeSelection: true + version: '2' + description: '' + id: campaign_name_filter + title: filterContext + - content: + filters: + - attributeFilter: + attributeElements: + uris: [] + displayForm: + identifier: + id: region + type: label + filterElementsBy: [] + localIdentifier: 2d5ef8df82444f6ba27b45f0990ee6af + negativeSelection: true + version: '2' + description: '' + id: region_filter + title: filterContext + memoryItems: [] + metrics: + - content: + format: '#,##0' + maql: SELECT COUNT({attribute/customer_id},{attribute/order_line_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_active_customers + title: '# of Active Customers' + - content: + format: '#,##0' + maql: SELECT COUNT({attribute/order_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_orders + title: '# of Orders' + - content: + format: '#,##0' + maql: 'SELECT {metric/amount_of_active_customers} WHERE (SELECT + {metric/revenue} BY {attribute/customer_id}) > 10000 ' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_top_customers + title: '# of Top Customers' + - content: + format: '#,##0.00' + maql: SELECT {metric/amount_of_orders} WHERE NOT ({label/order_status} + IN ("Returned", "Canceled")) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: amount_of_valid_orders + title: '# of Valid Orders' + - content: + format: $#,##0 + maql: SELECT SUM({fact/spend}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: campaign_spend + title: Campaign Spend + - content: + format: $#,##0 + maql: SELECT SUM({fact/price}*{fact/quantity}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: order_amount + title: Order Amount + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / {metric/total_revenue} + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue + title: '% Revenue' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10}\ + \ BY {attribute/customer_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_customers + title: '% Revenue from Top 10 Customers' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10_percent}\ + \ BY {attribute/customer_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_percent_customers + title: '% Revenue from Top 10% Customers' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10_percent}\ + \ BY {attribute/product_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_percent_products + title: '% Revenue from Top 10% Products' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10}\ + \ BY {attribute/product_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_products + title: '% Revenue from Top 10 Products' + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / (SELECT {metric/revenue} BY + {attribute/products.category}, ALL OTHER) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_in_category + title: '% Revenue in Category' + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / (SELECT {metric/revenue} BY + ALL {attribute/product_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_per_product + title: '% Revenue per Product' + - content: + format: $#,##0 + maql: SELECT {metric/order_amount} WHERE NOT ({label/order_status} + IN ("Returned", "Canceled")) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: revenue + title: Revenue + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Clothing") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-clothing + title: Revenue (Clothing) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ( "Electronics") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-electronic + title: Revenue (Electronic) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Home") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-home + title: Revenue (Home) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Outdoor") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-outdoor + title: Revenue (Outdoor) + - content: + format: $#,##0.0 + maql: SELECT AVG(SELECT {metric/revenue} BY {attribute/customer_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_customer + title: Revenue per Customer + - content: + format: $#,##0.0 + maql: SELECT {metric/revenue} / {metric/campaign_spend} + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_dollar_spent + title: Revenue per Dollar Spent + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE TOP(10) OF ({metric/revenue}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_top_10 + title: Revenue / Top 10 + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE TOP(10%) OF ({metric/revenue}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_top_10_percent + title: Revenue / Top 10% + - content: + format: $#,##0 + maql: SELECT {metric/revenue} BY ALL OTHER + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: total_revenue + title: Total Revenue + - content: + format: $#,##0 + maql: SELECT {metric/total_revenue} WITHOUT PARENT FILTER + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: total_revenue-no_filters + title: Total Revenue (No Filters) + parameters: [] + visualizationObjects: + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: campaign_spend + type: metric + localIdentifier: d319bcb2d8c04442a684e3b3cd063381 + title: Campaign Spend + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: campaign_channels.category + type: label + localIdentifier: 291c085e7df8420db84117ca49f59c49 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: campaign_name + type: label + localIdentifier: d9dd143d647d4d148405a60ec2cf59bc + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: type + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_channels.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_name + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:treemap + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: campaign_spend + title: Campaign Spend + - content: + buckets: + - items: + - measure: + alias: Active Customers + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: amount_of_active_customers + type: metric + localIdentifier: 2ba0b87b59ca41a4b1530e81a5c1d081 + title: '# of Active Customers' + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_per_customer + type: metric + localIdentifier: ec0606894b9f4897b7beaf1550608928 + title: Revenue per Customer + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 0de7d7f08af7480aa636857a26be72b6 + localIdentifier: view + filters: + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -12 + granularity: GDC.time.month + to: -1 + properties: + controls: + colorMapping: + - color: + type: guid + value: '20' + id: 2ba0b87b59ca41a4b1530e81a5c1d081 + - color: + type: guid + value: '4' + id: ec0606894b9f4897b7beaf1550608928 + dualAxis: true + legend: + position: bottom + primaryChartType: column + secondaryChartType: line + secondary_yaxis: + measures: + - ec0606894b9f4897b7beaf1550608928 + xaxis: + name: + visible: false + rotation: auto + version: '2' + visualizationUrl: local:combo2 + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: customers_trend + title: Customers Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: percent_revenue_per_product + type: metric + localIdentifier: 08d8346c1ce7438994b251991c0fbf65 + title: '% Revenue per Product' + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: b2350c06688b4da9b3833ebcce65527f + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: customer_name + type: label + localIdentifier: 7a4045fd00ac44579f52406df679435f + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 6a003ffd14994237ba64c4a02c488429 + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 75ea396d0c8b48098e31dccf8b5801d3 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: {} + sorts: + - attributeSortItem: + attributeIdentifier: 7a4045fd00ac44579f52406df679435f + direction: asc + version: '2' + visualizationUrl: local:table + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_per_product_by_customer_and_category + title: '% Revenue per Product by Customer and Category' + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: amount_of_active_customers + type: metric + localIdentifier: 1a14cdc1293c46e89a2e25d3e741d235 + title: '# of Active Customers' + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: c1feca1864244ec2ace7a9b9d7fda231 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: region + type: label + localIdentifier: 530cddbd7ca04d039e73462d81ed44d5 + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: region + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -11 + granularity: GDC.time.month + to: 0 + properties: + controls: + legend: + position: bottom + stackMeasuresToPercent: true + version: '2' + visualizationUrl: local:area + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percentage_of_customers_by_region + title: Percentage of Customers by Region + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 590d332ef686468b8878ae41b23341c6 + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: b166c71091864312a14c7ae8ff886ffe + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: e920a50e0bbb49788df0aac53634c1cd + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:treemap + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_breakdown + title: Product Breakdown + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + computeRatio: true + filters: [] + item: + identifier: + id: revenue + type: metric + format: '#,##0.00%' + localIdentifier: 162b857af49d45769bc12604a5c192b9 + title: '% Revenue' + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: fe513cef1c6244a5ac21c5f49c56b108 + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + dataLabels: + visible: auto + legend: + position: bottom + version: '2' + visualizationUrl: local:donut + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_categories_pie_chart + title: Product Categories Pie Chart + - content: + buckets: + - items: + - measure: + alias: Previous Period + definition: + popMeasureDefinition: + measureIdentifier: c82e025fa2db4afea9a600a424591dbe + popAttribute: + identifier: + id: date.year + type: attribute + localIdentifier: c82e025fa2db4afea9a600a424591dbe_pop + - measure: + alias: This Period + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: c82e025fa2db4afea9a600a424591dbe + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: c804ef5ba7944a5a9f360c86a9e95e9a + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -11 + granularity: GDC.time.month + to: 0 + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + stackMeasures: false + xaxis: + name: + visible: false + yaxis: + name: + visible: false + version: '2' + visualizationUrl: local:column + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_revenue_comparison-over_previous_period + title: Product Revenue Comparison (over previous period) + - content: + buckets: + - items: + - measure: + alias: Number of Orders + definition: + measureDefinition: + filters: [] + item: + identifier: + id: amount_of_orders + type: metric + localIdentifier: aeb5d51a162d4b59aba3bd6ddebcc780 + title: '# of Orders' + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 94b3edd3a73c4a48a4d13bbe9442cc98 + title: Revenue + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: d2a991bdd123448eb2be73d79f1180c4 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: + controls: + dataLabels: + visible: auto + grid: + enabled: true + version: '2' + visualizationUrl: local:scatter + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_saleability + title: Product Saleability + - content: + buckets: + - items: + - measure: + alias: Items Sold + definition: + measureDefinition: + aggregation: sum + filters: [] + item: + identifier: + id: quantity + type: fact + format: '#,##0.00' + localIdentifier: 29486504dd0e4a36a18b0b2f792d3a46 + title: Sum of Quantity + - measure: + definition: + measureDefinition: + aggregation: avg + filters: [] + item: + identifier: + id: price + type: fact + format: '#,##0.00' + localIdentifier: aa6391acccf1452f8011201aef9af492 + title: Avg Price + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: percent_revenue_in_category + type: metric + localIdentifier: 2cd39539d8da46c9883e63caa3ba7cc0 + title: '% Revenue in Category' + - measure: + alias: Total Revenue + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 9a0f08331c094c7facf2a0b4f418de0a + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 06bc6b3b9949466494e4f594c11f1bff + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 192668bfb6a74e9ab7b5d1ce7cb68ea3 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: {} + sorts: + - attributeSortItem: + attributeIdentifier: 06bc6b3b9949466494e4f594c11f1bff + direction: asc + version: '2' + visualizationUrl: local:table + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_and_quantity_by_product_and_category + title: Revenue and Quantity by Product and Category + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 7df6c34387744d69b23ec92e1a5cf543 + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 4bb4fc1986c546de9ad976e6ec23fed4 + localIdentifier: trend + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 34bddcb1cd024902a82396216b0fa9d8 + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + granularity: GDC.time.year + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:line + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_by_category_trend + title: Revenue by Category Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 4ae3401bdbba4938afe983df4ba04e1c + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 1c8ba72dbfc84ddd913bf81dc355c427 + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: {} + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_by_product + title: Revenue by Product + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: campaign_spend + type: metric + localIdentifier: 13a50d811e474ac6808d8da7f4673b35 + title: Campaign Spend + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_per_dollar_spent + type: metric + localIdentifier: a0f15e82e6334280a44dbedc7d086e7c + title: Revenue per Dollar Spent + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: campaign_name + type: label + localIdentifier: 1d9fa968bafb423eb29c938dfb1207ff + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_name + type: label + notIn: + values: [] + properties: + controls: + xaxis: + min: '0' + yaxis: + min: '0' + version: '2' + visualizationUrl: local:scatter + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_usd_vs_spend_by_campaign + title: Revenue per $ vs Spend by Campaign + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 60c854969a9c4c278ab596d99c222e92 + title: Revenue + localIdentifier: measures + - items: + - measure: + alias: Number of Orders + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: amount_of_orders + type: metric + localIdentifier: c2fa7ef48cc54af99f8c280eb451e051 + title: '# of Orders' + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 413ac374b65648fa96826ca01d47bdda + localIdentifier: view + filters: + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -3 + granularity: GDC.time.quarter + to: 0 + properties: + controls: + dualAxis: true + legend: + position: bottom + primaryChartType: column + secondaryChartType: line + secondary_yaxis: + measures: + - c2fa7ef48cc54af99f8c280eb451e051 + xaxis: + name: + visible: false + rotation: auto + version: '2' + visualizationUrl: local:combo2 + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_trend + title: Revenue Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_top_10 + type: metric + localIdentifier: 3f127ccfe57a40399e23f9ae2a4ad810 + title: Revenue / Top 10 + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: customer_name + type: label + localIdentifier: f4e39e24f11e4827a191c30d65c89d2c + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: state + type: label + localIdentifier: bbccd430176d428caed54c99afc9589e + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: state + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: top_10_customers + title: Top 10 Customers + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_top_10 + type: metric + localIdentifier: 77dc71bbac92412bac5f94284a5919df + title: Revenue / Top 10 + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 781952e728204dcf923142910cc22ae2 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: fe513cef1c6244a5ac21c5f49c56b108 + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: top_10_products + title: Top 10 Products + ldm: + datasets: + - aggregatedFacts: [] + attributes: + - description: Campaign channel id + id: campaign_channel_id + isNullable: false + labels: [] + sourceColumn: campaign_channel_id + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Campaign channel id + - description: Category + id: campaign_channels.category + isNullable: true + labels: [] + sourceColumn: category + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Category + - description: Type + id: type + isNullable: true + labels: [] + sourceColumn: type + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Type + dataSourceTableId: + dataSourceId: demo-test-ds + id: campaign_channels + path: + - demo + - campaign_channels + type: dataSource + description: Campaign channels + facts: + - description: Budget + id: budget + isNullable: true + sourceColumn: budget + sourceColumnDataType: NUMERIC + tags: + - Campaign channels + title: Budget + - description: Spend + id: spend + isNullable: true + sourceColumn: spend + sourceColumnDataType: NUMERIC + tags: + - Campaign channels + title: Spend + grain: + - id: campaign_channel_id + type: attribute + id: campaign_channels + references: + - identifier: + id: campaigns + type: dataset + multivalue: false + sources: + - column: campaign_id + dataType: INT + isNullable: true + target: + id: campaign_id + type: attribute + tags: + - Campaign channels + title: Campaign channels + - aggregatedFacts: + - description: Budget Agg + id: budget_agg + sourceColumn: budget + sourceColumnDataType: INT + sourceFactReference: + operation: SUM + reference: + id: budget + type: fact + tags: + - Campaign channels per category + attributes: [] + description: Campaign channels per categories + facts: [] + grain: [] + id: campaign_channels_per_category + precedence: 1 + references: + - identifier: + id: campaign_channels + type: dataset + multivalue: false + sources: + - column: campaign_channel_id + dataType: STRING + isNullable: true + target: + id: campaign_channel_id + type: attribute + sql: + dataSourceId: demo-test-ds + statement: SELECT category, SUM(budget) FROM campaign_channels + GROUP BY category + tags: + - Campaign channels per category + title: Campaign channels per category + - aggregatedFacts: [] + attributes: + - description: Campaign id + id: campaign_id + isNullable: false + labels: [] + sourceColumn: campaign_id + sourceColumnDataType: INT + tags: + - Campaigns + title: Campaign id + - description: Campaign name + id: campaign_name + isNullable: true + labels: [] + sourceColumn: campaign_name + sourceColumnDataType: STRING + tags: + - Campaigns + title: Campaign name + dataSourceTableId: + dataSourceId: demo-test-ds + id: campaigns + path: + - demo + - campaigns + type: dataSource + description: Campaigns + facts: [] + grain: + - id: campaign_id + type: attribute + id: campaigns + references: [] + tags: + - Campaigns + title: Campaigns + - aggregatedFacts: [] + attributes: + - description: Customer id + id: customer_id + isNullable: false + labels: [] + sourceColumn: customer_id + sourceColumnDataType: INT + tags: + - Customers + title: Customer id + - description: Customer name + id: customer_name + isNullable: true + labels: [] + sourceColumn: customer_name + sourceColumnDataType: STRING + tags: + - Customers + title: Customer name + - description: Region + id: region + isNullable: true + labels: [] + sourceColumn: region + sourceColumnDataType: STRING + tags: + - Customers + title: Region + - description: State + id: state + isNullable: true + labels: + - description: Location + id: geo__state__location + isNullable: true + sourceColumn: geo__state__location + sourceColumnDataType: STRING + tags: + - Customers + title: Location + valueType: GEO + sourceColumn: state + sourceColumnDataType: STRING + tags: + - Customers + title: State + dataSourceTableId: + dataSourceId: demo-test-ds + id: customers + path: + - demo + - customers + type: dataSource + description: Customers + facts: [] + grain: + - id: customer_id + type: attribute + id: customers + references: [] + tags: + - Customers + title: Customers + - aggregatedFacts: [] + attributes: + - description: Order id + id: order_id + isNullable: true + labels: [] + sourceColumn: order_id + sourceColumnDataType: STRING + tags: + - Order lines + title: Order id + - description: Order line id + id: order_line_id + isNullable: false + labels: [] + sourceColumn: order_line_id + sourceColumnDataType: STRING + tags: + - Order lines + title: Order line id + - description: Order status + id: order_status + isNullable: true + labels: [] + sourceColumn: order_status + sourceColumnDataType: STRING + tags: + - Order lines + title: Order status + dataSourceTableId: + dataSourceId: demo-test-ds + id: order_lines + path: + - demo + - order_lines + type: dataSource + description: Order lines + facts: + - description: Price + id: price + isNullable: true + sourceColumn: price + sourceColumnDataType: NUMERIC + tags: + - Order lines + title: Price + - description: Quantity + id: quantity + isNullable: true + sourceColumn: quantity + sourceColumnDataType: NUMERIC + tags: + - Order lines + title: Quantity + grain: + - id: order_line_id + type: attribute + id: order_lines + references: + - identifier: + id: campaigns + type: dataset + multivalue: false + sources: + - column: campaign_id + dataType: INT + isNullable: true + target: + id: campaign_id + type: attribute + - identifier: + id: customers + type: dataset + multivalue: false + sources: + - column: customer_id + dataType: INT + isNullable: true + target: + id: customer_id + type: attribute + - identifier: + id: date + type: dataset + multivalue: false + sources: + - column: date + dataType: DATE + isNullable: true + target: + id: date + type: date + - identifier: + id: products + type: dataset + multivalue: false + sources: + - column: product_id + dataType: INT + isNullable: true + target: + id: product_id + type: attribute + tags: + - Order lines + title: Order lines + workspaceDataFilterColumns: + - dataType: STRING + name: wdf__region + - dataType: STRING + name: wdf__state + workspaceDataFilterReferences: + - filterColumn: wdf__region + filterColumnDataType: STRING + filterId: + id: wdf__region + type: workspaceDataFilter + - aggregatedFacts: [] + attributes: + - description: Product id + id: product_id + isNullable: false + labels: [] + sourceColumn: product_id + sourceColumnDataType: INT + tags: + - Products + title: Product id + - description: Product name + id: product_name + isNullable: true + labels: [] + sourceColumn: product_name + sourceColumnDataType: STRING + tags: + - Products + title: Product name + - description: Category + id: products.category + isNullable: true + labels: [] + sourceColumn: category + sourceColumnDataType: STRING + tags: + - Products + title: Category + dataSourceTableId: + dataSourceId: demo-test-ds + id: products + path: + - demo + - products + type: dataSource + description: Products + facts: [] + grain: + - id: product_id + type: attribute + id: products + references: [] + tags: + - Products + title: Products + dateInstances: + - description: '' + granularities: + - DAY + - WEEK + - MONTH + - QUARTER + - YEAR + granularitiesFormatting: + titleBase: '' + titlePattern: '%titleBase - %granularityTitle' + id: date + tags: + - Date + title: Date + name: Demo + permissions: + - assignee: + id: demo2 + type: user + name: ANALYZE + - assignee: + id: demoGroup + type: userGroup + name: VIEW + settings: [] + userDataFilters: [] + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: [] + id: demo_west + model: + analytics: + analyticalDashboardExtensions: + - id: campaign + permissions: + - assigneeRule: + type: allWorkspaceUsers + name: VIEW + analyticalDashboards: [] + attributeHierarchies: [] + dashboardPlugins: [] + exportDefinitions: [] + filterContexts: [] + memoryItems: [] + metrics: [] + parameters: [] + visualizationObjects: [] + ldm: + datasetExtensions: + - id: order_lines + workspaceDataFilterReferences: + - filterColumn: wdf__state + filterColumnDataType: STRING + filterId: + id: wdf__state + type: workspaceDataFilter + datasets: [] + dateInstances: [] + name: Demo West + parent: + id: demo + type: workspace + permissions: [] + settings: [] + userDataFilters: [] + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: [] + id: demo_west_california + model: + analytics: + analyticalDashboardExtensions: [] + analyticalDashboards: [] + attributeHierarchies: [] + dashboardPlugins: [] + exportDefinitions: [] + filterContexts: [] + memoryItems: [] + metrics: [] + parameters: [] + visualizationObjects: [] + ldm: + datasets: [] + dateInstances: [] + name: Demo West California + parent: + id: demo_west + type: workspace + permissions: [] + settings: [] + userDataFilters: [] + headers: + Accept-Encoding: + - br, gzip, deflate + Content-Type: + - application/json + X-GDC-VALIDATE-RELATIONS: + - 'true' + X-Requested-With: + - XMLHttpRequest + method: PUT + uri: http://localhost:3000/api/v1/layout/organization + response: + body: + string: + detail: 'Invalid combination of auth properties. Only none, user + password, + token, key-pair or client id + secret are allowed. Datasource: demo-test-ds, + user: , password: , token: , key-pair: , clientId: , + clientSecret: ' + status: 400 + title: Bad Request + traceId: NORMALIZED_TRACE_ID_000000000000 + headers: + Content-Type: + - application/problem+json + DATE: *id001 + Expires: + - '0' + Pragma: + - no-cache + X-Content-Type-Options: + - nosniff + X-GDC-TRACE-ID: *id001 + status: + code: 400 + message: Bad Request + - request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - br, gzip, deflate + X-GDC-VALIDATE-RELATIONS: + - 'true' + X-Requested-With: + - XMLHttpRequest + method: GET + uri: http://localhost:3000/api/v1/layout/organization + response: + body: + string: + agents: + - aiKnowledge: true + availableToAll: true + enabled: true + id: default-ai-assistant + name: Default GoodData AI Assistant + skillsMode: all + customGeoCollections: [] + dataSources: + - alternativeDataSourceId: ds-put-abc-id + authenticationType: USERNAME_PASSWORD + id: demo-test-ds + name: demo-test-ds + permissions: + - assignee: + id: demo2 + type: user + name: MANAGE + - assignee: + id: demoGroup + type: userGroup + name: USE + schema: demo + type: POSTGRESQL + url: jdbc:postgresql://postgres:5432/tiger?sslmode=prefer + username: postgres + exportTemplates: [] + identityProviders: + - id: defaultIdp + idpType: DEX_IDP + ipAllowlistPolicies: [] + jwks: [] + notificationChannels: [] + organization: + allowedOrigins: [] + colorPalettes: [] + cspDirectives: [] + earlyAccess: enableAlerting + earlyAccessValues: + - enableAlerting + - enableSmtp + - enablePreAggregationDatasets + - enableScheduling + - enableCompositeGrain + - enableUserManagement + - enableRawExports + - enableFlexibleDashboardLayout + hostname: localhost + id: default + identityProvider: + id: defaultIdp + type: identityProvider + name: Default Organization + permissions: + - assignee: + id: adminGroup + type: userGroup + name: MANAGE + settings: [] + themes: [] + userGroups: + - id: adminGroup + permissions: [] + - id: adminQA1Group + parents: + - id: adminGroup + type: userGroup + permissions: [] + - id: demoGroup + name: demo group + permissions: [] + - id: visitorsGroup + name: visitors + parents: + - id: demoGroup + type: userGroup + permissions: [] + users: + - id: admin + permissions: [] + settings: [] + userGroups: + - id: adminGroup + type: userGroup + - authId: NORMALIZED_AUTH_ID + email: demo@example.com + firstname: Demo + id: demo + lastname: User + permissions: [] + settings: [] + userGroups: + - id: adminGroup + type: userGroup + - authId: NORMALIZED_AUTH_ID + id: demo2 + permissions: [] + settings: [] + userGroups: + - id: demoGroup + type: userGroup + workspaceDataFilters: + - columnName: wdf__region + id: wdf__region + title: Customer region + workspace: + id: demo + type: workspace + workspaceDataFilterSettings: + - filterValues: + - West + id: region_west + title: Region West + workspace: + id: demo_west + type: workspace + - columnName: wdf__state + id: wdf__state + title: Customer state + workspace: + id: demo_west + type: workspace + workspaceDataFilterSettings: + - filterValues: + - California + id: region_west_california + title: Region West California + workspace: + id: demo_west_california + type: workspace + workspaces: + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: + - assignee: + id: demo2 + type: user + name: MANAGE + - assignee: + id: demoGroup + type: userGroup + name: ANALYZE + id: demo + model: + analytics: + analyticalDashboardExtensions: [] + analyticalDashboards: + - content: + filterContextRef: + identifier: + id: campaign_name_filter + type: filterContext + layout: + sections: + - header: + description: The first insight shows a breakdown of + spend by category and campaign. The second shows + revenue per $ spend, for each campaign, to demonstrate, + how campaigns are successful. + title: Spend breakdown and Revenue + items: + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: campaign_spend + type: visualizationObject + properties: {} + title: Campaign Spend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: revenue_per_usd_vs_spend_by_campaign + type: visualizationObject + properties: {} + title: Revenue per $ vs Spend by Campaign + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: campaign + permissions: + - assigneeRule: + type: allWorkspaceUsers + name: VIEW + title: Campaign + - content: + filterContextRef: + identifier: + id: campaign_name_filter + type: filterContext + layout: + sections: + - items: + - size: + xl: + gridWidth: 12 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: top_10_products + type: visualizationObject + properties: {} + title: DHO simple + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + plugins: + - plugin: + identifier: + id: dashboard_plugin_1 + type: dashboardPlugin + version: '2' + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: dashboard_plugin + title: Dashboard plugin + - content: + filterContextRef: + identifier: + id: region_filter + type: filterContext + layout: + sections: + - items: + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: top_10_products + type: visualizationObject + properties: {} + title: Top 10 Products + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: revenue_trend + type: visualizationObject + properties: {} + title: Revenue Trend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: customers_trend + type: visualizationObject + properties: {} + title: Customers Trend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_categories_pie_chart + type: visualizationObject + properties: {} + title: Product Categories Pie Chart + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_breakdown + type: visualizationObject + properties: {} + title: Product Breakdown + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_saleability + type: visualizationObject + properties: {} + title: Product Saleability + type: insight + - size: + xl: + gridWidth: 12 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: percent_revenue_per_product_by_customer_and_category + type: visualizationObject + properties: {} + title: '% Revenue per Product by Customer and + Category' + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: product_and_category + title: Product & Category + attributeHierarchies: [] + dashboardPlugins: + - content: + url: https://www.example.com + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: Testing record dashboard_plugin_1 + id: dashboard_plugin_1 + title: dashboard_plugin_1 + - content: + url: https://www.example.com + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: Testing record dashboard_plugin_2 + id: dashboard_plugin_2 + title: dashboard_plugin_2 + exportDefinitions: [] + filterContexts: + - content: + filters: + - dateFilter: + from: '0' + granularity: GDC.time.month + to: '0' + type: relative + - attributeFilter: + attributeElements: + uris: [] + displayForm: + identifier: + id: campaign_name + type: label + filterElementsBy: [] + localIdentifier: 14b0807447ef4bc28f43e4fc5c337d1d + negativeSelection: true + version: '2' + description: '' + id: campaign_name_filter + title: filterContext + - content: + filters: + - attributeFilter: + attributeElements: + uris: [] + displayForm: + identifier: + id: region + type: label + filterElementsBy: [] + localIdentifier: 2d5ef8df82444f6ba27b45f0990ee6af + negativeSelection: true + version: '2' + description: '' + id: region_filter + title: filterContext + memoryItems: [] + metrics: + - content: + format: '#,##0' + maql: SELECT COUNT({attribute/customer_id},{attribute/order_line_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_active_customers + title: '# of Active Customers' + - content: + format: '#,##0' + maql: SELECT COUNT({attribute/order_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_orders + title: '# of Orders' + - content: + format: '#,##0' + maql: 'SELECT {metric/amount_of_active_customers} WHERE (SELECT + {metric/revenue} BY {attribute/customer_id}) > 10000 ' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_top_customers + title: '# of Top Customers' + - content: + format: '#,##0.00' + maql: SELECT {metric/amount_of_orders} WHERE NOT ({label/order_status} + IN ("Returned", "Canceled")) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: amount_of_valid_orders + title: '# of Valid Orders' + - content: + format: $#,##0 + maql: SELECT SUM({fact/spend}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: campaign_spend + title: Campaign Spend + - content: + format: $#,##0 + maql: SELECT SUM({fact/price}*{fact/quantity}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: order_amount + title: Order Amount + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / {metric/total_revenue} + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue + title: '% Revenue' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10}\ + \ BY {attribute/customer_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_customers + title: '% Revenue from Top 10 Customers' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10_percent}\ + \ BY {attribute/customer_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_percent_customers + title: '% Revenue from Top 10% Customers' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10_percent}\ + \ BY {attribute/product_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_percent_products + title: '% Revenue from Top 10% Products' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10}\ + \ BY {attribute/product_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_products + title: '% Revenue from Top 10 Products' + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / (SELECT {metric/revenue} BY + {attribute/products.category}, ALL OTHER) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_in_category + title: '% Revenue in Category' + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / (SELECT {metric/revenue} BY + ALL {attribute/product_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_per_product + title: '% Revenue per Product' + - content: + format: $#,##0 + maql: SELECT {metric/order_amount} WHERE NOT ({label/order_status} + IN ("Returned", "Canceled")) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: revenue + title: Revenue + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Clothing") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-clothing + title: Revenue (Clothing) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ( "Electronics") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-electronic + title: Revenue (Electronic) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Home") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-home + title: Revenue (Home) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Outdoor") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-outdoor + title: Revenue (Outdoor) + - content: + format: $#,##0.0 + maql: SELECT AVG(SELECT {metric/revenue} BY {attribute/customer_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_customer + title: Revenue per Customer + - content: + format: $#,##0.0 + maql: SELECT {metric/revenue} / {metric/campaign_spend} + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_dollar_spent + title: Revenue per Dollar Spent + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE TOP(10) OF ({metric/revenue}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_top_10 + title: Revenue / Top 10 + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE TOP(10%) OF ({metric/revenue}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_top_10_percent + title: Revenue / Top 10% + - content: + format: $#,##0 + maql: SELECT {metric/revenue} BY ALL OTHER + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: total_revenue + title: Total Revenue + - content: + format: $#,##0 + maql: SELECT {metric/total_revenue} WITHOUT PARENT FILTER + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: total_revenue-no_filters + title: Total Revenue (No Filters) + parameters: [] + visualizationObjects: + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: campaign_spend + type: metric + localIdentifier: d319bcb2d8c04442a684e3b3cd063381 + title: Campaign Spend + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: campaign_channels.category + type: label + localIdentifier: 291c085e7df8420db84117ca49f59c49 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: campaign_name + type: label + localIdentifier: d9dd143d647d4d148405a60ec2cf59bc + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: type + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_channels.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_name + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:treemap + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: campaign_spend + title: Campaign Spend + - content: + buckets: + - items: + - measure: + alias: Active Customers + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: amount_of_active_customers + type: metric + localIdentifier: 2ba0b87b59ca41a4b1530e81a5c1d081 + title: '# of Active Customers' + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_per_customer + type: metric + localIdentifier: ec0606894b9f4897b7beaf1550608928 + title: Revenue per Customer + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 0de7d7f08af7480aa636857a26be72b6 + localIdentifier: view + filters: + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -12 + granularity: GDC.time.month + to: -1 + properties: + controls: + colorMapping: + - color: + type: guid + value: '20' + id: 2ba0b87b59ca41a4b1530e81a5c1d081 + - color: + type: guid + value: '4' + id: ec0606894b9f4897b7beaf1550608928 + dualAxis: true + legend: + position: bottom + primaryChartType: column + secondaryChartType: line + secondary_yaxis: + measures: + - ec0606894b9f4897b7beaf1550608928 + xaxis: + name: + visible: false + rotation: auto + version: '2' + visualizationUrl: local:combo2 + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: customers_trend + title: Customers Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: percent_revenue_per_product + type: metric + localIdentifier: 08d8346c1ce7438994b251991c0fbf65 + title: '% Revenue per Product' + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: b2350c06688b4da9b3833ebcce65527f + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: customer_name + type: label + localIdentifier: 7a4045fd00ac44579f52406df679435f + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 6a003ffd14994237ba64c4a02c488429 + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 75ea396d0c8b48098e31dccf8b5801d3 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: {} + sorts: + - attributeSortItem: + attributeIdentifier: 7a4045fd00ac44579f52406df679435f + direction: asc + version: '2' + visualizationUrl: local:table + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_per_product_by_customer_and_category + title: '% Revenue per Product by Customer and Category' + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: amount_of_active_customers + type: metric + localIdentifier: 1a14cdc1293c46e89a2e25d3e741d235 + title: '# of Active Customers' + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: c1feca1864244ec2ace7a9b9d7fda231 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: region + type: label + localIdentifier: 530cddbd7ca04d039e73462d81ed44d5 + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: region + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -11 + granularity: GDC.time.month + to: 0 + properties: + controls: + legend: + position: bottom + stackMeasuresToPercent: true + version: '2' + visualizationUrl: local:area + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percentage_of_customers_by_region + title: Percentage of Customers by Region + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 590d332ef686468b8878ae41b23341c6 + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: b166c71091864312a14c7ae8ff886ffe + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: e920a50e0bbb49788df0aac53634c1cd + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:treemap + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_breakdown + title: Product Breakdown + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + computeRatio: true + filters: [] + item: + identifier: + id: revenue + type: metric + format: '#,##0.00%' + localIdentifier: 162b857af49d45769bc12604a5c192b9 + title: '% Revenue' + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: fe513cef1c6244a5ac21c5f49c56b108 + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + dataLabels: + visible: auto + legend: + position: bottom + version: '2' + visualizationUrl: local:donut + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_categories_pie_chart + title: Product Categories Pie Chart + - content: + buckets: + - items: + - measure: + alias: Previous Period + definition: + popMeasureDefinition: + measureIdentifier: c82e025fa2db4afea9a600a424591dbe + popAttribute: + identifier: + id: date.year + type: attribute + localIdentifier: c82e025fa2db4afea9a600a424591dbe_pop + - measure: + alias: This Period + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: c82e025fa2db4afea9a600a424591dbe + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: c804ef5ba7944a5a9f360c86a9e95e9a + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -11 + granularity: GDC.time.month + to: 0 + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + stackMeasures: false + xaxis: + name: + visible: false + yaxis: + name: + visible: false + version: '2' + visualizationUrl: local:column + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_revenue_comparison-over_previous_period + title: Product Revenue Comparison (over previous period) + - content: + buckets: + - items: + - measure: + alias: Number of Orders + definition: + measureDefinition: + filters: [] + item: + identifier: + id: amount_of_orders + type: metric + localIdentifier: aeb5d51a162d4b59aba3bd6ddebcc780 + title: '# of Orders' + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 94b3edd3a73c4a48a4d13bbe9442cc98 + title: Revenue + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: d2a991bdd123448eb2be73d79f1180c4 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: + controls: + dataLabels: + visible: auto + grid: + enabled: true + version: '2' + visualizationUrl: local:scatter + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_saleability + title: Product Saleability + - content: + buckets: + - items: + - measure: + alias: Items Sold + definition: + measureDefinition: + aggregation: sum + filters: [] + item: + identifier: + id: quantity + type: fact + format: '#,##0.00' + localIdentifier: 29486504dd0e4a36a18b0b2f792d3a46 + title: Sum of Quantity + - measure: + definition: + measureDefinition: + aggregation: avg + filters: [] + item: + identifier: + id: price + type: fact + format: '#,##0.00' + localIdentifier: aa6391acccf1452f8011201aef9af492 + title: Avg Price + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: percent_revenue_in_category + type: metric + localIdentifier: 2cd39539d8da46c9883e63caa3ba7cc0 + title: '% Revenue in Category' + - measure: + alias: Total Revenue + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 9a0f08331c094c7facf2a0b4f418de0a + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 06bc6b3b9949466494e4f594c11f1bff + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 192668bfb6a74e9ab7b5d1ce7cb68ea3 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: {} + sorts: + - attributeSortItem: + attributeIdentifier: 06bc6b3b9949466494e4f594c11f1bff + direction: asc + version: '2' + visualizationUrl: local:table + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_and_quantity_by_product_and_category + title: Revenue and Quantity by Product and Category + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 7df6c34387744d69b23ec92e1a5cf543 + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 4bb4fc1986c546de9ad976e6ec23fed4 + localIdentifier: trend + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 34bddcb1cd024902a82396216b0fa9d8 + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + granularity: GDC.time.year + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:line + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_by_category_trend + title: Revenue by Category Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 4ae3401bdbba4938afe983df4ba04e1c + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 1c8ba72dbfc84ddd913bf81dc355c427 + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: {} + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_by_product + title: Revenue by Product + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: campaign_spend + type: metric + localIdentifier: 13a50d811e474ac6808d8da7f4673b35 + title: Campaign Spend + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_per_dollar_spent + type: metric + localIdentifier: a0f15e82e6334280a44dbedc7d086e7c + title: Revenue per Dollar Spent + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: campaign_name + type: label + localIdentifier: 1d9fa968bafb423eb29c938dfb1207ff + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_name + type: label + notIn: + values: [] + properties: + controls: + xaxis: + min: '0' + yaxis: + min: '0' + version: '2' + visualizationUrl: local:scatter + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_usd_vs_spend_by_campaign + title: Revenue per $ vs Spend by Campaign + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 60c854969a9c4c278ab596d99c222e92 + title: Revenue + localIdentifier: measures + - items: + - measure: + alias: Number of Orders + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: amount_of_orders + type: metric + localIdentifier: c2fa7ef48cc54af99f8c280eb451e051 + title: '# of Orders' + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 413ac374b65648fa96826ca01d47bdda + localIdentifier: view + filters: + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -3 + granularity: GDC.time.quarter + to: 0 + properties: + controls: + dualAxis: true + legend: + position: bottom + primaryChartType: column + secondaryChartType: line + secondary_yaxis: + measures: + - c2fa7ef48cc54af99f8c280eb451e051 + xaxis: + name: + visible: false + rotation: auto + version: '2' + visualizationUrl: local:combo2 + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_trend + title: Revenue Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_top_10 + type: metric + localIdentifier: 3f127ccfe57a40399e23f9ae2a4ad810 + title: Revenue / Top 10 + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: customer_name + type: label + localIdentifier: f4e39e24f11e4827a191c30d65c89d2c + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: state + type: label + localIdentifier: bbccd430176d428caed54c99afc9589e + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: state + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: top_10_customers + title: Top 10 Customers + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_top_10 + type: metric + localIdentifier: 77dc71bbac92412bac5f94284a5919df + title: Revenue / Top 10 + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 781952e728204dcf923142910cc22ae2 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: fe513cef1c6244a5ac21c5f49c56b108 + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: top_10_products + title: Top 10 Products + ldm: + datasets: + - aggregatedFacts: [] + attributes: + - description: Campaign channel id + id: campaign_channel_id + isNullable: false + labels: [] + sourceColumn: campaign_channel_id + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Campaign channel id + - description: Category + id: campaign_channels.category + isNullable: true + labels: [] + sourceColumn: category + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Category + - description: Type + id: type + isNullable: true + labels: [] + sourceColumn: type + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Type + dataSourceTableId: + dataSourceId: demo-test-ds + id: campaign_channels + path: + - demo + - campaign_channels + type: dataSource + description: Campaign channels + facts: + - description: Budget + id: budget + isNullable: true + sourceColumn: budget + sourceColumnDataType: NUMERIC + tags: + - Campaign channels + title: Budget + - description: Spend + id: spend + isNullable: true + sourceColumn: spend + sourceColumnDataType: NUMERIC + tags: + - Campaign channels + title: Spend + grain: + - id: campaign_channel_id + type: attribute + id: campaign_channels + references: + - identifier: + id: campaigns + type: dataset + multivalue: false + sources: + - column: campaign_id + dataType: INT + isNullable: true + target: + id: campaign_id + type: attribute + tags: + - Campaign channels + title: Campaign channels + - aggregatedFacts: + - description: Budget Agg + id: budget_agg + sourceColumn: budget + sourceColumnDataType: INT + sourceFactReference: + operation: SUM + reference: + id: budget + type: fact + tags: + - Campaign channels per category + attributes: [] + description: Campaign channels per categories + facts: [] + grain: [] + id: campaign_channels_per_category + precedence: 1 + references: + - identifier: + id: campaign_channels + type: dataset + multivalue: false + sources: + - column: campaign_channel_id + dataType: STRING + isNullable: true + target: + id: campaign_channel_id + type: attribute + sql: + dataSourceId: demo-test-ds + statement: SELECT category, SUM(budget) FROM campaign_channels + GROUP BY category + tags: + - Campaign channels per category + title: Campaign channels per category + - aggregatedFacts: [] + attributes: + - description: Campaign id + id: campaign_id + isNullable: false + labels: [] + sourceColumn: campaign_id + sourceColumnDataType: INT + tags: + - Campaigns + title: Campaign id + - description: Campaign name + id: campaign_name + isNullable: true + labels: [] + sourceColumn: campaign_name + sourceColumnDataType: STRING + tags: + - Campaigns + title: Campaign name + dataSourceTableId: + dataSourceId: demo-test-ds + id: campaigns + path: + - demo + - campaigns + type: dataSource + description: Campaigns + facts: [] + grain: + - id: campaign_id + type: attribute + id: campaigns + references: [] + tags: + - Campaigns + title: Campaigns + - aggregatedFacts: [] + attributes: + - description: Customer id + id: customer_id + isNullable: false + labels: [] + sourceColumn: customer_id + sourceColumnDataType: INT + tags: + - Customers + title: Customer id + - description: Customer name + id: customer_name + isNullable: true + labels: [] + sourceColumn: customer_name + sourceColumnDataType: STRING + tags: + - Customers + title: Customer name + - description: Region + id: region + isNullable: true + labels: [] + sourceColumn: region + sourceColumnDataType: STRING + tags: + - Customers + title: Region + - description: State + id: state + isNullable: true + labels: + - description: Location + id: geo__state__location + isNullable: true + sourceColumn: geo__state__location + sourceColumnDataType: STRING + tags: + - Customers + title: Location + valueType: GEO + sourceColumn: state + sourceColumnDataType: STRING + tags: + - Customers + title: State + dataSourceTableId: + dataSourceId: demo-test-ds + id: customers + path: + - demo + - customers + type: dataSource + description: Customers + facts: [] + grain: + - id: customer_id + type: attribute + id: customers + references: [] + tags: + - Customers + title: Customers + - aggregatedFacts: [] + attributes: + - description: Order id + id: order_id + isNullable: true + labels: [] + sourceColumn: order_id + sourceColumnDataType: STRING + tags: + - Order lines + title: Order id + - description: Order line id + id: order_line_id + isNullable: false + labels: [] + sourceColumn: order_line_id + sourceColumnDataType: STRING + tags: + - Order lines + title: Order line id + - description: Order status + id: order_status + isNullable: true + labels: [] + sourceColumn: order_status + sourceColumnDataType: STRING + tags: + - Order lines + title: Order status + dataSourceTableId: + dataSourceId: demo-test-ds + id: order_lines + path: + - demo + - order_lines + type: dataSource + description: Order lines + facts: + - description: Price + id: price + isNullable: true + sourceColumn: price + sourceColumnDataType: NUMERIC + tags: + - Order lines + title: Price + - description: Quantity + id: quantity + isNullable: true + sourceColumn: quantity + sourceColumnDataType: NUMERIC + tags: + - Order lines + title: Quantity + grain: + - id: order_line_id + type: attribute + id: order_lines + references: + - identifier: + id: campaigns + type: dataset + multivalue: false + sources: + - column: campaign_id + dataType: INT + isNullable: true + target: + id: campaign_id + type: attribute + - identifier: + id: customers + type: dataset + multivalue: false + sources: + - column: customer_id + dataType: INT + isNullable: true + target: + id: customer_id + type: attribute + - identifier: + id: date + type: dataset + multivalue: false + sources: + - column: date + dataType: DATE + isNullable: true + target: + id: date + type: date + - identifier: + id: products + type: dataset + multivalue: false + sources: + - column: product_id + dataType: INT + isNullable: true + target: + id: product_id + type: attribute + tags: + - Order lines + title: Order lines + workspaceDataFilterColumns: + - dataType: STRING + name: wdf__region + - dataType: STRING + name: wdf__state + workspaceDataFilterReferences: + - filterColumn: wdf__region + filterColumnDataType: STRING + filterId: + id: wdf__region + type: workspaceDataFilter + - aggregatedFacts: [] + attributes: + - description: Product id + id: product_id + isNullable: false + labels: [] + sourceColumn: product_id + sourceColumnDataType: INT + tags: + - Products + title: Product id + - description: Product name + id: product_name + isNullable: true + labels: [] + sourceColumn: product_name + sourceColumnDataType: STRING + tags: + - Products + title: Product name + - description: Category + id: products.category + isNullable: true + labels: [] + sourceColumn: category + sourceColumnDataType: STRING + tags: + - Products + title: Category + dataSourceTableId: + dataSourceId: demo-test-ds + id: products + path: + - demo + - products + type: dataSource + description: Products + facts: [] + grain: + - id: product_id + type: attribute + id: products + references: [] + tags: + - Products + title: Products + dateInstances: + - description: '' + granularities: + - DAY + - WEEK + - MONTH + - QUARTER + - YEAR + granularitiesFormatting: + titleBase: '' + titlePattern: '%titleBase - %granularityTitle' + id: date + tags: + - Date + title: Date + name: Demo + permissions: + - assignee: + id: demo2 + type: user + name: ANALYZE + - assignee: + id: demoGroup + type: userGroup + name: VIEW + settings: [] + userDataFilters: [] + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: [] + id: demo_west + model: + analytics: + analyticalDashboardExtensions: + - id: campaign + permissions: + - assigneeRule: + type: allWorkspaceUsers + name: VIEW + analyticalDashboards: [] + attributeHierarchies: [] + dashboardPlugins: [] + exportDefinitions: [] + filterContexts: [] + memoryItems: [] + metrics: [] + parameters: [] + visualizationObjects: [] + ldm: + datasetExtensions: + - id: order_lines + workspaceDataFilterReferences: + - filterColumn: wdf__state + filterColumnDataType: STRING + filterId: + id: wdf__state + type: workspaceDataFilter + datasets: [] + dateInstances: [] + name: Demo West + parent: + id: demo + type: workspace + permissions: [] + settings: [] + userDataFilters: [] + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: [] + id: demo_west_california + model: + analytics: + analyticalDashboardExtensions: [] + analyticalDashboards: [] + attributeHierarchies: [] + dashboardPlugins: [] + exportDefinitions: [] + filterContexts: [] + memoryItems: [] + metrics: [] + parameters: [] + visualizationObjects: [] + ldm: + datasets: [] + dateInstances: [] + name: Demo West California + parent: + id: demo_west + type: workspace + permissions: [] + settings: [] + userDataFilters: [] + headers: + Content-Type: + - application/json + DATE: *id001 + Expires: + - '0' + Pragma: + - no-cache + X-Content-Type-Options: + - nosniff + X-GDC-TRACE-ID: *id001 + status: + code: 200 + message: OK + - request: + body: + agents: + - aiKnowledge: true + availableToAll: true + enabled: true + id: default-ai-assistant + name: Default GoodData AI Assistant + skillsMode: all + customGeoCollections: [] + dataSources: + - alternativeDataSourceId: ds-put-abc-id + authenticationType: USERNAME_PASSWORD + id: demo-test-ds + name: demo-test-ds + permissions: + - assignee: + id: demo2 + type: user + name: MANAGE + - assignee: + id: demoGroup + type: userGroup + name: USE + schema: demo + type: POSTGRESQL + url: jdbc:postgresql://postgres:5432/tiger?sslmode=prefer + username: postgres + exportTemplates: [] + identityProviders: + - id: defaultIdp + idpType: DEX_IDP + ipAllowlistPolicies: [] + jwks: [] + notificationChannels: [] + organization: + allowedOrigins: [] + colorPalettes: [] + cspDirectives: [] + earlyAccess: enableAlerting + earlyAccessValues: + - enableAlerting + - enableSmtp + - enablePreAggregationDatasets + - enableScheduling + - enableCompositeGrain + - enableUserManagement + - enableRawExports + - enableFlexibleDashboardLayout + hostname: localhost + id: default + identityProvider: + id: defaultIdp + type: identityProvider + name: Default Organization + permissions: + - assignee: + id: adminGroup + type: userGroup + name: MANAGE + settings: [] + themes: [] + userGroups: + - id: adminGroup + permissions: [] + - id: adminQA1Group + parents: + - id: adminGroup + type: userGroup + permissions: [] + - id: demoGroup + name: demo group + permissions: [] + - id: visitorsGroup + name: visitors + parents: + - id: demoGroup + type: userGroup + permissions: [] + users: + - id: admin + permissions: [] + settings: [] + userGroups: + - id: adminGroup + type: userGroup + - authId: NORMALIZED_AUTH_ID + email: demo@example.com + firstname: Demo + id: demo + lastname: User + permissions: [] + settings: [] + userGroups: + - id: adminGroup + type: userGroup + - authId: NORMALIZED_AUTH_ID + id: demo2 + permissions: [] + settings: [] + userGroups: + - id: demoGroup + type: userGroup + workspaceDataFilters: + - columnName: wdf__region + id: wdf__region + title: Customer region + workspace: + id: demo + type: workspace + workspaceDataFilterSettings: + - filterValues: + - West + id: region_west + title: Region West + workspace: + id: demo_west + type: workspace + - columnName: wdf__state + id: wdf__state + title: Customer state + workspace: + id: demo_west + type: workspace + workspaceDataFilterSettings: + - filterValues: + - California + id: region_west_california + title: Region West California + workspace: + id: demo_west_california + type: workspace + workspaces: + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: + - assignee: + id: demo2 + type: user + name: MANAGE + - assignee: + id: demoGroup + type: userGroup + name: ANALYZE + id: demo + model: + analytics: + analyticalDashboardExtensions: [] + analyticalDashboards: + - content: + filterContextRef: + identifier: + id: campaign_name_filter + type: filterContext + layout: + sections: + - header: + description: The first insight shows a breakdown of + spend by category and campaign. The second shows revenue + per $ spend, for each campaign, to demonstrate, how + campaigns are successful. + title: Spend breakdown and Revenue + items: + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: campaign_spend + type: visualizationObject + properties: {} + title: Campaign Spend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: revenue_per_usd_vs_spend_by_campaign + type: visualizationObject + properties: {} + title: Revenue per $ vs Spend by Campaign + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: campaign + permissions: + - assigneeRule: + type: allWorkspaceUsers + name: VIEW + title: Campaign + - content: + filterContextRef: + identifier: + id: campaign_name_filter + type: filterContext + layout: + sections: + - items: + - size: + xl: + gridWidth: 12 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: top_10_products + type: visualizationObject + properties: {} + title: DHO simple + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + plugins: + - plugin: + identifier: + id: dashboard_plugin_1 + type: dashboardPlugin + version: '2' + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: dashboard_plugin + title: Dashboard plugin + - content: + filterContextRef: + identifier: + id: region_filter + type: filterContext + layout: + sections: + - items: + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: top_10_products + type: visualizationObject + properties: {} + title: Top 10 Products + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: revenue_trend + type: visualizationObject + properties: {} + title: Revenue Trend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: customers_trend + type: visualizationObject + properties: {} + title: Customers Trend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_categories_pie_chart + type: visualizationObject + properties: {} + title: Product Categories Pie Chart + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_breakdown + type: visualizationObject + properties: {} + title: Product Breakdown + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_saleability + type: visualizationObject + properties: {} + title: Product Saleability + type: insight + - size: + xl: + gridWidth: 12 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: percent_revenue_per_product_by_customer_and_category + type: visualizationObject + properties: {} + title: '% Revenue per Product by Customer and Category' + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: product_and_category + title: Product & Category + attributeHierarchies: [] + dashboardPlugins: + - content: + url: https://www.example.com + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: Testing record dashboard_plugin_1 + id: dashboard_plugin_1 + title: dashboard_plugin_1 + - content: + url: https://www.example.com + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: Testing record dashboard_plugin_2 + id: dashboard_plugin_2 + title: dashboard_plugin_2 + exportDefinitions: [] + filterContexts: + - content: + filters: + - dateFilter: + from: '0' + granularity: GDC.time.month + to: '0' + type: relative + - attributeFilter: + attributeElements: + uris: [] + displayForm: + identifier: + id: campaign_name + type: label + filterElementsBy: [] + localIdentifier: 14b0807447ef4bc28f43e4fc5c337d1d + negativeSelection: true + version: '2' + description: '' + id: campaign_name_filter + title: filterContext + - content: + filters: + - attributeFilter: + attributeElements: + uris: [] + displayForm: + identifier: + id: region + type: label + filterElementsBy: [] + localIdentifier: 2d5ef8df82444f6ba27b45f0990ee6af + negativeSelection: true + version: '2' + description: '' + id: region_filter + title: filterContext + memoryItems: [] + metrics: + - content: + format: '#,##0' + maql: SELECT COUNT({attribute/customer_id},{attribute/order_line_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_active_customers + title: '# of Active Customers' + - content: + format: '#,##0' + maql: SELECT COUNT({attribute/order_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_orders + title: '# of Orders' + - content: + format: '#,##0' + maql: 'SELECT {metric/amount_of_active_customers} WHERE (SELECT + {metric/revenue} BY {attribute/customer_id}) > 10000 ' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_top_customers + title: '# of Top Customers' + - content: + format: '#,##0.00' + maql: SELECT {metric/amount_of_orders} WHERE NOT ({label/order_status} + IN ("Returned", "Canceled")) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: amount_of_valid_orders + title: '# of Valid Orders' + - content: + format: $#,##0 + maql: SELECT SUM({fact/spend}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: campaign_spend + title: Campaign Spend + - content: + format: $#,##0 + maql: SELECT SUM({fact/price}*{fact/quantity}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: order_amount + title: Order Amount + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / {metric/total_revenue} + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue + title: '% Revenue' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10}\ + \ BY {attribute/customer_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_customers + title: '% Revenue from Top 10 Customers' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10_percent}\ + \ BY {attribute/customer_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_percent_customers + title: '% Revenue from Top 10% Customers' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10_percent}\ + \ BY {attribute/product_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_percent_products + title: '% Revenue from Top 10% Products' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10}\ + \ BY {attribute/product_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_products + title: '% Revenue from Top 10 Products' + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / (SELECT {metric/revenue} BY + {attribute/products.category}, ALL OTHER) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_in_category + title: '% Revenue in Category' + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / (SELECT {metric/revenue} BY + ALL {attribute/product_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_per_product + title: '% Revenue per Product' + - content: + format: $#,##0 + maql: SELECT {metric/order_amount} WHERE NOT ({label/order_status} + IN ("Returned", "Canceled")) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: revenue + title: Revenue + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Clothing") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-clothing + title: Revenue (Clothing) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ( "Electronics") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-electronic + title: Revenue (Electronic) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Home") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-home + title: Revenue (Home) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Outdoor") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-outdoor + title: Revenue (Outdoor) + - content: + format: $#,##0.0 + maql: SELECT AVG(SELECT {metric/revenue} BY {attribute/customer_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_customer + title: Revenue per Customer + - content: + format: $#,##0.0 + maql: SELECT {metric/revenue} / {metric/campaign_spend} + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_dollar_spent + title: Revenue per Dollar Spent + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE TOP(10) OF ({metric/revenue}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_top_10 + title: Revenue / Top 10 + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE TOP(10%) OF ({metric/revenue}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_top_10_percent + title: Revenue / Top 10% + - content: + format: $#,##0 + maql: SELECT {metric/revenue} BY ALL OTHER + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: total_revenue + title: Total Revenue + - content: + format: $#,##0 + maql: SELECT {metric/total_revenue} WITHOUT PARENT FILTER + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: total_revenue-no_filters + title: Total Revenue (No Filters) + parameters: [] + visualizationObjects: + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: campaign_spend + type: metric + localIdentifier: d319bcb2d8c04442a684e3b3cd063381 + title: Campaign Spend + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: campaign_channels.category + type: label + localIdentifier: 291c085e7df8420db84117ca49f59c49 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: campaign_name + type: label + localIdentifier: d9dd143d647d4d148405a60ec2cf59bc + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: type + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_channels.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_name + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:treemap + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: campaign_spend + title: Campaign Spend + - content: + buckets: + - items: + - measure: + alias: Active Customers + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: amount_of_active_customers + type: metric + localIdentifier: 2ba0b87b59ca41a4b1530e81a5c1d081 + title: '# of Active Customers' + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_per_customer + type: metric + localIdentifier: ec0606894b9f4897b7beaf1550608928 + title: Revenue per Customer + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 0de7d7f08af7480aa636857a26be72b6 + localIdentifier: view + filters: + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -12 + granularity: GDC.time.month + to: -1 + properties: + controls: + colorMapping: + - color: + type: guid + value: '20' + id: 2ba0b87b59ca41a4b1530e81a5c1d081 + - color: + type: guid + value: '4' + id: ec0606894b9f4897b7beaf1550608928 + dualAxis: true + legend: + position: bottom + primaryChartType: column + secondaryChartType: line + secondary_yaxis: + measures: + - ec0606894b9f4897b7beaf1550608928 + xaxis: + name: + visible: false + rotation: auto + version: '2' + visualizationUrl: local:combo2 + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: customers_trend + title: Customers Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: percent_revenue_per_product + type: metric + localIdentifier: 08d8346c1ce7438994b251991c0fbf65 + title: '% Revenue per Product' + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: b2350c06688b4da9b3833ebcce65527f + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: customer_name + type: label + localIdentifier: 7a4045fd00ac44579f52406df679435f + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 6a003ffd14994237ba64c4a02c488429 + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 75ea396d0c8b48098e31dccf8b5801d3 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: {} + sorts: + - attributeSortItem: + attributeIdentifier: 7a4045fd00ac44579f52406df679435f + direction: asc + version: '2' + visualizationUrl: local:table + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_per_product_by_customer_and_category + title: '% Revenue per Product by Customer and Category' + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: amount_of_active_customers + type: metric + localIdentifier: 1a14cdc1293c46e89a2e25d3e741d235 + title: '# of Active Customers' + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: c1feca1864244ec2ace7a9b9d7fda231 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: region + type: label + localIdentifier: 530cddbd7ca04d039e73462d81ed44d5 + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: region + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -11 + granularity: GDC.time.month + to: 0 + properties: + controls: + legend: + position: bottom + stackMeasuresToPercent: true + version: '2' + visualizationUrl: local:area + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percentage_of_customers_by_region + title: Percentage of Customers by Region + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 590d332ef686468b8878ae41b23341c6 + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: b166c71091864312a14c7ae8ff886ffe + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: e920a50e0bbb49788df0aac53634c1cd + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:treemap + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_breakdown + title: Product Breakdown + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + computeRatio: true + filters: [] + item: + identifier: + id: revenue + type: metric + format: '#,##0.00%' + localIdentifier: 162b857af49d45769bc12604a5c192b9 + title: '% Revenue' + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: fe513cef1c6244a5ac21c5f49c56b108 + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + dataLabels: + visible: auto + legend: + position: bottom + version: '2' + visualizationUrl: local:donut + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_categories_pie_chart + title: Product Categories Pie Chart + - content: + buckets: + - items: + - measure: + alias: Previous Period + definition: + popMeasureDefinition: + measureIdentifier: c82e025fa2db4afea9a600a424591dbe + popAttribute: + identifier: + id: date.year + type: attribute + localIdentifier: c82e025fa2db4afea9a600a424591dbe_pop + - measure: + alias: This Period + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: c82e025fa2db4afea9a600a424591dbe + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: c804ef5ba7944a5a9f360c86a9e95e9a + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -11 + granularity: GDC.time.month + to: 0 + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + stackMeasures: false + xaxis: + name: + visible: false + yaxis: + name: + visible: false + version: '2' + visualizationUrl: local:column + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_revenue_comparison-over_previous_period + title: Product Revenue Comparison (over previous period) + - content: + buckets: + - items: + - measure: + alias: Number of Orders + definition: + measureDefinition: + filters: [] + item: + identifier: + id: amount_of_orders + type: metric + localIdentifier: aeb5d51a162d4b59aba3bd6ddebcc780 + title: '# of Orders' + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 94b3edd3a73c4a48a4d13bbe9442cc98 + title: Revenue + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: d2a991bdd123448eb2be73d79f1180c4 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: + controls: + dataLabels: + visible: auto + grid: + enabled: true + version: '2' + visualizationUrl: local:scatter + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_saleability + title: Product Saleability + - content: + buckets: + - items: + - measure: + alias: Items Sold + definition: + measureDefinition: + aggregation: sum + filters: [] + item: + identifier: + id: quantity + type: fact + format: '#,##0.00' + localIdentifier: 29486504dd0e4a36a18b0b2f792d3a46 + title: Sum of Quantity + - measure: + definition: + measureDefinition: + aggregation: avg + filters: [] + item: + identifier: + id: price + type: fact + format: '#,##0.00' + localIdentifier: aa6391acccf1452f8011201aef9af492 + title: Avg Price + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: percent_revenue_in_category + type: metric + localIdentifier: 2cd39539d8da46c9883e63caa3ba7cc0 + title: '% Revenue in Category' + - measure: + alias: Total Revenue + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 9a0f08331c094c7facf2a0b4f418de0a + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 06bc6b3b9949466494e4f594c11f1bff + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 192668bfb6a74e9ab7b5d1ce7cb68ea3 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: {} + sorts: + - attributeSortItem: + attributeIdentifier: 06bc6b3b9949466494e4f594c11f1bff + direction: asc + version: '2' + visualizationUrl: local:table + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_and_quantity_by_product_and_category + title: Revenue and Quantity by Product and Category + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 7df6c34387744d69b23ec92e1a5cf543 + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 4bb4fc1986c546de9ad976e6ec23fed4 + localIdentifier: trend + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 34bddcb1cd024902a82396216b0fa9d8 + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + granularity: GDC.time.year + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:line + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_by_category_trend + title: Revenue by Category Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 4ae3401bdbba4938afe983df4ba04e1c + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 1c8ba72dbfc84ddd913bf81dc355c427 + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: {} + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_by_product + title: Revenue by Product + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: campaign_spend + type: metric + localIdentifier: 13a50d811e474ac6808d8da7f4673b35 + title: Campaign Spend + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_per_dollar_spent + type: metric + localIdentifier: a0f15e82e6334280a44dbedc7d086e7c + title: Revenue per Dollar Spent + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: campaign_name + type: label + localIdentifier: 1d9fa968bafb423eb29c938dfb1207ff + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_name + type: label + notIn: + values: [] + properties: + controls: + xaxis: + min: '0' + yaxis: + min: '0' + version: '2' + visualizationUrl: local:scatter + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_usd_vs_spend_by_campaign + title: Revenue per $ vs Spend by Campaign + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 60c854969a9c4c278ab596d99c222e92 + title: Revenue + localIdentifier: measures + - items: + - measure: + alias: Number of Orders + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: amount_of_orders + type: metric + localIdentifier: c2fa7ef48cc54af99f8c280eb451e051 + title: '# of Orders' + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 413ac374b65648fa96826ca01d47bdda + localIdentifier: view + filters: + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -3 + granularity: GDC.time.quarter + to: 0 + properties: + controls: + dualAxis: true + legend: + position: bottom + primaryChartType: column + secondaryChartType: line + secondary_yaxis: + measures: + - c2fa7ef48cc54af99f8c280eb451e051 + xaxis: + name: + visible: false + rotation: auto + version: '2' + visualizationUrl: local:combo2 + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_trend + title: Revenue Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_top_10 + type: metric + localIdentifier: 3f127ccfe57a40399e23f9ae2a4ad810 + title: Revenue / Top 10 + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: customer_name + type: label + localIdentifier: f4e39e24f11e4827a191c30d65c89d2c + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: state + type: label + localIdentifier: bbccd430176d428caed54c99afc9589e + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: state + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: top_10_customers + title: Top 10 Customers + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_top_10 + type: metric + localIdentifier: 77dc71bbac92412bac5f94284a5919df + title: Revenue / Top 10 + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 781952e728204dcf923142910cc22ae2 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: fe513cef1c6244a5ac21c5f49c56b108 + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: top_10_products + title: Top 10 Products + ldm: + datasets: + - aggregatedFacts: [] + attributes: + - description: Campaign channel id + id: campaign_channel_id + isNullable: false + labels: [] + sourceColumn: campaign_channel_id + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Campaign channel id + - description: Category + id: campaign_channels.category + isNullable: true + labels: [] + sourceColumn: category + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Category + - description: Type + id: type + isNullable: true + labels: [] + sourceColumn: type + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Type + dataSourceTableId: + dataSourceId: demo-test-ds + id: campaign_channels + path: + - demo + - campaign_channels + type: dataSource + description: Campaign channels + facts: + - description: Budget + id: budget + isNullable: true + sourceColumn: budget + sourceColumnDataType: NUMERIC + tags: + - Campaign channels + title: Budget + - description: Spend + id: spend + isNullable: true + sourceColumn: spend + sourceColumnDataType: NUMERIC + tags: + - Campaign channels + title: Spend + grain: + - id: campaign_channel_id + type: attribute + id: campaign_channels + references: + - identifier: + id: campaigns + type: dataset + multivalue: false + sources: + - column: campaign_id + dataType: INT + isNullable: true + target: + id: campaign_id + type: attribute + tags: + - Campaign channels + title: Campaign channels + - aggregatedFacts: + - description: Budget Agg + id: budget_agg + sourceColumn: budget + sourceColumnDataType: INT + sourceFactReference: + operation: SUM + reference: + id: budget + type: fact + tags: + - Campaign channels per category + attributes: [] + description: Campaign channels per categories + facts: [] + grain: [] + id: campaign_channels_per_category + precedence: 1 + references: + - identifier: + id: campaign_channels + type: dataset + multivalue: false + sources: + - column: campaign_channel_id + dataType: STRING + isNullable: true + target: + id: campaign_channel_id + type: attribute + sql: + dataSourceId: demo-test-ds + statement: SELECT category, SUM(budget) FROM campaign_channels + GROUP BY category + tags: + - Campaign channels per category + title: Campaign channels per category + - aggregatedFacts: [] + attributes: + - description: Campaign id + id: campaign_id + isNullable: false + labels: [] + sourceColumn: campaign_id + sourceColumnDataType: INT + tags: + - Campaigns + title: Campaign id + - description: Campaign name + id: campaign_name + isNullable: true + labels: [] + sourceColumn: campaign_name + sourceColumnDataType: STRING + tags: + - Campaigns + title: Campaign name + dataSourceTableId: + dataSourceId: demo-test-ds + id: campaigns + path: + - demo + - campaigns + type: dataSource + description: Campaigns + facts: [] + grain: + - id: campaign_id + type: attribute + id: campaigns + references: [] + tags: + - Campaigns + title: Campaigns + - aggregatedFacts: [] + attributes: + - description: Customer id + id: customer_id + isNullable: false + labels: [] + sourceColumn: customer_id + sourceColumnDataType: INT + tags: + - Customers + title: Customer id + - description: Customer name + id: customer_name + isNullable: true + labels: [] + sourceColumn: customer_name + sourceColumnDataType: STRING + tags: + - Customers + title: Customer name + - description: Region + id: region + isNullable: true + labels: [] + sourceColumn: region + sourceColumnDataType: STRING + tags: + - Customers + title: Region + - description: State + id: state + isNullable: true + labels: + - description: Location + id: geo__state__location + isNullable: true + sourceColumn: geo__state__location + sourceColumnDataType: STRING + tags: + - Customers + title: Location + valueType: GEO + sourceColumn: state + sourceColumnDataType: STRING + tags: + - Customers + title: State + dataSourceTableId: + dataSourceId: demo-test-ds + id: customers + path: + - demo + - customers + type: dataSource + description: Customers + facts: [] + grain: + - id: customer_id + type: attribute + id: customers + references: [] + tags: + - Customers + title: Customers + - aggregatedFacts: [] + attributes: + - description: Order id + id: order_id + isNullable: true + labels: [] + sourceColumn: order_id + sourceColumnDataType: STRING + tags: + - Order lines + title: Order id + - description: Order line id + id: order_line_id + isNullable: false + labels: [] + sourceColumn: order_line_id + sourceColumnDataType: STRING + tags: + - Order lines + title: Order line id + - description: Order status + id: order_status + isNullable: true + labels: [] + sourceColumn: order_status + sourceColumnDataType: STRING + tags: + - Order lines + title: Order status + dataSourceTableId: + dataSourceId: demo-test-ds + id: order_lines + path: + - demo + - order_lines + type: dataSource + description: Order lines + facts: + - description: Price + id: price + isNullable: true + sourceColumn: price + sourceColumnDataType: NUMERIC + tags: + - Order lines + title: Price + - description: Quantity + id: quantity + isNullable: true + sourceColumn: quantity + sourceColumnDataType: NUMERIC + tags: + - Order lines + title: Quantity + grain: + - id: order_line_id + type: attribute + id: order_lines + references: + - identifier: + id: campaigns + type: dataset + multivalue: false + sources: + - column: campaign_id + dataType: INT + isNullable: true + target: + id: campaign_id + type: attribute + - identifier: + id: customers + type: dataset + multivalue: false + sources: + - column: customer_id + dataType: INT + isNullable: true + target: + id: customer_id + type: attribute + - identifier: + id: date + type: dataset + multivalue: false + sources: + - column: date + dataType: DATE + isNullable: true + target: + id: date + type: date + - identifier: + id: products + type: dataset + multivalue: false + sources: + - column: product_id + dataType: INT + isNullable: true + target: + id: product_id + type: attribute + tags: + - Order lines + title: Order lines + workspaceDataFilterColumns: + - dataType: STRING + name: wdf__region + - dataType: STRING + name: wdf__state + workspaceDataFilterReferences: + - filterColumn: wdf__region + filterColumnDataType: STRING + filterId: + id: wdf__region + type: workspaceDataFilter + - aggregatedFacts: [] + attributes: + - description: Product id + id: product_id + isNullable: false + labels: [] + sourceColumn: product_id + sourceColumnDataType: INT + tags: + - Products + title: Product id + - description: Product name + id: product_name + isNullable: true + labels: [] + sourceColumn: product_name + sourceColumnDataType: STRING + tags: + - Products + title: Product name + - description: Category + id: products.category + isNullable: true + labels: [] + sourceColumn: category + sourceColumnDataType: STRING + tags: + - Products + title: Category + dataSourceTableId: + dataSourceId: demo-test-ds + id: products + path: + - demo + - products + type: dataSource + description: Products + facts: [] + grain: + - id: product_id + type: attribute + id: products + references: [] + tags: + - Products + title: Products + dateInstances: + - description: '' + granularities: + - DAY + - WEEK + - MONTH + - QUARTER + - YEAR + granularitiesFormatting: + titleBase: '' + titlePattern: '%titleBase - %granularityTitle' + id: date + tags: + - Date + title: Date + name: Demo + permissions: + - assignee: + id: demo2 + type: user + name: ANALYZE + - assignee: + id: demoGroup + type: userGroup + name: VIEW + settings: [] + userDataFilters: [] + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: [] + id: demo_west + model: + analytics: + analyticalDashboardExtensions: + - id: campaign + permissions: + - assigneeRule: + type: allWorkspaceUsers + name: VIEW + analyticalDashboards: [] + attributeHierarchies: [] + dashboardPlugins: [] + exportDefinitions: [] + filterContexts: [] + memoryItems: [] + metrics: [] + parameters: [] + visualizationObjects: [] + ldm: + datasetExtensions: + - id: order_lines + workspaceDataFilterReferences: + - filterColumn: wdf__state + filterColumnDataType: STRING + filterId: + id: wdf__state + type: workspaceDataFilter + datasets: [] + dateInstances: [] + name: Demo West + parent: + id: demo + type: workspace + permissions: [] + settings: [] + userDataFilters: [] + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: [] + id: demo_west_california + model: + analytics: + analyticalDashboardExtensions: [] + analyticalDashboards: [] + attributeHierarchies: [] + dashboardPlugins: [] + exportDefinitions: [] + filterContexts: [] + memoryItems: [] + metrics: [] + parameters: [] + visualizationObjects: [] + ldm: + datasets: [] + dateInstances: [] + name: Demo West California + parent: + id: demo_west + type: workspace + permissions: [] + settings: [] + userDataFilters: [] + headers: + Accept-Encoding: + - br, gzip, deflate + Content-Type: + - application/json + X-GDC-VALIDATE-RELATIONS: + - 'true' + X-Requested-With: + - XMLHttpRequest + method: PUT + uri: http://localhost:3000/api/v1/layout/organization + response: + body: + string: + detail: 'Invalid combination of auth properties. Only none, user + password, + token, key-pair or client id + secret are allowed. Datasource: demo-test-ds, + user: , password: , token: , key-pair: , clientId: , + clientSecret: ' + status: 400 + title: Bad Request + traceId: NORMALIZED_TRACE_ID_000000000000 + headers: + Content-Type: + - application/problem+json + DATE: *id001 + Expires: + - '0' + Pragma: + - no-cache + X-Content-Type-Options: + - nosniff + X-GDC-TRACE-ID: *id001 + status: + code: 400 + message: Bad Request + - request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - br, gzip, deflate + X-GDC-VALIDATE-RELATIONS: + - 'true' + X-Requested-With: + - XMLHttpRequest + method: GET + uri: http://localhost:3000/api/v1/layout/organization + response: + body: + string: + agents: + - aiKnowledge: true + availableToAll: true + enabled: true + id: default-ai-assistant + name: Default GoodData AI Assistant + skillsMode: all + customGeoCollections: [] + dataSources: + - alternativeDataSourceId: ds-put-abc-id + authenticationType: USERNAME_PASSWORD + id: demo-test-ds + name: demo-test-ds + permissions: + - assignee: + id: demo2 + type: user + name: MANAGE + - assignee: + id: demoGroup + type: userGroup + name: USE + schema: demo + type: POSTGRESQL + url: jdbc:postgresql://postgres:5432/tiger?sslmode=prefer + username: postgres + exportTemplates: [] + identityProviders: + - id: defaultIdp + idpType: DEX_IDP + ipAllowlistPolicies: [] + jwks: [] + notificationChannels: [] + organization: + allowedOrigins: [] + colorPalettes: [] + cspDirectives: [] + earlyAccess: enableAlerting + earlyAccessValues: + - enableAlerting + - enableSmtp + - enablePreAggregationDatasets + - enableScheduling + - enableCompositeGrain + - enableUserManagement + - enableRawExports + - enableFlexibleDashboardLayout + hostname: localhost + id: default + identityProvider: + id: defaultIdp + type: identityProvider + name: Default Organization + permissions: + - assignee: + id: adminGroup + type: userGroup + name: MANAGE + settings: [] + themes: [] + userGroups: + - id: adminGroup + permissions: [] + - id: adminQA1Group + parents: + - id: adminGroup + type: userGroup + permissions: [] + - id: demoGroup + name: demo group + permissions: [] + - id: visitorsGroup + name: visitors + parents: + - id: demoGroup + type: userGroup + permissions: [] + users: + - id: admin + permissions: [] + settings: [] + userGroups: + - id: adminGroup + type: userGroup + - authId: NORMALIZED_AUTH_ID + email: demo@example.com + firstname: Demo + id: demo + lastname: User + permissions: [] + settings: [] + userGroups: + - id: adminGroup + type: userGroup + - authId: NORMALIZED_AUTH_ID + id: demo2 + permissions: [] + settings: [] + userGroups: + - id: demoGroup + type: userGroup + workspaceDataFilters: + - columnName: wdf__region + id: wdf__region + title: Customer region + workspace: + id: demo + type: workspace + workspaceDataFilterSettings: + - filterValues: + - West + id: region_west + title: Region West + workspace: + id: demo_west + type: workspace + - columnName: wdf__state + id: wdf__state + title: Customer state + workspace: + id: demo_west + type: workspace + workspaceDataFilterSettings: + - filterValues: + - California + id: region_west_california + title: Region West California + workspace: + id: demo_west_california + type: workspace + workspaces: + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: + - assignee: + id: demo2 + type: user + name: MANAGE + - assignee: + id: demoGroup + type: userGroup + name: ANALYZE + id: demo + model: + analytics: + analyticalDashboardExtensions: [] + analyticalDashboards: + - content: + filterContextRef: + identifier: + id: campaign_name_filter + type: filterContext + layout: + sections: + - header: + description: The first insight shows a breakdown of + spend by category and campaign. The second shows + revenue per $ spend, for each campaign, to demonstrate, + how campaigns are successful. + title: Spend breakdown and Revenue + items: + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: campaign_spend + type: visualizationObject + properties: {} + title: Campaign Spend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: revenue_per_usd_vs_spend_by_campaign + type: visualizationObject + properties: {} + title: Revenue per $ vs Spend by Campaign + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: campaign + permissions: + - assigneeRule: + type: allWorkspaceUsers + name: VIEW + title: Campaign + - content: + filterContextRef: + identifier: + id: campaign_name_filter + type: filterContext + layout: + sections: + - items: + - size: + xl: + gridWidth: 12 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: top_10_products + type: visualizationObject + properties: {} + title: DHO simple + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + plugins: + - plugin: + identifier: + id: dashboard_plugin_1 + type: dashboardPlugin + version: '2' + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: dashboard_plugin + title: Dashboard plugin + - content: + filterContextRef: + identifier: + id: region_filter + type: filterContext + layout: + sections: + - items: + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: top_10_products + type: visualizationObject + properties: {} + title: Top 10 Products + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: revenue_trend + type: visualizationObject + properties: {} + title: Revenue Trend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: customers_trend + type: visualizationObject + properties: {} + title: Customers Trend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_categories_pie_chart + type: visualizationObject + properties: {} + title: Product Categories Pie Chart + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_breakdown + type: visualizationObject + properties: {} + title: Product Breakdown + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_saleability + type: visualizationObject + properties: {} + title: Product Saleability + type: insight + - size: + xl: + gridWidth: 12 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: percent_revenue_per_product_by_customer_and_category + type: visualizationObject + properties: {} + title: '% Revenue per Product by Customer and + Category' + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: product_and_category + title: Product & Category + attributeHierarchies: [] + dashboardPlugins: + - content: + url: https://www.example.com + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: Testing record dashboard_plugin_1 + id: dashboard_plugin_1 + title: dashboard_plugin_1 + - content: + url: https://www.example.com + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: Testing record dashboard_plugin_2 + id: dashboard_plugin_2 + title: dashboard_plugin_2 + exportDefinitions: [] + filterContexts: + - content: + filters: + - dateFilter: + from: '0' + granularity: GDC.time.month + to: '0' + type: relative + - attributeFilter: + attributeElements: + uris: [] + displayForm: + identifier: + id: campaign_name + type: label + filterElementsBy: [] + localIdentifier: 14b0807447ef4bc28f43e4fc5c337d1d + negativeSelection: true + version: '2' + description: '' + id: campaign_name_filter + title: filterContext + - content: + filters: + - attributeFilter: + attributeElements: + uris: [] + displayForm: + identifier: + id: region + type: label + filterElementsBy: [] + localIdentifier: 2d5ef8df82444f6ba27b45f0990ee6af + negativeSelection: true + version: '2' + description: '' + id: region_filter + title: filterContext + memoryItems: [] + metrics: + - content: + format: '#,##0' + maql: SELECT COUNT({attribute/customer_id},{attribute/order_line_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_active_customers + title: '# of Active Customers' + - content: + format: '#,##0' + maql: SELECT COUNT({attribute/order_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_orders + title: '# of Orders' + - content: + format: '#,##0' + maql: 'SELECT {metric/amount_of_active_customers} WHERE (SELECT + {metric/revenue} BY {attribute/customer_id}) > 10000 ' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_top_customers + title: '# of Top Customers' + - content: + format: '#,##0.00' + maql: SELECT {metric/amount_of_orders} WHERE NOT ({label/order_status} + IN ("Returned", "Canceled")) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: amount_of_valid_orders + title: '# of Valid Orders' + - content: + format: $#,##0 + maql: SELECT SUM({fact/spend}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: campaign_spend + title: Campaign Spend + - content: + format: $#,##0 + maql: SELECT SUM({fact/price}*{fact/quantity}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: order_amount + title: Order Amount + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / {metric/total_revenue} + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue + title: '% Revenue' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10}\ + \ BY {attribute/customer_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_customers + title: '% Revenue from Top 10 Customers' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10_percent}\ + \ BY {attribute/customer_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_percent_customers + title: '% Revenue from Top 10% Customers' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10_percent}\ + \ BY {attribute/product_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_percent_products + title: '% Revenue from Top 10% Products' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10}\ + \ BY {attribute/product_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_products + title: '% Revenue from Top 10 Products' + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / (SELECT {metric/revenue} BY + {attribute/products.category}, ALL OTHER) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_in_category + title: '% Revenue in Category' + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / (SELECT {metric/revenue} BY + ALL {attribute/product_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_per_product + title: '% Revenue per Product' + - content: + format: $#,##0 + maql: SELECT {metric/order_amount} WHERE NOT ({label/order_status} + IN ("Returned", "Canceled")) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: revenue + title: Revenue + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Clothing") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-clothing + title: Revenue (Clothing) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ( "Electronics") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-electronic + title: Revenue (Electronic) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Home") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-home + title: Revenue (Home) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Outdoor") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-outdoor + title: Revenue (Outdoor) + - content: + format: $#,##0.0 + maql: SELECT AVG(SELECT {metric/revenue} BY {attribute/customer_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_customer + title: Revenue per Customer + - content: + format: $#,##0.0 + maql: SELECT {metric/revenue} / {metric/campaign_spend} + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_dollar_spent + title: Revenue per Dollar Spent + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE TOP(10) OF ({metric/revenue}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_top_10 + title: Revenue / Top 10 + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE TOP(10%) OF ({metric/revenue}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_top_10_percent + title: Revenue / Top 10% + - content: + format: $#,##0 + maql: SELECT {metric/revenue} BY ALL OTHER + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: total_revenue + title: Total Revenue + - content: + format: $#,##0 + maql: SELECT {metric/total_revenue} WITHOUT PARENT FILTER + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: total_revenue-no_filters + title: Total Revenue (No Filters) + parameters: [] + visualizationObjects: + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: campaign_spend + type: metric + localIdentifier: d319bcb2d8c04442a684e3b3cd063381 + title: Campaign Spend + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: campaign_channels.category + type: label + localIdentifier: 291c085e7df8420db84117ca49f59c49 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: campaign_name + type: label + localIdentifier: d9dd143d647d4d148405a60ec2cf59bc + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: type + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_channels.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_name + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:treemap + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: campaign_spend + title: Campaign Spend + - content: + buckets: + - items: + - measure: + alias: Active Customers + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: amount_of_active_customers + type: metric + localIdentifier: 2ba0b87b59ca41a4b1530e81a5c1d081 + title: '# of Active Customers' + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_per_customer + type: metric + localIdentifier: ec0606894b9f4897b7beaf1550608928 + title: Revenue per Customer + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 0de7d7f08af7480aa636857a26be72b6 + localIdentifier: view + filters: + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -12 + granularity: GDC.time.month + to: -1 + properties: + controls: + colorMapping: + - color: + type: guid + value: '20' + id: 2ba0b87b59ca41a4b1530e81a5c1d081 + - color: + type: guid + value: '4' + id: ec0606894b9f4897b7beaf1550608928 + dualAxis: true + legend: + position: bottom + primaryChartType: column + secondaryChartType: line + secondary_yaxis: + measures: + - ec0606894b9f4897b7beaf1550608928 + xaxis: + name: + visible: false + rotation: auto + version: '2' + visualizationUrl: local:combo2 + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: customers_trend + title: Customers Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: percent_revenue_per_product + type: metric + localIdentifier: 08d8346c1ce7438994b251991c0fbf65 + title: '% Revenue per Product' + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: b2350c06688b4da9b3833ebcce65527f + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: customer_name + type: label + localIdentifier: 7a4045fd00ac44579f52406df679435f + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 6a003ffd14994237ba64c4a02c488429 + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 75ea396d0c8b48098e31dccf8b5801d3 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: {} + sorts: + - attributeSortItem: + attributeIdentifier: 7a4045fd00ac44579f52406df679435f + direction: asc + version: '2' + visualizationUrl: local:table + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_per_product_by_customer_and_category + title: '% Revenue per Product by Customer and Category' + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: amount_of_active_customers + type: metric + localIdentifier: 1a14cdc1293c46e89a2e25d3e741d235 + title: '# of Active Customers' + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: c1feca1864244ec2ace7a9b9d7fda231 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: region + type: label + localIdentifier: 530cddbd7ca04d039e73462d81ed44d5 + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: region + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -11 + granularity: GDC.time.month + to: 0 + properties: + controls: + legend: + position: bottom + stackMeasuresToPercent: true + version: '2' + visualizationUrl: local:area + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percentage_of_customers_by_region + title: Percentage of Customers by Region + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 590d332ef686468b8878ae41b23341c6 + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: b166c71091864312a14c7ae8ff886ffe + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: e920a50e0bbb49788df0aac53634c1cd + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:treemap + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_breakdown + title: Product Breakdown + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + computeRatio: true + filters: [] + item: + identifier: + id: revenue + type: metric + format: '#,##0.00%' + localIdentifier: 162b857af49d45769bc12604a5c192b9 + title: '% Revenue' + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: fe513cef1c6244a5ac21c5f49c56b108 + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + dataLabels: + visible: auto + legend: + position: bottom + version: '2' + visualizationUrl: local:donut + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_categories_pie_chart + title: Product Categories Pie Chart + - content: + buckets: + - items: + - measure: + alias: Previous Period + definition: + popMeasureDefinition: + measureIdentifier: c82e025fa2db4afea9a600a424591dbe + popAttribute: + identifier: + id: date.year + type: attribute + localIdentifier: c82e025fa2db4afea9a600a424591dbe_pop + - measure: + alias: This Period + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: c82e025fa2db4afea9a600a424591dbe + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: c804ef5ba7944a5a9f360c86a9e95e9a + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -11 + granularity: GDC.time.month + to: 0 + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + stackMeasures: false + xaxis: + name: + visible: false + yaxis: + name: + visible: false + version: '2' + visualizationUrl: local:column + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_revenue_comparison-over_previous_period + title: Product Revenue Comparison (over previous period) + - content: + buckets: + - items: + - measure: + alias: Number of Orders + definition: + measureDefinition: + filters: [] + item: + identifier: + id: amount_of_orders + type: metric + localIdentifier: aeb5d51a162d4b59aba3bd6ddebcc780 + title: '# of Orders' + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 94b3edd3a73c4a48a4d13bbe9442cc98 + title: Revenue + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: d2a991bdd123448eb2be73d79f1180c4 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: + controls: + dataLabels: + visible: auto + grid: + enabled: true + version: '2' + visualizationUrl: local:scatter + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_saleability + title: Product Saleability + - content: + buckets: + - items: + - measure: + alias: Items Sold + definition: + measureDefinition: + aggregation: sum + filters: [] + item: + identifier: + id: quantity + type: fact + format: '#,##0.00' + localIdentifier: 29486504dd0e4a36a18b0b2f792d3a46 + title: Sum of Quantity + - measure: + definition: + measureDefinition: + aggregation: avg + filters: [] + item: + identifier: + id: price + type: fact + format: '#,##0.00' + localIdentifier: aa6391acccf1452f8011201aef9af492 + title: Avg Price + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: percent_revenue_in_category + type: metric + localIdentifier: 2cd39539d8da46c9883e63caa3ba7cc0 + title: '% Revenue in Category' + - measure: + alias: Total Revenue + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 9a0f08331c094c7facf2a0b4f418de0a + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 06bc6b3b9949466494e4f594c11f1bff + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 192668bfb6a74e9ab7b5d1ce7cb68ea3 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: {} + sorts: + - attributeSortItem: + attributeIdentifier: 06bc6b3b9949466494e4f594c11f1bff + direction: asc + version: '2' + visualizationUrl: local:table + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_and_quantity_by_product_and_category + title: Revenue and Quantity by Product and Category + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 7df6c34387744d69b23ec92e1a5cf543 + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 4bb4fc1986c546de9ad976e6ec23fed4 + localIdentifier: trend + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 34bddcb1cd024902a82396216b0fa9d8 + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + granularity: GDC.time.year + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:line + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_by_category_trend + title: Revenue by Category Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 4ae3401bdbba4938afe983df4ba04e1c + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 1c8ba72dbfc84ddd913bf81dc355c427 + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: {} + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_by_product + title: Revenue by Product + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: campaign_spend + type: metric + localIdentifier: 13a50d811e474ac6808d8da7f4673b35 + title: Campaign Spend + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_per_dollar_spent + type: metric + localIdentifier: a0f15e82e6334280a44dbedc7d086e7c + title: Revenue per Dollar Spent + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: campaign_name + type: label + localIdentifier: 1d9fa968bafb423eb29c938dfb1207ff + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_name + type: label + notIn: + values: [] + properties: + controls: + xaxis: + min: '0' + yaxis: + min: '0' + version: '2' + visualizationUrl: local:scatter + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_usd_vs_spend_by_campaign + title: Revenue per $ vs Spend by Campaign + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 60c854969a9c4c278ab596d99c222e92 + title: Revenue + localIdentifier: measures + - items: + - measure: + alias: Number of Orders + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: amount_of_orders + type: metric + localIdentifier: c2fa7ef48cc54af99f8c280eb451e051 + title: '# of Orders' + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 413ac374b65648fa96826ca01d47bdda + localIdentifier: view + filters: + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -3 + granularity: GDC.time.quarter + to: 0 + properties: + controls: + dualAxis: true + legend: + position: bottom + primaryChartType: column + secondaryChartType: line + secondary_yaxis: + measures: + - c2fa7ef48cc54af99f8c280eb451e051 + xaxis: + name: + visible: false + rotation: auto + version: '2' + visualizationUrl: local:combo2 + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_trend + title: Revenue Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_top_10 + type: metric + localIdentifier: 3f127ccfe57a40399e23f9ae2a4ad810 + title: Revenue / Top 10 + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: customer_name + type: label + localIdentifier: f4e39e24f11e4827a191c30d65c89d2c + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: state + type: label + localIdentifier: bbccd430176d428caed54c99afc9589e + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: state + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: top_10_customers + title: Top 10 Customers + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_top_10 + type: metric + localIdentifier: 77dc71bbac92412bac5f94284a5919df + title: Revenue / Top 10 + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 781952e728204dcf923142910cc22ae2 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: fe513cef1c6244a5ac21c5f49c56b108 + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: top_10_products + title: Top 10 Products + ldm: + datasets: + - aggregatedFacts: [] + attributes: + - description: Campaign channel id + id: campaign_channel_id + isNullable: false + labels: [] + sourceColumn: campaign_channel_id + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Campaign channel id + - description: Category + id: campaign_channels.category + isNullable: true + labels: [] + sourceColumn: category + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Category + - description: Type + id: type + isNullable: true + labels: [] + sourceColumn: type + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Type + dataSourceTableId: + dataSourceId: demo-test-ds + id: campaign_channels + path: + - demo + - campaign_channels + type: dataSource + description: Campaign channels + facts: + - description: Budget + id: budget + isNullable: true + sourceColumn: budget + sourceColumnDataType: NUMERIC + tags: + - Campaign channels + title: Budget + - description: Spend + id: spend + isNullable: true + sourceColumn: spend + sourceColumnDataType: NUMERIC + tags: + - Campaign channels + title: Spend + grain: + - id: campaign_channel_id + type: attribute + id: campaign_channels + references: + - identifier: + id: campaigns + type: dataset + multivalue: false + sources: + - column: campaign_id + dataType: INT + isNullable: true + target: + id: campaign_id + type: attribute + tags: + - Campaign channels + title: Campaign channels + - aggregatedFacts: + - description: Budget Agg + id: budget_agg + sourceColumn: budget + sourceColumnDataType: INT + sourceFactReference: + operation: SUM + reference: + id: budget + type: fact + tags: + - Campaign channels per category + attributes: [] + description: Campaign channels per categories + facts: [] + grain: [] + id: campaign_channels_per_category + precedence: 1 + references: + - identifier: + id: campaign_channels + type: dataset + multivalue: false + sources: + - column: campaign_channel_id + dataType: STRING + isNullable: true + target: + id: campaign_channel_id + type: attribute + sql: + dataSourceId: demo-test-ds + statement: SELECT category, SUM(budget) FROM campaign_channels + GROUP BY category + tags: + - Campaign channels per category + title: Campaign channels per category + - aggregatedFacts: [] + attributes: + - description: Campaign id + id: campaign_id + isNullable: false + labels: [] + sourceColumn: campaign_id + sourceColumnDataType: INT + tags: + - Campaigns + title: Campaign id + - description: Campaign name + id: campaign_name + isNullable: true + labels: [] + sourceColumn: campaign_name + sourceColumnDataType: STRING + tags: + - Campaigns + title: Campaign name + dataSourceTableId: + dataSourceId: demo-test-ds + id: campaigns + path: + - demo + - campaigns + type: dataSource + description: Campaigns + facts: [] + grain: + - id: campaign_id + type: attribute + id: campaigns + references: [] + tags: + - Campaigns + title: Campaigns + - aggregatedFacts: [] + attributes: + - description: Customer id + id: customer_id + isNullable: false + labels: [] + sourceColumn: customer_id + sourceColumnDataType: INT + tags: + - Customers + title: Customer id + - description: Customer name + id: customer_name + isNullable: true + labels: [] + sourceColumn: customer_name + sourceColumnDataType: STRING + tags: + - Customers + title: Customer name + - description: Region + id: region + isNullable: true + labels: [] + sourceColumn: region + sourceColumnDataType: STRING + tags: + - Customers + title: Region + - description: State + id: state + isNullable: true + labels: + - description: Location + id: geo__state__location + isNullable: true + sourceColumn: geo__state__location + sourceColumnDataType: STRING + tags: + - Customers + title: Location + valueType: GEO + sourceColumn: state + sourceColumnDataType: STRING + tags: + - Customers + title: State + dataSourceTableId: + dataSourceId: demo-test-ds + id: customers + path: + - demo + - customers + type: dataSource + description: Customers + facts: [] + grain: + - id: customer_id + type: attribute + id: customers + references: [] + tags: + - Customers + title: Customers + - aggregatedFacts: [] + attributes: + - description: Order id + id: order_id + isNullable: true + labels: [] + sourceColumn: order_id + sourceColumnDataType: STRING + tags: + - Order lines + title: Order id + - description: Order line id + id: order_line_id + isNullable: false + labels: [] + sourceColumn: order_line_id + sourceColumnDataType: STRING + tags: + - Order lines + title: Order line id + - description: Order status + id: order_status + isNullable: true + labels: [] + sourceColumn: order_status + sourceColumnDataType: STRING + tags: + - Order lines + title: Order status + dataSourceTableId: + dataSourceId: demo-test-ds + id: order_lines + path: + - demo + - order_lines + type: dataSource + description: Order lines + facts: + - description: Price + id: price + isNullable: true + sourceColumn: price + sourceColumnDataType: NUMERIC + tags: + - Order lines + title: Price + - description: Quantity + id: quantity + isNullable: true + sourceColumn: quantity + sourceColumnDataType: NUMERIC + tags: + - Order lines + title: Quantity + grain: + - id: order_line_id + type: attribute + id: order_lines + references: + - identifier: + id: campaigns + type: dataset + multivalue: false + sources: + - column: campaign_id + dataType: INT + isNullable: true + target: + id: campaign_id + type: attribute + - identifier: + id: customers + type: dataset + multivalue: false + sources: + - column: customer_id + dataType: INT + isNullable: true + target: + id: customer_id + type: attribute + - identifier: + id: date + type: dataset + multivalue: false + sources: + - column: date + dataType: DATE + isNullable: true + target: + id: date + type: date + - identifier: + id: products + type: dataset + multivalue: false + sources: + - column: product_id + dataType: INT + isNullable: true + target: + id: product_id + type: attribute + tags: + - Order lines + title: Order lines + workspaceDataFilterColumns: + - dataType: STRING + name: wdf__region + - dataType: STRING + name: wdf__state + workspaceDataFilterReferences: + - filterColumn: wdf__region + filterColumnDataType: STRING + filterId: + id: wdf__region + type: workspaceDataFilter + - aggregatedFacts: [] + attributes: + - description: Product id + id: product_id + isNullable: false + labels: [] + sourceColumn: product_id + sourceColumnDataType: INT + tags: + - Products + title: Product id + - description: Product name + id: product_name + isNullable: true + labels: [] + sourceColumn: product_name + sourceColumnDataType: STRING + tags: + - Products + title: Product name + - description: Category + id: products.category + isNullable: true + labels: [] + sourceColumn: category + sourceColumnDataType: STRING + tags: + - Products + title: Category + dataSourceTableId: + dataSourceId: demo-test-ds + id: products + path: + - demo + - products + type: dataSource + description: Products + facts: [] + grain: + - id: product_id + type: attribute + id: products + references: [] + tags: + - Products + title: Products + dateInstances: + - description: '' + granularities: + - DAY + - WEEK + - MONTH + - QUARTER + - YEAR + granularitiesFormatting: + titleBase: '' + titlePattern: '%titleBase - %granularityTitle' + id: date + tags: + - Date + title: Date + name: Demo + permissions: + - assignee: + id: demo2 + type: user + name: ANALYZE + - assignee: + id: demoGroup + type: userGroup + name: VIEW + settings: [] + userDataFilters: [] + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: [] + id: demo_west + model: + analytics: + analyticalDashboardExtensions: + - id: campaign + permissions: + - assigneeRule: + type: allWorkspaceUsers + name: VIEW + analyticalDashboards: [] + attributeHierarchies: [] + dashboardPlugins: [] + exportDefinitions: [] + filterContexts: [] + memoryItems: [] + metrics: [] + parameters: [] + visualizationObjects: [] + ldm: + datasetExtensions: + - id: order_lines + workspaceDataFilterReferences: + - filterColumn: wdf__state + filterColumnDataType: STRING + filterId: + id: wdf__state + type: workspaceDataFilter + datasets: [] + dateInstances: [] + name: Demo West + parent: + id: demo + type: workspace + permissions: [] + settings: [] + userDataFilters: [] + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: [] + id: demo_west_california + model: + analytics: + analyticalDashboardExtensions: [] + analyticalDashboards: [] + attributeHierarchies: [] + dashboardPlugins: [] + exportDefinitions: [] + filterContexts: [] + memoryItems: [] + metrics: [] + parameters: [] + visualizationObjects: [] + ldm: + datasets: [] + dateInstances: [] + name: Demo West California + parent: + id: demo_west + type: workspace + permissions: [] + settings: [] + userDataFilters: [] + headers: + Content-Type: + - application/json + DATE: *id001 + Expires: + - '0' + Pragma: + - no-cache + X-Content-Type-Options: + - nosniff + X-GDC-TRACE-ID: *id001 + status: + code: 200 + message: OK + - request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - br, gzip, deflate + X-GDC-VALIDATE-RELATIONS: + - 'true' + X-Requested-With: + - XMLHttpRequest + method: GET + uri: http://localhost:3000/api/v1/layout/organization + response: + body: + string: + agents: + - aiKnowledge: true + availableToAll: true + enabled: true + id: default-ai-assistant + name: Default GoodData AI Assistant + skillsMode: all + customGeoCollections: [] + dataSources: + - alternativeDataSourceId: ds-put-abc-id + authenticationType: USERNAME_PASSWORD + id: demo-test-ds + name: demo-test-ds + permissions: + - assignee: + id: demo2 + type: user + name: MANAGE + - assignee: + id: demoGroup + type: userGroup + name: USE + schema: demo + type: POSTGRESQL + url: jdbc:postgresql://postgres:5432/tiger?sslmode=prefer + username: postgres + exportTemplates: [] + identityProviders: + - id: defaultIdp + idpType: DEX_IDP + ipAllowlistPolicies: [] + jwks: [] + notificationChannels: [] + organization: + allowedOrigins: [] + colorPalettes: [] + cspDirectives: [] + earlyAccess: enableAlerting + earlyAccessValues: + - enableAlerting + - enableSmtp + - enablePreAggregationDatasets + - enableScheduling + - enableCompositeGrain + - enableUserManagement + - enableRawExports + - enableFlexibleDashboardLayout + hostname: localhost + id: default + identityProvider: + id: defaultIdp + type: identityProvider + name: Default Organization + permissions: + - assignee: + id: adminGroup + type: userGroup + name: MANAGE + settings: [] + themes: [] + userGroups: + - id: adminGroup + permissions: [] + - id: adminQA1Group + parents: + - id: adminGroup + type: userGroup + permissions: [] + - id: demoGroup + name: demo group + permissions: [] + - id: visitorsGroup + name: visitors + parents: + - id: demoGroup + type: userGroup + permissions: [] + users: + - id: admin + permissions: [] + settings: [] + userGroups: + - id: adminGroup + type: userGroup + - authId: NORMALIZED_AUTH_ID + email: demo@example.com + firstname: Demo + id: demo + lastname: User + permissions: [] + settings: [] + userGroups: + - id: adminGroup + type: userGroup + - authId: NORMALIZED_AUTH_ID + id: demo2 + permissions: [] + settings: [] + userGroups: + - id: demoGroup + type: userGroup + workspaceDataFilters: + - columnName: wdf__region + id: wdf__region + title: Customer region + workspace: + id: demo + type: workspace + workspaceDataFilterSettings: + - filterValues: + - West + id: region_west + title: Region West + workspace: + id: demo_west + type: workspace + - columnName: wdf__state + id: wdf__state + title: Customer state + workspace: + id: demo_west + type: workspace + workspaceDataFilterSettings: + - filterValues: + - California + id: region_west_california + title: Region West California + workspace: + id: demo_west_california + type: workspace + workspaces: + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: + - assignee: + id: demo2 + type: user + name: MANAGE + - assignee: + id: demoGroup + type: userGroup + name: ANALYZE + id: demo + model: + analytics: + analyticalDashboardExtensions: [] + analyticalDashboards: + - content: + filterContextRef: + identifier: + id: campaign_name_filter + type: filterContext + layout: + sections: + - header: + description: The first insight shows a breakdown of + spend by category and campaign. The second shows + revenue per $ spend, for each campaign, to demonstrate, + how campaigns are successful. + title: Spend breakdown and Revenue + items: + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: campaign_spend + type: visualizationObject + properties: {} + title: Campaign Spend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: revenue_per_usd_vs_spend_by_campaign + type: visualizationObject + properties: {} + title: Revenue per $ vs Spend by Campaign + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: campaign + permissions: + - assigneeRule: + type: allWorkspaceUsers + name: VIEW + title: Campaign + - content: + filterContextRef: + identifier: + id: campaign_name_filter + type: filterContext + layout: + sections: + - items: + - size: + xl: + gridWidth: 12 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: top_10_products + type: visualizationObject + properties: {} + title: DHO simple + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + plugins: + - plugin: + identifier: + id: dashboard_plugin_1 + type: dashboardPlugin + version: '2' + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: dashboard_plugin + title: Dashboard plugin + - content: + filterContextRef: + identifier: + id: region_filter + type: filterContext + layout: + sections: + - items: + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: top_10_products + type: visualizationObject + properties: {} + title: Top 10 Products + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: revenue_trend + type: visualizationObject + properties: {} + title: Revenue Trend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: customers_trend + type: visualizationObject + properties: {} + title: Customers Trend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_categories_pie_chart + type: visualizationObject + properties: {} + title: Product Categories Pie Chart + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_breakdown + type: visualizationObject + properties: {} + title: Product Breakdown + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_saleability + type: visualizationObject + properties: {} + title: Product Saleability + type: insight + - size: + xl: + gridWidth: 12 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: percent_revenue_per_product_by_customer_and_category + type: visualizationObject + properties: {} + title: '% Revenue per Product by Customer and + Category' + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: product_and_category + title: Product & Category + attributeHierarchies: [] + dashboardPlugins: + - content: + url: https://www.example.com + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: Testing record dashboard_plugin_1 + id: dashboard_plugin_1 + title: dashboard_plugin_1 + - content: + url: https://www.example.com + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: Testing record dashboard_plugin_2 + id: dashboard_plugin_2 + title: dashboard_plugin_2 + exportDefinitions: [] + filterContexts: + - content: + filters: + - dateFilter: + from: '0' + granularity: GDC.time.month + to: '0' + type: relative + - attributeFilter: + attributeElements: + uris: [] + displayForm: + identifier: + id: campaign_name + type: label + filterElementsBy: [] + localIdentifier: 14b0807447ef4bc28f43e4fc5c337d1d + negativeSelection: true + version: '2' + description: '' + id: campaign_name_filter + title: filterContext + - content: + filters: + - attributeFilter: + attributeElements: + uris: [] + displayForm: + identifier: + id: region + type: label + filterElementsBy: [] + localIdentifier: 2d5ef8df82444f6ba27b45f0990ee6af + negativeSelection: true + version: '2' + description: '' + id: region_filter + title: filterContext + memoryItems: [] + metrics: + - content: + format: '#,##0' + maql: SELECT COUNT({attribute/customer_id},{attribute/order_line_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_active_customers + title: '# of Active Customers' + - content: + format: '#,##0' + maql: SELECT COUNT({attribute/order_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_orders + title: '# of Orders' + - content: + format: '#,##0' + maql: 'SELECT {metric/amount_of_active_customers} WHERE (SELECT + {metric/revenue} BY {attribute/customer_id}) > 10000 ' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_top_customers + title: '# of Top Customers' + - content: + format: '#,##0.00' + maql: SELECT {metric/amount_of_orders} WHERE NOT ({label/order_status} + IN ("Returned", "Canceled")) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: amount_of_valid_orders + title: '# of Valid Orders' + - content: + format: $#,##0 + maql: SELECT SUM({fact/spend}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: campaign_spend + title: Campaign Spend + - content: + format: $#,##0 + maql: SELECT SUM({fact/price}*{fact/quantity}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: order_amount + title: Order Amount + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / {metric/total_revenue} + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue + title: '% Revenue' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10}\ + \ BY {attribute/customer_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_customers + title: '% Revenue from Top 10 Customers' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10_percent}\ + \ BY {attribute/customer_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_percent_customers + title: '% Revenue from Top 10% Customers' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10_percent}\ + \ BY {attribute/product_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_percent_products + title: '% Revenue from Top 10% Products' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10}\ + \ BY {attribute/product_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_products + title: '% Revenue from Top 10 Products' + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / (SELECT {metric/revenue} BY + {attribute/products.category}, ALL OTHER) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_in_category + title: '% Revenue in Category' + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / (SELECT {metric/revenue} BY + ALL {attribute/product_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_per_product + title: '% Revenue per Product' + - content: + format: $#,##0 + maql: SELECT {metric/order_amount} WHERE NOT ({label/order_status} + IN ("Returned", "Canceled")) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: revenue + title: Revenue + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Clothing") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-clothing + title: Revenue (Clothing) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ( "Electronics") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-electronic + title: Revenue (Electronic) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Home") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-home + title: Revenue (Home) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Outdoor") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-outdoor + title: Revenue (Outdoor) + - content: + format: $#,##0.0 + maql: SELECT AVG(SELECT {metric/revenue} BY {attribute/customer_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_customer + title: Revenue per Customer + - content: + format: $#,##0.0 + maql: SELECT {metric/revenue} / {metric/campaign_spend} + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_dollar_spent + title: Revenue per Dollar Spent + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE TOP(10) OF ({metric/revenue}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_top_10 + title: Revenue / Top 10 + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE TOP(10%) OF ({metric/revenue}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_top_10_percent + title: Revenue / Top 10% + - content: + format: $#,##0 + maql: SELECT {metric/revenue} BY ALL OTHER + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: total_revenue + title: Total Revenue + - content: + format: $#,##0 + maql: SELECT {metric/total_revenue} WITHOUT PARENT FILTER + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: total_revenue-no_filters + title: Total Revenue (No Filters) + parameters: [] + visualizationObjects: + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: campaign_spend + type: metric + localIdentifier: d319bcb2d8c04442a684e3b3cd063381 + title: Campaign Spend + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: campaign_channels.category + type: label + localIdentifier: 291c085e7df8420db84117ca49f59c49 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: campaign_name + type: label + localIdentifier: d9dd143d647d4d148405a60ec2cf59bc + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: type + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_channels.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_name + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:treemap + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: campaign_spend + title: Campaign Spend + - content: + buckets: + - items: + - measure: + alias: Active Customers + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: amount_of_active_customers + type: metric + localIdentifier: 2ba0b87b59ca41a4b1530e81a5c1d081 + title: '# of Active Customers' + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_per_customer + type: metric + localIdentifier: ec0606894b9f4897b7beaf1550608928 + title: Revenue per Customer + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 0de7d7f08af7480aa636857a26be72b6 + localIdentifier: view + filters: + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -12 + granularity: GDC.time.month + to: -1 + properties: + controls: + colorMapping: + - color: + type: guid + value: '20' + id: 2ba0b87b59ca41a4b1530e81a5c1d081 + - color: + type: guid + value: '4' + id: ec0606894b9f4897b7beaf1550608928 + dualAxis: true + legend: + position: bottom + primaryChartType: column + secondaryChartType: line + secondary_yaxis: + measures: + - ec0606894b9f4897b7beaf1550608928 + xaxis: + name: + visible: false + rotation: auto + version: '2' + visualizationUrl: local:combo2 + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: customers_trend + title: Customers Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: percent_revenue_per_product + type: metric + localIdentifier: 08d8346c1ce7438994b251991c0fbf65 + title: '% Revenue per Product' + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: b2350c06688b4da9b3833ebcce65527f + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: customer_name + type: label + localIdentifier: 7a4045fd00ac44579f52406df679435f + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 6a003ffd14994237ba64c4a02c488429 + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 75ea396d0c8b48098e31dccf8b5801d3 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: {} + sorts: + - attributeSortItem: + attributeIdentifier: 7a4045fd00ac44579f52406df679435f + direction: asc + version: '2' + visualizationUrl: local:table + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_per_product_by_customer_and_category + title: '% Revenue per Product by Customer and Category' + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: amount_of_active_customers + type: metric + localIdentifier: 1a14cdc1293c46e89a2e25d3e741d235 + title: '# of Active Customers' + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: c1feca1864244ec2ace7a9b9d7fda231 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: region + type: label + localIdentifier: 530cddbd7ca04d039e73462d81ed44d5 + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: region + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -11 + granularity: GDC.time.month + to: 0 + properties: + controls: + legend: + position: bottom + stackMeasuresToPercent: true + version: '2' + visualizationUrl: local:area + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percentage_of_customers_by_region + title: Percentage of Customers by Region + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 590d332ef686468b8878ae41b23341c6 + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: b166c71091864312a14c7ae8ff886ffe + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: e920a50e0bbb49788df0aac53634c1cd + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:treemap + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_breakdown + title: Product Breakdown + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + computeRatio: true + filters: [] + item: + identifier: + id: revenue + type: metric + format: '#,##0.00%' + localIdentifier: 162b857af49d45769bc12604a5c192b9 + title: '% Revenue' + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: fe513cef1c6244a5ac21c5f49c56b108 + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + dataLabels: + visible: auto + legend: + position: bottom + version: '2' + visualizationUrl: local:donut + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_categories_pie_chart + title: Product Categories Pie Chart + - content: + buckets: + - items: + - measure: + alias: Previous Period + definition: + popMeasureDefinition: + measureIdentifier: c82e025fa2db4afea9a600a424591dbe + popAttribute: + identifier: + id: date.year + type: attribute + localIdentifier: c82e025fa2db4afea9a600a424591dbe_pop + - measure: + alias: This Period + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: c82e025fa2db4afea9a600a424591dbe + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: c804ef5ba7944a5a9f360c86a9e95e9a + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -11 + granularity: GDC.time.month + to: 0 + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + stackMeasures: false + xaxis: + name: + visible: false + yaxis: + name: + visible: false + version: '2' + visualizationUrl: local:column + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_revenue_comparison-over_previous_period + title: Product Revenue Comparison (over previous period) + - content: + buckets: + - items: + - measure: + alias: Number of Orders + definition: + measureDefinition: + filters: [] + item: + identifier: + id: amount_of_orders + type: metric + localIdentifier: aeb5d51a162d4b59aba3bd6ddebcc780 + title: '# of Orders' + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 94b3edd3a73c4a48a4d13bbe9442cc98 + title: Revenue + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: d2a991bdd123448eb2be73d79f1180c4 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: + controls: + dataLabels: + visible: auto + grid: + enabled: true + version: '2' + visualizationUrl: local:scatter + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_saleability + title: Product Saleability + - content: + buckets: + - items: + - measure: + alias: Items Sold + definition: + measureDefinition: + aggregation: sum + filters: [] + item: + identifier: + id: quantity + type: fact + format: '#,##0.00' + localIdentifier: 29486504dd0e4a36a18b0b2f792d3a46 + title: Sum of Quantity + - measure: + definition: + measureDefinition: + aggregation: avg + filters: [] + item: + identifier: + id: price + type: fact + format: '#,##0.00' + localIdentifier: aa6391acccf1452f8011201aef9af492 + title: Avg Price + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: percent_revenue_in_category + type: metric + localIdentifier: 2cd39539d8da46c9883e63caa3ba7cc0 + title: '% Revenue in Category' + - measure: + alias: Total Revenue + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 9a0f08331c094c7facf2a0b4f418de0a + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 06bc6b3b9949466494e4f594c11f1bff + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 192668bfb6a74e9ab7b5d1ce7cb68ea3 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: {} + sorts: + - attributeSortItem: + attributeIdentifier: 06bc6b3b9949466494e4f594c11f1bff + direction: asc + version: '2' + visualizationUrl: local:table + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_and_quantity_by_product_and_category + title: Revenue and Quantity by Product and Category + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 7df6c34387744d69b23ec92e1a5cf543 + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 4bb4fc1986c546de9ad976e6ec23fed4 + localIdentifier: trend + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 34bddcb1cd024902a82396216b0fa9d8 + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + granularity: GDC.time.year + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:line + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_by_category_trend + title: Revenue by Category Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 4ae3401bdbba4938afe983df4ba04e1c + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 1c8ba72dbfc84ddd913bf81dc355c427 + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: {} + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_by_product + title: Revenue by Product + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: campaign_spend + type: metric + localIdentifier: 13a50d811e474ac6808d8da7f4673b35 + title: Campaign Spend + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_per_dollar_spent + type: metric + localIdentifier: a0f15e82e6334280a44dbedc7d086e7c + title: Revenue per Dollar Spent + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: campaign_name + type: label + localIdentifier: 1d9fa968bafb423eb29c938dfb1207ff + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_name + type: label + notIn: + values: [] + properties: + controls: + xaxis: + min: '0' + yaxis: + min: '0' + version: '2' + visualizationUrl: local:scatter + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_usd_vs_spend_by_campaign + title: Revenue per $ vs Spend by Campaign + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 60c854969a9c4c278ab596d99c222e92 + title: Revenue + localIdentifier: measures + - items: + - measure: + alias: Number of Orders + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: amount_of_orders + type: metric + localIdentifier: c2fa7ef48cc54af99f8c280eb451e051 + title: '# of Orders' + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 413ac374b65648fa96826ca01d47bdda + localIdentifier: view + filters: + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -3 + granularity: GDC.time.quarter + to: 0 + properties: + controls: + dualAxis: true + legend: + position: bottom + primaryChartType: column + secondaryChartType: line + secondary_yaxis: + measures: + - c2fa7ef48cc54af99f8c280eb451e051 + xaxis: + name: + visible: false + rotation: auto + version: '2' + visualizationUrl: local:combo2 + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_trend + title: Revenue Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_top_10 + type: metric + localIdentifier: 3f127ccfe57a40399e23f9ae2a4ad810 + title: Revenue / Top 10 + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: customer_name + type: label + localIdentifier: f4e39e24f11e4827a191c30d65c89d2c + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: state + type: label + localIdentifier: bbccd430176d428caed54c99afc9589e + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: state + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: top_10_customers + title: Top 10 Customers + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_top_10 + type: metric + localIdentifier: 77dc71bbac92412bac5f94284a5919df + title: Revenue / Top 10 + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 781952e728204dcf923142910cc22ae2 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: fe513cef1c6244a5ac21c5f49c56b108 + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: top_10_products + title: Top 10 Products + ldm: + datasets: + - aggregatedFacts: [] + attributes: + - description: Campaign channel id + id: campaign_channel_id + isNullable: false + labels: [] + sourceColumn: campaign_channel_id + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Campaign channel id + - description: Category + id: campaign_channels.category + isNullable: true + labels: [] + sourceColumn: category + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Category + - description: Type + id: type + isNullable: true + labels: [] + sourceColumn: type + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Type + dataSourceTableId: + dataSourceId: demo-test-ds + id: campaign_channels + path: + - demo + - campaign_channels + type: dataSource + description: Campaign channels + facts: + - description: Budget + id: budget + isNullable: true + sourceColumn: budget + sourceColumnDataType: NUMERIC + tags: + - Campaign channels + title: Budget + - description: Spend + id: spend + isNullable: true + sourceColumn: spend + sourceColumnDataType: NUMERIC + tags: + - Campaign channels + title: Spend + grain: + - id: campaign_channel_id + type: attribute + id: campaign_channels + references: + - identifier: + id: campaigns + type: dataset + multivalue: false + sources: + - column: campaign_id + dataType: INT + isNullable: true + target: + id: campaign_id + type: attribute + tags: + - Campaign channels + title: Campaign channels + - aggregatedFacts: + - description: Budget Agg + id: budget_agg + sourceColumn: budget + sourceColumnDataType: INT + sourceFactReference: + operation: SUM + reference: + id: budget + type: fact + tags: + - Campaign channels per category + attributes: [] + description: Campaign channels per categories + facts: [] + grain: [] + id: campaign_channels_per_category + precedence: 1 + references: + - identifier: + id: campaign_channels + type: dataset + multivalue: false + sources: + - column: campaign_channel_id + dataType: STRING + isNullable: true + target: + id: campaign_channel_id + type: attribute + sql: + dataSourceId: demo-test-ds + statement: SELECT category, SUM(budget) FROM campaign_channels + GROUP BY category + tags: + - Campaign channels per category + title: Campaign channels per category + - aggregatedFacts: [] + attributes: + - description: Campaign id + id: campaign_id + isNullable: false + labels: [] + sourceColumn: campaign_id + sourceColumnDataType: INT + tags: + - Campaigns + title: Campaign id + - description: Campaign name + id: campaign_name + isNullable: true + labels: [] + sourceColumn: campaign_name + sourceColumnDataType: STRING + tags: + - Campaigns + title: Campaign name + dataSourceTableId: + dataSourceId: demo-test-ds + id: campaigns + path: + - demo + - campaigns + type: dataSource + description: Campaigns + facts: [] + grain: + - id: campaign_id + type: attribute + id: campaigns + references: [] + tags: + - Campaigns + title: Campaigns + - aggregatedFacts: [] + attributes: + - description: Customer id + id: customer_id + isNullable: false + labels: [] + sourceColumn: customer_id + sourceColumnDataType: INT + tags: + - Customers + title: Customer id + - description: Customer name + id: customer_name + isNullable: true + labels: [] + sourceColumn: customer_name + sourceColumnDataType: STRING + tags: + - Customers + title: Customer name + - description: Region + id: region + isNullable: true + labels: [] + sourceColumn: region + sourceColumnDataType: STRING + tags: + - Customers + title: Region + - description: State + id: state + isNullable: true + labels: + - description: Location + id: geo__state__location + isNullable: true + sourceColumn: geo__state__location + sourceColumnDataType: STRING + tags: + - Customers + title: Location + valueType: GEO + sourceColumn: state + sourceColumnDataType: STRING + tags: + - Customers + title: State + dataSourceTableId: + dataSourceId: demo-test-ds + id: customers + path: + - demo + - customers + type: dataSource + description: Customers + facts: [] + grain: + - id: customer_id + type: attribute + id: customers + references: [] + tags: + - Customers + title: Customers + - aggregatedFacts: [] + attributes: + - description: Order id + id: order_id + isNullable: true + labels: [] + sourceColumn: order_id + sourceColumnDataType: STRING + tags: + - Order lines + title: Order id + - description: Order line id + id: order_line_id + isNullable: false + labels: [] + sourceColumn: order_line_id + sourceColumnDataType: STRING + tags: + - Order lines + title: Order line id + - description: Order status + id: order_status + isNullable: true + labels: [] + sourceColumn: order_status + sourceColumnDataType: STRING + tags: + - Order lines + title: Order status + dataSourceTableId: + dataSourceId: demo-test-ds + id: order_lines + path: + - demo + - order_lines + type: dataSource + description: Order lines + facts: + - description: Price + id: price + isNullable: true + sourceColumn: price + sourceColumnDataType: NUMERIC + tags: + - Order lines + title: Price + - description: Quantity + id: quantity + isNullable: true + sourceColumn: quantity + sourceColumnDataType: NUMERIC + tags: + - Order lines + title: Quantity + grain: + - id: order_line_id + type: attribute + id: order_lines + references: + - identifier: + id: campaigns + type: dataset + multivalue: false + sources: + - column: campaign_id + dataType: INT + isNullable: true + target: + id: campaign_id + type: attribute + - identifier: + id: customers + type: dataset + multivalue: false + sources: + - column: customer_id + dataType: INT + isNullable: true + target: + id: customer_id + type: attribute + - identifier: + id: date + type: dataset + multivalue: false + sources: + - column: date + dataType: DATE + isNullable: true + target: + id: date + type: date + - identifier: + id: products + type: dataset + multivalue: false + sources: + - column: product_id + dataType: INT + isNullable: true + target: + id: product_id + type: attribute + tags: + - Order lines + title: Order lines + workspaceDataFilterColumns: + - dataType: STRING + name: wdf__region + - dataType: STRING + name: wdf__state + workspaceDataFilterReferences: + - filterColumn: wdf__region + filterColumnDataType: STRING + filterId: + id: wdf__region + type: workspaceDataFilter + - aggregatedFacts: [] + attributes: + - description: Product id + id: product_id + isNullable: false + labels: [] + sourceColumn: product_id + sourceColumnDataType: INT + tags: + - Products + title: Product id + - description: Product name + id: product_name + isNullable: true + labels: [] + sourceColumn: product_name + sourceColumnDataType: STRING + tags: + - Products + title: Product name + - description: Category + id: products.category + isNullable: true + labels: [] + sourceColumn: category + sourceColumnDataType: STRING + tags: + - Products + title: Category + dataSourceTableId: + dataSourceId: demo-test-ds + id: products + path: + - demo + - products + type: dataSource + description: Products + facts: [] + grain: + - id: product_id + type: attribute + id: products + references: [] + tags: + - Products + title: Products + dateInstances: + - description: '' + granularities: + - DAY + - WEEK + - MONTH + - QUARTER + - YEAR + granularitiesFormatting: + titleBase: '' + titlePattern: '%titleBase - %granularityTitle' + id: date + tags: + - Date + title: Date + name: Demo + permissions: + - assignee: + id: demo2 + type: user + name: ANALYZE + - assignee: + id: demoGroup + type: userGroup + name: VIEW + settings: [] + userDataFilters: [] + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: [] + id: demo_west + model: + analytics: + analyticalDashboardExtensions: + - id: campaign + permissions: + - assigneeRule: + type: allWorkspaceUsers + name: VIEW + analyticalDashboards: [] + attributeHierarchies: [] + dashboardPlugins: [] + exportDefinitions: [] + filterContexts: [] + memoryItems: [] + metrics: [] + parameters: [] + visualizationObjects: [] + ldm: + datasetExtensions: + - id: order_lines + workspaceDataFilterReferences: + - filterColumn: wdf__state + filterColumnDataType: STRING + filterId: + id: wdf__state + type: workspaceDataFilter + datasets: [] + dateInstances: [] + name: Demo West + parent: + id: demo + type: workspace + permissions: [] + settings: [] + userDataFilters: [] + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: [] + id: demo_west_california + model: + analytics: + analyticalDashboardExtensions: [] + analyticalDashboards: [] + attributeHierarchies: [] + dashboardPlugins: [] + exportDefinitions: [] + filterContexts: [] + memoryItems: [] + metrics: [] + parameters: [] + visualizationObjects: [] + ldm: + datasets: [] + dateInstances: [] + name: Demo West California + parent: + id: demo_west + type: workspace + permissions: [] + settings: [] + userDataFilters: [] + headers: + Content-Type: + - application/json + DATE: *id001 + Expires: + - '0' + Pragma: + - no-cache + X-Content-Type-Options: + - nosniff + X-GDC-TRACE-ID: *id001 + status: + code: 200 + message: OK + - request: + body: + agents: + - aiKnowledge: true + availableToAll: true + enabled: true + id: default-ai-assistant + name: Default GoodData AI Assistant + skillsMode: all + customGeoCollections: [] + dataSources: + - alternativeDataSourceId: ds-put-abc-id + authenticationType: USERNAME_PASSWORD + id: demo-test-ds + name: demo-test-ds + permissions: + - assignee: + id: demo2 + type: user + name: MANAGE + - assignee: + id: demoGroup + type: userGroup + name: USE + schema: demo + type: POSTGRESQL + url: jdbc:postgresql://postgres:5432/tiger?sslmode=prefer + username: postgres + exportTemplates: [] + identityProviders: + - id: defaultIdp + idpType: DEX_IDP + ipAllowlistPolicies: + - allowedSources: + - 203.0.113.10/32 + - 198.51.100.0/24 + id: admin-vpn-only + userGroups: + - id: adminGroup + type: userGroup + users: + - id: demo + type: user + jwks: [] + notificationChannels: [] + organization: + allowedOrigins: [] + colorPalettes: [] + cspDirectives: [] + earlyAccess: enableAlerting + earlyAccessValues: + - enableAlerting + - enableSmtp + - enablePreAggregationDatasets + - enableScheduling + - enableCompositeGrain + - enableUserManagement + - enableRawExports + - enableFlexibleDashboardLayout + hostname: localhost + id: default + identityProvider: + id: defaultIdp + type: identityProvider + name: Default Organization + permissions: + - assignee: + id: adminGroup + type: userGroup + name: MANAGE + settings: [] + themes: [] + userGroups: + - id: adminGroup + permissions: [] + - id: adminQA1Group + parents: + - id: adminGroup + type: userGroup + permissions: [] + - id: demoGroup + name: demo group + permissions: [] + - id: visitorsGroup + name: visitors + parents: + - id: demoGroup + type: userGroup + permissions: [] + users: + - id: admin + permissions: [] + settings: [] + userGroups: + - id: adminGroup + type: userGroup + - authId: NORMALIZED_AUTH_ID + email: demo@example.com + firstname: Demo + id: demo + lastname: User + permissions: [] + settings: [] + userGroups: + - id: adminGroup + type: userGroup + - authId: NORMALIZED_AUTH_ID + id: demo2 + permissions: [] + settings: [] + userGroups: + - id: demoGroup + type: userGroup + workspaceDataFilters: + - columnName: wdf__region + id: wdf__region + title: Customer region + workspace: + id: demo + type: workspace + workspaceDataFilterSettings: + - filterValues: + - West + id: region_west + title: Region West + workspace: + id: demo_west + type: workspace + - columnName: wdf__state + id: wdf__state + title: Customer state + workspace: + id: demo_west + type: workspace + workspaceDataFilterSettings: + - filterValues: + - California + id: region_west_california + title: Region West California + workspace: + id: demo_west_california + type: workspace + workspaces: + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: + - assignee: + id: demo2 + type: user + name: MANAGE + - assignee: + id: demoGroup + type: userGroup + name: ANALYZE + id: demo + model: + analytics: + analyticalDashboardExtensions: [] + analyticalDashboards: + - content: + filterContextRef: + identifier: + id: campaign_name_filter + type: filterContext + layout: + sections: + - header: + description: The first insight shows a breakdown of + spend by category and campaign. The second shows revenue + per $ spend, for each campaign, to demonstrate, how + campaigns are successful. + title: Spend breakdown and Revenue + items: + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: campaign_spend + type: visualizationObject + properties: {} + title: Campaign Spend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: revenue_per_usd_vs_spend_by_campaign + type: visualizationObject + properties: {} + title: Revenue per $ vs Spend by Campaign + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: campaign + permissions: + - assigneeRule: + type: allWorkspaceUsers + name: VIEW + title: Campaign + - content: + filterContextRef: + identifier: + id: campaign_name_filter + type: filterContext + layout: + sections: + - items: + - size: + xl: + gridWidth: 12 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: top_10_products + type: visualizationObject + properties: {} + title: DHO simple + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + plugins: + - plugin: + identifier: + id: dashboard_plugin_1 + type: dashboardPlugin + version: '2' + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: dashboard_plugin + title: Dashboard plugin + - content: + filterContextRef: + identifier: + id: region_filter + type: filterContext + layout: + sections: + - items: + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: top_10_products + type: visualizationObject + properties: {} + title: Top 10 Products + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: revenue_trend + type: visualizationObject + properties: {} + title: Revenue Trend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: customers_trend + type: visualizationObject + properties: {} + title: Customers Trend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_categories_pie_chart + type: visualizationObject + properties: {} + title: Product Categories Pie Chart + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_breakdown + type: visualizationObject + properties: {} + title: Product Breakdown + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_saleability + type: visualizationObject + properties: {} + title: Product Saleability + type: insight + - size: + xl: + gridWidth: 12 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: percent_revenue_per_product_by_customer_and_category + type: visualizationObject + properties: {} + title: '% Revenue per Product by Customer and Category' + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: product_and_category + title: Product & Category + attributeHierarchies: [] + dashboardPlugins: + - content: + url: https://www.example.com + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: Testing record dashboard_plugin_1 + id: dashboard_plugin_1 + title: dashboard_plugin_1 + - content: + url: https://www.example.com + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: Testing record dashboard_plugin_2 + id: dashboard_plugin_2 + title: dashboard_plugin_2 + exportDefinitions: [] + filterContexts: + - content: + filters: + - dateFilter: + from: '0' + granularity: GDC.time.month + to: '0' + type: relative + - attributeFilter: + attributeElements: + uris: [] + displayForm: + identifier: + id: campaign_name + type: label + filterElementsBy: [] + localIdentifier: 14b0807447ef4bc28f43e4fc5c337d1d + negativeSelection: true + version: '2' + description: '' + id: campaign_name_filter + title: filterContext + - content: + filters: + - attributeFilter: + attributeElements: + uris: [] + displayForm: + identifier: + id: region + type: label + filterElementsBy: [] + localIdentifier: 2d5ef8df82444f6ba27b45f0990ee6af + negativeSelection: true + version: '2' + description: '' + id: region_filter + title: filterContext + memoryItems: [] + metrics: + - content: + format: '#,##0' + maql: SELECT COUNT({attribute/customer_id},{attribute/order_line_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_active_customers + title: '# of Active Customers' + - content: + format: '#,##0' + maql: SELECT COUNT({attribute/order_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_orders + title: '# of Orders' + - content: + format: '#,##0' + maql: 'SELECT {metric/amount_of_active_customers} WHERE (SELECT + {metric/revenue} BY {attribute/customer_id}) > 10000 ' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_top_customers + title: '# of Top Customers' + - content: + format: '#,##0.00' + maql: SELECT {metric/amount_of_orders} WHERE NOT ({label/order_status} + IN ("Returned", "Canceled")) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: amount_of_valid_orders + title: '# of Valid Orders' + - content: + format: $#,##0 + maql: SELECT SUM({fact/spend}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: campaign_spend + title: Campaign Spend + - content: + format: $#,##0 + maql: SELECT SUM({fact/price}*{fact/quantity}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: order_amount + title: Order Amount + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / {metric/total_revenue} + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue + title: '% Revenue' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10}\ + \ BY {attribute/customer_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_customers + title: '% Revenue from Top 10 Customers' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10_percent}\ + \ BY {attribute/customer_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_percent_customers + title: '% Revenue from Top 10% Customers' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10_percent}\ + \ BY {attribute/product_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_percent_products + title: '% Revenue from Top 10% Products' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10}\ + \ BY {attribute/product_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_products + title: '% Revenue from Top 10 Products' + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / (SELECT {metric/revenue} BY + {attribute/products.category}, ALL OTHER) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_in_category + title: '% Revenue in Category' + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / (SELECT {metric/revenue} BY + ALL {attribute/product_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_per_product + title: '% Revenue per Product' + - content: + format: $#,##0 + maql: SELECT {metric/order_amount} WHERE NOT ({label/order_status} + IN ("Returned", "Canceled")) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: revenue + title: Revenue + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Clothing") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-clothing + title: Revenue (Clothing) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ( "Electronics") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-electronic + title: Revenue (Electronic) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Home") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-home + title: Revenue (Home) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Outdoor") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-outdoor + title: Revenue (Outdoor) + - content: + format: $#,##0.0 + maql: SELECT AVG(SELECT {metric/revenue} BY {attribute/customer_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_customer + title: Revenue per Customer + - content: + format: $#,##0.0 + maql: SELECT {metric/revenue} / {metric/campaign_spend} + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_dollar_spent + title: Revenue per Dollar Spent + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE TOP(10) OF ({metric/revenue}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_top_10 + title: Revenue / Top 10 + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE TOP(10%) OF ({metric/revenue}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_top_10_percent + title: Revenue / Top 10% + - content: + format: $#,##0 + maql: SELECT {metric/revenue} BY ALL OTHER + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: total_revenue + title: Total Revenue + - content: + format: $#,##0 + maql: SELECT {metric/total_revenue} WITHOUT PARENT FILTER + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: total_revenue-no_filters + title: Total Revenue (No Filters) + parameters: [] + visualizationObjects: + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: campaign_spend + type: metric + localIdentifier: d319bcb2d8c04442a684e3b3cd063381 + title: Campaign Spend + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: campaign_channels.category + type: label + localIdentifier: 291c085e7df8420db84117ca49f59c49 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: campaign_name + type: label + localIdentifier: d9dd143d647d4d148405a60ec2cf59bc + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: type + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_channels.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_name + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:treemap + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: campaign_spend + title: Campaign Spend + - content: + buckets: + - items: + - measure: + alias: Active Customers + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: amount_of_active_customers + type: metric + localIdentifier: 2ba0b87b59ca41a4b1530e81a5c1d081 + title: '# of Active Customers' + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_per_customer + type: metric + localIdentifier: ec0606894b9f4897b7beaf1550608928 + title: Revenue per Customer + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 0de7d7f08af7480aa636857a26be72b6 + localIdentifier: view + filters: + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -12 + granularity: GDC.time.month + to: -1 + properties: + controls: + colorMapping: + - color: + type: guid + value: '20' + id: 2ba0b87b59ca41a4b1530e81a5c1d081 + - color: + type: guid + value: '4' + id: ec0606894b9f4897b7beaf1550608928 + dualAxis: true + legend: + position: bottom + primaryChartType: column + secondaryChartType: line + secondary_yaxis: + measures: + - ec0606894b9f4897b7beaf1550608928 + xaxis: + name: + visible: false + rotation: auto + version: '2' + visualizationUrl: local:combo2 + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: customers_trend + title: Customers Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: percent_revenue_per_product + type: metric + localIdentifier: 08d8346c1ce7438994b251991c0fbf65 + title: '% Revenue per Product' + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: b2350c06688b4da9b3833ebcce65527f + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: customer_name + type: label + localIdentifier: 7a4045fd00ac44579f52406df679435f + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 6a003ffd14994237ba64c4a02c488429 + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 75ea396d0c8b48098e31dccf8b5801d3 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: {} + sorts: + - attributeSortItem: + attributeIdentifier: 7a4045fd00ac44579f52406df679435f + direction: asc + version: '2' + visualizationUrl: local:table + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_per_product_by_customer_and_category + title: '% Revenue per Product by Customer and Category' + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: amount_of_active_customers + type: metric + localIdentifier: 1a14cdc1293c46e89a2e25d3e741d235 + title: '# of Active Customers' + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: c1feca1864244ec2ace7a9b9d7fda231 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: region + type: label + localIdentifier: 530cddbd7ca04d039e73462d81ed44d5 + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: region + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -11 + granularity: GDC.time.month + to: 0 + properties: + controls: + legend: + position: bottom + stackMeasuresToPercent: true + version: '2' + visualizationUrl: local:area + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percentage_of_customers_by_region + title: Percentage of Customers by Region + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 590d332ef686468b8878ae41b23341c6 + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: b166c71091864312a14c7ae8ff886ffe + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: e920a50e0bbb49788df0aac53634c1cd + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:treemap + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_breakdown + title: Product Breakdown + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + computeRatio: true + filters: [] + item: + identifier: + id: revenue + type: metric + format: '#,##0.00%' + localIdentifier: 162b857af49d45769bc12604a5c192b9 + title: '% Revenue' + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: fe513cef1c6244a5ac21c5f49c56b108 + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + dataLabels: + visible: auto + legend: + position: bottom + version: '2' + visualizationUrl: local:donut + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_categories_pie_chart + title: Product Categories Pie Chart + - content: + buckets: + - items: + - measure: + alias: Previous Period + definition: + popMeasureDefinition: + measureIdentifier: c82e025fa2db4afea9a600a424591dbe + popAttribute: + identifier: + id: date.year + type: attribute + localIdentifier: c82e025fa2db4afea9a600a424591dbe_pop + - measure: + alias: This Period + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: c82e025fa2db4afea9a600a424591dbe + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: c804ef5ba7944a5a9f360c86a9e95e9a + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -11 + granularity: GDC.time.month + to: 0 + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + stackMeasures: false + xaxis: + name: + visible: false + yaxis: + name: + visible: false + version: '2' + visualizationUrl: local:column + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_revenue_comparison-over_previous_period + title: Product Revenue Comparison (over previous period) + - content: + buckets: + - items: + - measure: + alias: Number of Orders + definition: + measureDefinition: + filters: [] + item: + identifier: + id: amount_of_orders + type: metric + localIdentifier: aeb5d51a162d4b59aba3bd6ddebcc780 + title: '# of Orders' + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 94b3edd3a73c4a48a4d13bbe9442cc98 + title: Revenue + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: d2a991bdd123448eb2be73d79f1180c4 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: + controls: + dataLabels: + visible: auto + grid: + enabled: true + version: '2' + visualizationUrl: local:scatter + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_saleability + title: Product Saleability + - content: + buckets: + - items: + - measure: + alias: Items Sold + definition: + measureDefinition: + aggregation: sum + filters: [] + item: + identifier: + id: quantity + type: fact + format: '#,##0.00' + localIdentifier: 29486504dd0e4a36a18b0b2f792d3a46 + title: Sum of Quantity + - measure: + definition: + measureDefinition: + aggregation: avg + filters: [] + item: + identifier: + id: price + type: fact + format: '#,##0.00' + localIdentifier: aa6391acccf1452f8011201aef9af492 + title: Avg Price + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: percent_revenue_in_category + type: metric + localIdentifier: 2cd39539d8da46c9883e63caa3ba7cc0 + title: '% Revenue in Category' + - measure: + alias: Total Revenue + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 9a0f08331c094c7facf2a0b4f418de0a + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 06bc6b3b9949466494e4f594c11f1bff + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 192668bfb6a74e9ab7b5d1ce7cb68ea3 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: {} + sorts: + - attributeSortItem: + attributeIdentifier: 06bc6b3b9949466494e4f594c11f1bff + direction: asc + version: '2' + visualizationUrl: local:table + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_and_quantity_by_product_and_category + title: Revenue and Quantity by Product and Category + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 7df6c34387744d69b23ec92e1a5cf543 + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 4bb4fc1986c546de9ad976e6ec23fed4 + localIdentifier: trend + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 34bddcb1cd024902a82396216b0fa9d8 + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + granularity: GDC.time.year + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:line + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_by_category_trend + title: Revenue by Category Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 4ae3401bdbba4938afe983df4ba04e1c + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 1c8ba72dbfc84ddd913bf81dc355c427 + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: {} + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_by_product + title: Revenue by Product + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: campaign_spend + type: metric + localIdentifier: 13a50d811e474ac6808d8da7f4673b35 + title: Campaign Spend + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_per_dollar_spent + type: metric + localIdentifier: a0f15e82e6334280a44dbedc7d086e7c + title: Revenue per Dollar Spent + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: campaign_name + type: label + localIdentifier: 1d9fa968bafb423eb29c938dfb1207ff + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_name + type: label + notIn: + values: [] + properties: + controls: + xaxis: + min: '0' + yaxis: + min: '0' + version: '2' + visualizationUrl: local:scatter + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_usd_vs_spend_by_campaign + title: Revenue per $ vs Spend by Campaign + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 60c854969a9c4c278ab596d99c222e92 + title: Revenue + localIdentifier: measures + - items: + - measure: + alias: Number of Orders + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: amount_of_orders + type: metric + localIdentifier: c2fa7ef48cc54af99f8c280eb451e051 + title: '# of Orders' + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 413ac374b65648fa96826ca01d47bdda + localIdentifier: view + filters: + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -3 + granularity: GDC.time.quarter + to: 0 + properties: + controls: + dualAxis: true + legend: + position: bottom + primaryChartType: column + secondaryChartType: line + secondary_yaxis: + measures: + - c2fa7ef48cc54af99f8c280eb451e051 + xaxis: + name: + visible: false + rotation: auto + version: '2' + visualizationUrl: local:combo2 + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_trend + title: Revenue Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_top_10 + type: metric + localIdentifier: 3f127ccfe57a40399e23f9ae2a4ad810 + title: Revenue / Top 10 + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: customer_name + type: label + localIdentifier: f4e39e24f11e4827a191c30d65c89d2c + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: state + type: label + localIdentifier: bbccd430176d428caed54c99afc9589e + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: state + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: top_10_customers + title: Top 10 Customers + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_top_10 + type: metric + localIdentifier: 77dc71bbac92412bac5f94284a5919df + title: Revenue / Top 10 + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 781952e728204dcf923142910cc22ae2 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: fe513cef1c6244a5ac21c5f49c56b108 + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: top_10_products + title: Top 10 Products + ldm: + datasets: + - aggregatedFacts: [] + attributes: + - description: Campaign channel id + id: campaign_channel_id + isNullable: false + labels: [] + sourceColumn: campaign_channel_id + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Campaign channel id + - description: Category + id: campaign_channels.category + isNullable: true + labels: [] + sourceColumn: category + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Category + - description: Type + id: type + isNullable: true + labels: [] + sourceColumn: type + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Type + dataSourceTableId: + dataSourceId: demo-test-ds + id: campaign_channels + path: + - demo + - campaign_channels + type: dataSource + description: Campaign channels + facts: + - description: Budget + id: budget + isNullable: true + sourceColumn: budget + sourceColumnDataType: NUMERIC + tags: + - Campaign channels + title: Budget + - description: Spend + id: spend + isNullable: true + sourceColumn: spend + sourceColumnDataType: NUMERIC + tags: + - Campaign channels + title: Spend + grain: + - id: campaign_channel_id + type: attribute + id: campaign_channels + references: + - identifier: + id: campaigns + type: dataset + multivalue: false + sources: + - column: campaign_id + dataType: INT + isNullable: true + target: + id: campaign_id + type: attribute + tags: + - Campaign channels + title: Campaign channels + - aggregatedFacts: + - description: Budget Agg + id: budget_agg + sourceColumn: budget + sourceColumnDataType: INT + sourceFactReference: + operation: SUM + reference: + id: budget + type: fact + tags: + - Campaign channels per category + attributes: [] + description: Campaign channels per categories + facts: [] + grain: [] + id: campaign_channels_per_category + precedence: 1 + references: + - identifier: + id: campaign_channels + type: dataset + multivalue: false + sources: + - column: campaign_channel_id + dataType: STRING + isNullable: true + target: + id: campaign_channel_id + type: attribute + sql: + dataSourceId: demo-test-ds + statement: SELECT category, SUM(budget) FROM campaign_channels + GROUP BY category + tags: + - Campaign channels per category + title: Campaign channels per category + - aggregatedFacts: [] + attributes: + - description: Campaign id + id: campaign_id + isNullable: false + labels: [] + sourceColumn: campaign_id + sourceColumnDataType: INT + tags: + - Campaigns + title: Campaign id + - description: Campaign name + id: campaign_name + isNullable: true + labels: [] + sourceColumn: campaign_name + sourceColumnDataType: STRING + tags: + - Campaigns + title: Campaign name + dataSourceTableId: + dataSourceId: demo-test-ds + id: campaigns + path: + - demo + - campaigns + type: dataSource + description: Campaigns + facts: [] + grain: + - id: campaign_id + type: attribute + id: campaigns + references: [] + tags: + - Campaigns + title: Campaigns + - aggregatedFacts: [] + attributes: + - description: Customer id + id: customer_id + isNullable: false + labels: [] + sourceColumn: customer_id + sourceColumnDataType: INT + tags: + - Customers + title: Customer id + - description: Customer name + id: customer_name + isNullable: true + labels: [] + sourceColumn: customer_name + sourceColumnDataType: STRING + tags: + - Customers + title: Customer name + - description: Region + id: region + isNullable: true + labels: [] + sourceColumn: region + sourceColumnDataType: STRING + tags: + - Customers + title: Region + - description: State + id: state + isNullable: true + labels: + - description: Location + id: geo__state__location + isNullable: true + sourceColumn: geo__state__location + sourceColumnDataType: STRING + tags: + - Customers + title: Location + valueType: GEO + sourceColumn: state + sourceColumnDataType: STRING + tags: + - Customers + title: State + dataSourceTableId: + dataSourceId: demo-test-ds + id: customers + path: + - demo + - customers + type: dataSource + description: Customers + facts: [] + grain: + - id: customer_id + type: attribute + id: customers + references: [] + tags: + - Customers + title: Customers + - aggregatedFacts: [] + attributes: + - description: Order id + id: order_id + isNullable: true + labels: [] + sourceColumn: order_id + sourceColumnDataType: STRING + tags: + - Order lines + title: Order id + - description: Order line id + id: order_line_id + isNullable: false + labels: [] + sourceColumn: order_line_id + sourceColumnDataType: STRING + tags: + - Order lines + title: Order line id + - description: Order status + id: order_status + isNullable: true + labels: [] + sourceColumn: order_status + sourceColumnDataType: STRING + tags: + - Order lines + title: Order status + dataSourceTableId: + dataSourceId: demo-test-ds + id: order_lines + path: + - demo + - order_lines + type: dataSource + description: Order lines + facts: + - description: Price + id: price + isNullable: true + sourceColumn: price + sourceColumnDataType: NUMERIC + tags: + - Order lines + title: Price + - description: Quantity + id: quantity + isNullable: true + sourceColumn: quantity + sourceColumnDataType: NUMERIC + tags: + - Order lines + title: Quantity + grain: + - id: order_line_id + type: attribute + id: order_lines + references: + - identifier: + id: campaigns + type: dataset + multivalue: false + sources: + - column: campaign_id + dataType: INT + isNullable: true + target: + id: campaign_id + type: attribute + - identifier: + id: customers + type: dataset + multivalue: false + sources: + - column: customer_id + dataType: INT + isNullable: true + target: + id: customer_id + type: attribute + - identifier: + id: date + type: dataset + multivalue: false + sources: + - column: date + dataType: DATE + isNullable: true + target: + id: date + type: date + - identifier: + id: products + type: dataset + multivalue: false + sources: + - column: product_id + dataType: INT + isNullable: true + target: + id: product_id + type: attribute + tags: + - Order lines + title: Order lines + workspaceDataFilterColumns: + - dataType: STRING + name: wdf__region + - dataType: STRING + name: wdf__state + workspaceDataFilterReferences: + - filterColumn: wdf__region + filterColumnDataType: STRING + filterId: + id: wdf__region + type: workspaceDataFilter + - aggregatedFacts: [] + attributes: + - description: Product id + id: product_id + isNullable: false + labels: [] + sourceColumn: product_id + sourceColumnDataType: INT + tags: + - Products + title: Product id + - description: Product name + id: product_name + isNullable: true + labels: [] + sourceColumn: product_name + sourceColumnDataType: STRING + tags: + - Products + title: Product name + - description: Category + id: products.category + isNullable: true + labels: [] + sourceColumn: category + sourceColumnDataType: STRING + tags: + - Products + title: Category + dataSourceTableId: + dataSourceId: demo-test-ds + id: products + path: + - demo + - products + type: dataSource + description: Products + facts: [] + grain: + - id: product_id + type: attribute + id: products + references: [] + tags: + - Products + title: Products + dateInstances: + - description: '' + granularities: + - DAY + - WEEK + - MONTH + - QUARTER + - YEAR + granularitiesFormatting: + titleBase: '' + titlePattern: '%titleBase - %granularityTitle' + id: date + tags: + - Date + title: Date + name: Demo + permissions: + - assignee: + id: demo2 + type: user + name: ANALYZE + - assignee: + id: demoGroup + type: userGroup + name: VIEW + settings: [] + userDataFilters: [] + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: [] + id: demo_west + model: + analytics: + analyticalDashboardExtensions: + - id: campaign + permissions: + - assigneeRule: + type: allWorkspaceUsers + name: VIEW + analyticalDashboards: [] + attributeHierarchies: [] + dashboardPlugins: [] + exportDefinitions: [] + filterContexts: [] + memoryItems: [] + metrics: [] + parameters: [] + visualizationObjects: [] + ldm: + datasetExtensions: + - id: order_lines + workspaceDataFilterReferences: + - filterColumn: wdf__state + filterColumnDataType: STRING + filterId: + id: wdf__state + type: workspaceDataFilter + datasets: [] + dateInstances: [] + name: Demo West + parent: + id: demo + type: workspace + permissions: [] + settings: [] + userDataFilters: [] + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: [] + id: demo_west_california + model: + analytics: + analyticalDashboardExtensions: [] + analyticalDashboards: [] + attributeHierarchies: [] + dashboardPlugins: [] + exportDefinitions: [] + filterContexts: [] + memoryItems: [] + metrics: [] + parameters: [] + visualizationObjects: [] + ldm: + datasets: [] + dateInstances: [] + name: Demo West California + parent: + id: demo_west + type: workspace + permissions: [] + settings: [] + userDataFilters: [] + headers: + Accept-Encoding: + - br, gzip, deflate + Content-Type: + - application/json + X-GDC-VALIDATE-RELATIONS: + - 'true' + X-Requested-With: + - XMLHttpRequest + method: PUT + uri: http://localhost:3000/api/v1/layout/organization + response: + body: + string: + detail: 'Invalid combination of auth properties. Only none, user + password, + token, key-pair or client id + secret are allowed. Datasource: demo-test-ds, + user: , password: , token: , key-pair: , clientId: , + clientSecret: ' + status: 400 + title: Bad Request + traceId: NORMALIZED_TRACE_ID_000000000000 + headers: + Content-Type: + - application/problem+json + DATE: *id001 + Expires: + - '0' + Pragma: + - no-cache + X-Content-Type-Options: + - nosniff + X-GDC-TRACE-ID: *id001 + status: + code: 400 + message: Bad Request + - request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - br, gzip, deflate + X-GDC-VALIDATE-RELATIONS: + - 'true' + X-Requested-With: + - XMLHttpRequest + method: GET + uri: http://localhost:3000/api/v1/layout/organization + response: + body: + string: + agents: + - aiKnowledge: true + availableToAll: true + enabled: true + id: default-ai-assistant + name: Default GoodData AI Assistant + skillsMode: all + customGeoCollections: [] + dataSources: + - alternativeDataSourceId: ds-put-abc-id + authenticationType: USERNAME_PASSWORD + id: demo-test-ds + name: demo-test-ds + permissions: + - assignee: + id: demo2 + type: user + name: MANAGE + - assignee: + id: demoGroup + type: userGroup + name: USE + schema: demo + type: POSTGRESQL + url: jdbc:postgresql://postgres:5432/tiger?sslmode=prefer + username: postgres + exportTemplates: [] + identityProviders: + - id: defaultIdp + idpType: DEX_IDP + ipAllowlistPolicies: [] + jwks: [] + notificationChannels: [] + organization: + allowedOrigins: [] + colorPalettes: [] + cspDirectives: [] + earlyAccess: enableAlerting + earlyAccessValues: + - enableAlerting + - enableSmtp + - enablePreAggregationDatasets + - enableScheduling + - enableCompositeGrain + - enableUserManagement + - enableRawExports + - enableFlexibleDashboardLayout + hostname: localhost + id: default + identityProvider: + id: defaultIdp + type: identityProvider + name: Default Organization + permissions: + - assignee: + id: adminGroup + type: userGroup + name: MANAGE + settings: [] + themes: [] + userGroups: + - id: adminGroup + permissions: [] + - id: adminQA1Group + parents: + - id: adminGroup + type: userGroup + permissions: [] + - id: demoGroup + name: demo group + permissions: [] + - id: visitorsGroup + name: visitors + parents: + - id: demoGroup + type: userGroup + permissions: [] + users: + - id: admin + permissions: [] + settings: [] + userGroups: + - id: adminGroup + type: userGroup + - authId: NORMALIZED_AUTH_ID + email: demo@example.com + firstname: Demo + id: demo + lastname: User + permissions: [] + settings: [] + userGroups: + - id: adminGroup + type: userGroup + - authId: NORMALIZED_AUTH_ID + id: demo2 + permissions: [] + settings: [] + userGroups: + - id: demoGroup + type: userGroup + workspaceDataFilters: + - columnName: wdf__region + id: wdf__region + title: Customer region + workspace: + id: demo + type: workspace + workspaceDataFilterSettings: + - filterValues: + - West + id: region_west + title: Region West + workspace: + id: demo_west + type: workspace + - columnName: wdf__state + id: wdf__state + title: Customer state + workspace: + id: demo_west + type: workspace + workspaceDataFilterSettings: + - filterValues: + - California + id: region_west_california + title: Region West California + workspace: + id: demo_west_california + type: workspace + workspaces: + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: + - assignee: + id: demo2 + type: user + name: MANAGE + - assignee: + id: demoGroup + type: userGroup + name: ANALYZE + id: demo + model: + analytics: + analyticalDashboardExtensions: [] + analyticalDashboards: + - content: + filterContextRef: + identifier: + id: campaign_name_filter + type: filterContext + layout: + sections: + - header: + description: The first insight shows a breakdown of + spend by category and campaign. The second shows + revenue per $ spend, for each campaign, to demonstrate, + how campaigns are successful. + title: Spend breakdown and Revenue + items: + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: campaign_spend + type: visualizationObject + properties: {} + title: Campaign Spend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: revenue_per_usd_vs_spend_by_campaign + type: visualizationObject + properties: {} + title: Revenue per $ vs Spend by Campaign + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: campaign + permissions: + - assigneeRule: + type: allWorkspaceUsers + name: VIEW + title: Campaign + - content: + filterContextRef: + identifier: + id: campaign_name_filter + type: filterContext + layout: + sections: + - items: + - size: + xl: + gridWidth: 12 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: top_10_products + type: visualizationObject + properties: {} + title: DHO simple + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + plugins: + - plugin: + identifier: + id: dashboard_plugin_1 + type: dashboardPlugin + version: '2' + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: dashboard_plugin + title: Dashboard plugin + - content: + filterContextRef: + identifier: + id: region_filter + type: filterContext + layout: + sections: + - items: + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: top_10_products + type: visualizationObject + properties: {} + title: Top 10 Products + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: revenue_trend + type: visualizationObject + properties: {} + title: Revenue Trend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: customers_trend + type: visualizationObject + properties: {} + title: Customers Trend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_categories_pie_chart + type: visualizationObject + properties: {} + title: Product Categories Pie Chart + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_breakdown + type: visualizationObject + properties: {} + title: Product Breakdown + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_saleability + type: visualizationObject + properties: {} + title: Product Saleability + type: insight + - size: + xl: + gridWidth: 12 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: percent_revenue_per_product_by_customer_and_category + type: visualizationObject + properties: {} + title: '% Revenue per Product by Customer and + Category' + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: product_and_category + title: Product & Category + attributeHierarchies: [] + dashboardPlugins: + - content: + url: https://www.example.com + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: Testing record dashboard_plugin_1 + id: dashboard_plugin_1 + title: dashboard_plugin_1 + - content: + url: https://www.example.com + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: Testing record dashboard_plugin_2 + id: dashboard_plugin_2 + title: dashboard_plugin_2 + exportDefinitions: [] + filterContexts: + - content: + filters: + - dateFilter: + from: '0' + granularity: GDC.time.month + to: '0' + type: relative + - attributeFilter: + attributeElements: + uris: [] + displayForm: + identifier: + id: campaign_name + type: label + filterElementsBy: [] + localIdentifier: 14b0807447ef4bc28f43e4fc5c337d1d + negativeSelection: true + version: '2' + description: '' + id: campaign_name_filter + title: filterContext + - content: + filters: + - attributeFilter: + attributeElements: + uris: [] + displayForm: + identifier: + id: region + type: label + filterElementsBy: [] + localIdentifier: 2d5ef8df82444f6ba27b45f0990ee6af + negativeSelection: true + version: '2' + description: '' + id: region_filter + title: filterContext + memoryItems: [] + metrics: + - content: + format: '#,##0' + maql: SELECT COUNT({attribute/customer_id},{attribute/order_line_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_active_customers + title: '# of Active Customers' + - content: + format: '#,##0' + maql: SELECT COUNT({attribute/order_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_orders + title: '# of Orders' + - content: + format: '#,##0' + maql: 'SELECT {metric/amount_of_active_customers} WHERE (SELECT + {metric/revenue} BY {attribute/customer_id}) > 10000 ' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_top_customers + title: '# of Top Customers' + - content: + format: '#,##0.00' + maql: SELECT {metric/amount_of_orders} WHERE NOT ({label/order_status} + IN ("Returned", "Canceled")) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: amount_of_valid_orders + title: '# of Valid Orders' + - content: + format: $#,##0 + maql: SELECT SUM({fact/spend}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: campaign_spend + title: Campaign Spend + - content: + format: $#,##0 + maql: SELECT SUM({fact/price}*{fact/quantity}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: order_amount + title: Order Amount + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / {metric/total_revenue} + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue + title: '% Revenue' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10}\ + \ BY {attribute/customer_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_customers + title: '% Revenue from Top 10 Customers' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10_percent}\ + \ BY {attribute/customer_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_percent_customers + title: '% Revenue from Top 10% Customers' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10_percent}\ + \ BY {attribute/product_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_percent_products + title: '% Revenue from Top 10% Products' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10}\ + \ BY {attribute/product_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_products + title: '% Revenue from Top 10 Products' + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / (SELECT {metric/revenue} BY + {attribute/products.category}, ALL OTHER) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_in_category + title: '% Revenue in Category' + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / (SELECT {metric/revenue} BY + ALL {attribute/product_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_per_product + title: '% Revenue per Product' + - content: + format: $#,##0 + maql: SELECT {metric/order_amount} WHERE NOT ({label/order_status} + IN ("Returned", "Canceled")) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: revenue + title: Revenue + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Clothing") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-clothing + title: Revenue (Clothing) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ( "Electronics") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-electronic + title: Revenue (Electronic) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Home") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-home + title: Revenue (Home) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Outdoor") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-outdoor + title: Revenue (Outdoor) + - content: + format: $#,##0.0 + maql: SELECT AVG(SELECT {metric/revenue} BY {attribute/customer_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_customer + title: Revenue per Customer + - content: + format: $#,##0.0 + maql: SELECT {metric/revenue} / {metric/campaign_spend} + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_dollar_spent + title: Revenue per Dollar Spent + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE TOP(10) OF ({metric/revenue}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_top_10 + title: Revenue / Top 10 + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE TOP(10%) OF ({metric/revenue}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_top_10_percent + title: Revenue / Top 10% + - content: + format: $#,##0 + maql: SELECT {metric/revenue} BY ALL OTHER + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: total_revenue + title: Total Revenue + - content: + format: $#,##0 + maql: SELECT {metric/total_revenue} WITHOUT PARENT FILTER + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: total_revenue-no_filters + title: Total Revenue (No Filters) + parameters: [] + visualizationObjects: + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: campaign_spend + type: metric + localIdentifier: d319bcb2d8c04442a684e3b3cd063381 + title: Campaign Spend + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: campaign_channels.category + type: label + localIdentifier: 291c085e7df8420db84117ca49f59c49 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: campaign_name + type: label + localIdentifier: d9dd143d647d4d148405a60ec2cf59bc + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: type + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_channels.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_name + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:treemap + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: campaign_spend + title: Campaign Spend + - content: + buckets: + - items: + - measure: + alias: Active Customers + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: amount_of_active_customers + type: metric + localIdentifier: 2ba0b87b59ca41a4b1530e81a5c1d081 + title: '# of Active Customers' + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_per_customer + type: metric + localIdentifier: ec0606894b9f4897b7beaf1550608928 + title: Revenue per Customer + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 0de7d7f08af7480aa636857a26be72b6 + localIdentifier: view + filters: + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -12 + granularity: GDC.time.month + to: -1 + properties: + controls: + colorMapping: + - color: + type: guid + value: '20' + id: 2ba0b87b59ca41a4b1530e81a5c1d081 + - color: + type: guid + value: '4' + id: ec0606894b9f4897b7beaf1550608928 + dualAxis: true + legend: + position: bottom + primaryChartType: column + secondaryChartType: line + secondary_yaxis: + measures: + - ec0606894b9f4897b7beaf1550608928 + xaxis: + name: + visible: false + rotation: auto + version: '2' + visualizationUrl: local:combo2 + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: customers_trend + title: Customers Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: percent_revenue_per_product + type: metric + localIdentifier: 08d8346c1ce7438994b251991c0fbf65 + title: '% Revenue per Product' + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: b2350c06688b4da9b3833ebcce65527f + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: customer_name + type: label + localIdentifier: 7a4045fd00ac44579f52406df679435f + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 6a003ffd14994237ba64c4a02c488429 + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 75ea396d0c8b48098e31dccf8b5801d3 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: {} + sorts: + - attributeSortItem: + attributeIdentifier: 7a4045fd00ac44579f52406df679435f + direction: asc + version: '2' + visualizationUrl: local:table + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_per_product_by_customer_and_category + title: '% Revenue per Product by Customer and Category' + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: amount_of_active_customers + type: metric + localIdentifier: 1a14cdc1293c46e89a2e25d3e741d235 + title: '# of Active Customers' + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: c1feca1864244ec2ace7a9b9d7fda231 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: region + type: label + localIdentifier: 530cddbd7ca04d039e73462d81ed44d5 + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: region + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -11 + granularity: GDC.time.month + to: 0 + properties: + controls: + legend: + position: bottom + stackMeasuresToPercent: true + version: '2' + visualizationUrl: local:area + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percentage_of_customers_by_region + title: Percentage of Customers by Region + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 590d332ef686468b8878ae41b23341c6 + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: b166c71091864312a14c7ae8ff886ffe + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: e920a50e0bbb49788df0aac53634c1cd + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:treemap + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_breakdown + title: Product Breakdown + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + computeRatio: true + filters: [] + item: + identifier: + id: revenue + type: metric + format: '#,##0.00%' + localIdentifier: 162b857af49d45769bc12604a5c192b9 + title: '% Revenue' + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: fe513cef1c6244a5ac21c5f49c56b108 + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + dataLabels: + visible: auto + legend: + position: bottom + version: '2' + visualizationUrl: local:donut + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_categories_pie_chart + title: Product Categories Pie Chart + - content: + buckets: + - items: + - measure: + alias: Previous Period + definition: + popMeasureDefinition: + measureIdentifier: c82e025fa2db4afea9a600a424591dbe + popAttribute: + identifier: + id: date.year + type: attribute + localIdentifier: c82e025fa2db4afea9a600a424591dbe_pop + - measure: + alias: This Period + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: c82e025fa2db4afea9a600a424591dbe + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: c804ef5ba7944a5a9f360c86a9e95e9a + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -11 + granularity: GDC.time.month + to: 0 + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + stackMeasures: false + xaxis: + name: + visible: false + yaxis: + name: + visible: false + version: '2' + visualizationUrl: local:column + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_revenue_comparison-over_previous_period + title: Product Revenue Comparison (over previous period) + - content: + buckets: + - items: + - measure: + alias: Number of Orders + definition: + measureDefinition: + filters: [] + item: + identifier: + id: amount_of_orders + type: metric + localIdentifier: aeb5d51a162d4b59aba3bd6ddebcc780 + title: '# of Orders' + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 94b3edd3a73c4a48a4d13bbe9442cc98 + title: Revenue + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: d2a991bdd123448eb2be73d79f1180c4 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: + controls: + dataLabels: + visible: auto + grid: + enabled: true + version: '2' + visualizationUrl: local:scatter + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_saleability + title: Product Saleability + - content: + buckets: + - items: + - measure: + alias: Items Sold + definition: + measureDefinition: + aggregation: sum + filters: [] + item: + identifier: + id: quantity + type: fact + format: '#,##0.00' + localIdentifier: 29486504dd0e4a36a18b0b2f792d3a46 + title: Sum of Quantity + - measure: + definition: + measureDefinition: + aggregation: avg + filters: [] + item: + identifier: + id: price + type: fact + format: '#,##0.00' + localIdentifier: aa6391acccf1452f8011201aef9af492 + title: Avg Price + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: percent_revenue_in_category + type: metric + localIdentifier: 2cd39539d8da46c9883e63caa3ba7cc0 + title: '% Revenue in Category' + - measure: + alias: Total Revenue + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 9a0f08331c094c7facf2a0b4f418de0a + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 06bc6b3b9949466494e4f594c11f1bff + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 192668bfb6a74e9ab7b5d1ce7cb68ea3 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: {} + sorts: + - attributeSortItem: + attributeIdentifier: 06bc6b3b9949466494e4f594c11f1bff + direction: asc + version: '2' + visualizationUrl: local:table + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_and_quantity_by_product_and_category + title: Revenue and Quantity by Product and Category + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 7df6c34387744d69b23ec92e1a5cf543 + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 4bb4fc1986c546de9ad976e6ec23fed4 + localIdentifier: trend + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 34bddcb1cd024902a82396216b0fa9d8 + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + granularity: GDC.time.year + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:line + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_by_category_trend + title: Revenue by Category Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 4ae3401bdbba4938afe983df4ba04e1c + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 1c8ba72dbfc84ddd913bf81dc355c427 + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: {} + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_by_product + title: Revenue by Product + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: campaign_spend + type: metric + localIdentifier: 13a50d811e474ac6808d8da7f4673b35 + title: Campaign Spend + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_per_dollar_spent + type: metric + localIdentifier: a0f15e82e6334280a44dbedc7d086e7c + title: Revenue per Dollar Spent + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: campaign_name + type: label + localIdentifier: 1d9fa968bafb423eb29c938dfb1207ff + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_name + type: label + notIn: + values: [] + properties: + controls: + xaxis: + min: '0' + yaxis: + min: '0' + version: '2' + visualizationUrl: local:scatter + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_usd_vs_spend_by_campaign + title: Revenue per $ vs Spend by Campaign + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 60c854969a9c4c278ab596d99c222e92 + title: Revenue + localIdentifier: measures + - items: + - measure: + alias: Number of Orders + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: amount_of_orders + type: metric + localIdentifier: c2fa7ef48cc54af99f8c280eb451e051 + title: '# of Orders' + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 413ac374b65648fa96826ca01d47bdda + localIdentifier: view + filters: + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -3 + granularity: GDC.time.quarter + to: 0 + properties: + controls: + dualAxis: true + legend: + position: bottom + primaryChartType: column + secondaryChartType: line + secondary_yaxis: + measures: + - c2fa7ef48cc54af99f8c280eb451e051 + xaxis: + name: + visible: false + rotation: auto + version: '2' + visualizationUrl: local:combo2 + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_trend + title: Revenue Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_top_10 + type: metric + localIdentifier: 3f127ccfe57a40399e23f9ae2a4ad810 + title: Revenue / Top 10 + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: customer_name + type: label + localIdentifier: f4e39e24f11e4827a191c30d65c89d2c + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: state + type: label + localIdentifier: bbccd430176d428caed54c99afc9589e + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: state + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: top_10_customers + title: Top 10 Customers + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_top_10 + type: metric + localIdentifier: 77dc71bbac92412bac5f94284a5919df + title: Revenue / Top 10 + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 781952e728204dcf923142910cc22ae2 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: fe513cef1c6244a5ac21c5f49c56b108 + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: top_10_products + title: Top 10 Products + ldm: + datasets: + - aggregatedFacts: [] + attributes: + - description: Campaign channel id + id: campaign_channel_id + isNullable: false + labels: [] + sourceColumn: campaign_channel_id + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Campaign channel id + - description: Category + id: campaign_channels.category + isNullable: true + labels: [] + sourceColumn: category + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Category + - description: Type + id: type + isNullable: true + labels: [] + sourceColumn: type + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Type + dataSourceTableId: + dataSourceId: demo-test-ds + id: campaign_channels + path: + - demo + - campaign_channels + type: dataSource + description: Campaign channels + facts: + - description: Budget + id: budget + isNullable: true + sourceColumn: budget + sourceColumnDataType: NUMERIC + tags: + - Campaign channels + title: Budget + - description: Spend + id: spend + isNullable: true + sourceColumn: spend + sourceColumnDataType: NUMERIC + tags: + - Campaign channels + title: Spend + grain: + - id: campaign_channel_id + type: attribute + id: campaign_channels + references: + - identifier: + id: campaigns + type: dataset + multivalue: false + sources: + - column: campaign_id + dataType: INT + isNullable: true + target: + id: campaign_id + type: attribute + tags: + - Campaign channels + title: Campaign channels + - aggregatedFacts: + - description: Budget Agg + id: budget_agg + sourceColumn: budget + sourceColumnDataType: INT + sourceFactReference: + operation: SUM + reference: + id: budget + type: fact + tags: + - Campaign channels per category + attributes: [] + description: Campaign channels per categories + facts: [] + grain: [] + id: campaign_channels_per_category + precedence: 1 + references: + - identifier: + id: campaign_channels + type: dataset + multivalue: false + sources: + - column: campaign_channel_id + dataType: STRING + isNullable: true + target: + id: campaign_channel_id + type: attribute + sql: + dataSourceId: demo-test-ds + statement: SELECT category, SUM(budget) FROM campaign_channels + GROUP BY category + tags: + - Campaign channels per category + title: Campaign channels per category + - aggregatedFacts: [] + attributes: + - description: Campaign id + id: campaign_id + isNullable: false + labels: [] + sourceColumn: campaign_id + sourceColumnDataType: INT + tags: + - Campaigns + title: Campaign id + - description: Campaign name + id: campaign_name + isNullable: true + labels: [] + sourceColumn: campaign_name + sourceColumnDataType: STRING + tags: + - Campaigns + title: Campaign name + dataSourceTableId: + dataSourceId: demo-test-ds + id: campaigns + path: + - demo + - campaigns + type: dataSource + description: Campaigns + facts: [] + grain: + - id: campaign_id + type: attribute + id: campaigns + references: [] + tags: + - Campaigns + title: Campaigns + - aggregatedFacts: [] + attributes: + - description: Customer id + id: customer_id + isNullable: false + labels: [] + sourceColumn: customer_id + sourceColumnDataType: INT + tags: + - Customers + title: Customer id + - description: Customer name + id: customer_name + isNullable: true + labels: [] + sourceColumn: customer_name + sourceColumnDataType: STRING + tags: + - Customers + title: Customer name + - description: Region + id: region + isNullable: true + labels: [] + sourceColumn: region + sourceColumnDataType: STRING + tags: + - Customers + title: Region + - description: State + id: state + isNullable: true + labels: + - description: Location + id: geo__state__location + isNullable: true + sourceColumn: geo__state__location + sourceColumnDataType: STRING + tags: + - Customers + title: Location + valueType: GEO + sourceColumn: state + sourceColumnDataType: STRING + tags: + - Customers + title: State + dataSourceTableId: + dataSourceId: demo-test-ds + id: customers + path: + - demo + - customers + type: dataSource + description: Customers + facts: [] + grain: + - id: customer_id + type: attribute + id: customers + references: [] + tags: + - Customers + title: Customers + - aggregatedFacts: [] + attributes: + - description: Order id + id: order_id + isNullable: true + labels: [] + sourceColumn: order_id + sourceColumnDataType: STRING + tags: + - Order lines + title: Order id + - description: Order line id + id: order_line_id + isNullable: false + labels: [] + sourceColumn: order_line_id + sourceColumnDataType: STRING + tags: + - Order lines + title: Order line id + - description: Order status + id: order_status + isNullable: true + labels: [] + sourceColumn: order_status + sourceColumnDataType: STRING + tags: + - Order lines + title: Order status + dataSourceTableId: + dataSourceId: demo-test-ds + id: order_lines + path: + - demo + - order_lines + type: dataSource + description: Order lines + facts: + - description: Price + id: price + isNullable: true + sourceColumn: price + sourceColumnDataType: NUMERIC + tags: + - Order lines + title: Price + - description: Quantity + id: quantity + isNullable: true + sourceColumn: quantity + sourceColumnDataType: NUMERIC + tags: + - Order lines + title: Quantity + grain: + - id: order_line_id + type: attribute + id: order_lines + references: + - identifier: + id: campaigns + type: dataset + multivalue: false + sources: + - column: campaign_id + dataType: INT + isNullable: true + target: + id: campaign_id + type: attribute + - identifier: + id: customers + type: dataset + multivalue: false + sources: + - column: customer_id + dataType: INT + isNullable: true + target: + id: customer_id + type: attribute + - identifier: + id: date + type: dataset + multivalue: false + sources: + - column: date + dataType: DATE + isNullable: true + target: + id: date + type: date + - identifier: + id: products + type: dataset + multivalue: false + sources: + - column: product_id + dataType: INT + isNullable: true + target: + id: product_id + type: attribute + tags: + - Order lines + title: Order lines + workspaceDataFilterColumns: + - dataType: STRING + name: wdf__region + - dataType: STRING + name: wdf__state + workspaceDataFilterReferences: + - filterColumn: wdf__region + filterColumnDataType: STRING + filterId: + id: wdf__region + type: workspaceDataFilter + - aggregatedFacts: [] + attributes: + - description: Product id + id: product_id + isNullable: false + labels: [] + sourceColumn: product_id + sourceColumnDataType: INT + tags: + - Products + title: Product id + - description: Product name + id: product_name + isNullable: true + labels: [] + sourceColumn: product_name + sourceColumnDataType: STRING + tags: + - Products + title: Product name + - description: Category + id: products.category + isNullable: true + labels: [] + sourceColumn: category + sourceColumnDataType: STRING + tags: + - Products + title: Category + dataSourceTableId: + dataSourceId: demo-test-ds + id: products + path: + - demo + - products + type: dataSource + description: Products + facts: [] + grain: + - id: product_id + type: attribute + id: products + references: [] + tags: + - Products + title: Products + dateInstances: + - description: '' + granularities: + - DAY + - WEEK + - MONTH + - QUARTER + - YEAR + granularitiesFormatting: + titleBase: '' + titlePattern: '%titleBase - %granularityTitle' + id: date + tags: + - Date + title: Date + name: Demo + permissions: + - assignee: + id: demo2 + type: user + name: ANALYZE + - assignee: + id: demoGroup + type: userGroup + name: VIEW + settings: [] + userDataFilters: [] + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: [] + id: demo_west + model: + analytics: + analyticalDashboardExtensions: + - id: campaign + permissions: + - assigneeRule: + type: allWorkspaceUsers + name: VIEW + analyticalDashboards: [] + attributeHierarchies: [] + dashboardPlugins: [] + exportDefinitions: [] + filterContexts: [] + memoryItems: [] + metrics: [] + parameters: [] + visualizationObjects: [] + ldm: + datasetExtensions: + - id: order_lines + workspaceDataFilterReferences: + - filterColumn: wdf__state + filterColumnDataType: STRING + filterId: + id: wdf__state + type: workspaceDataFilter + datasets: [] + dateInstances: [] + name: Demo West + parent: + id: demo + type: workspace + permissions: [] + settings: [] + userDataFilters: [] + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: [] + id: demo_west_california + model: + analytics: + analyticalDashboardExtensions: [] + analyticalDashboards: [] + attributeHierarchies: [] + dashboardPlugins: [] + exportDefinitions: [] + filterContexts: [] + memoryItems: [] + metrics: [] + parameters: [] + visualizationObjects: [] + ldm: + datasets: [] + dateInstances: [] + name: Demo West California + parent: + id: demo_west + type: workspace + permissions: [] + settings: [] + userDataFilters: [] + headers: + Content-Type: + - application/json + DATE: *id001 + Expires: + - '0' + Pragma: + - no-cache + X-Content-Type-Options: + - nosniff + X-GDC-TRACE-ID: *id001 + status: + code: 200 + message: OK + - request: + body: + agents: + - aiKnowledge: true + availableToAll: true + enabled: true + id: default-ai-assistant + name: Default GoodData AI Assistant + skillsMode: all + customGeoCollections: [] + dataSources: + - alternativeDataSourceId: ds-put-abc-id + authenticationType: USERNAME_PASSWORD + id: demo-test-ds + name: demo-test-ds + permissions: + - assignee: + id: demo2 + type: user + name: MANAGE + - assignee: + id: demoGroup + type: userGroup + name: USE + schema: demo + type: POSTGRESQL + url: jdbc:postgresql://postgres:5432/tiger?sslmode=prefer + username: postgres + exportTemplates: [] + identityProviders: + - id: defaultIdp + idpType: DEX_IDP + ipAllowlistPolicies: [] + jwks: [] + notificationChannels: [] + organization: + allowedOrigins: [] + colorPalettes: [] + cspDirectives: [] + earlyAccess: enableAlerting + earlyAccessValues: + - enableAlerting + - enableSmtp + - enablePreAggregationDatasets + - enableScheduling + - enableCompositeGrain + - enableUserManagement + - enableRawExports + - enableFlexibleDashboardLayout + hostname: localhost + id: default + identityProvider: + id: defaultIdp + type: identityProvider + name: Default Organization + permissions: + - assignee: + id: adminGroup + type: userGroup + name: MANAGE + settings: [] + themes: [] + userGroups: + - id: adminGroup + permissions: [] + - id: adminQA1Group + parents: + - id: adminGroup + type: userGroup + permissions: [] + - id: demoGroup + name: demo group + permissions: [] + - id: visitorsGroup + name: visitors + parents: + - id: demoGroup + type: userGroup + permissions: [] + users: + - id: admin + permissions: [] + settings: [] + userGroups: + - id: adminGroup + type: userGroup + - authId: NORMALIZED_AUTH_ID + email: demo@example.com + firstname: Demo + id: demo + lastname: User + permissions: [] + settings: [] + userGroups: + - id: adminGroup + type: userGroup + - authId: NORMALIZED_AUTH_ID + id: demo2 + permissions: [] + settings: [] + userGroups: + - id: demoGroup + type: userGroup + workspaceDataFilters: + - columnName: wdf__region + id: wdf__region + title: Customer region + workspace: + id: demo + type: workspace + workspaceDataFilterSettings: + - filterValues: + - West + id: region_west + title: Region West + workspace: + id: demo_west + type: workspace + - columnName: wdf__state + id: wdf__state + title: Customer state + workspace: + id: demo_west + type: workspace + workspaceDataFilterSettings: + - filterValues: + - California + id: region_west_california + title: Region West California + workspace: + id: demo_west_california + type: workspace + workspaces: + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: + - assignee: + id: demo2 + type: user + name: MANAGE + - assignee: + id: demoGroup + type: userGroup + name: ANALYZE + id: demo + model: + analytics: + analyticalDashboardExtensions: [] + analyticalDashboards: + - content: + filterContextRef: + identifier: + id: campaign_name_filter + type: filterContext + layout: + sections: + - header: + description: The first insight shows a breakdown of + spend by category and campaign. The second shows revenue + per $ spend, for each campaign, to demonstrate, how + campaigns are successful. + title: Spend breakdown and Revenue + items: + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: campaign_spend + type: visualizationObject + properties: {} + title: Campaign Spend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: revenue_per_usd_vs_spend_by_campaign + type: visualizationObject + properties: {} + title: Revenue per $ vs Spend by Campaign + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: campaign + permissions: + - assigneeRule: + type: allWorkspaceUsers + name: VIEW + title: Campaign + - content: + filterContextRef: + identifier: + id: campaign_name_filter + type: filterContext + layout: + sections: + - items: + - size: + xl: + gridWidth: 12 + type: IDashboardLayoutItem + widget: + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: top_10_products + type: visualizationObject + properties: {} + title: DHO simple + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + plugins: + - plugin: + identifier: + id: dashboard_plugin_1 + type: dashboardPlugin + version: '2' + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: dashboard_plugin + title: Dashboard plugin + - content: + filterContextRef: + identifier: + id: region_filter + type: filterContext + layout: + sections: + - items: + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: top_10_products + type: visualizationObject + properties: {} + title: Top 10 Products + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: revenue_trend + type: visualizationObject + properties: {} + title: Revenue Trend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: customers_trend + type: visualizationObject + properties: {} + title: Customers Trend + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_categories_pie_chart + type: visualizationObject + properties: {} + title: Product Categories Pie Chart + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_breakdown + type: visualizationObject + properties: {} + title: Product Breakdown + type: insight + - size: + xl: + gridWidth: 6 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: product_saleability + type: visualizationObject + properties: {} + title: Product Saleability + type: insight + - size: + xl: + gridWidth: 12 + type: IDashboardLayoutItem + widget: + dateDataSet: + identifier: + id: date + type: dataset + description: '' + drills: [] + ignoreDashboardFilters: [] + insight: + identifier: + id: percent_revenue_per_product_by_customer_and_category + type: visualizationObject + properties: {} + title: '% Revenue per Product by Customer and Category' + type: insight + type: IDashboardLayoutSection + type: IDashboardLayout + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: product_and_category + title: Product & Category + attributeHierarchies: [] + dashboardPlugins: + - content: + url: https://www.example.com + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: Testing record dashboard_plugin_1 + id: dashboard_plugin_1 + title: dashboard_plugin_1 + - content: + url: https://www.example.com + version: '2' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: Testing record dashboard_plugin_2 + id: dashboard_plugin_2 + title: dashboard_plugin_2 + exportDefinitions: [] + filterContexts: + - content: + filters: + - dateFilter: + from: '0' + granularity: GDC.time.month + to: '0' + type: relative + - attributeFilter: + attributeElements: + uris: [] + displayForm: + identifier: + id: campaign_name + type: label + filterElementsBy: [] + localIdentifier: 14b0807447ef4bc28f43e4fc5c337d1d + negativeSelection: true + version: '2' + description: '' + id: campaign_name_filter + title: filterContext + - content: + filters: + - attributeFilter: + attributeElements: + uris: [] + displayForm: + identifier: + id: region + type: label + filterElementsBy: [] + localIdentifier: 2d5ef8df82444f6ba27b45f0990ee6af + negativeSelection: true + version: '2' + description: '' + id: region_filter + title: filterContext + memoryItems: [] + metrics: + - content: + format: '#,##0' + maql: SELECT COUNT({attribute/customer_id},{attribute/order_line_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_active_customers + title: '# of Active Customers' + - content: + format: '#,##0' + maql: SELECT COUNT({attribute/order_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_orders + title: '# of Orders' + - content: + format: '#,##0' + maql: 'SELECT {metric/amount_of_active_customers} WHERE (SELECT + {metric/revenue} BY {attribute/customer_id}) > 10000 ' + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: amount_of_top_customers + title: '# of Top Customers' + - content: + format: '#,##0.00' + maql: SELECT {metric/amount_of_orders} WHERE NOT ({label/order_status} + IN ("Returned", "Canceled")) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: amount_of_valid_orders + title: '# of Valid Orders' + - content: + format: $#,##0 + maql: SELECT SUM({fact/spend}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: campaign_spend + title: Campaign Spend + - content: + format: $#,##0 + maql: SELECT SUM({fact/price}*{fact/quantity}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: order_amount + title: Order Amount + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / {metric/total_revenue} + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue + title: '% Revenue' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10}\ + \ BY {attribute/customer_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_customers + title: '% Revenue from Top 10 Customers' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10_percent}\ + \ BY {attribute/customer_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_percent_customers + title: '% Revenue from Top 10% Customers' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10_percent}\ + \ BY {attribute/product_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_percent_products + title: '% Revenue from Top 10% Products' + - content: + format: '#,##0.0%' + maql: "SELECT\n (SELECT {metric/revenue} WHERE (SELECT {metric/revenue_top_10}\ + \ BY {attribute/product_id}) > 0)\n /\n {metric/revenue}" + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_from_top_10_products + title: '% Revenue from Top 10 Products' + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / (SELECT {metric/revenue} BY + {attribute/products.category}, ALL OTHER) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_in_category + title: '% Revenue in Category' + - content: + format: '#,##0.0%' + maql: SELECT {metric/revenue} / (SELECT {metric/revenue} BY + ALL {attribute/product_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_per_product + title: '% Revenue per Product' + - content: + format: $#,##0 + maql: SELECT {metric/order_amount} WHERE NOT ({label/order_status} + IN ("Returned", "Canceled")) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + description: '' + id: revenue + title: Revenue + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Clothing") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-clothing + title: Revenue (Clothing) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ( "Electronics") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-electronic + title: Revenue (Electronic) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Home") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-home + title: Revenue (Home) + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE {label/products.category} + IN ("Outdoor") + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue-outdoor + title: Revenue (Outdoor) + - content: + format: $#,##0.0 + maql: SELECT AVG(SELECT {metric/revenue} BY {attribute/customer_id}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_customer + title: Revenue per Customer + - content: + format: $#,##0.0 + maql: SELECT {metric/revenue} / {metric/campaign_spend} + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_dollar_spent + title: Revenue per Dollar Spent + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE TOP(10) OF ({metric/revenue}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_top_10 + title: Revenue / Top 10 + - content: + format: $#,##0 + maql: SELECT {metric/revenue} WHERE TOP(10%) OF ({metric/revenue}) + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_top_10_percent + title: Revenue / Top 10% + - content: + format: $#,##0 + maql: SELECT {metric/revenue} BY ALL OTHER + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: total_revenue + title: Total Revenue + - content: + format: $#,##0 + maql: SELECT {metric/total_revenue} WITHOUT PARENT FILTER + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: total_revenue-no_filters + title: Total Revenue (No Filters) + parameters: [] + visualizationObjects: + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: campaign_spend + type: metric + localIdentifier: d319bcb2d8c04442a684e3b3cd063381 + title: Campaign Spend + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: campaign_channels.category + type: label + localIdentifier: 291c085e7df8420db84117ca49f59c49 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: campaign_name + type: label + localIdentifier: d9dd143d647d4d148405a60ec2cf59bc + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: type + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_channels.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_name + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:treemap + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: campaign_spend + title: Campaign Spend + - content: + buckets: + - items: + - measure: + alias: Active Customers + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: amount_of_active_customers + type: metric + localIdentifier: 2ba0b87b59ca41a4b1530e81a5c1d081 + title: '# of Active Customers' + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_per_customer + type: metric + localIdentifier: ec0606894b9f4897b7beaf1550608928 + title: Revenue per Customer + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 0de7d7f08af7480aa636857a26be72b6 + localIdentifier: view + filters: + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -12 + granularity: GDC.time.month + to: -1 + properties: + controls: + colorMapping: + - color: + type: guid + value: '20' + id: 2ba0b87b59ca41a4b1530e81a5c1d081 + - color: + type: guid + value: '4' + id: ec0606894b9f4897b7beaf1550608928 + dualAxis: true + legend: + position: bottom + primaryChartType: column + secondaryChartType: line + secondary_yaxis: + measures: + - ec0606894b9f4897b7beaf1550608928 + xaxis: + name: + visible: false + rotation: auto + version: '2' + visualizationUrl: local:combo2 + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: customers_trend + title: Customers Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: percent_revenue_per_product + type: metric + localIdentifier: 08d8346c1ce7438994b251991c0fbf65 + title: '% Revenue per Product' + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: b2350c06688b4da9b3833ebcce65527f + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: customer_name + type: label + localIdentifier: 7a4045fd00ac44579f52406df679435f + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 6a003ffd14994237ba64c4a02c488429 + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 75ea396d0c8b48098e31dccf8b5801d3 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: {} + sorts: + - attributeSortItem: + attributeIdentifier: 7a4045fd00ac44579f52406df679435f + direction: asc + version: '2' + visualizationUrl: local:table + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percent_revenue_per_product_by_customer_and_category + title: '% Revenue per Product by Customer and Category' + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: amount_of_active_customers + type: metric + localIdentifier: 1a14cdc1293c46e89a2e25d3e741d235 + title: '# of Active Customers' + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: c1feca1864244ec2ace7a9b9d7fda231 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: region + type: label + localIdentifier: 530cddbd7ca04d039e73462d81ed44d5 + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: region + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -11 + granularity: GDC.time.month + to: 0 + properties: + controls: + legend: + position: bottom + stackMeasuresToPercent: true + version: '2' + visualizationUrl: local:area + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: percentage_of_customers_by_region + title: Percentage of Customers by Region + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 590d332ef686468b8878ae41b23341c6 + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: b166c71091864312a14c7ae8ff886ffe + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: e920a50e0bbb49788df0aac53634c1cd + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:treemap + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_breakdown + title: Product Breakdown + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + computeRatio: true + filters: [] + item: + identifier: + id: revenue + type: metric + format: '#,##0.00%' + localIdentifier: 162b857af49d45769bc12604a5c192b9 + title: '% Revenue' + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: fe513cef1c6244a5ac21c5f49c56b108 + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + dataLabels: + visible: auto + legend: + position: bottom + version: '2' + visualizationUrl: local:donut + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_categories_pie_chart + title: Product Categories Pie Chart + - content: + buckets: + - items: + - measure: + alias: Previous Period + definition: + popMeasureDefinition: + measureIdentifier: c82e025fa2db4afea9a600a424591dbe + popAttribute: + identifier: + id: date.year + type: attribute + localIdentifier: c82e025fa2db4afea9a600a424591dbe_pop + - measure: + alias: This Period + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: c82e025fa2db4afea9a600a424591dbe + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: c804ef5ba7944a5a9f360c86a9e95e9a + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -11 + granularity: GDC.time.month + to: 0 + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + stackMeasures: false + xaxis: + name: + visible: false + yaxis: + name: + visible: false + version: '2' + visualizationUrl: local:column + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_revenue_comparison-over_previous_period + title: Product Revenue Comparison (over previous period) + - content: + buckets: + - items: + - measure: + alias: Number of Orders + definition: + measureDefinition: + filters: [] + item: + identifier: + id: amount_of_orders + type: metric + localIdentifier: aeb5d51a162d4b59aba3bd6ddebcc780 + title: '# of Orders' + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 94b3edd3a73c4a48a4d13bbe9442cc98 + title: Revenue + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: d2a991bdd123448eb2be73d79f1180c4 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: + controls: + dataLabels: + visible: auto + grid: + enabled: true + version: '2' + visualizationUrl: local:scatter + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: product_saleability + title: Product Saleability + - content: + buckets: + - items: + - measure: + alias: Items Sold + definition: + measureDefinition: + aggregation: sum + filters: [] + item: + identifier: + id: quantity + type: fact + format: '#,##0.00' + localIdentifier: 29486504dd0e4a36a18b0b2f792d3a46 + title: Sum of Quantity + - measure: + definition: + measureDefinition: + aggregation: avg + filters: [] + item: + identifier: + id: price + type: fact + format: '#,##0.00' + localIdentifier: aa6391acccf1452f8011201aef9af492 + title: Avg Price + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: percent_revenue_in_category + type: metric + localIdentifier: 2cd39539d8da46c9883e63caa3ba7cc0 + title: '% Revenue in Category' + - measure: + alias: Total Revenue + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 9a0f08331c094c7facf2a0b4f418de0a + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 06bc6b3b9949466494e4f594c11f1bff + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 192668bfb6a74e9ab7b5d1ce7cb68ea3 + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: {} + sorts: + - attributeSortItem: + attributeIdentifier: 06bc6b3b9949466494e4f594c11f1bff + direction: asc + version: '2' + visualizationUrl: local:table + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_and_quantity_by_product_and_category + title: Revenue and Quantity by Product and Category + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 7df6c34387744d69b23ec92e1a5cf543 + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 4bb4fc1986c546de9ad976e6ec23fed4 + localIdentifier: trend + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: 34bddcb1cd024902a82396216b0fa9d8 + localIdentifier: segment + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + granularity: GDC.time.year + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:line + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_by_category_trend + title: Revenue by Category Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 4ae3401bdbba4938afe983df4ba04e1c + title: Revenue + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 1c8ba72dbfc84ddd913bf81dc355c427 + localIdentifier: view + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + properties: {} + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_by_product + title: Revenue by Product + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: campaign_spend + type: metric + localIdentifier: 13a50d811e474ac6808d8da7f4673b35 + title: Campaign Spend + localIdentifier: measures + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_per_dollar_spent + type: metric + localIdentifier: a0f15e82e6334280a44dbedc7d086e7c + title: Revenue per Dollar Spent + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: campaign_name + type: label + localIdentifier: 1d9fa968bafb423eb29c938dfb1207ff + localIdentifier: attribute + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: campaign_name + type: label + notIn: + values: [] + properties: + controls: + xaxis: + min: '0' + yaxis: + min: '0' + version: '2' + visualizationUrl: local:scatter + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_per_usd_vs_spend_by_campaign + title: Revenue per $ vs Spend by Campaign + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: revenue + type: metric + localIdentifier: 60c854969a9c4c278ab596d99c222e92 + title: Revenue + localIdentifier: measures + - items: + - measure: + alias: Number of Orders + definition: + measureDefinition: + computeRatio: false + filters: [] + item: + identifier: + id: amount_of_orders + type: metric + localIdentifier: c2fa7ef48cc54af99f8c280eb451e051 + title: '# of Orders' + localIdentifier: secondary_measures + - items: + - attribute: + displayForm: + identifier: + id: date.month + type: label + localIdentifier: 413ac374b65648fa96826ca01d47bdda + localIdentifier: view + filters: + - relativeDateFilter: + dataSet: + identifier: + id: date + type: dataset + from: -3 + granularity: GDC.time.quarter + to: 0 + properties: + controls: + dualAxis: true + legend: + position: bottom + primaryChartType: column + secondaryChartType: line + secondary_yaxis: + measures: + - c2fa7ef48cc54af99f8c280eb451e051 + xaxis: + name: + visible: false + rotation: auto + version: '2' + visualizationUrl: local:combo2 + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: revenue_trend + title: Revenue Trend + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_top_10 + type: metric + localIdentifier: 3f127ccfe57a40399e23f9ae2a4ad810 + title: Revenue / Top 10 + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: customer_name + type: label + localIdentifier: f4e39e24f11e4827a191c30d65c89d2c + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: state + type: label + localIdentifier: bbccd430176d428caed54c99afc9589e + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: customer_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: state + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: top_10_customers + title: Top 10 Customers + - content: + buckets: + - items: + - measure: + definition: + measureDefinition: + filters: [] + item: + identifier: + id: revenue_top_10 + type: metric + localIdentifier: 77dc71bbac92412bac5f94284a5919df + title: Revenue / Top 10 + localIdentifier: measures + - items: + - attribute: + displayForm: + identifier: + id: product_name + type: label + localIdentifier: 781952e728204dcf923142910cc22ae2 + localIdentifier: view + - items: + - attribute: + displayForm: + identifier: + id: products.category + type: label + localIdentifier: fe513cef1c6244a5ac21c5f49c56b108 + localIdentifier: stack + filters: + - negativeAttributeFilter: + displayForm: + identifier: + id: product_name + type: label + notIn: + values: [] + - negativeAttributeFilter: + displayForm: + identifier: + id: products.category + type: label + notIn: + values: [] + properties: + controls: + legend: + position: bottom + version: '2' + visualizationUrl: local:bar + createdAt: 2000-01-01 00:00 + createdBy: + id: admin + type: user + id: top_10_products + title: Top 10 Products + ldm: + datasets: + - aggregatedFacts: [] + attributes: + - description: Campaign channel id + id: campaign_channel_id + isNullable: false + labels: [] + sourceColumn: campaign_channel_id + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Campaign channel id + - description: Category + id: campaign_channels.category + isNullable: true + labels: [] + sourceColumn: category + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Category + - description: Type + id: type + isNullable: true + labels: [] + sourceColumn: type + sourceColumnDataType: STRING + tags: + - Campaign channels + title: Type + dataSourceTableId: + dataSourceId: demo-test-ds + id: campaign_channels + path: + - demo + - campaign_channels + type: dataSource + description: Campaign channels + facts: + - description: Budget + id: budget + isNullable: true + sourceColumn: budget + sourceColumnDataType: NUMERIC + tags: + - Campaign channels + title: Budget + - description: Spend + id: spend + isNullable: true + sourceColumn: spend + sourceColumnDataType: NUMERIC + tags: + - Campaign channels + title: Spend + grain: + - id: campaign_channel_id + type: attribute + id: campaign_channels + references: + - identifier: + id: campaigns + type: dataset + multivalue: false + sources: + - column: campaign_id + dataType: INT + isNullable: true + target: + id: campaign_id + type: attribute + tags: + - Campaign channels + title: Campaign channels + - aggregatedFacts: + - description: Budget Agg + id: budget_agg + sourceColumn: budget + sourceColumnDataType: INT + sourceFactReference: + operation: SUM + reference: + id: budget + type: fact + tags: + - Campaign channels per category + attributes: [] + description: Campaign channels per categories + facts: [] + grain: [] + id: campaign_channels_per_category + precedence: 1 + references: + - identifier: + id: campaign_channels + type: dataset + multivalue: false + sources: + - column: campaign_channel_id + dataType: STRING + isNullable: true + target: + id: campaign_channel_id + type: attribute + sql: + dataSourceId: demo-test-ds + statement: SELECT category, SUM(budget) FROM campaign_channels + GROUP BY category + tags: + - Campaign channels per category + title: Campaign channels per category + - aggregatedFacts: [] + attributes: + - description: Campaign id + id: campaign_id + isNullable: false + labels: [] + sourceColumn: campaign_id + sourceColumnDataType: INT + tags: + - Campaigns + title: Campaign id + - description: Campaign name + id: campaign_name + isNullable: true + labels: [] + sourceColumn: campaign_name + sourceColumnDataType: STRING + tags: + - Campaigns + title: Campaign name + dataSourceTableId: + dataSourceId: demo-test-ds + id: campaigns + path: + - demo + - campaigns + type: dataSource + description: Campaigns + facts: [] + grain: + - id: campaign_id + type: attribute + id: campaigns + references: [] + tags: + - Campaigns + title: Campaigns + - aggregatedFacts: [] + attributes: + - description: Customer id + id: customer_id + isNullable: false + labels: [] + sourceColumn: customer_id + sourceColumnDataType: INT + tags: + - Customers + title: Customer id + - description: Customer name + id: customer_name + isNullable: true + labels: [] + sourceColumn: customer_name + sourceColumnDataType: STRING + tags: + - Customers + title: Customer name + - description: Region + id: region + isNullable: true + labels: [] + sourceColumn: region + sourceColumnDataType: STRING + tags: + - Customers + title: Region + - description: State + id: state + isNullable: true + labels: + - description: Location + id: geo__state__location + isNullable: true + sourceColumn: geo__state__location + sourceColumnDataType: STRING + tags: + - Customers + title: Location + valueType: GEO + sourceColumn: state + sourceColumnDataType: STRING + tags: + - Customers + title: State + dataSourceTableId: + dataSourceId: demo-test-ds + id: customers + path: + - demo + - customers + type: dataSource + description: Customers + facts: [] + grain: + - id: customer_id + type: attribute + id: customers + references: [] + tags: + - Customers + title: Customers + - aggregatedFacts: [] + attributes: + - description: Order id + id: order_id + isNullable: true + labels: [] + sourceColumn: order_id + sourceColumnDataType: STRING + tags: + - Order lines + title: Order id + - description: Order line id + id: order_line_id + isNullable: false + labels: [] + sourceColumn: order_line_id + sourceColumnDataType: STRING + tags: + - Order lines + title: Order line id + - description: Order status + id: order_status + isNullable: true + labels: [] + sourceColumn: order_status + sourceColumnDataType: STRING + tags: + - Order lines + title: Order status + dataSourceTableId: + dataSourceId: demo-test-ds + id: order_lines + path: + - demo + - order_lines + type: dataSource + description: Order lines + facts: + - description: Price + id: price + isNullable: true + sourceColumn: price + sourceColumnDataType: NUMERIC + tags: + - Order lines + title: Price + - description: Quantity + id: quantity + isNullable: true + sourceColumn: quantity + sourceColumnDataType: NUMERIC + tags: + - Order lines + title: Quantity + grain: + - id: order_line_id + type: attribute + id: order_lines + references: + - identifier: + id: campaigns + type: dataset + multivalue: false + sources: + - column: campaign_id + dataType: INT + isNullable: true + target: + id: campaign_id + type: attribute + - identifier: + id: customers + type: dataset + multivalue: false + sources: + - column: customer_id + dataType: INT + isNullable: true + target: + id: customer_id + type: attribute + - identifier: + id: date + type: dataset + multivalue: false + sources: + - column: date + dataType: DATE + isNullable: true + target: + id: date + type: date + - identifier: + id: products + type: dataset + multivalue: false + sources: + - column: product_id + dataType: INT + isNullable: true + target: + id: product_id + type: attribute + tags: + - Order lines + title: Order lines + workspaceDataFilterColumns: + - dataType: STRING + name: wdf__region + - dataType: STRING + name: wdf__state + workspaceDataFilterReferences: + - filterColumn: wdf__region + filterColumnDataType: STRING + filterId: + id: wdf__region + type: workspaceDataFilter + - aggregatedFacts: [] + attributes: + - description: Product id + id: product_id + isNullable: false + labels: [] + sourceColumn: product_id + sourceColumnDataType: INT + tags: + - Products + title: Product id + - description: Product name + id: product_name + isNullable: true + labels: [] + sourceColumn: product_name + sourceColumnDataType: STRING + tags: + - Products + title: Product name + - description: Category + id: products.category + isNullable: true + labels: [] + sourceColumn: category + sourceColumnDataType: STRING + tags: + - Products + title: Category + dataSourceTableId: + dataSourceId: demo-test-ds + id: products + path: + - demo + - products + type: dataSource + description: Products + facts: [] + grain: + - id: product_id + type: attribute + id: products + references: [] + tags: + - Products + title: Products + dateInstances: + - description: '' + granularities: + - DAY + - WEEK + - MONTH + - QUARTER + - YEAR + granularitiesFormatting: + titleBase: '' + titlePattern: '%titleBase - %granularityTitle' + id: date + tags: + - Date + title: Date + name: Demo + permissions: + - assignee: + id: demo2 + type: user + name: ANALYZE + - assignee: + id: demoGroup + type: userGroup + name: VIEW + settings: [] + userDataFilters: [] + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: [] + id: demo_west + model: + analytics: + analyticalDashboardExtensions: + - id: campaign + permissions: + - assigneeRule: + type: allWorkspaceUsers + name: VIEW + analyticalDashboards: [] + attributeHierarchies: [] + dashboardPlugins: [] + exportDefinitions: [] + filterContexts: [] + memoryItems: [] + metrics: [] + parameters: [] + visualizationObjects: [] + ldm: + datasetExtensions: + - id: order_lines + workspaceDataFilterReferences: + - filterColumn: wdf__state + filterColumnDataType: STRING + filterId: + id: wdf__state + type: workspaceDataFilter + datasets: [] + dateInstances: [] + name: Demo West + parent: + id: demo + type: workspace + permissions: [] + settings: [] + userDataFilters: [] + - automations: [] + customApplicationSettings: [] + filterViews: [] + hierarchyPermissions: [] + id: demo_west_california + model: + analytics: + analyticalDashboardExtensions: [] + analyticalDashboards: [] + attributeHierarchies: [] + dashboardPlugins: [] + exportDefinitions: [] + filterContexts: [] + memoryItems: [] + metrics: [] + parameters: [] + visualizationObjects: [] + ldm: + datasets: [] + dateInstances: [] + name: Demo West California + parent: + id: demo_west + type: workspace + permissions: [] + settings: [] + userDataFilters: [] + headers: + Accept-Encoding: + - br, gzip, deflate + Content-Type: + - application/json + X-GDC-VALIDATE-RELATIONS: + - 'true' + X-Requested-With: + - XMLHttpRequest + method: PUT + uri: http://localhost:3000/api/v1/layout/organization + response: + body: + string: + detail: 'Invalid combination of auth properties. Only none, user + password, + token, key-pair or client id + secret are allowed. Datasource: demo-test-ds, + user: , password: , token: , key-pair: , clientId: , + clientSecret: ' + status: 400 + title: Bad Request + traceId: NORMALIZED_TRACE_ID_000000000000 + headers: + Content-Type: + - application/problem+json + DATE: *id001 + Expires: + - '0' + Pragma: + - no-cache + X-Content-Type-Options: + - nosniff + X-GDC-TRACE-ID: *id001 + status: + code: 400 + message: Bad Request +version: 1 diff --git a/packages/gooddata-sdk/tests/catalog/test_catalog_organization.py b/packages/gooddata-sdk/tests/catalog/test_catalog_organization.py index 53e88c566..26f524b78 100644 --- a/packages/gooddata-sdk/tests/catalog/test_catalog_organization.py +++ b/packages/gooddata-sdk/tests/catalog/test_catalog_organization.py @@ -6,6 +6,7 @@ from gooddata_api_client.exceptions import NotFoundException from gooddata_sdk import ( CatalogCspDirective, + CatalogDeclarativeIpAllowlistPolicy, CatalogDeclarativeNotificationChannel, CatalogJwk, CatalogOrganization, @@ -14,6 +15,7 @@ CatalogWebhook, GoodDataSdk, ) +from gooddata_sdk.catalog.identifier import CatalogDeclarativeUserGroupIdentifier, CatalogUserIdentifier from tests_support.vcrpy_utils import get_vcr from .conftest import safe_delete @@ -563,3 +565,36 @@ def test_layout_notification_channels(test_config, snapshot_notification_channel # sdk.catalog_organization.put_declarative_identity_providers([]) # idps = sdk.catalog_organization.get_declarative_identity_providers() # assert len(idps) == 0 + + +@gd_vcr.use_cassette(str(_fixtures_dir / "layout_ip_allowlist_policies.yaml")) +def test_layout_ip_allowlist_policies(test_config): + sdk = GoodDataSdk.create(host_=test_config["host"], token_=test_config["token"]) + + original_policies = sdk.catalog_organization.get_declarative_ip_allowlist_policies() + + new_policies = [ + CatalogDeclarativeIpAllowlistPolicy( + id="admin-vpn-only", + allowed_sources=["203.0.113.10/32", "198.51.100.0/24"], + user_groups=[ + CatalogDeclarativeUserGroupIdentifier(id="adminGroup", type="userGroup"), + ], + users=[ + CatalogUserIdentifier(id="demo", type="user"), + ], + ), + ] + + try: + sdk.catalog_organization.put_declarative_ip_allowlist_policies(new_policies) + result = sdk.catalog_organization.get_declarative_ip_allowlist_policies() + assert len(result) == 1 + assert result[0].id == "admin-vpn-only" + assert result[0].allowed_sources == ["203.0.113.10/32", "198.51.100.0/24"] + assert len(result[0].user_groups) == 1 + assert result[0].user_groups[0].id == "adminGroup" + assert len(result[0].users) == 1 + assert result[0].users[0].id == "demo" + finally: + sdk.catalog_organization.put_declarative_ip_allowlist_policies(original_policies) diff --git a/transcripts/fix-C009-attempt1.jsonl b/transcripts/fix-C009-attempt1.jsonl new file mode 100644 index 000000000..98415cb87 --- /dev/null +++ b/transcripts/fix-C009-attempt1.jsonl @@ -0,0 +1,125 @@ +{"ts": 1778657705.926109, "type": "system"} +{"ts": 1778657707.6267068, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "Let me start by understanding the failing test and the code involved to figure out what needs to be fixed.", "truncated": false}]} +{"ts": 1778657708.555351, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01V1xi1HNZ3ijhWzf8xz9TX7", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/test_catalog_organization.py\", \"offset\": 570, \"limit\": 60}", "truncated": false}]} +{"ts": 1778657708.966267, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01V1xi1HNZ3ijhWzf8xz9TX7", "is_error": false, "content": "570\t@gd_vcr.use_cassette(str(_fixtures_dir / \"layout_ip_allowlist_policies.yaml\"))\n571\tdef test_layout_ip_allowlist_policies(test_config):\n572\t sdk = GoodDataSdk.create(host_=test_config[\"host\"], token_=test_config[\"token\"])\n573\t\n574\t original_policies = sdk.catalog_organization.get_declarative_ip_allowlist_policies()\n575\t\n576\t new_policies = [\n577\t CatalogDeclarativeIpAllowlistPolicy(\n578\t id=\"admin-vpn-only\",\n579\t allowed_sources=[\"203.0.113.10/32\", \"198.51.100.0/24\"],\n580\t user_groups=[\n581\t CatalogDeclarativeUserGroupIdentifier(id=\"adminGroup\", type=\"userGroup\"),\n582\t ],\n583\t users=[\n584\t CatalogUserIdentifier(id=\"demo\", type=\"user\"),\n585\t ],\n586\t ),\n587\t ]\n588\t\n589\t try:\n590\t sdk.catalog_organization.put_declarative_ip_allowlist_policies(new_policies)\n591\t result = sdk.catalog_organization.get_declarative_ip_allowlist_policies()\n592\t assert len(result) == 1\n593\t assert result[0].id == \"admin-vpn-only\"\n594\t assert result[0].allowed_sources == [\"203.0.113.10/32\", \"198.51.100.0/24\"]\n595\t assert len(result[0].user_groups) == 1\n596\t assert result[0].user_groups[0].id == \"adminGroup\"\n597\t assert len(result[0].users) == 1\n598\t assert result[0].users[0].id == \"demo\"\n599\t finally:\n600\t sdk.catalog_organization.put_declarative_ip_allowlist_policies(original_policies)\n601\t", "truncated": false}]} +{"ts": 1778657708.966795, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Glob", "id": "toolu_014ybk7p2xDy5eXsMrKQe3fS", "input": "{\"pattern\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/**/*.py\"}", "truncated": false}]} +{"ts": 1778657708.9997265, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_014ybk7p2xDy5eXsMrKQe3fS", "is_error": false, "content": "packages/gooddata-sdk/src/gooddata_sdk/__init__.py\npackages/gooddata-sdk/src/gooddata_sdk/_version.py\npackages/gooddata-sdk/src/gooddata_sdk/catalog/__init__.py\npackages/gooddata-sdk/src/gooddata_sdk/catalog/ai_lake/__init__.py\npackages/gooddata-sdk/src/gooddata_sdk/catalog/ai_lake/service.py\npackages/gooddata-sdk/src/gooddata_sdk/catalog/appearance/__init__.py\npackages/gooddata-sdk/src/gooddata_sdk/catalog/appearance/entity_model/__init__.py\npackages/gooddata-sdk/src/gooddata_sdk/catalog/appearance/entity_model/color_palette.py\npackages/gooddata-sdk/src/gooddata_sdk/catalog/appearance/entity_model/theme.py\npackages/gooddata-sdk/src/gooddata_sdk/catalog/appearance/service.py\npackages/gooddata-sdk/src/gooddata_sdk/catalog/base.py\npackages/gooddata-sdk/src/gooddata_sdk/catalog/catalog_service_base.py\npackages/gooddata-sdk/src/gooddata_sdk/catalog/data_source/__init__.py\npackages/gooddata-sdk/src/gooddata_sdk/catalog/data_source/action_model/__init__.py\npackages/gooddata-sdk/src/gooddata_sdk/catalog/data_source/action_model/requests/__init__.py\npackages/gooddata-sdk/src/gooddata_sdk/catalog/data_source/action_model/requests/ldm_request.py\npackages/gooddata-sdk/src/gooddata_sdk/catalog/data_source/action_model/requests/scan_model_request.py\npackages/gooddata-sdk/src/gooddata_sdk/catalog/data_source/action_model/requests/scan_sql_request.py\npackages/gooddata-sdk/src/gooddata_sdk/catalog/data_source/action_model/responses/__init__.py\npackages/gooddata-sdk/src/gooddata_sdk/catalog/data_source/action_model/responses/scan_sql_response.py\npackages/gooddata-sdk/src/gooddata_sdk/catalog/data_source/action_model/sql_column.py\npackages/gooddata-sdk/src/gooddata_sdk/catalog/data_source/declarative_model/__init__.py\npackages/gooddata-sdk/src/gooddata_sdk/catalog/data_source/declarative_model/data_source.py\npackages/gooddata-sdk/src/gooddata_sdk/catalog/data_source/declarative_model/physical_model/__init__.py\npackages/gooddata-sdk/src/gooddata_sdk/catalog/data_source/declarative_mod", "truncated": true}]} +{"ts": 1778657714.121351, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "Let me look at the test more carefully and the service implementation to understand what's happening.", "truncated": false}]} +{"ts": 1778657715.0583978, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01UPHpLuFFtCwhpxD1qZTZ4G", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/src/gooddata_sdk/catalog/organization/service.py\", \"offset\": 750, \"limit\": 60}", "truncated": false}]} +{"ts": 1778657715.4865263, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01UPHpLuFFtCwhpxD1qZTZ4G", "is_error": false, "content": "750\t IP allowlist policies are returned as part of the full organization layout.\n751\t\n752\t Returns:\n753\t list[CatalogDeclarativeIpAllowlistPolicy]:\n754\t List of declarative IP allowlist policies.\n755\t \"\"\"\n756\t org_layout = self._layout_api.get_organization_layout(_check_return_type=False)\n757\t policies = getattr(org_layout, \"ip_allowlist_policies\", None) or []\n758\t return [CatalogDeclarativeIpAllowlistPolicy.from_api(policy) for policy in policies]\n759\t\n760\t def put_declarative_ip_allowlist_policies(\n761\t self, ip_allowlist_policies: list[CatalogDeclarativeIpAllowlistPolicy]\n762\t ) -> None:\n763\t \"\"\"Put declarative IP allowlist policies for the current organization.\n764\t\n765\t Reads the full organization layout, replaces the ip_allowlist_policies field,\n766\t and writes the full layout back. All other organization settings are preserved.\n767\t\n768\t Args:\n769\t ip_allowlist_policies (list[CatalogDeclarativeIpAllowlistPolicy]):\n770\t List of declarative IP allowlist policies to set.\n771\t\n772\t Returns:\n773\t None\n774\t \"\"\"\n775\t org_layout = self._layout_api.get_organization_layout(_check_return_type=False)\n776\t org_layout.ip_allowlist_policies = [policy.to_api() for policy in ip_allowlist_policies]\n777\t self._layout_api.set_organization_layout(org_layout, _check_return_type=False)\n778\t", "truncated": false}]} +{"ts": 1778657715.8553169, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01EE32v8poAvJ5HCNas8tcKb", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/src/gooddata_sdk/catalog/organization/layout/ip_allowlist_policy.py\"}", "truncated": false}]} +{"ts": 1778657715.9019704, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01EE32v8poAvJ5HCNas8tcKb", "is_error": false, "content": "1\t# (C) 2025 GoodData Corporation\n2\tfrom __future__ import annotations\n3\t\n4\timport builtins\n5\t\n6\tfrom attrs import define, field\n7\tfrom gooddata_api_client.model.declarative_ip_allowlist_policy import DeclarativeIpAllowlistPolicy\n8\t\n9\tfrom gooddata_sdk.catalog.base import Base\n10\tfrom gooddata_sdk.catalog.identifier import CatalogDeclarativeUserGroupIdentifier, CatalogUserIdentifier\n11\t\n12\t\n13\t@define(kw_only=True)\n14\tclass CatalogDeclarativeIpAllowlistPolicy(Base):\n15\t id: str\n16\t allowed_sources: list[str] = field(factory=list)\n17\t user_groups: list[CatalogDeclarativeUserGroupIdentifier] = field(factory=list)\n18\t users: list[CatalogUserIdentifier] = field(factory=list)\n19\t\n20\t @staticmethod\n21\t def client_class() -> builtins.type[DeclarativeIpAllowlistPolicy]:\n22\t return DeclarativeIpAllowlistPolicy\n23\t", "truncated": false}]} +{"ts": 1778657731.4571333, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "The error is:\n```\nHTTP response body: {\"detail\":\"Invalid combination of auth properties. Only none, user + password, token, key-pair or client id + secret are allowed. Datasource: demo-test-ds, user: , password: , token: , key-pair: , clientId: , clientSecret: \",\"status\":400,\"title\":\"Bad Request\",\"traceId\":\"63741292b6fb78b580ca7a5aec9de811\"}\n```\n\nThis is interesting - the error mentions \"Invalid combination of auth properties\" for a data source. This happens when the test tries to restore the original policies via `put_declarative_ip_allowlist_policies(original_policies)`.\n \nSo the issue is that when `put_declarative_ip_allowlist_policies` reads and writes back the full org layout, it's also writing the data source configurations. The demo-test-ds data source has an invalid auth setup - it has a username but no password, which violates the auth property rules. The `set_organization_layout` call is failing because it's trying to persist this invalid configuration. The root cause is that when the method retrieves the org layout, data source passwords are redacted for security, but the subsequent PUT request fails validation because it requires either complete credential pairs or nothing at all. I should check how other methods in the service handle this credential stripping problem.", "truncated": false}]} +{"ts": 1778657731.4607584, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01TwnSKrwXdsCWrRXqU9QqWJ", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/src/gooddata_sdk/catalog/organization/service.py\", \"offset\": 1, \"limit\": 100}", "truncated": false}]} +{"ts": 1778657731.4805062, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01TwnSKrwXdsCWrRXqU9QqWJ", "is_error": false, "content": "1\t# (C) 2022 GoodData Corporation\n2\tfrom __future__ import annotations\n3\t\n4\timport functools\n5\tfrom typing import Any, Literal\n6\t\n7\tfrom gooddata_api_client.exceptions import NotFoundException\n8\tfrom gooddata_api_client.model.declarative_export_templates import DeclarativeExportTemplates\n9\tfrom gooddata_api_client.model.declarative_notification_channels import DeclarativeNotificationChannels\n10\tfrom gooddata_api_client.model.json_api_csp_directive_in_document import JsonApiCspDirectiveInDocument\n11\tfrom gooddata_api_client.model.json_api_export_template_in_document import JsonApiExportTemplateInDocument\n12\tfrom gooddata_api_client.model.json_api_export_template_post_optional_id_document import (\n13\t JsonApiExportTemplatePostOptionalIdDocument,\n14\t)\n15\tfrom gooddata_api_client.model.json_api_identity_provider_in_document import JsonApiIdentityProviderInDocument\n16\tfrom gooddata_api_client.model.json_api_organization_patch import JsonApiOrganizationPatch\n17\tfrom gooddata_api_client.model.json_api_organization_patch_document import JsonApiOrganizationPatchDocument\n18\tfrom gooddata_api_client.model.json_api_organization_setting_in_document import JsonApiOrganizationSettingInDocument\n19\tfrom gooddata_api_client.model.switch_identity_provider_request import SwitchIdentityProviderRequest\n20\t\n21\tfrom gooddata_sdk import CatalogDeclarativeExportTemplate, CatalogExportTemplate\n22\tfrom gooddata_sdk.catalog.catalog_service_base import CatalogServiceBase\n23\tfrom gooddata_sdk.catalog.organization.entity_model.directive import CatalogCspDirective\n24\tfrom gooddata_sdk.catalog.organization.entity_model.identity_provider import CatalogIdentityProvider\n25\tfrom gooddata_sdk.catalog.organization.entity_model.jwk import CatalogJwk, CatalogJwkDocument\n26\tfrom gooddata_sdk.catalog.organization.entity_model.llm_provider import (\n27\t CatalogLlmProvider,\n28\t CatalogLlmProviderDocument,\n29\t CatalogLlmProviderPatch,\n30\t CatalogLlmProviderPatchDocument,\n31\t)\n32\tfrom gooddata_sdk.c", "truncated": true}]} +{"ts": 1778657732.1002498, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01JyocCsmp9bhepMFQH1EE76", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/test_catalog_organization.py\", \"offset\": 560, \"limit\": 15}", "truncated": false}]} +{"ts": 1778657732.129557, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01JyocCsmp9bhepMFQH1EE76", "is_error": false, "content": "560\t# assert identity_providers_o[0].identifiers == identity_providers_e[0].identifiers\n561\t# assert identity_providers_o[0].oauth_client_id == identity_providers_e[0].oauth_client_id\n562\t# assert identity_providers_o[0].oauth_client_secret is None\n563\t# assert identity_providers_o[0].oauth_issuer_location == identity_providers_e[0].oauth_issuer_location\n564\t# finally:\n565\t# sdk.catalog_organization.put_declarative_identity_providers([])\n566\t# idps = sdk.catalog_organization.get_declarative_identity_providers()\n567\t# assert len(idps) == 0\n568\t\n569\t\n570\t@gd_vcr.use_cassette(str(_fixtures_dir / \"layout_ip_allowlist_policies.yaml\"))\n571\tdef test_layout_ip_allowlist_policies(test_config):\n572\t sdk = GoodDataSdk.create(host_=test_config[\"host\"], token_=test_config[\"token\"])\n573\t\n574\t original_policies = sdk.catalog_organization.get_declarative_ip_allowlist_policies()", "truncated": false}]} +{"ts": 1778657754.5471907, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "The error is: \"Invalid combination of auth properties. Only none, user + password, token, key-pair or client id + secret are allowed. Datasource: demo-test-ds, user: , password: , token: , key-pair: , clientId: , clientSecret: \"\n\nThis happens when the test restores original_policies by calling `put_declarative_ip_allowlist_policies(original_policies)` which:\n1. GETs the org layout\n2. Replaces ip_allowlist_policies\n\n\n3. PUTs the entire org layout back\n\nThe problem is a classic read-back-write issue: when retrieving the org layout, data sources include credentials like usernames but passwords aren't returned, so PUTting it back fails validation since the username exists without a password. The real culprit might be that the VCR cassette for this test doesn't exist yet since stale cassettes were cleaned up, meaning it's hitting a live server instead of recorded responses. So the issue is that when we GET the organization layout, the data source passwords are stripped for security, but the usernames remain\u2014and when we PUT it back, the API validation fails because it expects either both credentials or neither. I need to either clear out the data source credentials entirely before the PUT, or find a way to preserve the original credentials. Let me check what the organization layout structure looks like and whether there's a cleaner approach than modifying the entire layout just to update IP allowlist policies. But the test is using the installed version.\n\nWait, but the installed version should match the source... Unless the package was installed before the fix was made.\n\nActually looking at line 777 in the installed service.py:\n```\nself._layout_api.set_organization_layout(org_layout, _check_return_type=False)\n```\n\nThis is the same code I see in the source at line 777. So the installed code matches.\n\nThe issue is that the org layout returned by `get_organization_layout` includes data sources with usernames but not passwords. When we PUT the org lay", "truncated": true}]} +{"ts": 1778657754.5714526, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01SFFmtmRgY8pzkatS4M8Gy1", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/src/gooddata_sdk/catalog/organization/service.py\", \"offset\": 730, \"limit\": 50}", "truncated": false}]} +{"ts": 1778657754.6293762, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01SFFmtmRgY8pzkatS4M8Gy1", "is_error": false, "content": "730\t None\n731\t\n732\t Raises:\n733\t ValueError:\n734\t Identity provider does not exist or operation failed.\n735\t \"\"\"\n736\t try:\n737\t request = SwitchIdentityProviderRequest(idp_id=identity_provider_id)\n738\t self._actions_api.switch_active_identity_provider(request, _check_return_type=False)\n739\t\n740\t except NotFoundException:\n741\t raise ValueError(\n742\t f\"Cannot switch to identity provider {identity_provider_id}. This identity provider does not exist.\"\n743\t )\n744\t except Exception as e:\n745\t raise ValueError(f\"Error switching active identity provider: {str(e)}\")\n746\t\n747\t def get_declarative_ip_allowlist_policies(self) -> list[CatalogDeclarativeIpAllowlistPolicy]:\n748\t \"\"\"Get all declarative IP allowlist policies for the current organization.\n749\t\n750\t IP allowlist policies are returned as part of the full organization layout.\n751\t\n752\t Returns:\n753\t list[CatalogDeclarativeIpAllowlistPolicy]:\n754\t List of declarative IP allowlist policies.\n755\t \"\"\"\n756\t org_layout = self._layout_api.get_organization_layout(_check_return_type=False)\n757\t policies = getattr(org_layout, \"ip_allowlist_policies\", None) or []\n758\t return [CatalogDeclarativeIpAllowlistPolicy.from_api(policy) for policy in policies]\n759\t\n760\t def put_declarative_ip_allowlist_policies(\n761\t self, ip_allowlist_policies: list[CatalogDeclarativeIpAllowlistPolicy]\n762\t ) -> None:\n763\t \"\"\"Put declarative IP allowlist policies for the current organization.\n764\t\n765\t Reads the full organization layout, replaces the ip_allowlist_policies field,\n766\t and writes the full layout back. All other organization settings are preserved.\n767\t\n768\t Args:\n769\t ip_allowlist_policies (list[CatalogDeclarativeIpAllowlistPolicy]):\n770\t List of declarative ", "truncated": true}]} +{"ts": 1778657754.6355796, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01Y1hSM51rq2eP3M6Zav2bXv", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/test_catalog_organization.py\", \"offset\": 1, \"limit\": 50}", "truncated": false}]} +{"ts": 1778657754.7064903, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01Y1hSM51rq2eP3M6Zav2bXv", "is_error": false, "content": "1\t# (C) 2022 GoodData Corporation\n2\tfrom __future__ import annotations\n3\t\n4\tfrom pathlib import Path\n5\t\n6\tfrom gooddata_api_client.exceptions import NotFoundException\n7\tfrom gooddata_sdk import (\n8\t CatalogCspDirective,\n9\t CatalogDeclarativeIpAllowlistPolicy,\n10\t CatalogDeclarativeNotificationChannel,\n11\t CatalogJwk,\n12\t CatalogOrganization,\n13\t CatalogOrganizationSetting,\n14\t CatalogRsaSpecification,\n15\t CatalogWebhook,\n16\t GoodDataSdk,\n17\t)\n18\tfrom gooddata_sdk.catalog.identifier import CatalogDeclarativeUserGroupIdentifier, CatalogUserIdentifier\n19\tfrom tests_support.vcrpy_utils import get_vcr\n20\t\n21\tfrom .conftest import safe_delete\n22\t\n23\tgd_vcr = get_vcr()\n24\t\n25\t_default_jwk_id = \"demoJwk\"\n26\t_current_dir = Path(__file__).parent.absolute()\n27\t_fixtures_dir = _current_dir / \"fixtures\" / \"organization\"\n28\t\n29\t\n30\tdef _default_organization_check(organization: CatalogOrganization, test_config: dict):\n31\t # Relaxed assertions \u2014 cassettes may come from local or staging environment,\n32\t # so we only verify the fields are populated, not their exact values.\n33\t assert organization.id\n34\t assert organization.attributes.name\n35\t assert organization.attributes.hostname\n36\t\n37\t\n38\tdef _default_jwk(jwk_id=_default_jwk_id, alg=None, kid=None):\n39\t rsa_specification = CatalogRsaSpecification(\n40\t alg=alg if alg else \"RS256\",\n41\t kid=kid if kid else \"Gkncy6f9BSp7wdav8w4SNZE1yt0\",\n42\t use=\"sig\",\n43\t kty=\"RSA\",\n44\t e=\"AQAB\",\n45\t n=\"sXiw5dOOl6DwAFKYQP623hUOs7T0f2mJEvMN1P-AkVbwagxanJzyRpJJX2zyXtIYnsTrlsjrSRzBTvWBZCXGW4XpmcSIKoRky_hBJGgK6shK\"\n46\t \"3375oThcAq01JZWEWXU2roYyBQz7VxvUSIAPmQVXn9TCQ7YT_TQOg39Dzot2PZPZtabKM2IQEtV6vVmciqz3QkMnclvGnUAca6KZCPBotdNX\"\n47\t \"9dWedAOBHmipCzzYIHutnMXsTZtPCEvrlgYyS1yD7u1WaxPgl84D6uglqDBF6WF1Vr2fgeeWfyujiJ_U26BCq6DmGQCN7BPAhJJso6bvP27G\"\n48\t \"58wZGR3HM_bjJ716Zw\",\n49\t x5c=[\n50\t \"MIIDbjCCAlagAwIBAgIUQ/BwWYGeDkDQjbUycWZqI/FeUDcwD", "truncated": true}]} +{"ts": 1778657756.5455644, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "Now let me look at the test fixtures for the organization to understand the cassette format, and also look at how the data source service handles this:", "truncated": false}]} +{"ts": 1778657757.4010956, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Glob", "id": "toolu_01AieC1B49iYUCLC1ZzvdEW1", "input": "{\"pattern\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/fixtures/organization/*.yaml\"}", "truncated": false}]} +{"ts": 1778657757.8486648, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01AieC1B49iYUCLC1ZzvdEW1", "is_error": false, "content": "packages/gooddata-sdk/tests/catalog/fixtures/organization/create_csp_directive.yaml\npackages/gooddata-sdk/tests/catalog/fixtures/organization/create_jwk.yaml\npackages/gooddata-sdk/tests/catalog/fixtures/organization/create_organization_setting.yaml\npackages/gooddata-sdk/tests/catalog/fixtures/organization/delete_csp_directive.yaml\npackages/gooddata-sdk/tests/catalog/fixtures/organization/delete_jwk.yaml\npackages/gooddata-sdk/tests/catalog/fixtures/organization/delete_organization_setting.yaml\npackages/gooddata-sdk/tests/catalog/fixtures/organization/layout_notification_channels.yaml\npackages/gooddata-sdk/tests/catalog/fixtures/organization/list_csp_directives.yaml\npackages/gooddata-sdk/tests/catalog/fixtures/organization/list_jwk.yaml\npackages/gooddata-sdk/tests/catalog/fixtures/organization/list_organization_settings.yaml\npackages/gooddata-sdk/tests/catalog/fixtures/organization/organization.yaml\npackages/gooddata-sdk/tests/catalog/fixtures/organization/update_allowed_origins.yaml\npackages/gooddata-sdk/tests/catalog/fixtures/organization/update_csp_directive.yaml\npackages/gooddata-sdk/tests/catalog/fixtures/organization/update_jwk.yaml\npackages/gooddata-sdk/tests/catalog/fixtures/organization/update_name.yaml\npackages/gooddata-sdk/tests/catalog/fixtures/organization/update_organization_setting.yaml\npackages/gooddata-sdk/tests/catalog/fixtures/organization/layout_ip_allowlist_policies.yaml", "truncated": false}]} +{"ts": 1778657758.4498749, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01R7st7Eu2qGWngZ9sixHbrU", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/src/gooddata_sdk/catalog/organization/service.py\", \"offset\": 400, \"limit\": 100}", "truncated": false}]} +{"ts": 1778657758.5041695, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01R7st7Eu2qGWngZ9sixHbrU", "is_error": false, "content": "400\t def create_identity_provider(self, identity_provider: CatalogIdentityProvider) -> None:\n401\t \"\"\"Create a new identity provider.\n402\t\n403\t Args:\n404\t identity_provider (CatalogIdentityProvider):\n405\t A catalog identity provider object to be created.\n406\t\n407\t Returns:\n408\t None\n409\t \"\"\"\n410\t identity_provider_document = JsonApiIdentityProviderInDocument(data=identity_provider.to_api())\n411\t self._entities_api.create_entity_identity_providers(\n412\t json_api_identity_provider_in_document=identity_provider_document\n413\t )\n414\t\n415\t def delete_identity_provider(self, identity_provider_id: str) -> None:\n416\t \"\"\"Delete an identity provider.\n417\t\n418\t Args:\n419\t identity_provider_id (str):\n420\t Identity provider identification string e.g. \"demo\"\n421\t\n422\t Returns:\n423\t None\n424\t \"\"\"\n425\t self._entities_api.delete_entity_identity_providers(identity_provider_id)\n426\t\n427\t def update_identity_provider(self, identity_provider: CatalogIdentityProvider) -> None:\n428\t \"\"\"Update an identity provider.\n429\t\n430\t Args:\n431\t identity_provider (CatalogIdentityProvider):\n432\t A catalog identity provider object to be updated.\n433\t\n434\t Returns:\n435\t None\n436\t\n437\t Raises:\n438\t ValueError:\n439\t Identity provider does not exist.\n440\t \"\"\"\n441\t try:\n442\t identity_provider_document = JsonApiIdentityProviderInDocument(data=identity_provider.to_api())\n443\t self._entities_api.update_entity_identity_providers(identity_provider.id, identity_provider_document)\n444\t except NotFoundException:\n445\t raise ValueError(\n446\t f\"Can not update {identity_provider.id} identity provider. This identity provider does not exist.\"\n447\t )\n448\t\n449\t def patch_identit", "truncated": true}]} +{"ts": 1778657760.7370214, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "Let me look at the existing cassette for the test to understand what it captures:", "truncated": false}]} +{"ts": 1778657761.5203195, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01JGyR8XddbXA6jW1meVQv7D", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/fixtures/organization/layout_ip_allowlist_policies.yaml\"}", "truncated": false}]} +{"ts": 1778657761.5555537, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01JGyR8XddbXA6jW1meVQv7D", "is_error": true, "content": "File content (463.7KB) exceeds maximum allowed size (256KB). Use offset and limit parameters to read specific portions of the file, or search for specific content instead of reading the whole file.", "truncated": false}]} +{"ts": 1778657763.3975809, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01CNVLK1xQvZE3v3fReZ3AMX", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/fixtures/organization/layout_ip_allowlist_policies.yaml\", \"offset\": 1, \"limit\": 100}", "truncated": false}]} +{"ts": 1778657763.429623, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01CNVLK1xQvZE3v3fReZ3AMX", "is_error": false, "content": "1\tinteractions:\n2\t - request:\n3\t body: null\n4\t headers:\n5\t Accept:\n6\t - application/json\n7\t Accept-Encoding:\n8\t - br, gzip, deflate\n9\t X-GDC-VALIDATE-RELATIONS:\n10\t - 'true'\n11\t X-Requested-With:\n12\t - XMLHttpRequest\n13\t method: GET\n14\t uri: http://localhost:3000/api/v1/layout/organization\n15\t response:\n16\t body:\n17\t string:\n18\t agents:\n19\t - aiKnowledge: true\n20\t availableToAll: true\n21\t enabled: true\n22\t id: default-ai-assistant\n23\t name: Default GoodData AI Assistant\n24\t skillsMode: all\n25\t customGeoCollections: []\n26\t dataSources:\n27\t - alternativeDataSourceId: ds-put-abc-id\n28\t authenticationType: USERNAME_PASSWORD\n29\t id: demo-test-ds\n30\t name: demo-test-ds\n31\t permissions:\n32\t - assignee:\n33\t id: demo2\n34\t type: user\n35\t name: MANAGE\n36\t - assignee:\n37\t id: demoGroup\n38\t type: userGroup\n39\t name: USE\n40\t schema: demo\n41\t type: POSTGRESQL\n42\t url: jdbc:postgresql://postgres:5432/tiger?sslmode=prefer\n43\t username: postgres\n44\t exportTemplates: []\n45\t identityProviders:\n46\t - id: defaultIdp\n47\t idpType: DEX_IDP\n48\t ipAllowlistPolicies: []\n49\t jwks: []\n50\t notificationChannels: []\n51\t organization:\n52\t allowedOrigins: []\n53\t colorPalettes: []\n54\t cspDirectives: []\n55\t earlyAccess: enableAlerting\n56\t earlyAccessValues:\n57\t - enableAlerting\n58\t - enableSmtp\n59\t - enablePreAggregationDatasets\n60\t - enableScheduling\n61\t - e", "truncated": true}]} +{"ts": 1778657766.6971674, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "I can see the cassette has a data source with:\n- `authenticationType: USERNAME_PASSWORD`\n- `username: postgres`\n\nBut no `password` field. This is the problem. When we GET the org layout and then PUT it back, the server validates the data source and finds that it has a username but no password.\n\nLet me look at more of the cassette to understand the full structure and then look at the PUT request:", "truncated": false}]} +{"ts": 1778657768.3152275, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Bash", "id": "toolu_01XTyyjCEZaczvzx4xiRpeeV", "input": "{\"command\": \"grep -n \\\"PUT\\\\|post\\\\|password\\\\|authenticationType\\\\|username\\\\|demo-test-ds\\\" /home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/fixtures/organization/layout_ip_allowlist_policies.yaml | head -80\", \"description\": \"Find PUT requests and credential fields in the cassette\"}", "truncated": false}]} +{"ts": 1778657768.3755836, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01XTyyjCEZaczvzx4xiRpeeV", "is_error": true, "content": "Blocked: grep -n \"PUT\\|post\\|password\\|authenticationType\\|username\\|demo-test-ds\" /home/", "truncated": false}]} +{"ts": 1778657771.013694, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Grep", "id": "toolu_015198Zi93H73HoDSGNvb54P", "input": "{\"pattern\": \"PUT|password|authenticationType|username|demo-test-ds\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/fixtures/organization/layout_ip_allowlist_policies.yaml\", \"output_mode\": \"content\", \"head_limit\": 80}", "truncated": false}]} +{"ts": 1778657771.094231, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_015198Zi93H73HoDSGNvb54P", "is_error": false, "content": "28: authenticationType: USERNAME_PASSWORD\n29: id: demo-test-ds\n30: name: demo-test-ds\n43: username: postgres\n1694: dataSourceId: demo-test-ds\n1768: dataSourceId: demo-test-ds\n1795: dataSourceId: demo-test-ds\n1859: dataSourceId: demo-test-ds\n1905: dataSourceId: demo-test-ds\n2022: dataSourceId: demo-test-ds\n2173: authenticationType: USERNAME_PASSWORD\n2174: id: demo-test-ds\n2175: name: demo-test-ds\n2188: username: postgres\n3839: dataSourceId: demo-test-ds\n3913: dataSourceId: demo-test-ds\n3940: dataSourceId: demo-test-ds\n4004: dataSourceId: demo-test-ds\n4050: dataSourceId: demo-test-ds\n4167: dataSourceId: demo-test-ds\n4303: authenticationType: USERNAME_PASSWORD\n4304: id: demo-test-ds\n4305: name: demo-test-ds\n4318: username: postgres\n5978: dataSourceId: demo-test-ds\n6052: dataSourceId: demo-test-ds\n6079: dataSourceId: demo-test-ds\n6143: dataSourceId: demo-test-ds\n6189: dataSourceId: demo-test-ds\n6306: dataSourceId: demo-test-ds\n6425: method: PUT\n6430: detail: 'Invalid combination of auth properties. Only none, user + password,\n6431: token, key-pair or client id + secret are allowed. Datasource: demo-test-ds,\n6432: user: , password: , token: , key-pair: , clientId: ,\n6477: authenticationType: USERNAME_PASSWORD\n6478: id: demo-test-ds\n6479: name: demo-test-ds\n6492: username: postgres\n8143: dataSourceId: d", "truncated": true}]} +{"ts": 1778657791.7224348, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "The cassette has recorded PUT requests that result in 400 errors. The cassette was recorded with these failures - the test was expected to handle the 400 error, but it doesn't.\n\nLooking at line 6425: `method: PUT` with line 6430 showing the 400 error detail.\n\nSo the cassette itself contains the 400 error response. This means the test was run against a real server which returned a 400 error, and the cassette recorded it. When the test runs again with the cassette, it will replay the 400 error.\n\nThe problem is in the `put_declarative_ip_allowlist_policies` method - it does a GET of the org layout, then PUTs it back, but the data source in the org layout has `username: postgres` but no password. The server validates this and returns 400.\n\n\nThe fix would be to strip credential fields from data sources before PUTting the org layout back, or to clear the data source credentials entirely rather than sending partial credentials with an auth type that requires them.\n\nBut I'm realizing the cassette file already exists and contains the 400 error baked in. The instructions say not to fabricate cassettes since they'll be re-recorded by the pipeline, but this cassette wasn't deleted and has the failure recorded. So either I need to fix the service code to prevent the 400 error from happening in the first place, or the cassette needs to be updated to remove it. If the cassette replays the 400 error, the test will fail when the code tries to handle that response. The cassette has two PUT requests that both return 400 errors \u2014 one for the initial policy update and one for the restoration in the finally block. Looking at the test flow, it seems the cassette was recorded when the server was already rejecting these PUT requests, which is why the test is failing now.\n\nI need to examine what's actually in the PUT request body around line 6425 to understand what's causing the 400 error.", "truncated": false}]} +{"ts": 1778657791.7420647, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_019zPt6KseUAiQt7Xy4Vnigc", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/fixtures/organization/layout_ip_allowlist_policies.yaml\", \"offset\": 6415, \"limit\": 70}", "truncated": false}]} +{"ts": 1778657791.7590632, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_019zPt6KseUAiQt7Xy4Vnigc", "is_error": false, "content": "6415\t userDataFilters: []\n6416\t headers:\n6417\t Accept-Encoding:\n6418\t - br, gzip, deflate\n6419\t Content-Type:\n6420\t - application/json\n6421\t X-GDC-VALIDATE-RELATIONS:\n6422\t - 'true'\n6423\t X-Requested-With:\n6424\t - XMLHttpRequest\n6425\t method: PUT\n6426\t uri: http://localhost:3000/api/v1/layout/organization\n6427\t response:\n6428\t body:\n6429\t string:\n6430\t detail: 'Invalid combination of auth properties. Only none, user + password,\n6431\t token, key-pair or client id + secret are allowed. Datasource: demo-test-ds,\n6432\t user: , password: , token: , key-pair: , clientId: ,\n6433\t clientSecret: '\n6434\t status: 400\n6435\t title: Bad Request\n6436\t traceId: NORMALIZED_TRACE_ID_000000000000\n6437\t headers:\n6438\t Content-Type:\n6439\t - application/problem+json\n6440\t DATE: *id001\n6441\t Expires:\n6442\t - '0'\n6443\t Pragma:\n6444\t - no-cache\n6445\t X-Content-Type-Options:\n6446\t - nosniff\n6447\t X-GDC-TRACE-ID: *id001\n6448\t status:\n6449\t code: 400\n6450\t message: Bad Request\n6451\t - request:\n6452\t body: null\n6453\t headers:\n6454\t Accept:\n6455\t - application/json\n6456\t Accept-Encoding:\n6457\t - br, gzip, deflate\n6458\t X-GDC-VALIDATE-RELATIONS:\n6459\t - 'true'\n6460\t X-Requested-With:\n6461\t - XMLHttpRequest\n6462\t method: GET\n6463\t uri: http://localhost:3000/api/v1/layout/organization\n6464\t response:\n6465\t body:\n6466\t string:\n6467\t agents:\n6468\t - aiKnowledge: true\n6469\t availableToAll: true\n6470\t enabled: true\n6471\t id: default-ai-assistant\n6472\t name: Default GoodData AI Assistant\n6473\t skillsMode: all\n6474\t cu", "truncated": true}]} +{"ts": 1778657840.2344227, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "So the cassette captures:\n1. First GET of org layout \u2192 returned with data source that has `username: postgres` but no password\n2. First PUT (for new_policies) \u2192 400 error (because the data source has username but no password)\n3. Second GET (part of the teardown's `put_declarative_ip_allowlist_policies`)\n4. Second PUT (in finally block) \u2192 Also 400 error\n\nThe cassette has the 400 error BAKED IN. This is the problem - the cassette was recorded when the service had this bug.\n\nThe fix for the service would be to strip partial credentials from data sources before PUTting the org layout back. \n\nBut here's the catch: if we fix the service to not send the `username` field, the PUT request body changes, so the cassette won't match it anymore. We'd either get a mismatch error or VCR would use the next unmatched request depending on configuration. The real solution is to delete the cassette and re-record it with the fixed service.\n\nThe instructions say stale cassettes were already deleted, but this cassette still exists at the layout_ip_allowlist_policies.yaml path with the 400 error recorded in it. That seems contradictory\u2014either the cassette should have been deleted, or maybe it's supposed to be there for a reason. Let me reconsider what's actually happening here.\n\nThe cassette exists and contains the 400 error response, so when the test runs against it via VCR, it replays that failure and the test fails. The real issue is that the service code reads the full org layout and writes it back without the password field, which causes the 400 error. So I need to both fix the service code and delete the bad cassette so the pipeline can re-record it. But if the underlying data source in the org layout is missing the password, the real server might return the same 400 error when re-recording happens.\n\nThe root cause is that the data source has `authenticationType: USERNAME_PASSWORD` with a username but no password, which the server rejects as invalid. The fix is to handle this in the ", "truncated": true}]} +{"ts": 1778657840.277337, "type": "assistant", "blocks": [{"type": "text", "text": "Let me look at how the notification channels method handles this and also check what's in the cassette around the PUT request body:", "truncated": false}]} +{"ts": 1778657840.5905392, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01WThxBaVkZSkmpvVcJwv3Uh", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/fixtures/organization/layout_ip_allowlist_policies.yaml\", \"offset\": 6200, \"limit\": 100}", "truncated": false}]} +{"ts": 1778657841.0213256, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01WThxBaVkZSkmpvVcJwv3Uh", "is_error": false, "content": "6200\t sourceColumn: price\n6201\t sourceColumnDataType: NUMERIC\n6202\t tags:\n6203\t - Order lines\n6204\t title: Price\n6205\t - description: Quantity\n6206\t id: quantity\n6207\t isNullable: true\n6208\t sourceColumn: quantity\n6209\t sourceColumnDataType: NUMERIC\n6210\t tags:\n6211\t - Order lines\n6212\t title: Quantity\n6213\t grain:\n6214\t - id: order_line_id\n6215\t type: attribute\n6216\t id: order_lines\n6217\t references:\n6218\t - identifier:\n6219\t id: campaigns\n6220\t type: dataset\n6221\t multivalue: false\n6222\t sources:\n6223\t - column: campaign_id\n6224\t dataType: INT\n6225\t isNullable: true\n6226\t target:\n6227\t id: campaign_id\n6228\t type: attribute\n6229\t - identifier:\n6230\t id: customers\n6231\t type: dataset\n6232\t multivalue: false\n6233\t sources:\n6234\t - column: customer_id\n6235\t dataType: INT\n6236\t isNullable: true\n6237\t target:\n6238\t id: customer_id\n6239\t type: attribute\n6240\t - identifier:\n6241\t id: date\n6242\t type: dataset\n6243\t multivalue: false\n62", "truncated": true}]} +{"ts": 1778657841.3705266, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Grep", "id": "toolu_0127DK74yLG5UZuiaTbrdMCp", "input": "{\"pattern\": \"def put_declarative_notification_channels\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/src/gooddata_sdk/catalog/organization/service.py\", \"output_mode\": \"content\"}", "truncated": false}]} +{"ts": 1778657841.410275, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_0127DK74yLG5UZuiaTbrdMCp", "is_error": false, "content": "647: def put_declarative_notification_channels(", "truncated": false}]} +{"ts": 1778657843.7097712, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01W6h5szKFVWjGRbozToZ43L", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/src/gooddata_sdk/catalog/organization/service.py\", \"offset\": 647, \"limit\": 80}", "truncated": false}]} +{"ts": 1778657843.7434337, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01W6h5szKFVWjGRbozToZ43L", "is_error": false, "content": "647\t def put_declarative_notification_channels(\n648\t self, notification_channels: list[CatalogDeclarativeNotificationChannel]\n649\t ) -> None:\n650\t \"\"\"\n651\t Put declarative notification channels in the current organization.\n652\t\n653\t Args:\n654\t notification_channels (list[CatalogDeclarativeNotificationChannel]):\n655\t List of declarative notification channels.\n656\t\n657\t Returns:\n658\t None\n659\t \"\"\"\n660\t api_ncs = [nc.to_api() for nc in notification_channels]\n661\t self._layout_api.set_notification_channels(DeclarativeNotificationChannels(notification_channels=api_ncs))\n662\t\n663\t def get_declarative_identity_providers(self) -> list[CatalogDeclarativeIdentityProvider]:\n664\t \"\"\"\n665\t Get all declarative identity providers in the current organization.\n666\t\n667\t Returns:\n668\t list[CatalogDeclarativeIdentityProvider]:\n669\t List of declarative identity providers.\n670\t \"\"\"\n671\t return [\n672\t CatalogDeclarativeIdentityProvider.from_api(idp) for idp in self._layout_api.get_identity_providers_layout()\n673\t ]\n674\t\n675\t def put_declarative_identity_providers(self, identity_providers: list[CatalogDeclarativeIdentityProvider]) -> None:\n676\t \"\"\"\n677\t Put declarative identity providers in the current organization.\n678\t\n679\t Args:\n680\t identity_providers (list[CatalogDeclarativeIdentityProvider]):\n681\t List of declarative identity providers.\n682\t\n683\t Returns:\n684\t None\n685\t \"\"\"\n686\t api_idps = [idp.to_api() for idp in identity_providers]\n687\t self._layout_api.set_identity_providers(declarative_identity_provider=api_idps)\n688\t\n689\t def get_declarative_export_templates(self) -> list[CatalogDeclarativeExportTemplate]:\n690\t \"\"\"\n691\t Get all declarative export templates in the current organization.\n692\t\n693\t ", "truncated": true}]} +{"ts": 1778657850.3061254, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "Now I see the pattern! The `put_declarative_notification_channels` and `put_declarative_identity_providers` methods use **dedicated APIs**:\n- `self._layout_api.set_notification_channels(...)` for notification channels\n- `self._layout_api.set_identity_providers(...)` for identity providers\n- `self._layout_api.set_export_templates(...)` for export templates\n\nBut the `put_declarative_ip_allowlist_policies` method uses the full org layout:\n\n\nFetching the entire organization layout, updating just the IP allowlist policies field, then writing it back. This is the inconsistency\u2014there's no dedicated API for IP allowlist policies, so it's falling back to the full layout approach. Let me check if a dedicated API exists for this.", "truncated": false}]} +{"ts": 1778657850.3066306, "type": "assistant", "blocks": [{"type": "text", "text": "Let me check the layout API for dedicated IP allowlist endpoints:", "truncated": false}]} +{"ts": 1778657850.734343, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Grep", "id": "toolu_01Jwc2TJC7d4wZd17ohZX6He", "input": "{\"pattern\": \"ip_allowlist\\\\|IpAllowlist\\\\|allowlist\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-api-client/gooddata_api_client/api/layout_api.py\", \"output_mode\": \"content\", \"head_limit\": 50}", "truncated": false}]} +{"ts": 1778657850.7744575, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01Jwc2TJC7d4wZd17ohZX6He", "is_error": true, "content": "Path does not exist: /home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-api-client/gooddata_api_client/api/layout_api.py. Note: your current working directory is /home/runner/_work/gdc-nas/gdc-nas/sdk.", "truncated": false}]} +{"ts": 1778657852.6123154, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Bash", "id": "toolu_011KzeT2X88itJaj8VhU4xAA", "input": "{\"command\": \"find /home/runner/_work/gdc-nas/gdc-nas/sdk -name \\\"layout_api.py\\\" 2>/dev/null | head -5\", \"description\": \"Find layout_api.py location\"}", "truncated": false}]} +{"ts": 1778657852.6764345, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_011KzeT2X88itJaj8VhU4xAA", "is_error": true, "content": "Blocked: find /home/runner/_work/gdc-nas/gdc-nas/sdk -name \"layout_api.py\" 2>/dev/null | ", "truncated": false}]} +{"ts": 1778657854.0668855, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Glob", "id": "toolu_016eCbFrheFRKLftVMQX1HJ7", "input": "{\"pattern\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/**/layout_api.py\"}", "truncated": false}]} +{"ts": 1778657854.098902, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_016eCbFrheFRKLftVMQX1HJ7", "is_error": false, "content": "gooddata-api-client/build/lib/gooddata_api_client/api/layout_api.py\ngooddata-api-client/gooddata_api_client/api/layout_api.py\npackages/gooddata-sdk/.tox/py312/lib/python3.12/site-packages/gooddata_api_client/api/layout_api.py", "truncated": false}]} +{"ts": 1778657856.6146915, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Grep", "id": "toolu_015H9WSanwy3zcS914hCGJNc", "input": "{\"pattern\": \"ip_allowlist\\\\|IpAllowlist\\\\|allowlist\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/gooddata-api-client/gooddata_api_client/api/layout_api.py\", \"output_mode\": \"content\", \"head_limit\": 50}", "truncated": false}]} +{"ts": 1778657856.9475136, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_015H9WSanwy3zcS914hCGJNc", "is_error": false, "content": "No matches found", "truncated": false}]} +{"ts": 1778657878.7492108, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "There's no dedicated IP allowlist API in the layout_api. So the prior agent's approach of using the full org layout is the only option.\n\nThe problem is that the `put_declarative_ip_allowlist_policies` method reads the full org layout and PUTs it back. The full org layout includes data sources with partial credentials, which causes a 400 error.\n\nThe fix needs to handle this. Let me think about options:\n\n1. **Strip data source credentials before PUT**: When the data source has a username but no password (or partial credentials), clear those credentials before PUTting.\n\n\n2. **Handle the validation issue differently**: The error shows that data sources with a username but no password violate the allowed auth combinations. The valid options are none, user + password, token, key-pair, or clientId + clientSecret. So when I encounter a data source with incomplete credentials like just a username, I need to either remove all credentials or ensure they're in a valid combination.\n\nBut clearing the username would lose data when the org layout is written back. The real issue is that the test reads the entire org layout, modifies it, and writes it back\u2014but passwords aren't returned in GET responses, so credentials can't be fully round-tripped. This is a fundamental design problem with using the full org layout for IP allowlist policies, whereas notification channels and identity providers have dedicated APIs that avoid this. The cleanest solution is to handle this in the declarative IP allowlist function itself.\n\nImplementing a helper method to strip partial data source credentials before the PUT request... I need to verify the exact attribute names the API client uses\u2014the JSON response shows camelCase keys like `authenticationType` and `username`, but the Python client likely converts these to snake_case attributes like `authentication_type`. Let me check the data source model to confirm the field mappings.", "truncated": false}]} +{"ts": 1778657878.788093, "type": "assistant", "blocks": [{"type": "text", "text": "Let me look at the data source API model to understand the field names:", "truncated": false}]} +{"ts": 1778657878.8533247, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Glob", "id": "toolu_01Bn823Kj6BMNt9p47fDsuF5", "input": "{\"pattern\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/gooddata-api-client/gooddata_api_client/model/declarative_data_source*.py\"}", "truncated": false}]} +{"ts": 1778657878.9086435, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01Bn823Kj6BMNt9p47fDsuF5", "is_error": false, "content": "gooddata-api-client/gooddata_api_client/model/declarative_data_source.py\ngooddata-api-client/gooddata_api_client/model/declarative_data_source_permission.py\ngooddata-api-client/gooddata_api_client/model/declarative_data_source_permissions.py\ngooddata-api-client/gooddata_api_client/model/declarative_data_sources.py", "truncated": false}]} +{"ts": 1778657880.7297103, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01Jetxi4ZzELPftjvoUafFH7", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/gooddata-api-client/gooddata_api_client/model/declarative_data_source.py\", \"offset\": 1, \"limit\": 100}", "truncated": false}]} +{"ts": 1778657880.7662528, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01Jetxi4ZzELPftjvoUafFH7", "is_error": false, "content": "1\t\"\"\"\n2\t OpenAPI definition\n3\t\n4\t No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) # noqa: E501\n5\t\n6\t The version of the OpenAPI document: v0\n7\t Contact: support@gooddata.com\n8\t Generated by: https://openapi-generator.tech\n9\t\"\"\"\n10\t\n11\t\n12\timport re # noqa: F401\n13\timport sys # noqa: F401\n14\t\n15\tfrom gooddata_api_client.model_utils import ( # noqa: F401\n16\t ApiTypeError,\n17\t ModelComposed,\n18\t ModelNormal,\n19\t ModelSimple,\n20\t cached_property,\n21\t change_keys_js_to_python,\n22\t convert_js_args_to_python_args,\n23\t date,\n24\t datetime,\n25\t file_type,\n26\t none_type,\n27\t validate_get_composed_info,\n28\t OpenApiModel\n29\t)\n30\tfrom gooddata_api_client.exceptions import ApiAttributeError\n31\t\n32\t\n33\tdef lazy_import():\n34\t from gooddata_api_client.model.declarative_data_source_permission import DeclarativeDataSourcePermission\n35\t from gooddata_api_client.model.parameter import Parameter\n36\t globals()['DeclarativeDataSourcePermission'] = DeclarativeDataSourcePermission\n37\t globals()['Parameter'] = Parameter\n38\t\n39\t\n40\tclass DeclarativeDataSource(ModelNormal):\n41\t \"\"\"NOTE: This class is auto generated by OpenAPI Generator.\n42\t Ref: https://openapi-generator.tech\n43\t\n44\t Do not edit the class manually.\n45\t\n46\t Attributes:\n47\t allowed_values (dict): The key is the tuple path to the attribute\n48\t and the for var_name this is (var_name,). The value is a dict\n49\t with a capitalized key describing the allowed value and an allowed\n50\t value. These dicts store the allowed enum values.\n51\t attribute_map (dict): The key is attribute name\n52\t and the value is json key in definition.\n53\t discriminator_value_class_map (dict): A dict to go from the discriminator\n54\t variable value to the discriminator class name.\n55\t validations (dict): The key is the tuple path to the attribute\n56\t a", "truncated": true}]} +{"ts": 1778657883.180254, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_014HKWiMqoqtYLmUSgocvxzZ", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/gooddata-api-client/gooddata_api_client/model/declarative_data_source.py\", \"offset\": 100, \"limit\": 100}", "truncated": false}]} +{"ts": 1778657883.2163785, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_014HKWiMqoqtYLmUSgocvxzZ", "is_error": false, "content": "100\t 'CLIENT_SECRET': \"CLIENT_SECRET\",\n101\t 'ACCESS_TOKEN': \"ACCESS_TOKEN\",\n102\t },\n103\t ('cache_strategy',): {\n104\t 'ALWAYS': \"ALWAYS\",\n105\t 'NEVER': \"NEVER\",\n106\t },\n107\t ('date_time_semantics',): {\n108\t 'None': None,\n109\t 'LOCAL': \"LOCAL\",\n110\t 'UTC': \"UTC\",\n111\t },\n112\t }\n113\t\n114\t validations = {\n115\t ('id',): {\n116\t 'regex': {\n117\t 'pattern': r'^(?!\\.)[.A-Za-z0-9_-]{1,255}$', # noqa: E501\n118\t },\n119\t },\n120\t ('name',): {\n121\t 'max_length': 255,\n122\t },\n123\t ('schema',): {\n124\t 'max_length': 255,\n125\t },\n126\t ('alternative_data_source_id',): {\n127\t 'regex': {\n128\t 'pattern': r'^(?!\\.)[.A-Za-z0-9_-]{1,255}$', # noqa: E501\n129\t },\n130\t },\n131\t ('client_id',): {\n132\t 'max_length': 255,\n133\t },\n134\t ('client_secret',): {\n135\t 'max_length': 255,\n136\t },\n137\t ('password',): {\n138\t 'max_length': 255,\n139\t },\n140\t ('private_key',): {\n141\t 'max_length': 15000,\n142\t },\n143\t ('private_key_passphrase',): {\n144\t 'max_length': 255,\n145\t },\n146\t ('token',): {\n147\t 'max_length': 10000,\n148\t },\n149\t ('url',): {\n150\t 'max_length': 255,\n151\t },\n152\t ('username',): {\n153\t 'max_length': 255,\n154\t },\n155\t }\n156\t\n157\t @cached_property\n158\t def additional_properties_type():\n159\t \"\"\"\n160\t This must be a method because a model may have properties that are\n161\t of type self, this must run after the class is loaded\n162\t \"\"\"\n163\t lazy_import()\n164\t return (bool, date, datetime, dict, float, int, list, str, none_type,) # noqa: E501\n165\t\n166\t _nullable = False\n167\t\n168\t ", "truncated": true}]} +{"ts": 1778657900.0442698, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "Now I understand the data source model structure. The data source has:\n- `authentication_type` (optional)\n- `username` (optional)\n- `password` (optional)\n- `token` (optional)\n- etc.\n\nWhen the GET returns a data source with `authentication_type: USERNAME_PASSWORD` and `username: postgres` but no `password`, putting this back will fail because the server requires username+password for `USERNAME_PASSWORD` auth.\n\nNow, the fix approach:\n1. In `put_declarative_ip_allowlist_policies`, before PUTting the org layout, clear the `username` from any data source that has `authentication_type: USERNAME_PASSWORD` but no `password`.\n\n\n2. The real issue is that the cassette is replaying the 400 error from the failed request, so I need to delete it and let it get re-recorded with the corrected data. The instructions claim the cassette was already deleted, but it still exists with bad data in it. I need to figure out whether the cassette should have been deleted or if there's something else going on\u2014let me look more closely at what the second PUT request in the cassette is actually doing.", "truncated": false}]} +{"ts": 1778657900.0537574, "type": "assistant", "blocks": [{"type": "text", "text": "Let me look at the second PUT in the cassette:", "truncated": false}]} +{"ts": 1778657900.4519372, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01GdAqgz7pjLMDi6VFq7nkNY", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/fixtures/organization/layout_ip_allowlist_policies.yaml\", \"offset\": 10700, \"limit\": 50}", "truncated": false}]} +{"ts": 1778657900.4897232, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01GdAqgz7pjLMDi6VFq7nkNY", "is_error": false, "content": "10700\t ldm:\n10701\t datasets: []\n10702\t dateInstances: []\n10703\t name: Demo West California\n10704\t parent:\n10705\t id: demo_west\n10706\t type: workspace\n10707\t permissions: []\n10708\t settings: []\n10709\t userDataFilters: []\n10710\t headers:\n10711\t Accept-Encoding:\n10712\t - br, gzip, deflate\n10713\t Content-Type:\n10714\t - application/json\n10715\t X-GDC-VALIDATE-RELATIONS:\n10716\t - 'true'\n10717\t X-Requested-With:\n10718\t - XMLHttpRequest\n10719\t method: PUT\n10720\t uri: http://localhost:3000/api/v1/layout/organization\n10721\t response:\n10722\t body:\n10723\t string:\n10724\t detail: 'Invalid combination of auth properties. Only none, user + password,\n10725\t token, key-pair or client id + secret are allowed. Datasource: demo-test-ds,\n10726\t user: , password: , token: , key-pair: , clientId: ,\n10727\t clientSecret: '\n10728\t status: 400\n10729\t title: Bad Request\n10730\t traceId: NORMALIZED_TRACE_ID_000000000000\n10731\t headers:\n10732\t Content-Type:\n10733\t - application/problem+json\n10734\t DATE: *id001\n10735\t Expires:\n10736\t - '0'\n10737\t Pragma:\n10738\t - no-cache\n10739\t X-Content-Type-Options:\n10740\t - nosniff\n10741\t X-GDC-TRACE-ID: *id001\n10742\t status:\n10743\t code: 400\n10744\t message: Bad Request\n10745\tversion: 1\n10746\t", "truncated": false}]} +{"ts": 1778657910.8223526, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "OK so the cassette has two PUT requests, both returning 400. The cassette was recorded when the bug was present.\n\nNow I need to understand: is this cassette supposed to be used in playback mode, or does it need to be re-recorded?\n\nThe problem statement says the test FAILS. The cassette records the failure. So to fix the test, I need to:\n1. Fix the service code \n2. Delete the cassette (so it gets re-recorded with the fixed code)\n\nBut if I delete the cassette, the test will run against the real staging server. \n\nSo my strategy is to fix `put_declarative_ip_allowlist_policies` to handle incomplete credentials gracefully\u2014specifically, when a data source has a username but no password, I'll clear the username to prevent validation errors. Same logic applies for token-based auth. Then I'll delete the bad cassette so it gets re-recorded with the corrected behavior.\n\nBut wait, I should check if there's actually a dedicated IP allowlist API endpoint rather than relying on the full org layout like I'm doing now.", "truncated": false}]} +{"ts": 1778657910.8414037, "type": "assistant", "blocks": [{"type": "text", "text": "Let me look at all available layout API methods to check if there's a dedicated IP allowlist endpoint:", "truncated": false}]} +{"ts": 1778657912.5031137, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Grep", "id": "toolu_017LyDMFEumfqJ6jQnYSWQuM", "input": "{\"pattern\": \"def (get|set|put|post).*ip|def (get|set|put|post).*allowlist\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/gooddata-api-client/gooddata_api_client/api/layout_api.py\", \"output_mode\": \"content\", \"head_limit\": 30, \"-i\": true}", "truncated": false}]} +{"ts": 1778657912.972119, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_017LyDMFEumfqJ6jQnYSWQuM", "is_error": false, "content": "No matches found", "truncated": false}]} +{"ts": 1778657913.3084219, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Grep", "id": "toolu_014sa7RMid7NxsJBHmQD54cw", "input": "{\"pattern\": \"def [a-z]\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/gooddata-api-client/gooddata_api_client/api/layout_api.py\", \"output_mode\": \"content\", \"head_limit\": 100}", "truncated": false}]} +{"ts": 1778657913.4933436, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_014sa7RMid7NxsJBHmQD54cw", "is_error": false, "content": "2495: def delete_data_source_statistics(\n2578: def get_agents_layout(\n2656: def get_analytics_model(\n2740: def get_automations(\n2824: def get_custom_geo_collections_layout(\n2902: def get_data_source_permissions(\n2985: def get_data_source_statistics(\n3070: def get_data_sources_layout(\n3148: def get_export_templates_layout(\n3226: def get_filter_views(\n3310: def get_identity_providers_layout(\n3388: def get_logical_model(\n3472: def get_notification_channels_layout(\n3550: def get_organization_layout(\n3629: def get_organization_permissions(\n3707: def get_user_data_filters(\n3790: def get_user_group_permissions(\n3873: def get_user_groups_layout(\n3951: def get_user_permissions(\n4034: def get_users_layout(\n4112: def get_users_user_groups_layout(\n4190: def get_workspace_data_filters_layout(\n4268: def get_workspace_layout(\n4352: def get_workspace_permissions(\n4435: def get_workspaces_layout(\n4514: def put_data_source_statistics(\n4601: def put_data_sources_layout(\n4684: def put_user_groups_layout(\n4767: def put_users_layout(\n4850: def put_users_user_groups_layout(\n4933: def put_workspace_layout(\n5020: def set_agents_layout(\n5103: def set_analytics_model(\n5190: def set_automations(\n5277: def set_custom_geo_collections(\n5360: def set_data_source_permissions(\n5447: def set_export_templates(\n5530: def set_filter_views(\n5617: def set_identity_providers(\n5700: def set_logical_model(\n5787: def set_notification_channels(\n5870: def set_organization_layout(\n5953: def set_organization_permissions(\n6036: def set_user_data_filters(\n6123: def set_user_group_permissions(\n6210: def set_user_permissions(\n6297: def set_workspace_data_filters_layout(\n6380: def set_workspace_permissions(\n6467: def set_workspaces_layout(", "truncated": false}]} +{"ts": 1778657928.2554069, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "There's no dedicated `set_ip_allowlist_policies` or similar method in the layout API. So the prior agent had to use `set_organization_layout`, which requires the full org layout. The issue is that the full org layout includes data sources with partial credentials.\n\nNow let me think about the fix:\n\nThe `put_declarative_ip_allowlist_policies` method needs to handle data sources with partial credentials. The cleanest fix would be to clear all credential fields from data sources before PUTting.\n\n\nLooking at the data source model, I need to strip out all the credential fields\u2014username, password, token, client_id, client_secret, private_key, and private_key_passphrase\u2014since the server returns them partially populated when we GET the org layout, which causes the PUT to fail. Clearing these fields entirely will let the IP allowlist update go through without authentication conflicts.\n\nBut wait, that would permanently wipe the data source credentials, which isn't acceptable. The real issue is that we're using a full org layout API when we only need to update the IP allowlist policy\u2014notification channels avoid this problem by having a dedicated endpoint. Since there's no dedicated API for IP allowlist policies, I'm stuck using the full org layout call, but I need to find a way to update the policy without corrupting the data source auth configuration.\n\nMaybe I should change the authentication type to `None` and clear the username before the PUT, then restore it afterward\u2014or check if there's another approach that doesn't require modifying the data source at all.", "truncated": false}]} +{"ts": 1778657928.2561488, "type": "assistant", "blocks": [{"type": "text", "text": "Let me check how the data source layout PUT handles credentials:", "truncated": false}]} +{"ts": 1778657929.4480197, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01TEJfnPvisb3gPQppUHTSts", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/src/gooddata_sdk/catalog/data_source/declarative_model/data_source.py\", \"offset\": 1, \"limit\": 80}", "truncated": false}]} +{"ts": 1778657929.4817834, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01TEJfnPvisb3gPQppUHTSts", "is_error": false, "content": "1\t# (C) 2022 GoodData Corporation\n2\tfrom __future__ import annotations\n3\t\n4\timport builtins\n5\tfrom pathlib import Path\n6\tfrom typing import Any, Union\n7\tfrom warnings import warn\n8\t\n9\tfrom attrs import define, field\n10\tfrom gooddata_api_client.model.declarative_data_source import DeclarativeDataSource\n11\tfrom gooddata_api_client.model.declarative_data_sources import DeclarativeDataSources\n12\tfrom gooddata_api_client.model.test_definition_request import TestDefinitionRequest\n13\t\n14\tfrom gooddata_sdk.catalog.base import Base, value_in_allowed\n15\tfrom gooddata_sdk.catalog.entity import ClientSecretCredentialsFromFile, TokenCredentialsFromFile\n16\tfrom gooddata_sdk.catalog.parameter import CatalogParameter\n17\tfrom gooddata_sdk.catalog.permission.declarative_model.permission import CatalogDeclarativeDataSourcePermission\n18\tfrom gooddata_sdk.utils import create_directory, get_ds_credentials, read_layout_from_file, write_layout_to_file\n19\t\n20\tBIGQUERY_TYPE = \"BIGQUERY\"\n21\tDATABRICKS_TYPE = \"DATABRICKS\"\n22\tSNOWFLAKE_TYPE = \"SNOWFLAKE\"\n23\tPRIVATE_KEY = \"private_key\"\n24\tPRIVATE_KEY_PASSPHRASE = \"private_key_passphrase\"\n25\tLAYOUT_DATA_SOURCES_DIR = \"data_sources\"\n26\t\n27\t\n28\t@define(kw_only=True)\n29\tclass CatalogDeclarativeDataSources(Base):\n30\t data_sources: list[CatalogDeclarativeDataSource]\n31\t\n32\t def _inject_base(self, credentials: dict[str, Any]) -> DeclarativeDataSources:\n33\t data_sources = []\n34\t client_class = self.client_class()\n35\t for data_source in self.data_sources:\n36\t data_source_credentials = credentials.get(data_source.id)\n37\t if data_source_credentials is None:\n38\t data_sources.append(data_source.to_api())\n39\t else:\n40\t if data_source.type == BIGQUERY_TYPE:\n41\t token = TokenCredentialsFromFile.token_from_file(data_source_credentials)\n42\t data_sources.append(data_source.to_api(token=token))\n43\t elif data_source.type == D", "truncated": true}]} +{"ts": 1778657932.5852613, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "OK, so the data source model handles credentials separately. The issue is specifically about the `put_declarative_ip_allowlist_policies` using `set_organization_layout`, which includes data sources.\n\nLet me look at the body of the PUT request in the cassette to understand what's being sent:", "truncated": false}]} +{"ts": 1778657932.9054344, "type": "assistant", "blocks": [{"type": "text", "text": "Let me look at what's in the PUT request body in the cassette to understand what's being sent:", "truncated": false}]} +{"ts": 1778657934.008978, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01MU57tkhJLqjKAUK9WRt7Cu", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/fixtures/organization/layout_ip_allowlist_policies.yaml\", \"offset\": 4280, \"limit\": 60}", "truncated": false}]} +{"ts": 1778657934.0615108, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01MU57tkhJLqjKAUK9WRt7Cu", "is_error": false, "content": "4280\t DATE: *id001\n4281\t Expires:\n4282\t - '0'\n4283\t Pragma:\n4284\t - no-cache\n4285\t X-Content-Type-Options:\n4286\t - nosniff\n4287\t X-GDC-TRACE-ID: *id001\n4288\t status:\n4289\t code: 200\n4290\t message: OK\n4291\t - request:\n4292\t body:\n4293\t agents:\n4294\t - aiKnowledge: true\n4295\t availableToAll: true\n4296\t enabled: true\n4297\t id: default-ai-assistant\n4298\t name: Default GoodData AI Assistant\n4299\t skillsMode: all\n4300\t customGeoCollections: []\n4301\t dataSources:\n4302\t - alternativeDataSourceId: ds-put-abc-id\n4303\t authenticationType: USERNAME_PASSWORD\n4304\t id: demo-test-ds\n4305\t name: demo-test-ds\n4306\t permissions:\n4307\t - assignee:\n4308\t id: demo2\n4309\t type: user\n4310\t name: MANAGE\n4311\t - assignee:\n4312\t id: demoGroup\n4313\t type: userGroup\n4314\t name: USE\n4315\t schema: demo\n4316\t type: POSTGRESQL\n4317\t url: jdbc:postgresql://postgres:5432/tiger?sslmode=prefer\n4318\t username: postgres\n4319\t exportTemplates: []\n4320\t identityProviders:\n4321\t - id: defaultIdp\n4322\t idpType: DEX_IDP\n4323\t ipAllowlistPolicies:\n4324\t - allowedSources:\n4325\t - 203.0.113.10/32\n4326\t - 198.51.100.0/24\n4327\t id: admin-vpn-only\n4328\t userGroups:\n4329\t - id: adminGroup\n4330\t type: userGroup\n4331\t users:\n4332\t - id: demo\n4333\t type: user\n4334\t jwks: []\n4335\t notificationChannels: []\n4336\t organization:\n4337\t allowedOrigins: []\n4338\t colorPalettes: []\n4339\t cspDirectives: []", "truncated": false}]} +{"ts": 1778657954.4674172, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "Now I can see clearly what's in the PUT request body:\n\n```yaml\ndataSources:\n - alternativeDataSourceId: ds-put-abc-id\n authenticationType: USERNAME_PASSWORD\n id: demo-test-ds\n name: demo-test-ds\n ...\n username: postgres\n```\n\nThe `authenticationType: USERNAME_PASSWORD` and `username: postgres` is present without a `password`. The server rejects this.\n\nThe fix in the service is to clear the `username` (and set `authenticationType` to None) when there's no `password` for a `USERNAME_PASSWORD` auth type data source.\n\n\nI need to decide between fixing the service code and re-recording the cassette versus modifying the cassette directly, and the right approach is to fix the service and delete the cassette so it gets re-recorded. For the actual fix, I need to modify `put_declarative_ip_allowlist_policies` to strip out credentials from data sources before sending the PUT request.\n\nLooking at what's in the cassette, the PUT body includes authentication details like username and password. The most robust solution is to clear all credential fields (username, password, token, etc.) and set the authentication type to None for each data source in the org layout\u2014this way the server receives a data source with no authentication configured, which should be accepted.\n\nBut there's a catch: this approach would permanently change the data source's authentication settings on the server after the IP allowlist update completes. The real issue is that `set_organization_layout` is an all-or-nothing operation that touches every aspect of the organization, including data sources, so there's no way to update just the IP allowlist policies in isolation.\n\nLet me reconsider this differently\u2014what if we simply omit the credential fields from the data sources in the request and rely on the server to leave those fields unchanged? The key question is whether the API is designed to understand that missing credential fields means \"don't modify these,\" or if it interprets them as \"clear the", "truncated": true}]} +{"ts": 1778657954.4691813, "type": "assistant", "blocks": [{"type": "text", "text": "Let me check if there's a dedicated IP allowlist API endpoint or model:", "truncated": false}]} +{"ts": 1778657954.4848812, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Grep", "id": "toolu_017oskxfkXcJW58jrZmb36Jt", "input": "{\"pattern\": \"ip_allowlist\\\\|IpAllowlist\\\\|ipAllowlist\\\\|allowlist\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/gooddata-api-client/gooddata_api_client\", \"output_mode\": \"content\", \"head_limit\": 50, \"-i\": true}", "truncated": false}]} +{"ts": 1778657954.5137534, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_017oskxfkXcJW58jrZmb36Jt", "is_error": false, "content": "No matches found", "truncated": false}]} +{"ts": 1778657956.568534, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Glob", "id": "toolu_01LtqK5J6N9PTEz16RbdMALi", "input": "{\"pattern\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/gooddata-api-client/gooddata_api_client/model/*allowlist*\"}", "truncated": false}]} +{"ts": 1778657956.581887, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01LtqK5J6N9PTEz16RbdMALi", "is_error": false, "content": "gooddata-api-client/gooddata_api_client/model/declarative_ip_allowlist_policy.py", "truncated": false}]} +{"ts": 1778657956.8620856, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Glob", "id": "toolu_018YNMu9UMG5hoH18DBV9KWB", "input": "{\"pattern\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/gooddata-api-client/gooddata_api_client/model/*ip_allow*\"}", "truncated": false}]} +{"ts": 1778657956.906059, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_018YNMu9UMG5hoH18DBV9KWB", "is_error": false, "content": "gooddata-api-client/gooddata_api_client/model/declarative_ip_allowlist_policy.py", "truncated": false}]} +{"ts": 1778657960.2904315, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Glob", "id": "toolu_016595xLkzkh5dzJQ8iLrHnZ", "input": "{\"pattern\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/gooddata-api-client/gooddata_api_client/model/*allowlist_policies*\"}", "truncated": false}]} +{"ts": 1778657960.3217595, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_016595xLkzkh5dzJQ8iLrHnZ", "is_error": false, "content": "No files found", "truncated": false}]} +{"ts": 1778657976.0328674, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "There's no `DeclarativeIpAllowlistPolicies` model (like there is `DeclarativeNotificationChannels` for notification channels). And there's no dedicated `set_ip_allowlist_policies` in the layout API.\n\nSo the only option is to use `set_organization_layout`. But we need to handle the data source credentials issue.\n\nNow, let me think about a different approach. What if we create a wrapper model like `DeclarativeIpAllowlistPolicies` manually and use `set_organization_layout` differently?\n\nActually, the real issue is that we're using `_check_return_type=False` to get the full org layout and then PUTting it back. \n\nWhen I fetch the org layout with `_check_return_type=False`, it returns the raw JSON response as a dict, which means data sources include their credentials. I could manipulate this dict directly to strip out sensitive fields before updating the layout back.\n\nLooking at the available credential fields in the data source model, I need to clear the appropriate ones based on the authentication type\u2014for username/password auth, I'd remove the username when the password isn't set.\n\nBut clearing all credentials would be a risky side effect that permanently modifies data sources during policy updates. The real issue is that when the test runs against the cassette, the request body needs to match what's recorded, so if I fix the service to not send the username, the cassette entry won't match anymore and the test will fail unless I regenerate it.\n\nLet me look at how VCR is configured in this project to understand the matching behavior better.", "truncated": false}]} +{"ts": 1778657976.0330205, "type": "assistant", "blocks": [{"type": "text", "text": "Let me check the VCR configuration used in tests:", "truncated": false}]} +{"ts": 1778657977.2148771, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Grep", "id": "toolu_01R2USTzhRSs9TeMxsUQNfa5", "input": "{\"pattern\": \"def get_vcr|match_on|record_mode\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests\", \"output_mode\": \"content\", \"head_limit\": 30}", "truncated": false}]} +{"ts": 1778657977.5498319, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01R2USTzhRSs9TeMxsUQNfa5", "is_error": false, "content": "packages/gooddata-sdk/tests/overview.md:62:gd_vcr = vcr.VCR(filter_headers=[\"authorization\", \"user-agent\"], serializer=\"json\", match_on=VCR_MATCH_ON)", "truncated": false}]} +{"ts": 1778657977.5500436, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Glob", "id": "toolu_01Tp46cqT43e6mw9UxpAJALE", "input": "{\"pattern\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/**/vcrpy_utils.py\"}", "truncated": false}]} +{"ts": 1778657977.585121, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01Tp46cqT43e6mw9UxpAJALE", "is_error": false, "content": "packages/tests-support/src/tests_support/vcrpy_utils.py\npackages/gooddata-sdk/.tox/py312/lib/python3.12/site-packages/tests_support/vcrpy_utils.py", "truncated": false}]} +{"ts": 1778657979.3661816, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_014c6W2eLRfAPkymrkwFsa7w", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/tests-support/src/tests_support/vcrpy_utils.py\"}", "truncated": false}]} +{"ts": 1778657979.5560007, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_014c6W2eLRfAPkymrkwFsa7w", "is_error": false, "content": "1\t# (C) 2022 GoodData Corporation\n2\tfrom __future__ import annotations\n3\t\n4\timport os\n5\timport re\n6\timport typing\n7\timport xml.etree.ElementTree as ET\n8\tfrom typing import Any\n9\tfrom urllib.parse import urlparse\n10\t\n11\timport orjson\n12\timport vcr\n13\timport yaml\n14\tfrom vcr.record_mode import RecordMode\n15\t\n16\tVCR_MATCH_ON = (\"method\", \"path\", \"query\", \"body\")\n17\tNON_STATIC_HEADERS = [\"DATE\", \"X-GDC-CANCEL-TOKEN\", \"X-GDC-TRACE-ID\"]\n18\t\n19\t# Infrastructure headers injected by reverse proxies / load balancers.\n20\t# These differ between local and staging and are irrelevant to API behavior.\n21\t_STRIPPED_RESPONSE_HEADERS = {\n22\t \"X-Xss-Protection\",\n23\t \"Strict-Transport-Security\",\n24\t \"Vary\",\n25\t \"Content-Encoding\",\n26\t \"Content-Length\",\n27\t \"Transfer-Encoding\",\n28\t \"Referrer-Policy\",\n29\t \"Cache-Control\",\n30\t}\n31\tHEADERS_STR = \"headers\"\n32\tPLACEHOLDER = [\"PLACEHOLDER\"]\n33\t\n34\t# Fields stripped from request bodies before VCR body matching.\n35\t# These differ between local and staging environments but don't affect\n36\t# the logical identity of a request.\n37\t_ENV_SPECIFIC_BODY_FIELDS = {\n38\t \"password\",\n39\t \"token\",\n40\t \"url\",\n41\t \"username\",\n42\t \"privateKey\",\n43\t \"client_secret\",\n44\t \"private_key_passphrase\",\n45\t}\n46\t\n47\t# Canonical (local) values \u2014 cassettes always use these.\n48\t_CANONICAL_HOST = \"http://localhost:3000\"\n49\t_CANONICAL_HOSTNAME = \"localhost\"\n50\t_CANONICAL_ORG_ID = \"default\"\n51\t_CANONICAL_ORG_NAME = \"Default Organization\"\n52\t_CANONICAL_DS_URL = \"jdbc:postgresql://postgres:5432/tiger?sslmode=prefer\"\n53\t_CANONICAL_DS_PASSWORD = \"passw0rd\"\n54\t\n55\t# Module-level normalization state.\n56\t# Populated by configure_normalization() before tests run.\n57\t# Each entry is (source_string, replacement_string).\n58\t# Ordered longest-first so more specific patterns match before substrings.\n59\t_normalization_replacements: list[tuple[str, str]] = []\n60\t_password_replacements: list[tuple[re.Pattern, str]] = []\n61\t_normalization_configured", "truncated": true}]} +{"ts": 1778658022.467154, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "Now I understand! The key insight is in the `_ENV_SPECIFIC_BODY_FIELDS` set and the `_body_matcher` function:\n\n```python\n_ENV_SPECIFIC_BODY_FIELDS = {\n \"password\",\n \"token\",\n \"url\",\n \"username\",\n \"privateKey\",\n \"client_secret\",\n \"private_key_passphrase\",\n}\n```\n\nAnd the `_body_matcher`:\n```python\ndef _body_matcher(r1: Any, r2: Any) -> None:\n \"\"\"Custom VCR body matcher that ignores environment-specific fields.\"\"\"\n\n\n```\n\nThe body matcher normalizes both request bodies by stripping out sensitive fields like username and password before comparing them. So even if the service sends different credentials in the PUT request, the normalized bodies would match the cassette. But here's the problem\u2014the cassette is still recording that 400 error response, so even if the request bodies align after normalization, we're still replaying the failed response.\n\nThe real issue is that the cassette itself contains the bad response. Fixing just the service code won't help because VCR will still match the normalized request to the cassette entry and return that 400 error. We need to delete the cassette and let it regenerate with the corrected service behavior.\n\nOnce we fix the service to properly handle credentials before the PUT request and delete the cassette, it'll be re-recorded with the successful response. Then the test will pass with the new cassette in place.\n\nNow I need to figure out the exact fix for the service code.\n\nOption 1 seems most robust\u2014clearing all credential fields from data sources before sending the PUT request. This prevents sending partial credentials that would cause the server to reject the request. If we clear the `authenticationType` to None along with the username and password fields, the PUT should succeed without trying to update the data source's authentication.\n\nBut I'm realizing there's a potential issue: if we change `authenticationType` to None, we're actually modifying the data source's auth configuration, which is a side effect", "truncated": true}]} +{"ts": 1778658022.474538, "type": "assistant", "blocks": [{"type": "text", "text": "Let me look at how the org layout object is structured when returned with `_check_return_type=False`:", "truncated": false}]} +{"ts": 1778658023.2992053, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Grep", "id": "toolu_01MDHEo8rVuxgyb1hcCL6gfH", "input": "{\"pattern\": \"data_sources|dataSources\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/gooddata-api-client/gooddata_api_client/model/declarative_organization.py\", \"output_mode\": \"content\", \"head_limit\": 50}", "truncated": false}]} +{"ts": 1778658023.6827464, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01MDHEo8rVuxgyb1hcCL6gfH", "is_error": false, "content": "118: 'data_sources': ([DeclarativeDataSource],), # noqa: E501\n139: 'data_sources': 'dataSources', # noqa: E501\n197: data_sources ([DeclarativeDataSource]): [optional] # noqa: E501\n298: data_sources ([DeclarativeDataSource]): [optional] # noqa: E501", "truncated": false}]} +{"ts": 1778658052.7359948, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "OK, so `data_sources` (snake_case Python attribute) maps to `dataSources` (JSON key). And the data source model has:\n\n- `authentication_type` \u2192 `authenticationType`\n- `username` \u2192 `username`\n\nNow let me think about the fix. When using `_check_return_type=False`, the API returns a raw object. I need to check how to access and modify data source credential fields.\n\nActually, looking at the code pattern:\n\n\nWhen `_check_return_type=False` is used, the API returns a dict with camelCase keys matching the JSON response, so I'd need to use camelCase keys directly when accessing or modifying attributes on that returned object. I need to figure out what type of object `org_layout` is when `_check_return_type=False` is used, since the code assigns attributes to it directly like `org_layout.ip_allowlist_policies = [...]`, which wouldn't work if it were a dict. The API client must be deserializing the response into some kind of object that supports attribute assignment.\n\nSo I can access the data sources through `org_layout.data_sources`, where each one is a `DeclarativeDataSource` object with properties like `authentication_type`, `username`, and `password`. My approach is to iterate through these data sources before making the PUT request and strip out any partial credentials\u2014specifically when `authentication_type` is `\"USERNAME_PASSWORD\"` but only `username` is set without a `password`.\n\nWriting the implementation... I'm iterating through each data source and checking if it has a username without a password in USERNAME_PASSWORD auth mode\u2014if so, I'm clearing both the username and authentication type to avoid validation errors on the PUT request. But I'm realizing I need to verify that setting authentication_type to None is actually allowed by the model schema, so I'm checking what values are permitted. I'm trying to figure out the best way to handle clearing credentials from data sources when setting the organization layout. Since `_check_return_type=False` makes the model more", "truncated": true}]} +{"ts": 1778658052.739875, "type": "assistant", "blocks": [{"type": "text", "text": "Let me look at the API client model_utils to understand what `_check_return_type=False` returns:", "truncated": false}]} +{"ts": 1778658052.7595937, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Bash", "id": "toolu_01Hof4XSaYHCYrsZbcp63Re4", "input": "{\"command\": \"wc -l /home/runner/_work/gdc-nas/gdc-nas/sdk/gooddata-api-client/gooddata_api_client/api_client.py\", \"description\": \"Check size of api_client.py\"}", "truncated": false}]} +{"ts": 1778658052.7900693, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01Hof4XSaYHCYrsZbcp63Re4", "is_error": true, "content": "Blocked: wc -l /home/runner/_work/gdc-nas/gdc-nas/sdk/gooddata-api-client/gooddata_api_cl", "truncated": false}]} +{"ts": 1778658055.2910984, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Grep", "id": "toolu_01XL95PQL1Mqqw4UaJjr5dTk", "input": "{\"pattern\": \"check_return_type|_check_return_type|deserialize\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/gooddata-api-client/gooddata_api_client/api_client.py\", \"output_mode\": \"content\", \"head_limit\": 30}", "truncated": false}]} +{"ts": 1778658055.3399844, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01XL95PQL1Mqqw4UaJjr5dTk", "is_error": false, "content": "35: deserialize_file,\n216: # deserialize response data\n227: return_data = self.deserialize(\n300: def deserialize(self, response, response_type, _check_type):\n303: :param response: RESTResponse object to be deserialized.\n318: :return: deserialized object.\n324: return deserialize_file(response.data, self.configuration,\n335: deserialized_data = validate_and_convert_types(\n343: return deserialized_data\n366: \"\"\"Makes the HTTP request (synchronous) and returns deserialized data.\n704: '_check_return_type',\n720: '_check_return_type': (bool,),\n893: _check_type=kwargs['_check_return_type'],", "truncated": false}]} +{"ts": 1778658057.4095178, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01DpSEfkhszAJ4enpmmnavDL", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/gooddata-api-client/gooddata_api_client/api_client.py\", \"offset\": 207, \"limit\": 40}", "truncated": false}]} +{"ts": 1778658057.4751542, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01DpSEfkhszAJ4enpmmnavDL", "is_error": false, "content": "207\t\n208\t self.last_response = response_data\n209\t\n210\t return_data = response_data\n211\t\n212\t if not _preload_content:\n213\t return (return_data)\n214\t return return_data\n215\t\n216\t # deserialize response data\n217\t if response_type:\n218\t if response_type != (file_type,):\n219\t encoding = \"utf-8\"\n220\t content_type = response_data.getheader('content-type')\n221\t if content_type is not None:\n222\t match = re.search(r\"charset=([a-zA-Z\\-\\d]+)[\\s\\;]?\", content_type)\n223\t if match:\n224\t encoding = match.group(1)\n225\t response_data.data = response_data.data.decode(encoding)\n226\t\n227\t return_data = self.deserialize(\n228\t response_data,\n229\t response_type,\n230\t _check_type\n231\t )\n232\t else:\n233\t return_data = None\n234\t\n235\t if _return_http_data_only:\n236\t return (return_data)\n237\t else:\n238\t return (return_data, response_data.status,\n239\t response_data.getheaders())\n240\t\n241\t def parameters_to_multipart(self, params, collection_types):\n242\t \"\"\"Get parameters as list of tuples, formatting as json if value is collection_types\n243\t\n244\t :param params: Parameters as list of two-tuples\n245\t :param dict collection_types: Parameter collection types\n246\t :return: Parameters as list of tuple or urllib3.fields.RequestField", "truncated": false}]} +{"ts": 1778658059.3788228, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01NjhR4wQbjWAcDLifuF9uJc", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/gooddata-api-client/gooddata_api_client/api_client.py\", \"offset\": 300, \"limit\": 50}", "truncated": false}]} +{"ts": 1778658059.454762, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01NjhR4wQbjWAcDLifuF9uJc", "is_error": false, "content": "300\t def deserialize(self, response, response_type, _check_type):\n301\t \"\"\"Deserializes response into an object.\n302\t\n303\t :param response: RESTResponse object to be deserialized.\n304\t :param response_type: For the response, a tuple containing:\n305\t valid classes\n306\t a list containing valid classes (for list schemas)\n307\t a dict containing a tuple of valid classes as the value\n308\t Example values:\n309\t (str,)\n310\t (Pet,)\n311\t (float, none_type)\n312\t ([int, none_type],)\n313\t ({str: (bool, str, int, float, date, datetime, str, none_type)},)\n314\t :param _check_type: boolean, whether to check the types of the data\n315\t received from the server\n316\t :type _check_type: bool\n317\t\n318\t :return: deserialized object.\n319\t \"\"\"\n320\t # handle file downloading\n321\t # save response body into a tmp file and return the instance\n322\t if response_type == (file_type,):\n323\t content_disposition = response.getheader(\"Content-Disposition\")\n324\t return deserialize_file(response.data, self.configuration,\n325\t content_disposition=content_disposition)\n326\t\n327\t # fetch data from response object\n328\t try:\n329\t received_data = json.loads(response.data)\n330\t except ValueError:\n331\t received_data = response.data\n332\t\n333\t # store our data under the key of 'received_data' so users have some\n334\t # context if they are deserializing a string and the data type is wrong\n335\t deserialized_data = validate_and_convert_types(\n336\t received_data,\n337\t response_type,\n338\t ['received_data'],\n339\t True,\n340\t _check_type,\n341\t configuration=self.configuration\n342\t )\n343\t return deserialized_data\n344\t\n345\t def call_api(\n346\t ", "truncated": true}]} +{"ts": 1778658069.1626291, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "Let me understand how `_check_return_type=False` affects the deserialization. When `_check_return_type=False`, it calls `validate_and_convert_types` with `_check_type=False`. This means the type checking is disabled, but the deserialization still happens.\n\nLooking at the GET response body in the cassette:\n- The response body is a YAML dict (which gets deserialized to JSON in memory)\n- The cassette serializer handles this\n\nWhen the API client gets the response, it calls `json.loads(response.data)` to get `received_data`, then calls `validate_and_convert_types` with the response type.\n\n\nFor `get_organization_layout`, the response type is `DeclarativeOrganization` (or something similar). With `_check_return_type=False`, type checking is skipped but the object still gets constructed as a `DeclarativeOrganization` instance with snake_case attributes. The nested objects like `data_sources` might remain as dicts rather than being fully deserialized into their proper types.\n\nLooking at how `get_declarative_ip_allowlist_policies` handles this, it retrieves the nested policies from the org layout and then explicitly converts each one using `from_api()`. So I need to check how `from_api()` works for these nested objects to understand the proper conversion pattern.", "truncated": false}]} +{"ts": 1778658069.1633177, "type": "assistant", "blocks": [{"type": "text", "text": "Let me look at the `Base.from_api` method to understand how deserialization works:", "truncated": false}]} +{"ts": 1778658069.4911737, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01LWSq5SYLNH4FRGbNHiqMP2", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/src/gooddata_sdk/catalog/base.py\", \"offset\": 1, \"limit\": 100}", "truncated": false}]} +{"ts": 1778658069.5302095, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01LWSq5SYLNH4FRGbNHiqMP2", "is_error": false, "content": "1\t# (C) 2022 GoodData Corporation\n2\tfrom __future__ import annotations\n3\t\n4\timport builtins\n5\tfrom typing import Any, TypeVar\n6\t\n7\tfrom attrs import Attribute, asdict, define, field\n8\tfrom cattrs import structure\n9\t\n10\tfrom gooddata_sdk.utils import AllPagedEntities\n11\t\n12\tT = TypeVar(\"T\", bound=\"Base\")\n13\tU = TypeVar(\"U\", bound=\"JsonApiEntityBase\")\n14\t\n15\t\n16\tdef value_in_allowed(instance: type[Base], attribute: Attribute, value: str, client_class: Any | None = None) -> None:\n17\t if client_class is None:\n18\t client_class = instance.client_class()\n19\t allowed_values = client_class.allowed_values.get((attribute.name,))\n20\t if allowed_values is not None and value not in list(allowed_values.values()):\n21\t raise ValueError(\n22\t f\"Allowed values for attribute {attribute.name} are: {', '.join(list(allowed_values.values()))}. \"\n23\t f\"But value {value} was passed.\"\n24\t )\n25\t\n26\t\n27\t@define\n28\tclass Base:\n29\t @classmethod\n30\t def from_api(cls: type[T], entity: dict[str, Any]) -> T:\n31\t \"\"\"\n32\t Creates object from entity passed by client class, which represents it as dictionary.\n33\t \"\"\"\n34\t return structure(entity, cls)\n35\t\n36\t @classmethod\n37\t def from_dict(cls: type[T], data: dict[str, Any], camel_case: bool = True) -> T:\n38\t \"\"\"\n39\t Creates object from dictionary. It needs to be specified if the dictionary is in camelCase or snake_case.\n40\t \"\"\"\n41\t if camel_case:\n42\t data = cls.client_class().from_dict(data, camel_case).to_dict()\n43\t return structure(data, cls)\n44\t\n45\t def to_dict(self, camel_case: bool = True) -> dict[str, Any]:\n46\t \"\"\"\n47\t Converts object into dictionary. Optional argument if the dictionary should be camelCase or snake_case can be\n48\t specified.\n49\t \"\"\"\n50\t # This branch does not have to exist, but in case of camel_case=False it can perform fewer calls\n51\t if not camel_case:", "truncated": true}]} +{"ts": 1778658082.7864625, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "Interesting! I see that `from_api` uses `structure(entity, cls)` where `entity` is a dict. So when `from_api` is called in `get_declarative_ip_allowlist_policies`, the `policy` is passed as... what? \n\nLet me trace through:\n1. `org_layout = self._layout_api.get_organization_layout(_check_return_type=False)` \u2192 returns a `DeclarativeOrganization` object (or something similar from the API client)\n2. `policies = getattr(org_layout, \"ip_allowlist_policies\", None) or []` \u2192 returns a list of `DeclarativeIpAllowlistPolicy` objects from the API client\n\n\n3. When `CatalogDeclarativeIpAllowlistPolicy.from_api(policy)` is called, it's trying to structure a `DeclarativeIpAllowlistPolicy` object into `CatalogDeclarativeIpAllowlistPolicy`, but `structure` typically expects a dict. The `_check_return_type=False` flag might be returning raw dicts instead of model objects, which would make this work.\n\nActually, the real issue is that the test fails because `put_declarative_ip_allowlist_policies` sends incomplete credentials\u2014just `username: postgres` without a password\u2014in the org layout PUT request. The fix is to clear partial credentials from data sources before PUTting. Since the cassette has the 400 error baked in, I need to delete it so the pipeline can re-record it with the corrected behavior.\n\nNow I need to check what `org_layout.data_sources` returns when `_check_return_type=False` is used by looking at the `get_organization_layout` method in layout_api.py.", "truncated": false}]} +{"ts": 1778658082.7868505, "type": "assistant", "blocks": [{"type": "text", "text": "Let me look at the get_organization_layout return type:", "truncated": false}]} +{"ts": 1778658083.4802897, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_012PBtfvkWkANUv41td5cqCU", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/gooddata-api-client/gooddata_api_client/api/layout_api.py\", \"offset\": 3550, \"limit\": 100}", "truncated": false}]} +{"ts": 1778658083.513738, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_012PBtfvkWkANUv41td5cqCU", "is_error": false, "content": "3550\t def get_organization_layout(\n3551\t self,\n3552\t **kwargs\n3553\t ):\n3554\t \"\"\"Get organization layout # noqa: E501\n3555\t\n3556\t Retrieve complete layout of organization, workspaces, user-groups, etc. # noqa: E501\n3557\t This method makes a synchronous HTTP request by default. To make an\n3558\t asynchronous HTTP request, please pass async_req=True\n3559\t\n3560\t >>> thread = api.get_organization_layout(async_req=True)\n3561\t >>> result = thread.get()\n3562\t\n3563\t\n3564\t Keyword Args:\n3565\t exclude ([str]): [optional]\n3566\t _return_http_data_only (bool): response data without head status\n3567\t code and headers. Default is True.\n3568\t _preload_content (bool): if False, the urllib3.HTTPResponse object\n3569\t will be returned without reading/decoding response data.\n3570\t Default is True.\n3571\t _request_timeout (int/float/tuple): timeout setting for this request. If\n3572\t one number provided, it will be total request timeout. It can also\n3573\t be a pair (tuple) of (connection, read) timeouts.\n3574\t Default is None.\n3575\t _check_input_type (bool): specifies if type checking\n3576\t should be done one the data sent to the server.\n3577\t Default is True.\n3578\t _check_return_type (bool): specifies if type checking\n3579\t should be done one the data received from the server.\n3580\t Default is True.\n3581\t _spec_property_naming (bool): True if the variable names in the input data\n3582\t are serialized names, as specified in the OpenAPI document.\n3583\t False if the variable names in the input data\n3584\t are pythonic names, e.g. snake case (default)\n3585\t _content_type (str/None): force body content-type.\n3586\t Default is None and c", "truncated": true}]} +{"ts": 1778658117.4009137, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "The `get_organization_layout` returns `DeclarativeOrganization`. When `_check_return_type=False`, the type checking is disabled but the object is still deserialized as a `DeclarativeOrganization`.\n\nNow, looking at the `DeclarativeOrganization` model:\n- `data_sources` \u2192 list of `DeclarativeDataSource`\n- `ip_allowlist_policies` \u2192 list of `DeclarativeIpAllowlistPolicy`\n\nWhen `_check_return_type=False`, the deserialization might still create `DeclarativeDataSource` objects for each data source. \n\nThese objects would have `authentication_type` and `username` attributes. The real question is whether we can actually modify them. Since `DeclarativeDataSource` inherits from `ModelNormal`, which uses `__setattr__` to manage attribute setting, there's likely validation happening even when `_check_return_type=False` is set. I'm starting to think modifying the `DeclarativeDataSource` object directly might not be the right approach here.\n\nLet me consider a different strategy instead. What if I convert the organization layout to a dictionary, strip out the credentials, and then convert it back? I'm realizing that `_check_return_type=False` doesn't actually change how the response gets deserialized\u2014it just skips type checking. The object still comes back as a `DeclarativeOrganization`. So when I pass a modified `DeclarativeOrganization` object to `set_organization_layout`, I'm still working with the same object structure, just with potentially modified attributes.\n\nThe real issue is that when the API serializes this object for the PUT request, it will include the data sources exactly as they exist in memory\u2014including that `username: postgres` field from the original GET response. So I need to either remove the username from the data source object itself, or prevent it from being serialized in the first place. Let me explore whether I can directly set attributes on a `DeclarativeDataSource` instance to null out the username field, or if I need to look at how the model's attribute as", "truncated": true}]} +{"ts": 1778658117.4035115, "type": "assistant", "blocks": [{"type": "text", "text": "Let me look at the ModelNormal class to understand attribute setting:", "truncated": false}]} diff --git a/transcripts/fix-C009-attempt2.jsonl b/transcripts/fix-C009-attempt2.jsonl new file mode 100644 index 000000000..79a2c2b89 --- /dev/null +++ b/transcripts/fix-C009-attempt2.jsonl @@ -0,0 +1,133 @@ +{"ts": 1778658128.172304, "type": "system"} +{"ts": 1778658129.8550375, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "Let me start by reading the failing test to understand what's going wrong.", "truncated": false}]} +{"ts": 1778658130.5511568, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01UpJK6kTjk8muLpp7VaLTYf", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/test_catalog_organization.py\", \"offset\": 580, \"limit\": 60}", "truncated": false}]} +{"ts": 1778658130.5971906, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01UpJK6kTjk8muLpp7VaLTYf", "is_error": false, "content": "580\t user_groups=[\n581\t CatalogDeclarativeUserGroupIdentifier(id=\"adminGroup\", type=\"userGroup\"),\n582\t ],\n583\t users=[\n584\t CatalogUserIdentifier(id=\"demo\", type=\"user\"),\n585\t ],\n586\t ),\n587\t ]\n588\t\n589\t try:\n590\t sdk.catalog_organization.put_declarative_ip_allowlist_policies(new_policies)\n591\t result = sdk.catalog_organization.get_declarative_ip_allowlist_policies()\n592\t assert len(result) == 1\n593\t assert result[0].id == \"admin-vpn-only\"\n594\t assert result[0].allowed_sources == [\"203.0.113.10/32\", \"198.51.100.0/24\"]\n595\t assert len(result[0].user_groups) == 1\n596\t assert result[0].user_groups[0].id == \"adminGroup\"\n597\t assert len(result[0].users) == 1\n598\t assert result[0].users[0].id == \"demo\"\n599\t finally:\n600\t sdk.catalog_organization.put_declarative_ip_allowlist_policies(original_policies)\n601\t", "truncated": false}]} +{"ts": 1778658132.487697, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01TahnLFxmuRUK4HAsckxVDC", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/test_catalog_organization.py\", \"offset\": 550, \"limit\": 35}", "truncated": false}]} +{"ts": 1778658132.516303, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01TahnLFxmuRUK4HAsckxVDC", "is_error": false, "content": "550\t# identifiers=identifiers,\n551\t# oauth_client_id=oauth_client_id,\n552\t# oauth_client_secret=oauth_client_secret,\n553\t# oauth_issuer_location=oauth_issuer_location,\n554\t# ),\n555\t# ]\n556\t# sdk.catalog_organization.put_declarative_identity_providers(identity_providers_e)\n557\t# identity_providers_o = sdk.catalog_organization.get_declarative_identity_providers()\n558\t# assert identity_providers_o[0].id == identity_providers_e[0].id\n559\t# assert identity_providers_o[0].custom_claim_mapping == identity_providers_e[0].custom_claim_mapping\n560\t# assert identity_providers_o[0].identifiers == identity_providers_e[0].identifiers\n561\t# assert identity_providers_o[0].oauth_client_id == identity_providers_e[0].oauth_client_id\n562\t# assert identity_providers_o[0].oauth_client_secret is None\n563\t# assert identity_providers_o[0].oauth_issuer_location == identity_providers_e[0].oauth_issuer_location\n564\t# finally:\n565\t# sdk.catalog_organization.put_declarative_identity_providers([])\n566\t# idps = sdk.catalog_organization.get_declarative_identity_providers()\n567\t# assert len(idps) == 0\n568\t\n569\t\n570\t@gd_vcr.use_cassette(str(_fixtures_dir / \"layout_ip_allowlist_policies.yaml\"))\n571\tdef test_layout_ip_allowlist_policies(test_config):\n572\t sdk = GoodDataSdk.create(host_=test_config[\"host\"], token_=test_config[\"token\"])\n573\t\n574\t original_policies = sdk.catalog_organization.get_declarative_ip_allowlist_policies()\n575\t\n576\t new_policies = [\n577\t CatalogDeclarativeIpAllowlistPolicy(\n578\t id=\"admin-vpn-only\",\n579\t allowed_sources=[\"203.0.113.10/32\", \"198.51.100.0/24\"],\n580\t user_groups=[\n581\t CatalogDeclarativeUserGroupIdentifier(id=\"adminGroup\", type=\"userGroup\"),\n582\t ],\n583\t users=[\n584\t CatalogUserIdentifier(id=\"de", "truncated": true}]} +{"ts": 1778658137.0965571, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "The test is failing because when restoring the `original_policies`, the API is returning a 400 error. The error says:\n\n\"Invalid combination of auth properties. Only none, user + password, token, key-pair or client id + secret are allowed. Datasource: demo-test-ds, user: , password: , token: , key-pair: , clientId: , clientSecret: \"\n\nThis error is from a PUT request to `set_organization_layout`. This is calling `set_organization_layout` instead of a more specific endpoint.\n\n\nNow I need to check the service implementation for `put_declarative_ip_allowlist_policies` to understand what's happening.", "truncated": false}]} +{"ts": 1778658137.1102817, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Grep", "id": "toolu_01965AHvJidtcnDZzoYh3ALV", "input": "{\"pattern\": \"put_declarative_ip_allowlist_policies\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk\", \"output_mode\": \"content\"}", "truncated": false}]} +{"ts": 1778658137.1415513, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Grep", "id": "toolu_01Xu9Vy9cofS2RXv9MQwTAtR", "input": "{\"pattern\": \"ip_allowlist\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk\", \"output_mode\": \"content\"}", "truncated": false}]} +{"ts": 1778658137.1421459, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01965AHvJidtcnDZzoYh3ALV", "is_error": false, "content": "packages/gooddata-sdk/tests/catalog/test_catalog_organization.py:590: sdk.catalog_organization.put_declarative_ip_allowlist_policies(new_policies)\npackages/gooddata-sdk/tests/catalog/test_catalog_organization.py:600: sdk.catalog_organization.put_declarative_ip_allowlist_policies(original_policies)\npackages/gooddata-sdk/src/gooddata_sdk/catalog/organization/service.py:760: def put_declarative_ip_allowlist_policies(", "truncated": false}]} +{"ts": 1778658137.2406247, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01Xu9Vy9cofS2RXv9MQwTAtR", "is_error": false, "content": "packages/gooddata-sdk/tests/catalog/test_catalog_organization.py:570:@gd_vcr.use_cassette(str(_fixtures_dir / \"layout_ip_allowlist_policies.yaml\"))\npackages/gooddata-sdk/tests/catalog/test_catalog_organization.py:571:def test_layout_ip_allowlist_policies(test_config):\npackages/gooddata-sdk/tests/catalog/test_catalog_organization.py:574: original_policies = sdk.catalog_organization.get_declarative_ip_allowlist_policies()\npackages/gooddata-sdk/tests/catalog/test_catalog_organization.py:590: sdk.catalog_organization.put_declarative_ip_allowlist_policies(new_policies)\npackages/gooddata-sdk/tests/catalog/test_catalog_organization.py:591: result = sdk.catalog_organization.get_declarative_ip_allowlist_policies()\npackages/gooddata-sdk/tests/catalog/test_catalog_organization.py:600: sdk.catalog_organization.put_declarative_ip_allowlist_policies(original_policies)\npackages/gooddata-sdk/src/gooddata_sdk/catalog/organization/service.py:34:from gooddata_sdk.catalog.organization.layout.ip_allowlist_policy import CatalogDeclarativeIpAllowlistPolicy\npackages/gooddata-sdk/src/gooddata_sdk/catalog/organization/service.py:747: def get_declarative_ip_allowlist_policies(self) -> list[CatalogDeclarativeIpAllowlistPolicy]:\npackages/gooddata-sdk/src/gooddata_sdk/catalog/organization/service.py:757: policies = getattr(org_layout, \"ip_allowlist_policies\", None) or []\npackages/gooddata-sdk/src/gooddata_sdk/catalog/organization/service.py:760: def put_declarative_ip_allowlist_policies(\npackages/gooddata-sdk/src/gooddata_sdk/catalog/organization/service.py:761: self, ip_allowlist_policies: list[CatalogDeclarativeIpAllowlistPolicy]\npackages/gooddata-sdk/src/gooddata_sdk/catalog/organization/service.py:765: Reads the full organization layout, replaces the ip_allowlist_policies field,\npackages/gooddata-sdk/src/gooddata_sdk/catalog/organization/service.py:769: ip_allowlist_policies (list[CatalogDeclarativeIpAllowlistPolicy]):\npackages/go", "truncated": true}]} +{"ts": 1778658139.1359537, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01SXRjh2hMeZaLbNfK242DfX", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/src/gooddata_sdk/catalog/organization/service.py\", \"offset\": 745, \"limit\": 45}", "truncated": false}]} +{"ts": 1778658139.1483042, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01SXRjh2hMeZaLbNfK242DfX", "is_error": false, "content": "745\t raise ValueError(f\"Error switching active identity provider: {str(e)}\")\n746\t\n747\t def get_declarative_ip_allowlist_policies(self) -> list[CatalogDeclarativeIpAllowlistPolicy]:\n748\t \"\"\"Get all declarative IP allowlist policies for the current organization.\n749\t\n750\t IP allowlist policies are returned as part of the full organization layout.\n751\t\n752\t Returns:\n753\t list[CatalogDeclarativeIpAllowlistPolicy]:\n754\t List of declarative IP allowlist policies.\n755\t \"\"\"\n756\t org_layout = self._layout_api.get_organization_layout(_check_return_type=False)\n757\t policies = getattr(org_layout, \"ip_allowlist_policies\", None) or []\n758\t return [CatalogDeclarativeIpAllowlistPolicy.from_api(policy) for policy in policies]\n759\t\n760\t def put_declarative_ip_allowlist_policies(\n761\t self, ip_allowlist_policies: list[CatalogDeclarativeIpAllowlistPolicy]\n762\t ) -> None:\n763\t \"\"\"Put declarative IP allowlist policies for the current organization.\n764\t\n765\t Reads the full organization layout, replaces the ip_allowlist_policies field,\n766\t and writes the full layout back. All other organization settings are preserved.\n767\t\n768\t Args:\n769\t ip_allowlist_policies (list[CatalogDeclarativeIpAllowlistPolicy]):\n770\t List of declarative IP allowlist policies to set.\n771\t\n772\t Returns:\n773\t None\n774\t \"\"\"\n775\t org_layout = self._layout_api.get_organization_layout(_check_return_type=False)\n776\t org_layout.ip_allowlist_policies = [policy.to_api() for policy in ip_allowlist_policies]\n777\t self._layout_api.set_organization_layout(org_layout, _check_return_type=False)\n778\t", "truncated": false}]} +{"ts": 1778658146.6000776, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "Now I understand the issue. The `put_declarative_ip_allowlist_policies` reads the full organization layout and then sets it back. The error message says:\n\n\"Invalid combination of auth properties. Only none, user + password, token, key-pair or client id + secret are allowed. Datasource: demo-test-ds, user: , password: , token: , key-pair: , clientId: , clientSecret: \"\n\nThis is a 400 error because the organization layout contains a datasource (`demo-test-ds`) that has a `username` set but when you do a PUT, you need to provide either the full combination (user + password) or no auth. \n\nThe root cause is that when retrieving the org layout, datasource passwords are masked and never returned, so when I write it back, the username remains but the password is missing\u2014an invalid state the API won't accept. This is a classic read-modify-write problem with sensitive data. I need to check how other PUT operations handle this scenario to find the right approach.", "truncated": false}]} +{"ts": 1778658146.6060953, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_0155xugKzG1dRLUj1tUdA9g2", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/src/gooddata_sdk/catalog/organization/service.py\", \"offset\": 1, \"limit\": 50}", "truncated": false}]} +{"ts": 1778658146.612122, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_0155xugKzG1dRLUj1tUdA9g2", "is_error": false, "content": "1\t# (C) 2022 GoodData Corporation\n2\tfrom __future__ import annotations\n3\t\n4\timport functools\n5\tfrom typing import Any, Literal\n6\t\n7\tfrom gooddata_api_client.exceptions import NotFoundException\n8\tfrom gooddata_api_client.model.declarative_export_templates import DeclarativeExportTemplates\n9\tfrom gooddata_api_client.model.declarative_notification_channels import DeclarativeNotificationChannels\n10\tfrom gooddata_api_client.model.json_api_csp_directive_in_document import JsonApiCspDirectiveInDocument\n11\tfrom gooddata_api_client.model.json_api_export_template_in_document import JsonApiExportTemplateInDocument\n12\tfrom gooddata_api_client.model.json_api_export_template_post_optional_id_document import (\n13\t JsonApiExportTemplatePostOptionalIdDocument,\n14\t)\n15\tfrom gooddata_api_client.model.json_api_identity_provider_in_document import JsonApiIdentityProviderInDocument\n16\tfrom gooddata_api_client.model.json_api_organization_patch import JsonApiOrganizationPatch\n17\tfrom gooddata_api_client.model.json_api_organization_patch_document import JsonApiOrganizationPatchDocument\n18\tfrom gooddata_api_client.model.json_api_organization_setting_in_document import JsonApiOrganizationSettingInDocument\n19\tfrom gooddata_api_client.model.switch_identity_provider_request import SwitchIdentityProviderRequest\n20\t\n21\tfrom gooddata_sdk import CatalogDeclarativeExportTemplate, CatalogExportTemplate\n22\tfrom gooddata_sdk.catalog.catalog_service_base import CatalogServiceBase\n23\tfrom gooddata_sdk.catalog.organization.entity_model.directive import CatalogCspDirective\n24\tfrom gooddata_sdk.catalog.organization.entity_model.identity_provider import CatalogIdentityProvider\n25\tfrom gooddata_sdk.catalog.organization.entity_model.jwk import CatalogJwk, CatalogJwkDocument\n26\tfrom gooddata_sdk.catalog.organization.entity_model.llm_provider import (\n27\t CatalogLlmProvider,\n28\t CatalogLlmProviderDocument,\n29\t CatalogLlmProviderPatch,\n30\t CatalogLlmProviderPatchDocument,\n31\t)\n32\tfrom gooddata_sdk.c", "truncated": true}]} +{"ts": 1778658146.613672, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Grep", "id": "toolu_01HBe377TCqhMzCCqG6xKcnx", "input": "{\"pattern\": \"set_organization_layout\\\\|get_organization_layout\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/src/gooddata_sdk/catalog/organization/service.py\", \"output_mode\": \"content\"}", "truncated": false}]} +{"ts": 1778658146.63869, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01HBe377TCqhMzCCqG6xKcnx", "is_error": false, "content": "No matches found", "truncated": false}]} +{"ts": 1778658149.0903397, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Grep", "id": "toolu_01LmSadAKPhJmpYu8tR7Z684", "input": "{\"pattern\": \"set_organization_layout\\\\|get_organization_layout\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/src/gooddata_sdk/catalog/organization/service.py\", \"output_mode\": \"content\", \"head_limit\": 0}", "truncated": false}]} +{"ts": 1778658149.208318, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01LmSadAKPhJmpYu8tR7Z684", "is_error": false, "content": "No matches found", "truncated": false}]} +{"ts": 1778658151.0275986, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Bash", "id": "toolu_01Seo7dCoocGNifCVZTterGu", "input": "{\"command\": \"grep -n \\\"set_organization_layout\\\\|get_organization_layout\\\\|_layout_api\\\" /home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/src/gooddata_sdk/catalog/organization/service.py | head -30\"}", "truncated": false}]} +{"ts": 1778658151.0614524, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01Seo7dCoocGNifCVZTterGu", "is_error": true, "content": "Blocked: grep -n \"set_organization_layout\\|get_organization_layout\\|_layout_api\" /home/ru", "truncated": false}]} +{"ts": 1778658152.9363732, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Grep", "id": "toolu_018yh11PzNAPpJMXCZJ72jW6", "input": "{\"pattern\": \"_layout_api\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/src/gooddata_sdk/catalog/organization/service.py\", \"output_mode\": \"content\"}", "truncated": false}]} +{"ts": 1778658152.9701207, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_018yh11PzNAPpJMXCZJ72jW6", "is_error": false, "content": "644: for nc in self._layout_api.get_notification_channels_layout().notification_channels\n661: self._layout_api.set_notification_channels(DeclarativeNotificationChannels(notification_channels=api_ncs))\n672: CatalogDeclarativeIdentityProvider.from_api(idp) for idp in self._layout_api.get_identity_providers_layout()\n687: self._layout_api.set_identity_providers(declarative_identity_provider=api_idps)\n697: export_templates_api = self._layout_api.get_export_templates_layout()\n718: self._layout_api.set_export_templates(\n756: org_layout = self._layout_api.get_organization_layout(_check_return_type=False)\n775: org_layout = self._layout_api.get_organization_layout(_check_return_type=False)\n777: self._layout_api.set_organization_layout(org_layout, _check_return_type=False)", "truncated": false}]} +{"ts": 1778658158.4884343, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "The issue is clear now. The `put_declarative_ip_allowlist_policies` method does a read-modify-write on the full organization layout. When it reads the organization layout, the datasource passwords are masked/not returned. Then when it writes back the full layout, the API rejects it because the datasource (`demo-test-ds`) has a username but no password, which is an invalid combination.\n\nThe fix should not read the full org layout and write it back. Instead, it should use a more targeted API that only sets the IP allowlist policies, if one exists. \n\nLet me check what API endpoints are available in the layout_api to see if there's a dedicated method for updating just the IP allowlist policies.", "truncated": false}]} +{"ts": 1778658158.4954226, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Bash", "id": "toolu_01DvirAig61gegGNhEyB21CE", "input": "{\"command\": \"ls /home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/.tox/py312/lib/python3.12/site-packages/gooddata_api_client/api/layout_api.py 2>/dev/null && echo \\\"EXISTS\\\"\"}", "truncated": false}]} +{"ts": 1778658158.5212982, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01DvirAig61gegGNhEyB21CE", "is_error": true, "content": "Blocked: ls /home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/.tox/py312/lib/p", "truncated": false}]} +{"ts": 1778658160.2775197, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Glob", "id": "toolu_01BA4EfiB7DnDBCdqELGWh3o", "input": "{\"pattern\": \"**/*.py\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-api-client/gooddata_api_client/api\"}", "truncated": false}]} +{"ts": 1778658160.3073556, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01BA4EfiB7DnDBCdqELGWh3o", "is_error": true, "content": "Directory does not exist: /home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-api-client/gooddata_api_client/api. Note: your current working directory is /home/runner/_work/gdc-nas/gdc-nas/sdk.", "truncated": false}]} +{"ts": 1778658162.089768, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Glob", "id": "toolu_01GBmF2winZ84VfZQW26aYvM", "input": "{\"pattern\": \"**/layout_api.py\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk\"}", "truncated": false}]} +{"ts": 1778658162.1251004, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01GBmF2winZ84VfZQW26aYvM", "is_error": false, "content": "gooddata-api-client/build/lib/gooddata_api_client/api/layout_api.py\ngooddata-api-client/gooddata_api_client/api/layout_api.py\npackages/gooddata-sdk/.tox/py312/lib/python3.12/site-packages/gooddata_api_client/api/layout_api.py", "truncated": false}]} +{"ts": 1778658164.6073005, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Bash", "id": "toolu_01X7Ey5BHsov4mreAgWLbC2L", "input": "{\"command\": \"python3 -c \\\"import re; content=open('/home/runner/_work/gdc-nas/gdc-nas/sdk/gooddata-api-client/gooddata_api_client/api/layout_api.py').read(); matches=[m.group(1) for m in re.finditer(r'def (set_\\\\w+|put_\\\\w+|patch_\\\\w+)', content)]; print('\\\\n'.join(matches))\\\"\"}", "truncated": false}]} +{"ts": 1778658164.6417167, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01X7Ey5BHsov4mreAgWLbC2L", "is_error": true, "content": "Blocked: python3 -c \"import re; content=open('/home/runner/_work/gdc-nas/gdc-nas/sdk/good", "truncated": false}]} +{"ts": 1778658167.1447573, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Grep", "id": "toolu_01KD4BWxCueH82oN5dFztDgH", "input": "{\"pattern\": \"def set_|def put_|def patch_\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/gooddata-api-client/gooddata_api_client/api/layout_api.py\", \"output_mode\": \"content\"}", "truncated": false}]} +{"ts": 1778658167.187173, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01KD4BWxCueH82oN5dFztDgH", "is_error": false, "content": "4514: def put_data_source_statistics(\n4601: def put_data_sources_layout(\n4684: def put_user_groups_layout(\n4767: def put_users_layout(\n4850: def put_users_user_groups_layout(\n4933: def put_workspace_layout(\n5020: def set_agents_layout(\n5103: def set_analytics_model(\n5190: def set_automations(\n5277: def set_custom_geo_collections(\n5360: def set_data_source_permissions(\n5447: def set_export_templates(\n5530: def set_filter_views(\n5617: def set_identity_providers(\n5700: def set_logical_model(\n5787: def set_notification_channels(\n5870: def set_organization_layout(\n5953: def set_organization_permissions(\n6036: def set_user_data_filters(\n6123: def set_user_group_permissions(\n6210: def set_user_permissions(\n6297: def set_workspace_data_filters_layout(\n6380: def set_workspace_permissions(\n6467: def set_workspaces_layout(", "truncated": false}]} +{"ts": 1778658181.0075648, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "I don't see a specific `set_ip_allowlist_policies` or similar method in the layout API. The only option available is `set_organization_layout` which sets the full organization layout.\n\nLet me look at the error more carefully. The issue is that when getting the org layout, the datasource passwords are masked/not returned. Then when PUT-ing back the full layout, the API rejects it because a datasource (`demo-test-ds`) has a username but no password.\n\nWait, but the error is in the `put_declarative_ip_allowlist_policies` method calling `set_organization_layout`. The original test logic:\n\n\n1. Get original_policies\n2. Try: put new_policies \u2192 assert result \u2192 put original_policies back\n3. The PUT in the finally block fails because the org layout contains sensitive credentials that got masked when fetching\n\nThe problem is clear now: when I fetch the organization layout, datasource passwords are redacted, so when I try to restore the original layout, the API sees a datasource with a username but no password and rejects it. I need to find how other functions handle this\u2014maybe there's an exclude_sensitive parameter or some other workaround for dealing with masked credentials. When you then PUT that back, the API says \"invalid combination\".\n\nBut wait - this should also happen in `put_declarative_ip_allowlist_policies` in the `try` block, not just the `finally` block. Let me re-read the error:\n\n```\ntests/catalog/test_catalog_organization.py:600: in test_layout_ip_allowlist_policies\n sdk.catalog_organization.put_declarative_ip_allowlist_policies(original_policies)\n.tox/py312/lib/python3.12/site-packages/gooddata_sdk/catalog/organization/service.py:777: in put_declarative_ip_allowlist_policies\n self._layout_api.set_organization_layout(org_layout, _check_return_type=False)\n```\n\nThe error is in line 600, which is the `finally` block - restoring the original_policies. But the same PUT is used in line 590 as well... The assertion at line 592 passes, which means the first call suc", "truncated": true}]} +{"ts": 1778658181.0142188, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Glob", "id": "toolu_0118zDCo9qhr62ZeF1aC9Ebz", "input": "{\"pattern\": \"**/layout_ip_allowlist_policies.yaml\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk\"}", "truncated": false}]} +{"ts": 1778658181.0457087, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_0118zDCo9qhr62ZeF1aC9Ebz", "is_error": false, "content": "packages/gooddata-sdk/tests/catalog/fixtures/organization/layout_ip_allowlist_policies.yaml", "truncated": false}]} +{"ts": 1778658182.6371295, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01HU4nZbWrf4CkP18wnmRe86", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/fixtures/organization/layout_ip_allowlist_policies.yaml\"}", "truncated": false}]} +{"ts": 1778658182.6715636, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01HU4nZbWrf4CkP18wnmRe86", "is_error": true, "content": "File content (927.3KB) exceeds maximum allowed size (256KB). Use offset and limit parameters to read specific portions of the file, or search for specific content instead of reading the whole file.", "truncated": false}]} +{"ts": 1778658184.659972, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01T9S4ijN6CtxzbQoH3gmWpw", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/fixtures/organization/layout_ip_allowlist_policies.yaml\", \"offset\": 1, \"limit\": 50}", "truncated": false}]} +{"ts": 1778658184.6903453, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01T9S4ijN6CtxzbQoH3gmWpw", "is_error": false, "content": "1\tinteractions:\n2\t - request:\n3\t body: null\n4\t headers:\n5\t Accept:\n6\t - application/json\n7\t Accept-Encoding:\n8\t - br, gzip, deflate\n9\t X-GDC-VALIDATE-RELATIONS:\n10\t - 'true'\n11\t X-Requested-With:\n12\t - XMLHttpRequest\n13\t method: GET\n14\t uri: http://localhost:3000/api/v1/layout/organization\n15\t response:\n16\t body:\n17\t string:\n18\t agents:\n19\t - aiKnowledge: true\n20\t availableToAll: true\n21\t enabled: true\n22\t id: default-ai-assistant\n23\t name: Default GoodData AI Assistant\n24\t skillsMode: all\n25\t customGeoCollections: []\n26\t dataSources:\n27\t - alternativeDataSourceId: ds-put-abc-id\n28\t authenticationType: USERNAME_PASSWORD\n29\t id: demo-test-ds\n30\t name: demo-test-ds\n31\t permissions:\n32\t - assignee:\n33\t id: demo2\n34\t type: user\n35\t name: MANAGE\n36\t - assignee:\n37\t id: demoGroup\n38\t type: userGroup\n39\t name: USE\n40\t schema: demo\n41\t type: POSTGRESQL\n42\t url: jdbc:postgresql://postgres:5432/tiger?sslmode=prefer\n43\t username: postgres\n44\t exportTemplates: []\n45\t identityProviders:\n46\t - id: defaultIdp\n47\t idpType: DEX_IDP\n48\t ipAllowlistPolicies: []\n49\t jwks: []\n50\t notificationChannels: []", "truncated": false}]} +{"ts": 1778658190.084132, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "Now I understand the issue! Looking at the cassette, the organization layout response contains a datasource `demo-test-ds` with `authenticationType: USERNAME_PASSWORD` and `username: postgres`, but no password (it's not in the cassette response). This is the \"masked\" credential.\n\nSo when `put_declarative_ip_allowlist_policies` does:\n1. `get_organization_layout` \u2192 returns layout with datasource that has username but no password\n2. Sets `ip_allowlist_policies` on the layout\n\n\n3. `set_organization_layout(org_layout)` \u2192 400 because username without password\n\nI need to check what interactions are actually recorded in the cassette to see if there's a workaround or if I'm missing something about how the credentials are being handled.", "truncated": false}]} +{"ts": 1778658190.0924082, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Grep", "id": "toolu_019dGrhVYzfWzvdLawmAS9oh", "input": "{\"pattern\": \"method: (GET|POST|PUT|DELETE|PATCH)\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/fixtures/organization/layout_ip_allowlist_policies.yaml\", \"output_mode\": \"content\"}", "truncated": false}]} +{"ts": 1778658190.1181717, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_019dGrhVYzfWzvdLawmAS9oh", "is_error": false, "content": "13: method: GET\n2158: method: GET\n6425: method: PUT\n6462: method: GET\n10719: method: PUT\n10756: method: GET\n12900: method: GET\n17167: method: PUT\n17204: method: GET\n21461: method: PUT", "truncated": false}]} +{"ts": 1778658196.117602, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "Let me look at the structure of the cassette interactions. It seems to have:\n1. GET (line 13)\n2. GET (line 2158)\n3. PUT (line 6425)\n4. GET (line 6462)\n5. PUT (line 10719)\n6. GET (line 10756)\n7. GET (line 12900)\n8. PUT (line 17167)\n9. GET (line 17204)\n10. PUT (line 21461)\n\nThe test flow is:\n1. `get_declarative_ip_allowlist_policies()` \u2192 calls `get_organization_layout` (GET #1)\n\n\n2. `put_declarative_ip_allowlist_policies(new_policies)` \u2192 calls `get_organization_layout` (GET) + `set_organization_layout` (PUT)\n3. `get_declarative_ip_allowlist_policies()` \u2192 calls `get_organization_layout` (GET)\n4. `put_declarative_ip_allowlist_policies(original_policies)` \u2192 calls `get_organization_layout` (GET) + `set_organization_layout` (PUT)\n\nThe cassette has more GETs and PUTs than expected, so I should check the actual URIs to understand what's happening\u2014there might be retries or additional calls I'm not accounting for.", "truncated": false}]} +{"ts": 1778658196.126212, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Grep", "id": "toolu_01UffKXjD6ojk4Js4PZbXHrD", "input": "{\"pattern\": \"method:|uri:\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/fixtures/organization/layout_ip_allowlist_policies.yaml\", \"output_mode\": \"content\"}", "truncated": false}]} +{"ts": 1778658196.1511416, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01UffKXjD6ojk4Js4PZbXHrD", "is_error": false, "content": "13: method: GET\n14: uri: http://localhost:3000/api/v1/layout/organization\n2158: method: GET\n2159: uri: http://localhost:3000/api/v1/layout/organization\n6425: method: PUT\n6426: uri: http://localhost:3000/api/v1/layout/organization\n6462: method: GET\n6463: uri: http://localhost:3000/api/v1/layout/organization\n10719: method: PUT\n10720: uri: http://localhost:3000/api/v1/layout/organization\n10756: method: GET\n10757: uri: http://localhost:3000/api/v1/layout/organization\n12900: method: GET\n12901: uri: http://localhost:3000/api/v1/layout/organization\n17167: method: PUT\n17168: uri: http://localhost:3000/api/v1/layout/organization\n17204: method: GET\n17205: uri: http://localhost:3000/api/v1/layout/organization\n21461: method: PUT\n21462: uri: http://localhost:3000/api/v1/layout/organization", "truncated": false}]} +{"ts": 1778658222.6353385, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "So the cassette has 10 interactions, all to `http://localhost:3000/api/v1/layout/organization`. The expected flow should be:\n\nTest flow with current code:\n1. `get_declarative_ip_allowlist_policies()` \u2192 GET org_layout\n2. `put_declarative_ip_allowlist_policies(new_policies)` \u2192 GET org_layout + PUT org_layout\n3. `get_declarative_ip_allowlist_policies()` \u2192 GET org_layout\n\n\n4. `put_declarative_ip_allowlist_policies(original_policies)` \u2192 GET org_layout + PUT org_layout\n\nThat's 4 GETs and 2 PUTs for 6 total interactions, but the cassette has 10. The test is using `@gd_vcr.use_cassette` which should play back all recorded interactions, but it's failing because the current code isn't making enough requests to consume them all.\n\nLooking at the error, the test is hitting a live server with a 400 error instead of using the cassette. The cassette should intercept the `set_organization_layout` call, but it seems like the cassette is running out of interactions\u2014if the code makes more requests than what's recorded, the VCR library will fall back to making actual HTTP requests.\n\nLet me count the interactions in the cassette more carefully. I'm seeing a mix of GETs and PUTs recorded, but I need to verify the exact count and whether the cassette was recorded with a different implementation than what's currently running.\n\nThe key issue is that the code is making a real HTTP request to localhost:3000 and getting a 400 error, which means the cassette has run out of recorded interactions. So the cassette must have fewer interactions than what the current code flow is attempting to make. Looking at the actual implementation, `put_declarative_ip_allowlist_policies` does a GET to fetch the org layout, modifies the policies, then PUTs it back. The test calls `get_declarative_ip_allowlist_policies` first, then `put_declarative_ip_allowlist_policies`, so I need to trace through exactly how many HTTP calls that generates and compare it to what's recorded in the cassette. The error trace shows th", "truncated": true}]} +{"ts": 1778658222.6404736, "type": "assistant", "blocks": [{"type": "text", "text": "Let me look at the installed service.py to understand the version being tested:", "truncated": false}]} +{"ts": 1778658222.6771126, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Bash", "id": "toolu_01K3EFc9L2qcC9a7MrYT6P2y", "input": "{\"command\": \"wc -l /home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/.tox/py312/lib/python3.12/site-packages/gooddata_sdk/catalog/organization/service.py 2>/dev/null && tail -30 /home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/.tox/py312/lib/python3.12/site-packages/gooddata_sdk/catalog/organization/service.py 2>/dev/null || echo \\\"not accessible\\\"\"}", "truncated": false}]} +{"ts": 1778658222.716143, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01K3EFc9L2qcC9a7MrYT6P2y", "is_error": true, "content": "Blocked: wc -l /home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/.tox/py312/li", "truncated": false}]} +{"ts": 1778658224.7530277, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01VGCyVELFXPJdTm1MsxBcBQ", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/src/gooddata_sdk/catalog/organization/service.py\", \"offset\": 740, \"limit\": 50}", "truncated": false}]} +{"ts": 1778658224.7838168, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01VGCyVELFXPJdTm1MsxBcBQ", "is_error": false, "content": "740\t except NotFoundException:\n741\t raise ValueError(\n742\t f\"Cannot switch to identity provider {identity_provider_id}. This identity provider does not exist.\"\n743\t )\n744\t except Exception as e:\n745\t raise ValueError(f\"Error switching active identity provider: {str(e)}\")\n746\t\n747\t def get_declarative_ip_allowlist_policies(self) -> list[CatalogDeclarativeIpAllowlistPolicy]:\n748\t \"\"\"Get all declarative IP allowlist policies for the current organization.\n749\t\n750\t IP allowlist policies are returned as part of the full organization layout.\n751\t\n752\t Returns:\n753\t list[CatalogDeclarativeIpAllowlistPolicy]:\n754\t List of declarative IP allowlist policies.\n755\t \"\"\"\n756\t org_layout = self._layout_api.get_organization_layout(_check_return_type=False)\n757\t policies = getattr(org_layout, \"ip_allowlist_policies\", None) or []\n758\t return [CatalogDeclarativeIpAllowlistPolicy.from_api(policy) for policy in policies]\n759\t\n760\t def put_declarative_ip_allowlist_policies(\n761\t self, ip_allowlist_policies: list[CatalogDeclarativeIpAllowlistPolicy]\n762\t ) -> None:\n763\t \"\"\"Put declarative IP allowlist policies for the current organization.\n764\t\n765\t Reads the full organization layout, replaces the ip_allowlist_policies field,\n766\t and writes the full layout back. All other organization settings are preserved.\n767\t\n768\t Args:\n769\t ip_allowlist_policies (list[CatalogDeclarativeIpAllowlistPolicy]):\n770\t List of declarative IP allowlist policies to set.\n771\t\n772\t Returns:\n773\t None\n774\t \"\"\"\n775\t org_layout = self._layout_api.get_organization_layout(_check_return_type=False)\n776\t org_layout.ip_allowlist_policies = [policy.to_api() for policy in ip_allowlist_policies]\n777\t self._layout_api.set_organization_layout(org_layout, _check_return_type=Fa", "truncated": true}]} +{"ts": 1778658234.424368, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "So the current implementation reads the full org layout and writes it back. The cassette was recorded with 10 interactions: GET, GET, PUT, GET, PUT, GET, GET, PUT, GET, PUT.\n\nThe test flow with current code makes:\n1. `get_declarative_ip_allowlist_policies()` \u2192 GET #1 (line 574)\n2. `put_declarative_ip_allowlist_policies(new_policies)` \u2192 GET #2 + PUT #3 (line 590) \n3. `get_declarative_ip_allowlist_policies()` \u2192 GET #4 (line 591)\n\n\n4. `put_declarative_ip_allowlist_policies(original_policies)` \u2192 GET #5 + PUT #6 (line 600)\n\nThat's 6 interactions so far, but the cassette has 10 total. Looking at the actual recorded sequence, I see it's GET, GET, PUT, GET, PUT, GET, GET, PUT, GET, PUT \u2014 so there are more interactions happening than what my current code path accounts for. The test must be failing because it's trying to make a 7th call that doesn't match what's recorded next in the cassette.\n\nLet me trace through what the code actually does: first GET for the initial policy check, then GET inside the put operation for the new policies, PUT to set those, then GET for the result check, then GET inside put for the original policies, and PUT to restore them. That's 4 GETs and 2 PUTs. But the cassette shows a different pattern \u2014 maybe the original implementation was structured differently, or there's additional setup happening in conftest that's adding extra calls. Let me check the conftest teardown code to see if that's where the extra interactions are coming from.", "truncated": false}]} +{"ts": 1778658234.441746, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01JMb6mizVUGhzjPMp6VSfGR", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/conftest.py\", \"offset\": 270, \"limit\": 50}", "truncated": false}]} +{"ts": 1778658234.470523, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01JMb6mizVUGhzjPMp6VSfGR", "is_error": false, "content": "270\t logger.error(f\"Snapshot capture failed [notification_channels]: {e}\")\n271\t\n272\t # Per-workspace permissions\n273\t ws_permissions = {}\n274\t try:\n275\t for ws in sdk.catalog_workspace.list_workspaces():\n276\t try:\n277\t ws_permissions[ws.id] = sdk.catalog_permission.get_declarative_permissions(ws.id)\n278\t except Exception as e: # noqa: PERF203\n279\t logger.warning(f\"Could not capture permissions for workspace {ws.id}: {e}\")\n280\t except Exception as e:\n281\t logger.error(f\"Snapshot capture failed [workspace list for permissions]: {e}\")\n282\t snapshot[\"ws_permissions\"] = ws_permissions\n283\t\n284\t logger.info(\"Session snapshot captured\")\n285\t\n286\t # --- Tests run ---\n287\t yield snapshot\n288\t\n289\t # --- Restore (only if failures) ---\n290\t if not _session_has_failures:\n291\t logger.info(\"All tests passed \u2014 skipping session restore\")\n292\t return\n293\t\n294\t logger.warning(\"Test failures detected \u2014 restoring session snapshot...\")\n295\t\n296\t # Restore order matters: users/groups \u2192 data sources \u2192 workspaces \u2192\n297\t # workspace data filters \u2192 org permissions \u2192 notification channels \u2192\n298\t # per-workspace permissions\n299\t\n300\t if \"users_groups\" in snapshot:\n301\t try:\n302\t sdk.catalog_user.put_declarative_users_user_groups(snapshot[\"users_groups\"])\n303\t logger.info(\"Restored [users_groups]\")\n304\t except Exception as e:\n305\t logger.error(f\"RESTORE FAILED [users_groups]: {e}\")\n306\t\n307\t if \"data_sources\" in snapshot:\n308\t try:\n309\t sdk.catalog_data_source.put_declarative_data_sources(snapshot[\"data_sources\"], _credentials_path)\n310\t logger.info(\"Restored [data_sources]\")\n311\t except Exception as e:\n312\t logger.error(f\"RESTORE FAILED [data_sources]: {e}\")\n313\t\n314\t if \"workspaces\" in snapshot:\n315\t try:\n316\t sdk.catalog_workspace.put_declarativ", "truncated": true}]} +{"ts": 1778658236.2565567, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_011uS2rptuuXvsMjXcXpD6mj", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/conftest.py\", \"offset\": 315, \"limit\": 80}", "truncated": false}]} +{"ts": 1778658236.2868376, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_011uS2rptuuXvsMjXcXpD6mj", "is_error": false, "content": "315\t try:\n316\t sdk.catalog_workspace.put_declarative_workspaces(snapshot[\"workspaces\"])\n317\t logger.info(\"Restored [workspaces]\")\n318\t except Exception as e:\n319\t logger.error(f\"RESTORE FAILED [workspaces]: {e}\")\n320\t\n321\t if \"workspace_data_filters\" in snapshot:\n322\t try:\n323\t sdk.catalog_workspace.put_declarative_workspace_data_filters(snapshot[\"workspace_data_filters\"])\n324\t logger.info(\"Restored [workspace_data_filters]\")\n325\t except Exception as e:\n326\t logger.error(f\"RESTORE FAILED [workspace_data_filters]: {e}\")\n327\t\n328\t if \"org_permissions\" in snapshot:\n329\t try:\n330\t sdk.catalog_permission.put_declarative_organization_permissions(snapshot[\"org_permissions\"])\n331\t logger.info(\"Restored [org_permissions]\")\n332\t except Exception as e:\n333\t logger.error(f\"RESTORE FAILED [org_permissions]: {e}\")\n334\t\n335\t if \"notification_channels\" in snapshot:\n336\t try:\n337\t sdk.catalog_organization.put_declarative_notification_channels(snapshot[\"notification_channels\"])\n338\t logger.info(\"Restored [notification_channels]\")\n339\t except Exception as e:\n340\t logger.error(f\"RESTORE FAILED [notification_channels]: {e}\")\n341\t\n342\t for ws_id, perms in snapshot.get(\"ws_permissions\", {}).items():\n343\t try:\n344\t sdk.catalog_permission.put_declarative_permissions(ws_id, perms)\n345\t logger.info(f\"Restored [permissions for {ws_id}]\")\n346\t except Exception as e: # noqa: PERF203\n347\t logger.warning(f\"RESTORE FAILED [permissions for {ws_id}]: {e}\")\n348\t", "truncated": false}]} +{"ts": 1778658246.526303, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "I see. The conftest doesn't have an IP allowlist policies restore. The conftest teardown runs after the test and also under the session fixture, so it might make additional calls.\n\nBut wait, the problem is more fundamental. The cassette was recorded with a specific sequence of interactions. The issue is that the cassette was recorded with a different implementation (perhaps one that uses `put_ip_allowlist_policies` as a separate API call, or makes fewer GET calls). \n\nBut now the implementation uses `get_organization_layout` + `set_organization_layout` for both GET and PUT operations, and each PUT makes an extra GET, consuming more cassette entries than were expected.\n \nLet me trace through the actual sequence: the test makes a GET, then a PUT (which itself does a GET first), then another GET, then a final PUT with its own GET. That's six total interactions with the cassette, but there are ten recorded, so there's room for the conftest teardown and other operations before we hit the limit.\n\nThe key question is how VCR is configured\u2014if it's in \"none\" mode, it'll error on any request beyond what's recorded, but if it's in \"new_episodes\" mode, it'll make real HTTP calls for the extras. I need to check the actual VCR configuration in `gd_vcr`.", "truncated": false}]} +{"ts": 1778658246.5416713, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Glob", "id": "toolu_01F7a81P6WefP9mdRDDLg6KL", "input": "{\"pattern\": \"**/conftest.py\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests\"}", "truncated": false}]} +{"ts": 1778658246.5686505, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01F7a81P6WefP9mdRDDLg6KL", "is_error": false, "content": "packages/gooddata-sdk/tests/catalog/conftest.py\npackages/gooddata-sdk/tests/conftest.py", "truncated": false}]} +{"ts": 1778658248.1426349, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01KfETFGTut6coarZjFGTKfu", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/conftest.py\"}", "truncated": false}]} +{"ts": 1778658248.1890478, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01KfETFGTut6coarZjFGTKfu", "is_error": false, "content": "1\t# (C) 2026 GoodData Corporation\n2\t\"\"\"\n3\tShared test fixtures for catalog tests.\n4\t\n5\tProvides:\n6\t- safe_delete: cleanup utility that swallows exceptions\n7\t- sdk: session-scoped GoodDataSdk instance\n8\t- Snapshot/restore fixtures for staging-safe testing\n9\t\"\"\"\n10\t\n11\tfrom __future__ import annotations\n12\t\n13\timport copy\n14\timport logging\n15\tfrom pathlib import Path\n16\tfrom typing import Any, Callable\n17\t\n18\timport pytest\n19\tfrom gooddata_sdk import CatalogDeclarativeDataSources, GoodDataSdk\n20\tfrom tests_support.file_utils import load_json\n21\t\n22\tlogger = logging.getLogger(__name__)\n23\t\n24\t_current_dir = Path(__file__).parent.absolute()\n25\t_credentials_path = _current_dir / \"load\" / \"data_source_credentials\" / \"data_sources_credentials.yaml\"\n26\t\n27\t\n28\tdef _capture_workspace_permissions(sdk: GoodDataSdk, workspaces: list) -> dict:\n29\t \"\"\"Capture permissions for all workspaces, skipping failures.\"\"\"\n30\t ws_permissions: dict = {}\n31\t for ws in workspaces:\n32\t try:\n33\t ws_permissions[ws.id] = sdk.catalog_permission.get_declarative_permissions(ws.id)\n34\t except Exception as e: # noqa: PERF203\n35\t logger.warning(f\"Could not capture permissions for workspace {ws.id}: {e}\")\n36\t return ws_permissions\n37\t\n38\t\n39\tdef _restore_workspace_permissions(sdk: GoodDataSdk, ws_permissions: dict) -> None:\n40\t \"\"\"Restore permissions for all workspaces, logging failures.\"\"\"\n41\t for ws_id, perms in ws_permissions.items():\n42\t try:\n43\t sdk.catalog_permission.put_declarative_permissions(ws_id, perms)\n44\t except Exception as e: # noqa: PERF203\n45\t logger.warning(f\"RESTORE FAILED [permissions for {ws_id}]: {e}\")\n46\t\n47\t\n48\tdef safe_delete(func: Callable, *args: Any, **kwargs: Any) -> None:\n49\t \"\"\"Call a cleanup function, swallowing any exception so it doesn't mask test failures.\n50\t\n51\t Use this in `finally` blocks instead of bare cleanup calls.\n52\t \"\"\"\n53\t try:\n54\t func(*args, **kwa", "truncated": true}]} +{"ts": 1778658251.9786317, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "The test `test_layout_ip_allowlist_policies` uses only `@gd_vcr.use_cassette` decorator and `test_config` fixture. It doesn't use snapshot fixtures. Let me check the VCR configuration.", "truncated": false}]} +{"ts": 1778658253.1924894, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Glob", "id": "toolu_01TNM6R6FtYh2s2RzQ1F6FU1", "input": "{\"pattern\": \"**/conftest.py\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk\"}", "truncated": false}]} +{"ts": 1778658253.562414, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01TNM6R6FtYh2s2RzQ1F6FU1", "is_error": false, "content": "packages/gooddata-sdk/tests/catalog/conftest.py\npackages/gooddata-sdk/tests/conftest.py\npackages/gooddata-sdk/.tox/py312/lib/python3.12/site-packages/pyarrow/conftest.py\npackages/gooddata-sdk/.tox/py312/lib/python3.12/site-packages/pyarrow/tests/conftest.py\npackages/gooddata-sdk/.tox/py312/lib/python3.12/site-packages/pyarrow/tests/parquet/conftest.py", "truncated": false}]} +{"ts": 1778658254.0934076, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Grep", "id": "toolu_01AAeVjxQiN62YjH96GXXKo1", "input": "{\"pattern\": \"gd_vcr\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/test_catalog_organization.py\", \"output_mode\": \"content\", \"head_limit\": 10}", "truncated": false}]} +{"ts": 1778658254.3219137, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01AAeVjxQiN62YjH96GXXKo1", "is_error": false, "content": "23:gd_vcr = get_vcr()\n68:@gd_vcr.use_cassette(str(_fixtures_dir / \"organization.yaml\"))\n75:@gd_vcr.use_cassette(str(_fixtures_dir / \"update_name.yaml\"))\n93:@gd_vcr.use_cassette(str(_fixtures_dir / \"create_jwk.yaml\"))\n106:@gd_vcr.use_cassette(str(_fixtures_dir / \"update_jwk.yaml\"))\n120:@gd_vcr.use_cassette(str(_fixtures_dir / \"delete_jwk.yaml\"))\n134:@gd_vcr.use_cassette(str(_fixtures_dir / \"list_jwk.yaml\"))\n150:@gd_vcr.use_cassette(str(_fixtures_dir / \"create_organization_setting.yaml\"))\n168:@gd_vcr.use_cassette(str(_fixtures_dir / \"list_organization_settings.yaml\"))\n190:@gd_vcr.use_cassette(str(_fixtures_dir / \"delete_organization_setting.yaml\"))\n\n[Showing results with pagination = limit: 10]", "truncated": false}]} +{"ts": 1778658256.498675, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Glob", "id": "toolu_017RSgPSMJAXmEVJpRPzXnJu", "input": "{\"pattern\": \"**/vcr_utils.py\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk\"}", "truncated": false}]} +{"ts": 1778658256.9184864, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_017RSgPSMJAXmEVJpRPzXnJu", "is_error": false, "content": "No files found", "truncated": false}]} +{"ts": 1778658256.9246876, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Grep", "id": "toolu_01Y1kwbyQyAoFFC8YUYhJ18D", "input": "{\"pattern\": \"def get_vcr\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk\", \"output_mode\": \"content\"}", "truncated": false}]} +{"ts": 1778658256.9713993, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01Y1kwbyQyAoFFC8YUYhJ18D", "is_error": false, "content": "transcripts/fix-C009-attempt2.jsonl:72:{\"ts\": 1778658256.9246876, \"type\": \"assistant\", \"blocks\": [{\"type\": \"tool_use\", \"name\": \"Grep\", \"id\": \"toolu_01Y1kwbyQyAoFFC8YUYhJ18D\", \"input\": \"{\\\"pattern\\\": \\\"def get_vcr\\\", \\\"path\\\": \\\"/home/runner/_work/gdc-nas/gdc-nas/sdk\\\", \\\"output_mode\\\": \\\"content\\\"}\", \"truncated\": false}]}\ntranscripts/fix-C009-attempt1.jsonl:96:{\"ts\": 1778657977.2148771, \"type\": \"assistant\", \"blocks\": [{\"type\": \"tool_use\", \"name\": \"Grep\", \"id\": \"toolu_01R2USTzhRSs9TeMxsUQNfa5\", \"input\": \"{\\\"pattern\\\": \\\"def get_vcr|match_on|record_mode\\\", \\\"path\\\": \\\"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests\\\", \\\"output_mode\\\": \\\"content\\\", \\\"head_limit\\\": 30}\", \"truncated\": false}]}\npackages/tests-support/src/tests_support/vcrpy_utils.py:329:def get_vcr() -> vcr.VCR:", "truncated": false}]} +{"ts": 1778658260.2893312, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01XSpMjGiHhEjru6jTAtfFM7", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/tests-support/src/tests_support/vcrpy_utils.py\", \"offset\": 325, \"limit\": 40}", "truncated": false}]} +{"ts": 1778658260.3942592, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01XSpMjGiHhEjru6jTAtfFM7", "is_error": false, "content": "325\tdef _noop_matcher(r1: Any, r2: Any) -> None:\n326\t \"\"\"Always matches \u2014 used to disable scheme/host/port matching across environments.\"\"\"\n327\t\n328\t\n329\tdef get_vcr() -> vcr.VCR:\n330\t gd_vcr = vcr.VCR(\n331\t filter_headers=[\"authorization\", \"user-agent\"],\n332\t match_on=VCR_MATCH_ON,\n333\t before_record_request=custom_before_request,\n334\t before_record_response=custom_before_response,\n335\t decode_compressed_response=True,\n336\t record_mode=RecordMode.ALL if \"OVERWRITE\" in os.environ else RecordMode.ONCE,\n337\t )\n338\t gd_vcr.register_matcher(\"body\", _body_matcher)\n339\t # Cassettes recorded against staging must replay in CI (localhost).\n340\t # Disable scheme/host/port matching \u2014 path is sufficient.\n341\t for matcher in (\"scheme\", \"host\", \"port\"):\n342\t gd_vcr.register_matcher(matcher, _noop_matcher)\n343\t\n344\t gd_vcr.register_serializer(\"custom\", CustomSerializerYaml())\n345\t gd_vcr.serializer = \"custom\"\n346\t return gd_vcr\n347\t\n348\t\n349\tdef _stabilize_hash_placeholders(cassette_dict: dict[str, Any]) -> dict[str, str]:\n350\t \"\"\"Build a rename map for hash placeholders based on export format context.\n351\t\n352\t During recording, execution/export hashes get session-global numeric indices\n353\t (EXECUTION_NORMALIZED_3, EXPORT_NORMALIZED_5). These indices shift when test\n354\t execution order changes, causing spurious cassette diffs.\n355\t\n356\t This function scans the cassette for export context (POST /export/tabular with\n357\t a ``format`` field) and returns a mapping from old placeholder names to stable,\n358\t format-based names (e.g. EXECUTION_NORMALIZED_CSV, EXPORT_NORMALIZED_XLSX).\n359\t\n360\t Non-export hashes (pure compute results) are re-indexed per-cassette starting\n361\t from 1.\n362\t \"\"\"\n363\t exec_renames: dict[str, str] = {} # old placeholder \u2192 new name\n364\t export_renames: dict[str, str] = {}", "truncated": false}]} +{"ts": 1778658269.7868629, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "So the record mode is `ONCE` (unless `OVERWRITE` is in env). In \"ONCE\" mode, VCR.py records the cassette the first time (when it doesn't exist), and plays it back afterwards. In playback mode, if there are no more interactions in the cassette, VCR.py raises a `CannotSendRequest` error (in vcrpy), not a 400 from a live server.\n\nBut the error we're seeing is a real 400 from the live server, not a `CannotSendRequest`. This is strange...\n\n\nActually, the cassette file does exist, and it's returning a real 400 response. That means the cassette has a 400 status recorded for one of the PUT requests in this test. Let me check what's actually stored in the cassette entries.\n\nI'm noticing the cassette has 10 interactions total, with the last PUT request at line 21461, but the test should only be making 6 requests. I need to understand how VCR_MATCH_ON works to see if those extra interactions are being consumed or if there's a mismatch in how requests are being matched.", "truncated": false}]} +{"ts": 1778658269.8061934, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Grep", "id": "toolu_014oHXhbomFc64cQKWwiEAvg", "input": "{\"pattern\": \"VCR_MATCH_ON\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/tests-support/src/tests_support/vcrpy_utils.py\", \"output_mode\": \"content\"}", "truncated": false}]} +{"ts": 1778658269.821447, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_014oHXhbomFc64cQKWwiEAvg", "is_error": false, "content": "16:VCR_MATCH_ON = (\"method\", \"path\", \"query\", \"body\")\n332: match_on=VCR_MATCH_ON,", "truncated": false}]} +{"ts": 1778658298.3136282, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "So it matches on `method`, `path`, `query`, `body`. This means that each request is matched based on these attributes. If there are multiple matching requests (same method, path, query, body), VCR.py will use the FIRST unplayed one.\n\nFor `set_organization_layout`, the body is the full org layout JSON. So two PUT requests with the same body will both match the same cassette entry. That should work fine.\n\nBut wait - the test is failing with a 400 error. Since VCR.py intercepts requests and plays back cassette responses, the 400 must be IN the cassette as a recorded response.\n \nActually, looking at the error more closely, the request is hitting a real server - the response headers include `X-Gdc-Trace-Id`, which means VCR.py isn't intercepting it. This could happen if the cassette is in ONCE mode and the PUT request body doesn't match any existing cassette entry, causing VCR.py to fall through to a real request. Or the cassette entries have been exhausted and it's allowing fallthrough.\n\nBut wait, there's something I'm missing here. The error message says \"During handling of the above exception\" - there was already an exception before this one. The pytest output we're seeing starts partway through, showing a 400 response with a trace ID. I need to look at the full output to understand what the initial exception was.\n\nLooking more carefully, I can see there are actually TWO different trace IDs in the errors - one ending in `889ab95` from the first exception, and another `ce58692` from a second exception that occurred during cleanup. Both are real 400 responses from the live server, which means both PUT requests are hitting the actual server instead of being matched by the cassette.\n\nLet me trace through what the test is actually doing. It calls `get_declarative_ip_allowlist_policies()` which internally calls `get_organization_layout`, then it calls `put_declarative_ip_allowlist_policies()` with new policies, which also calls `get_organization_layout`. The cassette has 10", "truncated": true}]} +{"ts": 1778658298.32708, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01XmFsctPWXUrbSB5bKzjhC4", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/conftest.py\", \"offset\": 1, \"limit\": 100}", "truncated": false}]} +{"ts": 1778658298.3988633, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01XmFsctPWXUrbSB5bKzjhC4", "is_error": false, "content": "1\t# (C) 2022 GoodData Corporation\n2\timport logging\n3\timport os\n4\tfrom pathlib import Path\n5\tfrom unittest import mock\n6\t\n7\timport pytest\n8\timport yaml\n9\tfrom gooddata_sdk import GoodDataSdk\n10\tfrom gooddata_sdk.catalog.data_source.service import CatalogDataSourceService\n11\tfrom tests_support.vcrpy_utils import configure_normalization\n12\t\n13\tlogger = logging.getLogger(__name__)\n14\t\n15\t_current_dir = Path(__file__).parent.absolute()\n16\t_credentials_path = _current_dir / \"catalog\" / \"load\" / \"data_source_credentials\" / \"data_sources_credentials.yaml\"\n17\t\n18\t# ---------------------------------------------------------------------------\n19\t# Failure tracker \u2014 used by session snapshot to decide whether to restore\n20\t# ---------------------------------------------------------------------------\n21\t_session_has_failures = False\n22\t\n23\t\n24\tdef pytest_runtest_makereport(item, call):\n25\t \"\"\"Track whether any test has failed during the session.\"\"\"\n26\t global _session_has_failures\n27\t if call.excinfo is not None:\n28\t _session_has_failures = True\n29\t\n30\t\n31\tdef pytest_addoption(parser):\n32\t default_config_path = Path(__file__).parent / \"gd_test_config.yaml\"\n33\t parser.addoption(\n34\t \"--gd-test-config\",\n35\t action=\"store\",\n36\t default=str(default_config_path),\n37\t help=\"Path to test configuration file\",\n38\t )\n39\t\n40\t\n41\t@pytest.fixture(scope=\"session\")\n42\tdef test_config(request):\n43\t config_path = Path(request.config.getoption(\"--gd-test-config\"))\n44\t with open(config_path) as f:\n45\t config = yaml.safe_load(f)\n46\t\n47\t # Merge environment-specific overrides into top-level config\n48\t env_name = os.environ.get(\"GD_TEST_ENV\", \"local\")\n49\t environments = config.pop(\"environments\", {})\n50\t if env_name in environments:\n51\t config.update(environments[env_name])\n52\t\n53\t # Override secrets from env vars (staging CI passes these)\n54\t env_token = os.environ.get(\"TOKEN\")\n55\t if env_token:\n56\t co", "truncated": true}]} +{"ts": 1778658301.0169902, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "I now see that VCR.py is set to `RecordMode.ONCE`, which means it plays back if the cassette exists. But the test is actually making real HTTP requests to the staging server. This is strange.\n\nActually, let me look at `configure_normalization` and check if there's a mode where VCR might be bypassed when running against staging:", "truncated": false}]} +{"ts": 1778658301.8107648, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_011FHvLeTGU9nzt51rHEoCy9", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/tests-support/src/tests_support/vcrpy_utils.py\", \"offset\": 1, \"limit\": 80}", "truncated": false}]} +{"ts": 1778658301.841822, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_011FHvLeTGU9nzt51rHEoCy9", "is_error": false, "content": "1\t# (C) 2022 GoodData Corporation\n2\tfrom __future__ import annotations\n3\t\n4\timport os\n5\timport re\n6\timport typing\n7\timport xml.etree.ElementTree as ET\n8\tfrom typing import Any\n9\tfrom urllib.parse import urlparse\n10\t\n11\timport orjson\n12\timport vcr\n13\timport yaml\n14\tfrom vcr.record_mode import RecordMode\n15\t\n16\tVCR_MATCH_ON = (\"method\", \"path\", \"query\", \"body\")\n17\tNON_STATIC_HEADERS = [\"DATE\", \"X-GDC-CANCEL-TOKEN\", \"X-GDC-TRACE-ID\"]\n18\t\n19\t# Infrastructure headers injected by reverse proxies / load balancers.\n20\t# These differ between local and staging and are irrelevant to API behavior.\n21\t_STRIPPED_RESPONSE_HEADERS = {\n22\t \"X-Xss-Protection\",\n23\t \"Strict-Transport-Security\",\n24\t \"Vary\",\n25\t \"Content-Encoding\",\n26\t \"Content-Length\",\n27\t \"Transfer-Encoding\",\n28\t \"Referrer-Policy\",\n29\t \"Cache-Control\",\n30\t}\n31\tHEADERS_STR = \"headers\"\n32\tPLACEHOLDER = [\"PLACEHOLDER\"]\n33\t\n34\t# Fields stripped from request bodies before VCR body matching.\n35\t# These differ between local and staging environments but don't affect\n36\t# the logical identity of a request.\n37\t_ENV_SPECIFIC_BODY_FIELDS = {\n38\t \"password\",\n39\t \"token\",\n40\t \"url\",\n41\t \"username\",\n42\t \"privateKey\",\n43\t \"client_secret\",\n44\t \"private_key_passphrase\",\n45\t}\n46\t\n47\t# Canonical (local) values \u2014 cassettes always use these.\n48\t_CANONICAL_HOST = \"http://localhost:3000\"\n49\t_CANONICAL_HOSTNAME = \"localhost\"\n50\t_CANONICAL_ORG_ID = \"default\"\n51\t_CANONICAL_ORG_NAME = \"Default Organization\"\n52\t_CANONICAL_DS_URL = \"jdbc:postgresql://postgres:5432/tiger?sslmode=prefer\"\n53\t_CANONICAL_DS_PASSWORD = \"passw0rd\"\n54\t\n55\t# Module-level normalization state.\n56\t# Populated by configure_normalization() before tests run.\n57\t# Each entry is (source_string, replacement_string).\n58\t# Ordered longest-first so more specific patterns match before substrings.\n59\t_normalization_replacements: list[tuple[str, str]] = []\n60\t_password_replacements: list[tuple[re.Pattern, str]] = []\n61\t_normalization_configured", "truncated": true}]} +{"ts": 1778658304.1183975, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Grep", "id": "toolu_01DVFMwSgYFkoewDZi4Ab9E5", "input": "{\"pattern\": \"RecordMode|OVERWRITE|record_mode|staging\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/tests-support/src/tests_support/vcrpy_utils.py\", \"output_mode\": \"content\"}", "truncated": false}]} +{"ts": 1778658304.1501255, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01DVFMwSgYFkoewDZi4Ab9E5", "is_error": false, "content": "14:from vcr.record_mode import RecordMode\n20:# These differ between local and staging and are irrelevant to API behavior.\n35:# These differ between local and staging environments but don't affect\n215: # Full origin URL: \"https://staging.example.com\" \u2192 \"http://localhost:3000\"\n220: # from the bare hostname): \"staging.example.com:8443\" \u2192 \"localhost:3000\"\n228: # Bare hostname: \"staging.example.com\" \u2192 \"localhost\"\n240: # Data source JDBC URL: staging DB host/name \u2192 local DB host/name\n250: # Data source password: staging password \u2192 local password\n336: record_mode=RecordMode.ALL if \"OVERWRITE\" in os.environ else RecordMode.ONCE,\n339: # Cassettes recorded against staging must replay in CI (localhost).\n588: if not _normalization_configured and \"OVERWRITE\" in os.environ:\n625: if not _normalization_configured and \"OVERWRITE\" in os.environ:", "truncated": false}]} +{"ts": 1778658312.1328156, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "OK, so the VCR mode is:\n- `RecordMode.ALL` if `OVERWRITE` env var is set (rerecords everything)\n- `RecordMode.ONCE` otherwise (plays back cassette if exists, records if not)\n\nThe cassette exists, so the test is in playback mode. This means VCR.py is intercepting requests and playing back responses from the cassette.\n\nBut then how is it getting a REAL 400 response from the server with a real `X-Gdc-Trace-Id`?!\n\nUnless... the cassette doesn't have a matching entry for the specific PUT request, and in `ONCE` mode, VCR makes a real network call for unmatched requests (rather than raising an error). Let me verify this VCR behavior.\n\n\nActually, checking the VCR.py docs, `ONCE` mode should raise `CannotSendRequest` if a request doesn't match anything in the cassette during replay. So that's not it. The vcrpy version might behave differently, or maybe the issue is with how `_body_matcher` is matching requests.", "truncated": false}]} +{"ts": 1778658312.1405597, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01HLR9CfNZ5WuQskn8bnN1n7", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/tests-support/src/tests_support/vcrpy_utils.py\", \"offset\": 280, \"limit\": 50}", "truncated": false}]} +{"ts": 1778658312.1698356, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01HLR9CfNZ5WuQskn8bnN1n7", "is_error": false, "content": "280\t \"\"\"Replace transient server values with deterministic placeholders.\"\"\"\n281\t text = _EXEC_HASH_BODY_RE.sub(_exec_hash_replacer, text)\n282\t text = _EXPORT_HASH_BODY_RE.sub(_export_hash_replacer, text)\n283\t text = _CREATED_AT_RE.sub(_CANONICAL_CREATED_AT, text)\n284\t text = _TRACE_ID_RE.sub(_CANONICAL_TRACE_ID, text)\n285\t text = _AUTH_ID_RE.sub(_CANONICAL_AUTH_ID, text)\n286\t text = _BEARER_TOKEN_RE.sub(_CANONICAL_BEARER_TOKEN, text)\n287\t text = _CACHE_ID_RE.sub(_CANONICAL_CACHE_ID, text)\n288\t text = _QUERY_DURATION_RE.sub(\"0\", text)\n289\t return text\n290\t\n291\t\n292\tdef _normalize_hashes_in_uri(uri: str) -> str:\n293\t \"\"\"Replace executionResult/exportResult hashes in a request URI.\"\"\"\n294\t uri = _EXEC_HASH_URI_RE.sub(_exec_hash_replacer, uri)\n295\t uri = _EXPORT_HASH_URI_RE.sub(_export_hash_replacer, uri)\n296\t return uri\n297\t\n298\t\n299\tdef _normalize_body(body: str | None) -> str:\n300\t \"\"\"Strip environment-specific fields from a JSON request body for matching.\"\"\"\n301\t if not body:\n302\t return body or \"\"\n303\t try:\n304\t data = orjson.loads(body)\n305\t except (orjson.JSONDecodeError, TypeError):\n306\t return body\n307\t\n308\t def _strip(obj: Any) -> Any:\n309\t if isinstance(obj, dict):\n310\t return {k: _strip(v) for k, v in obj.items() if k not in _ENV_SPECIFIC_BODY_FIELDS}\n311\t if isinstance(obj, list):\n312\t return [_strip(item) for item in obj]\n313\t return obj\n314\t\n315\t return orjson.dumps(_strip(data), option=orjson.OPT_SORT_KEYS).decode(\"utf-8\")\n316\t\n317\t\n318\tdef _body_matcher(r1: Any, r2: Any) -> None:\n319\t \"\"\"Custom VCR body matcher that ignores environment-specific fields.\"\"\"\n320\t b1 = _normalize_body(r1.body)\n321\t b2 = _normalize_body(r2.body)\n322\t assert b1 == b2, f\"Request bodies differ after normalization:\\n{b1}\\n!=\\n{b2}\"\n323\t\n324\t\n325\tdef _noop_matcher(r1: Any, r2: Any) -> None:\n326\t \"\"\"Always matches \u2014 used to disable scheme/host/", "truncated": true}]} +{"ts": 1778658322.7483032, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "Now I see! The body matcher strips environment-specific fields (`password`, `token`, `url`, `username`, etc.) before comparing. So when comparing PUT request bodies, these fields are stripped.\n\nNow, looking at the error: the cassette has an interaction for a PUT to `/api/v1/layout/organization` that contains a 400 response with the message \"Invalid combination of auth properties\". This means the cassette WAS recorded when the PUT was made with an incomplete datasource credentials. \n\nWait... that doesn't make sense. If the cassette was recorded with a successful PUT (200 response) but now the PUT fails with a 400, that would mean the cassette has different content.\n \nActually, I'm looking at the error more carefully. The response headers like `X-Xss-Protection`, `Strict-Transport-Security`, and `Vary` are being stripped during comparison, so those shouldn't be causing a mismatch.\n\nWait, I'm realizing something important here. These headers are showing up in the actual error output, which means this is a real HTTP response, not a cassette playback at all. The VCR interception isn't working for this request. Let me check the cassette file around the PUT request to see what was actually recorded versus what's being sent now.", "truncated": false}]} +{"ts": 1778658322.7898874, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01FNDibJ4pRGV8mRtpz8kWEX", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/fixtures/organization/layout_ip_allowlist_policies.yaml\", \"offset\": 6425, \"limit\": 60}", "truncated": false}]} +{"ts": 1778658322.841283, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01FNDibJ4pRGV8mRtpz8kWEX", "is_error": false, "content": "6425\t method: PUT\n6426\t uri: http://localhost:3000/api/v1/layout/organization\n6427\t response:\n6428\t body:\n6429\t string:\n6430\t detail: 'Invalid combination of auth properties. Only none, user + password,\n6431\t token, key-pair or client id + secret are allowed. Datasource: demo-test-ds,\n6432\t user: , password: , token: , key-pair: , clientId: ,\n6433\t clientSecret: '\n6434\t status: 400\n6435\t title: Bad Request\n6436\t traceId: NORMALIZED_TRACE_ID_000000000000\n6437\t headers:\n6438\t Content-Type:\n6439\t - application/problem+json\n6440\t DATE: *id001\n6441\t Expires:\n6442\t - '0'\n6443\t Pragma:\n6444\t - no-cache\n6445\t X-Content-Type-Options:\n6446\t - nosniff\n6447\t X-GDC-TRACE-ID: *id001\n6448\t status:\n6449\t code: 400\n6450\t message: Bad Request\n6451\t - request:\n6452\t body: null\n6453\t headers:\n6454\t Accept:\n6455\t - application/json\n6456\t Accept-Encoding:\n6457\t - br, gzip, deflate\n6458\t X-GDC-VALIDATE-RELATIONS:\n6459\t - 'true'\n6460\t X-Requested-With:\n6461\t - XMLHttpRequest\n6462\t method: GET\n6463\t uri: http://localhost:3000/api/v1/layout/organization\n6464\t response:\n6465\t body:\n6466\t string:\n6467\t agents:\n6468\t - aiKnowledge: true\n6469\t availableToAll: true\n6470\t enabled: true\n6471\t id: default-ai-assistant\n6472\t name: Default GoodData AI Assistant\n6473\t skillsMode: all\n6474\t customGeoCollections: []\n6475\t dataSources:\n6476\t - alternativeDataSourceId: ds-put-abc-id\n6477\t authenticationType: USERNAME_PASSWORD\n6478\t id: demo-test-ds\n6479\t name: demo-test-ds\n6480\t permissions:\n6481\t - assignee:\n6482", "truncated": true}]} +{"ts": 1778658343.214608, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "Now I understand! The cassette itself has a recorded 400 response for the first PUT request! And then it records the next GET (after the 400), and then possibly retrying...\n\nWait, looking at the cassette structure:\n- GET (line 13) - initial get\n- GET (line 2158) - second get (from inside put method for reading org layout)\n- PUT (line 6425) - with 400 response!\n- GET (line 6462) - get after the 400 PUT\n- PUT (line 10719) - another PUT\n\n\n- GET (line 10756)\n- GET (line 12900)\n- PUT (line 17167)\n- GET (line 17204)\n- PUT (line 21461)\n\nSo the cassette was recorded when the test was actually failing with a 400 error. Since it's in ONCE mode and the cassette exists, it's replaying those recorded responses, but the 400 triggers an exception that sends the test into the except/finally block, which then tries to make real HTTP requests instead of using the cassette.\n\nBut wait, the actual error output shows real HTTP requests are being made\u2014the response headers in the pytest output include `Vary`, `X-Xss-Protection`, and `Strict-Transport-Security`, which aren't present in the cassette. This means VCR isn't intercepting these requests at all, they're hitting a real staging server. Let me figure out why VCR isn't catching them.\n\nLooking at the cassette entry for the PUT request, the body field is null, but the actual request being sent has a JSON body with the org layout content. Since the body doesn't match what's recorded in the cassette, VCR can't find a matching entry and falls through to making a real HTTP request instead. So when the body matcher fails with an AssertionError, vcrpy treats that cassette entry as non-matching and moves to the next one. If no entries match, it should raise `CannotSendRequest`, but we're seeing actual HTTP requests go through instead\u2014which suggests something else is happening with how the matcher is being applied or the cassette is being configured. Let me look more closely at what's actually in the PUT request body within the cassette.", "truncated": false}]} +{"ts": 1778658343.2363448, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01CEowbgTcqRNbQJWJgmke3t", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/fixtures/organization/layout_ip_allowlist_policies.yaml\", \"offset\": 6400, \"limit\": 30}", "truncated": false}]} +{"ts": 1778658343.2517872, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01CEowbgTcqRNbQJWJgmke3t", "is_error": false, "content": "6400\t exportDefinitions: []\n6401\t filterContexts: []\n6402\t memoryItems: []\n6403\t metrics: []\n6404\t parameters: []\n6405\t visualizationObjects: []\n6406\t ldm:\n6407\t datasets: []\n6408\t dateInstances: []\n6409\t name: Demo West California\n6410\t parent:\n6411\t id: demo_west\n6412\t type: workspace\n6413\t permissions: []\n6414\t settings: []\n6415\t userDataFilters: []\n6416\t headers:\n6417\t Accept-Encoding:\n6418\t - br, gzip, deflate\n6419\t Content-Type:\n6420\t - application/json\n6421\t X-GDC-VALIDATE-RELATIONS:\n6422\t - 'true'\n6423\t X-Requested-With:\n6424\t - XMLHttpRequest\n6425\t method: PUT\n6426\t uri: http://localhost:3000/api/v1/layout/organization\n6427\t response:\n6428\t body:\n6429\t string:", "truncated": false}]} +{"ts": 1778658345.0951164, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "Let me look further back to see the full PUT request, including the body.", "truncated": false}]} +{"ts": 1778658345.7388573, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01QgJDhE7EeuHoRQC2ipccpu", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/fixtures/organization/layout_ip_allowlist_policies.yaml\", \"offset\": 2155, \"limit\": 30}", "truncated": false}]} +{"ts": 1778658345.7862532, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01QgJDhE7EeuHoRQC2ipccpu", "is_error": false, "content": "2155\t - 'true'\n2156\t X-Requested-With:\n2157\t - XMLHttpRequest\n2158\t method: GET\n2159\t uri: http://localhost:3000/api/v1/layout/organization\n2160\t response:\n2161\t body:\n2162\t string:\n2163\t agents:\n2164\t - aiKnowledge: true\n2165\t availableToAll: true\n2166\t enabled: true\n2167\t id: default-ai-assistant\n2168\t name: Default GoodData AI Assistant\n2169\t skillsMode: all\n2170\t customGeoCollections: []\n2171\t dataSources:\n2172\t - alternativeDataSourceId: ds-put-abc-id\n2173\t authenticationType: USERNAME_PASSWORD\n2174\t id: demo-test-ds\n2175\t name: demo-test-ds\n2176\t permissions:\n2177\t - assignee:\n2178\t id: demo2\n2179\t type: user\n2180\t name: MANAGE\n2181\t - assignee:\n2182\t id: demoGroup\n2183\t type: userGroup\n2184\t name: USE", "truncated": false}]} +{"ts": 1778658348.1601665, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01VUiMu2rrKMQuXGzT9oenfZ", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/fixtures/organization/layout_ip_allowlist_policies.yaml\", \"offset\": 2140, \"limit\": 20}", "truncated": false}]} +{"ts": 1778658348.200517, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01VUiMu2rrKMQuXGzT9oenfZ", "is_error": false, "content": "2140\t - no-cache\n2141\t X-Content-Type-Options:\n2142\t - nosniff\n2143\t X-GDC-TRACE-ID: *id001\n2144\t status:\n2145\t code: 200\n2146\t message: OK\n2147\t - request:\n2148\t body: null\n2149\t headers:\n2150\t Accept:\n2151\t - application/json\n2152\t Accept-Encoding:\n2153\t - br, gzip, deflate\n2154\t X-GDC-VALIDATE-RELATIONS:\n2155\t - 'true'\n2156\t X-Requested-With:\n2157\t - XMLHttpRequest\n2158\t method: GET\n2159\t uri: http://localhost:3000/api/v1/layout/organization", "truncated": false}]} +{"ts": 1778658350.4898698, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "The second GET request has `body: null`. Let me look for where the PUT request body starts - I need to look backwards from line 6425 to find the `body:` key.", "truncated": false}]} +{"ts": 1778658351.2334259, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01E44YQkJj91q93axY4HFsTB", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/fixtures/organization/layout_ip_allowlist_policies.yaml\", \"offset\": 2147, \"limit\": 30}", "truncated": false}]} +{"ts": 1778658351.2797103, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01E44YQkJj91q93axY4HFsTB", "is_error": false, "content": "2147\t - request:\n2148\t body: null\n2149\t headers:\n2150\t Accept:\n2151\t - application/json\n2152\t Accept-Encoding:\n2153\t - br, gzip, deflate\n2154\t X-GDC-VALIDATE-RELATIONS:\n2155\t - 'true'\n2156\t X-Requested-With:\n2157\t - XMLHttpRequest\n2158\t method: GET\n2159\t uri: http://localhost:3000/api/v1/layout/organization\n2160\t response:\n2161\t body:\n2162\t string:\n2163\t agents:\n2164\t - aiKnowledge: true\n2165\t availableToAll: true\n2166\t enabled: true\n2167\t id: default-ai-assistant\n2168\t name: Default GoodData AI Assistant\n2169\t skillsMode: all\n2170\t customGeoCollections: []\n2171\t dataSources:\n2172\t - alternativeDataSourceId: ds-put-abc-id\n2173\t authenticationType: USERNAME_PASSWORD\n2174\t id: demo-test-ds\n2175\t name: demo-test-ds\n2176\t permissions:", "truncated": false}]} +{"ts": 1778658353.77851, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "Let me look at the structure right before line 6425 (the PUT) more carefully. Let me look at where the PUT request body starts:", "truncated": false}]} +{"ts": 1778658354.3081646, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Grep", "id": "toolu_01HcR5Xqy7CHNATHcko3jQGP", "input": "{\"pattern\": \"- request:\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/fixtures/organization/layout_ip_allowlist_policies.yaml\", \"output_mode\": \"content\"}", "truncated": false}]} +{"ts": 1778658354.3452504, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01HcR5Xqy7CHNATHcko3jQGP", "is_error": false, "content": "2: - request:\n2147: - request:\n4291: - request:\n6451: - request:\n8595: - request:\n10745: - request:\n12889: - request:\n15033: - request:\n17193: - request:\n19337: - request:", "truncated": false}]} +{"ts": 1778658363.2770407, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "So there are 10 request/response pairs in the cassette:\n1. Lines 2-2146 (request at 2, GET)\n2. Lines 2147-4290 (request at 2147, GET)\n3. Lines 4291-6450 (request at 4291, PUT - response is 400)\n4. Lines 6451-8594 (request at 6451, GET)\n5. Lines 8595-10744 (request at 8595, PUT)\n\n\n6. Lines 10745-12888 (request at 10745, GET)\n7. Lines 12889-15032 (request at 12889, GET)\n8. Lines 15033-17192 (request at 15033, PUT)\n9. Lines 17193-19336 (request at 17193, GET)\n10. Lines 19337-end (request at 19337, PUT)\n\nBut I'm noticing some inconsistencies with the PUT method markers I spotted earlier at lines 6425, 10719, 17167, and 21461. Let me recheck the actual request indices to make sure my boundaries are correct.\n\nLooking at where each request actually starts:\n- Line 2, 2147, 4291, 6451, 8595, 10745, 12889, 15033, 17193, 19337\n\nAnd cross-referencing with the method and URI lines:\n- Line 13: GET\n- Line 2158: GET\n- Line 6425: PUT\n- Line 6462: GET\n- Line 10719: PUT\n- Line 10756: GET\n- Line 12900: GET\n- Line 17167: PUT\n- Line 17204: GET\n- Line 21461: PUT\n\nThe discrepancy is coming from the fact that PUT request bodies span multiple lines before the method declaration appears. Let me trace through what's happening at line 4291 to understand the pattern better.", "truncated": false}]} +{"ts": 1778658363.2917686, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_017c7Mxu7KAPKsoXJAfUTdg2", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/fixtures/organization/layout_ip_allowlist_policies.yaml\", \"offset\": 4291, \"limit\": 15}", "truncated": false}]} +{"ts": 1778658363.324055, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_017c7Mxu7KAPKsoXJAfUTdg2", "is_error": false, "content": "4291\t - request:\n4292\t body:\n4293\t agents:\n4294\t - aiKnowledge: true\n4295\t availableToAll: true\n4296\t enabled: true\n4297\t id: default-ai-assistant\n4298\t name: Default GoodData AI Assistant\n4299\t skillsMode: all\n4300\t customGeoCollections: []\n4301\t dataSources:\n4302\t - alternativeDataSourceId: ds-put-abc-id\n4303\t authenticationType: USERNAME_PASSWORD\n4304\t id: demo-test-ds\n4305\t name: demo-test-ds", "truncated": false}]} +{"ts": 1778658393.9892564, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "The request body for the PUT at line 4291 is the full organization layout JSON. So the cassette DOES have the request body for the PUT.\n\nNow, the response for this PUT is a 400. And looking at the cassette interaction structure:\n1. GET (request at 2) \n2. GET (request at 2147)\n3. PUT (request at 4291, response is 400 at lines 6425-6450)\n4. GET (request at 6451)\n\n\n5. PUT (request at 8595)\n6. GET (request at 10745)\n7. GET (request at 12889)\n8. PUT (request at 15033)\n9. GET (request at 17193)\n10. PUT (request at 19337)\n\nSo there are 3 GETs and 4 PUTs total, with the first PUT failing with a 400 error. This looks like a test that was recorded mid-execution and stopped partway through, likely after encountering that initial failure.\n\nNow I'm mapping out the actual test flow. The test starts by fetching the original policies, then attempts to update with new ones (which fails with 400), and in the finally block it restores the original policies. The sequence shows a GET to fetch the current state, a PUT that returns 400 due to auth issues, then another GET inside the finally block to retrieve the original policies again before restoring them.\n\nBut wait\u2014looking at the cassette more carefully, there are more interactions recorded than what the current code structure would produce. This suggests the implementation might have changed since the cassette was recorded. If the `put_declarative_ip_allowlist_policies` method originally didn't perform an internal read-modify-write operation, the cassette would show just four interactions: an initial GET, a PUT, a GET to verify the result, and a final PUT to restore the original state\u2014without any extra GETs nested inside the PUT calls.\n\nThe real issue is that the cassette has a 400 response recorded for the first PUT request, which happened because the test was run against staging with a malformed request body\u2014the datasource had a username but no password. To fix this, I need to correct the request payload so it's valid. to make `put_", "truncated": true}]} +{"ts": 1778658394.0010993, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01ToPLs2YPaAzpAXQUd3KV3J", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/fixtures/organization/layout_ip_allowlist_policies.yaml\", \"offset\": 8595, \"limit\": 20}", "truncated": false}]} +{"ts": 1778658394.0277512, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01ToPLs2YPaAzpAXQUd3KV3J", "is_error": false, "content": "8595\t - request:\n8596\t body:\n8597\t agents:\n8598\t - aiKnowledge: true\n8599\t availableToAll: true\n8600\t enabled: true\n8601\t id: default-ai-assistant\n8602\t name: Default GoodData AI Assistant\n8603\t skillsMode: all\n8604\t customGeoCollections: []\n8605\t dataSources:\n8606\t - alternativeDataSourceId: ds-put-abc-id\n8607\t authenticationType: USERNAME_PASSWORD\n8608\t id: demo-test-ds\n8609\t name: demo-test-ds\n8610\t permissions:\n8611\t - assignee:\n8612\t id: demo2\n8613\t type: user\n8614\t name: MANAGE", "truncated": false}]} +{"ts": 1778658396.0471694, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01NMDTuAQGsqUNMSM2JirdsT", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/fixtures/organization/layout_ip_allowlist_policies.yaml\", \"offset\": 10719, \"limit\": 30}", "truncated": false}]} +{"ts": 1778658396.0786192, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01NMDTuAQGsqUNMSM2JirdsT", "is_error": false, "content": "10719\t method: PUT\n10720\t uri: http://localhost:3000/api/v1/layout/organization\n10721\t response:\n10722\t body:\n10723\t string:\n10724\t detail: 'Invalid combination of auth properties. Only none, user + password,\n10725\t token, key-pair or client id + secret are allowed. Datasource: demo-test-ds,\n10726\t user: , password: , token: , key-pair: , clientId: ,\n10727\t clientSecret: '\n10728\t status: 400\n10729\t title: Bad Request\n10730\t traceId: NORMALIZED_TRACE_ID_000000000000\n10731\t headers:\n10732\t Content-Type:\n10733\t - application/problem+json\n10734\t DATE: *id001\n10735\t Expires:\n10736\t - '0'\n10737\t Pragma:\n10738\t - no-cache\n10739\t X-Content-Type-Options:\n10740\t - nosniff\n10741\t X-GDC-TRACE-ID: *id001\n10742\t status:\n10743\t code: 400\n10744\t message: Bad Request\n10745\t - request:\n10746\t body: null\n10747\t headers:\n10748\t Accept:", "truncated": false}]} +{"ts": 1778658399.4231002, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "So the cassette has TWO 400 responses! Both PUT requests #3 (line 4291) and #5 (line 8595) return 400 errors.\n\nNow let me look at interactions 6, 7, 8, 9, 10 (the GET at 10745, GET at 12889, PUT at 15033, GET at 17193, PUT at 19337).", "truncated": false}]} +{"ts": 1778658400.1312182, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_015akJNWc8FQ8m8JMCkkW7W3", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/fixtures/organization/layout_ip_allowlist_policies.yaml\", \"offset\": 15033, \"limit\": 10}", "truncated": false}]} +{"ts": 1778658400.1353521, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_015akJNWc8FQ8m8JMCkkW7W3", "is_error": false, "content": "15033\t - request:\n15034\t body:\n15035\t agents:\n15036\t - aiKnowledge: true\n15037\t availableToAll: true\n15038\t enabled: true\n15039\t id: default-ai-assistant\n15040\t name: Default GoodData AI Assistant\n15041\t skillsMode: all\n15042\t customGeoCollections: []", "truncated": false}]} +{"ts": 1778658400.7599125, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01CEkgYLpsarH7Sw4DQpugns", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/packages/gooddata-sdk/tests/catalog/fixtures/organization/layout_ip_allowlist_policies.yaml\", \"offset\": 17167, \"limit\": 30}", "truncated": false}]} +{"ts": 1778658400.7972205, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01CEkgYLpsarH7Sw4DQpugns", "is_error": false, "content": "17167\t method: PUT\n17168\t uri: http://localhost:3000/api/v1/layout/organization\n17169\t response:\n17170\t body:\n17171\t string:\n17172\t detail: 'Invalid combination of auth properties. Only none, user + password,\n17173\t token, key-pair or client id + secret are allowed. Datasource: demo-test-ds,\n17174\t user: , password: , token: , key-pair: , clientId: ,\n17175\t clientSecret: '\n17176\t status: 400\n17177\t title: Bad Request\n17178\t traceId: NORMALIZED_TRACE_ID_000000000000\n17179\t headers:\n17180\t Content-Type:\n17181\t - application/problem+json\n17182\t DATE: *id001\n17183\t Expires:\n17184\t - '0'\n17185\t Pragma:\n17186\t - no-cache\n17187\t X-Content-Type-Options:\n17188\t - nosniff\n17189\t X-GDC-TRACE-ID: *id001\n17190\t status:\n17191\t code: 400\n17192\t message: Bad Request\n17193\t - request:\n17194\t body: null\n17195\t headers:\n17196\t Accept:", "truncated": false}]} +{"ts": 1778658413.8071806, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "All PUT responses are 400! So the cassette was recorded when ALL PUTs were failing with 400. The cassette captures this failure state.\n\nThis means the cassette itself is broken - it was recorded from a failing test run where all `set_organization_layout` calls were returning 400 errors. But the cassette still exists and hasn't been deleted (it contains content, even though all the content is failure responses).\n\nThe instructions say: \"Do NOT fabricate cassette YAML files \u2014 they will be re-recorded by the pipeline after you finish. Stale cassettes were already deleted (see below).\"\n\nBut this cassette is NOT stale (not deleted) - it's present but has broken content. \n\nSo the real issue is that `put_declarative_ip_allowlist_policies` uses a read-modify-write pattern with `set_organization_layout` that loses datasource passwords, and deleting the cassette won't help since the test would just fail again against the live server with the same bug.\n\nThe core problem is that getting the org layout doesn't return datasource passwords, so when you PUT it back with those missing credentials, the API rejects it. I need to find either a dedicated IP allowlist endpoint, or figure out how to update just that part of the layout without touching the datasource auth fields. Let me search through the available APIs to see if there's a more targeted endpoint for this.", "truncated": false}]} +{"ts": 1778658413.8369935, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Glob", "id": "toolu_01VzAnLk8bomy3sMjx2RTFig", "input": "{\"pattern\": \"**/*api*.py\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/gooddata-api-client/gooddata_api_client/api\"}", "truncated": false}]} +{"ts": 1778658413.867863, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01VzAnLk8bomy3sMjx2RTFig", "is_error": false, "content": "gooddata-api-client/gooddata_api_client/api/actions_api.py\ngooddata-api-client/gooddata_api_client/api/agent_controller_api.py\ngooddata-api-client/gooddata_api_client/api/aggregated_fact_controller_api.py\ngooddata-api-client/gooddata_api_client/api/ai_agents_api.py\ngooddata-api-client/gooddata_api_client/api/ai_api.py\ngooddata-api-client/gooddata_api_client/api/ai_lake_api.py\ngooddata-api-client/gooddata_api_client/api/ai_lake_databases_api.py\ngooddata-api-client/gooddata_api_client/api/ai_lake_pipe_tables_api.py\ngooddata-api-client/gooddata_api_client/api/ai_lake_services_operations_api.py\ngooddata-api-client/gooddata_api_client/api/analytical_dashboard_controller_api.py\ngooddata-api-client/gooddata_api_client/api/analytics_model_api.py\ngooddata-api-client/gooddata_api_client/api/api_token_controller_api.py\ngooddata-api-client/gooddata_api_client/api/api_tokens_api.py\ngooddata-api-client/gooddata_api_client/api/appearance_api.py\ngooddata-api-client/gooddata_api_client/api/attribute_controller_api.py\ngooddata-api-client/gooddata_api_client/api/attribute_hierarchies_api.py\ngooddata-api-client/gooddata_api_client/api/attribute_hierarchy_controller_api.py\ngooddata-api-client/gooddata_api_client/api/attributes_api.py\ngooddata-api-client/gooddata_api_client/api/authentication_api.py\ngooddata-api-client/gooddata_api_client/api/automation_controller_api.py\ngooddata-api-client/gooddata_api_client/api/automation_organization_view_controller_api.py\ngooddata-api-client/gooddata_api_client/api/automation_result_controller_api.py\ngooddata-api-client/gooddata_api_client/api/automations_api.py\ngooddata-api-client/gooddata_api_client/api/available_drivers_api.py\ngooddata-api-client/gooddata_api_client/api/cache_usage_api.py\ngooddata-api-client/gooddata_api_client/api/certification_api.py\ngooddata-api-client/gooddata_api_client/api/color_palette_controller_api.py\ngooddata-api-client/gooddata_api_client/api/computation_api.py\ngooddata-api-client/gooddata_api_client/api/cookie_securit", "truncated": true}]} +{"ts": 1778658419.2907581, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Grep", "id": "toolu_011Kqe7U112Ppx32Beuids29", "input": "{\"pattern\": \"ip_allowlist\\\\|allowlist\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/gooddata-api-client/gooddata_api_client/api/organization_declarative_apis_api.py\", \"output_mode\": \"content\", \"head_limit\": 20}", "truncated": false}]} +{"ts": 1778658419.4668484, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_011Kqe7U112Ppx32Beuids29", "is_error": false, "content": "No matches found", "truncated": false}]} +{"ts": 1778658419.9507258, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Grep", "id": "toolu_01J3f5ackPHGvCWXgkrSegnC", "input": "{\"pattern\": \"ip_allowlist\\\\|allowlist\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/gooddata-api-client/gooddata_api_client/api/layout_api.py\", \"output_mode\": \"content\", \"head_limit\": 20}", "truncated": false}]} +{"ts": 1778658419.993418, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01J3f5ackPHGvCWXgkrSegnC", "is_error": false, "content": "No matches found", "truncated": false}]} +{"ts": 1778658427.7390513, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "There's no dedicated `ip_allowlist` endpoint in the layout API. So the approach of using `set_organization_layout` is the only way. But the problem is that `set_organization_layout` requires proper datasource credentials.\n\nNow, let me think about a different fix. The issue is that when we read the org layout, the datasource username is returned but the password is not. When we write it back, the API rejects \"username without password\".\n\nPossible fixes:\n1. Remove the datasource credential fields from the layout before PUT (set `username = None` for datasources that have username but no password)\n\n\n2. Remove the entire datasource section before PUT (not a good idea - would delete datasources)\n3. Use a different approach that doesn't require the full credentials\n\nI notice that `put_declarative_data_sources` handles this by using a credentials file to supply passwords during updates, but `set_organization_layout` doesn't have that option. Let me check what happens when `set_organization_layout` processes datasource credentials.", "truncated": false}]} +{"ts": 1778658427.7478237, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Grep", "id": "toolu_01KhucV8K9dxeK6Bm9zdnx8v", "input": "{\"pattern\": \"def set_organization_layout\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/gooddata-api-client/gooddata_api_client/api/layout_api.py\", \"output_mode\": \"content\"}", "truncated": false}]} +{"ts": 1778658427.7742152, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01KhucV8K9dxeK6Bm9zdnx8v", "is_error": false, "content": "5870: def set_organization_layout(", "truncated": false}]} +{"ts": 1778658430.050673, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Read", "id": "toolu_01KLDpSo52FDNZKjmzUa5EzL", "input": "{\"file_path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/gooddata-api-client/gooddata_api_client/api/layout_api.py\", \"offset\": 5870, \"limit\": 90}", "truncated": false}]} +{"ts": 1778658430.1035178, "type": "user", "blocks": [{"type": "tool_result", "tool_use_id": "toolu_01KLDpSo52FDNZKjmzUa5EzL", "is_error": false, "content": "5870\t def set_organization_layout(\n5871\t self,\n5872\t declarative_organization,\n5873\t **kwargs\n5874\t ):\n5875\t \"\"\"Set organization layout # noqa: E501\n5876\t\n5877\t Sets complete layout of organization, like workspaces, user-groups, etc. # noqa: E501\n5878\t This method makes a synchronous HTTP request by default. To make an\n5879\t asynchronous HTTP request, please pass async_req=True\n5880\t\n5881\t >>> thread = api.set_organization_layout(declarative_organization, async_req=True)\n5882\t >>> result = thread.get()\n5883\t\n5884\t Args:\n5885\t declarative_organization (DeclarativeOrganization):\n5886\t\n5887\t Keyword Args:\n5888\t _return_http_data_only (bool): response data without head status\n5889\t code and headers. Default is True.\n5890\t _preload_content (bool): if False, the urllib3.HTTPResponse object\n5891\t will be returned without reading/decoding response data.\n5892\t Default is True.\n5893\t _request_timeout (int/float/tuple): timeout setting for this request. If\n5894\t one number provided, it will be total request timeout. It can also\n5895\t be a pair (tuple) of (connection, read) timeouts.\n5896\t Default is None.\n5897\t _check_input_type (bool): specifies if type checking\n5898\t should be done one the data sent to the server.\n5899\t Default is True.\n5900\t _check_return_type (bool): specifies if type checking\n5901\t should be done one the data received from the server.\n5902\t Default is True.\n5903\t _spec_property_naming (bool): True if the variable names in the input data\n5904\t are serialized names, as specified in the OpenAPI document.\n5905\t False if the variable names in the input data\n5906\t are pythonic names, e.g. snake case (default)\n5", "truncated": true}]} +{"ts": 1778658440.2209585, "type": "assistant", "blocks": [{"type": "thinking", "thinking": "Let me look at the `DeclarativeOrganization` model to understand what fields are there for data sources, and whether we can clear credentials.", "truncated": false}]} +{"ts": 1778658440.9255314, "type": "assistant", "blocks": [{"type": "tool_use", "name": "Grep", "id": "toolu_015KLusPLym8PCSLExxAH5TC", "input": "{\"pattern\": \"class DeclarativeOrganization\", \"path\": \"/home/runner/_work/gdc-nas/gdc-nas/sdk/gooddata-api-client/gooddata_api_client/model\", \"output_mode\": \"content\"}", "truncated": false}]}