Partager via


Stockage de données

Azure DevOps Services | Azure DevOps Server | Azure DevOps Server 2022

Les extensions Azure DevOps peuvent stocker les préférences utilisateur et les structures de données complexes directement sur l’infrastructure fournie par Microsoft, ce qui garantit que les données de votre utilisateur sont sécurisées et sauvegardées comme d’autres données d’organisation et de projet. Cela signifie également que pour les besoins de stockage de données simples, vous, en tant que fournisseur d’extension, ne sont pas tenus de configurer, de gérer ou de payer pour les services de stockage de données tiers.

Il existe deux méthodes permettant d’interagir avec le service de stockage de données : via des API REST ou via un service client fourni par Microsoft, qui fait partie du Kit de développement logiciel (SDK) VSS. Nous conseillons aux développeurs d’extensions d’utiliser les API de service client fournies, car ils offrent une encapsulation conviviale des API REST.

Note

Vous recherchez des API REST Azure DevOps ? Consultez la dernière référence de l’API REST Azure DevOps.

Pour plus d’informations sur les bibliothèques clientes .NET, consultez les bibliothèques clientes .NET pour Azure DevOps.

Ce que vous pouvez stocker

Le service est conçu pour vous permettre de stocker et de gérer deux types de données différents :

  • Paramètres : paramètres de clé-valeur simples (comme les préférences utilisateur)
  • Documents : collections d’objets complexes similaires (documents)

Une collection est un conteneur indexé pour les documents. Un document est un objet blob JSON qui appartient à une collection. À part quelques noms de propriétés réservées, vous contrôlez et gérez le schéma de ces documents.

Comment étendre les données

Les paramètres et les collections de documents peuvent être limités à l’une des options suivantes :

  • Collection de projets : partagée par tous les utilisateurs de la collection de projets sur laquelle l’extension est installée
  • Utilisateur : un seul utilisateur d’une collection de projets sur laquelle l’extension est installée

Stockage des paramètres

Les deux méthodes principales de gestion des paramètres sont getValue() les setValue()suivantes :

  • getValue() accepte une clé de chaîne (ainsi que d’autres options telles que l’étendue) et retourne un IPromise. La valeur résolue de cette promesse est la valeur associée à la clé fournie.
  • setValue() accepte une clé de chaîne, une valeur et d’autres options telles que l’étendue et retourne un IPromise. La valeur résolue de cette promesse est la valeur mise à jour du paramètre.

Voici un exemple de définition d’une valeur :

        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
            });
        });
    }

Voici un exemple de récupération d’une valeur de paramètre :

    // 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);
        });
    });

S’il scopeType n’est pas spécifié, les paramètres sont stockés au niveau de la collection de projets et sont accessibles à tous les utilisateurs de cette collection de projets à l’aide de l’extension. Voici un exemple de définition d’une valeur de paramètre au niveau de la collection de projets :

    // 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);
        });
    });

Stockage de données (collections de documents)

Pour gérer des données plus complexes au-delà des paires clé-valeur, vous pouvez utiliser le concept de documents pour exécuter des opérations CRUD sur les données de votre extension. Un document est un objet blob JSON, amélioré avec deux propriétés spéciales : ID et __etag. S’ils sont essentiels au modèle de données d’une extension, les ID peuvent être définis par l’utilisateur ou, s’ils ne sont pas spécifiés, le système les génère. Ces ID doivent être uniques dans une collection spécifique. Étant donné qu’une collection fait référence à une étendue et une instance particulières d’une extension, elle implique que le même ID de document peut être réutilisé dans différentes collections.

Les opérations de document suivantes sont disponibles :

  • Obtenir un document
  • Créer un document
  • Définir un document (créer ou mettre à jour)
  • Mettre à jour un document
  • Supprimer un document

Il existe également une seule opération qui peut être effectuée sur une collection : obtenir tous les documents

Obtenir un document par ID

L’obtention d’un document à partir d’une collection à l’aide de son identificateur est simple, comme dans l’exemple suivant :

    // 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);
        });
    });

Cette opération tente d’extraire un document avec l’ID « MyDocumentId » de la collection « MyCollection ». En l’absence d’une étendue fournie, le service utilise par défaut la collection étendue à l’instance entière de cette extension. Si cette collection ou un document avec l’ID spécifié n’existe pas, une erreur 404 est retournée, que l’extension doit gérer. Le document retourné est un objet JSON qui inclut toutes ses propriétés, ainsi que l’ID spécial et __etag les propriétés utilisées par le service de stockage de données.

    // 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);
        });
    });

Cet appel tente de récupérer un document avec l’ID « MyDocumentId », à partir de la collection « MyCollection ». Étant donné qu’aucune étendue n’est fournie, la collection utilisée par le service est étendue à la valeur par défaut de l’instance entière de cette extension. Si cette collection n’existe pas ou qu’un document avec cet ID n’existe pas, un 404 est retourné, que l’extension doit gérer. Le document retourné est un objet JSON contenant toutes ses propres propriétés, en plus de l’ID spécial et __etag des propriétés utilisées par le service de stockage de données.

