workspace-builder
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseApp 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
└── PortalsApplication (sys_scope)
├── Tables & Forms
├── Workflows
├── Workspaces (sys_aw_workspace)
│ ├── Lists
│ ├── Forms
│ └── Dashboards
└── PortalsKey Tables
核心表
| Table | Purpose |
|---|---|
| Application scope |
| Application record |
| Workspace definition |
| UI Builder pages |
| Custom components |
| 表名 | 用途 |
|---|---|
| 应用作用域 |
| 应用记录 |
| 工作区定义 |
| UI Builder页面 |
| 自定义组件 |
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
可用工具
| Tool | Purpose |
|---|---|
| Query app components |
| Test app scripts |
| Find configurations |
| Create update sets |
| 工具 | 用途 |
|---|---|
| 查询应用组件 |
| 测试应用脚本 |
| 查找配置项 |
| 创建更新集 |
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
最佳实践
- Naming Conventions - Consistent prefixes
- Scoped Apps - Use scope isolation
- Reusable Components - Modular design
- Data Brokers - Efficient data fetching
- Workspace Design - User-focused layouts
- Testing - ATF tests for apps
- Documentation - App documentation
- ES5 Only - No modern JavaScript syntax
- 命名规范 - 使用统一前缀
- 作用域应用 - 利用作用域隔离
- 可复用组件 - 采用模块化设计
- 数据代理 - 高效获取数据
- 工作区设计 - 以用户为中心的布局
- 测试 - 为应用编写ATF测试
- 文档 - 完善应用文档
- 仅用ES5 - 不使用现代JavaScript语法