Schema 教程
Schema 教程第25课:转换外部 API 的数据

第25课:转换外部 API 的数据

本教程课程通过示例演示如何将外部 API 的响应适配为所需的任意格式。

为每个条目添加默认值和额外属性

REST API 端点 newapi.getpop.org/wp-json/wp/v2/users/?_fields=id,name,url 返回用户数据,其中部分用户的 url 属性为空:

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

以下 GraphQL query 对该响应进行转换:

  • url 属性为空的用户添加默认 URL
  • 为每个用户条目添加 link 属性(由用户的姓名和 URL 值组合而成)
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
      )
}

响应如下:

{
  "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>"
      }
    ]
  }
}

可组合指令可以在其中嵌套一个或多个指令。当嵌套多个指令时,需通过参数 affectDirectivesUnderPos 来指定,该参数包含从该指令到其嵌套指令的相对位置。

在上面的 GraphQL query 中,指令 @underEachArrayItem(由 Field Value Iteration and Manipulation 扩展提供)是一个可组合指令。在第一次出现时,它只嵌套了一个指令,因此可以省略参数 affectDirectivesUnderPos

    @underEachArrayItem
      @underJSONObjectProperty(
        # ...
      )

(顺便提一下,@underJSONObjectProperty 也是一个可组合指令,它嵌套了 @default 指令。)

在第二次出现时,通过将参数 affectDirectivesUnderPos 的值设为 [1, 2, 3, 4],它嵌套了右侧的 4 个指令:

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



🔥 提示:

指令 @applyField(由 Field on Field 扩展提供)有两种输出目标:

  • 指定参数 passOnwardsAs: "someVariableName" 时,新值将被赋给动态变量 $someVariableName,后续嵌套的指令可以读取该变量:
      @applyField(
        name: "_objectProperty",
        arguments: {
          object: $userListItem,
          by: {
            key: "name"
          }
        },
        passOnwardsAs: "userName"
      )
  • 指定参数 setResultInResponse: true 时,新值将被重新赋给该字段(从而修改响应):
      @applyField(
        name: "_objectAddEntry",
        arguments: {
          object: $userListItem,
          key: "link",
          value: $userLink
        },
        setResultInResponse: true
      )

从 JSON 对象中提取特定属性

REST API 端点 newapi.getpop.org/wp-json/newsletter/v1/subscriptions 返回电子邮件订阅数据的集合,包括订阅者的邮箱地址和语言:

[
  {
    "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"
  }
]

以下 GraphQL query 通过从每个条目中提取 email 属性并替换字段值,仅输出 API 响应中的邮箱地址:

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

响应如下:

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

有条件地修改字段值

本示例在上一个示例的基础上进一步演示,同时对响应中的邮箱地址格式进行转换。

以下 GraphQL query 从 API 响应中提取邮箱地址,并通过可组合指令 @if(由 Conditional Field Manipulation 扩展提供)将语言为英语或德语的用户邮箱地址转换为大写:

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
}

响应如下:

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

在 Gato GraphQL 中,条件逻辑的执行可以是动态的:通过将在被查询对象上求值的动态变量传递给 @if(condition:)(以及 @unless(condition:)),逻辑是否执行将取决于该实体的条件。

这样,我们可以基于以下条件,有选择地对部分实体动态修改响应:

  • 文章是否有评论?
  • 评论是否有回复?
  • 用户是否为管理员?
  • 标签/分类是否应用于某篇文章?
  • 等等