Créer un document

Pour créer un document, effectuez un appel comme dans l’exemple suivant :

    // 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);
        });
    });

Si la collection portant le nom et l’étendue fournis n’existe pas encore, elle est créée dynamiquement avant la création du document lui-même.

Si le document fourni contient une id propriété, cette valeur est utilisée comme ID unique pour le document. Notez que les caractères fournis id doivent être limités à 50 caractères. Si ce champ n’existe pas, un GUID est généré par le service et inclus dans le document retourné lorsque la promesse est résolue.

Si un autre document de la collection existe déjà avec le même ID que celui fourni sur le document, l’opération échoue. Si le comportement souhaité est créé un document si l’ID n’existe pas, mais modifiez le document existant le cas échéant, la setDocument() méthode doit être utilisée.

Définir un document (mettre à jour ou créer)

La setDocument() fonction effectue une opération « upsert » : elle modifie un document existant si son ID est présent et correspond à un document de la collection. Si l’ID est absent ou ne correspond à aucun document de la collection, un nouveau document est ajouté à la collection.

    // 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);
        });
    });

Mettre à jour un document

La updateDocument fonction exige que le document en cours de modification réside déjà dans la collection. Une exception est levée si aucun ID n’est fourni ou si l’ID fourni ne correspond à aucun document de la collection.

Voici un exemple de la façon dont la mise à jour est utilisée :

    // 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);
            });
        });
    });

Supprimer un document

Cette fonction supprime le document avec l’ID fourni de la collection fournie. Si la collection n’existe pas ou si le document n’existe pas, un 404 est retourné.

Voici un exemple d’utilisation :

    // 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");
        });
    });

Obtenir tous les documents d’une collection

L’exemple suivant récupère tous les documents de la collection « MyCollection » à l’aide du service de données, puis enregistre le nombre de documents dans la console :

    // 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.");
        });
    });

Cet appel récupère tous les documents d’une collection délimitée, avec une limite de 100 000 documents. Si la collection n’existe pas, elle renvoie une erreur 404.

Advanced

Comment les paramètres sont stockés

Cet appel encapsule la setDocument méthode cliente, en lui fournissant plusieurs éléments de données. Comme indiqué précédemment, les paramètres sont enregistrés en interne en tant que documents. Par conséquent, un document de base est généré dynamiquement, où l’ID du document est la clé donnée dans la setValue() méthode. Le document a deux propriétés supplémentaires. La value propriété contient la valeur passée à la méthode, et la revision propriété est définie sur -1. Bien que la propriété soit développée plus loin dans la section « Utilisation des documents », dans le contexte des paramètres, la revision définition revision-1 dans le document signifie que nous ne sommes pas préoccupés par le contrôle de version de ce document de paramètres.

Étant donné que les paramètres sont stockés en tant que documents, nous devons fournir un nom de collection, indiquant où stocker le document. Pour simplifier les choses, lorsque vous travaillez avec les setValue()/getValue() méthodes, le nom de la collection est toujours le nom $settingsspécial. L’appel précédent émet une demande PUT au point de terminaison suivant :

GET _apis/ExtensionManagement/InstalledExtensions/{publisherName}/{extensionName}/Data/Scopes/User/Me/Collections/%24settings/Documents

La charge utile de la requête est semblable à l’exemple suivant :

{
                "id": "myKey",
                "__etag": -1,
                "value": "myValue"
}

API REST

En supposant que cet extrait de code est exécuté une fois la valeur définie, vous devez voir un message d’alerte contenant le texte « La valeur est myValue ». La méthode getValue est à nouveau un wrapper autour des API REST, en émettant une requête GET au point de terminaison suivant :

GET _apis/ExtensionManagement/InstalledExtensions/{publisherName}/{extensionName}/Data/Scopes/User/Me/Collections/%24settings/Documents/myKey

etags

Le __etag champ est utilisé par le service de stockage de données pour la gestion de la concurrence des documents. Avant l’enregistrement d’une mise à jour, le service vérifie que le __etag document actuellement stocké correspond au __etag document mis à jour. S’ils correspondent, le __etag document est incrémenté et le document mis à jour est retourné à l’appelant. S’ils ne correspondent pas, cela indique que le document à mettre à jour est obsolète et qu’une exception est levée. L’enregistreur d’extensions est chargé de gérer cette exception correctement, soit en récupérant la dernière version __etag du document, en fusionnant les modifications et en retenant la mise à jour, soit en informant l’utilisateur.

Pour certains types de documents, le niveau d’accès concurrentiel fourni peut ne pas être nécessaire, et un modèle dernier-in-wins peut être plus approprié. Dans ce cas, lors de la modification du document, l’entrée -1 comme __etag valeur pour signer cette fonctionnalité. Le service de paramètres mentionné précédemment utilise ce modèle pour stocker les paramètres et les préférences.