Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
.build/
.swiftpm/
.local/
.public-dns/
DerivedData/
*.xcuserstate
.DS_Store
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@
7. 支持列出 `listPendingUploads()` 返回的本地待恢复任务。
8. 支持调用 `resumeUpload(logicalUploadID:)`、`abortUpload(logicalUploadID:)`、`deleteLocalSnapshot(logicalUploadID:)`。

建议模拟器 smoke 命令:
建议从 package 根目录执行模拟器 smoke 命令:

```bash
cd data-sdk
export DGW_LOCAL_AUTH_ENDPOINT='http://127.0.0.1:15055'
export DGW_LOCAL_GATEWAY_ENDPOINT='http://127.0.0.1:15053'
export DGW_LOCAL_INIT_ENDPOINT='http://127.0.0.1:15057'
Expand Down
File renamed without changes.
3 changes: 3 additions & 0 deletions swift/Package.swift → Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ let package = Package(
"DGWProto",
"DGWStore",
.product(name: "GRPCCore", package: "grpc-swift-2"),
],
resources: [
.process("Resources/PublicEndpoints.json"),
]
),
.testTarget(
Expand Down
81 changes: 44 additions & 37 deletions swift/README.md → README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ SDK 提供以下能力:
| macOS 开发环境 | `>= 15` 推荐 |
| 并发模型 | Swift Concurrency,主要 API 使用 `async throws` 和 `AsyncThrowingStream` |

SDK 固定使用 Archebase 公共 HTTPS 服务域名,App 不需要也不能在 public API 中传入认证、上传网关或设备初始化端点。
SDK 使用资源文件定义 Archebase 公共服务端点,App 不需要也不能在 public API 中传入认证、上传网关或设备初始化端点。

## 3. 接入前需要准备

Expand Down Expand Up @@ -56,34 +56,34 @@ DGWStore

### 4.2 Package.swift 接入

本 repo 是多语言布局,Swift package 根目录是 `data-sdk/swift`。SwiftPM 的 Git 依赖要求 `Package.swift` 位于被依赖 repo 根目录;如果需要远端 URL 接入,请将 `swift/` 发布或镜像为独立 Swift package repo。
本 repo 根目录就是标准 SwiftPM package 根目录,`Package.swift` 位于 `data-sdk/Package.swift`。宿主 App 可以直接通过 Git URL 或本地 path 依赖本 repo。

独立 Swift package repo 远端包示例:
远端包示例:

```swift
dependencies: [
.package(url: "https://<swift-sdk-git-url>.git", from: "0.1.0")
.package(url: "https://github.com/<org>/data-sdk.git", from: "0.1.0")
]
```

本地源码示例:

```swift
dependencies: [
.package(path: "../data-sdk/swift")
.package(path: "../data-sdk")
]
```

target 依赖示例。以下 `package: "swift"` 匹配上面的本地 path 示例;如果使用独立远端 repo,请替换为 SwiftPM 解析出的 package identity
target 依赖示例。`package` 参数使用 SwiftPM package identity;Git URL 和上面的本地 path 示例通常解析为 `data-sdk`

```swift
targets: [
.target(
name: "YourAppCore",
dependencies: [
.product(name: "DataGatewayClient", package: "swift"),
.product(name: "DGWControlPlane", package: "swift"),
.product(name: "DGWStore", package: "swift")
.product(name: "DataGatewayClient", package: "data-sdk"),
.product(name: "DGWControlPlane", package: "data-sdk"),
.product(name: "DGWStore", package: "data-sdk")
]
)
]
Expand Down Expand Up @@ -158,19 +158,7 @@ print(deviceConfig.tags)
2. 本地已经存在配置文件时,抛出 `DataGatewayClientError.alreadyInitialized(configURL:)`。
3. 写入成功后返回 `ArchebaseConfig`,其中包含 `API Key` 和设备 tags。

默认构建固定使用生产公共初始化端点:

```text
https://init-device.platform.archebase.ai
```

如果编译 Swift target 时定义 `DEV`,初始化端点会切换为:

```text
https://dev-init-device.platform.archebase.ai
```

App 不需要自行配置初始化端点。
初始化端点来自 `DataGatewayClient` target 的必需资源文件 `PublicEndpoints.json` 中的 `deviceInit` 配置。App 不需要在运行时自行配置初始化端点。

### 7.2 重新初始化

