Проверка подлинности с помощью GraphQL
Вы можете пройти проверку подлинности в API GraphQL с помощью personal access token, GitHub Appили OAuth app.
Проверка подлинности с помощью personal access token
Чтобы выполнить проверку подлинности с помощью personal access token, выполните действия, описанные в разделеУправление личными маркерами доступа". Запрашиваемые данные определяют, какие области или разрешения потребуются.
Например, выберите разрешение "issues:read", чтобы прочитать все проблемы в репозиториях, к которым имеется доступ маркера.
Все данные fine-grained personal access tokens включают доступ на чтение к общедоступным репозиториям. Чтобы получить доступ к общедоступным репозиториям с помощью personal access token (classic), выберите область "public_repo".
Если у маркера нет необходимых областей или разрешений для доступа к ресурсу, API вернет сообщение об ошибке, которое указывает области или разрешения, необходимые маркеру.
Проверка подлинности с помощью GitHub App
Если вы хотите использовать API от имени организации или другого пользователя, GitHub рекомендует использовать GitHub App. Чтобы атрибутировать действие для приложения, можно выполнить проверку подлинности приложения в качестве установки приложения. Чтобы атрибутировать действие приложения пользователю, можно выполнить проверку подлинности приложения от имени пользователя. В обоих случаях вы создайте маркер, который можно использовать для проверки подлинности в API GraphQL. Дополнительные сведения см. в разделе "[AUTOTITLE" и "Регистрация приложения GitHub](/apps/creating-github-apps/authenticating-with-a-github-app/about-authentication-with-a-github-app)".
Проверка подлинности с помощью OAuth app
Чтобы выполнить проверку подлинности с помощью маркера OAuth из OAuth app, необходимо сначала авторизовать OAuth app с помощью потока веб-приложения или потока устройства. Затем можно использовать маркер доступа, полученный для доступа к API. Дополнительные сведения см. в разделе "[AUTOTITLE" и "Создание приложения OAuth](/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps)".
Конечная точка GraphQL
REST API имеет множество конечных точек. С ПОМОЩЬЮ API GraphQL конечная точка остается постоянной, независимо от того, какая операция выполняется. Для GitHub.com, эта конечная точка:
https://api.github.com/graphql
Если вы обращаетесь к GitHub в другом домене, например octocorp.ghe.com
, конечная точка будет отражать этот домен. Например: https://api.octocorp.ghe.com/graphql
.
Взаимодействие с GraphQL
Так как операции GraphQL состоят из нескольких строк кода JSON, GitHub рекомендует использовать Explorer для выполнения вызовов GraphQL. Вы также можете использовать curl
или любую другую библиотеку HTTP-речи.
В REST HTTP-команды определяют выполняемую операцию. В GraphQL вы предоставите текст в кодировке JSON независимо от того, выполняете ли вы запрос или изменение, поэтому HTTP-команда — POST
. Исключением является запрос интроспекции, который представляет собой простой запрос GET
к конечной точке. Дополнительные сведения о GraphQL и REST см. в разделе "Миграция из REST в GraphQL".
Чтобы запросить GraphQL в команде curl
, выполните POST
запрос с полезными данными JSON. Полезные данные должны содержать строку query
:
curl -H "Authorization: bearer TOKEN" -X POST -d " \
{ \
\"query\": \"query { viewer { login }}\" \
} \
" https://api.github.com/graphql
Note
Строковое значение "query"
должно экранировать символы новой строки или схема не будет правильно анализировать ее. Для текста запроса POST
используйте внешние двойные кавычки и экранированные внутренние двойные кавычки.
Операции запроса и изменения
В API GraphQL на GitHub разрешены два типа операций: запросы и изменения. Если сравнивать GraphQL с REST, запросы работают как запросы GET
, а изменения — как POST
/PATCH
/DELETE
. Имя изменения определяет производимую модификацию.
Сведения об ограничении скорости см. в разделе "Ограничения скорости и ограничения узлов для API GraphQL".
Запросы и изменения похожи по форме за несколькими важными различиями.
Сведения о запросах
Запросы GraphQL возвращают только указанные данные. Для формирования запроса необходимо указать поля внутри полей (также известные как вложенные подполя), пока не будут возвращены только скалярные значения.
Запросы имеют следующую структуру:
query { JSON-OBJECT-TO-RETURN }
Реальный пример см. в разделе Пример запроса.
Сведения об изменениях
Чтобы сформировать изменение, необходимо указать три элемента.
- Имя изменения — тип нужного изменения.
- Входной объект — данные, которые необходимо отправить на сервер; состоят из полей входных данных. Передайте его в качестве аргумента имени изменения.
- Объект полезных данных — данные, которые должны быть получены с сервера; состоят из возвращаемых полей. Передайте его в качестве тела имени мутации.
Изменения имеют следующую структуру:
mutation { MUTATION-NAME(input: {MUTATION-NAME-INPUT!}) { MUTATION-NAME-PAYLOAD } }
Входной объект в этом примере — MutationNameInput
, а объект полезных данных — MutationNamePayload
.
В справочнике по изменениям перечислены поля входных данных, передаваемые в качестве входного объекта. Кроме того, перечислены возвращаемые поля, передаваемые в качестве объекта полезных данных.
Реальный пример см. в разделе Пример изменения.
Работа с переменными
Переменные могут сделать запросы более динамичными и эффективными, снизив сложность при передаче входных объектов изменений.
Note
Если вы используете обозреватель, обязательно введите переменные в отдельной области переменных запроса и не включайте слово variables
перед объектом JSON.
Ниже приведен пример запроса с одной переменной.
query($number_of_repos:Int!) {
viewer {
name
repositories(last: $number_of_repos) {
nodes {
name
}
}
}
}
variables {
"number_of_repos": 3
}
Переменные используются в три этапа.
-
Определите переменную вне операции в объекте
variables
:variables { "number_of_repos": 3 }
Объект должен быть допустимым кодом JSON. В этом примере показана переменная простого типа
Int
, но можно определять переменные и более сложных типов, такие как входные объекты. Здесь также можно определить несколько переменных. -
Передайте переменную в операцию в качестве аргумента:
query($number_of_repos:Int!){
Аргумент представляет собой пару "ключ-значение", где ключ — это имя, начинающееся с
$
(например,$number_of_repos
), а значение — это тип (например,Int
). Чтобы указать, что тип является обязательным, добавьте!
. Если вы определили несколько переменных, включите их здесь как несколько аргументов. -
Используйте переменную в операции:
repositories(last: $number_of_repos) {
В этом примере переменная означает количество получаемых репозиториев. Тип указывается на шаге 2, так как в GraphQL действует строгая типизация.
Этот процесс делает аргумент запроса динамическим. Теперь мы можем просто изменить значение в объекте variables
, оставив остальную часть запроса без изменений.
Использование переменных в качестве аргументов позволяет динамически обновлять значения в объекте variables
, не меняя запрос.
Пример запроса
Давайте рассмотрим более сложный запрос в контексте.
Следующий запрос обращается к репозиторию octocat/Hello-World
, находит 20 последних закрытых проблем и возвращает заголовок, URL-адрес и первые пять меток каждой проблемы:
query {
repository(owner:"octocat", name:"Hello-World") {
issues(last:20, states:CLOSED) {
edges {
node {
title
url
labels(first:5) {
edges {
node {
name
}
}
}
}
}
}
}
}
Рассмотрим его структуру построчно.
-
query {
Так как мы хотим считать данные с сервера, а не изменить их,
query
является корневой операцией. (Если операция не указана,query
также выполняется по умолчанию.) -
repository(owner:"octocat", name:"Hello-World") {
Сначала нужно найти объект
repository
. Проверка схемы показывает, что для этого объекта требуются аргументыowner
иname
. -
issues(last:20, states:CLOSED) {
Чтобы получить сведения о всех проблемах в репозитории, мы вызываем объект
issues
. (Мы могли бы запросить одну проблемуissue
из объектаrepository
, но для этого нужно знать количество возвращаемых проблем и предоставить его в качестве аргумента.)Некоторые сведения об объекте
issues
:- В документации говорится, что этот объект имеет тип
IssueConnection
. - Проверка схемы показывает, что в качестве аргумента для этого объекта требуется количество последних (
last
) или первых (first
) результатов, поэтому мы указываем20
. - В документации также говорится, что этот объект принимает аргумент
states
, который содержит одно из значений перечисленияIssueState
:OPEN
илиCLOSED
. Чтобы найти только закрытые проблемы, мы присваиваем ключуstates
значениеCLOSED
.
- В документации говорится, что этот объект имеет тип
-
edges {
Мы знаем, что объект
issues
— это соединение, так как он имеет типIssueConnection
. Чтобы получить сведения об отдельных проблемах, необходимо получить доступ к узлу черезedges
. -
node {
В данном случае мы получаем узлы в конце ребра. В документации по
IssueConnection
указано, что узел в конце типаIssueConnection
является объектомIssue
. -
Теперь, когда мы знаем, что извлекаем объект
Issue
, мы можем обратиться к документации и указать поля, которые нужно вернуть:title url labels(first:5) { edges { node { name } } }
В данном случае мы указываем поля
title
,url
иlabels
объектаIssue
.Тип поля
labels
—LabelConnection
. Как и в случае с объектомissues
, так какlabels
— это соединение, необходимо пройти по его ребрам к подключенному узлу: объектуlabel
. В этом узле можно указать поля объектаlabel
, которые нужно вернуть, в данном случаеname
.
Вы можете заметить, что выполнение этого запроса в общедоступном Hello-World
репозитории Octocat не возвращает много меток. Попробуйте выполнить его в одном из собственных репозиториев, использующих метки, и вы, скорее всего, заметите разницу.
Пример изменения
Для изменений часто требуются сведения, которые можно узнать только путем предварительного выполнения запроса. В этом примере показаны две операции:
- запрос для получения идентификатора проблемы;
- изменение для добавления реакции эмодзи на проблему.
query FindIssueID {
repository(owner:"octocat", name:"Hello-World") {
issue(number:349) {
id
}
}
}
mutation AddReactionToIssue {
addReaction(input:{subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY}) {
reaction {
content
}
subject {
id
}
}
}
Хотя запрос и изменение можно добавить в одном окне Explorer, если присвоить им имена (в данном примере FindIssueID
и AddReactionToIssue
), операции будут выполняться как отдельные вызовы конечной точки GraphQL. Запрос невозможно выполнить одновременно с изменением, и наоборот.
Разберем этот пример. Задача звучит просто: добавить реакцию эмодзи на проблему.
С какого же запроса нам начать? Пока мы не знаем.
Так как мы хотим изменить данные на сервере (добавить эмодзи к проблеме), мы начинаем с поиска подходящего изменения в схеме. В справочной документации есть изменение addReaction
с таким описанием: Adds a reaction to a subject.
Отлично!
В документации по изменению перечислены три поля входных данных:
clientMutationId
(String
)subjectId
(ID!
)content
(ReactionContent!
)
Символы !
указывают, что subjectId
и content
являются обязательными полями. То, что поле content
обязательное, разумно: мы хотим добавить реакцию, поэтому нам нужно указать, какой эмодзи следует использовать.
Но почему обязательным является поле subjectId
? Это связано с тем, что subjectId
— это единственный способ указать, на какую проблему в каком репозитории следует отреагировать.
Вот почему мы начинаем этот пример с запроса: чтобы получить ID
.
Давайте разберем запрос построчно.
-
query FindIssueID {
Здесь мы выполняем запрос и называем его
FindIssueID
. Обратите внимание, что именование запроса является необязательным. В данном случае мы присваиваем ему имя, чтобы можно было включить его в то же окно Explorer, что и изменение. -
repository(owner:"octocat", name:"Hello-World") {
Мы указываем репозиторий, запрашивая объект
repository
и передавая аргументыowner
иname
. -
issue(number:349) {
Мы указываем проблему, на которую нужно отреагировать, запрашивая объект
issue
и передавая аргументnumber
. -
id
Здесь мы получаем
id
https://github.com/octocat/Hello-World/issues/349
для передачи вsubjectId
.
При выполнении запроса мы получаем id
MDU6SXNzdWUyMzEzOTE1NTE=
.
Note
Возвращаемое id
в запросе значение, которое мы передадим в виде subjectID
изменения. Ни в документации, ни в схеме эта связь не определена. Необходимо просто понимать, как работают имена.
Зная идентификатор, мы можем перейти к изменению:
-
mutation AddReactionToIssue {
Здесь мы выполняем изменение и называем его
AddReactionToIssue
. Как и в случае с запросами, именование изменения является необязательным. В данном случае мы присваиваем ему имя, чтобы можно было включить его в то же окно Explorer, что и запрос. -
addReaction(input:{subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY}) {
Разберем эту строку:
addReaction
— это имя изменения.input
— это обязательный ключ аргумента. Для изменения это всегдаinput
.{subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY}
— это обязательное значение аргумента. Для изменения это всегда будет входной объект (отсюда фигурные скобки), состоящий из полей входных данных (в данном случаеsubjectId
иcontent
).
Как узнать, какое значение следует использовать для содержимого? В документации по
addReaction
говорится, что полеcontent
имеет типReactionContent
, который является перечислением, так как для проблем GitHub поддерживаются только некоторые реакции эмодзи. Ниже перечислены допустимые значения для реакций (обратите внимание, что некоторые значения отличаются от соответствующих имен эмодзи).Содержимое Эмодзи +1
👍 -1
👎 laugh
😄 confused
😕 heart
❤️ hooray
🎉 rocket
🚀 eyes
👀 -
Остальная часть вызова — это объект полезных данных. Здесь мы указываем данные, которые сервер должен вернуть после изменения. В документации по
addReaction
определены три возможных возвращаемых поля:clientMutationId
(String
)reaction
(Reaction!
)subject
(Reactable!
)
В этом примере возвращаются два обязательных поля (
reaction
иsubject
); оба они имеют обязательные вложенные поля (content
иid
соответственно).
При выполнении изменения ответ будет следующим:
{
"data": {
"addReaction": {
"reaction": {
"content": "HOORAY"
},
"subject": {
"id": "MDU6SXNzdWUyMTc5NTQ0OTc="
}
}
}
}
Вот и все! Проверьте реакцию на проблему, наведя указатель мыши на 🎉 и посмотрев имя пользователя.
Последнее замечание: при передаче нескольких полей во входном объекте синтаксис может получиться громоздким. Помочь может помещение полей в переменную. Вот как можно переписать исходное изменение с использованием переменной:
mutation($myVar:AddReactionInput!) {
addReaction(input:$myVar) {
reaction {
content
}
subject {
id
}
}
}
variables {
"myVar": {
"subjectId":"MDU6SXNzdWUyMTc5NTQ0OTc=",
"content":"HOORAY"
}
}
Вы можете заметить, что в значении поля content
в предыдущем примере (где оно используется в изменении напрямую) нет кавычек вокруг HOORAY
, но при использовании в переменной кавычки есть. Этому есть причина:
- При использовании
content
в изменении напрямую схема предполагает, что значение имеет типReactionContent
, который является перечислением, а не строкой. Если добавить кавычки вокруг значения перечисления, при проверке схемы возникнет ошибка, так как кавычки зарезервированы для строк. - При использовании
content
в переменной раздел переменных должен быть допустимым кодом JSON, поэтому кавычки требуются. Проверка схемы правильно интерпретирует типReactionContent
, когда переменная передается в изменение во время выполнения.
Дополнительные сведения о различиях между перечислениями и строками см. в официальной спецификации GraphQL.
Дополнительные материалы
При формировании вызовов GraphQL доступно гораздо больше возможностей. Дополнительную информацию можно найти в следующих ресурсах: