domain-separation

安装量: 49
排名: #15261

安装

npx skills add https://github.com/groeimetai/snow-flow --skill domain-separation

Domain Separation for ServiceNow

Domain Separation enables multi-tenancy by partitioning data and processes between domains.

Domain Architecture TOP (Global) ├── Domain A (Customer 1) │ ├── Sub-domain A1 │ └── Sub-domain A2 └── Domain B (Customer 2) └── Sub-domain B1

Key Tables Table Purpose domain Domain definitions sys_user_has_domain User domain membership domain_path Domain hierarchy paths sys_db_object Table domain settings Domain Configuration (ES5) Create Domain // Create domain (ES5 ONLY!) var domain = new GlideRecord('domain'); domain.initialize();

domain.setValue('name', 'Acme Corp'); domain.setValue('description', 'Domain for Acme Corporation');

// Parent domain (empty for top-level) domain.setValue('parent', parentDomainSysId);

// Domain visibility domain.setValue('active', true);

domain.insert();

Domain-Aware Queries // Query respecting domain separation (ES5 ONLY!) function getDomainAwareRecords(tableName, query) { var gr = new GlideRecord(tableName);

// Domain separation is automatic when enabled
// Records are filtered to user's visible domains

if (query) {
    gr.addEncodedQuery(query);
}
gr.query();

var records = [];
while (gr.next()) {
    records.push({
        sys_id: gr.getUniqueValue(),
        sys_domain: gr.getValue('sys_domain'),
        sys_domain_path: gr.getValue('sys_domain_path')
    });
}

return records;

}

Cross-Domain Access // Access records across domains (requires elevated privileges) (ES5 ONLY!) function getCrossdomainRecords(tableName) { var gr = new GlideRecord(tableName);

// Disable domain separation for this query
gr.setQueryReferences(false);

// Query all domains
gr.queryNoDomain();

var records = [];
while (gr.next()) {
    records.push({
        sys_id: gr.getUniqueValue(),
        domain: gr.sys_domain.getDisplayValue()
    });
}

return records;

}

User Domain Membership (ES5) Assign User to Domain // Add user to domain (ES5 ONLY!) function addUserToDomain(userSysId, domainSysId, isPrimary) { // Check if already assigned var existing = new GlideRecord('sys_user_has_domain'); existing.addQuery('user', userSysId); existing.addQuery('domain', domainSysId); existing.query();

if (existing.next()) {
    return existing.getUniqueValue();
}

// Create assignment
var assignment = new GlideRecord('sys_user_has_domain');
assignment.initialize();
assignment.setValue('user', userSysId);
assignment.setValue('domain', domainSysId);
assignment.setValue('primary', isPrimary);
return assignment.insert();

}

Get User's Domains // Get domains accessible to user (ES5 ONLY!) function getUserDomains(userSysId) { var domains = [];

var membership = new GlideRecord('sys_user_has_domain');
membership.addQuery('user', userSysId);
membership.query();

while (membership.next()) {
    var domain = membership.domain.getRefRecord();
    domains.push({
        sys_id: domain.getUniqueValue(),
        name: domain.getValue('name'),
        is_primary: membership.getValue('primary') === 'true'
    });
}

return domains;

}

Domain-Separated Tables (ES5) Configure Table for Domain Separation // Enable domain separation on table (ES5 ONLY!) // Note: This is typically done via UI, shown for reference

var tableConfig = new GlideRecord('sys_db_object'); if (tableConfig.get('name', 'u_custom_table')) { // Enable domain separation tableConfig.setValue('domain_separated', true);

// Domain separation type
// 'simple' = records belong to one domain
// 'containment' = records visible to parent domains
tableConfig.setValue('domain_id_type', 'simple');

tableConfig.update();

}

Create Record in Specific Domain // Create record in specific domain (ES5 ONLY!) function createInDomain(tableName, data, domainSysId) { var gr = new GlideRecord(tableName); gr.initialize();

// Set field values
for (var field in data) {
    if (data.hasOwnProperty(field)) {
        gr.setValue(field, data[field]);
    }
}

// Set domain
gr.setValue('sys_domain', domainSysId);

return gr.insert();

}

Domain Picker (ES5) Get Available Domains for Picker // Get domains for domain picker widget (ES5 ONLY!) function getDomainsForPicker() { var domains = []; var userId = gs.getUserID();

