workspace-builder

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

App Engine Studio & Workspace Builder for ServiceNow

适用于ServiceNow的App Engine Studio与Workspace Builder

App Engine Studio (AES) enables low-code application development with custom workspaces.
App Engine Studio(AES)支持通过自定义工作区进行低代码应用开发。

AES Architecture

AES架构

Application (sys_scope)
    ├── Tables & Forms
    ├── Workflows
    ├── Workspaces (sys_aw_workspace)
    │   ├── Lists
    │   ├── Forms
    │   └── Dashboards
    └── Portals
Application (sys_scope)
    ├── Tables & Forms
    ├── Workflows
    ├── Workspaces (sys_aw_workspace)
    │   ├── Lists
    │   ├── Forms
    │   └── Dashboards
    └── Portals

Key Tables

核心表

TablePurpose
sys_scope
Application scope
sys_app
Application record
sys_aw_workspace
Workspace definition
sys_ux_page
UI Builder pages
sys_ux_macroponent
Custom components
表名用途
sys_scope
应用作用域
sys_app
应用记录
sys_aw_workspace
工作区定义
sys_ux_page
UI Builder页面
sys_ux_macroponent
自定义组件

Application Development (ES5)

应用开发(ES5)

Create Scoped Application

创建作用域应用

javascript
// Create scoped application (ES5 ONLY!)
var app = new GlideRecord('sys_scope');
app.initialize();

// Basic info
app.setValue('name', 'IT Asset Tracker');
app.setValue('scope', 'x_myco_asset_track');
app.setValue('short_description', 'Track IT assets across the organization');
app.setValue('version', '1.0.0');

// Vendor
app.setValue('vendor', 'My Company');
app.setValue('vendor_prefix', 'x_myco');

// License
app.setValue('licensable', true);

app.insert();
javascript
// 创建作用域应用(仅支持ES5!)
var app = new GlideRecord('sys_scope');
app.initialize();

// 基础信息
app.setValue('name', 'IT Asset Tracker');
app.setValue('scope', 'x_myco_asset_track');
app.setValue('short_description', '在企业内追踪IT资产');
app.setValue('version', '1.0.0');

// 供应商信息
app.setValue('vendor', 'My Company');
app.setValue('vendor_prefix', 'x_myco');

// 许可设置
app.setValue('licensable', true);

app.insert();

Create Application Table

创建应用表

javascript
// Create table in scoped app (ES5 ONLY!)
function createAppTable(scope, tableDef) {
    var table = new GlideRecord('sys_db_object');
    table.initialize();

    table.setValue('name', scope + '_' + tableDef.name);
    table.setValue('label', tableDef.label);
    table.setValue('super_class', tableDef.extends || 'task');

    // Scope assignment
    table.setValue('sys_scope', getAppSysId(scope));

    // Options
    table.setValue('is_extendable', tableDef.extendable || false);
    table.setValue('create_access_controls', true);

    table.insert();

    // Create fields
    if (tableDef.fields) {
        for (var i = 0; i < tableDef.fields.length; i++) {
            createField(scope + '_' + tableDef.name, tableDef.fields[i]);
        }
    }

    return table.getUniqueValue();
}

// Example
createAppTable('x_myco_asset_track', {
    name: 'asset_item',
    label: 'Asset Item',
    extends: 'cmdb_ci',
    fields: [
        { name: 'u_purchase_date', label: 'Purchase Date', type: 'glide_date' },
        { name: 'u_warranty_end', label: 'Warranty End', type: 'glide_date' },
        { name: 'u_assigned_user', label: 'Assigned User', type: 'reference', reference: 'sys_user' }
    ]
});
javascript
// 在作用域应用中创建表(仅支持ES5!)
function createAppTable(scope, tableDef) {
    var table = new GlideRecord('sys_db_object');
    table.initialize();

    table.setValue('name', scope + '_' + tableDef.name);
    table.setValue('label', tableDef.label);
    table.setValue('super_class', tableDef.extends || 'task');

    // 分配作用域
    table.setValue('sys_scope', getAppSysId(scope));

    // 选项设置
    table.setValue('is_extendable', tableDef.extendable || false);
    table.setValue('create_access_controls', true);

    table.insert();

    // 创建字段
    if (tableDef.fields) {
        for (var i = 0; i < tableDef.fields.length; i++) {
            createField(scope + '_' + tableDef.name, tableDef.fields[i]);
        }
    }

    return table.getUniqueValue();
}

