Azure DevOps Services |Azure DevOps Server |Azure DevOps Server 2022 |Azure DevOps Server 2020
Azure DevOps 扩展可以直接将用户首选项和复杂的数据结构存储在Microsoft提供的基础结构上,这可确保用户的数据像其他组织和项目数据一样安全并备份。 这也意味着,对于简单的数据存储需求,你不需要作为扩展提供程序来设置、管理或支付第三方数据存储服务的费用。
可通过两种方法与数据存储服务进行交互:通过 REST API 或通过Microsoft提供的客户端服务(这是 VSS SDK 的一部分)。 我们建议扩展开发人员利用提供的客户端服务 API,因为它们提供对 REST API 的用户友好封装。
注释
正在查找 Azure DevOps REST API? 请参阅最新的 Azure DevOps REST API 参考。
有关 .NET 客户端库的信息,请参阅 适用于 Azure DevOps 的 .NET 客户端库。
可以存储的内容
该服务旨在让你存储和管理两种不同类型的数据:
- 设置:简单的键值设置(如用户首选项)
- 文档:类似复杂对象的集合(文档)
集合作为文档的索引容器。 文档是属于集合的 JSON Blob。 除了几个保留的属性名称之外,还可以控制和管理这些文档的架构。
如何确定数据的范围
设置和文档集合可以限定为以下任一范围:
- 项目集合:由安装扩展的项目集合的所有用户共享
- 用户:安装扩展的项目集合的单个用户
设置存储
管理设置getValue()的两个主要方法是:setValue()
-
getValue()接受字符串键(以及其他选项(如范围)并返回 IPromise。 此承诺的解析值是与提供的密钥关联的值。 -
setValue()接受字符串键、值和其他选项(如范围),并返回 IPromise。 此承诺的已解析值是设置的更新值。
下面是如何设置值的示例:
private async initializeState(): Promise<void> {
await SDK.ready();
const accessToken = await SDK.getAccessToken();
const extDataService = await SDK.getService<IExtensionDataService>(CommonServiceIds.ExtensionDataService);
this._dataManager = await extDataService.getExtensionDataManager(SDK.getExtensionContext().id, accessToken);
this._dataManager.getValue<string>("test-id").then((data) => {
this.setState({
dataText: data,
persistedText: data,
ready: true
});
}, () => {
this.setState({
dataText: "",
ready: true
});
});
}
下面是如何检索设置值的示例:
// Get data service
SDK.getService(SDK.getContributionId()).then(function(dataService) {
// Get value in user scope
dataService.getValue("userScopedKey", {scopeType: "User"}).then(function(value) {
console.log("User scoped key value is " + value);
});
});
如果未 scopeType 指定,则设置存储在项目集合级别,并且可使用扩展访问该项目集合中的所有用户。
下面是如何在项目集合级别设置设置值的示例:
// Get data service
SDK.getService(SDK.getContributionId()).then(function(dataService) {
// Set value (default is project collection scope)
dataService.setValue("someKey", "abcd-efgh").then(function(value) {
console.log("Key value is " + value);
});
});
数据(文档集合)存储
若要处理除键值对之外的更复杂的数据,可以利用文档的概念对扩展的数据执行 CRUD作。 文档是 JSON Blob,具有两个特殊属性:ID 和 __etag。 如果它们对扩展的数据模型至关重要,则 ID 可以是用户定义的,或者如果未指定,则系统会生成它们。 这些 ID 在特定集合中必须是唯一的。 由于集合引用了扩展的特定范围和实例,因此这意味着可以在不同的集合中重复使用相同的文档 ID。
以下文档作可用:
- 获取文档
- 创建文档
- 设置文档(创建或更新)
- 更新文档
- 删除文档
还可以对集合执行单个作:获取所有文档
按 ID 获取文档
使用文档标识符从集合中获取文档非常简单,如以下示例所示:
// Acquire data service
SDK.getService(SDK.getContributionId()).then(function(dataService) {
// Retrieve document by id
dataService.getDocument("MyCollection", "MyDocumentId").then(function(doc) {
// Assuming document has a property named foo
console.log("Doc foo: " + doc.foo);
});
});
此作尝试从“MyCollection”集合中提取 ID 为“MyDocumentId”的文档。 如果没有提供的范围,服务默认使用范围限定为此扩展的整个实例的集合。 如果此集合或具有指定 ID 的文档不存在,则返回 404 错误,扩展应处理此错误。 返回的文档是一个 JSON 对象,其中包含其所有属性,以及数据存储服务利用的特殊 ID 和 __etag 属性。
// Get data service
SDK.getService(SDK.getContributionId()).then(function(dataService) {
// Get document by id
dataService.getDocument("MyCollection", "MyDocumentId").then(function(doc) {
// Assuming document has a property named foo
console.log("Doc foo: " + doc.foo);
});
});
此调用尝试从集合“MyCollection”检索 ID 为“MyDocumentId”的文档。由于未提供作用域,因此服务使用的集合的范围限定为此扩展的整个实例的默认值。 如果此集合不存在,或者具有该 ID 的文档不存在,则返回 404,该扩展应处理该集合。 返回的文档是包含其所有属性的 JSON 对象,以及数据存储服务使用的特殊 ID 和 __etag 属性。
创建文档
若要创建新文档,请执行如下例所示的调用:
// Get data service
SDK.getService(SDK.getContributionId()).then(function(dataService) {
// Prepare document first
var newDoc = {
fullScreen: false,
screenWidth: 500
};
dataService.createDocument("MyCollection", newDoc).then(function(doc) {
// Even if no ID was passed to createDocument, one gets generated
console.log("Doc id: " + doc.id);
});
});
如果提供的名称和作用域的集合尚不存在,则会在创建文档本身之前动态创建该集合。
如果提供的文档包含属性,该值将用作文档的唯一 id ID。 请注意,提供的 id 字符应限制为 50 个字符。 如果该字段不存在,则由服务生成 GUID,并在解析承诺时返回的文档中包含该 GUID。
如果集合中的另一个文档与文档上提供的文档的 ID 相同,则作将失败。 如果所需的行为是创建新文档(如果 ID 不存在),但修改现有文档(如果存在), setDocument() 则应使用该方法。
设置文档(更新或创建)
该 setDocument() 函数执行“upsert”作 - 如果现有文档的 ID 存在并且与集合中的文档匹配,它将修改现有文档。 如果 ID 不存在或与集合中的任何文档不对应,则将新文档添加到集合中。
// Get data service
SDK.getService(SDK.getContributionId()).then(function(dataService) {
// Prepare document first
var myDoc = {
id: 1,
fullScreen: false,
screenWidth: 500
};
dataService.setDocument("MyCollection", myDoc).then(function(doc) {
console.log("Doc id: " + doc.id);
});
});
更新文档
该 updateDocument 函数要求正在更改的文档已驻留在集合中。 如果未提供 ID,或者提供的 ID 与集合中的任何文档不对应,则会引发异常。
下面是如何使用更新的示例:
// Get data service
SDK.getService(SDK.getContributionId()).then(function(dataService) {
var collection = "MyCollection";
var docId = "1234-4567-8910";
// Get document first
dataService.getDocument(collection, docId, { scopeType: "User" }).then(function(doc) {
// Update the document
doc.name = "John Doe";
dataService.updateDocument(collection, doc, { scopeType: "User" }).then(function(d) {
// Check the new version
console.log("Doc version: " + d.__etag);
});
});
});
删除文档
此函数从提供的集合中删除具有提供的 ID 的文档。 如果集合不存在或文档不存在,则返回 404。
下面是一个示例用法:
// Get data service
SDK.getService(SDK.getContributionId()).then(function(dataService) {
var docId = "1234-4567-8910";
// Delete document
dataService.deleteDocument("MyCollection", docId).then(function() {
console.log("Doc deleted");
});
});
获取集合中的所有文档
以下示例使用数据服务从“MyCollection”集合中检索所有文档,然后将文档数记录到控制台:
// Get data service
SDK.getService(SDK.getContributionId()).then(function(dataService) {
// Get all document under the collection
dataService.getDocuments("MyCollection").then(function(docs) {
console.log("There are " + docs.length + " in the collection.");
});
});
此调用检索范围集合中的所有文档,限制为 100,000 个文档。 如果集合不存在,则返回 404 错误。
高级
如何存储设置
此调用封装 setDocument 客户端方法,并为其提供多个数据片段。 如前所述,设置在内部保存为文档。 因此,将动态生成基本文档,其中文档的 ID 是方法中 setValue() 提供的密钥。 文档还有两个属性。 该 value 属性保存传递给方法的值,并且该 revision 属性设置为 -1。 虽然该revision属性在“使用文档”部分中进行了详细说明,但在设置上下文中,设置为revision-1文档表示我们不关心此设置文档的版本控制。
由于设置存储为文档,因此我们需要提供集合名称,指示文档的存储位置。 为简单起见,使用setValue()/getValue()方法时,集合名称始终是特殊名称。$settings 前面的调用在以下终结点发出 PUT 请求:
GET _apis/ExtensionManagement/InstalledExtensions/{publisherName}/{extensionName}/Data/Scopes/User/Me/Collections/%24settings/Documents
请求有效负载类似于以下示例:
{
"id": "myKey",
"__etag": -1,
"value": "myValue"
}
REST API
假设在设置值后执行此代码段,应会看到一条包含文本“Value is myValue”的警报消息。getValue 方法再次是 REST API 的包装器,向以下终结点发出 GET 请求:
GET _apis/ExtensionManagement/InstalledExtensions/{publisherName}/{extensionName}/Data/Scopes/User/Me/Collections/%24settings/Documents/myKey
etags
数据存储 __etag 服务使用该字段进行文档并发管理。 保存更新之前,服务会验证当前存储的文档是否 __etag 与更新的文档匹配 __etag 。 如果匹配,则会 __etag 递增更新的文档,并将更新的文档返回到调用方。 如果它们不匹配,则表示要更新的文档已过时,并引发异常。 扩展编写器负责正常处理此异常,方法是检索文档的最新 __etag 内容、合并更改和重试更新,或者通知用户。
对于某些类型的文档,可能不需要提供的并发级别,并且最后一个 wins 模型可能更合适。 在这种情况下,在编辑文档时,输入 -1 作为 __etag 表示此功能的值。 前面提到的设置服务使用此模型来存储设置和首选项。