// Get user's accessible domains
var membership = new GlideRecord('sys_user_has_domain');
membership.addQuery('user', userId);
membership.query();

while (membership.next()) {
    var domain = membership.domain.getRefRecord();
    if (domain.getValue('active') === 'true') {
        domains.push({
            sys_id: domain.getUniqueValue(),
            name: domain.getValue('name'),
            is_primary: membership.getValue('primary') === 'true',
            is_current: domain.getUniqueValue() === gs.getSession().getCurrentDomainID()
        });
    }
}

// Sort: primary first, then alphabetically
domains.sort(function(a, b) {
    if (a.is_primary && !b.is_primary) return -1;
    if (!a.is_primary && b.is_primary) return 1;
    return a.name.localeCompare(b.name);
});

return domains;

}

Switch Current Domain // Switch user's current domain (ES5 ONLY!) function switchDomain(domainSysId) { var session = gs.getSession();

// Verify user has access
var membership = new GlideRecord('sys_user_has_domain');
membership.addQuery('user', gs.getUserID());
membership.addQuery('domain', domainSysId);
membership.query();

if (!membership.next()) {
    gs.addErrorMessage('You do not have access to this domain');
    return false;
}

// Switch domain
session.setDomainID(domainSysId);
gs.addInfoMessage('Switched to domain: ' + membership.domain.getDisplayValue());

return true;

}

Domain Visibility Rules (ES5) Check Domain Visibility // Check if record is visible in current domain (ES5 ONLY!) function isRecordVisibleInDomain(tableName, recordSysId) { var gr = new GlideRecord(tableName); gr.addQuery('sys_id', recordSysId); gr.query();

// If record is found, it's visible in current domain context
return gr.hasNext();

}

Get Domain Path // Get full domain hierarchy path (ES5 ONLY!) function getDomainPath(domainSysId) { var path = [];

var domain = new GlideRecord('domain');
if (!domain.get(domainSysId)) {
    return path;
}

// Build path from current to root
while (domain.isValidRecord()) {
    path.unshift({
        sys_id: domain.getUniqueValue(),
        name: domain.getValue('name')
    });

    if (!domain.parent) break;
    domain = domain.parent.getRefRecord();
}

return path;

}

MSP/Managed Services Patterns (ES5) Onboard New Tenant // Create new tenant domain with initial setup (ES5 ONLY!) function onboardTenant(tenantData) { // Create domain var domain = new GlideRecord('domain'); domain.initialize(); domain.setValue('name', tenantData.name); domain.setValue('parent', tenantData.parentDomain || ''); var domainSysId = domain.insert();

// Create tenant admin user
var adminUser = new GlideRecord('sys_user');
adminUser.initialize();
adminUser.setValue('user_name', tenantData.adminEmail);
adminUser.setValue('email', tenantData.adminEmail);
adminUser.setValue('first_name', tenantData.adminFirstName);
adminUser.setValue('last_name', tenantData.adminLastName);
var adminSysId = adminUser.insert();

// Assign user to domain
addUserToDomain(adminSysId, domainSysId, true);

// Assign tenant admin role
var role = new GlideRecord('sys_user_has_role');
role.initialize();
role.setValue('user', adminSysId);
role.setValue('role', getTenantAdminRoleSysId());
role.insert();

return {
    domain_sys_id: domainSysId,
    admin_sys_id: adminSysId
};

}

MCP Tool Integration Available Tools Tool Purpose snow_query_table Query domain-aware data snow_execute_script_with_output Test domain scripts snow_find_artifact Find domain configurations Example Workflow // 1. Query domains await snow_query_table({ table: 'domain', query: 'active=true', fields: 'name,parent,sys_id' });

// 2. Get user domain memberships await snow_query_table({ table: 'sys_user_has_domain', query: 'user=user_sys_id', fields: 'domain,primary' });

// 3. Check domain-separated tables await snow_query_table({ table: 'sys_db_object', query: 'domain_separated=true', fields: 'name,label,domain_id_type' });

Best Practices Plan Hierarchy - Design domain structure before implementation Minimal Domains - Only create necessary separation User Access - Assign minimum required domains Testing - Test with domain picker Global Data - Keep shared data in TOP domain Performance - Domain queries add overhead Documentation - Document domain purposes ES5 Only - No modern JavaScript syntax

返回排行榜