Almacenamiento de datos

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

Las extensiones de Azure DevOps pueden almacenar preferencias de usuario y estructuras de datos complejas directamente en la infraestructura proporcionada por Microsoft, lo que garantiza que los datos del usuario sean seguros y se realicen copias de seguridad igual que otros datos de la organización y del proyecto. También significa que para las necesidades de almacenamiento de datos simples, usted, como proveedor de extensiones, no es necesario configurar, administrar ni pagar por servicios de almacenamiento de datos de terceros.

Hay dos métodos para interactuar con el servicio de almacenamiento de datos: a través de las API REST o a través de un servicio de cliente proporcionado por Microsoft, que forma parte del SDK de VSS. Recomendamos a los desarrolladores de extensiones que usen las API de servicio de cliente proporcionadas, ya que ofrecen una encapsulación fácil de usar de las API REST.

Nota:

¿Buscas API REST de Azure DevOps? Consulte la referencia más reciente de la API rest de Azure DevOps.

Para obtener información sobre las bibliotecas cliente de .NET, consulte Bibliotecas cliente de .NET para Azure DevOps.

Lo que puede almacenar

El servicio está diseñado para permitirle almacenar y administrar dos tipos diferentes de datos:

  • Configuración: configuración de clave-valor simple (como preferencias de usuario)
  • Documentos: colecciones de objetos complejos similares (documentos)

Una colección es como un contenedor indizado para documentos. Un documento es un blob JSON que pertenece a una colección. Aparte de algunos nombres de propiedad reservados, puede controlar y administrar el esquema de estos documentos.

Cómo puede definir el ámbito de los datos

Configuración y colecciones de documentos se pueden limitar a:

  • Colección de proyectos: compartida por todos los usuarios de la colección de proyectos a la que está instalada la extensión
  • Usuario: un único usuario de una colección de proyectos en la que se instala la extensión

almacenamiento de Configuración

Los dos métodos principales para administrar la configuración son getValue() y setValue():

  • getValue() acepta una clave de cadena (junto con otras opciones como ámbito) y devuelve un IPromise. El valor resuelto de esta promesa es el valor asociado a la clave proporcionada.
  • setValue() acepta una clave de cadena, un valor y otras opciones, como el ámbito, y devuelve un IPromise. El valor resuelto de esta promesa es el valor actualizado de la configuración.

Este es un ejemplo de cómo establecer un valor:

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

Este es un ejemplo de cómo recuperar un valor de configuración:

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

Si scopeType no se especifica, la configuración se almacena en el nivel de colección de proyectos y se puede acceder a todos los usuarios de esa colección de proyectos mediante la extensión . Este es un ejemplo de cómo establecer un valor de configuración en el nivel de colección de proyectos:

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

Almacenamiento de datos (colecciones de documentos)

Para controlar datos más complejos más allá de los pares clave-valor, puede usar el concepto de documentos para ejecutar operaciones CRUD en los datos de la extensión. Un documento es un blob JSON, mejorado con dos propiedades especiales: id. y __etag. Si son cruciales para el modelo de datos de una extensión, los identificadores se pueden definir por el usuario o, si no se especifican, el sistema los genera. Estos identificadores deben ser únicos dentro de una colección específica. Dado que una colección hace referencia a un ámbito y una instancia concretos de una extensión, implica que se puede reutilizar el mismo identificador de documento en distintas colecciones.

Están disponibles las siguientes operaciones de documento:

  • Obtención de un documento
  • Creación de un documento
  • Establecer un documento (crear o actualizar)
  • Actualización de un documento
  • Eliminar un documento

También hay una sola operación que se puede realizar en una colección: Obtener todos los documentos

Obtención de un documento por identificador

Obtener un documento de una colección mediante su identificador es sencillo, como en el ejemplo siguiente:

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

Esta operación intenta capturar un documento con el identificador "MyDocumentId" de la colección "MyCollection". En ausencia de un ámbito proporcionado, el servicio usa el ámbito de la colección en toda la instancia de esta extensión. Si esta colección o un documento con el identificador especificado no existe, se devuelve un error 404, que debe controlar la extensión. El documento devuelto es un objeto JSON que incluye todas sus propiedades, junto con el identificador especial y __etag las propiedades utilizadas por el servicio de almacenamiento de datos.

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

