没有不值得去解决的问题,也没有不值得去学习的技术!

Thoughts and Practices on Migrating From REST to GraphQL: Authorizing Operations With Reference to GitHub

I ultimately decided to implement the type themeAsset(key:String!) and add the following fields to the response: deletable, renameable, and updatable

1. The left side shows a list of theme assets. Each time a user clicks an asset, its action buttons—Delete File, Rename, and Save—change according to the selected asset. See Figure 1.

The left side shows a list of theme assets. Each time a user clicks an asset, its action buttons—Delete File, Rename, and Save—change according to the selected asset
Figure 1

2. For example, for ajax-loader.gif, only the actions Delete File and Rename are allowed; the action Save is not allowed. See Figure 2.

For example, for ajax-loader.gif, only the actions Delete File and Rename are allowed; the action Save is not allowed
Figure 2

3. For example, for settings_data.json, only the action Save is allowed; the actions Delete File and Rename are not allowed. See Figure 3.

For example, for settings_data.json, only the action Save is allowed; the actions Delete File and Rename are not allowed
Figure 3

4. I checked the network request for the file page on GitHub. Viewing README.md at the request URL: https://github.com/shuijingwan/larabbs/blob/main/README.md, the response is in HTML structure, but it allows the actions Edit File and Delete File. See Figure 4.

I checked the network request for the file page on GitHub. Viewing README.md at the request URL: https://github.com/shuijingwan/larabbs/blob/main/README.md, the response is in HTML structure, but it allows the actions Edit File and Delete File
Figure 4

5. I checked the REST API for fetching a file on GitHub. Viewing README.md at the request URL: /repos/{owner}/{repo}/readme, the response is in JSON structure, but it lacks a field indicating whether an action is allowed. See Figure 5.

I checked the REST API for fetching a file on GitHub. Viewing README.md at the request URL: /repos/{owner}/{repo}/readme, the response is in JSON structure, but it lacks a field indicating whether an action is allowed
Figure 5

6. I checked the GitHub App permissions. Searching for /repos/:owner/:repo/readme, it only indicates that this endpoint is of the read type. It does not define more specific action permissions based on the specific current user and the specific file. This is a granular permission within the endpoint itself. See Figure 6.

I checked the GitHub App permissions. Searching for /repos/:owner/:repo/readme, it only indicates that this endpoint is of the read type. It does not define more specific action permissions based on the specific current user and the specific file. This is a granular permission within the endpoint itself
Figure 6

7. I checked the GraphQL API for fetching a file on GitHub. Requesting resource returns the type: UniformResourceLocatable. See Figure 7.

I checked the GraphQL API for fetching a file on GitHub. Requesting resource returns the type: UniformResourceLocatable
Figure 7

8. Looking at the type: UniformResourceLocatable, it is essentially an interface type. Looking at another interface type: Updatable, the permission control for theme asset actions should be implemented with reference to Updatable. It checks whether the current user can update this object. See Figure 8.

Looking at the type: UniformResourceLocatable, it is essentially an interface type. Looking at another interface type: Updatable, the permission control for theme asset actions should be implemented with reference to Updatable. It checks whether the current user can update this object
Figure 8

9. Referring to the Explorer usage, I plan to make the request in Altair. I opened a new window: https://api.github.com/graphql, and clicked Set Headers in the upper left corner. See Figure 9.

Referring to the Explorer usage, I plan to make the request in Altair. I opened a new window: https://api.github.com/graphql, and clicked Set Headers in the upper left corner
Figure 9

10. In the Key field, enter Authorization. In the Value field, enter Bearer <token>, where <token> is the OAuth token you generated. See Figure 10.

In the Key field, enter Authorization. In the Value field, enter Bearer <token>, where <token> is the OAuth token you generated.
Figure 10

11. Test your access with a self-query; the test passed. See Figure 11.

Test your access with a self-query; the test passed
Figure 11

Request

GraphQL

query {
  viewer {
    login
  }
}

Response

JSON

{
  "data": {
    "viewer": {
      "login": "shuijingwan"
    }
  }
}

12. Querying the resource: https://github.com/shuijingwan/larabbs/blob/main/README.md returns a null response, with no response field indicating whether the update action is allowed. See Figure 12.

Querying the resource: https://github.com/shuijingwan/larabbs/blob/main/README.md returns a null response, with no response field indicating whether the update action is allowed
Figure 12

13. Querying the project with ID 1 under a user, I fetched the single project and the project list respectively, along with viewerCanUpdate. See Figure 13.

Querying the project with ID 1 under a user, I fetched the single project and the project list respectively, along with viewerCanUpdate
Figure 13

Request

GraphQL

query{
  user(login: "shuijingwan"){
    project(number: 1){
      viewerCanUpdate
    }
    ...projects
    
  }
}
fragment projects on User{
  projects(first:1){
    edges{
      cursor
      node{
        viewerCanUpdate
      }
    }
    totalCount
  }
}

Response

JSON

{
  "data": {
    "user": {
      "project": {
        "viewerCanUpdate": true
      },
      "projects": {
        "edges": [
          {
            "cursor": "Y3Vyc29yOnYyOpHOANZ4WQ==",
            "node": {
              "viewerCanUpdate": true
            }
          }
        ],
        "totalCount": 1
      }
    }
  }
}

14. I ultimately decided to implement the type themeAsset(key:String!) and add the following fields to the response: deletable, renameable, and updatable. See Figure 14.

I ultimately decided to implement the type themeAsset(key:String!) and add the following fields to the response: deletable, renameable, and updatable
Figure 14

