or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

api

features

charts

charts.mdconditional-formatting.mdvisualizations.md
authorization.mdchangesets.mdcharts-as-code.mdcompiler.mddashboards.mddbt.mdee-features.mdformatting.mdparameters.mdpivot.mdprojects-spaces.mdsql-runner.mdtemplating.mdwarehouse.md
index.md
tile.json

projects-spaces.mddocs/api/features/

Project and Space Management

Managing projects, spaces, and organizational structures in Lightdash.

Overview

Projects and spaces provide organizational structure:

  • Projects: Contain DBT connections, warehouse credentials, and explores
  • Spaces: Organize dashboards and charts within projects
  • Space access: Control who can view and edit content in spaces

Project Types

Project Interface

interface Project {
  organizationUuid: string;
  projectUuid: string;
  name: string;
  type: ProjectType;
  dbtConnection: DbtProjectConfig;
  warehouseConnection?: WarehouseCredentials;
  pinnedListUuid?: string;
  upstreamProjectUuid?: string;
  dbtVersion: DbtVersionOption;
  schedulerTimezone: string;
  createdByUserUuid: string | null;
  organizationWarehouseCredentialsUuid?: string;
}

enum ProjectType {
  DEFAULT = 'DEFAULT',
  PREVIEW = 'PREVIEW',
}

interface ProjectSummary {
  organizationUuid: string;
  projectUuid: string;
  name: string;
  type: ProjectType;
  dbtConnection: Pick<DbtProjectConfig, 'type'>;
  warehouseConnection?: Pick<WarehouseCredentials, 'type'>;
}

Example:

const project: Project = {
  organizationUuid: 'org-uuid',
  projectUuid: 'project-uuid',
  name: 'Analytics Project',
  type: ProjectType.DEFAULT,
  dbtConnection: {
    type: DbtProjectType.GITHUB,
    repository: 'org/analytics-dbt',
    branch: 'main',
    project_sub_path: '/',
  },
  warehouseConnection: {
    type: WarehouseTypes.BIGQUERY,
    project: 'my-gcp-project',
    dataset: 'analytics',
  },
  dbtVersion: SupportedDbtVersions.V1_10,
  schedulerTimezone: 'UTC',
  createdByUserUuid: 'user-uuid',
};

DBT Project Configuration

DbtProjectType

enum DbtProjectType {
  DBT = 'dbt',
  GITHUB = 'github',
  GITLAB = 'gitlab',
  BITBUCKET = 'bitbucket',
  AZURE_DEVOPS = 'azure_devops',
  DBT_CLOUD_IDE = 'dbt_cloud_ide',
  NONE = 'none',
  MANIFEST = 'manifest',
}

DbtProjectConfig Types

type DbtProjectConfig =
  | DbtLocalProjectConfig
  | DbtGithubProjectConfig
  | DbtGitlabProjectConfig
  | DbtBitBucketProjectConfig
  | DbtAzureDevOpsProjectConfig
  | DbtCloudIDEProjectConfig
  | DbtNoneProjectConfig
  | DbtManifestProjectConfig;

interface DbtLocalProjectConfig {
  type: DbtProjectType.DBT;
  profiles_dir?: string;
  project_dir?: string;
  target?: string;
}

interface DbtManifestProjectConfig {
  type: DbtProjectType.MANIFEST;
  manifest: string;
  hideRefreshButton: boolean;
}

interface DbtGithubProjectConfig {
  type: DbtProjectType.GITHUB;
  repository: string;
  branch: string;
  project_sub_path: string;
  host_domain?: string;
  personal_access_token?: string;
}

interface DbtGitlabProjectConfig {
  type: DbtProjectType.GITLAB;
  repository: string;
  branch: string;
  project_sub_path: string;
  host_domain?: string;
  personal_access_token?: string;
}

// Similar interfaces for BitBucket, Azure DevOps, DBT Cloud IDE

DBT Versions

enum SupportedDbtVersions {
  V1_4 = 'v1_4',
  V1_5 = 'v1_5',
  V1_6 = 'v1_6',
  V1_7 = 'v1_7',
  V1_8 = 'v1_8',
  V1_9 = 'v1_9',
  V1_10 = 'v1_10',
}

const DefaultSupportedDbtVersion: SupportedDbtVersions;

function getLatestSupportDbtVersion(): SupportedDbtVersions;
function isDbtVersion110OrHigher(version: SupportedDbtVersions): boolean;

Project Utility Functions

function isGitProjectType(type: DbtProjectType): boolean;

function maybeOverrideDbtConnection(
  dbtConnection: DbtProjectConfig,
  override?: Partial<DbtProjectConfig>
): DbtProjectConfig;

function maybeOverrideWarehouseConnection(
  warehouseConnection: WarehouseCredentials,
  override?: Partial<CreateWarehouseCredentials>
): CreateWarehouseCredentials;