// 示例
createAppTable('x_myco_asset_track', {
    name: 'asset_item',
    label: '资产项',
    extends: 'cmdb_ci',
    fields: [
        { name: 'u_purchase_date', label: '采购日期', type: 'glide_date' },
        { name: 'u_warranty_end', label: '保修截止日期', type: 'glide_date' },
        { name: 'u_assigned_user', label: '分配用户', type: 'reference', reference: 'sys_user' }
    ]
});

Workspace Configuration (ES5)

工作区配置(ES5)

Create Custom Workspace

创建自定义工作区

javascript
// Create workspace (ES5 ONLY!)
var workspace = new GlideRecord('sys_aw_workspace');
workspace.initialize();

workspace.setValue('name', 'asset_tracker_workspace');
workspace.setValue('title', 'Asset Tracker');
workspace.setValue('description', 'Workspace for IT asset management');

// Primary table
workspace.setValue('primary_table', 'x_myco_asset_track_asset_item');

// URL
workspace.setValue('url', 'asset-tracker');

// Branding
workspace.setValue('icon', 'laptop');
workspace.setValue('color', '#2E7D32');

// App scope
workspace.setValue('sys_scope', appScopeSysId);

// Features
workspace.setValue('agent_assist_enabled', false);
workspace.setValue('contextual_side_panel_enabled', true);

workspace.insert();
javascript
// 创建工作区(仅支持ES5!)
var workspace = new GlideRecord('sys_aw_workspace');
workspace.initialize();

workspace.setValue('name', 'asset_tracker_workspace');
workspace.setValue('title', '资产追踪器');
workspace.setValue('description', 'IT资产管理工作区');

// 主表
workspace.setValue('primary_table', 'x_myco_asset_track_asset_item');

// URL
workspace.setValue('url', 'asset-tracker');

// 品牌设置
workspace.setValue('icon', 'laptop');
workspace.setValue('color', '#2E7D32');

// 应用作用域
workspace.setValue('sys_scope', appScopeSysId);

// 功能开关
workspace.setValue('agent_assist_enabled', false);
workspace.setValue('contextual_side_panel_enabled', true);

workspace.insert();

Configure Workspace Lists

配置工作区列表

javascript
// Create workspace list (ES5 ONLY!)
function createWorkspaceList(workspaceSysId, listDef) {
    var list = new GlideRecord('sys_aw_list');
    list.initialize();

    list.setValue('workspace', workspaceSysId);
    list.setValue('name', listDef.name);
    list.setValue('table', listDef.table);

    // Filter
    list.setValue('filter', listDef.filter || '');

    // Columns
    list.setValue('columns', listDef.columns.join(','));

    // Sorting
    if (listDef.orderBy) {
        list.setValue('order_by', listDef.orderBy);
        list.setValue('order_by_desc', listDef.orderDesc || false);
    }

    // Grouping
    if (listDef.groupBy) {
        list.setValue('group_by', listDef.groupBy);
    }

    list.insert();

    return list.getUniqueValue();
}

// Example lists
createWorkspaceList(workspaceSysId, {
    name: 'My Assets',
    table: 'x_myco_asset_track_asset_item',
    filter: 'u_assigned_user=javascript:gs.getUserID()',
    columns: ['number', 'name', 'u_purchase_date', 'u_warranty_end', 'state']
});

createWorkspaceList(workspaceSysId, {
    name: 'Expiring Warranties',
    table: 'x_myco_asset_track_asset_item',
    filter: 'u_warranty_endBETWEENjavascript:gs.daysAgoStart(0)@javascript:gs.daysAgoEnd(-30)',
    columns: ['number', 'name', 'u_assigned_user', 'u_warranty_end'],
    orderBy: 'u_warranty_end'
});
javascript
// 创建工作区列表(仅支持ES5!)
function createWorkspaceList(workspaceSysId, listDef) {
    var list = new GlideRecord('sys_aw_list');
    list.initialize();

    list.setValue('workspace', workspaceSysId);
    list.setValue('name', listDef.name);
    list.setValue('table', listDef.table);

    // 过滤器
    list.setValue('filter', listDef.filter || '');

    // 列
    list.setValue('columns', listDef.columns.join(','));

    // 排序
    if (listDef.orderBy) {
        list.setValue('order_by', listDef.orderBy);
        list.setValue('order_by_desc', listDef.orderDesc || false);
    }

    // 分组
    if (listDef.groupBy) {
        list.setValue('group_by', listDef.groupBy);
    }

    list.insert();

    return list.getUniqueValue();
}

