Giter Site home page Giter Site logo

npm-module-super-alu0101228587's Introduction

Cliente de GitHub

Creación de una extensión con GraphQL

Jaime Simeón Palomar Blumenthal - [email protected]

El objetivo de esta práctica es crear una extensión para el cliente de GitHub (gh) para la consola en GraphQl.

Índice

  1. ¿Por qué GraphQL?
  2. Estructura de una operación GraphQL
  3. Cómo insertar las consultas en un ejecutable JavaScript
  4. Cómo ejecutar las consultas GraphQL en JavaScript
  5. Tests con Mocha y Chai
  6. Documentación con JSDOC
  7. Pruebas de producción

¿Por qué GraphQL?

GraphQL es un lenguaje de consulta y modificación de la información de un servicio web. Es decir, sirve para que un programa se comunique con una página web.

A diferencia de REST, con el que al hacer la consulta obteníamos una página entera en formato JSON o XML, con este lenguaje sólo le solicitaremos al servicio web los objetos que queramos, sin necesidad de tener que investigar y averiguar dónde se sitúan.

Estructura de una operación GraphQL

Una operación en GraphQL puede ser tanto de consulta como de modificación. A estas se las denominan query y mutation respectivamente. Tienen la siguiente estructura:

[query|mutation] [Nombre de la operación] (Posibles argumentos){
    Campos a extraer/modificar en la operación
}

La operación empieza indiccando si queremos hacer una query o una mutation, podemos darle un nombre y pasarle argumentos si queremos. Dentro de la consulta especificaremos con qué campos trabajaremos. A su vez estos también pueden tener argumentos y otros campos. Veamos una consulta real:

query {
  organization(login: "ULL-ESIT-DMSI-1920") {
    membersWithRole(first: 20) {
      nodes {
        name
      }
    }
  }
}

Empezamos indicando que vamos a hacer un query, luego le indicamos que lo vamos a hacer sobre el objeto organization que toma como parámetro un nombre de usuario u organización. Dentro de este consultaremos el objeto membersWithRole y como parámetro le pasamos que nos muestre los 20 primeros nodos, y de estos el nombre.

Esta consulta devuelve una lista de nombres de usuario pertenecientes a una organización dada.

Cómo insertar las consultas en un ejecutable JavaScript

En primer lugar, tendremos que generalizar las consultas, y las insertaremos en funciones flecha, y tomarán varios parámetros.

La primera consulta es la del apartado anterior pero generalizada para funcionar con cualquier organización:

const getMemberList = (owner) => `
query {
  organization(login: "${owner}") {
    membersWithRole(first: 20) {
      nodes {
        name
      }
    }
  }
}
`;

La segunda devuelve una lista de repositorios dada una organización:

const getRepoList = (owner) => `
query {
  organization(login: "${owner}") {
    repositories(first: 50) {
      nodes {
        name
      }
    }
  }
}
`;

La tercera devuelve la lista ficheros de un repositorio dado su nombre y organización:

const getRepoContent = (owner, repo) => `
query {
  repository(owner: "${owner}", name: "${repo}") {
    object(expression: "master:") {
      ... on Tree {
        entries {
          name
        }
      }
    }
  }
}
`;

Y la última nos informará sobre qué ramas tiene un repositorio dado su nombre y organización:

const getRepoBranches = (owner, repo) => `
query {
  repository(owner: "${owner}", name: "${repo}") {
    refs(first: 10, refPrefix: "refs/heads/") {
      nodes {
        name
      }
    }
  }
}
`;

Cómo ejecutar las consultas de GraphQL en JavaScript

Para ejecutar nuestras consultas haremos uso de la librería shelljs y del cliente de GitHub, gh.

En primer lugar, tenemos que copiar la clase shelljs en una constante para utilizarla en nuestro programa:

const shell = require('shelljs');

Y comprobar que gh y git están instalados:

if (!shell.which('git')) {
	shell.echo("Git not installed.");
	shell.exit(1);
}

if (!shell.which('gh')) {
	shell.echo("gh not installed.");
	shell.exit(1);
}

Ahora sí, para ejecutar las consultas escribiremos lo siguiente:

shell.exec(`gh api graphql --paginate -f query='${getRepoContent(owner, repoName)}' --jq '.data.repository.object.entries.[].name'`);

Estamos haciendo uso del comando gh api con la opción para GraphQL. Le estamos indicando que no queremos forzar la consulta (-f) y en el query estamos insertando nuestra tercera consulta, a la que le estamos pasando dos variables que previamente hemos cargado con la organización y el nombre de un repositorio.

Esta consulta a secas nos devuelve un objeto JSON que aún debe ser filtrado con JQuery (--jq).

Tests con Mocha y Chai

Antes de publicar nuestro módulo debemos hacerle ciertos tests. En este caso se los haremos únicamente a las funciones exportadas desde el fichero members.js.

Para esto utilizaremos los módulos Mocha y Chai. Los añadimos a las devDependencies, los instalamos con npm install y añadimos el script de tests:

"scripts": {
    "test": "mocha --reporter spec"
}

