Create a Multitenant Environment

How do I share my project with multiple clients while maintaining client data privacy?

This tutorial will guide you through the process of creating a multitenant environment.

The multitenant environment allows you to share the same logical data model and metrics, visualizations, and dashboards (so-called “analytical model”) among multiple consumers while keeping their data private (each consumer can access only their data).

For example, your company has multiple clients (tenants) who use your application. You want to build a business analytics experience where all your tenants will use the same metrics, visualizations, and dashboards, but each tenant will see only their own data (the data that is relevant to and is associated with a specific tenant). You can also use a multitenant environment inside the company to present different data to different company departments while the analytical model is the same across the company.

A multitenant environment is implemented through a workspace hierarchy and data filters.

  • A workspace hierarchy is a tree structure describing relations between parent workspaces and their child workspaces. A child workspace inherits entities from its parent workspace. Any change in the parent workspace is immediately propagated to the child workspace. At the same time, the child workspace can have its own entities in addition to those inherited from the parent workspace. For more information about the workspace hierarchy, see Build a Workspace Hierarchy.
  • Data filters allow you to limit the data available in child workspaces. By setting a data filter, you can define what subset of the data from a parent workspace will be available in its child workspaces. For more information about the data filters, see Set Up Data Filters in Workspaces.
  • Alternatively, you can define different data sources for each tenant. See the Unique Data Sources for Tenants section for details.

The following picture shows how a visualization in a child workspace looks like comparing to the same visualization in the parent workspace when the data filter that filters the data in the child workspace by region is applied:

Filtered workspaces

To create a multitenant hierarchy, do the following:

  1. Build a workspace hierarchy.
  2. Set up data filters.

Build a Workspace Hierarchy

You are going to create a simple workspace hierarchy where the demo workspace is a parent workspace with two child workspaces.

demo
West
NorthEast

For more information about the workspace hierarchy, see Build a Workspace Hierarchy.

Steps:

  1. Create a child workspace called West under the demo workspace.

    UI
    API

    Open the Workspaces tab, click the demo workspace, then click Create child workspace.

    For details, see Build a Workspace Hierarchy.

    Bash
    PowerShell 7
    curl http://localhost:3000/api/v1/entities/workspaces \
      -H "Content-Type: application/vnd.gooddata.api+json" \
      -H "Accept: application/vnd.gooddata.api+json" \
      -H "Authorization: Bearer YWRtaW46Ym9vdHN0cmFwOmFkbWluMTIz" \
      -X POST \
      -d '{
          "data": {
            "attributes": {
              "description": "My child workspace.",
              "name": "West"
            },
            "id": "myWestWorkspaceId",
            "type": "workspace",
            "relationships": {
              "parent": {
                "data": {
                  "id": "demo_workspace_id",
                  "type": "workspace"
                }
              }
            }
          }
          }' | jq .
    Invoke-RestMethod -Method Post -Uri 'http://localhost:3000/api/v1/entities/workspaces' `
      -ContentType 'application/vnd.gooddata.api+json' `
      -H @{
        'Accept' = 'application/vnd.gooddata.api+json'
        'Authorization' = 'Bearer YWRtaW46Ym9vdHN0cmFwOmFkbWluMTIz'
      } `
      -Body '{
          "data": {
            "attributes": {
              "description": "My child workspace.",
              "name": "West"
            },
            "id": "myWestWorkspaceId",
            "type": "workspace",
            "relationships": {
              "parent": {
                "data": {
                  "id": "demo_workspace_id",
                  "type": "workspace"
                }
              }
            }
          }
          }' | ConvertTo-Json
    To confirm that the workspace has been created, the server returns the following response:
    ```json
    {
      "data": {
        "id": "myWestWorkspaceId",
        "type": "workspace",
        "attributes": {
          "description": "My child workspace.",
          "name": "West"
        }
      },
      "links": {
        "self": "http://localhost:3000/api/v1/entities/workspaces/West"
      }
    }
    ```
    

  2. Create a child workspace called NorthEast under the demo workspace.

  3. Go to the home page and check that the two workspaces, West and NorthEast, have appeared in the list of workspaces.

    Multiple workspaces
  4. (Optional) Open the West workspace and check that it contains the dashboard and visualization that you created in the demo workspace.

How to Create Multiple Child Workspaces at Once

The procedure described earlier is helpful when you need to create just a few child workspaces, and you can create them one by one. If you need to create many workspaces (for example, dozens or hundreds), you can update the workspace declarative API layout that describes all your workspaces.

Steps:

  1. Download the complete declarative document, and save it to your machine (/tmp/ws-layout is used as an example location in the following code samples):

    Bash
    PowerShell 7
    curl http://localhost:3000/api/v1/layout/workspaces \
      -H "Authorization: Bearer YWRtaW46Ym9vdHN0cmFwOmFkbWluMTIz" \
      | jq > /tmp/ws-layout.json
    Invoke-RestMethod -Uri 'http://localhost:3000/api/v1/layout/workspaces' `
      -H @{ 'Authorization' = 'Bearer YWRtaW46Ym9vdHN0cmFwOmFkbWluMTIz' } `
      -OutFile $env:TEMP\ws-layout.json

    The downloaded document contains the definition of the demo workspace with all of the objects that it holds as well as the definitions of the West and NorthEast workspaces, which are empty.

  2. Add the definition of another child workspace to the document. To do so, add the following JSON object to the top-level workspaces array:

    {
      "id": "myWestWorkspaceId",
      "model": {
        "analytics": {
          "analyticalDashboards": [],
          "filterContexts": [],
          "metrics": [],
          "visualizationObjects": []
        },
        "ldm": {
          "datasets": [],
          "dateInstances": []
        }
      },
      "name": "Midwest",
      "parent": {
        "id": "demo_workspace_id",
        "type": "workspace"
      },
      "settings": [
        {
          "id": "timezone",
          "content": {
            "value": "Europe/Prague"
          }
        }
      ]
    }
  3. Add as many child workspaces as you need by adding more JSON objects with a different name and ID for each workspace.

  4. Upload the updated declarative document back to the server.

    Bash
    PowerShell 7
    curl http://localhost:3000/api/v1/layout/workspaces \
      -H "Authorization: Bearer YWRtaW46Ym9vdHN0cmFwOmFkbWluMTIz" \
      -H "Content-Type: application/json" \
      -X PUT -d @/tmp/ws-layout.json
    Invoke-RestMethod -Method Put -Uri 'http://localhost:3000/api/v1/layout/workspaces' `
      -ContentType 'application/json' `
      -H @{ 'Authorization' = 'Bearer YWRtaW46Ym9vdHN0cmFwOmFkbWluMTIz' } `
      -InFile $env:TEMP\ws-layout.json

  5. Go to the home page and check that the workspaces that you added have appeared in the list of workspaces.

