Importar datos en la NUEVA Calculadora de Precios de Azure

¿Quieres no sólo exportar datos en la nueva calculadora de Azure, sino también importarlos? ¿Quieres guardar las estimaciones en disco? ¡Te cuento cómo!

4 min de lectura
Importar datos en la NUEVA Calculadora de Precios de Azure

Hace 6 meses hablaba de la estupenda Calculadora de Precios de Microsoft Azure y de como a pesar de ser muy funcional, tenía el inconveniente de no poder importar y exportar datos (puede exportar Excel formateados con presupuestos), por lo que tras hacer estimaciones complejas en las que inviertes algunas horas entre dibujos y elementos, obtienes un Excel que después es complicado de modificar.

Poquito después, Microsoft publicaba la nueva versión de su calculadora; con muchas mejoras:

  • Mejor organización de los recursos de Azure.
  • Posibilidad de ver los recursos agregados al detalle o bien como un listado, de forma que cuando hacemos estimaciones complejas es mucho más fácil manejarse en la herramienta.
  • La más importante de todas: posibilidad de iniciar sensión con nuestra Microsoft Account o Azure AD para poder guardar todas las estimaciones que hagamos y recuperarlas más adelante.

Con estas nuevas capacidades, la función de Importar o Exportar datos no se vuelve tan crítica... y sin embargo la sigo considerando necesaria. La razón es porque si guardamos nuestra estimación en un archivo JSON es fácil compartirla con otras personas de tu equipo de trabajo, ya sea por email, Teams, OneDrive, etc... La función de guardar la estimación en nuestra cuenta sigue pareciéndome demasiado personal.

Azure Calculator JSON Export/Import: versión original

La extensión original que había compartido en el anterior artículo estaba desarrollada por Andhika Nugraha y compartía su código públicamente en un repositorio de Github. Se me ocurrió comentarle al autor que la extensión había dejado de funcionar en la nueva calculadora con la esperanza de que pudiera actualizarla.

Me respondió que no podía hacerlo por falta de tiempo, pero que tras hacer un par de comprobaciones sobre el funcionamiento de la nueva versión, la adaptación debería ser muy trivial. Sin más información que esa la pregunta que me quedaba era ¿sería cierto?, ¡vamos a intentarlo!

Funcionamiento interno de la calculadora

El código de la extensión original era muy sencillo, extremadamente sencillo; lo cual me hacía pensar que el funcionamiento de la calculadora no debe ser excesivamente complicado. Así, entrando en la calculadora y abriendo las herramientas de desarrollador del navegador web me dispuse a echar un vistazo.

En el caso de Chrome, no me costó mucho ver donde estaba la información relevante. Haciendo una inspección al almacenamiento local de la aplicación HTML5, pude ver lo siguiente.

azcalcnew2

La clave azure_calculator_modules_v3 contiene el JSON de la estimación que actualmente tenemos en pantalla. Así que nuestra extensión no tiene más que hacer dos cosas:

  • Extraer el valor de esa clave en caso de que queramos exportar o sustituirlo por otro que especifiquemos en el caso de querer importar.
  • Encontrar algún lugar en la interfaz donde podamos ubicar los dos nuevos botones que vamos a crear para realizar estas operaciones.

Talk is cheap, show me the code

Servidor ha creado un fork del repositorio original de Andhika para poder modificar el código, que es el siguiente:

var SERVICES_KEY = 'azure_calculator_modules_v3';
var CURRENCY_KEY = 'acomuser.currency';

function createButtonElement(label, onclick) {
    var button = document.createElement('button');
    button.className = 'calculator-button';
    button.innerText = label;
    button.addEventListener('click', function(e) {
        e.preventDefault();
        onclick(e);
    });

    return button;
}

function createDivElement(headerText, descriptionText) {
    var div = document.createElement('div');
    div.className = 'row column estimate-holder';
    var header = document.createElement('h3');
    header.className = 'text-heading4';
    header.innerText = headerText;
    var p = document.createElement('p');
    p.innerText = descriptionText;

    div.appendChild(header);
    div.appendChild(p);

    return div;
}