// 示例列表
createWorkspaceList(workspaceSysId, {
    name: '我的资产',
    table: 'x_myco_asset_track_asset_item',
    filter: 'u_assigned_user=javascript:gs.getUserID()',
    columns: ['number', 'name', 'u_purchase_date', 'u_warranty_end', 'state']
});

createWorkspaceList(workspaceSysId, {
    name: '即将到期的保修',
    table: 'x_myco_asset_track_asset_item',
    filter: 'u_warranty_endBETWEENjavascript:gs.daysAgoStart(0)@javascript:gs.daysAgoEnd(-30)',
    columns: ['number', 'name', 'u_assigned_user', 'u_warranty_end'],
    orderBy: 'u_warranty_end'
});

UI Builder Pages (ES5)

UI Builder页面(ES5)

Page Configuration

页面配置

javascript
// Create UI Builder page (ES5 ONLY!)
// Note: Full page creation typically done via UI Builder

var page = new GlideRecord('sys_ux_page');
page.initialize();

page.setValue('name', 'asset_dashboard');
page.setValue('title', 'Asset Dashboard');
page.setValue('description', 'Dashboard for asset overview');

// Page type
page.setValue('page_type', 'workspace');

// Workspace link
page.setValue('workspace', workspaceSysId);

// Scope
page.setValue('sys_scope', appScopeSysId);

page.insert();
javascript
// 创建UI Builder页面(仅支持ES5!)
// 注意:完整页面创建通常通过UI Builder完成

var page = new GlideRecord('sys_ux_page');
page.initialize();

page.setValue('name', 'asset_dashboard');
page.setValue('title', '资产仪表盘');
page.setValue('description', '资产概览仪表盘');

// 页面类型
page.setValue('page_type', 'workspace');

// 关联工作区
page.setValue('workspace', workspaceSysId);

// 作用域
page.setValue('sys_scope', appScopeSysId);

page.insert();

Custom Component (Macroponent)

自定义组件(Macroponent)

javascript
// Create custom macroponent definition (ES5 ONLY!)
// Note: Actual components created via UI Builder

var component = new GlideRecord('sys_ux_macroponent');
component.initialize();

component.setValue('name', 'asset_summary_card');
component.setValue('label', 'Asset Summary Card');
component.setValue('description', 'Displays asset summary information');

// Component category
component.setValue('category', 'data_visualization');

// Scope
component.setValue('sys_scope', appScopeSysId);

// Properties (inputs)
component.setValue('properties', JSON.stringify([
    { name: 'title', type: 'string', label: 'Card Title' },
    { name: 'assetTable', type: 'string', label: 'Asset Table' },
    { name: 'filter', type: 'string', label: 'Filter' }
]));

component.insert();
javascript
// 创建自定义Macroponent定义(仅支持ES5!)
// 注意:实际组件创建通过UI Builder完成

var component = new GlideRecord('sys_ux_macroponent');
component.initialize();

component.setValue('name', 'asset_summary_card');
component.setValue('label', '资产汇总卡片');
component.setValue('description', '展示资产汇总信息');

// 组件分类
component.setValue('category', 'data_visualization');

// 作用域
component.setValue('sys_scope', appScopeSysId);

// 属性(输入项)
component.setValue('properties', JSON.stringify([
    { name: 'title', type: 'string', label: '卡片标题' },
    { name: 'assetTable', type: 'string', label: '资产表' },
    { name: 'filter', type: 'string', label: '过滤器' }
]));

component.insert();

Data Brokers (ES5)

数据代理(ES5)

Create Data Broker

创建数据代理

javascript
// Data broker for workspace data (ES5 ONLY!)
// Data brokers provide data to UI Builder pages

var broker = new GlideRecord('sys_ux_data_broker');
broker.initialize();

broker.setValue('name', 'asset_stats');
broker.setValue('label', 'Asset Statistics');

// Data source type
broker.setValue('type', 'script');

