# GraphQL API

## 개요

Panther는 공개 GraphQL-over-HTTP API를 제공하므로 일반적인 HTTP 요청을 사용해 GraphQL 쿼리를 작성하고 API를 호출할 수 있습니다. GraphQL에 대한 자세한 내용은 [GraphQL의 문서](https://graphql.org/learn/).

현재 GraphQL API를 통해 다음 엔터티와 상호작용할 수 있습니다:

* [알러트 및 오류](https://docs.panther.com/ko/panther/api/graphql/alerts-and-errors)
* [클라우드 계정](https://docs.panther.com/ko/panther/api/graphql/cloud-account)
* [데이터 레이크 쿼리](https://docs.panther.com/ko/panther/api/graphql/data-lake-queries)
* [로그 소스](https://docs.panther.com/ko/panther/api/graphql/log-source)
* [메트릭](https://docs.panther.com/ko/panther/api/graphql/metrics)
* [스키마](https://docs.panther.com/ko/panther/api/graphql/schemas)
* [API 토큰](https://docs.panther.com/ko/panther/api/graphql/token-rotation)
* [사용자 및 역할](https://docs.panther.com/ko/panther/api/graphql/user-management)

추가 작업은 [REST API](https://docs.panther.com/ko/panther/api/rest).

### GraphQL 쿼리 이해하기

<details>

<summary>클릭하여 GraphQL 쿼리 예시 확장</summary>

아래 예제 쿼리의 이름은 `ListAlerts`입니다. 이 쿼리는 각 알러트의 모든 `id`, `title`, `severity` 와 `status` 제공된 시간 범위를 기반으로 반환합니다.

* 사용자를 사용할 것이며, `input` 형식의 변수 `AlertsInput` 는 createdAtAfter와 같은 특정 조건을 기반으로 알러트를 필터링하는 데 사용됩니다, `createdAtAfter` 와 `createdAtBefore`입니다. 이러한 조건들은 쿼리에 대한 시간 범위를 제공합니다.
* 사용자를 사용할 것이며, `알러트` 필드는 `edges` 와 `pageInfo`를 포함한 객체를 반환합니다. 각 엣지는 `node` 필드를 가지며 실제 알러트 데이터(예: `id`, `title`, `severity` 와 `status`.
* 사용자를 사용할 것이며, `pageInfo` 필드는 `hasNextPage` 와 `endCursor`, 사용자가 `hasNextPage` 가 되면 모든 알러트 페이지를 순회할 수 있게 해주는) 페이지네이션에 대한 정보를 포함합니다. `true`.

```graphql
query ListAlerts($input: AlertsInput!) {
    alerts(input: $input) {
      edges {
        node {
          id
          title
          severity
          status
        }
      }
      pageInfo {
        hasNextPage
        endCursor
      }
    }
  }
```

</details>

### Panther GraphQL 스키마 알아보기

GraphQL 스키마를 찾는 방법은 세 가지가 있습니다:

* 옵션 1(가장 빠름): 공개적으로 제공되는 GraphQL 스키마 파일 다운로드
* 옵션 2(가장 사용자 친화적): 사용 [Panther의 API 플레이그라운드](https://docs.panther.com/ko/panther/api/api-playground)
* 옵션 3(도구 및 서비스에 가장 적합): 수행 [인트로스펙션 쿼리](https://graphql.org/learn/introspection/) 를 GraphQL 엔드포인트에 대해

{% tabs %}
{% tab title="GraphQL 파일 다운로드" %}
**옵션 1: 공개적으로 제공되는 GraphQL 스키마 파일 다운로드**

최신 버전의 GraphQL 스키마 파일을 다운로드할 수 있습니다 [에 대해 더 읽을 수 있습니다](https://panther-community-us-east-1.s3.amazonaws.com/latest/graphql/schema.public.graphql).
{% endtab %}

{% tab title="Panther API 플레이그라운드" %}
**옵션 2: GraphQL 플레이그라운드 사용**

Panther의 API 플레이그라운드는 API에서 지원하는 항목을 탐색하고 발견하는 데 사용자 친화적인 방법입니다. 사용 방법에 대해서는 [API 플레이그라운드 문서](https://docs.runpanther.io/api-beta/api-playground) 를 참조하십시오. 발견 메커니즘으로서 이를 사용하는 방법에 대한 정보입니다.
{% endtab %}

{% tab title="인트로스펙션 쿼리" %}
**옵션 3: 인트로스펙션 쿼리 실행**

인트로스펙션 쿼리는 대부분의 서드파티 라이브러리가 파싱할 수 있는 형식으로 GraphQL API의 모든 엔티티를 반환합니다. 이 발견 옵션은 다른 라이브러리나 서비스가 Panther API가 지원하는 작업과 타입을 인식하게 하려는 경우에 유용합니다. 이러한 라이브러리들은 일반적으로 자체적인 인트로스펙션 쿼리를 발행하므로 API URL만 가리키면 됩니다.

보안상의 이유로 인트로스펙션 쿼리는 권한이 필요한 작업입니다. 이는 인트로스펙션이 작동하려면 `X-API-Key` 헤더를 HTTP 호출에 추가하고 값으로 [API 토큰](https://docs.runpanther.io/api-beta/generating-an-api-token) 을(를) 제공해야 함을 의미합니다.

```
$ curl -H "X-API-Key: <API_KEY>" -d <INTROSPECTION_QUERY> <YOUR_PANTHER_API_URL>
```

인트로스펙션 쿼리의 실제 형태는 사용자 정의가 가능합니다. 제한된 엔티티 집합만 요청하거나 스키마에 대한 모든 가능한 정보를 요청할 수 있습니다. 예를 들어 다음과 같은 쿼리는 스키마의 모든 정보를 반환합니다:

```graphql
query IntrospectionQuery {
    __schema {
      queryType { name }
      mutationType { name }
      types {
        ...FullType
      }
      directives {
        이름
        설명
        locations
        args {
          ...InputValue
        }
      }
    }
  }
  fragment FullType on __Type {
    kind
    이름
    설명
    fields(includeDeprecated: true) {
      이름
      설명
      args {
        ...InputValue
      }
      type {
        ...TypeRef
      }
      isDeprecated
      deprecationReason
    }
    inputFields {
      ...InputValue
    }
    interfaces {
      ...TypeRef
    }
    enumValues(includeDeprecated: true) {
      이름
      설명
      isDeprecated
      deprecationReason
    }
    possibleTypes {
      ...TypeRef
    }
  }
  fragment InputValue on __InputValue {
    이름
    설명
    type { ...TypeRef }
    defaultValue
  }
  fragment TypeRef on __Type {
    kind
    이름
    ofType {
      kind
      이름
      ofType {
        kind
        이름
        ofType {
          kind
          이름
          ofType {
            kind
            이름
            ofType {
              kind
              이름
              ofType {
                kind
                이름
                ofType {
                  kind
                  이름
                }
              }
            }
          }
        }
      }
    }
  }
```

{% endtab %}
{% endtabs %}

## Panther GraphQL API 사용 방법

### 1단계: Panther GraphQL API URL 확인

GraphQL API URL을 찾으려면:

* Panther 콘솔의 오른쪽 상단에서 톱니바퀴 아이콘을 클릭한 다음, **API 토큰**.
  * 페이지 상단에서 **API URL**.
  * GraphQL API URL 구조는 Panther 배포 모델에 따라 다릅니다:
    * [SaaS](https://docs.panther.com/ko/system-configuration/panther-deployment-types) 배포: `https://api.{YOUR_PANTHER_DOMAIN}.runpanther.net/public/graphql`
    * [Cloud Connected](https://docs.panther.com/ko/system-configuration/panther-deployment-types) 와 [셀프호스트](https://docs.panther.com/ko/system-configuration/panther-deployment-types) 배포: `https://{YOUR_PANTHER_DOMAIN}/v1/public/graphql`

<figure><img src="https://2400888838-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LgdiSWdyJcXPahGi9Rs-2910905616%2Fuploads%2Fgit-blob-20e1dd8a8366cc1aef4ed43a5aefa73883e04736%2FScreenshot%202023-12-13%20at%2011.35.41%20AM.png?alt=media" alt=""><figcaption></figcaption></figure>

### 2단계: API 토큰 생성

* 참조 [API 토큰 생성 방법에 관한 이 지침](https://docs.panther.com/ko/panther/api/..#how-to-create-a-panther-api-token).

### 3단계: Panther GraphQL API 호출

테스트용으로 [API 플레이그라운드](https://docs.panther.com/ko/panther/api/api-playground)외에도 GraphQL-over-HTTP API를 호출하는 방법은 두 가지가 있습니다:

* 옵션 1 (권장): GraphQL 클라이언트를 설치하여 전송 관련 복잡성을 추상화하여 사용
* 옵션 2: 수동으로 HTTP 호출 구성

{% tabs %}
{% tab title="GraphQL 클라이언트" %}
**옵션 1: GraphQL 클라이언트 설치 및 사용&#x20;*****(권장)***

모든 GraphQL 연산은 본질적으로 단순한 HTTP 호출이지만, GraphQL 클라이언트를 사용하면 더 사용자 친화적이라는 장점이 있습니다.

다음을 사용하는 것을 권장합니다:

* [`graphql-request`](https://github.com/prisma-labs/graphql-request) NodeJS 프로젝트용
* [`gql`](https://github.com/graphql-python/gql) Python 프로젝트용
* [`go-graphql-client`](https://github.com/hasura/go-graphql-client) Go 프로젝트용

아래는 시스템에서 첫 페이지의 알러트를 가져오기 위해 GraphQL 쿼리를 구성하는 몇 가지 예입니다:

{% tabs %}
{% tab title="NodeJS" %}

```javascript
// npm install graphql graphql-request

import { GraphQLClient, gql } from 'graphql-request';

const client = new GraphQLClient(
  'YOUR_PANTHER_API_URL', 
  { headers: { 'X-API-Key': 'YOUR_API_KEY' } 
});

// `PaginateAlerts`는 이 연산의 별칭입니다
const query = gql` 
  query PaginateAlerts {
  alerts(
  input: {
    createdAtAfter: "2023-06-14T21:00:00Z",
    createdAtBefore: "2023-06-21T21:59:59Z"
  }) {
    edges {
      node {
        id
        title
        severity
        status
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}
`;

client.request(query).then((data) => console.log(data));
```

{% endtab %}

{% tab title="Python" %}

```python
# pip install gql aiohttp

from gql import gql, Client
from gql.transport.aiohttp import AIOHTTPTransport

transport = AIOHTTPTransport(
  url="YOUR_PANTHER_API_URL",
  headers={"X-API-Key": "YOUR_API_KEY"}
)

client = Client(transport=transport, fetch_schema_from_transport=True)

# `PaginateAlerts`는 이 연산의 별칭입니다
query = gql(
  """
  query PaginateAlerts {
  alerts(
  input: {
    createdAtAfter: "2023-06-14T21:00:00Z",
    createdAtBefore: "2023-06-21T21:59:59Z"
  }) {
    edges {
      node {
        id
        title
        severity
        status
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}
  """
)

result = client.execute(query)
print(result)
```

{% endtab %}

{% tab title="Golang" %}

```go
// go get -u github.com/hasura/go-graphql-client

package main

import (
  "context"
  "encoding/json"
  "fmt"
  "net/http"

  "github.com/hasura/go-graphql-client"
)

// 정적 타입 언어는 GraphQL과 잘 맞지 않습니다
var query struct {
  Alerts struct {
    Edges []struct {
      Node struct {
        Id       graphql.String
        Title    graphql.String
        Severity graphql.String
        Status   graphql.String
      }
    }
    PageInfo struct {
      HasNextPage graphql.Boolean
      EndCursor   graphql.String
    }
  } `graphql:"alerts(input: { createdAtAfter: \"2023-06-14T21:00:00Z\", createdAtBefore: \"2023-06-21T21:59:59Z\" })"`
}

func main() {
  client := graphql.
    NewClient("YOUR_PANTHER_API_URL", nil).
    WithRequestModifier(func(req *http.Request) {
      req.Header.Set("X-API-KEY", "YOUR_API_KEY")
    })

  if err := client.Query(context.Background(), &query, nil); err != nil {
    panic(err)
  }

  formattedResult, _ := json.MarshalIndent(query.Alerts, "", "\t")
  fmt.Println(string(formattedResult))
  fmt.Println(query.Alerts.PageInfo.HasNextPage)
}
```

{% endtab %}
{% endtabs %}
{% endtab %}

{% tab title="HTTP 호출 구성" %}
**옵션 2: 수동으로 HTTP 호출 구성하기**

요청 예시:

```bash
curl 'YOUR_PANTHER_GRAPHQL_API_URL' \
-H 'Content-Type: application/json' \
-H 'X-API-Key: {YOUR_API_KEY}' \
-d '{"query":"\n query Foo {\n alerts {\n edges {\n node {\n id\n }\n }\n }\n }","variables":{}}' 
```

위의 쿼리는 모든 Panther 알러트의 첫 페이지를 반환합니다. GraphQL을 처음 사용하신다면 다음 사항을 참고하세요:

* 엔드포인트는 하나뿐입니다.
* HTTP 작업은 항상 `POST입니다.`
* API 작업은 `HTTP 원시 이벤트(HTTP Raw events)`의 본문에 정의되어 있습니다.
* 의 본문은 `HTTP 원시 이벤트(HTTP Raw events)` 작업 **항상** 다음 키들을 포함합니다:
  * `query` - 실행되어야 할 GraphQL 작업을 정의하는 GraphQL 문자열
  * `variables` - 쿼리에 전달될 선택적 변수 집합
  * `operationName` - 이 작업에 대한 선택적 "별명"
* 데이터를 반환하는 작업인 경우 항상 반환할 필드 집합을 선택해야 합니다.

참고: 한 GraphQL 작업에서 다른 작업으로 변경되는 유일한 것은 HTTP의 본문뿐입니다 `HTTP 원시 이벤트(HTTP Raw events)`.
{% endtab %}
{% endtabs %}
