Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…External Lookup Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
|
This PR is very large. Consider breaking it into smaller PRs for easier review. |
There was a problem hiding this comment.
Pull request overview
This pull request implements four critical missing protocols to complete the ObjectStack specification for enterprise features: Notification Management, Document Management, Change Management, and External Lookup.
Changes:
- Added Notification Management protocol with multi-channel delivery support (Email, SMS, Push, In-app, Slack, Teams, Webhook) including template engine, retry policies, and delivery tracking
- Added Document Management protocol with version control, template-driven generation, e-signature workflows, and time-based access control
- Added Change Management protocol following ITIL compliance with request lifecycle management, impact assessment, CAB approval routing, and mandatory rollback procedures
- Added External Lookup protocol for real-time external data queries with OAuth2/API Key/Basic authentication, multi-strategy caching, and rate limiting
Reviewed changes
Copilot reviewed 38 out of 38 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/spec/src/system/notification.zod.ts | Implements unified notification protocol with multi-channel support (email, SMS, push, in-app, Slack, Teams, webhook) including retry policies and tracking |
| packages/spec/src/system/notification.test.ts | Comprehensive test suite (560 lines) covering all notification channels and configurations |
| packages/spec/src/system/change-management.zod.ts | Implements ITIL-compliant change management protocol with impact assessment, approval workflows, and rollback procedures |
| packages/spec/src/system/change-management.test.ts | Comprehensive test suite (656 lines) covering all change types and lifecycle states |
| packages/spec/src/system/index.ts | Adds exports for notification and change management protocols |
| packages/spec/src/data/document.zod.ts | Implements document management protocol with versioning, templates, e-signatures, and access control |
| packages/spec/src/data/document.test.ts | Comprehensive test suite (624 lines) covering all document features |
| packages/spec/src/data/external-lookup.zod.ts | Implements external data lookup protocol similar to Salesforce External Objects with caching and rate limiting |
| packages/spec/src/data/external-lookup.test.ts | Comprehensive test suite (664 lines) covering OData, REST, GraphQL, and custom data sources |
| packages/spec/src/data/index.ts | Adds exports for document and external lookup protocols |
| packages/spec/json-schema/system/*.json | Generated JSON schemas (9 files) for notification and change management protocols |
| packages/spec/json-schema/data/*.json | Generated JSON schemas (6 files) for document and external lookup protocols |
| content/docs/references/system/notification.mdx | Documentation for notification protocol schemas |
| content/docs/references/system/change-management.mdx | Documentation for change management protocol schemas |
| content/docs/references/data/document.mdx | Documentation for document protocol schemas |
| content/docs/references/data/external-lookup.mdx | Documentation for external lookup protocol schemas |
| content/docs/references/data/connector.mdx | Incorrectly added documentation file (no corresponding connector.zod.ts exists) |
| content/docs/references/system/meta.json | Adds notification and change-management to system protocol navigation |
| content/docs/references/data/meta.json | Adds document and external-lookup to data protocol navigation |
| content/docs/references/system/index.mdx | Adds links to notification and change management documentation |
| content/docs/references/data/index.mdx | Adds links to document and external lookup documentation |
| export const FieldMappingSchema = z.object({ | ||
| /** | ||
| * Field name in the external system | ||
| */ | ||
| externalField: z.string().describe('External field name'), | ||
|
|
||
| /** | ||
| * Corresponding local field name (snake_case) | ||
| */ | ||
| localField: z.string().describe('Local field name'), | ||
|
|
||
| /** | ||
| * Data type of the field | ||
| */ | ||
| type: z.string().describe('Field type'), | ||
|
|
||
| /** | ||
| * Whether the field is read-only | ||
| * @default true | ||
| */ | ||
| readonly: z.boolean().optional().default(true).describe('Read-only field'), | ||
| }); |
There was a problem hiding this comment.
There is a naming collision: FieldMappingSchema is already defined in packages/spec/src/data/mapping.zod.ts. This will cause import conflicts when both schemas are used in the same file.
The schema should be renamed to something more specific like ExternalFieldMappingSchema to avoid this collision and make the purpose clearer.
| // Type exports | ||
| export type ExternalLookup = z.infer<typeof ExternalLookupSchema>; | ||
| export type ExternalDataSource = z.infer<typeof ExternalDataSourceSchema>; | ||
| export type FieldMapping = z.infer<typeof FieldMappingSchema>; |
There was a problem hiding this comment.
The type export FieldMapping will also collide with the existing FieldMapping type from packages/spec/src/data/mapping.zod.ts. This should be renamed to ExternalFieldMapping to match the renamed schema.
| { | ||
| "$ref": "#/definitions/FieldMapping", | ||
| "definitions": { | ||
| "FieldMapping": { | ||
| "type": "object", | ||
| "properties": { | ||
| "externalField": { | ||
| "type": "string", | ||
| "description": "External field name" | ||
| }, | ||
| "localField": { | ||
| "type": "string", | ||
| "description": "Local field name" | ||
| }, | ||
| "type": { | ||
| "type": "string", | ||
| "description": "Field type" | ||
| }, | ||
| "readonly": { | ||
| "type": "boolean", | ||
| "default": true, | ||
| "description": "Read-only field" | ||
| } | ||
| }, | ||
| "required": [ | ||
| "externalField", | ||
| "localField", | ||
| "type" | ||
| ], | ||
| "additionalProperties": false | ||
| } | ||
| }, | ||
| "$schema": "http://json-schema.org/draft-07/schema#" | ||
| } No newline at end of file |
There was a problem hiding this comment.
This JSON schema file will overwrite the existing FieldMapping.json that is generated from packages/spec/src/data/mapping.zod.ts.
The Zod schema naming collision (FieldMappingSchema exists in both external-lookup.zod.ts and mapping.zod.ts) causes this JSON schema file collision. Once the Zod schema is renamed to ExternalFieldMappingSchema, this JSON schema file should be renamed to ExternalFieldMapping.json.
| /** | ||
| * Corresponding local field name (snake_case) | ||
| */ | ||
| localField: z.string().describe('Local field name'), |
There was a problem hiding this comment.
The localField property should enforce snake_case naming convention with a regex pattern, similar to how targetField is validated in packages/spec/src/integration/connector.zod.ts:219.
According to the coding guidelines and codebase conventions, machine names and field identifiers should be validated as snake_case. Please add the regex validation:
localField: z.string().regex(/^[a-z_][a-z0-9_]*$/).describe('Local field name (snake_case)'),| /** | ||
| * Name of the field that uses external lookup | ||
| */ | ||
| fieldName: z.string().describe('Field name'), |
There was a problem hiding this comment.
The fieldName property should enforce snake_case naming convention with a regex pattern. The example in the JSDoc shows "external_account" which is snake_case, and this aligns with the coding guidelines that machine names should be in snake_case format.
Please add the regex validation:
fieldName: z.string().regex(/^[a-z_][a-z0-9_]*$/).describe('Field name (snake_case)'),| /** | ||
| * Placeholder identifier (used in template) | ||
| */ | ||
| key: z.string().describe('Placeholder key'), |
There was a problem hiding this comment.
The key property for template placeholders should enforce snake_case naming convention. The examples in the JSDoc show keys like "client_name" and "contract_date" which are snake_case, consistent with the coding guidelines for machine identifiers.
Please add the regex validation:
key: z.string().regex(/^[a-z_][a-z0-9_]*$/).describe('Placeholder key (snake_case)'),| --- | ||
| title: Connector | ||
| description: Connector protocol schemas | ||
| --- | ||
|
|
||
| # Connector | ||
|
|
||
| <Callout type="info"> | ||
| **Source:** `packages/spec/src/data/connector.zod.ts` | ||
| </Callout> | ||
|
|
||
| ## TypeScript Usage | ||
|
|
||
| ```typescript | ||
| import { FieldMappingSchema } from '@objectstack/spec/data'; | ||
| import type { FieldMapping } from '@objectstack/spec/data'; | ||
|
|
||
| // Validate data | ||
| const result = FieldMappingSchema.parse(data); | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## FieldMapping | ||
|
|
||
| ### Properties | ||
|
|
||
| | Property | Type | Required | Description | | ||
| | :--- | :--- | :--- | :--- | | ||
| | **externalField** | `string` | ✅ | External field name | | ||
| | **localField** | `string` | ✅ | Local field name | | ||
| | **type** | `string` | ✅ | Field type | | ||
| | **readonly** | `boolean` | optional | Read-only field | | ||
|
|
There was a problem hiding this comment.
This documentation file appears to be incorrect. There is no packages/spec/src/data/connector.zod.ts file in the codebase. The FieldMappingSchema is actually exported from packages/spec/src/data/external-lookup.zod.ts, not from a connector module.
Additionally, there is a naming collision: FieldMappingSchema is defined in both:
packages/spec/src/data/external-lookup.zod.ts(newly added in this PR)packages/spec/src/data/mapping.zod.ts(existing)
The schema in external-lookup.zod.ts should be renamed to avoid this conflict, perhaps to ExternalFieldMappingSchema, and this documentation file should either be removed or updated to reference the correct schema location.
Implements 4 critical missing protocols to complete the ObjectStack specification for enterprise features.
Protocols Added
1. Notification Management (
system/notification.zod.ts)2. Document Management (
data/document.zod.ts)3. Change Management (
system/change-management.zod.ts)4. External Lookup (
data/external-lookup.zod.ts)Usage Example
Technical Notes
camelCasefor configuration keys,snake_casefor data identifiersOriginal prompt
Phase 2: Add P0 Missing Protocols
目标: 添加4个关键缺失协议
负责人: Protocol Team
时间: 3周
Task 2.1: Notification Management Protocol
文件: packages/spec/src/system/notification.zod.ts
协议定义:
import { z } from 'zod';
/**
*/
export const EmailTemplateSchema = z.object({
id: z.string(),
subject: z.string(),
body: z.string(),
bodyType: z.enum(['text', 'html', 'markdown']).default('html'),
variables: z.array(z.string()).optional(),
attachments: z.array(z.object({
name: z.string(),
url: z.string().url(),
})).optional(),
});
export const SMSTemplateSchema = z.object({
id: z.string(),
message: z.string(),
maxLength: z.number().default(160),
variables: z.array(z.string()).optional(),
});
export const PushNotificationSchema = z.object({
title: z.string(),
body: z.string(),
icon: z.string().url().optional(),
badge: z.number().optional(),
data: z.record(z.any()).optional(),
actions: z.array(z.object({
action: z.string(),
title: z.string(),
})).optional(),
});
export const InAppNotificationSchema = z.object({
title: z.string(),
message: z.string(),
type: z.enum(['info', 'success', 'warning', 'error']),
actionUrl: z.string().optional(),
dismissible: z.boolean().default(true),
expiresAt: z.number().optional(),
});
export const NotificationChannelSchema = z.enum([
'email',
'sms',
'push',
'in-app',
'slack',
'teams',
'webhook',
]);
export const NotificationConfigSchema = z.object({
id: z.string(),
name: z.string(),
channel: NotificationChannelSchema,
template: z.union([
EmailTemplateSchema,
SMSTemplateSchema,
PushNotificationSchema,
InAppNotificationSchema,
]),
recipients: z.object({
to: z.array(z.string()),
cc: z.array(z.string()).optional(),
bcc: z.array(z.string()).optional(),
}),
schedule: z.object({
type: z.enum(['immediate', 'delayed', 'scheduled']),
delay: z.number().optional(),
scheduledAt: z.number().optional(),
}).optional(),
retryPolicy: z.object({
enabled: z.boolean().default(true),
maxRetries: z.number().default(3),
backoffStrategy: z.enum(['exponential', 'linear', 'fixed']),
}).optional(),
tracking: z.object({
trackOpens: z.boolean().default(false),
trackClicks: z.boolean().default(false),
trackDelivery: z.boolean().default(true),
}).optional(),
});
export type NotificationConfig = z.infer;
export type NotificationChannel = z.infer;
export type EmailTemplate = z.infer;
export type SMSTemplate = z.infer;
export type PushNotification = z.infer;
export type InAppNotification = z.infer;
测试文件: packages/spec/src/system/notification.test.ts
import { describe, it, expect } from 'vitest';
import { NotificationConfigSchema } from './notification.zod';
describe('NotificationConfigSchema', () => {
it('should validate email notification', () => {
const valid = {
id: 'welcome-email',
name: 'Welcome Email',
channel: 'email',
template: {
id: 'tpl-001',
subject: 'Welcome to ObjectStack',
body: '
Welcome!
',bodyType: 'html',
},
recipients: {
to: ['user@example.com'],
},
};
expect(() => NotificationConfigSchema.parse(valid)).not.toThrow();
});
// More tests...
});
验收标准:
协议定义完成并符合 Zod 规范
测试覆盖率 ≥ 90%
JSDoc 文档完整
添加到主 index.ts
生成 JSON Schema
Task 2.2: Document Management Protocol
文件: packages/spec/src/data/document.zod.ts
协议定义:
import { z } from 'zod';
/**
*/
export const DocumentVersionSchema = z.object({
versionNumber: z.number(),
createdAt: z.number(),
createdBy: z.string(),
size: z.number(),
checksum: z.string(),
downloadUrl: z.string().url(),
isLatest: z.boolean().default(false),
});
export const DocumentTemplateSchema = z.object({
id: z.string(),
name: z.string(),
description: z.string().optional(),
fileUrl: z.string().url(),
fileType: z.string(),
placeholders: z.array(z.object({
key: z.string(),
label: z.string(),
type: z.enum(['text', 'number', 'date', 'image']),
required: z.boolean().default(false),
})),
});
export const ESignatureConfigSchema = z.object({
provider: z.enum(['docusign', 'adobe-sign', 'hellosign', 'custom']),
enabled: z.boolean().default(false),
signers: z.array(z.object({
email: z.string().email(),
name: z.string(),
role: z.string(),
order: z.number(),
})),
expirationDays: z.number().default(30),
reminderDays: z.number().default(7),
});
export const DocumentSchema = z.object({
id: z.string(),
name: z.string(),
description: z.string().optional(),
fileType: z.string(),
fileSize: z.number(),
...
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.