Azure DevOps 服務 |Azure DevOps Server |Azure DevOps Server 2022
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。 此 Promise 的解析值是與所提供索引鍵相關聯的值。 -
setValue()接受字串索引鍵、值和其他選項 (例如 scope),並傳回 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 在特定集合中必須是唯一的。 由於集合是指延伸模組的特定範圍和實例,因此表示相同的文件識別碼可以在不同的集合中重複使用。
下列文件作業可供使用:
- 取得文件
- 建立文件
- 設定文件(建立或更新)
- 更新文件
- 刪除文件
也可以對集合執行單一作業:取得所有文件
依身分證件取得文件
使用集合中的識別碼取得文件非常簡單,如下列範例所示:
// 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” 集合擷取識別碼為 “MyDocumentId” 的文件。 如果沒有提供範圍,服務預設會使用範圍限定為此延伸模組的整個實例的集合。 如果此集合或具有指定識別碼的檔不存在,則會傳回 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” 擷取識別碼為 “MyDocumentId” 的文件。由於未提供範圍,因此服務使用的集合會限定為此延伸模組之整個實例的預設值。 如果此集合不存在,或具有該識別碼的檔不存在,則會傳回 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,並包含在解析 Promise 時傳回的文件中。
如果集合中已存在另一個文件,其識別碼與文件上提供的識別碼相同,則作業會失敗。 如果所需的行為是在 ID 不存在時建立新文檔,但如果存在,則修改現有文檔,則應使用該 setDocument() 方法。
設定文件 (更新或建立)
該 setDocument() 函數執行「更新插入」操作 - 如果現有文檔的 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 函式要求正在變更的文件已位於集合中。 如果未提供識別碼,或提供的識別碼不對應至集合中的任何檔,則會擲回例外狀況。
以下是如何使用 update 的範例:
// 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
假設此程式碼片段是在設定值後執行的,您應該會看到包含文字「值為 myValue」的警示訊息。getValue 方法再次是 REST API 的包裝函式,會向下列端點發出 GET 要求:
GET _apis/ExtensionManagement/InstalledExtensions/{publisherName}/{extensionName}/Data/Scopes/User/Me/Collections/%24settings/Documents/myKey
e標籤
__etag資料儲存服務會使用此欄位來進行文件並行管理。 在儲存更新之前,服務會驗證目前儲存的文件的 是否__etag符合已更新文件的 。__etag 如果它們相符,則會 __etag 遞增,並將更新的文件傳回給呼叫端。 如果它們不匹配,則表示要更新的文件已過時,並拋出異常。 延伸模組寫入器負責正常處理此例外狀況,方法是擷取最新的 __etag 檔、合併變更,然後重試更新,或通知使用者。
對於某些類型的文件,可能不需要提供的並行層級,而最後獲勝的模型可能更合適。 在這種情況下,在編輯文件時,輸入 -1 作為值來 __etag 表示此功能。 前面提到的設定服務採用此模型來儲存設定和首選項。