function createDivElementForDivide() {
    var div = document.createElement('div');
    div.style = 'width: 10px; height:auto; display:inline-block;'

    return div;
}

function triggerExportJSON() {
    try {
        var servicesJson = window.localStorage.getItem(SERVICES_KEY);
        var servicesObj = JSON.parse(servicesJson);
        var currencyStr = window.localStorage.getItem(CURRENCY_KEY);
        var now = new Date();
        var exportedObj = {
            version: '0.1',
            createdAt: now.toISOString(),
            data: [
                {
                    key: SERVICES_KEY,
                    transform: 'JSON.stringify',
                    value: servicesObj
                },
                {
                    key: CURRENCY_KEY,
                    value: currencyStr
                }
            ]
        };
        var exportedJson = JSON.stringify(exportedObj, null, 2);
        var exportedBlob = new Blob([exportedJson]);
        var exportedBlobUrl = URL.createObjectURL(exportedBlob, {type:'application/json'});
        var fauxLink = document.createElement('a');
        fauxLink.href = exportedBlobUrl;
        fauxLink.setAttribute('download', 'export.json');
        document.body.appendChild(fauxLink);
        alert("Exporting estimate as JSON. Due to a bug, if you're using Edge, make sure to rename the file as *.json.");
        fauxLink.click();
    }
    catch (e) {
        console.error(e);
    }
}

function triggerImportJSON() {
    var input = document.createElement('input');
    input.type = 'file';
    input.style.display = 'none';
    input.addEventListener('change', handleFileUpload);
    document.body.appendChild(input);
    input.click();
}

function handleFileUpload(e) {
    try {
        var file = e.target.files[0];
        var reader = new FileReader();
        reader.onload = function(e) {
            var importedJson = e.target.result;
            var importedObj = JSON.parse(importedJson);
            importedObj.data.forEach(function(entry) {
                var value = entry.value;
                if (entry.transform === 'JSON.stringify') {
                    value = JSON.stringify(value);
                }
                localStorage.setItem(entry.key, value);
            });
            alert('Import successful. Reloading the page...');
            window.location.reload();
        }
        reader.readAsText(file);
    }
    catch (e) {
        alert('Importing failed: ' + e.toString());
    }
}

function init (evt) {
    var DomSelector = '#azure-calculator';
    var divImportExport = createDivElement('Import/Export estimate to JSON', 'The web\
        extension for importing and exporting estimates from Azure Pricing Calculator is enabled, this box is not part \
        of the official Microsoft Pricing Calculator tool. "Import JSON" will allow you to load previously exported \
        data into the tool, discarding the existing one. If you want to export your current estimate, use the \
        "Export JSON" button and select download location.');
    var buttonImport = createButtonElement('Import JSON', triggerImportJSON);
    var buttonExport = createButtonElement('Export JSON', triggerExportJSON);
    var dividerSpace = createDivElementForDivide();
    var targetSection = document.querySelector(DomSelector);          
    divImportExport.appendChild(buttonImport);
    divImportExport.appendChild(dividerSpace);
    divImportExport.appendChild(buttonExport);
    targetSection.insertBefore(divImportExport, targetSection.childNodes[0]);
}

init();

Los principales cambios con respecto al código original son:

  • Línea 1. La clave donde se almacenan los datos de nuestra estimación se llama ahora azure_calculator_modules_v3.
  • Líneas 4-36. Unas funciones para crear elementos en el DOM donde vamos a establecer los botones.
  • Líneas 107-122. El lugar donde se encuentra el elemento con id azure-calculator me pareció un buen lugar para ubicar los botones de importar y exportar. La calculadora cambia su layout en función de si hemos empezado a agregar elementos o hemos iniciado sesión, por lo que me pareció oportuno ubicar la interfaz en una parte relativamente alta de la página.

El resultado de la ejecución del plugin se puede apreciar en la siguiente imagen:

azcalcnew3

Así que una vez más... ¡happy estimating!

PD: Por supuesto, he dejado un pull request de vuelta en el repositorio de Andhika para que pueda incorporar los cambios.