function getProjectDirectory(
  dbtConnection?: DbtProjectConfig
): string | undefined;

Example:

import {
  isGitProjectType,
  DbtProjectType,
  getLatestSupportDbtVersion,
} from '@lightdash/common';

if (isGitProjectType(project.dbtConnection.type)) {
  console.log('Project uses git integration');
}

const latestVersion = getLatestSupportDbtVersion();
console.log(`Latest DBT version: ${latestVersion}`);

Space Types

Space Interface

interface Space {
  organizationUuid: string;
  uuid: string;
  name: string;
  isPrivate: boolean;
  pinnedListUuid: string | null;
  pinnedListOrder: number | null;
  dashboards: SpaceDashboard[];
  queries: SpaceQuery[];
  access: SpaceShare[];
  projectUuid: string;
  slug: string;
  groupsAccess: SpaceGroup[];
  // Nested Spaces fields
  childSpaces: Omit<SpaceSummary, 'userAccess'>[];
  parentSpaceUuid: string | null;
  path: string; // ltree path serialized as string
  breadcrumbs?: {
    name: string;
    uuid: string;
  }[];
}

interface SpaceSummary {
  organizationUuid: string;
  uuid: string;
  name: string;
  isPrivate: boolean;
  pinnedListUuid: string | null;
  pinnedListOrder: number | null;
  projectUuid: string;
  slug: string;
  parentSpaceUuid: string | null;
  path: string;
  userAccess: SpaceShare | undefined;
  access: string[];
  chartCount: number;
  dashboardCount: number;
}

Space Content

interface SpaceDashboard {
  uuid: string;
  name: string;
  description?: string;
  slug: string;
  updatedAt: Date;
  updatedByUser?: UpdatedByUser;
  pinnedListOrder: number | null;
  views: number;
  firstViewedAt: Date | null;
  validationErrors?: ValidationError[];
}

interface SpaceQuery {
  uuid: string;
  name: string;
  description?: string;
  slug: string;
  updatedAt: Date;
  updatedByUser?: UpdatedByUser;
  spaceUuid: string;
  pinnedListOrder: number | null;
  views: number;
  firstViewedAt: Date | null;
  chartType: ChartType;
  chartKind: ChartKind;
  validationErrors?: ValidationError[];
}

Space Access Control

enum SpaceMemberRole {
  VIEWER = 'viewer',
  EDITOR = 'editor',
  ADMIN = 'admin',
}

interface SpaceShare {
  userUuid: string;
  firstName: string;
  lastName: string;
  email: string;
  role: SpaceMemberRole;
  hasDirectAccess: boolean;
  projectRole: ProjectMemberRole | undefined;
  inheritedRole: OrganizationMemberRole | ProjectMemberRole | undefined;
  inheritedFrom: 'organization' | 'project' | 'group' | 'space_group' | undefined;
}

interface SpaceGroup {
  groupUuid: string;
  groupName: string;
  spaceRole: SpaceMemberRole;
}

interface AddSpaceUserAccess {
  userUuid: string;
  spaceRole: SpaceMemberRole;
}

interface AddSpaceGroupAccess {
  groupUuid: string;
  spaceRole: SpaceMemberRole;
}

Creating and Updating Spaces

interface CreateSpace {
  name: string;
  isPrivate?: boolean;
  access?: Pick<SpaceShare, 'userUuid' | 'role'>[];
  parentSpaceUuid?: string;
}

interface UpdateSpace {
  name: string;
  isPrivate?: boolean;
}

Example:

import { type CreateSpace, SpaceMemberRole } from '@lightdash/common';

const newSpace: CreateSpace = {
  name: 'Sales Analytics',
  isPrivate: true,
  access: [
    {
      userUuid: 'user-1',
      firstName: 'John',
      lastName: 'Doe',
      email: 'john@example.com',
      role: SpaceMemberRole.ADMIN,
      hasDirectAccess: true,
    },
    {
      userUuid: 'user-2',
      firstName: 'Jane',
      lastName: 'Smith',
      email: 'jane@example.com',
      role: SpaceMemberRole.EDITOR,
      hasDirectAccess: true,
    },
  ],
  tags: ['sales', 'quarterly'],
};

Environment Variables

DbtProjectEnvironmentVariable

interface DbtProjectEnvironmentVariable {
  key: string;
  value: string;
}

Used for setting DBT environment variables in project configuration.

Scheduler Settings

UpdateSchedulerSettings

interface UpdateSchedulerSettings {
  schedulerTimezone?: string;
  schedulerCron?: string;
  schedulerEnabled?: boolean;
  schedulerTarget?: 'dashboard' | 'csv';
}

Configure scheduled deliveries for dashboards and reports.

Complete Project and Space Example

import {
  type Project,
  type Space,
  ProjectType,
  DbtProjectType,
  WarehouseTypes,
  SupportedDbtVersions,
  SpaceMemberRole,
} from '@lightdash/common';

