Bitzsoft.Integrations.GoogleWorkspace 1.0.0-alpha.10

This is a prerelease version of Bitzsoft.Integrations.GoogleWorkspace.
dotnet add package Bitzsoft.Integrations.GoogleWorkspace --version 1.0.0-alpha.10
                    
NuGet\Install-Package Bitzsoft.Integrations.GoogleWorkspace -Version 1.0.0-alpha.10
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Bitzsoft.Integrations.GoogleWorkspace" Version="1.0.0-alpha.10" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Bitzsoft.Integrations.GoogleWorkspace" Version="1.0.0-alpha.10" />
                    
Directory.Packages.props
<PackageReference Include="Bitzsoft.Integrations.GoogleWorkspace" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Bitzsoft.Integrations.GoogleWorkspace --version 1.0.0-alpha.10
                    
#r "nuget: Bitzsoft.Integrations.GoogleWorkspace, 1.0.0-alpha.10"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Bitzsoft.Integrations.GoogleWorkspace@1.0.0-alpha.10
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Bitzsoft.Integrations.GoogleWorkspace&version=1.0.0-alpha.10&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Bitzsoft.Integrations.GoogleWorkspace&version=1.0.0-alpha.10&prerelease
                    
Install as a Cake Tool

Bitzsoft.Integrations.GoogleWorkspace

Google Workspace Admin SDK Directory、Gmail v1、Calendar v3 与 Drive v3 的多租户连接器,面向 ToB 组织目录、协作邮箱、企业日历和文档协作场景, 支持 .NET 5、.NET 8 和 .NET 10。

安装与前置条件

dotnet add package Bitzsoft.Integrations.GoogleWorkspace

生产宿主必须提供服务账号凭据解析器,并在 Google Workspace 管理控制台完成 Domain-wide Delegation 与最小 scope 授权。

已实现

  • OAuth 2.0 JWT Bearer 服务账号授权;
  • 强制域级委派管理员(JWT sub),缺少时不会发出 token 请求;
  • users.list / users.get;
  • groups.list / members.list;
  • 用户 suspended 状态赋值;
  • 用户、组、成员的开放字段模型与完整 Raw JSON;
  • 不透明 nextPageToken 分页与租户/profile/凭据隔离 token cache;
  • 读取与用户写入使用两套最小权限 scope;
  • Gmail profile、message list/get、完整 MIME/Raw 与 history 增量;
  • Gmail messages 全量基线、可恢复分页和 history 增量 ISyncPageSource
  • 增量删除、并发删除与过滤移出使用 tombstone,单页重复变更会折叠;
  • message 水化并发可配置且强制限制在 1–16,不限制单封响应正文大小;
  • 原始 RFC 2822 send(不做隐式重试);
  • Gmail Cloud Pub/Sub watch / stop;
  • Cloud Pub/Sub wrapped push 的 Google OIDC 验签、可信 audience / 服务账号 / subscription / mailbox 绑定;
  • Webhook Inbox 原始报文留存、可变 deliveryAttempt 语义去重和类型化 Gmail history 水位解析。
  • CalendarList list 与 events list / get;
  • Calendar event 创建:调用方提供确定性 base32hex event ID,并显式选择 sendUpdates
  • 可选 Google Meet 创建请求、参与人、周期规则、全天/定时事件和取消 tombstone;
  • Calendar 开放字段模型与完整 Raw JSON,不限制描述或响应正文大小。
  • Drive files.list / files.get、共享盘/allDrives 查询和 incompleteSearch 显式暴露;
  • Drive changes.getStartPageToken / changes.list,文件移除、共享盘变更、 独立 change token 与完整 Raw JSON;
  • Drive files 全量基线、changes 增量、可恢复分页和 tombstone ISyncPageSource
  • Drive blob 流式下载、Range / ETag 条件读取和 Google Workspace 文档导出;
  • Drive 预生成 file ID、可恢复上传会话、256 KiB 分片、服务端 Range 对账和 会话 URL 轮换;
  • Drive 文件读取与 app-created 文件写入使用独立最小权限 scope,上传分片 不做隐式重试。

