Tutorial del esquema
Tutorial del esquemaLección 25: Transformando datos desde una API externa

Lección 25: Transformando datos desde una API externa

Esta lección del tutorial demuestra ejemplos sobre cómo adaptar la respuesta de una API externa a cualquier cosa que necesitemos.

Añadiendo valores por defecto y propiedades extra a cada entrada

El endpoint de la API REST newapi.getpop.org/wp-json/wp/v2/users/?_fields=id,name,url produce datos de usuario, con algunos usuarios teniendo la propiedad url vacía:

[
  {
    "id": 1,
    "name": "leo",
    "url": "https://leoloso.com"
  },
  {
    "id": 7,
    "name": "Test",
    "url": ""
  },
  {
    "id": 2,
    "name": "Theme Demos",
    "url": ""
  }
]

La consulta GraphQL de abajo transforma esta respuesta:

  • Añadiendo una URL por defecto a aquellos usuarios cuya propiedad url está vacía
  • Añadiendo una propiedad link a cada entrada de usuario (compuesta usando los valores de nombre y URL del usuario)
query {
  # Retrieve data from the external API
  usersWithLinkAndDefaultURL: _sendJSONObjectCollectionHTTPRequest(
    input: {
      url: "https://newapi.getpop.org/wp-json/wp/v2/users/?_fields=id,name,url"
    }
  )
    # Set a default URL for users without any
    @underEachArrayItem
      @underJSONObjectProperty(
        by: {
          key: "url"
        }
      )
        @default(
          value: "https://mysite.com"
          condition: IS_EMPTY
        )
 
    # Add a new "link" entry on the JSON object
    @underEachArrayItem(
      affectDirectivesUnderPos: [1, 2, 3, 4],
      passValueOnwardsAs: "userListItem"
    )
      @applyField(
        name: "_objectProperty",
        arguments: {
          object: $userListItem,
          by: {
            key: "name"
          }
        },
        passOnwardsAs: "userName"
      )
      @applyField(
        name: "_objectProperty",
        arguments: {
          object: $userListItem,
          by: {
            key: "url"
          }
        },
        passOnwardsAs: "userURL"
      )
      @applyField(
        name: "_sprintf",
        arguments: {
          string: "<a href=\"%s\">%s</a>",
          values: [$userURL, $userName]
        },
        passOnwardsAs: "userLink"
      )
      @applyField(
        name: "_objectAddEntry",
        arguments: {
          object: $userListItem,
          key: "link",
          value: $userLink
        },
        setResultInResponse: true
      )
}

La respuesta es:

{
  "data": {
    "usersWithLinkAndDefaultURL": [
      {
        "id": 1,
        "name": "leo",
        "url": "https://leoloso.com",
        "link": "<a href=\"https://leoloso.com\">leo</a>"
      },
      {
        "id": 7,
        "name": "Test",
        "url": "https://mysite.com",
        "link": "<a href=\"https://mysite.com\">Test</a>"
      },
      {
        "id": 2,
        "name": "Theme Demos",
        "url": "https://mysite.com",
        "link": "<a href=\"https://mysite.com\">Theme Demos</a>"
      }
    ]
  }
}

Las directivas componibles pueden anidar una o más directivas dentro de ellas. Al anidar más de una, indicamos esto mediante el argumento affectDirectivesUnderPos, que contiene las posiciones relativas desde esa directiva a sus directivas anidadas.

En la consulta GraphQL de arriba, la directiva @underEachArrayItem (proporcionada por la extensión Iteración y Manipulación del Valor de Campos) es una directiva componible. En su primera ocurrencia, está anidando solo una directiva, y el argumento affectDirectivesUnderPos puede omitirse:

    @underEachArrayItem
      @underJSONObjectProperty(
        # ...
      )

(Por cierto, nótese que @underJSONObjectProperty también es una directiva componible, anidando la directiva @default).

En su segunda ocurrencia, está anidando las 4 directivas a su derecha, como indica el argumento affectDirectivesUnderPos con valor [1, 2, 3, 4]:

    @underEachArrayItem(
      affectDirectivesUnderPos: [1, 2, 3, 4],
      # ...
    )
      @applyField(
        name: "_objectProperty",
        # ...
      )
      @applyField(
        name: "_objectProperty",
        # ...
      )
      @applyField(
        name: "_sprintf",
       # ...
      )
      @applyField(
        name: "_objectAddEntry",
        # ...
      )



🔥 Consejos:

La directiva @applyField (proporcionada por la extensión Campo sobre Campo) tiene dos destinos potenciales para su salida:

  • Proporcionar el argumento passOnwardsAs: "someVariableName" asignará el nuevo valor a la variable dinámica $someVariableName, desde la que puede ser leída por las siguientes directivas anidadas:
      @applyField(
        name: "_objectProperty",
        arguments: {
          object: $userListItem,
          by: {
            key: "name"
          }
        },
        passOnwardsAs: "userName"
      )
  • Proporcionar el argumento setResultInResponse: true asignará el nuevo valor de nuevo al campo (por tanto modificará la respuesta):
      @applyField(
        name: "_objectAddEntry",
        arguments: {
          object: $userListItem,
          key: "link",
          value: $userLink
        },
        setResultInResponse: true
      )

