Loading...
Loading...
Compare original and translation side by side
| Feature | Global Scope | Scoped App |
|---|---|---|
| Naming conflicts | Possible | Prevented (x_prefix) |
| Portability | Difficult | Easy (Update Sets) |
| Security | Open | Controlled (Cross-scope) |
| Store publishing | No | Yes |
| Dependencies | Implicit | Explicit |
| 特性 | 全局作用域 | 限定范围应用 |
|---|---|---|
| 命名冲突 | 可能存在 | 已避免(x_前缀) |
| 可移植性 | 困难 | 容易(更新集) |
| 安全性 | 开放 | 受控(跨作用域) |
| 商店发布 | 不支持 | 支持 |
| 依赖关系 | 隐式 | 显式 |
1. Navigate: System Applications > Studio
2. Click: Create Application
3. Enter:
- Name: "My Custom App"
- Scope: "x_mycom_myapp" (auto-generated)
- Version: 1.0.0
4. Configure:
- Runtime access: Check tables needing cross-scope access1. 导航至:系统应用 > Studio
2. 点击:创建应用
3. 输入:
- 名称:"My Custom App"
- 作用域:"x_mycom_myapp"(自动生成)
- 版本:1.0.0
4. 配置:
- 运行时访问:勾选需要跨作用域访问的表snow_create_application({
name: "My Custom Application",
scope: "x_mycom_custom",
version: "1.0.0",
description: "Custom application for..."
});snow_create_application({
name: "My Custom Application",
scope: "x_mycom_custom",
version: "1.0.0",
description: "Custom application for..."
});x_[vendor]_[app]
Examples:
- x_acme_hr (ACME Corp HR App)
- x_mycom_inventory (My Company Inventory)
- x_snc_global (ServiceNow Global)x_[供应商]_[应用]
示例:
- x_acme_hr (ACME Corp HR应用)
- x_mycom_inventory (我司库存管理应用)
- x_snc_global (ServiceNow全局)// Scoped tables are automatically prefixed
// Table name in Studio: "task_tracker"
// Actual table name: "x_mycom_myapp_task_tracker"
// Creating records
var gr = new GlideRecord('x_mycom_myapp_task_tracker');
gr.initialize();
gr.setValue('name', 'My Task');
gr.insert();// 限定范围的表会自动添加前缀
// Studio中的表名:"task_tracker"
// 实际表名:"x_mycom_myapp_task_tracker"
// 创建记录
var gr = new GlideRecord('x_mycom_myapp_task_tracker');
gr.initialize();
gr.setValue('name', 'My Task');
gr.insert();var TaskManager = Class.create();
TaskManager.prototype = {
initialize: function() {
this.tableName = 'x_mycom_myapp_task_tracker';
},
createTask: function(name, description) {
var gr = new GlideRecord(this.tableName);
gr.initialize();
gr.setValue('name', name);
gr.setValue('description', description);
return gr.insert();
},
// Mark as accessible from other scopes
// Requires: "Accessible from: All application scopes"
getTask: function(sysId) {
var gr = new GlideRecord(this.tableName);
if (gr.get(sysId)) {
return {
name: gr.getValue('name'),
description: gr.getValue('description')
};
}
return null;
},
type: 'TaskManager'
};var TaskManager = Class.create();
TaskManager.prototype = {
initialize: function() {
this.tableName = 'x_mycom_myapp_task_tracker';
},
createTask: function(name, description) {
var gr = new GlideRecord(this.tableName);
gr.initialize();
gr.setValue('name', name);
gr.setValue('description', description);
return gr.insert();
},
// 标记为可从其他作用域访问
// 要求:"可访问范围:所有应用作用域"
getTask: function(sysId) {
var gr = new GlideRecord(this.tableName);
if (gr.get(sysId)) {
return {
name: gr.getValue('name'),
description: gr.getValue('description')
};
}
return null;
},
type: 'TaskManager'
};// From scope: x_mycom_otherapp
// Calling: x_mycom_myapp.TaskManager
// Option 1: Direct call (if accessible)
var tm = new x_mycom_myapp.TaskManager();
var task = tm.getTask(sysId);
// Option 2: GlideScopedEvaluator
var evaluator = new GlideScopedEvaluator();
evaluator.putVariable('sysId', sysId);
var result = evaluator.evaluateScript(
'x_mycom_myapp',
'new TaskManager().getTask(sysId)'
);// 当前作用域:x_mycom_otherapp
// 调用:x_mycom_myapp.TaskManager
// 选项1:直接调用(如果允许访问)
var tm = new x_mycom_myapp.TaskManager();
var task = tm.getTask(sysId);
// 选项2:使用GlideScopedEvaluator
var evaluator = new GlideScopedEvaluator();
evaluator.putVariable('sysId', sysId);
var result = evaluator.evaluateScript(
'x_mycom_myapp',
'new TaskManager().getTask(sysId)'
);// Check if cross-scope access is allowed
var gr = new GlideRecord('x_other_app_table');
if (!gr.isValid()) {
gs.error('No access to x_other_app_table');
return;
}
// If accessible, query normally
gr.addQuery('active', true);
gr.query();// 检查是否允许跨作用域访问
var gr = new GlideRecord('x_other_app_table');
if (!gr.isValid()) {
gs.error('No access to x_other_app_table');
return;
}
// 如果允许,正常查询
gr.addQuery('active', true);
gr.query();// In Application > Properties
// Name: x_mycom_myapp.default_priority
// Value: 3
// Type: string
// In Application > Modules
// Create "Properties" module pointing to:
// /sys_properties_list.do?sysparm_query=name=x_mycom_myapp// 在 应用 > 属性 中
// 名称:x_mycom_myapp.default_priority
// 值:3
// 类型:字符串
// 在 应用 > 模块 中
// 创建指向以下地址的「属性」模块:
// /sys_properties_list.do?sysparm_query=name=x_mycom_myapp// Get property value
var defaultPriority = gs.getProperty('x_mycom_myapp.default_priority', '3');
// Set property value (requires admin)
gs.setProperty('x_mycom_myapp.default_priority', '2');// 获取属性值
var defaultPriority = gs.getProperty('x_mycom_myapp.default_priority', '3');
// 设置属性值(需要管理员权限)
gs.setProperty('x_mycom_myapp.default_priority', '2');x_mycom_myapp/
├── Tables
│ ├── x_mycom_myapp_task
│ └── x_mycom_myapp_config
├── Script Includes
│ ├── TaskManager
│ └── ConfigUtils
├── Business Rules
│ └── Validate Task
├── UI Pages
│ └── task_dashboard
├── REST API
│ └── Task API
├── Scheduled Jobs
│ └── Daily Cleanup
└── Application Properties
├── default_priority
└── enable_notificationsx_mycom_myapp/
├── 表
│ ├── x_mycom_myapp_task
│ └── x_mycom_myapp_config
├── 脚本包含
│ ├── TaskManager
│ └── ConfigUtils
├── 业务规则
│ └── 验证任务
├── UI页面
│ └── task_dashboard
├── REST API
│ └── 任务API
├── 计划任务
│ └── 每日清理
└── 应用属性
├── default_priority
└── enable_notifications// Resource: /api/x_mycom_myapp/tasks
// HTTP Method: GET
(function process(request, response) {
var tasks = [];
var gr = new GlideRecord('x_mycom_myapp_task_tracker');
gr.addQuery('active', true);
gr.query();
while (gr.next()) {
tasks.push({
sys_id: gr.getUniqueValue(),
name: gr.getValue('name'),
status: gr.getValue('status')
});
}
response.setBody({
result: tasks,
count: tasks.length
});
})(request, response);// 资源:/api/x_mycom_myapp/tasks
// HTTP方法:GET
(function process(request, response) {
var tasks = [];
var gr = new GlideRecord('x_mycom_myapp_task_tracker');
gr.addQuery('active', true);
gr.query();
while (gr.next()) {
tasks.push({
sys_id: gr.getUniqueValue(),
name: gr.getValue('name'),
status: gr.getValue('status')
});
}
response.setBody({
result: tasks,
count: tasks.length
});
})(request, response);curl -X GET \
"https://instance.service-now.com/api/x_mycom_myapp/tasks" \
-H "Authorization: Bearer token"curl -X GET \
"https://instance.service-now.com/api/x_mycom_myapp/tasks" \
-H "Authorization: Bearer token"Application > Dependencies
Add:
- sn_hr_core (HR Core)
- sn_cmdb (CMDB)应用 > 依赖关系
添加:
- sn_hr_core (HR核心)
- sn_cmdb (配置管理数据库)// Check if plugin is active
if (GlidePluginManager.isActive('com.snc.hr.core')) {
// HR Core is available
var hrCase = new sn_hr_core.hr_case();
}// 检查插件是否激活
if (GlidePluginManager.isActive('com.snc.hr.core')) {
// HR核心可用
var hrCase = new sn_hr_core.hr_case();
}□ All tables have proper ACLs
□ No hard-coded sys_ids
□ No hard-coded instance URLs
□ All dependencies declared
□ Properties have default values
□ Documentation complete
□ Test cases pass
□ No global scope modifications
□ Update Set tested on clean instance□ 所有表都配置了合适的ACL
□ 没有硬编码的sys_id
□ 没有硬编码的实例URL
□ 已声明所有依赖
□ 属性都有默认值
□ 文档完整
□ 测试用例通过
□ 未修改全局作用域
□ 更新集已在干净实例上测试Major.Minor.Patch
1.0.0 - Initial release
1.1.0 - New feature added
1.1.1 - Bug fix
2.0.0 - Breaking change主版本.次版本.补丁版本
1.0.0 - 初始版本
1.1.0 - 新增功能
1.1.1 - 修复Bug
2.0.0 - 破坏性变更| Mistake | Problem | Solution |
|---|---|---|
| Global modifications | Won't deploy cleanly | Keep changes in scope |
| Hard-coded sys_ids | Fails on other instances | Use properties or lookups |
| Missing ACLs | Security vulnerabilities | Create ACLs for all tables |
| No error handling | Silent failures | Add try/catch, logging |
| Accessing global tables directly | Upgrade conflicts | Use references, not copies |
| 错误 | 问题 | 解决方案 |
|---|---|---|
| 修改全局作用域 | 无法干净部署 | 将变更限定在自身作用域内 |
| 硬编码sys_id | 在其他实例上运行失败 | 使用属性或查找方式 |
| 缺少ACL | 安全漏洞 | 为所有表创建ACL |
| 无错误处理 | 静默失败 | 添加try/catch和日志 |
| 直接访问全局表 | 升级时冲突 | 使用引用而非复制 |