Set Up Data Filters

You now have the workspace hierarchy where the child workspaces inherit the analytical model from the parent workspace, demo. They also share the same data source and the data stored in this data source.

You are going to set up a data filter to limit the data available in the child workspaces by region, which is determined by the values of the Region attribute. When the data filter is applied:

  • The West workspace should be able to access only the data that is related to the West region.
  • The NorthEast workspace should not be able to access any region-related data.

For more information about the data filters, see Set Up Data Filters in Workspaces.

Steps:

  1. Create a JSON document that describes the data filter. The column that should be used for filtering the data is wdf__region (it contains the values of the Region attribute).

    • To limit the data in the West workspace to the West region, assign the West filter value to the workspace.

    • To restrict all the region-related data in the West workspace (child of demo workspace), do not assign any filter value to it. Because the definition of data filters is tied to the parent workspaces, no data will be available in the child workspace if a filter value is not explicitly assigned to the child (here West) workspace in the filter definition.

    {
      "workspaceDataFilters": [
        {
          "id": "region",
          "title": "Customer Region",
          "columnName": "wdf__region",
          "workspace": {
            "id": "demo_workspace_id",
            "type": "workspace"
          },
          "workspaceDataFilterSettings": [
            {
              "id": "region_west",
              "title": "Region West",
              "filterValues": [
                "West"
              ],
              "workspace": {
                "id": "west_workspace_id",
                "type": "workspace"
              }
            }
          ]
        }
      ]
    }
  2. Upload the JSON document to the server.

    Bash
    PowerShell 7
    curl http://localhost:3000/api/v1/layout/workspaceDataFilters \
      -H "Authorization: Bearer YWRtaW46Ym9vdHN0cmFwOmFkbWluMTIz" \
      -H "Content-Type:application/json" \
      -X PUT -d @/tmp/ws-filter.json
    Invoke-RestMethod -Method Put -Uri 'http://localhost:3000/api/v1/layout/workspaceDataFilters' `
      -ContentType 'application/json' `
      -H @{ 'Authorization' = 'Bearer YWRtaW46Ym9vdHN0cmFwOmFkbWluMTIz' } `
      -InFile $env:TEMP\ws-filter.json

    The data filter is applied.

With this data filter in place, when opening or creating a visualization where the Region attribute is used, you can expect the following:

  • In the demo workspace, the visualization shows data for all the regions.
  • In the West workspace, the visualization shows only the data related to the Western region.
  • In the NorthEast workspace, the visualization shows no region-related data (no values from the Region attribute are available).

Unique Data Sources for Tenants

You can set up distinct data sources for child workspaces, which can be useful when your source data is structured on a tenant-specific basis, such as utilizing different database schemas or databases for each tenant. This approach aids in managing strictly separated data, adding an extra dimension of data management alongside workspace data filters.

The child workspace must use the same logical data model as its parent workspace and must be the last node in the hierarchy, meaning it cannot have its own child workspaces. Creating a new child workspace subordinated to a child workspace with a unique data source will cause the new workspace to inherit the settings, including the data source, from the root (top-level parent) workspace.

In setting up data sources for child workspaces, it’s required to include the dataSource in the definition. The dataSource ID may be the same as the parent workspace’s. Yet, you’re allowed to define a unique schemaPath for the child workspace, enabling customized data management under the same data source structure. Without a specified schemaPath, the child workspace defaults to inheriting its settings, including the data source, from the root (top-level parent) workspace.

To define the data source, you must possess the organization.MANAGE permission.

Configuration:

Entity API
Declarative API

To set a data source for a child workspace, make a PUT request to the /api/v1/entities/workspaces/{workspaceId} API. Use the following parameters in your child workspace definition:

{
 "data": {
   "id": "childWorkspace",
   "type": "workspace",
   "attributes": {
     "dataSource": {
       "id": "workspaceDatasource",
       "schemaPath": [
         "schemaName"
       ]
     }
   },
   "relationships": {
     "parent": {
       "data": {
         "id": "parentWorkspace",
         "type": "workspace"
       }
     }
   }
 }
}

The Declarative API can set up multiple workspaces in one request. To set a data source for a child workspace, make a PUT request to the /api/v1/layout/workspaces/{workspaceId} API with the following parameters in your child workspace definition:

{
 "workspaces": [
   {
      "id": "subws3",
      "name": "sub-work-space3",
      "parent": {
        "id": "ws1",
        "type": "workspace"
      },
      "dataSource": {
       "id": "workspaceDatasource",
       "schemaPath": [
         "schema"
       ]
      }
   }
 ],
 "workspaceDataFilters": []
}