Expand Down Expand Up @@ -249,27 +237,45 @@ let config = DataGatewayClientConfig.recommended(
let client = try DataGatewayClient(config: config)
```

### 8.3 固定公共服务域名
### 8.3 公共服务端点资源

SDK 内置以下公共 HTTPS 服务域名。默认构建使用生产域名;如果编译 Swift target 时定义 `DEV`,SDK 会在三个 hostname 前增加 `dev-` 前缀。URL 中不显式指定端口,HTTPS 默认端口为 `443`。
认证、上传网关和设备初始化端点全部来自 SwiftPM 资源文件:

| 服务 | 默认域名 | `-DDEV` 域名 |
|---|---|---|
| 认证 | `https://auth.platform.archebase.ai` | `https://dev-auth.platform.archebase.ai` |
| 上传网关 | `https://gateway.platform.archebase.ai` | `https://dev-gateway.platform.archebase.ai` |
| 设备初始化 | `https://init-device.platform.archebase.ai` | `https://dev-init-device.platform.archebase.ai` |
资源文件路径:

SwiftPM 命令行启用 dev 域名示例:
```text
Sources/DataGatewayClient/Resources/PublicEndpoints.json
```

```bash
cd data-sdk/swift
swift build -Xswiftc -DDEV
swift test -Xswiftc -DDEV
`Package.swift` 会精确处理这个文件;如果文件不存在,`swift build` 或 `swift test` 会在构建阶段失败。资源文件存在但格式不合法时,SDK 在解析端点时会失败。

示例:

```json
{
"auth": { "scheme": "http", "host": "nlb-example.cn-shanghai.nlb.aliyuncsslb.com", "port": 50051 },
"gateway": { "scheme": "http", "host": "nlb-example.cn-shanghai.nlb.aliyuncsslb.com", "port": 50053 },
"deviceInit": { "scheme": "http", "host": "nlb-example.cn-shanghai.nlb.aliyuncsslb.com", "port": 50057 }
}
```

Xcode 打开本地 Swift package 调试时,可在 package target 的 `Other Swift Flags` 中加入 `-DDEV`。
字段说明:

公共 DNS 集成测试脚本默认准备生产域名。如果要测试 `-DDEV` 构建,请在运行 `swift/Scripts/public_dns_path_test.sh` 或 `swift/Scripts/simulator_smoke.sh` 时同时设置 `DGW_PUBLIC_DNS_DEV=1`。
| 字段 | 说明 |
|---|---|
| `scheme` | 只允许 `http` 或 `https` |
| `host` | DNS hostname、IPv4 或 IPv6,不包含 scheme 和 port |
| `port` | `1...65535` 的整数 |

`http` 会使用 plaintext gRPC 连接,`https` 会使用 TLS gRPC 连接。认证、上传网关和设备初始化可以分别指定不同的 `scheme`、`host` 和 `port`。

SwiftPM 命令行构建示例:

```bash
cd data-sdk
swift build
swift test
```

App 只负责提供 `deviceID`、本地配置文件路径、上传持久化目录和凭证来源。

Expand Down Expand Up @@ -863,6 +869,7 @@ public enum PersistedUploadPhase: String, Codable, Sendable, Equatable {

### 18.5 Configuration Types

```swift
public struct DataGatewayClientConfig: Sendable {
public static func recommended(
credentialBase64: String,
Expand Down Expand Up @@ -948,7 +955,7 @@ public enum DataGatewayClientError: Error, Sendable, Equatable {

## 19. 上线前检查清单

1. 确认 App 网络环境可以访问 SDK 内置的 Archebase 公共 HTTPS 域名
1. 确认 `Sources/DataGatewayClient/Resources/PublicEndpoints.json` 中的认证、上传网关和设备初始化端点正确,App 网络环境可以访问这些端点
2. `archebase-config.json` 写入 App 私有目录,不进入日志、备份导出或共享容器。
3. App 支持首次初始化、已初始化跳过、重新初始化和初始化失败提示。
4. 上传 UI 支持进度、成功、失败、重试、恢复和取消。
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PACKAGE_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
REPO_ROOT="$(cd "$PACKAGE_ROOT/.." && pwd)"
TOOL_BIN="$REPO_ROOT/.local/toolchains/swift-proto/bin"
TOOL_BIN="$PACKAGE_ROOT/.local/toolchains/swift-proto/bin"

mkdir -p "$REPO_ROOT/.local/src" "$TOOL_BIN"
mkdir -p "$PACKAGE_ROOT/.local/src" "$TOOL_BIN"

swift build -c release --product protoc-gen-swift --package-path "$PACKAGE_ROOT/.build/checkouts/swift-protobuf"
swift build -c release --product protoc-gen-grpc-swift-2 --package-path "$PACKAGE_ROOT/.build/checkouts/grpc-swift-protobuf"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ PACKAGE_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
"$SCRIPT_DIR/gen_swift_proto.sh"

if ! git -C "$PACKAGE_ROOT" diff --exit-code -- Sources/DGWProto/Generated; then
echo "Swift proto generated sources are stale. Run swift/Scripts/gen_swift_proto.sh and commit the updated files." >&2
echo "Swift proto generated sources are stale. Run Scripts/gen_swift_proto.sh and commit the updated files." >&2
exit 1
fi

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,22 @@ set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PACKAGE_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
REPO_ROOT="$(cd "$PACKAGE_ROOT/.." && pwd)"
PROTO_ROOT="$REPO_ROOT/protos"
PROTO_ROOT="$PACKAGE_ROOT/protos"
OUT_DIR="$PACKAGE_ROOT/Sources/DGWProto/Generated"
TOOL_BIN="$REPO_ROOT/.local/toolchains/swift-proto/bin"
TOOL_BIN="$PACKAGE_ROOT/.local/toolchains/swift-proto/bin"

SWIFT_PLUGIN="$TOOL_BIN/protoc-gen-swift"
GRPC_PLUGIN="$TOOL_BIN/protoc-gen-grpc-swift-2"

if [[ ! -x "$SWIFT_PLUGIN" ]]; then
echo "missing executable: $SWIFT_PLUGIN" >&2
echo "run swift/Scripts/bootstrap_swift_proto_toolchain.sh first" >&2
echo "run Scripts/bootstrap_swift_proto_toolchain.sh first" >&2
exit 1
fi

if [[ ! -x "$GRPC_PLUGIN" ]]; then
echo "missing executable: $GRPC_PLUGIN" >&2
echo "run swift/Scripts/bootstrap_swift_proto_toolchain.sh first" >&2
echo "run Scripts/bootstrap_swift_proto_toolchain.sh first" >&2
exit 1
fi

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#!/usr/bin/env bash
set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
PACKAGE_DIR="${ROOT_DIR}/swift"
DEFAULT_DATA_PLATFORM_ROOT="$(cd "${ROOT_DIR}/../data-platform" 2>/dev/null && pwd || true)"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PACKAGE_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
WORKSPACE_DIR="$(cd "${PACKAGE_DIR}/.." && pwd)"
DEFAULT_DATA_PLATFORM_ROOT="$(cd "${WORKSPACE_DIR}/data-platform" 2>/dev/null && pwd || true)"
DATA_PLATFORM_ROOT="${DATA_PLATFORM_ROOT:-$DEFAULT_DATA_PLATFORM_ROOT}"
DATA_PLATFORM_PROTO_ROOT="${DATA_PLATFORM_PROTO_ROOT:-${DATA_PLATFORM_ROOT}/common/proto}"

Expand Down Expand Up @@ -35,7 +36,7 @@ CURL_MAX_TIME_SECONDS="${DGW_LOCAL_BOOTSTRAP_MAX_TIME_SECONDS:-10}"

usage() {
cat <<'EOF'
Usage: swift/Scripts/local_integration_bootstrap.sh [--start-stack] [--run-tests] [--print-env-only]
Usage: Scripts/local_integration_bootstrap.sh [--start-stack] [--run-tests] [--print-env-only]

Options:
--start-stack Build and deploy the local Rust stack before bootstrapping credentials.
Expand Down Expand Up @@ -215,6 +216,67 @@ EOF
printf '%s\n' "$token"
}

bootstrap_devices_via_grpc() {
if ! command_exists grpcurl; then
echo "grpcurl is required when HTTP device routes are unavailable" >&2
exit 1
fi

local credential_base64="$1"
local site_id="$2"
local admin_token device_body device_response device_name device_id
local unbound_body unbound_response unbound_name unbound_device_id
local suite_body suite_response suite_name add_device_body add_device_response
admin_token=$(admin_bearer_token)

device_body=$(cat <<EOF
{"displayName":$(json_string "$BOOTSTRAP_DEVICE_DISPLAY_NAME"),"description":$(json_string "$BOOTSTRAP_DEVICE_DESCRIPTION")}
EOF
)
device_response=$(grpc_call "$META_ENDPOINT" archebase.meta.v1.DeviceManagementService/RegisterDevice "$device_body" -H "Authorization: Bearer ${admin_token}")
device_name=$(read_json_field "$device_response" "name")
if [[ -z "$device_name" ]]; then
echo "failed to register device through grpc: $device_response" >&2
exit 1
fi
device_id="${device_name#devices/}"

unbound_body=$(cat <<EOF
{"displayName":$(json_string "$BOOTSTRAP_UNBOUND_DEVICE_DISPLAY_NAME"),"description":$(json_string "$BOOTSTRAP_UNBOUND_DEVICE_DESCRIPTION")}
EOF
)
unbound_response=$(grpc_call "$META_ENDPOINT" archebase.meta.v1.DeviceManagementService/RegisterDevice "$unbound_body" -H "Authorization: Bearer ${admin_token}")
unbound_name=$(read_json_field "$unbound_response" "name")
if [[ -z "$unbound_name" ]]; then
echo "failed to register unbound device through grpc: $unbound_response" >&2
exit 1
fi
unbound_device_id="${unbound_name#devices/}"

suite_body=$(cat <<EOF
{"siteId":$(json_string "$site_id"),"displayName":$(json_string "$BOOTSTRAP_SUITE_DISPLAY_NAME"),"description":$(json_string "$BOOTSTRAP_SUITE_DESCRIPTION")}
EOF
)
suite_response=$(grpc_call "$META_ENDPOINT" archebase.meta.v1.DeviceManagementService/CreateDeviceSuite "$suite_body" -H "Authorization: Bearer ${admin_token}")
suite_name=$(read_json_field "$suite_response" "name")
if [[ -z "$suite_name" ]]; then
echo "failed to create device suite through grpc: $suite_response" >&2
exit 1
fi

add_device_body=$(cat <<EOF
{"suite":$(json_string "$suite_name"),"device":$(json_string "$device_name")}
EOF
)
add_device_response=$(grpc_call "$META_ENDPOINT" archebase.meta.v1.DeviceManagementService/AddDeviceToSuite "$add_device_body" -H "Authorization: Bearer ${admin_token}")
if [[ -n "$add_device_response" && "$add_device_response" != "{}" ]]; then
echo "failed to bind device to suite through grpc: $add_device_response" >&2
exit 1
fi

emit_exports "$credential_base64" "$device_id" "$unbound_device_id"
}

bootstrap_via_grpc() {
if ! command_exists grpcurl; then
echo "grpcurl is required when HTTP admin gateway is unavailable" >&2
Expand Down Expand Up @@ -416,14 +478,19 @@ curl -fsS \
--max-time "$CURL_MAX_TIME_SECONDS" \
"${GATEWAY_HTTP_BASE%/}/healthz" >/dev/null

if ! curl -fsS -o /dev/null \
LOGIN_ROUTE_STATUS=$(curl -sS -o /dev/null -w '%{http_code}' \
--connect-timeout "$CURL_CONNECT_TIMEOUT_SECONDS" \
--max-time "$CURL_MAX_TIME_SECONDS" \
"${GATEWAY_HTTP_BASE%/}/api/dataplatform/v1/auth/login"; then
echo "HTTP admin gateway routes are unavailable at ${GATEWAY_HTTP_BASE}; falling back to grpc bootstrap" >&2
bootstrap_via_grpc
exit 0
fi
"${GATEWAY_HTTP_BASE%/}/api/dataplatform/v1/auth/login" || true)
case "$LOGIN_ROUTE_STATUS" in
200|204|400|401|403|405)
;;
*)
echo "HTTP admin gateway routes are unavailable at ${GATEWAY_HTTP_BASE}; falling back to grpc bootstrap" >&2
bootstrap_via_grpc
exit 0
;;
esac

LOGIN_BODY=$(cat <<EOF
{"organization":$(json_string "$BOOTSTRAP_ORG"),"userName":$(json_string "$BOOTSTRAP_ADMIN_USER"),"password":$(json_string "$BOOTSTRAP_ADMIN_PASSWORD")}
Expand Down Expand Up @@ -483,6 +550,12 @@ DEVICE_RESPONSE=$(http_post "${GATEWAY_HTTP_BASE%/}/api/dataplatform/v1/devices:
-b "$COOKIE_JAR")
DEVICE_NAME=$(read_json_field "$DEVICE_RESPONSE" "name")
if [[ -z "$DEVICE_NAME" ]]; then
DEVICE_ERROR_CODE=$(read_json_field "$DEVICE_RESPONSE" "code")
if [[ "$DEVICE_ERROR_CODE" == "5" ]]; then
echo "HTTP device routes are unavailable at ${GATEWAY_HTTP_BASE}; falling back to grpc device bootstrap" >&2
bootstrap_devices_via_grpc "$CREDENTIAL_BASE64" "$SITE_ID"
exit 0
fi
echo "failed to register device: $DEVICE_RESPONSE" >&2
exit 1
fi
Expand Down
Loading
Loading