// Script to fetch data (ES5 ONLY!)
broker.setValue('script',
    '(function getData(inputs) {\n' +
    '    var result = {\n' +
    '        total: 0,\n' +
    '        assigned: 0,\n' +
    '        available: 0,\n' +
    '        expiring_warranty: 0\n' +
    '    };\n' +
    '    \n' +
    '    var ga = new GlideAggregate("x_myco_asset_track_asset_item");\n' +
    '    ga.addAggregate("COUNT");\n' +
    '    ga.groupBy("state");\n' +
    '    ga.query();\n' +
    '    \n' +
    '    while (ga.next()) {\n' +
    '        var count = parseInt(ga.getAggregate("COUNT"), 10);\n' +
    '        result.total += count;\n' +
    '        \n' +
    '        var state = ga.getValue("state");\n' +
    '        if (state === "in_use") {\n' +
    '            result.assigned = count;\n' +
    '        } else if (state === "available") {\n' +
    '            result.available = count;\n' +
    '        }\n' +
    '    }\n' +
    '    \n' +
    '    // Expiring warranties\n' +
    '    var expiring = new GlideAggregate("x_myco_asset_track_asset_item");\n' +
    '    expiring.addQuery("u_warranty_end", "BETWEEN", "javascript:gs.daysAgoStart(0)@javascript:gs.daysAgoEnd(-30)");\n' +
    '    expiring.addAggregate("COUNT");\n' +
    '    expiring.query();\n' +
    '    \n' +
    '    if (expiring.next()) {\n' +
    '        result.expiring_warranty = parseInt(expiring.getAggregate("COUNT"), 10);\n' +
    '    }\n' +
    '    \n' +
    '    return result;\n' +
    '})(inputs);'
);

broker.setValue('sys_scope', appScopeSysId);

broker.insert();
javascript
// 用于工作区数据的数据代理(仅支持ES5!)
// 数据代理为UI Builder页面提供数据

var broker = new GlideRecord('sys_ux_data_broker');
broker.initialize();

broker.setValue('name', 'asset_stats');
broker.setValue('label', '资产统计');

// 数据源类型
broker.setValue('type', 'script');

// 数据获取脚本(仅支持ES5!)
broker.setValue('script',
    '(function getData(inputs) {\n' +
    '    var result = {\n' +
    '        total: 0,\n' +
    '        assigned: 0,\n' +
    '        available: 0,\n' +
    '        expiring_warranty: 0\n' +
    '    };\n' +
    '    \n' +
    '    var ga = new GlideAggregate("x_myco_asset_track_asset_item");\n' +
    '    ga.addAggregate("COUNT");\n' +
    '    ga.groupBy("state");\n' +
    '    ga.query();\n' +
    '    \n' +
    '    while (ga.next()) {\n' +
    '        var count = parseInt(ga.getAggregate("COUNT"), 10);\n' +
    '        result.total += count;\n' +
    '        \n' +
    '        var state = ga.getValue("state");\n' +
    '        if (state === "in_use") {\n' +
    '            result.assigned = count;\n' +
    '        } else if (state === "available") {\n' +
    '            result.available = count;\n' +
    '        }\n' +
    '    }\n' +
    '    \n' +
    '    // 即将到期的保修
' +
    '    var expiring = new GlideAggregate("x_myco_asset_track_asset_item");\n' +
    '    expiring.addQuery("u_warranty_end", "BETWEEN", "javascript:gs.daysAgoStart(0)@javascript:gs.daysAgoEnd(-30)");\n' +
    '    expiring.addAggregate("COUNT");\n' +
    '    expiring.query();\n' +
    '    \n' +
    '    if (expiring.next()) {\n' +
    '        result.expiring_warranty = parseInt(expiring.getAggregate("COUNT"), 10);\n' +
    '    }\n' +
    '    \n' +
    '    return result;\n' +
    '})(inputs);'
);

broker.setValue('sys_scope', appScopeSysId);

broker.insert();

Application Deployment (ES5)

应用部署(ES5)

Create Update Set

创建更新集

javascript
// Create update set for app deployment (ES5 ONLY!)
function createAppUpdateSet(appName, description) {
    var updateSet = new GlideRecord('sys_update_set');
    updateSet.initialize();
    updateSet.setValue('name', appName + ' - ' + new GlideDateTime().getDate());
    updateSet.setValue('description', description);
    updateSet.setValue('application', getAppSysId(appName));
    updateSet.setValue('state', 'in progress');
    return updateSet.insert();
}
javascript
// 创建用于应用部署的更新集(仅支持ES5!)
function createAppUpdateSet(appName, description) {
    var updateSet = new GlideRecord('sys_update_set');
    updateSet.initialize();
    updateSet.setValue('name', appName + ' - ' + new GlideDateTime().getDate());
    updateSet.setValue('description', description);
    updateSet.setValue('application', getAppSysId(appName));
    updateSet.setValue('state', 'in progress');
    return updateSet.insert();
}