Esta llamada intenta recuperar un documento con el identificador "MyDocumentId", de la colección "MyCollection". Puesto que no se proporciona ningún ámbito, la colección que usa el servicio se limita al valor predeterminado de toda la instancia de esta extensión. Si esta colección no existe o no existe un documento con ese identificador, se devuelve un 404, que debe controlar la extensión. El documento que se devuelve es un objeto JSON que contiene todas sus propias propiedades, además del identificador especial y __etag las propiedades utilizadas por el servicio de almacenamiento de datos.

Creación de un documento

Para crear un nuevo documento, realice una llamada como en el ejemplo siguiente:

    // 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 colección con el nombre y el ámbito proporcionados, aún no existe, se crea dinámicamente antes de crear el propio documento.

Si el documento proporcionado contiene una id propiedad , ese valor se usa como identificador único para el documento. Tenga en cuenta que el proporcionado id debe estar limitado a 50 caracteres. Si ese campo no existe, el servicio genera un GUID y se incluye en el documento que se devuelve cuando se resuelve la promesa.

Si ya existe otro documento de la colección con el mismo identificador que el proporcionado en el documento, se produce un error en la operación. Si el comportamiento deseado es crear un nuevo documento si el identificador no existe, pero modifica el documento existente si lo hace, se debe usar el setDocument() método .

Establecer un documento (actualizar o crear)

La setDocument() función lleva a cabo una operación "upsert", modifica un documento existente si su identificador está presente y coincide con un documento de la colección. Si el identificador está ausente o no corresponde a ningún documento de la colección, se agrega un nuevo documento a la colección.

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

Actualización de un documento

La updateDocument función requiere que el documento que se modifique ya reside en la colección. Se produce una excepción si no se proporciona ningún identificador o si el identificador proporcionado no corresponde a ningún documento de la colección.

Este es un ejemplo de cómo se usa la actualización:

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

Eliminar un documento

Esta función elimina el documento con el identificador proporcionado de la colección proporcionada. Si la colección no existe o el documento no existe, se devuelve un 404.

Observa el siguiente ejemplo de uso:

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

Obtener todos los documentos de una colección

En el ejemplo siguiente se recuperan todos los documentos de la colección "MyCollection" mediante el servicio de datos y, a continuación, se registra el número de documentos en la consola:

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

Esta llamada recupera todos los documentos de una colección con ámbito, con un límite de 100 000 documentos. Si la colección no existe, devuelve un error 404.

Avanzado

Cómo se almacena la configuración

Esta llamada encapsula el setDocument método de cliente, proporcionándolo con varios fragmentos de datos. Como se indicó antes, la configuración se guarda internamente como documentos. Por lo tanto, se genera dinámicamente un documento básico, donde el identificador del documento es la clave especificada en el setValue() método . El documento tiene dos propiedades más. La value propiedad contiene el valor pasado al método y la revision propiedad se establece en -1. Aunque la revision propiedad se elabora más en la sección "Trabajar con documentos", en el contexto de la configuración, establecer revision-1 en en el documento significa que no nos preocupa el control de versiones de este documento de configuración.

Dado que la configuración se almacena como documentos, es necesario proporcionar un nombre de colección, que indica dónde almacenar el documento. Para simplificar las cosas, al trabajar con los setValue()/getValue() métodos, el nombre de la colección siempre es el nombre $settingsespecial . La llamada anterior emite una solicitud PUT en el punto de conexión siguiente:

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

La carga de la solicitud es similar al ejemplo siguiente:

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

API de REST

Suponiendo que este fragmento de código se ejecuta después de establecer el valor, debería ver un mensaje de alerta que contiene el texto "El valor es myValue". El método getValue es de nuevo un contenedor alrededor de las API REST, emitiendo una solicitud GET al siguiente punto de conexión:

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

etags

El __etag campo lo usa el Servicio de almacenamiento de datos para la administración de simultaneidad de documentos. Antes de guardar una actualización, el servicio comprueba que el __etag del documento almacenado actualmente coincide con el __etag del documento actualizado. Si coinciden, __etag se incrementa y el documento actualizado se devuelve al autor de la llamada. Si no coinciden, indica que el documento que se va a actualizar está obsoleto y se produce una excepción. El escritor de extensiones es responsable de controlar esta excepción correctamente, ya sea recuperando la versión más reciente __etag del documento, combinando los cambios y reintentando la actualización, o notificando al usuario.

Para algunos tipos de documentos, es posible que el nivel de simultaneidad proporcionado no sea necesario y un modelo de último en ganar podría ser más adecuado. En tales casos, al editar el documento, escriba -1 como valor __etag para indicar esta funcionalidad. El servicio de configuración mencionado anteriormente emplea este modelo para almacenar la configuración y las preferencias.