请求和响应正文不会被隐式截断或限制大小。OAuth token 请求/响应属于凭据, 会逐请求显式禁止正文日志;普通 Admin SDK 业务正文仍保持完整。

凭据

宿主通过 IIntegrationCredentialResolverProductivity:google-workspace 提供:

  • OAuthJwtIssuer:服务账号 email;
  • OAuthJwtPrivateKey:PKCS#8 PEM RSA 私钥;
  • OAuthJwtKeyId:服务账号私钥 ID,可选;
  • OAuthJwtSubject:默认被模拟的 Workspace 主体;Directory 固定场景可用。

Gmail、Calendar 和 Drive 应在 GoogleWorkspaceRequestContext 传入 delegatedSubject。同一服务账号可以按调用委派不同用户,token cache 以 主体 SHA-256 指纹隔离,不在缓存键或 ToString() 中保存邮箱明文。

除创建服务账号外,Workspace 超级管理员还必须在 Admin Console 中为该 服务账号 client ID 授权实际使用的域级委派 scopes。连接器不会扩大 Admin Console 已批准的权限。

只读目录使用:

  • admin.directory.user.readonly
  • admin.directory.group.readonly
  • admin.directory.group.member.readonly

停用/恢复用户单独使用 admin.directory.user,不会让只读调用复用写 token。

注册

固定 Google 公网端点:

services.AddBitzsoftGoogleWorkspace(new GoogleWorkspaceEndpoint());

固定单邮箱同步可同时注册显式委派主体:

services.AddBitzsoftGoogleWorkspace(
    new GoogleWorkspaceEndpoint(),
    syncProfiles: null,
    gmailSyncProfiles: new[]
    {
        new GoogleWorkspaceGmailSyncProfile(
            delegatedSubject: "alice@example.com",
            credentialName: "workspace-service-account")
    });

固定单日历的 CalendarEvents 同步 profile 应显式绑定委派主体和 Calendar ID:

services.AddBitzsoftGoogleWorkspace(
    new GoogleWorkspaceEndpoint(),
    syncProfiles: null,
    gmailSyncProfiles: null,
    pubSubPushProfiles: null,
    calendarSyncProfiles: new[]
    {
        new GoogleWorkspaceCalendarSyncProfile(
            delegatedSubject: "alice@example.com",
            calendarId: "team#calendar@example.com",
            credentialName: "workspace-service-account")
    });

固定委派用户或单个共享盘的 DriveFiles 同步也应显式注册。共享盘使用 driveId;委派用户的 My Drive 则省略该参数:

services.AddBitzsoftGoogleWorkspace(
    new GoogleWorkspaceEndpoint(),
    syncProfiles: null,
    gmailSyncProfiles: null,
    pubSubPushProfiles: null,
    calendarSyncProfiles: null,
    driveSyncProfiles: new[]
    {
        new GoogleWorkspaceDriveSyncProfile(
            delegatedSubject: "alice@example.com",
            driveId: "shared-drive-id",
            credentialName: "workspace-service-account")
    });

多租户私有代理或不同 profile 可由宿主实现 IGoogleWorkspaceEndpointResolverIGoogleWorkspaceDirectorySyncProfileResolver。多邮箱同步还应实现 IGoogleWorkspaceGmailSyncProfileResolver;多日历或多 Drive 范围还应实现 IGoogleWorkspaceCalendarSyncProfileResolverIGoogleWorkspaceDriveSyncProfileResolver。通常按 SyncJobKey.SinkName 选择委派邮箱、Calendar ID 或共享盘。需要同时自定义所有 resolver 时使用六泛型参数重载。

services.AddBitzsoftGoogleWorkspace<
    TenantGoogleWorkspaceEndpointResolver,
    TenantGoogleWorkspaceDirectorySyncProfileResolver,
    TenantGoogleWorkspaceGmailSyncProfileResolver,
    TenantGoogleWorkspacePubSubPushProfileResolver,
    TenantGoogleWorkspaceCalendarSyncProfileResolver,
    TenantGoogleWorkspaceDriveSyncProfileResolver>();

如果只有 Calendar 或 Drive 需要动态路由,也可以先按标准 DI 注册对应的 profile resolver;随后调用连接器注册时使用的 TryAdd 不会覆盖宿主实现。

凭据解析器仍按每次调用的 GoogleWorkspaceRequestContext 解析,注册固定端点 不会把服务账号密钥放入 singleton。

固定端点注册会把 Drive API 与 upload API 的精确 origin 加入文件传输 allow-list。动态端点解析器或额外 CDN 必须由宿主在连接器注册前显式注册 IGoogleWorkspaceTransferUrlPolicy;后续 TryAdd 不会覆盖宿主策略:

services.AddSingleton<IGoogleWorkspaceTransferUrlPolicy>(
    new GoogleWorkspaceTransferUrlPolicy(
        additionalAllowedOrigins: new[]
        {
            new Uri("https://workspace-proxy.example.com:8443/")
        }));

官方 googleapis.comgoogleusercontent.comdrive.usercontent.google.com 默认受信;相似后缀、userinfo、fragment、 默认策略下的 IP 地址及未显式配置的非标准端口会在请求前拒绝。userinfo 与 fragment 即使配置了 origin 也不会放行。HTTP 只可在显式开发选项下用于 loopback。

Gmail Pub/Sub push

Cloud Pub/Sub subscription 应启用 authenticated push,并用专用服务账号签发 OIDC token。固定 endpoint 可把所有可信绑定一起注册:

services.AddInMemoryWebhookInfrastructure(); // 仅开发/测试
services.AddBitzsoftGoogleWorkspace(
    new GoogleWorkspaceEndpoint(),
    syncProfiles: null,
    gmailSyncProfiles: new[]
    {
        new GoogleWorkspaceGmailSyncProfile("alice@example.com")
    },
    pubSubPushProfiles: new[]
    {
        new GoogleWorkspacePubSubPushProfile(
            expectedAudience:
                "https://integrations.example.com/webhooks/google/gmail",
            expectedServiceAccountEmail:
                "pubsub-push@project.iam.gserviceaccount.com",
            expectedSubscription:
                "projects/project/subscriptions/gmail-events",
            expectedMailbox: "alice@example.com",
            endpointId: "alice-gmail")
    });

生产环境必须用数据库实现替换进程内 Inbox/replay store,并以 WebhookRoute.EndpointId 从可信路由选择 profile。多租户宿主应实现 IGoogleWorkspacePubSubPushProfileResolver,不要从请求正文反向选择 audience、服务账号、subscription 或邮箱。

HTTP 入口应保留请求原始 bytes 和全部 Header,构造 WebhookRequest 后交给 IWebhookGateway.ReceiveAsync。只有返回 AcceptedDuplicate 才应向 Pub/Sub 返回 2xx;验签依赖的 Google 证书暂时不可用、Inbox 持久化失败等异常 应返回非 2xx 让 Pub/Sub 重试。验签失败可按宿主安全策略返回 4xx。

var result = await gateway.ReceiveAsync(
    new WebhookRoute(
        tenantId: "tenant-a",
        providerKey: GoogleWorkspaceConstants.ProviderKey,
        endpointId: "alice-gmail"),
    request,
    cancellationToken);

if (result.Envelope is not null)
{
    var notification =
        GoogleGmailPushNotification.Parse(result.Envelope);
    // 以 notification.HistoryId 触发/合并 Gmail Incremental sync。
}

Pub/Sub messageId 与 subscription 形成稳定 Inbox identity; deliveryAttempt 只作为诊断扩展,不参与语义摘要。每次投递的原始正文及 PayloadSha256 均保持不变并完整留存。Gmail/Google 可能延迟或丢弃通知, 因此 push 只是加速器,仍需定期执行 history 增量同步并在 watch 到期前续订。

调用

var context = new GoogleWorkspaceRequestContext(
    tenantId: "tenant-a",
    credentialName: "workspace-service-account");

var users = await directory.ListUsersAsync(
    context,
    new GoogleWorkspaceUserQuery(maxResults: 200),
    cancellationToken: cancellationToken);

await directory.SetUserSuspendedAsync(
    context,
    "user-id-or-primary-email",
    suspended: true,
    cancellationToken);

Gmail 使用显式委派主体,并始终访问 users/me,避免 URL 用户与 JWT sub 不一致:

var mailboxContext = new GoogleWorkspaceRequestContext(
    tenantId: "tenant-a",
    credentialName: "workspace-service-account",
    delegatedSubject: "alice@example.com");

var profile = await gmail.GetProfileAsync(
    mailboxContext,
    cancellationToken);

var history = await gmail.ListHistoryAsync(
    mailboxContext,
    new GoogleGmailHistoryQuery(profile.HistoryId),
    cancellationToken: cancellationToken);

Gmail historyId 是邮箱增量水位,不是分页 token。过期 404 会返回 GoogleWorkspaceException.IsIncrementalCursorExpired = true,此时必须 重新建立全量消息基线。Gmail watch 只向 Cloud Pub/Sub 发布邮箱和 historyId 水位;通知到达后仍需调用 history API,并在到期前续订。

Calendar 也必须使用显式委派主体:

var calendars = await calendar.ListCalendarsAsync(
    mailboxContext,
    cancellationToken: cancellationToken);

var created = await calendar.CreateEventAsync(
    mailboxContext,
    calendarId: "primary",
    new GoogleCalendarEventCreateRequest(
        eventId: "4f4d9a2c6b8e0",
        start: GoogleCalendarEventDateTime.Timed(startsAt),
        end: GoogleCalendarEventDateTime.Timed(endsAt),
        sendUpdates: GoogleCalendarSendUpdates.All,
        summary: "Customer review",
        conferenceRequestId: "crm-opportunity-42"),
    cancellationToken);

Calendar 读取使用 calendar.readonly,event 创建单独使用 calendar.events。创建不会做 transport 自动重试,也不会伪造服务端不支持的 Idempotency-Key;网络结果不确定时应按调用方提供的 event ID 执行 get 对账。sendUpdates 必须由业务显式选择,避免默认值悄悄发送或抑制通知。

Drive 文件与 change feed 同样使用显式委派主体:

var files = await drive.ListFilesAsync(
    mailboxContext,
    new GoogleDriveFileQuery(
        corpus: GoogleDriveCorpus.Drive,
        driveId: "shared-drive-id",
        query: "trashed = false"),
    cancellationToken: cancellationToken);

var start = await drive.GetStartPageTokenAsync(
    mailboxContext,
    new GoogleDriveChangeQuery(driveId: "shared-drive-id"),
    cancellationToken);

files.list 默认会返回回收站项目;业务若不要回收站内容,必须像示例一样 显式提供 trashed = falseincompleteSearch=true 表示 allDrives 查询遗漏 了部分语料,调用方不得把该页当作完整基线。files 分页 token 通常只在数小时 内有效,而 change token 是长期水位;两者使用不同类型,不能互换或记录到日志。 共享盘查询会强制 supportsAllDrives=true,不一致的 corpus/driveId 参数在 网络请求前失败。

blob 下载返回拥有底层 HTTP lease 的流式响应,调用方必须释放。连接器不会 限制或截断文件正文;Range、ETag 和 abuse 确认必须显式选择:

await using var download = await drive.OpenFileContentAsync(
    mailboxContext,
    fileId: "drive-file-id",
    new GoogleDriveDownloadOptions(
        rangeStart: 0,
        rangeEnd: 1024 * 1024 - 1,
        ifNoneMatch: "\"etag\""),
    cancellationToken);

await using var source = await download.OpenBodyStreamAsync(
    cancellationToken);
await source.CopyToAsync(destination, cancellationToken);

acknowledgeAbuse 默认关闭,且 Google 只允许文件所有者或共享盘 organizer 确认。下载/导出发生重定向时,连接器不会向目标转发 OAuth Authorization 或 Cookie,并会先执行 SSRF allow-list 校验。导出接口自身不增加大小上限, 但 Google files.export 当前有 10 MB 上游限制。