Export Application

导出应用

javascript
// Prepare app for export (ES5 ONLY!)
function prepareAppExport(appScope) {
    // Validate all components
    var issues = [];

    // Check for missing dependencies
    var dependency = new GlideRecord('sys_app_dependency');
    dependency.addQuery('app.scope', appScope);
    dependency.query();

    while (dependency.next()) {
        if (!isDependencyInstalled(dependency.getValue('dependency'))) {
            issues.push('Missing dependency: ' + dependency.dependency.getDisplayValue());
        }
    }

    // Validate update sets
    var updateSet = new GlideRecord('sys_update_set');
    updateSet.addQuery('application.scope', appScope);
    updateSet.addQuery('state', 'in progress');
    updateSet.query();

    while (updateSet.next()) {
        issues.push('Open update set: ' + updateSet.getValue('name'));
    }

    return {
        ready: issues.length === 0,
        issues: issues
    };
}
javascript
// 准备应用导出(仅支持ES5!)
function prepareAppExport(appScope) {
    // 验证所有组件
    var issues = [];

    // 检查缺失的依赖
    var dependency = new GlideRecord('sys_app_dependency');
    dependency.addQuery('app.scope', appScope);
    dependency.query();

    while (dependency.next()) {
        if (!isDependencyInstalled(dependency.getValue('dependency'))) {
            issues.push('缺失依赖: ' + dependency.dependency.getDisplayValue());
        }
    }

    // 验证更新集
    var updateSet = new GlideRecord('sys_update_set');
    updateSet.addQuery('application.scope', appScope);
    updateSet.addQuery('state', 'in progress');
    updateSet.query();

    while (updateSet.next()) {
        issues.push('未完成的更新集: ' + updateSet.getValue('name'));
    }

    return {
        ready: issues.length === 0,
        issues: issues
    };
}

MCP Tool Integration

MCP工具集成

Available Tools

可用工具

ToolPurpose
snow_query_table
Query app components
snow_execute_script_with_output
Test app scripts
snow_find_artifact
Find configurations
snow_update_set_create
Create update sets
工具用途
snow_query_table
查询应用组件
snow_execute_script_with_output
测试应用脚本
snow_find_artifact
查找配置项
snow_update_set_create
创建更新集

Example Workflow

示例工作流

javascript
// 1. Query applications
await snow_query_table({
    table: 'sys_scope',
    query: 'scopeSTARTSWITHx_',
    fields: 'name,scope,version,vendor'
});

// 2. Find app tables
await snow_query_table({
    table: 'sys_db_object',
    query: 'nameSTARTSWITHx_myco',
    fields: 'name,label,super_class'
});

// 3. Get workspace configs
await snow_query_table({
    table: 'sys_aw_workspace',
    query: 'sys_scope.scopeSTARTSWITHx_',
    fields: 'name,title,primary_table,url'
});
javascript
// 1. 查询应用
await snow_query_table({
    table: 'sys_scope',
    query: 'scopeSTARTSWITHx_',
    fields: 'name,scope,version,vendor'
});

// 2. 查找应用表
await snow_query_table({
    table: 'sys_db_object',
    query: 'nameSTARTSWITHx_myco',
    fields: 'name,label,super_class'
});

// 3. 获取工作区配置
await snow_query_table({
    table: 'sys_aw_workspace',
    query: 'sys_scope.scopeSTARTSWITHx_',
    fields: 'name,title,primary_table,url'
});

Best Practices

最佳实践

  1. Naming Conventions - Consistent prefixes
  2. Scoped Apps - Use scope isolation
  3. Reusable Components - Modular design
  4. Data Brokers - Efficient data fetching
  5. Workspace Design - User-focused layouts
  6. Testing - ATF tests for apps
  7. Documentation - App documentation
  8. ES5 Only - No modern JavaScript syntax
  1. 命名规范 - 使用统一前缀
  2. 作用域应用 - 利用作用域隔离
  3. 可复用组件 - 采用模块化设计
  4. 数据代理 - 高效获取数据
  5. 工作区设计 - 以用户为中心的布局
  6. 测试 - 为应用编写ATF测试
  7. 文档 - 完善应用文档
  8. 仅用ES5 - 不使用现代JavaScript语法