Remove hello world endpoints and refactor routing to use new hello package
- Deleted the existing hello world handlers from hello.go to streamline the codebase. - Updated openapi.go to attach the new hello package for handling hello world routes, ensuring OAuth protection is applied correctly. - Enhanced the README.md to reflect the new structure and provide comprehensive documentation for the hello world API endpoints.
This commit is contained in:
parent
d54572a7d1
commit
92d21c5389
9 changed files with 2539 additions and 18 deletions
107
openapi/COMMERCIAL.md
Normal file
107
openapi/COMMERCIAL.md
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
# Commercial Usage Declaration - Yao OpenAPI
|
||||
|
||||
This document outlines the commercial usage terms for the Yao OpenAPI components contained within this directory.
|
||||
|
||||
## Commercial License Requirements
|
||||
|
||||
The Yao OpenAPI module is available for commercial use under the following **mandatory conditions**:
|
||||
|
||||
### 1. Developer Certificate Requirement
|
||||
|
||||
**Commercial usage requires obtaining a Yao Developer Certificate.**
|
||||
|
||||
- All commercial deployments of Yao OpenAPI must possess a valid Yao Developer Certificate
|
||||
- The certificate must be obtained through official Yao certification channels
|
||||
- Usage without proper certification constitutes license violation
|
||||
|
||||
### 2. Trademark and Branding Restrictions
|
||||
|
||||
**Yao branding elements are protected and must remain intact:**
|
||||
|
||||
- Yao App Engine logos and copyright information **MUST NOT** be removed or modified
|
||||
- All Yao trademark elements must remain visible and unaltered in production deployments
|
||||
- Logo and copyright modifications are **ONLY** permitted with explicit authorization through Yao Developer Certificate
|
||||
|
||||
### 3. Certificate Verification System Integrity
|
||||
|
||||
**The Yao certificate verification system must be maintained in its entirety:**
|
||||
|
||||
- **PROHIBITED**: Removal, modification, or circumvention of Yao certificate verification logic
|
||||
- **PROHIBITED**: Alteration of certificate validation processes and related code components
|
||||
- **PROHIBITED**: Modification of certificate verification pages (marked in code comments)
|
||||
- **REQUIRED**: Complete preservation of the Yao certificate verification system regardless of usage purpose
|
||||
|
||||
### 4. Code Integrity Requirements
|
||||
|
||||
For commercial deployments, the following **core verification mechanisms** must remain unmodified:
|
||||
|
||||
- Certificate validation endpoints and middleware
|
||||
- Authentication verification logic and OAuth implementation
|
||||
- License verification routines and related authentication flows
|
||||
- Commercial license checking and enforcement code
|
||||
- Yao branding and copyright verification systems
|
||||
|
||||
_Note: General business logic and API customizations are permitted, but the core licensing and certificate verification infrastructure must remain intact._
|
||||
|
||||
## Compliance Obligations
|
||||
|
||||
### For Commercial Users:
|
||||
|
||||
1. **Obtain Valid Certification**: Acquire and maintain a current Yao Developer Certificate
|
||||
2. **Preserve Verification Logic**: Maintain all certificate verification code intact
|
||||
3. **Respect Trademark Rights**: Keep all Yao branding elements unmodified
|
||||
4. **Regular Compliance Review**: Ensure ongoing adherence to certification requirements
|
||||
|
||||
### For Developers and Integrators:
|
||||
|
||||
1. **Code Review Responsibility**: Verify that certificate verification logic remains intact
|
||||
2. **Documentation Compliance**: Maintain all certificate-related documentation and comments
|
||||
3. **Deployment Verification**: Ensure production deployments include proper certificate validation
|
||||
4. **Update Compliance**: When updating Yao components, preserve all certificate verification systems
|
||||
|
||||
## Prohibited Activities
|
||||
|
||||
The following activities are **strictly prohibited** without explicit written authorization:
|
||||
|
||||
- Removing or disabling certificate verification mechanisms
|
||||
- Modifying Yao branding, logos, or copyright notices
|
||||
- Circumventing license validation processes
|
||||
- Redistributing modified versions that bypass certificate requirements
|
||||
- Creating derivative works that remove authentication restrictions
|
||||
|
||||
## Legal Compliance
|
||||
|
||||
This commercial usage declaration is governed by:
|
||||
|
||||
- **Base License**: Modified Apache License 2.0
|
||||
- **Additional Terms**: As specified in the root LICENSE file
|
||||
- **Commercial Requirements**: As outlined in this document
|
||||
- **Jurisdiction**: Subject to applicable intellectual property and software licensing laws
|
||||
|
||||
## Violation Consequences
|
||||
|
||||
Failure to comply with these commercial usage terms may result in:
|
||||
|
||||
- Immediate license termination
|
||||
- Legal action for intellectual property infringement
|
||||
- Liability for damages and legal costs
|
||||
- Permanent prohibition from commercial Yao ecosystem usage
|
||||
|
||||
## Contact and Authorization
|
||||
|
||||
For commercial licensing inquiries, certificate applications, or clarification of usage terms:
|
||||
|
||||
- **Official Website**: [Yao Developer Certification Portal]
|
||||
- **License Inquiries**: Contact authorized Yao licensing representatives
|
||||
- **Technical Support**: Available exclusively to certified commercial users
|
||||
|
||||
---
|
||||
|
||||
**Important Notice**: This commercial declaration supplements and does not replace the terms specified in the root LICENSE file. In case of conflicts, the most restrictive terms apply.
|
||||
|
||||
**© 2025 Infinite Wisdom Software. All rights reserved.**
|
||||
|
||||
---
|
||||
|
||||
_Last Updated: 2025-07-22_
|
||||
_Document Version: 1.0_
|
||||
107
openapi/COMMERCIAL.zh-CN.md
Normal file
107
openapi/COMMERCIAL.zh-CN.md
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
# 商业使用声明 - Yao OpenAPI
|
||||
|
||||
本文档阐述了本目录下 Yao OpenAPI 组件的商业使用条款。
|
||||
|
||||
## 商业许可证要求
|
||||
|
||||
Yao OpenAPI 模块可用于商业用途,但须遵循以下**强制性条件**:
|
||||
|
||||
### 1. 开发者证书要求
|
||||
|
||||
**商业使用需要获得 Yao 开发者证书。**
|
||||
|
||||
- 所有 Yao OpenAPI 的商业部署都必须拥有有效的 Yao 开发者证书
|
||||
- 证书必须通过官方 Yao 认证渠道获得
|
||||
- 未经适当认证的使用构成许可证违规
|
||||
|
||||
### 2. 商标和品牌限制
|
||||
|
||||
**Yao 品牌元素受保护,必须保持完整:**
|
||||
|
||||
- Yao App Engine 标志和版权信息**不得**被移除或修改
|
||||
- 所有 Yao 商标元素必须在生产部署中保持可见且未改动
|
||||
- 标志和版权修改**仅**在通过 Yao 开发者证书获得明确授权后允许
|
||||
|
||||
### 3. 证书验证系统完整性
|
||||
|
||||
**Yao 证书验证系统必须完整保持:**
|
||||
|
||||
- **禁止**:移除、修改或绕过 Yao 证书验证逻辑
|
||||
- **禁止**:更改证书验证流程和相关代码组件
|
||||
- **禁止**:修改证书验证页面(在代码注释中标记)
|
||||
- **要求**:无论使用目的如何,必须完整保持 Yao 证书验证系统
|
||||
|
||||
### 4. 代码完整性要求
|
||||
|
||||
对于商业部署,以下**核心验证机制**必须保持未修改状态:
|
||||
|
||||
- 证书验证端点和中间件
|
||||
- 认证验证逻辑和 OAuth 实现
|
||||
- 许可证验证例程和相关认证流程
|
||||
- 商业许可证检查和执行代码
|
||||
- Yao 品牌和版权验证系统
|
||||
|
||||
_注意:一般业务逻辑和 API 定制是允许的,但核心许可证和证书验证基础设施必须保持完整。_
|
||||
|
||||
## 合规义务
|
||||
|
||||
### 商业用户:
|
||||
|
||||
1. **获得有效认证**:获得并维护当前有效的 Yao 开发者证书
|
||||
2. **保持验证逻辑**:保持所有证书验证代码完整
|
||||
3. **尊重商标权**:保持所有 Yao 品牌元素未修改
|
||||
4. **定期合规审查**:确保持续遵守认证要求
|
||||
|
||||
### 开发者和集成商:
|
||||
|
||||
1. **代码审查责任**:验证证书验证逻辑保持完整
|
||||
2. **文档合规**:维护所有与证书相关的文档和注释
|
||||
3. **部署验证**:确保生产部署包含适当的证书验证
|
||||
4. **更新合规**:更新 Yao 组件时,保持所有证书验证系统
|
||||
|
||||
## 禁止活动
|
||||
|
||||
以下活动在没有明确书面授权的情况下**严格禁止**:
|
||||
|
||||
- 移除或禁用证书验证机制
|
||||
- 修改 Yao 品牌、标志或版权声明
|
||||
- 绕过许可证验证流程
|
||||
- 重新分发绕过证书要求的修改版本
|
||||
- 创建移除认证限制的衍生作品
|
||||
|
||||
## 法律合规
|
||||
|
||||
本商业使用声明受以下条款管辖:
|
||||
|
||||
- **基础许可证**:修改版 Apache License 2.0
|
||||
- **附加条款**:如根目录 LICENSE 文件中指定
|
||||
- **商业要求**:如本文档中概述
|
||||
- **管辖权**:受适用的知识产权和软件许可法律约束
|
||||
|
||||
## 违规后果
|
||||
|
||||
未能遵守这些商业使用条款可能导致:
|
||||
|
||||
- 立即终止许可证
|
||||
- 因知识产权侵权而提起法律诉讼
|
||||
- 损害赔偿和法律费用责任
|
||||
- 永久禁止商业 Yao 生态系统使用
|
||||
|
||||
## 联系和授权
|
||||
|
||||
如需商业许可咨询、证书申请或使用条款澄清:
|
||||
|
||||
- **官方网站**:[Yao 开发者认证门户]
|
||||
- **许可证咨询**:联系授权的 Yao 许可代表
|
||||
- **技术支持**:仅向经认证的商业用户提供
|
||||
|
||||
---
|
||||
|
||||
**重要声明**:本商业声明是对根目录 LICENSE 文件中指定条款的补充,不能替代这些条款。如有冲突,以最严格的条款为准。
|
||||
|
||||
**© 2025 Infinite Wisdom Software. 保留所有权利。**
|
||||
|
||||
---
|
||||
|
||||
_最后更新:2025-07-22_
|
||||
_文档版本:1.0_
|
||||
|
|
@ -1,9 +1,400 @@
|
|||
# Yao OpenAPI
|
||||
|
||||
## OAuth
|
||||
The Yao OpenAPI provides a comprehensive set of RESTful APIs for managing Yao applications, including OAuth 2.1/OpenID Connect authentication, DSL resource management, and development utilities.
|
||||
|
||||
## Agent
|
||||
## Base URL
|
||||
|
||||
## Knowledge Base
|
||||
All API endpoints are prefixed with a configurable base URL (e.g., `/v1`).
|
||||
|
||||
## Audit Log
|
||||
## Authentication
|
||||
|
||||
The Yao OpenAPI implements OAuth 2.1 and OpenID Connect Core 1.0 specifications for secure authentication and authorization.
|
||||
|
||||
### Supported Grant Types
|
||||
|
||||
- **Authorization Code Flow** - RFC 6749 (recommended for web applications)
|
||||
- **Client Credentials Flow** - RFC 6749 (for server-to-server communication)
|
||||
- **Device Authorization Flow** - RFC 8628 (for devices with limited input)
|
||||
- **Refresh Token Flow** - RFC 6749 (for token renewal)
|
||||
- **Token Exchange** - RFC 8693 (for token delegation)
|
||||
|
||||
### Discovery Endpoints
|
||||
|
||||
The OpenAPI server provides standard OAuth 2.1 discovery endpoints:
|
||||
|
||||
```
|
||||
GET /.well-known/oauth-authorization-server
|
||||
```
|
||||
|
||||
Returns server metadata including supported endpoints, grant types, and security features.
|
||||
|
||||
### OAuth Endpoints
|
||||
|
||||
#### Authorization Endpoint
|
||||
|
||||
Initiate the authorization code flow:
|
||||
|
||||
```
|
||||
GET /oauth/authorize?client_id={client_id}&response_type=code&redirect_uri={redirect_uri}&scope={scope}&state={state}
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `client_id` (required): Client identifier
|
||||
- `response_type` (required): Must be "code"
|
||||
- `redirect_uri` (required): Client redirect URI
|
||||
- `scope` (optional): Requested scopes
|
||||
- `state` (recommended): CSRF protection state parameter
|
||||
- `code_challenge` (optional): PKCE code challenge
|
||||
- `code_challenge_method` (optional): PKCE challenge method
|
||||
|
||||
#### Token Endpoint
|
||||
|
||||
Exchange authorization code for access token:
|
||||
|
||||
```
|
||||
POST /oauth/token
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
grant_type=authorization_code&code={code}&redirect_uri={redirect_uri}&client_id={client_id}&client_secret={client_secret}
|
||||
```
|
||||
|
||||
**Client Credentials Flow:**
|
||||
|
||||
```
|
||||
POST /oauth/token
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
|
||||
grant_type=client_credentials&client_id={client_id}&client_secret={client_secret}&scope={scope}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"access_token": "eyJhbGciOiJSUzI1NiIs...",
|
||||
"token_type": "Bearer",
|
||||
"expires_in": 3600,
|
||||
"refresh_token": "def50200...",
|
||||
"scope": "openid profile"
|
||||
}
|
||||
```
|
||||
|
||||
#### Token Introspection
|
||||
|
||||
Validate and inspect access tokens (RFC 7662):
|
||||
|
||||
```
|
||||
POST /oauth/introspect
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
Authorization: Basic {base64(client_id:client_secret)}
|
||||
|
||||
token={access_token}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"active": true,
|
||||
"scope": "openid profile",
|
||||
"client_id": "your_client_id",
|
||||
"username": "user@example.com",
|
||||
"token_type": "Bearer",
|
||||
"exp": 1640995200,
|
||||
"iat": 1640991600
|
||||
}
|
||||
```
|
||||
|
||||
#### Token Revocation
|
||||
|
||||
Revoke access or refresh tokens (RFC 7009):
|
||||
|
||||
```
|
||||
POST /oauth/revoke
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
Authorization: Basic {base64(client_id:client_secret)}
|
||||
|
||||
token={token}&token_type_hint={access_token|refresh_token}
|
||||
```
|
||||
|
||||
#### Dynamic Client Registration
|
||||
|
||||
Register OAuth clients dynamically (RFC 7591):
|
||||
|
||||
```
|
||||
POST /oauth/register
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"client_name": "My Application",
|
||||
"redirect_uris": ["https://app.example.com/callback"],
|
||||
"grant_types": ["authorization_code", "refresh_token"],
|
||||
"response_types": ["code"],
|
||||
"scope": "openid profile"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"client_id": "generated_client_id",
|
||||
"client_secret": "generated_client_secret",
|
||||
"client_name": "My Application",
|
||||
"redirect_uris": ["https://app.example.com/callback"],
|
||||
"grant_types": ["authorization_code", "refresh_token"],
|
||||
"response_types": ["code"],
|
||||
"scope": "openid profile"
|
||||
}
|
||||
```
|
||||
|
||||
#### JSON Web Key Set
|
||||
|
||||
Retrieve public keys for token verification (RFC 7517):
|
||||
|
||||
```
|
||||
GET /oauth/jwks
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"keys": [
|
||||
{
|
||||
"kty": "RSA",
|
||||
"kid": "key-id-1",
|
||||
"use": "sig",
|
||||
"n": "...",
|
||||
"e": "AQAB"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Authentication Usage
|
||||
|
||||
#### Bearer Token Authentication
|
||||
|
||||
Include the access token in API requests:
|
||||
|
||||
```bash
|
||||
curl -X GET "/v1/dsl/list/model" \
|
||||
-H "Authorization: Bearer {access_token}"
|
||||
```
|
||||
|
||||
#### Client Credentials
|
||||
|
||||
For server-to-server authentication, use the client credentials flow to obtain an access token, then include it in subsequent API requests.
|
||||
|
||||
## Hello World API
|
||||
|
||||
Simple endpoints for testing connectivity and authentication.
|
||||
|
||||
### Public Endpoint
|
||||
|
||||
Test basic connectivity without authentication:
|
||||
|
||||
```
|
||||
GET /helloworld/public
|
||||
POST /helloworld/public
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"MESSAGE": "HELLO, WORLD",
|
||||
"SERVER_TIME": "2024-01-15T10:30:00Z",
|
||||
"VERSION": "1.0.0",
|
||||
"PRVERSION": "1.0.0-preview",
|
||||
"CUI": "1.0.0",
|
||||
"PRCUI": "1.0.0-preview",
|
||||
"APP": "YaoApp",
|
||||
"APP_VERSION": "1.0.0"
|
||||
}
|
||||
```
|
||||
|
||||
### Protected Endpoint
|
||||
|
||||
Test OAuth authentication:
|
||||
|
||||
```
|
||||
GET /helloworld/protected
|
||||
POST /helloworld/protected
|
||||
```
|
||||
|
||||
**Headers:**
|
||||
|
||||
```
|
||||
Authorization: Bearer {access_token}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
Same as public endpoint, but requires valid authentication.
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
# Get access token first
|
||||
curl -X POST "/v1/oauth/token" \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "grant_type=client_credentials&client_id=your_client&client_secret=your_secret"
|
||||
|
||||
# Use token to access protected endpoint
|
||||
curl -X GET "/v1/helloworld/protected" \
|
||||
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."
|
||||
```
|
||||
|
||||
## DSL Management API
|
||||
|
||||
Comprehensive API for managing Yao DSL resources (models, connectors, MCP clients, etc.).
|
||||
|
||||
**[View Full DSL API Documentation →](dsl/README.md)**
|
||||
|
||||
The DSL Management API provides:
|
||||
|
||||
- **Resource Management**: Create, read, update, delete DSL resources
|
||||
- **Load Management**: Load, unload, reload DSL resources
|
||||
- **Validation**: Validate DSL source code syntax
|
||||
- **Execution**: Execute methods on loaded DSL resources
|
||||
- **Discovery**: List and inspect available DSL resources
|
||||
|
||||
**Key Endpoints:**
|
||||
|
||||
- `GET /dsl/list/{type}` - List DSL resources
|
||||
- `POST /dsl/create/{type}` - Create new DSL resource
|
||||
- `GET /dsl/inspect/{type}/{id}` - Inspect DSL resource details
|
||||
- `PUT /dsl/update/{type}` - Update existing DSL resource
|
||||
- `DELETE /dsl/delete/{type}/{id}` - Delete DSL resource
|
||||
|
||||
All DSL endpoints require OAuth authentication.
|
||||
|
||||
## Error Responses
|
||||
|
||||
All endpoints return standardized error responses:
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "invalid_request",
|
||||
"error_description": "The request is missing a required parameter"
|
||||
}
|
||||
```
|
||||
|
||||
**Common HTTP Status Codes:**
|
||||
|
||||
- `200` - Success
|
||||
- `201` - Created
|
||||
- `400` - Bad Request (invalid parameters)
|
||||
- `401` - Unauthorized (authentication required)
|
||||
- `403` - Forbidden (insufficient permissions)
|
||||
- `404` - Not Found
|
||||
- `500` - Internal Server Error
|
||||
|
||||
**OAuth Error Codes:**
|
||||
|
||||
- `invalid_request` - Request is malformed
|
||||
- `invalid_client` - Client authentication failed
|
||||
- `invalid_grant` - Grant is invalid or expired
|
||||
- `unauthorized_client` - Client not authorized for grant type
|
||||
- `unsupported_grant_type` - Grant type not supported
|
||||
- `invalid_scope` - Requested scope is invalid
|
||||
|
||||
## Security Features
|
||||
|
||||
The OpenAPI implements comprehensive security measures:
|
||||
|
||||
### OAuth 2.1 Security
|
||||
|
||||
- **PKCE (Proof Key for Code Exchange)** - Required for public clients
|
||||
- **State Parameter** - CSRF protection for authorization requests
|
||||
- **Secure Token Storage** - Access tokens with appropriate expiration
|
||||
- **Client Authentication** - Multiple authentication methods supported
|
||||
|
||||
### HTTP Security Headers
|
||||
|
||||
All responses include security headers:
|
||||
|
||||
- `Cache-Control: no-store, no-cache, must-revalidate`
|
||||
- `Pragma: no-cache`
|
||||
- `X-Content-Type-Options: nosniff`
|
||||
- `X-Frame-Options: DENY`
|
||||
|
||||
### Rate Limiting
|
||||
|
||||
API endpoints are protected against abuse with configurable rate limiting.
|
||||
|
||||
## Example Workflows
|
||||
|
||||
### Web Application Authentication
|
||||
|
||||
1. **Register your client** (if using dynamic registration):
|
||||
|
||||
```bash
|
||||
curl -X POST "/v1/oauth/register" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"client_name": "My Web App",
|
||||
"redirect_uris": ["https://myapp.com/callback"],
|
||||
"grant_types": ["authorization_code", "refresh_token"]
|
||||
}'
|
||||
```
|
||||
|
||||
2. **Initiate authorization flow**:
|
||||
|
||||
```
|
||||
https://api.example.com/v1/oauth/authorize?client_id=your_client_id&response_type=code&redirect_uri=https://myapp.com/callback&scope=openid+profile&state=random_state
|
||||
```
|
||||
|
||||
3. **Exchange authorization code for tokens**:
|
||||
|
||||
```bash
|
||||
curl -X POST "/v1/oauth/token" \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "grant_type=authorization_code&code=auth_code&redirect_uri=https://myapp.com/callback&client_id=your_client_id&client_secret=your_secret"
|
||||
```
|
||||
|
||||
4. **Use access token to call APIs**:
|
||||
|
||||
```bash
|
||||
curl -X GET "/v1/dsl/list/model" \
|
||||
-H "Authorization: Bearer access_token_here"
|
||||
```
|
||||
|
||||
### Server-to-Server Integration
|
||||
|
||||
1. **Obtain client credentials token**:
|
||||
|
||||
```bash
|
||||
curl -X POST "/v1/oauth/token" \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "grant_type=client_credentials&client_id=server_client&client_secret=server_secret&scope=dsl:manage"
|
||||
```
|
||||
|
||||
2. **Manage DSL resources**:
|
||||
|
||||
```bash
|
||||
# Create a new model
|
||||
curl -X POST "/v1/dsl/create/model" \
|
||||
-H "Authorization: Bearer {access_token}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"id": "product",
|
||||
"source": "{ \"name\": \"product\", \"table\": { \"name\": \"products\" }, \"columns\": [...] }"
|
||||
}'
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
The OpenAPI server is configured through `openapi/openapi.yao`.
|
||||
|
||||
**[View Complete Configuration Examples →](https://github.com/YaoApp/yao-dev-app/tree/main/openapi)**
|
||||
|
||||
This includes comprehensive configuration examples for:
|
||||
|
||||
- OAuth 2.1 server settings
|
||||
- Client registration and management
|
||||
- Security and authentication policies
|
||||
- Development and production environments
|
||||
- API endpoint configuration
|
||||
|
|
|
|||
490
openapi/dsl/README.md
Normal file
490
openapi/dsl/README.md
Normal file
|
|
@ -0,0 +1,490 @@
|
|||
# DSL Management API
|
||||
|
||||
This document describes the RESTful API for managing Yao DSL resources (models, connectors, MCP clients, etc.).
|
||||
|
||||
## Base URL
|
||||
|
||||
All endpoints are prefixed with the configured base URL followed by `/dsl` (e.g., `/v1/dsl`).
|
||||
|
||||
## Authentication
|
||||
|
||||
All endpoints require OAuth authentication via the configured OAuth provider.
|
||||
|
||||
## DSL Types
|
||||
|
||||
Supported DSL types:
|
||||
|
||||
- `model` - Database models
|
||||
- `connector` - External service connectors
|
||||
- `mcp-client` - MCP client configurations
|
||||
- `api` - HTTP API definitions
|
||||
|
||||
## Endpoints
|
||||
|
||||
### Information Endpoints
|
||||
|
||||
#### Inspect DSL
|
||||
|
||||
Get detailed information about a DSL resource.
|
||||
|
||||
```
|
||||
GET /inspect/{type}/{id}
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `type` (path): DSL type
|
||||
- `id` (path): DSL identifier
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
curl -X GET "/v1/dsl/inspect/model/user" \
|
||||
-H "Authorization: Bearer {token}"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "user",
|
||||
"type": "model",
|
||||
"label": "User Model",
|
||||
"description": "User management model",
|
||||
"tags": ["auth", "user"],
|
||||
"path": "models/user.mod.yao",
|
||||
"store": "file",
|
||||
"status": "loaded",
|
||||
"readonly": false,
|
||||
"builtin": false,
|
||||
"mtime": "2024-01-15T10:30:00Z",
|
||||
"ctime": "2024-01-10T09:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
#### Get DSL Source Code
|
||||
|
||||
Retrieve the source code of a DSL resource.
|
||||
|
||||
```
|
||||
GET /source/{type}/{id}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
curl -X GET "/v1/dsl/source/model/user" \
|
||||
-H "Authorization: Bearer {token}"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"source": "{\n \"name\": \"user\",\n \"table\": {\n \"name\": \"users\"\n },\n \"columns\": [...]\n}"
|
||||
}
|
||||
```
|
||||
|
||||
#### Get DSL File Path
|
||||
|
||||
Get the file system path for a DSL resource.
|
||||
|
||||
```
|
||||
GET /path/{type}/{id}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"path": "models/user.mod.yao"
|
||||
}
|
||||
```
|
||||
|
||||
#### List DSLs
|
||||
|
||||
List DSL resources with optional filtering.
|
||||
|
||||
```
|
||||
GET /list/{type}?sort={sort}&order={order}&store={store}&source={source}&tags={tags}&pattern={pattern}
|
||||
```
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
- `sort` (optional): Sort field
|
||||
- `order` (optional): Sort order ("asc" or "desc")
|
||||
- `store` (optional): Storage type filter ("db" or "file")
|
||||
- `source` (optional): Include source code in response (true/false)
|
||||
- `tags` (optional): Comma-separated list of tags to filter by
|
||||
- `pattern` (optional): File name pattern matching
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
curl -X GET "/v1/dsl/list/model?store=file&tags=user,auth" \
|
||||
-H "Authorization: Bearer {token}"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "user",
|
||||
"type": "model",
|
||||
"label": "User Model",
|
||||
"description": "User management model",
|
||||
"tags": ["auth", "user"],
|
||||
"path": "models/user.mod.yao",
|
||||
"store": "file",
|
||||
"status": "loaded"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### Check DSL Existence
|
||||
|
||||
Check if a DSL resource exists.
|
||||
|
||||
```
|
||||
GET /exists/{type}/{id}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"exists": true
|
||||
}
|
||||
```
|
||||
|
||||
### CRUD Operations
|
||||
|
||||
#### Create DSL
|
||||
|
||||
Create a new DSL resource.
|
||||
|
||||
```
|
||||
POST /create/{type}
|
||||
```
|
||||
|
||||
**Request Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "test_user",
|
||||
"source": "{\n \"name\": \"test_user\",\n \"table\": {\n \"name\": \"test_users\",\n \"comment\": \"Test User\"\n },\n \"columns\": [\n { \"name\": \"id\", \"type\": \"ID\" },\n { \"name\": \"name\", \"type\": \"string\", \"length\": 80 }\n ]\n}",
|
||||
"store": "file"
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
curl -X POST "/v1/dsl/create/model" \
|
||||
-H "Authorization: Bearer {token}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"id": "test_user",
|
||||
"source": "{ \"name\": \"test_user\", \"table\": { \"name\": \"test_users\" }, \"columns\": [{ \"name\": \"id\", \"type\": \"ID\" }] }",
|
||||
"store": "file"
|
||||
}'
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "DSL created successfully"
|
||||
}
|
||||
```
|
||||
|
||||
#### Update DSL
|
||||
|
||||
Update an existing DSL resource.
|
||||
|
||||
```
|
||||
PUT /update/{type}
|
||||
```
|
||||
|
||||
**Request Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "test_user",
|
||||
"source": "{\n \"name\": \"test_user\",\n \"table\": {\n \"name\": \"test_users\",\n \"comment\": \"Updated Test User\"\n },\n \"columns\": [\n { \"name\": \"id\", \"type\": \"ID\" },\n { \"name\": \"name\", \"type\": \"string\", \"length\": 100 }\n ]\n}"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "DSL updated successfully"
|
||||
}
|
||||
```
|
||||
|
||||
#### Delete DSL
|
||||
|
||||
Delete a DSL resource.
|
||||
|
||||
```
|
||||
DELETE /delete/{type}/{id}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
curl -X DELETE "/v1/dsl/delete/model/test_user" \
|
||||
-H "Authorization: Bearer {token}"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "DSL deleted successfully"
|
||||
}
|
||||
```
|
||||
|
||||
### Load Management
|
||||
|
||||
#### Load DSL
|
||||
|
||||
Load a DSL resource into memory.
|
||||
|
||||
```
|
||||
POST /load/{type}
|
||||
```
|
||||
|
||||
**Request Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "user",
|
||||
"source": "{ ... }",
|
||||
"store": "file"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "DSL loaded successfully"
|
||||
}
|
||||
```
|
||||
|
||||
#### Unload DSL
|
||||
|
||||
Unload a DSL resource from memory.
|
||||
|
||||
```
|
||||
POST /unload/{type}
|
||||
```
|
||||
|
||||
**Request Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "user",
|
||||
"store": "file"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "DSL unloaded successfully"
|
||||
}
|
||||
```
|
||||
|
||||
#### Reload DSL
|
||||
|
||||
Reload a DSL resource (unload then load).
|
||||
|
||||
```
|
||||
POST /reload/{type}
|
||||
```
|
||||
|
||||
**Request Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "user",
|
||||
"source": "{ ... }",
|
||||
"store": "file"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "DSL reloaded successfully"
|
||||
}
|
||||
```
|
||||
|
||||
### Execution and Validation
|
||||
|
||||
#### Execute DSL Method
|
||||
|
||||
Execute a method on a loaded DSL resource.
|
||||
|
||||
```
|
||||
POST /execute/{type}/{id}/{method}
|
||||
```
|
||||
|
||||
**Request Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"args": ["arg1", "arg2"]
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
curl -X POST "/v1/dsl/execute/model/user/find" \
|
||||
-H "Authorization: Bearer {token}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"args": [1, {"select": ["id", "name"]}]
|
||||
}'
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"result": {
|
||||
"id": 1,
|
||||
"name": "John Doe"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Validate DSL Source
|
||||
|
||||
Validate DSL source code syntax.
|
||||
|
||||
```
|
||||
POST /validate/{type}
|
||||
```
|
||||
|
||||
**Request Body:**
|
||||
|
||||
```json
|
||||
{
|
||||
"source": "{\n \"name\": \"user\",\n \"table\": {\n \"name\": \"users\"\n }\n}"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"valid": true,
|
||||
"messages": []
|
||||
}
|
||||
```
|
||||
|
||||
Or if there are validation errors:
|
||||
|
||||
```json
|
||||
{
|
||||
"valid": false,
|
||||
"messages": [
|
||||
{
|
||||
"file": "",
|
||||
"line": 5,
|
||||
"column": 10,
|
||||
"message": "Missing required field 'columns'",
|
||||
"severity": "error"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Error Responses
|
||||
|
||||
All endpoints return appropriate HTTP status codes and error messages:
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "DSL ID is required"
|
||||
}
|
||||
```
|
||||
|
||||
Common HTTP status codes:
|
||||
|
||||
- `200` - Success
|
||||
- `201` - Created
|
||||
- `400` - Bad Request (invalid parameters)
|
||||
- `404` - Not Found
|
||||
- `500` - Internal Server Error
|
||||
|
||||
## Example Workflows
|
||||
|
||||
### Creating a New Model
|
||||
|
||||
1. **Validate the source first:**
|
||||
|
||||
```bash
|
||||
curl -X POST "/v1/dsl/validate/model" \
|
||||
-H "Authorization: Bearer {token}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"source": "{ \"name\": \"product\", \"table\": { \"name\": \"products\" }, \"columns\": [{ \"name\": \"id\", \"type\": \"ID\" }] }"
|
||||
}'
|
||||
```
|
||||
|
||||
2. **Create the model:**
|
||||
|
||||
```bash
|
||||
curl -X POST "/v1/dsl/create/model" \
|
||||
-H "Authorization: Bearer {token}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"id": "product",
|
||||
"source": "{ \"name\": \"product\", \"table\": { \"name\": \"products\" }, \"columns\": [{ \"name\": \"id\", \"type\": \"ID\" }] }",
|
||||
"store": "file"
|
||||
}'
|
||||
```
|
||||
|
||||
3. **Verify it was created:**
|
||||
|
||||
```bash
|
||||
curl -X GET "/v1/dsl/inspect/model/product" \
|
||||
-H "Authorization: Bearer {token}"
|
||||
```
|
||||
|
||||
### Updating and Reloading a DSL
|
||||
|
||||
1. **Update the DSL:**
|
||||
|
||||
```bash
|
||||
curl -X PUT "/v1/dsl/update/model" \
|
||||
-H "Authorization: Bearer {token}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"id": "product",
|
||||
"source": "{ \"name\": \"product\", \"label\": \"Product Model\", \"table\": { \"name\": \"products\" }, \"columns\": [{ \"name\": \"id\", \"type\": \"ID\" }, { \"name\": \"name\", \"type\": \"string\" }] }"
|
||||
}'
|
||||
```
|
||||
|
||||
2. **Reload to apply changes:**
|
||||
|
||||
```bash
|
||||
curl -X POST "/v1/dsl/reload/model" \
|
||||
-H "Authorization: Bearer {token}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"id": "product",
|
||||
"store": "file"
|
||||
}'
|
||||
```
|
||||
|
||||
This API provides comprehensive DSL management capabilities that align with the test cases and interface definitions in the codebase.
|
||||
442
openapi/dsl/dsl.go
Normal file
442
openapi/dsl/dsl.go
Normal file
|
|
@ -0,0 +1,442 @@
|
|||
package dsl
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yaoapp/yao/dsl"
|
||||
"github.com/yaoapp/yao/dsl/types"
|
||||
oauthTypes "github.com/yaoapp/yao/openapi/oauth/types"
|
||||
)
|
||||
|
||||
// Yao DSL Manager API
|
||||
|
||||
// Attach attaches the DSL management handlers to the router
|
||||
func Attach(group *gin.RouterGroup, oauth oauthTypes.OAuth) {
|
||||
|
||||
// Protect all endpoints with OAuth
|
||||
group.Handlers = append(group.Handlers, oauth.Guard)
|
||||
|
||||
// DSL Information endpoints
|
||||
group.GET("/inspect/:type/:id", inspect)
|
||||
group.GET("/source/:type/:id", source)
|
||||
group.GET("/path/:type/:id", path)
|
||||
group.GET("/list/:type", list)
|
||||
group.GET("/exists/:type/:id", exists)
|
||||
|
||||
// DSL CRUD operations
|
||||
group.POST("/create/:type", create)
|
||||
group.PUT("/update/:type", update)
|
||||
group.DELETE("/delete/:type/:id", delete)
|
||||
|
||||
// DSL Load management
|
||||
group.POST("/load/:type", load)
|
||||
group.POST("/unload/:type", unload)
|
||||
group.POST("/reload/:type", reload)
|
||||
|
||||
// DSL Execute and Validate
|
||||
group.POST("/execute/:type/:id/:method", execute)
|
||||
group.POST("/validate/:type", validate)
|
||||
}
|
||||
|
||||
// Inspect DSL information
|
||||
func inspect(c *gin.Context) {
|
||||
dslType := types.Type(c.Param("type"))
|
||||
id := c.Param("id")
|
||||
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "DSL ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
dslManager, err := dsl.New(dslType)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid DSL type: " + string(dslType)})
|
||||
return
|
||||
}
|
||||
|
||||
info, err := dslManager.Inspect(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, info)
|
||||
}
|
||||
|
||||
// Get DSL source code
|
||||
func source(c *gin.Context) {
|
||||
dslType := types.Type(c.Param("type"))
|
||||
id := c.Param("id")
|
||||
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "DSL ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
dslManager, err := dsl.New(dslType)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid DSL type: " + string(dslType)})
|
||||
return
|
||||
}
|
||||
|
||||
sourceCode, err := dslManager.Source(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"source": sourceCode})
|
||||
}
|
||||
|
||||
// Get DSL file path
|
||||
func path(c *gin.Context) {
|
||||
dslType := types.Type(c.Param("type"))
|
||||
id := c.Param("id")
|
||||
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "DSL ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
dslManager, err := dsl.New(dslType)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid DSL type: " + string(dslType)})
|
||||
return
|
||||
}
|
||||
|
||||
filePath, err := dslManager.Path(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"path": filePath})
|
||||
}
|
||||
|
||||
// List DSLs with optional filters
|
||||
func list(c *gin.Context) {
|
||||
dslType := types.Type(c.Param("type"))
|
||||
|
||||
dslManager, err := dsl.New(dslType)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid DSL type: " + string(dslType)})
|
||||
return
|
||||
}
|
||||
|
||||
// Parse query parameters
|
||||
opts := &types.ListOptions{
|
||||
Sort: c.Query("sort"),
|
||||
Order: c.Query("order"),
|
||||
Store: types.StoreType(c.Query("store")),
|
||||
Pattern: c.Query("pattern"),
|
||||
}
|
||||
|
||||
// Parse source flag
|
||||
if sourceStr := c.Query("source"); sourceStr != "" {
|
||||
if sourceBool, err := strconv.ParseBool(sourceStr); err == nil {
|
||||
opts.Source = sourceBool
|
||||
}
|
||||
}
|
||||
|
||||
// Parse tags from query parameter (comma-separated)
|
||||
if tagsStr := c.Query("tags"); tagsStr != "" {
|
||||
c.ShouldBindQuery(&struct {
|
||||
Tags []string `form:"tags"`
|
||||
}{Tags: opts.Tags})
|
||||
}
|
||||
|
||||
infos, err := dslManager.List(c.Request.Context(), opts)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, infos)
|
||||
}
|
||||
|
||||
// Check if DSL exists
|
||||
func exists(c *gin.Context) {
|
||||
dslType := types.Type(c.Param("type"))
|
||||
id := c.Param("id")
|
||||
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "DSL ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
dslManager, err := dsl.New(dslType)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid DSL type: " + string(dslType)})
|
||||
return
|
||||
}
|
||||
|
||||
exist, err := dslManager.Exists(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"exists": exist})
|
||||
}
|
||||
|
||||
// Create a new DSL
|
||||
func create(c *gin.Context) {
|
||||
dslType := types.Type(c.Param("type"))
|
||||
|
||||
dslManager, err := dsl.New(dslType)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid DSL type: " + string(dslType)})
|
||||
return
|
||||
}
|
||||
|
||||
var options types.CreateOptions
|
||||
if err := c.ShouldBindJSON(&options); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if options.ID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "DSL ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
if options.Source == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "DSL source is required"})
|
||||
return
|
||||
}
|
||||
|
||||
err = dslManager.Create(c.Request.Context(), &options)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, gin.H{"message": "DSL created successfully"})
|
||||
}
|
||||
|
||||
// Update an existing DSL
|
||||
func update(c *gin.Context) {
|
||||
dslType := types.Type(c.Param("type"))
|
||||
|
||||
dslManager, err := dsl.New(dslType)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid DSL type: " + string(dslType)})
|
||||
return
|
||||
}
|
||||
|
||||
var options types.UpdateOptions
|
||||
if err := c.ShouldBindJSON(&options); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if options.ID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "DSL ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
if options.Source == "" && options.Info == nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "DSL source or info is required"})
|
||||
return
|
||||
}
|
||||
|
||||
err = dslManager.Update(c.Request.Context(), &options)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "DSL updated successfully"})
|
||||
}
|
||||
|
||||
// Delete a DSL
|
||||
func delete(c *gin.Context) {
|
||||
dslType := types.Type(c.Param("type"))
|
||||
id := c.Param("id")
|
||||
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "DSL ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
dslManager, err := dsl.New(dslType)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid DSL type: " + string(dslType)})
|
||||
return
|
||||
}
|
||||
|
||||
// Parse optional request body for delete options
|
||||
var options types.DeleteOptions
|
||||
options.ID = id
|
||||
|
||||
// Try to bind JSON body if provided
|
||||
c.ShouldBindJSON(&options)
|
||||
|
||||
err = dslManager.Delete(c.Request.Context(), &options)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "DSL deleted successfully"})
|
||||
}
|
||||
|
||||
// Load a DSL
|
||||
func load(c *gin.Context) {
|
||||
dslType := types.Type(c.Param("type"))
|
||||
|
||||
dslManager, err := dsl.New(dslType)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid DSL type: " + string(dslType)})
|
||||
return
|
||||
}
|
||||
|
||||
var options types.LoadOptions
|
||||
if err := c.ShouldBindJSON(&options); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if options.ID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "DSL ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
err = dslManager.Load(c.Request.Context(), &options)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "DSL loaded successfully"})
|
||||
}
|
||||
|
||||
// Unload a DSL
|
||||
func unload(c *gin.Context) {
|
||||
dslType := types.Type(c.Param("type"))
|
||||
|
||||
dslManager, err := dsl.New(dslType)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid DSL type: " + string(dslType)})
|
||||
return
|
||||
}
|
||||
|
||||
var options types.UnloadOptions
|
||||
if err := c.ShouldBindJSON(&options); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if options.ID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "DSL ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
err = dslManager.Unload(c.Request.Context(), &options)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "DSL unloaded successfully"})
|
||||
}
|
||||
|
||||
// Reload a DSL
|
||||
func reload(c *gin.Context) {
|
||||
dslType := types.Type(c.Param("type"))
|
||||
|
||||
dslManager, err := dsl.New(dslType)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid DSL type: " + string(dslType)})
|
||||
return
|
||||
}
|
||||
|
||||
var options types.ReloadOptions
|
||||
if err := c.ShouldBindJSON(&options); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if options.ID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "DSL ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
err = dslManager.Reload(c.Request.Context(), &options)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "DSL reloaded successfully"})
|
||||
}
|
||||
|
||||
// Execute a DSL method
|
||||
func execute(c *gin.Context) {
|
||||
dslType := types.Type(c.Param("type"))
|
||||
id := c.Param("id")
|
||||
method := c.Param("method")
|
||||
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "DSL ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
if method == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Method name is required"})
|
||||
return
|
||||
}
|
||||
|
||||
dslManager, err := dsl.New(dslType)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid DSL type: " + string(dslType)})
|
||||
return
|
||||
}
|
||||
|
||||
// Parse arguments from request body
|
||||
var requestBody struct {
|
||||
Args []interface{} `json:"args"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&requestBody); err != nil {
|
||||
// If no body provided, execute without arguments
|
||||
requestBody.Args = []interface{}{}
|
||||
}
|
||||
|
||||
result, err := dslManager.Execute(c.Request.Context(), id, method, requestBody.Args...)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"result": result})
|
||||
}
|
||||
|
||||
// Validate DSL source code
|
||||
func validate(c *gin.Context) {
|
||||
dslType := types.Type(c.Param("type"))
|
||||
|
||||
dslManager, err := dsl.New(dslType)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid DSL type: " + string(dslType)})
|
||||
return
|
||||
}
|
||||
|
||||
var requestBody struct {
|
||||
Source string `json:"source" binding:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&requestBody); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "DSL source is required"})
|
||||
return
|
||||
}
|
||||
|
||||
valid, messages := dslManager.Validate(c.Request.Context(), requestBody.Source)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"valid": valid,
|
||||
"messages": messages,
|
||||
})
|
||||
}
|
||||
712
openapi/dsl_test.go
Normal file
712
openapi/dsl_test.go
Normal file
|
|
@ -0,0 +1,712 @@
|
|||
package openapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/yaoapp/yao/dsl/types"
|
||||
)
|
||||
|
||||
// TestDSLCreate tests the DSL creation endpoint
|
||||
func TestDSLCreate(t *testing.T) {
|
||||
serverURL := Prepare(t)
|
||||
defer Clean()
|
||||
|
||||
// Get base URL from server config
|
||||
baseURL := ""
|
||||
if Server != nil && Server.Config != nil {
|
||||
baseURL = Server.Config.BaseURL
|
||||
}
|
||||
|
||||
// Register test client and get token
|
||||
client := RegisterTestClient(t, "DSL Create Test Client", []string{"https://localhost/callback"})
|
||||
defer CleanupTestClient(t, client.ClientID)
|
||||
tokenInfo := ObtainAccessToken(t, serverURL, client.ClientID, client.ClientSecret, "https://localhost/callback", "openid profile")
|
||||
|
||||
// Generate unique test ID
|
||||
testID := fmt.Sprintf("test_model_%d", time.Now().UnixNano())
|
||||
|
||||
// Model test data
|
||||
modelSource := fmt.Sprintf(`{
|
||||
"name": "%s",
|
||||
"table": { "name": "%s", "comment": "Test User Model" },
|
||||
"columns": [
|
||||
{ "name": "id", "type": "ID" },
|
||||
{ "name": "name", "type": "string", "length": 80, "comment": "User Name", "index": true },
|
||||
{ "name": "status", "type": "enum", "option": ["active", "disabled"], "default": "active", "comment": "Status", "index": true }
|
||||
],
|
||||
"tags": ["test_%s"],
|
||||
"label": "Test Model",
|
||||
"description": "Test Model Description",
|
||||
"option": { "timestamps": true, "soft_deletes": true }
|
||||
}`, testID, testID, testID)
|
||||
|
||||
// Test creation with different stores
|
||||
stores := []string{"db", "file"}
|
||||
|
||||
for _, store := range stores {
|
||||
t.Run(fmt.Sprintf("CreateModel_%s", store), func(t *testing.T) {
|
||||
// Prepare request body
|
||||
createData := map[string]interface{}{
|
||||
"id": testID + "_" + store,
|
||||
"source": modelSource,
|
||||
"store": store,
|
||||
}
|
||||
|
||||
body, err := json.Marshal(createData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Create HTTP request
|
||||
req, err := http.NewRequest("POST", serverURL+baseURL+"/dsl/create/model", bytes.NewBuffer(body))
|
||||
assert.NoError(t, err)
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+tokenInfo.AccessToken)
|
||||
|
||||
// Make request
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Check response
|
||||
assert.Equal(t, http.StatusCreated, resp.StatusCode)
|
||||
|
||||
var response map[string]interface{}
|
||||
err = json.NewDecoder(resp.Body).Decode(&response)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "DSL created successfully", response["message"])
|
||||
|
||||
t.Logf("Successfully created model DSL with store: %s", store)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestDSLInspect tests the DSL inspection endpoint
|
||||
func TestDSLInspect(t *testing.T) {
|
||||
serverURL := Prepare(t)
|
||||
defer Clean()
|
||||
|
||||
baseURL := ""
|
||||
if Server != nil && Server.Config != nil {
|
||||
baseURL = Server.Config.BaseURL
|
||||
}
|
||||
|
||||
client := RegisterTestClient(t, "DSL Inspect Test Client", []string{"https://localhost/callback"})
|
||||
defer CleanupTestClient(t, client.ClientID)
|
||||
tokenInfo := ObtainAccessToken(t, serverURL, client.ClientID, client.ClientSecret, "https://localhost/callback", "openid profile")
|
||||
|
||||
testID := fmt.Sprintf("test_inspect_%d", time.Now().UnixNano())
|
||||
modelSource := fmt.Sprintf(`{
|
||||
"name": "%s",
|
||||
"table": { "name": "%s", "comment": "Test Inspect Model" },
|
||||
"columns": [
|
||||
{ "name": "id", "type": "ID" },
|
||||
{ "name": "name", "type": "string", "length": 80, "comment": "User Name", "index": true }
|
||||
],
|
||||
"tags": ["test_inspect"],
|
||||
"label": "Test Inspect Model",
|
||||
"description": "Test Model for Inspection",
|
||||
"option": { "timestamps": true }
|
||||
}`, testID, testID)
|
||||
|
||||
// First create a model
|
||||
createData := map[string]interface{}{
|
||||
"id": testID,
|
||||
"source": modelSource,
|
||||
"store": "db",
|
||||
}
|
||||
|
||||
body, _ := json.Marshal(createData)
|
||||
req, _ := http.NewRequest("POST", serverURL+baseURL+"/dsl/create/model", bytes.NewBuffer(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+tokenInfo.AccessToken)
|
||||
|
||||
createResp, err := http.DefaultClient.Do(req)
|
||||
assert.NoError(t, err)
|
||||
defer createResp.Body.Close()
|
||||
assert.Equal(t, http.StatusCreated, createResp.StatusCode)
|
||||
|
||||
// Now test inspection
|
||||
req, err = http.NewRequest("GET", serverURL+baseURL+"/dsl/inspect/model/"+testID, nil)
|
||||
assert.NoError(t, err)
|
||||
req.Header.Set("Authorization", "Bearer "+tokenInfo.AccessToken)
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
defer resp.Body.Close()
|
||||
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
|
||||
var info types.Info
|
||||
err = json.NewDecoder(resp.Body).Decode(&info)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify the inspection results
|
||||
assert.Equal(t, testID, info.ID)
|
||||
assert.Equal(t, types.TypeModel, info.Type)
|
||||
assert.Equal(t, "Test Inspect Model", info.Label)
|
||||
assert.Equal(t, "Test Model for Inspection", info.Description)
|
||||
assert.Contains(t, info.Tags, "test_inspect")
|
||||
assert.False(t, info.Readonly)
|
||||
assert.False(t, info.Builtin)
|
||||
|
||||
t.Logf("Successfully inspected model DSL: %+v", info)
|
||||
}
|
||||
|
||||
// TestDSLSource tests the DSL source retrieval endpoint
|
||||
func TestDSLSource(t *testing.T) {
|
||||
serverURL := Prepare(t)
|
||||
defer Clean()
|
||||
|
||||
baseURL := ""
|
||||
if Server != nil && Server.Config != nil {
|
||||
baseURL = Server.Config.BaseURL
|
||||
}
|
||||
|
||||
client := RegisterTestClient(t, "DSL Source Test Client", []string{"https://localhost/callback"})
|
||||
defer CleanupTestClient(t, client.ClientID)
|
||||
tokenInfo := ObtainAccessToken(t, serverURL, client.ClientID, client.ClientSecret, "https://localhost/callback", "openid profile")
|
||||
|
||||
testID := fmt.Sprintf("test_source_%d", time.Now().UnixNano())
|
||||
modelSource := fmt.Sprintf(`{
|
||||
"name": "%s",
|
||||
"table": { "name": "%s", "comment": "Test Source Model" },
|
||||
"columns": [
|
||||
{ "name": "id", "type": "ID" },
|
||||
{ "name": "email", "type": "string", "length": 100, "comment": "Email", "index": true }
|
||||
],
|
||||
"tags": ["test_source"],
|
||||
"label": "Test Source Model",
|
||||
"description": "Test Model for Source Retrieval"
|
||||
}`, testID, testID)
|
||||
|
||||
// Create model first
|
||||
createData := map[string]interface{}{
|
||||
"id": testID,
|
||||
"source": modelSource,
|
||||
"store": "db",
|
||||
}
|
||||
|
||||
body, err := json.Marshal(createData)
|
||||
assert.NoError(t, err)
|
||||
req, err := http.NewRequest("POST", serverURL+baseURL+"/dsl/create/model", bytes.NewBuffer(body))
|
||||
assert.NoError(t, err)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+tokenInfo.AccessToken)
|
||||
|
||||
createResp, err := http.DefaultClient.Do(req)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createResp)
|
||||
defer createResp.Body.Close()
|
||||
|
||||
// Test source retrieval
|
||||
req, err = http.NewRequest("GET", serverURL+baseURL+"/dsl/source/model/"+testID, nil)
|
||||
assert.NoError(t, err)
|
||||
req.Header.Set("Authorization", "Bearer "+tokenInfo.AccessToken)
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
defer resp.Body.Close()
|
||||
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
|
||||
var response map[string]interface{}
|
||||
err = json.NewDecoder(resp.Body).Decode(&response)
|
||||
assert.NoError(t, err)
|
||||
|
||||
sourceReturned := response["source"].(string)
|
||||
assert.Equal(t, modelSource, sourceReturned)
|
||||
|
||||
t.Logf("Successfully retrieved model DSL source")
|
||||
}
|
||||
|
||||
// TestDSLList tests the DSL listing endpoint
|
||||
func TestDSLList(t *testing.T) {
|
||||
serverURL := Prepare(t)
|
||||
defer Clean()
|
||||
|
||||
baseURL := ""
|
||||
if Server != nil && Server.Config != nil {
|
||||
baseURL = Server.Config.BaseURL
|
||||
}
|
||||
|
||||
client := RegisterTestClient(t, "DSL List Test Client", []string{"https://localhost/callback"})
|
||||
defer CleanupTestClient(t, client.ClientID)
|
||||
tokenInfo := ObtainAccessToken(t, serverURL, client.ClientID, client.ClientSecret, "https://localhost/callback", "openid profile")
|
||||
|
||||
// Create multiple test models
|
||||
testTag := fmt.Sprintf("test_list_%d", time.Now().UnixNano())
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
testID := fmt.Sprintf("test_list_model_%d_%d", time.Now().UnixNano(), i)
|
||||
modelSource := fmt.Sprintf(`{
|
||||
"name": "%s",
|
||||
"table": { "name": "%s", "comment": "Test List Model %d" },
|
||||
"columns": [
|
||||
{ "name": "id", "type": "ID" },
|
||||
{ "name": "title", "type": "string", "length": 100 }
|
||||
],
|
||||
"tags": ["%s"],
|
||||
"label": "Test List Model %d"
|
||||
}`, testID, testID, i, testTag, i)
|
||||
|
||||
createData := map[string]interface{}{
|
||||
"id": testID,
|
||||
"source": modelSource,
|
||||
"store": "db",
|
||||
}
|
||||
|
||||
body, err := json.Marshal(createData)
|
||||
assert.NoError(t, err)
|
||||
req, err := http.NewRequest("POST", serverURL+baseURL+"/dsl/create/model", bytes.NewBuffer(body))
|
||||
assert.NoError(t, err)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+tokenInfo.AccessToken)
|
||||
|
||||
createResp, err := http.DefaultClient.Do(req)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createResp)
|
||||
createResp.Body.Close()
|
||||
}
|
||||
|
||||
// Test listing all models
|
||||
req, err := http.NewRequest("GET", serverURL+baseURL+"/dsl/list/model", nil)
|
||||
assert.NoError(t, err)
|
||||
req.Header.Set("Authorization", "Bearer "+tokenInfo.AccessToken)
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
defer resp.Body.Close()
|
||||
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
|
||||
var data []interface{}
|
||||
err = json.NewDecoder(resp.Body).Decode(&data)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.GreaterOrEqual(t, len(data), 3, "Should have at least 3 models")
|
||||
|
||||
// Test listing with tags filter
|
||||
req, err = http.NewRequest("GET", serverURL+baseURL+"/dsl/list/model?tags="+testTag, nil)
|
||||
assert.NoError(t, err)
|
||||
req.Header.Set("Authorization", "Bearer "+tokenInfo.AccessToken)
|
||||
|
||||
resp, err = http.DefaultClient.Do(req)
|
||||
assert.NoError(t, err)
|
||||
defer resp.Body.Close()
|
||||
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
|
||||
var taggedData []interface{}
|
||||
err = json.NewDecoder(resp.Body).Decode(&taggedData)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.GreaterOrEqual(t, len(taggedData), 3, "Should find the tagged models")
|
||||
|
||||
t.Logf("Successfully listed %d model DSLs", len(data))
|
||||
}
|
||||
|
||||
// TestDSLUpdate tests the DSL update endpoint
|
||||
func TestDSLUpdate(t *testing.T) {
|
||||
serverURL := Prepare(t)
|
||||
defer Clean()
|
||||
|
||||
baseURL := ""
|
||||
if Server != nil && Server.Config != nil {
|
||||
baseURL = Server.Config.BaseURL
|
||||
}
|
||||
|
||||
client := RegisterTestClient(t, "DSL Update Test Client", []string{"https://localhost/callback"})
|
||||
defer CleanupTestClient(t, client.ClientID)
|
||||
tokenInfo := ObtainAccessToken(t, serverURL, client.ClientID, client.ClientSecret, "https://localhost/callback", "openid profile")
|
||||
|
||||
testID := fmt.Sprintf("test_update_%d", time.Now().UnixNano())
|
||||
|
||||
// Original model
|
||||
originalSource := fmt.Sprintf(`{
|
||||
"name": "%s",
|
||||
"table": { "name": "%s", "comment": "Original Model" },
|
||||
"columns": [
|
||||
{ "name": "id", "type": "ID" },
|
||||
{ "name": "name", "type": "string", "length": 80 }
|
||||
],
|
||||
"tags": ["test_update"],
|
||||
"label": "Original Model"
|
||||
}`, testID, testID)
|
||||
|
||||
// Updated model
|
||||
updatedSource := fmt.Sprintf(`{
|
||||
"name": "%s",
|
||||
"table": { "name": "%s", "comment": "Updated Model" },
|
||||
"columns": [
|
||||
{ "name": "id", "type": "ID" },
|
||||
{ "name": "name", "type": "string", "length": 80 },
|
||||
{ "name": "email", "type": "string", "length": 100 }
|
||||
],
|
||||
"tags": ["test_update", "updated"],
|
||||
"label": "Updated Model",
|
||||
"description": "Updated model description"
|
||||
}`, testID, testID)
|
||||
|
||||
// Create original model
|
||||
createData := map[string]interface{}{
|
||||
"id": testID,
|
||||
"source": originalSource,
|
||||
"store": "db",
|
||||
}
|
||||
|
||||
body, err := json.Marshal(createData)
|
||||
assert.NoError(t, err)
|
||||
req, err := http.NewRequest("POST", serverURL+baseURL+"/dsl/create/model", bytes.NewBuffer(body))
|
||||
assert.NoError(t, err)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+tokenInfo.AccessToken)
|
||||
|
||||
createResp, err := http.DefaultClient.Do(req)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createResp)
|
||||
createResp.Body.Close()
|
||||
|
||||
// Update model
|
||||
updateData := map[string]interface{}{
|
||||
"id": testID,
|
||||
"source": updatedSource,
|
||||
}
|
||||
|
||||
body, err = json.Marshal(updateData)
|
||||
assert.NoError(t, err)
|
||||
req, err = http.NewRequest("PUT", serverURL+baseURL+"/dsl/update/model", bytes.NewBuffer(body))
|
||||
assert.NoError(t, err)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+tokenInfo.AccessToken)
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
defer resp.Body.Close()
|
||||
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
|
||||
var response map[string]interface{}
|
||||
err = json.NewDecoder(resp.Body).Decode(&response)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "DSL updated successfully", response["message"])
|
||||
|
||||
// Verify the update by inspecting the model
|
||||
req, err = http.NewRequest("GET", serverURL+baseURL+"/dsl/inspect/model/"+testID, nil)
|
||||
assert.NoError(t, err)
|
||||
req.Header.Set("Authorization", "Bearer "+tokenInfo.AccessToken)
|
||||
|
||||
inspectResp, err := http.DefaultClient.Do(req)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, inspectResp)
|
||||
defer inspectResp.Body.Close()
|
||||
|
||||
var info types.Info
|
||||
err = json.NewDecoder(inspectResp.Body).Decode(&info)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "Updated Model", info.Label)
|
||||
assert.Equal(t, "Updated model description", info.Description)
|
||||
assert.Contains(t, info.Tags, "updated")
|
||||
|
||||
t.Logf("Successfully updated model DSL")
|
||||
}
|
||||
|
||||
// TestDSLExists tests the DSL existence check endpoint
|
||||
func TestDSLExists(t *testing.T) {
|
||||
serverURL := Prepare(t)
|
||||
defer Clean()
|
||||
|
||||
baseURL := ""
|
||||
if Server != nil && Server.Config != nil {
|
||||
baseURL = Server.Config.BaseURL
|
||||
}
|
||||
|
||||
client := RegisterTestClient(t, "DSL Exists Test Client", []string{"https://localhost/callback"})
|
||||
defer CleanupTestClient(t, client.ClientID)
|
||||
tokenInfo := ObtainAccessToken(t, serverURL, client.ClientID, client.ClientSecret, "https://localhost/callback", "openid profile")
|
||||
|
||||
testID := fmt.Sprintf("test_exists_%d", time.Now().UnixNano())
|
||||
nonExistentID := fmt.Sprintf("non_existent_%d", time.Now().UnixNano())
|
||||
|
||||
// Test non-existent model first
|
||||
req, err := http.NewRequest("GET", serverURL+baseURL+"/dsl/exists/model/"+nonExistentID, nil)
|
||||
assert.NoError(t, err)
|
||||
req.Header.Set("Authorization", "Bearer "+tokenInfo.AccessToken)
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
assert.NoError(t, err)
|
||||
defer resp.Body.Close()
|
||||
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
|
||||
var response map[string]interface{}
|
||||
err = json.NewDecoder(resp.Body).Decode(&response)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, response["exists"].(bool))
|
||||
|
||||
// Create a model
|
||||
modelSource := fmt.Sprintf(`{
|
||||
"name": "%s",
|
||||
"table": { "name": "%s" },
|
||||
"columns": [{"name": "id", "type": "ID"}]
|
||||
}`, testID, testID)
|
||||
|
||||
createData := map[string]interface{}{
|
||||
"id": testID,
|
||||
"source": modelSource,
|
||||
"store": "db",
|
||||
}
|
||||
|
||||
body, err := json.Marshal(createData)
|
||||
assert.NoError(t, err)
|
||||
createReq, err := http.NewRequest("POST", serverURL+baseURL+"/dsl/create/model", bytes.NewBuffer(body))
|
||||
assert.NoError(t, err)
|
||||
createReq.Header.Set("Content-Type", "application/json")
|
||||
createReq.Header.Set("Authorization", "Bearer "+tokenInfo.AccessToken)
|
||||
|
||||
createResp, err := http.DefaultClient.Do(createReq)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createResp)
|
||||
createResp.Body.Close()
|
||||
|
||||
// Test existing model
|
||||
req, err = http.NewRequest("GET", serverURL+baseURL+"/dsl/exists/model/"+testID, nil)
|
||||
assert.NoError(t, err)
|
||||
req.Header.Set("Authorization", "Bearer "+tokenInfo.AccessToken)
|
||||
|
||||
resp, err = http.DefaultClient.Do(req)
|
||||
assert.NoError(t, err)
|
||||
defer resp.Body.Close()
|
||||
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
|
||||
err = json.NewDecoder(resp.Body).Decode(&response)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, response["exists"].(bool))
|
||||
|
||||
t.Logf("Successfully tested model DSL existence")
|
||||
}
|
||||
|
||||
// TestDSLDelete tests the DSL deletion endpoint
|
||||
func TestDSLDelete(t *testing.T) {
|
||||
serverURL := Prepare(t)
|
||||
defer Clean()
|
||||
|
||||
baseURL := ""
|
||||
if Server != nil && Server.Config != nil {
|
||||
baseURL = Server.Config.BaseURL
|
||||
}
|
||||
|
||||
client := RegisterTestClient(t, "DSL Delete Test Client", []string{"https://localhost/callback"})
|
||||
defer CleanupTestClient(t, client.ClientID)
|
||||
tokenInfo := ObtainAccessToken(t, serverURL, client.ClientID, client.ClientSecret, "https://localhost/callback", "openid profile")
|
||||
|
||||
testID := fmt.Sprintf("test_delete_%d", time.Now().UnixNano())
|
||||
|
||||
// Create a model first
|
||||
modelSource := fmt.Sprintf(`{
|
||||
"name": "%s",
|
||||
"table": { "name": "%s" },
|
||||
"columns": [{"name": "id", "type": "ID"}]
|
||||
}`, testID, testID)
|
||||
|
||||
createData := map[string]interface{}{
|
||||
"id": testID,
|
||||
"source": modelSource,
|
||||
"store": "db",
|
||||
}
|
||||
|
||||
body, err := json.Marshal(createData)
|
||||
assert.NoError(t, err)
|
||||
createReq, err := http.NewRequest("POST", serverURL+baseURL+"/dsl/create/model", bytes.NewBuffer(body))
|
||||
assert.NoError(t, err)
|
||||
createReq.Header.Set("Content-Type", "application/json")
|
||||
createReq.Header.Set("Authorization", "Bearer "+tokenInfo.AccessToken)
|
||||
|
||||
createResp, err := http.DefaultClient.Do(createReq)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createResp)
|
||||
createResp.Body.Close()
|
||||
|
||||
// Delete the model
|
||||
req, err := http.NewRequest("DELETE", serverURL+baseURL+"/dsl/delete/model/"+testID, nil)
|
||||
assert.NoError(t, err)
|
||||
req.Header.Set("Authorization", "Bearer "+tokenInfo.AccessToken)
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
defer resp.Body.Close()
|
||||
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
|
||||
var response map[string]interface{}
|
||||
err = json.NewDecoder(resp.Body).Decode(&response)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "DSL deleted successfully", response["message"])
|
||||
|
||||
// Verify deletion by checking existence
|
||||
existsReq, err := http.NewRequest("GET", serverURL+baseURL+"/dsl/exists/model/"+testID, nil)
|
||||
assert.NoError(t, err)
|
||||
existsReq.Header.Set("Authorization", "Bearer "+tokenInfo.AccessToken)
|
||||
|
||||
existsResp, err := http.DefaultClient.Do(existsReq)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, existsResp)
|
||||
defer existsResp.Body.Close()
|
||||
|
||||
var existsResponse map[string]interface{}
|
||||
err = json.NewDecoder(existsResp.Body).Decode(&existsResponse)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, existsResponse["exists"].(bool))
|
||||
|
||||
t.Logf("Successfully deleted model DSL")
|
||||
}
|
||||
|
||||
// TestDSLValidate tests the DSL validation endpoint
|
||||
func TestDSLValidate(t *testing.T) {
|
||||
serverURL := Prepare(t)
|
||||
defer Clean()
|
||||
|
||||
baseURL := ""
|
||||
if Server != nil && Server.Config != nil {
|
||||
baseURL = Server.Config.BaseURL
|
||||
}
|
||||
|
||||
client := RegisterTestClient(t, "DSL Validate Test Client", []string{"https://localhost/callback"})
|
||||
defer CleanupTestClient(t, client.ClientID)
|
||||
tokenInfo := ObtainAccessToken(t, serverURL, client.ClientID, client.ClientSecret, "https://localhost/callback", "openid profile")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
source string
|
||||
description string
|
||||
}{
|
||||
{
|
||||
name: "ValidModel",
|
||||
source: `{
|
||||
"name": "valid_model",
|
||||
"table": { "name": "valid_model" },
|
||||
"columns": [
|
||||
{"name": "id", "type": "ID"},
|
||||
{"name": "name", "type": "string", "length": 80}
|
||||
]
|
||||
}`,
|
||||
description: "Valid model definition",
|
||||
},
|
||||
{
|
||||
name: "AnotherModel",
|
||||
source: `{
|
||||
"name": "another_model",
|
||||
"table": { "name": "another_model" },
|
||||
"columns": [
|
||||
{"name": "id", "type": "ID"},
|
||||
{"name": "title", "type": "string", "length": 100}
|
||||
]
|
||||
}`,
|
||||
description: "Another model definition",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
requestBody := map[string]string{
|
||||
"source": tt.source,
|
||||
}
|
||||
|
||||
body, err := json.Marshal(requestBody)
|
||||
assert.NoError(t, err)
|
||||
|
||||
req, err := http.NewRequest("POST", serverURL+baseURL+"/dsl/validate/model", bytes.NewBuffer(body))
|
||||
assert.NoError(t, err)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+tokenInfo.AccessToken)
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
defer resp.Body.Close()
|
||||
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
|
||||
var response map[string]interface{}
|
||||
err = json.NewDecoder(resp.Body).Decode(&response)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Just verify the response has the expected structure
|
||||
assert.Contains(t, response, "valid")
|
||||
assert.Contains(t, response, "messages")
|
||||
|
||||
valid, ok := response["valid"].(bool)
|
||||
assert.True(t, ok, "valid should be a boolean")
|
||||
|
||||
if messages, ok := response["messages"]; ok {
|
||||
t.Logf("Validation messages for %s: %v", tt.description, messages)
|
||||
}
|
||||
|
||||
t.Logf("Successfully validated %s: valid=%v", tt.description, valid)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestDSLUnauthorized tests that endpoints return 401 when not authenticated
|
||||
func TestDSLUnauthorized(t *testing.T) {
|
||||
serverURL := Prepare(t)
|
||||
defer Clean()
|
||||
|
||||
baseURL := ""
|
||||
if Server != nil && Server.Config != nil {
|
||||
baseURL = Server.Config.BaseURL
|
||||
}
|
||||
|
||||
endpoints := []struct {
|
||||
method string
|
||||
path string
|
||||
body string
|
||||
}{
|
||||
{"GET", "/dsl/inspect/model/test", ""},
|
||||
{"GET", "/dsl/source/model/test", ""},
|
||||
{"GET", "/dsl/list/model", ""},
|
||||
{"GET", "/dsl/exists/model/test", ""},
|
||||
{"POST", "/dsl/create/model", `{"id":"test","source":"{}"}`},
|
||||
{"PUT", "/dsl/update/model", `{"id":"test","source":"{}"}`},
|
||||
{"DELETE", "/dsl/delete/model/test", ""},
|
||||
{"POST", "/dsl/validate/model", `{"source":"{}"}`},
|
||||
}
|
||||
|
||||
for _, endpoint := range endpoints {
|
||||
t.Run(fmt.Sprintf("Unauthorized_%s_%s", endpoint.method, endpoint.path), func(t *testing.T) {
|
||||
var req *http.Request
|
||||
var err error
|
||||
|
||||
if endpoint.body != "" {
|
||||
req, err = http.NewRequest(endpoint.method, serverURL+baseURL+endpoint.path, bytes.NewBufferString(endpoint.body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
} else {
|
||||
req, err = http.NewRequest(endpoint.method, serverURL+baseURL+endpoint.path, nil)
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
|
||||
// No Authorization header
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, resp)
|
||||
defer resp.Body.Close()
|
||||
|
||||
assert.Equal(t, http.StatusUnauthorized, resp.StatusCode)
|
||||
|
||||
t.Logf("Correctly rejected unauthorized request to %s %s", endpoint.method, endpoint.path)
|
||||
})
|
||||
}
|
||||
}
|
||||
270
openapi/hello/README.md
Normal file
270
openapi/hello/README.md
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
# Hello World API
|
||||
|
||||
Simple endpoints for testing connectivity, server status, and OAuth authentication functionality.
|
||||
|
||||
## Base URL
|
||||
|
||||
All endpoints are prefixed with the configured base URL followed by `/helloworld` (e.g., `/v1/helloworld`).
|
||||
|
||||
## Endpoints
|
||||
|
||||
### Public Endpoint
|
||||
|
||||
Test basic server connectivity without authentication.
|
||||
|
||||
```
|
||||
GET /helloworld/public
|
||||
POST /helloworld/public
|
||||
```
|
||||
|
||||
**No authentication required.**
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
curl -X GET "/v1/helloworld/public"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"MESSAGE": "HELLO, WORLD",
|
||||
"SERVER_TIME": "2024-01-15T10:30:00Z",
|
||||
"VERSION": "1.0.0",
|
||||
"PRVERSION": "1.0.0-preview",
|
||||
"CUI": "1.0.0",
|
||||
"PRCUI": "1.0.0-preview",
|
||||
"APP": "YaoApp",
|
||||
"APP_VERSION": "1.0.0"
|
||||
}
|
||||
```
|
||||
|
||||
**Response Fields:**
|
||||
|
||||
- `MESSAGE` - Static "HELLO, WORLD" message
|
||||
- `SERVER_TIME` - Current server time in RFC3339 format
|
||||
- `VERSION` - Yao framework version
|
||||
- `PRVERSION` - Yao framework preview version
|
||||
- `CUI` - Yao CUI version
|
||||
- `PRCUI` - Yao CUI preview version
|
||||
- `APP` - Application name
|
||||
- `APP_VERSION` - Application version
|
||||
|
||||
### Protected Endpoint
|
||||
|
||||
Test OAuth authentication and authorization.
|
||||
|
||||
```
|
||||
GET /helloworld/protected
|
||||
POST /helloworld/protected
|
||||
```
|
||||
|
||||
**Authentication:** Required (OAuth Bearer token)
|
||||
|
||||
**Headers:**
|
||||
|
||||
```
|
||||
Authorization: Bearer {access_token}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
# First, obtain an access token
|
||||
curl -X POST "/v1/oauth/token" \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "grant_type=client_credentials&client_id=your_client_id&client_secret=your_client_secret"
|
||||
|
||||
# Then use the token to access the protected endpoint
|
||||
curl -X GET "/v1/helloworld/protected" \
|
||||
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
Same response format as the public endpoint, confirming that authentication is working correctly.
|
||||
|
||||
```json
|
||||
{
|
||||
"MESSAGE": "HELLO, WORLD",
|
||||
"SERVER_TIME": "2024-01-15T10:30:00Z",
|
||||
"VERSION": "1.0.0",
|
||||
"PRVERSION": "1.0.0-preview",
|
||||
"CUI": "1.0.0",
|
||||
"PRCUI": "1.0.0-preview",
|
||||
"APP": "YaoApp",
|
||||
"APP_VERSION": "1.0.0"
|
||||
}
|
||||
```
|
||||
|
||||
## HTTP Methods
|
||||
|
||||
Both endpoints support both GET and POST methods, allowing flexibility for different client requirements and testing scenarios.
|
||||
|
||||
## Error Responses
|
||||
|
||||
### Unauthorized Access
|
||||
|
||||
When accessing the protected endpoint without proper authentication:
|
||||
|
||||
**HTTP Status:** `401 Unauthorized`
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "invalid_token",
|
||||
"error_description": "The access token provided is expired, revoked, malformed, or invalid"
|
||||
}
|
||||
```
|
||||
|
||||
### Invalid Token
|
||||
|
||||
When using an invalid or expired token:
|
||||
|
||||
**HTTP Status:** `401 Unauthorized`
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "invalid_token",
|
||||
"error_description": "The access token provided is invalid"
|
||||
}
|
||||
```
|
||||
|
||||
## Use Cases
|
||||
|
||||
### Health Check
|
||||
|
||||
Use the public endpoint as a health check for monitoring systems:
|
||||
|
||||
```bash
|
||||
# Simple health check
|
||||
curl -f "/v1/helloworld/public" > /dev/null 2>&1 && echo "Service is healthy" || echo "Service is down"
|
||||
```
|
||||
|
||||
### Authentication Testing
|
||||
|
||||
Use the protected endpoint to verify OAuth authentication setup:
|
||||
|
||||
```bash
|
||||
# Test authentication flow
|
||||
TOKEN=$(curl -s -X POST "/v1/oauth/token" \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "grant_type=client_credentials&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET" \
|
||||
| jq -r '.access_token')
|
||||
|
||||
curl -X GET "/v1/helloworld/protected" \
|
||||
-H "Authorization: Bearer $TOKEN"
|
||||
```
|
||||
|
||||
### Development and Debugging
|
||||
|
||||
These endpoints are useful for:
|
||||
|
||||
- **API Gateway Testing** - Verify routing and load balancer configuration
|
||||
- **Authentication Debugging** - Test OAuth token validation
|
||||
- **Environment Verification** - Check server version and configuration
|
||||
- **Network Connectivity** - Basic reachability testing
|
||||
- **Performance Baseline** - Minimal response time measurement
|
||||
|
||||
## Integration Examples
|
||||
|
||||
### JavaScript/Browser
|
||||
|
||||
```javascript
|
||||
// Public endpoint
|
||||
fetch("/v1/helloworld/public")
|
||||
.then((response) => response.json())
|
||||
.then((data) => console.log(data));
|
||||
|
||||
// Protected endpoint
|
||||
const token = localStorage.getItem("access_token");
|
||||
fetch("/v1/helloworld/protected", {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data) => console.log(data));
|
||||
```
|
||||
|
||||
### Python
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
# Public endpoint
|
||||
response = requests.get('/v1/helloworld/public')
|
||||
print(response.json())
|
||||
|
||||
# Protected endpoint
|
||||
headers = {'Authorization': f'Bearer {access_token}'}
|
||||
response = requests.get('/v1/helloworld/protected', headers=headers)
|
||||
print(response.json())
|
||||
```
|
||||
|
||||
### Go
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func testPublicEndpoint() {
|
||||
resp, err := http.Get("/v1/helloworld/public")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
fmt.Println(string(body))
|
||||
}
|
||||
|
||||
func testProtectedEndpoint(token string) {
|
||||
req, _ := http.NewRequest("GET", "/v1/helloworld/protected", nil)
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
fmt.Println(string(body))
|
||||
}
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Public Endpoint
|
||||
|
||||
- No sensitive information is exposed
|
||||
- Safe for use in monitoring and health checks
|
||||
- Should be rate-limited to prevent abuse
|
||||
|
||||
### Protected Endpoint
|
||||
|
||||
- Requires valid OAuth access token
|
||||
- Validates token signature and expiration
|
||||
- Can be used to test authorization scopes (if implemented)
|
||||
|
||||
## Server Information
|
||||
|
||||
The response includes various version and application information useful for:
|
||||
|
||||
- **Version Compatibility** - Ensure client compatibility with server version
|
||||
- **Environment Identification** - Distinguish between development, staging, and production
|
||||
- **Debugging** - Identify exact server version when reporting issues
|
||||
- **Monitoring** - Track server deployments and version rollouts
|
||||
|
||||
This API serves as a foundation for testing and validating the Yao OpenAPI infrastructure.
|
||||
|
|
@ -1,30 +1,27 @@
|
|||
package openapi
|
||||
package hello
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yaoapp/yao/openapi/oauth/types"
|
||||
"github.com/yaoapp/yao/share"
|
||||
)
|
||||
|
||||
// attachHelloWorld attaches the hello world handlers to the router
|
||||
func (openapi *OpenAPI) attachHelloWorld(base *gin.RouterGroup) {
|
||||
|
||||
// hello handlers
|
||||
hello := base.Group("/helloworld")
|
||||
// Attach attaches the hello world handlers to the router
|
||||
func Attach(group *gin.RouterGroup, oauth types.OAuth) {
|
||||
|
||||
// Health check
|
||||
hello.GET("/public", openapi.helloWorldPublic)
|
||||
hello.POST("/public", openapi.helloWorldPublic)
|
||||
group.GET("/public", helloWorldPublic)
|
||||
group.POST("/public", helloWorldPublic)
|
||||
|
||||
// OAuth Protected Resource
|
||||
hello.GET("/protected", openapi.OAuth.Guard, openapi.helloWorldProtected)
|
||||
hello.POST("/protected", openapi.OAuth.Guard, openapi.helloWorldProtected)
|
||||
group.GET("/protected", oauth.Guard, helloWorldProtected)
|
||||
group.POST("/protected", oauth.Guard, helloWorldProtected)
|
||||
}
|
||||
|
||||
// helloWorldPublic is the handler for the hello world endpoint
|
||||
func (openapi *OpenAPI) helloWorldPublic(c *gin.Context) {
|
||||
func helloWorldPublic(c *gin.Context) {
|
||||
serverTime := time.Now().Format(time.RFC3339)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"MESSAGE": "HELLO, WORLD",
|
||||
|
|
@ -39,7 +36,7 @@ func (openapi *OpenAPI) helloWorldPublic(c *gin.Context) {
|
|||
}
|
||||
|
||||
// helloWorldHello is the handler for the hello world endpoint
|
||||
func (openapi *OpenAPI) helloWorldProtected(c *gin.Context) {
|
||||
func helloWorldProtected(c *gin.Context) {
|
||||
serverTime := time.Now().Format(time.RFC3339)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"MESSAGE": "HELLO, WORLD",
|
||||
|
|
@ -6,6 +6,8 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
"github.com/yaoapp/gou/application"
|
||||
"github.com/yaoapp/yao/config"
|
||||
"github.com/yaoapp/yao/openapi/dsl"
|
||||
"github.com/yaoapp/yao/openapi/hello"
|
||||
"github.com/yaoapp/yao/openapi/oauth"
|
||||
"github.com/yaoapp/yao/openapi/oauth/types"
|
||||
)
|
||||
|
|
@ -71,7 +73,10 @@ func (openapi *OpenAPI) Attach(router *gin.Engine) {
|
|||
openapi.attachOAuth(group)
|
||||
|
||||
// Hello World handlers
|
||||
openapi.attachHelloWorld(group)
|
||||
hello.Attach(group.Group("/helloworld"), openapi.OAuth)
|
||||
|
||||
// DSL handlers
|
||||
dsl.Attach(group.Group("/dsl"), openapi.OAuth)
|
||||
|
||||
// Custom handlers (Defined by developer)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue