通过 sgcOpenAPI 获得 GitHub REST API Delphi 客户端
GitHub 维护着公开发布的最大 OpenAPI 描述之一,并以 MIT 许可证发布。sgcOpenAPI 提供的不是手写的 GitHub 组件,而是一个生成器。对 api.github.com.json 执行一条命令行,就会产出一个 Pascal 单元,包含 1,225 个方法、每个方法一个类型化的响应类,以及一个 GetOpenAPIClient 函数,直接交给您一个可用的客户端。
GitHub 维护着公开发布的最大 OpenAPI 描述之一,并以 MIT 许可证发布。sgcOpenAPI 提供的不是手写的 GitHub 组件,而是一个生成器。对 api.github.com.json 执行一条命令行,就会产出一个 Pascal 单元,包含 1,225 个方法、每个方法一个类型化的响应类,以及一个 GetOpenAPIClient 函数,直接交给您一个可用的客户端。
下面的数字来自对当前描述运行生成器并编译其结果的实测,不是估算。
github/rest-api-description 中的 descriptions/api.github.com/api.github.com.json,声明为 OpenAPI 3.0.3,无需任何转换步骤。
813 条路径变成 1,225 个方法和 1,134 个响应类,另有 3,250 个模型类,全部位于一个约 274,000 行的单元中。
使用 -a 2 生成,并在运行时设置 Authentication.Token.BearerToken。个人访问令牌和安装令牌都适用。
生成的单元在 RAD Studio 12 的 Win32 平台上干净编译通过,库路径上除了 sgcOpenAPI 的 Source 文件夹之外无需任何内容。
GitHub 为同一份描述发布了多个版本。api.github.com.json 描述托管服务,ghes-3.x.json 描述 GitHub Enterprise Server。针对哪一个,就用哪一个生成。
> sgcOpenAPI.exe -i "api.github.com.json" -o "github.pas" -a 2 File successfully created github.pas
-i 接受本地文件或 URL,JSON 和 YAML 都可以。-o 是要写出的 Pascal 单元,单元名取自该文件名。-a 2 选择令牌认证,因此生成的每个方法都会发送 Authorization: Bearer。同一个可执行文件在不带参数启动时是一个图形向导,成功时退出码为 0,输入文件有问题为 5,输出文件有问题为 6,文档无法转换为有效的 OpenAPI 3 文档时为 7。
把生成的 .pas 加入项目并写进 uses 子句。没有需要安装的组件,因为 sgcOpenAPI 不注册任何组件,也不提供设计期包。
GitHub 的操作 id 里带有斜杠和连字符,例如 repos/list-for-authenticated-user。这些字符不能出现在 Pascal 标识符中,生成器会把它们去掉,方法最终写作 reposlistforauthenticateduser。
uses github; // 刚刚生成的单元 procedure TfrmGitHub.btnReposClick(Sender: TObject); var oResponse: TsgcOpenAPI_reposlistforauthenticateduser_Response; oRepo: TsgcOpenAPI_repository_Class; begin GetOpenAPIClient.Authentication.Token.BearerToken := txtToken.Text; oResponse := GetOpenAPIClient.reposlistforauthenticateduser( 'private', 'owner', 'all', 'full_name', '', 100, 1); try if oResponse.IsSuccessful then begin for oRepo in oResponse.Successful.Items do memoLog.Lines.Add(oRepo.Full_name + ' ' + oRepo.Description); end else memoLog.Lines.Add(IntToStr(oResponse.ResponseCode) + ' ' + oResponse.ResponseError); finally oResponse.Free; end; end;
返回数组的端点,其响应的 Successful 是 TsgcOpenAPIArray 的后代,带有类型化的 Items,这里是 TArray<TsgcOpenAPI_repository_Class>。基础 URL 来自 servers 条目,因此生成的构造函数已经设好 https://api.github.com。分页没有被隐藏:aPer_page 和 aPage 就是普通参数,翻页由您自己循环完成。
如果全小写的名称让您不适应,用 -m 1 生成,方法名就改用操作摘要,或者用 -m 2 用端点命名。
路径参数按文档声明的顺序排在参数列表前面。请求体是一个字符串,原因见下文。
var oIssue: TsgcOpenAPI_issuescreate_Response; oPulls: TsgcOpenAPI_pullslist_Response; begin oIssue := GetOpenAPIClient.issuescreate('octocat', 'Hello-World', '{"title":"Memory leak in the HTTP/2 reader",' + '"body":"Repro steps: ...","labels":["bug","http2"]}'); try if oIssue.IsSuccessful then memoLog.Lines.Add('filed issue #' + IntToStr(oIssue.Successful.Number) + ' ' + oIssue.Successful.Html_url) else memoLog.Lines.Add(oIssue.Error422._message); finally oIssue.Free; end; oPulls := GetOpenAPIClient.pullslist('octocat', 'Hello-World', 'open', 'updated'); try memoLog.Lines.Add(IntToStr(oPulls.ResponseCode)); finally oPulls.Free; end; end;
每个响应类都带有 Successful,以及文档声明的每个状态码各一个属性,因此调用失败时可以读取 Error304、Error401、Error403 和 Error422。GitHub 用具名模式描述的状态会变成一个类,什么都没描述的状态则变成普通字符串。错误属性按需创建,因此它永远不为 nil,您应该检查 IsSuccessful,而不是检查对象本身。
_message 里的下划线不是笔误。message 是生成器需要转义的 68 个 Pascal 保留字之一,因此同名的模式字段会带一个前导下划线。type、object、default、index 以及名单上的其余保留字也是如此,它们都在 GitHub 的模式里出现过。
单元是这份描述的镜像。没有任何人工筛选,因此 GitHub 记录过的内容都在,GitHub 没有写的内容也就没有。
1,225 个方法,涵盖仓库和内容、议题和拉取请求、Actions 和检查运行、包、组织和团队、GitHub Apps、代码扫描以及其余接口。
每一个都继承自 TsgcOpenAPIResponse,并继承 IsSuccessful(状态码 200 到 299 时为 true),以及 ResponseCode 和 ResponseError。
TsgcOpenAPI_repository_Class、TsgcOpenAPI_issue_Class、TsgcOpenAPI_basic_error_Class、TsgcOpenAPI_validation_error_Class,以及 components 部分中的其他每一个模式。
GitHub 的标签会输出为注释,把方法在同一个客户端类里分组。它们不会变成独立的类,因此一切都挂在 GetOpenAPIClient 之下。
GitHub 自己的描述会作为 Pascal 注释出现在每个方法和属性上方,因此 IDE 会在您使用它们的地方显示出来。
ghes-3.x 描述以同样的方式生成。如果两边都要访问,就为每个目标各保留一个生成的单元。
这四点都来自对当前描述的一次真实生成运行。
大约 274,000 行、12 MB,是这些页面上我们生成过的公开规范里最大的一个。它在两秒内编译完成,但 IDE 编辑器处理这么大的文件会变慢。-x 会剔除您以 "VERB endpoint" 形式列出的操作,随后 -p 移除剩余操作都不再用到的类。
有 343 个操作声明了 application/json 请求体,但其中几乎全部都把它描述为匿名的内联对象,而不是具名模式。内联对象没有可命名的类,因此参数是 const aBody: string,JSON 由您自己拼。少数引用具名模式的操作确实会得到一个类型化的类。
大多数与没有判别符映射的组合有关,此时生成的类为每个分支保留一个成员。少数几条报告文档未能解析的 $ref,还有几条报告某个操作声明了两个成功状态,而其中只有一个被生成。生成器会指明是哪一个,而不是默默做选择。
生成的客户端就是一个忠实的 HTTP 客户端,仅此而已。它不缓存 ETag 值,不在 403 时重试,也不刷新 GitHub App 的安装令牌。请读取 ResponseCode,用 OnBeforeRequest 添加条件请求头,并用单元中已有的 apps 方法签发安装令牌。