Extrayendo una propiedad específica desde los objetos JSON

El endpoint de la API REST newapi.getpop.org/wp-json/newsletter/v1/subscriptions produce una colección de datos de suscripción por email, incluyendo el email del suscriptor y el idioma:

[
  {
    "email": "abracadabra@ganga.com",
    "lang": "de"
  },
  {
    "email": "longon@caramanon.com",
    "lang": "es"
  },
  {
    "email": "rancotanto@parabara.com",
    "lang": "en"
  },
  {
    "email": "quezarapadon@quebrulacha.net",
    "lang": "fr"
  },
  {
    "email": "test@test.com",
    "lang": "de"
  },
  {
    "email": "emilanga@pedrola.com",
    "lang": "fr"
  }
]

Esta consulta GraphQL imprime solo los emails desde la respuesta de la API, extrayendo la propiedad email de cada entrada y reemplazando el valor del campo con ella:

query {
  emails: _sendJSONObjectCollectionHTTPRequest(
    input: {
      url: "https://newapi.getpop.org/wp-json/newsletter/v1/subscriptions"
    }
  )
    @underEachArrayItem(
      passValueOnwardsAs: "userEntry"
    )
      @applyField(
        name: "_objectProperty"
        arguments: {
          object: $userEntry,
          by: {
            key: "email"
          }
        }
        setResultInResponse: true
      )
}

La respuesta es:

{
  "data": {
    "emails": [
      "abracadabra@ganga.com",
      "longon@caramanon.com",
      "rancotanto@parabara.com",
      "quezarapadon@quebrulacha.net",
      "test@test.com",
      "emilanga@pedrola.com"
    ]
  }
}

Modificando condicionalmente valores de campo

Este ejemplo continúa con el anterior, además también convirtiendo el formato de los emails en la respuesta.

La consulta GraphQL de abajo extrae los emails de la respuesta de la API, y convierte a mayúsculas aquellos de usuarios cuyo idioma es inglés o alemán mediante la directiva componible @if (proporcionada por la extensión Manipulación Condicional de Campos):

query {
  # Retrieve data from a REST API endpoint
  userEntries: _sendJSONObjectCollectionHTTPRequest(
    input: {
      url: "https://newapi.getpop.org/wp-json/newsletter/v1/subscriptions"
    }
  )
    @remove
 
  emails: _echo(value: $__userEntries)
 
    # Iterate all the entries, passing every entry
    # (under the dynamic variable $userEntry)
    # to each of the next 4 directives
    @underEachArrayItem(
      passValueOnwardsAs: "userEntry"
      affectDirectivesUnderPos: [1, 2, 3, 4]
    )
 
      # Extract property "lang" from the entry
      # via the functionality field `_objectProperty`,
      # and pass it onwards as dynamic variable $userLang
      @applyField(
        name: "_objectProperty"
        arguments: {
          object: $userEntry,
          by: {
            key: "lang"
          }
        }
        passOnwardsAs: "userLang"
      )
 
      # Execute functionality field `_inArray` to find out
      # if $userLang is either "en" or "de", and place the
      # result under dynamic variable $isSpecialLang
      @applyField(
        name: "_inArray"
        arguments: {
          value: $userLang,
          array: ["en", "de"]
        }
        passOnwardsAs: "isSpecialLang"
      )
 
      # Extract property "email" from the entry
      # and set it back as the value for that entry
      @applyField(
        name: "_objectProperty"
        arguments: {
          object: $userEntry,
          by: {
            key: "email"
          }
        }
        setResultInResponse: true
      )
 
      # If $isSpecialLang is `true` then execute
      # directive `@strUpperCase` 
      @if(condition: $isSpecialLang)
        @strUpperCase
}

La respuesta es:

{
  "data": {
    "emails": [
      "ABRACADABRA@GANGA.COM",
      "longon@caramanon.com",
      "RANCOTANTO@PARABARA.COM",
      "quezarapadon@quebrulacha.net",
      "TEST@TEST.COM",
      "emilanga@pedrola.com"
    ]
  }
}

La ejecución de lógica condicional en Gato GraphQL puede hacerse dinámica: Pasando una variable dinámica a @if(condition:) (y también a @unless(condition:)) que se evaluó sobre el objeto consultado, la lógica se ejecutará o no dependiendo de condiciones de esa entidad.

De esta forma, podemos modificar dinámicamente la respuesta para algunas entidades (y no otras), según condiciones como:

  • ¿Tiene la entrada comentarios?
  • ¿Tiene el comentario respuestas?
  • ¿Es el usuario un admin?
  • ¿Está la etiqueta/categoría aplicada a alguna entrada?
  • Etc.