Request

GraphQL

query{
  onlineStoreTheme(themeId: "vogue"){
    ...themeAsset
  }
}

fragment themeAsset on OnlineStoreTheme {
  themeAsset(key: "assets/iconfont/iconfont.css") {
    id
    themeId
    version
    content
    key
    mimeType
    category
    schema
    createdAt
    updatedAt
    deletable
    renameable
    updatable
  }
}


Response

JSON

{
  "data": {
    "onlineStoreTheme": {
      "themeAsset": {
        "id": "1",
        "themeId": "vogue",
        "version": "",
        "content": "@font-face {\n  font-family: \"iconfont\"; /* Project id 2455235 */\n  src: \n       url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2');\n}\n\n.iconfont {\n  font-family: \"iconfont\" !important;\n  font-size: 16px;\n  font-style: normal;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n.icon-shouqidefuben:before {\n  content: \"\\e61f\";\n}\n\n.icon-zhankai:before {\n  content: \"\\e620\";\n}\n\n.icon-guanbiicon:before {\n  content: \"\\e605\";\n}\n\n.icon-yigou:before {\n  content: \"\\e606\";\n}\n\n.icon-bofang:before {\n  content: \"\\e940\";\n}\n\n.icon-fanyeyou:before {\n  content: \"\\e61d\";\n}\n\n.icon-you1:before {\n  content: \"\\e7b9\";\n}\n\n.icon-fanyezuo:before {\n  content: \"\\e61c\";\n}\n\n.icon-diqiu:before {\n  content: \"\\e844\";\n}\n\n.icon-youtube:before {\n  content: \"\\e612\";\n}\n\n.icon-instagram:before {\n  content: \"\\f1d3\";\n}\n\n.icon-leftarrow:before {\n  content: \"\\e691\";\n}\n\n.icon-sousuo:before {\n  content: \"\\e6b9\";\n}\n\n.icon-youjiantou1:before {\n  content: \"\\e609\";\n}\n\n.icon-sanjiaodown:before {\n  content: \"\\e6b5\";\n}\n\n.icon-huobi:before {\n  content: \"\\e608\";\n}\n\n.icon-earth:before {\n  content: \"\\e9ee\";\n}\n\n.icon-menu:before {\n  content: \"\\e633\";\n}\n\n.icon-LC_icon_search_line_2:before {\n  content: \"\\e60e\";\n}\n\n.icon-gouwuche:before {\n  content: \"\\e61b\";\n}\n\n.icon-ren:before {\n  content: \"\\e6c5\";\n}\n\n.icon-youjiantoufuben:before {\n  content: \"\\e64d\";\n}\n\n.icon-bofang1:before {\n  content: \"\\e6c6\";\n}\n\n.icon-you:before {\n  content: \"\\e671\";\n}\n\n.icon-zuo:before {\n  content: \"\\e6f7\";\n}\n\n.icon-xia:before {\n  content: \"\\e63c\";\n}\n\n.icon-youjiantou2:before {\n  content: \"\\e602\";\n}\n\n.icon-zuojiantou:before {\n  content: \"\\e603\";\n}\n\n.icon-jingdiananli_wujiaoxing_shoucanghou:before {\n  content: \"\\e64a\";\n}\n\n.icon-jingdiananli_kongwujiaoxing_shoucang:before {\n  content: \"\\e64b\";\n}\n\n.icon-icontouxiang:before {\n  content: \"\\e678\";\n}\n\n.icon-gou:before {\n  content: \"\\e63b\";\n}\n\n.icon-web__bitebiyoujiantou:before {\n  content: \"\\e62f\";\n}\n\n.icon-web__bitebizuojiantou:before {\n  content: \"\\e630\";\n}\n\n.icon-cancel-1-copy:before {\n  content: \"\\e61a\";\n}\n\n.icon-shangxia:before {\n  content: \"\\e60b\";\n}\n\n.icon-gouwucheman:before {\n  content: \"\\e604\";\n}\n\n.icon-duigou:before {\n  content: \"\\e60f\";\n}\n\n.icon-xiajiantou1:before {\n  content: \"\\e62a\";\n}\n\n.icon-_pinterest:before {\n  content: \"\\e653\";\n}\n\n.icon-icomoonfacebook:before {\n  content: \"\\e654\";\n}\n\n.icon-guge1:before {\n  content: \"\\e655\";\n}\n\n.icon-twitter:before {\n  content: \"\\e656\";\n}\n\n.icon-biaoqian:before {\n  content: \"\\e718\";\n}\n\n.icon-2:before {\n  content: \"\\e601\";\n}\n\n",
        "key": "assets/iconfont/iconfont.css",
        "mimeType": "text/plain",
        "category": "unknown",
        "schema": null,
        "createdAt": "2022-02-15 09:59:41",
        "updatedAt": "2022-02-15 09:59:41",
        "deletable": false,
        "renameable": false,
        "updatable": false
      }
    }
  }
}

关于作者 · 持续学习与技术实践

我是一名拥有 15 年以上开发经验的技术从业者,自 2013 年起持续运营个人技术博客,记录工作与个人项目中遇到的真实问题,以及解决问题过程中的探索、实践和思考。

除了技术写作,我也在持续开发和维护自己的独立项目,探索新技术和新工具在实际场景中的应用。我始终相信,没有不值得去解决的问题,也没有不值得去学习的技术。

如果这篇文章对你有所帮助,欢迎浏览博客中的其他文章,也欢迎交流技术经验与想法。

关于我  ·  GitHub  ·  邮件联系