Ahora, tendremos que escribir los tests en sí. Según la documentación de Chai debemos crear un directorio test donde crearemos un fichero con el mismo nombre que el que exporta las funciones de nuestro proyecto (members.js en nuestro caso). Los tests unitarios tienen bloques descriptivos anidados unos dentro de otros. Si nos fijamos bien, no son más que funciones que toman como primer parámetro un string con una descripción breve, y como segundo parámetro una función que puede contener otro bloque descriptivo o un test en sí con la siguiente semántica:

funcionExportada(argumentos...).should.equal(valor)

Esta semántica permite unos tests muy legibles. Podemos leer que "La funciónExportada debería ser igual a valor". Chai permite otras comparaciones (desigualdad, por ejemplo), la negación, y valores predefinidos.

Finalmente, nuestros tests quedan de la siguiente forma:

// Lista de miebros de la organización
const memberList = "Este string es muy largo";

// Lista de repositorios de la organización
const repoList = "Este string es aún más largo";

const reposAndMembers = memberList + repoList;

describe('Gets a:', function() {
    it('\t - List of organization members', function() {
        orgMembers("ULL-ESIT-DMSI-1920", null).should.equal(memberList);
    });

    it('\t - List of organization members and repositories', function() {
        orgMembers("ULL-ESIT-DMSI-1920", "-r").should.equal(reposAndMembers);
    });
});

De esta forma, si ejecutamos los tests con npm test obtendremos la

> @alu0101228587/[email protected] test /home/usuario/Documents/gh-members-gql
> mocha --reporter spec

  Gets a:

-- Members of ULL-ESIT-DMSI-1920 --

Casiano Rodriguez-Leon
Casiano
Jaime Simeón Palomar Blumenthal
Antonella García
Sergio P
Laura Manzini

    ✔    - List of organization members (566ms)

-- Members of ULL-ESIT-DMSI-1920 --

Casiano Rodriguez-Leon
Casiano
Jaime Simeón Palomar Blumenthal
Antonella García
Sergio P
Laura Manzini

-- Repositories of ULL-ESIT-DMSI-1920 --

ull-esit-dmsi-1920.github.io
react-apollo
graphql-js
...
prueba-laura-funciona
gh-teresa-repo-rename
pruebaTeresa

    ✔    - List of organization members and repositories (1080ms)

  2 passing (2s)

Nuestros tests han tenido éxito. Sólo falta formalizar la documentación y la versión final de nuestro módulo estaría listo.

Documentación con JsDoc

JsDoc es una herramienta muy cómoda que genera documentación estandarizada en formato web a partir de nuestros comentarios de código y REDADME.md. Para utilizarla debemos añadirla a nuestras devDependencies e instalarla con npm install.

Hay que tener en cuenta que JsDoc requiere una sintaxis específica para que interprete correctamente nuestros comentarios.

/** 
 * Returns list of members. If repo != null also a list of repositories
 * @param {string} orgName - Owner
 * @param {string} repo - If != null returns repositories
*/
function getOrgMembers(orgName, repo) {
    // ...
    // Código de la función
    // ...
}

El comentario se debe de situar justo encima de las funciones que lo requieran, y debe estar encorchetado por /** Comentario */. Cada línea debe empezar por un asterisco y podemos utilizar ciertas palabras clave como @constructor o @param para indicar que la función es el constructor, o qué argumentos toma. En este caso le hemos indicado a JsDoc tras una breve descripción que la función toma dos argumentos de tipo string y una pequeña descripción de cada uno.

Tras haber hecho esto con todas las funciones de nuestro código que requieran una explicación, ejecutaremos jsdoc members.js. Esto crea un directorio out con todos loos ficheros y código CSS, HTML y JavaScript de la web con nuestra documentación.

Pruebas de producción

El objetivo de estas pruebas concretas serán que, cada vez que hagamos un push o un pull, se instalen todas las dependencias desde cero y se ejecuten los tests en un docker para comprobar que todo funciona correctamente.

En primer lugar, debemos crear nuestro fichero yaml en el directorio .github/workflows/node.js.yml, siendo node.js.yml nuestro fichero. Este se obtuvo de una plantilla para nodeJS de GitHub, y contiene lo siguiente:

name: Node.js CI

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v2
      with:
        node-version: ${{ matrix.node-version }}
        cache: 'npm'
    - run: npm ci
    - run: npm test
      env:
        GITHUB_TOKEN: ${{secrets.GH_TOKEN}}

En primer lugar, en el bloque on definimos qué acciones dispararán las instrucciones siguientes. En nuestro caso son los push y las solicitudes de pull en la rama master. Luego, en el bloque jobs definiremos las acciones en sí.

En un docker con la última versión de ubuntu (runs-on: ubuntu-latest) se ejecutan las acciones definidas en el tag V2 del repositorio con las acciones definidas por GitHub actions/checkout para que un flujo de trabajo como el nuestro pueda acceder al docker. Después se instala la versión por defecto de Node, definida en la variable matrix.node-version. Finalmente se ejecutan los comandos para instalar las dependencias y ejecutar los tests (npm ci y npm test). Además, como se accederá a nuestro repositorio desde una máquina externa, debemos proporcionarle un token de acceso de GitHub que previamente habremos definido en los secretos del repositorio. Esto lo hacemos estableciendo un valor para la variable de entorno GITHUB_TOKEN.

npm-module-super-alu0101228587's People

Contributors

jpalomarbl avatar

Watchers

Casiano Rodriguez-Leon avatar

npm-module-super-alu0101228587's Issues

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.