// Create a project
const project: Project = {
  organizationUuid: 'org-uuid',
  projectUuid: 'project-uuid',
  name: 'Analytics',
  type: ProjectType.DEFAULT,

  dbtConnection: {
    type: DbtProjectType.GITHUB,
    repository: 'company/analytics-dbt',
    branch: 'main',
    project_sub_path: '/',
    personal_access_token: 'github_pat_xxx',
  },

  warehouseConnection: {
    type: WarehouseTypes.BIGQUERY,
    project: 'my-gcp-project',
    dataset: 'analytics',
    keyfileContents: { /* service account key */ },
    startOfWeek: WeekDay.MONDAY,
  },

  dbtVersion: SupportedDbtVersions.V1_10,
};

// Create spaces for organization
const publicSpace: Space = {
  organizationUuid: 'org-uuid',
  uuid: 'space-1',
  name: 'Public Dashboards',
  projectUuid: 'project-uuid',
  isPrivate: false,
  slug: 'public-dashboards',
  pinnedListUuid: null,
  pinnedListOrder: null,
  chartCount: 5,
  dashboardCount: 3,

  dashboards: [
    {
      uuid: 'dash-1',
      name: 'Sales Overview',
      slug: 'sales-overview',
      updatedAt: new Date(),
      pinnedListOrder: 0,
      views: 150,
      firstViewedAt: new Date('2024-01-01'),
    },
  ],

  queries: [
    {
      uuid: 'chart-1',
      name: 'Monthly Revenue',
      slug: 'monthly-revenue',
      spaceUuid: 'space-1',
      updatedAt: new Date(),
      pinnedListOrder: 0,
      views: 75,
      firstViewedAt: new Date('2024-01-01'),
      chartType: ChartType.CARTESIAN,
      chartKind: ChartKind.LINE,
    },
  ],

  access: [], // Public space has no access restrictions

  groupsAccess: [],
};

const privateSpace: Space = {
  organizationUuid: 'org-uuid',
  uuid: 'space-2',
  name: 'Finance Reports',
  projectUuid: 'project-uuid',
  isPrivate: true,
  slug: 'finance-reports',
  pinnedListUuid: null,
  pinnedListOrder: null,
  chartCount: 8,
  dashboardCount: 2,

  dashboards: [],
  queries: [],

  access: [
    {
      userUuid: 'user-1',
      firstName: 'Finance',
      lastName: 'Manager',
      email: 'finance@example.com',
      role: SpaceMemberRole.ADMIN,
      hasDirectAccess: true,
    },
    {
      userUuid: 'user-2',
      firstName: 'Finance',
      lastName: 'Analyst',
      email: 'analyst@example.com',
      role: SpaceMemberRole.EDITOR,
      hasDirectAccess: true,
    },
  ],

  groupsAccess: [
    {
      groupUuid: 'group-1',
      groupName: 'Finance Team',
      spaceRole: SpaceMemberRole.EDITOR,
    },
  ],
};

Space Management Examples

Managing Space Access

import { type Space, SpaceMemberRole } from '@lightdash/common';

function addUserToSpace(
  space: Space,
  userUuid: string,
  role: SpaceMemberRole
): Space {
  return {
    ...space,
    access: [
      ...space.access,
      {
        userUuid,
        firstName: 'New',
        lastName: 'User',
        email: 'newuser@example.com',
        role,
        hasDirectAccess: true,
      },
    ],
  };
}

function updateUserRole(
  space: Space,
  userUuid: string,
  newRole: SpaceMemberRole
): Space {
  return {
    ...space,
    access: space.access.map(share =>
      share.userUuid === userUuid
        ? { ...share, role: newRole }
        : share
    ),
  };
}

function removeUserFromSpace(space: Space, userUuid: string): Space {
  return {
    ...space,
    access: space.access.filter(share => share.userUuid !== userUuid),
  };
}

Space Content Organization

import { type Space, type SpaceDashboard } from '@lightdash/common';

function getSpaceContent(space: Space) {
  return {
    dashboards: space.dashboards.sort((a, b) => {
      // Sort by pinned order, then by views
      if (a.pinnedListOrder !== null && b.pinnedListOrder !== null) {
        return a.pinnedListOrder - b.pinnedListOrder;
      }
      return b.views - a.views;
    }),
    charts: space.queries.sort((a, b) => {
      if (a.pinnedListOrder !== null && b.pinnedListOrder !== null) {
        return a.pinnedListOrder - b.pinnedListOrder;
      }
      return b.views - a.views;
    }),
  };
}

function getPopularContent(space: Space, limit: number = 5) {
  const allContent = [
    ...space.dashboards.map(d => ({ ...d, type: 'dashboard' as const })),
    ...space.queries.map(q => ({ ...q, type: 'chart' as const })),
  ];

  return allContent
    .sort((a, b) => b.views - a.views)
    .slice(0, limit);
}