可恢复上传只覆盖普通 blob,不隐式执行 Google Workspace 文档转换。先生成 file ID,再以 drive.file scope 创建会话,可以在初始化结果不确定时按 ID 对账,避免盲目创建重复文件:

var generated = await drive.GenerateFileIdsAsync(
    mailboxContext,
    cancellationToken: cancellationToken);
var upload = new GoogleDriveResumableUploadRequest(
    generated.Ids.Single(),
    name: "archive.bin",
    contentType: "application/octet-stream",
    contentLength: content.Length,
    parents: new[] { "parent-folder-id" });
var session = await drive.CreateResumableUploadSessionAsync(
    mailboxContext,
    upload,
    cancellationToken);

const int fragmentSize = 8 * 1024 * 1024; // 256 KiB 的整数倍
long offset = 0;
while (offset < session.TotalLength)
{
    content.Position = offset; // 示例使用可重放的 seekable 流
    var length = Math.Min(
        fragmentSize,
        session.TotalLength - offset);
    var progress = await drive.UploadFragmentAsync(
        mailboxContext,
        session,
        content,
        offset,
        length,
        cancellationToken);
    session = progress.Session;
    offset = progress.NextOffset;
}

上传 session URL 是约一周有效的 bearer secret。只有 ExportUploadUrl() 会显式导出它;宿主必须把 URL 加密,并将 ExportContextBinding()、内容类型、总长度和到期时间作为同一会话记录持久化, 不得进入日志或遥测。恢复时 RestoreResumableUploadSession 会校验该不可逆 绑定是否仍属于相同租户、profile、凭据名和委派主体;每次查询/上传也会复核。 非末片必须是 256 KiB 的整数倍;底层严格只读取本次声明的字节数,因此同一 seekable 大流可连续发送多个分片。 网络结果不确定时不能盲目重试,应先调用 QueryUploadStatusAsync,以服务端 确认的 NextOffset 重新定位可重放内容流;不可重放流必须由业务重新取得。

GoogleWorkspacePageToken 必须原样用于下一页,不应解析、持久化到日志或拼接 新的查询条件。Google 当前只保证 Directory 分页 token 有限期有效,过期后应 重新开始全量枚举。

写入语义

SetUserSuspendedAsync 使用 users.update 的状态赋值 PUT。重复设置相同目标 状态是幂等的,但 transport 不做自动重试;网络结果不确定时应调用 users.get 核对最终状态,再由业务操作台账决定是否补偿。

同步

Directory 没有被认证为可靠 delta feed。通用 ISyncPageSource 因而只接受 Directory 的显式 SyncMode.Full,并在最终页清除旧 delta cursor:

var request = new SyncRunRequest(
    jobKey,
    mode: SyncMode.Full,
    pageSize: 200);

var result = await engine.RunAsync(
    request,
    workspaceSource,
    sink,
    reconciler,
    cancellationToken);

必须提供与 Sink 一致的 reconciler,只有完整消费所有页后才能判断缺失记录。 Directory 不要使用 SyncMode.Automatic:首次运行虽会解析为 Full,但后续 会解析为 Incremental 并被明确拒绝。Users 最大页大小按 500、Groups 按 200 自动收敛;page token 只在当前全量运行内使用。

Gmail 的 stream 名为 GmailMessages。首次 Automatic 运行执行全量 messages.list + messages.get,最终提交独立的 history delta cursor;后续 运行调用 history.list。分页 continuation 同时保留原始基线,暂停恢复不会把 page token 误当成 delta cursor。history 404/410 会以 gmail_history_expired 失败,宿主应显式发起新的 Full 基线。

Gmail 搜索查询 q 无法由 history feed 安全重现,因此使用查询时必须设置 enableIncremental: false;最终页不会提交 delta cursor,后续 Automatic 仍会执行 Full。标签过滤可做增量同步,但连接器需要读取邮箱 history 后按当前 标签生成记录或 tombstone,API 调用量会高于无过滤同步。

