tenantpilot/specs/001-global-policy-search/data-model.md
2025-12-05 22:06:22 +01:00

4.4 KiB

Data Model: Global Policy Search

Feature: 001-global-policy-search
Version: 1.0.0

Entity: PolicySetting

Drizzle Schema Definition

// lib/db/schema/policySettings.ts

import { pgTable, text, timestamp, index } from 'drizzle-orm/pg-core';
import { createId } from '@paralleldrive/cuid2';

export const policySettings = pgTable(
  'policy_settings',
  {
    id: text('id')
      .primaryKey()
      .$defaultFn(() => createId()),
    
    tenantId: text('tenant_id').notNull(),
    
    policyName: text('policy_name').notNull(),
    
    policyType: text('policy_type').notNull(),
    // e.g., 'deviceConfiguration', 'compliancePolicy', 'windowsUpdateForBusiness'
    
    settingName: text('setting_name').notNull(),
    
    settingValue: text('setting_value').notNull(),
    
    graphPolicyId: text('graph_policy_id').notNull(),
    // Microsoft Graph ID of the policy
    
    lastSyncedAt: timestamp('last_synced_at').defaultNow().notNull(),
    
    createdAt: timestamp('created_at').defaultNow().notNull(),
  },
  (table) => ({
    // Index for tenant-scoped searches
    tenantIdIdx: index('policy_settings_tenant_id_idx').on(table.tenantId),
    
    // Index for search queries
    settingNameIdx: index('policy_settings_setting_name_idx').on(table.settingName),
    
    // Composite index for upsert operations
    upsertIdx: index('policy_settings_upsert_idx').on(
      table.tenantId,
      table.graphPolicyId,
      table.settingName
    ),
  })
);

// Type exports for type-safe queries
export type PolicySetting = typeof policySettings.$inferSelect;
export type NewPolicySetting = typeof policySettings.$inferInsert;

Field Specifications

Field Type Required Description
id CUID2 Yes Auto-generated unique identifier
tenantId text Yes Azure AD tenant ID (from tid claim)
policyName text Yes Display name of the Intune policy
policyType text Yes Type/category of policy (see Policy Types below)
settingName text Yes Name of the individual setting
settingValue text Yes Value of the setting (JSON-stringified if complex)
graphPolicyId text Yes Microsoft Graph API policy ID
lastSyncedAt timestamp Yes When this setting was last synced from Graph
createdAt timestamp Yes Record creation timestamp

Policy Types (enum values)

export const POLICY_TYPES = [
  'deviceConfiguration',
  'compliancePolicy', 
  'windowsUpdateForBusiness',
  'endpointSecurity',
  'appConfiguration',
  'enrollmentRestriction',
  'conditionalAccess',
] as const;

export type PolicyType = typeof POLICY_TYPES[number];

Relationships

User (Azure AD)
  └── tenantId ─────────┐
                        │
PolicySetting           │
  └── tenantId ─────────┘ (implicit FK via Azure AD tid)

Note: No explicit foreign key to users table. Tenant isolation is enforced by filtering on tenantId which comes from the authenticated Azure AD session.


Validation Rules

Input Validation (Zod Schema)

// lib/validators/policySettings.ts
import { z } from 'zod';
import { POLICY_TYPES } from '@/lib/db/schema/policySettings';

export const policySettingSchema = z.object({
  tenantId: z.string().min(1, 'Tenant ID is required'),
  policyName: z.string().min(1, 'Policy name is required'),
  policyType: z.enum(POLICY_TYPES),
  settingName: z.string().min(1, 'Setting name is required'),
  settingValue: z.string(), // Can be empty string
  graphPolicyId: z.string().min(1, 'Graph Policy ID is required'),
});

export const bulkPolicySettingsSchema = z.object({
  settings: z.array(policySettingSchema).min(1).max(1000),
});

export type PolicySettingInput = z.infer<typeof policySettingSchema>;
export type BulkPolicySettingsInput = z.infer<typeof bulkPolicySettingsSchema>;

Indexes

Index Name Columns Purpose
policy_settings_tenant_id_idx tenant_id Fast tenant filtering
policy_settings_setting_name_idx setting_name Fast search on setting names
policy_settings_upsert_idx tenant_id, graph_policy_id, setting_name Efficient upsert operations

Migration

After adding the schema, run:

npm run db:push

Or for production with migrations:

npm run db:generate
npm run db:migrate