Calendar 的 stream 名为 CalendarEvents。首次 Automatic 运行完整枚举 events,并且只在最终页提交 nextSyncToken;后续运行使用该 token 获取 增量。增量多页的每一个请求都会同时保留原始 committed sync token,分页 continuation 不会冒充新的 delta 水位。取消事件映射为 tombstone,410 以 calendar_sync_token_expired 失败,宿主必须显式发起新的 Full 基线。

Calendar continuation 和 delta cursor 会绑定 endpoint、凭据名、委派主体、 Calendar ID 及查询形状的不可逆摘要。同步中途修改 profile 会以 cursor_invalid 失败,不会把旧 page/sync token 用到另一个日历。完整 cursor 还会保留首次实际 maxResults;恢复或增量运行即使收到不同的 SyncRunRequest.PageSize,也不会改变 Google 要求稳定的查询形状。cursor 和 正文均不会写入 ToString();事件原始 JSON 和大字段仍不做大小截断。

默认 singleEvents: false,同步周期事件主记录与例外,避免无界展开重复实例。 需要实例级仓库时可显式启用,并在整个 cursor 生命周期保持 profile 不变。 Calendar 全量和增量都使用 showDeleted=true,确保取消实例不会丢失。

Drive 的 stream 名为 DriveFiles。Full 会先取得 changes.getStartPageToken,再用固定的 trashed = false 查询枚举 files.list;这样全量期间发生的变更可能在首次增量中重放,但不会出现 “取得全量结果后、建立增量水位前”的漏数窗口。仅支持能与 change feed 一一对应的范围:委派用户的 user corpus,或显式 driveId 的共享盘 corpus;appDataFolder 只允许委派用户范围。任意搜索 q 不进入同步 profile,因为 changes.list 无法可靠重现任意搜索谓词。

全量分页 continuation 会同时保存原始 change baseline、首次实际页大小和 files page token;最终页提交的仍是全量开始前取得的 baseline。files page token 通常只在数小时内有效,被 Google 拒绝时以 drive_file_page_token_invalid 失败,必须重新执行 Full。 incompleteSearch=true 也会以 drive_incomplete_search 失败关闭,不能把 不完整结果提交为基线。

后续 Automatic 运行使用 changes.list。文件移除、失去访问权限以及进入回收站 都会映射为 tombstone;非文件的共享盘变更被忽略,同一页对同一文件的多次变更 折叠为最后状态。增量 continuation 始终绑定原 committed change token,不会 把中间 page token 提交成新水位;只有最终页的 newStartPageToken 会提交。 Google 文档说明 change token 不会过期,但若服务端实际以 400/410 拒绝, 连接器仍会稳定返回 drive_change_token_invalid 并要求重新 Full。

Drive cursor 绑定 endpoint profile、凭据名、委派主体、共享盘 ID 和 space 的不可逆摘要。恢复时改变任何范围都会以 cursor_invalid 失败;cursor、 metadata 和 ToString() 都不会暴露委派邮箱、共享盘 ID 或原始 Google token。

边界

  • 当前覆盖 Admin SDK Directory、Gmail v1、Calendar v3、Drive v3 文件元数据 与 change-feed typed client、文件下载/导出和可恢复 blob 上传,以及 Calendar events 和 Drive files 可恢复全量/增量同步;
  • Drive 原生文档转换上传、权限/共享管理和真实商业租户契约认证仍在后续 子切片;
  • Gmail send 没有原生幂等键,连接器不做隐式重试;结果不确定时应按 RFC Message-ID 对账;
  • Directory API 没有被本包声明为增量变更源;不得把 page token 当 delta cursor;
  • 服务账号 JSON 文件不应整体进入配置或日志,只应将所需字段写入 Vault/KMS 背后的凭据解析器;
  • 尚未完成真实 Workspace 商业租户的 sandbox 契约认证,因此当前能力应按 Preview 管理。
Product Compatible and additional computed target framework versions.
.NET net5.0 is compatible.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Bitzsoft.Integrations.GoogleWorkspace:

Package Downloads
Bitzsoft.Integrations.All

Bitzsoft 第三方集成聚合包 — net5.0 包含传统连接器,net8.0+ 额外包含受上游 TFM 限制的 AI 模块

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0-alpha.10 51 7/26/2026