Full Code of yuki24/artemis for AI

main 8b3d76a11bf7 cached
72 files
5.0 MB
1.3M tokens
183 symbols
1 requests
Download .txt
Showing preview only (5,224K chars total). Download the full file or copy to clipboard to get everything.
Repository: yuki24/artemis
Branch: main
Commit: 8b3d76a11bf7
Files: 72
Total size: 5.0 MB

Directory structure:
gitextract_iw48rug4/

├── .github/
│   └── workflows/
│       └── ruby.yml
├── .gitignore
├── .rspec
├── Appraisals
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── artemis.gemspec
├── bin/
│   ├── console
│   └── setup
├── gemfiles/
│   ├── .bundle/
│   │   └── config
│   ├── graphql_2_0.gemfile
│   ├── rails_70.gemfile
│   ├── rails_71.gemfile
│   ├── rails_72.gemfile
│   ├── rails_80.gemfile
│   ├── rails_81.gemfile
│   └── rails_edge.gemfile
├── lib/
│   ├── artemis/
│   │   ├── adapters/
│   │   │   ├── abstract_adapter.rb
│   │   │   ├── curb_adapter.rb
│   │   │   ├── multi_domain_adapter.rb
│   │   │   ├── net_http_adapter.rb
│   │   │   ├── net_http_persistent_adapter.rb
│   │   │   └── test_adapter.rb
│   │   ├── adapters.rb
│   │   ├── client.rb
│   │   ├── exceptions.rb
│   │   ├── graphql_endpoint.rb
│   │   ├── railtie.rb
│   │   ├── rspec.rb
│   │   ├── test_helper.rb
│   │   └── version.rb
│   ├── artemis.rb
│   ├── generators/
│   │   └── artemis/
│   │       ├── install/
│   │       │   ├── USAGE
│   │       │   ├── install_generator.rb
│   │       │   └── templates/
│   │       │       ├── client.rb
│   │       │       └── graphql.yml
│   │       ├── mutation/
│   │       │   ├── USAGE
│   │       │   ├── mutation_generator.rb
│   │       │   └── templates/
│   │       │       └── mutation.graphql
│   │       └── query/
│   │           ├── USAGE
│   │           ├── query_generator.rb
│   │           └── templates/
│   │               ├── fixture.yml
│   │               └── query.graphql
│   └── tasks/
│       └── artemis.rake
└── test/
    ├── adapters_test.rb
    ├── autoloading_test.rb
    ├── backport/
    │   └── method_call_assertions.rb
    ├── callbacks_test.rb
    ├── client_test.rb
    ├── endpoint_test.rb
    ├── fixtures/
    │   ├── github/
    │   │   ├── _repository_fields.graphql
    │   │   ├── repository.graphql
    │   │   ├── schema.json
    │   │   ├── user.graphql
    │   │   └── user_repositories.graphql
    │   ├── github.rb
    │   └── responses/
    │       ├── github/
    │       │   ├── repository.yml
    │       │   └── user.json
    │       └── spotify_client/
    │           └── artist.yml
    ├── generators/
    │   ├── install_generator_test.rb
    │   ├── mutation_generator_test.rb
    │   └── query_generator_test.rb
    ├── helpers/
    │   ├── fake_server.rb
    │   ├── isolated_test_helper.rb
    │   └── test_helper.rb
    ├── railtie_test.rb
    ├── rake_tasks_test.rb
    └── test_helper_test.rb

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/ruby.yml
================================================
name: build

on:
  push:
  pull_request:

jobs:
  mri:
    strategy:
      fail-fast: false
      matrix:
        ruby_version:
          - '3.4'
          - '3.3'
          - '3.2'
          - '3.1'
          - '3.0'
          - '2.7'
        gemfile:
          - gemfiles/rails_81.gemfile
          - gemfiles/rails_80.gemfile
          - gemfiles/rails_72.gemfile
          - gemfiles/rails_71.gemfile
          - gemfiles/rails_70.gemfile
          - gemfiles/graphql_2_0.gemfile
        exclude:
          - ruby_version: '3.4'
            gemfile: gemfiles/rails_71.gemfile
          - ruby_version: '3.4'
            gemfile: gemfiles/rails_70.gemfile
          - ruby_version: '3.1'
            gemfile: gemfiles/rails_81.gemfile
          - ruby_version: '3.1'
            gemfile: gemfiles/rails_80.gemfile
          - ruby_version: '3.0'
            gemfile: gemfiles/rails_81.gemfile
          - ruby_version: '3.0'
            gemfile: gemfiles/rails_80.gemfile
          - ruby_version: '3.0'
            gemfile: gemfiles/rails_72.gemfile
          - ruby_version: '3.0'
            gemfile: gemfiles/graphql_2_0.gemfile
          - ruby_version: '2.7'
            gemfile: gemfiles/rails_81.gemfile
          - ruby_version: '2.7'
            gemfile: gemfiles/rails_80.gemfile
          - ruby_version: '2.7'
            gemfile: gemfiles/rails_72.gemfile
          - ruby_version: '2.7'
            gemfile: gemfiles/rails_71.gemfile
          - ruby_version: '2.7'
            gemfile: gemfiles/graphql_2_0.gemfile
    runs-on: ubuntu-22.04
    env:
      BUNDLE_GEMFILE: ${{ matrix.gemfile }}
    steps:
      - uses: actions/checkout@v4
      - name: Install curl
        run: sudo apt-get install curl libcurl4-openssl-dev
      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: ${{ matrix.ruby_version }}
          bundler-cache: true
      - run: bundle exec rake

  rails_edge:
    needs:
      - mri
    runs-on: ubuntu-22.04
    env:
      BUNDLE_GEMFILE: gemfiles/rails_edge.gemfile
    steps:
      - uses: actions/checkout@v4
      - name: Install curl
        run: sudo apt-get install curl libcurl4-openssl-dev
      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: 3.4
          bundler-cache: true
      - run: bundle exec rake || echo "Rails edge test is done."

  ruby_edge:
    needs:
      - mri
    strategy:
      matrix:
        ruby_version:
          - 'ruby-head'
        gemfile:
          - gemfiles/rails_edge.gemfile
          - gemfiles/rails_80.gemfile
    runs-on: ubuntu-22.04
    env:
      BUNDLE_GEMFILE: ${{ matrix.gemfile }}
    steps:
      - uses: actions/checkout@v4
      - name: Install curl
        run: sudo apt-get install curl libcurl4-openssl-dev
      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: ${{ matrix.ruby_version }}
          bundler-cache: true
      - run: bundle exec rake || echo "Ruby edge test is done."

#  The curb gem does not work well with JRuby, so skipping for now...
#  jruby:
#    needs:
#      - mri
#    strategy:
#      matrix:
#        ruby_version:
#          - 'jruby-9.4'
#          - 'jruby-head'
#        gemfile:
#          - gemfiles/rails_70.gemfile
#    runs-on: ubuntu-22.04
#    env:
#      BUNDLE_GEMFILE: ${{ matrix.gemfile }}
#    steps:
#      - uses: actions/checkout@v4
#      - name: Set up Ruby
#        uses: ruby/setup-ruby@v1
#        with:
#          ruby-version: ${{ matrix.ruby_version }}
#          bundler-cache: true
#      - run: bundle exec rake || echo "JRuby test is done."


================================================
FILE: .gitignore
================================================
/.bundle/
/.yardoc
/_yardoc/
/coverage/
/doc/
/pkg/
/spec/reports/
/tmp/
Gemfile.lock
gemfiles/*.lock
.byebug_history


================================================
FILE: .rspec
================================================
--require spec_helper
--colour

================================================
FILE: Appraisals
================================================
appraise "rails_edge" do
  git 'https://github.com/rails/rails.git' do
    gem "rails"
    gem "railties"
    gem "activesupport"
  end

  gem "rackup"
end

appraise "graphql_2_0" do
  gem "rails", '~> 7.1.0'
  gem "railties", '~> 7.1.0'
  gem "activesupport", '~> 7.1.0'
  gem "rackup"

  gem "graphql", "< 2.1"
end

appraise "rails_80" do
  gem "rails", '~> 8.0.0'
  gem "railties", '~> 8.0.0'
  gem "activesupport", '~> 8.0.0'
  gem "rackup"
end

appraise "rails_72" do
  gem "rails", '~> 7.2.0'
  gem "railties", '~> 7.2.0'
  gem "activesupport", '~> 7.2.0'
  gem "rackup"
end

appraise "rails_71" do
  gem "rails", '~> 7.1.0'
  gem "railties", '~> 7.1.0'
  gem "activesupport", '~> 7.1.0'
  gem "rackup"
end

appraise "rails_70" do
  gem "rails", '~> 7.0.0'
  gem "railties", '~> 7.0.0'
  gem "activesupport", '~> 7.0.0'
end

appraise "rails_61" do
  gem "rails", '~> 6.1.0'
  gem "railties", '~> 6.1.0'
  gem "activesupport", '~> 6.1.0'
end

appraise "rails_60" do
  gem "rails", '~> 6.0.0'
  gem "railties", '~> 6.0.0'
  gem "activesupport", '~> 6.0.0'
end

appraise "rails_52" do
  gem "rails", '~> 5.2.0'
  gem "railties", '~> 5.2.0'
  gem "activesupport", '~> 5.2.0'
end

appraise "rails_51" do
  gem "rails", '~> 5.1.0'
  gem "railties", '~> 5.1.0'
  gem "activesupport", '~> 5.1.0'
end

appraise "rails_50" do
  gem "rails", '~> 5.0.0'
  gem "railties", '~> 5.0.0'
  gem "activesupport", '~> 5.0.0'
  gem "minitest", '5.10.3'
end


================================================
FILE: CHANGELOG.md
================================================
## Unreleased

#### 🚨 Breaking Changes

- No changes.

#### ⭐️ New Features

- Add support for Rails 8.0 ([<tt>#96</tt>](https://github.com/yuki24/artemis/pull/96))

#### 🐞 Bug Fixes

- No changes.

## [v1.1.0](https://github.com/yuki24/artemis/tree/v1.1.0)

_<sup>released at 2024-08-16 05:38:33 UTC</sup>_

#### ⭐️ New Features

- Add support for Ruby 3.3. ([<tt>e057567</tt>](https://github.com/yuki24/artemis/commit/e05756768c1535babccfca71f32d5218dd4da626))

## [v1.0.2](https://github.com/yuki24/artemis/tree/v1.0.2)

_<sup>released at 2024-05-02 02:41:10 UTC</sup>_

#### 🐞 Bug Fixes

- Fixes a bug where abstract client classes are not loaded correctly ([#93](https://github.com/yuki24/artemis/issues/93), `494d30b`)

## [v1.0.1](https://github.com/yuki24/artemis/tree/v1.0.1)

_<sup>released at 2024-05-02 02:40:54 UTC</sup>_

> Yanked due to inconsistent commit history.

## [v1.0.0](https://github.com/yuki24/artemis/tree/v1.0.0)

_<sup>released at 2024-02-05 06:16:35 UTC</sup>_

#### 🚨 Breaking Changes

- Drop support for Ruby 2.6. For those of you looking to use Artemis on Ruby 2.6, please use the `artemis` version
  `0.9.0` and the `graphql-client` version `0.17.0`. ([#90](https://github.com/yuki24/artemis/pull/90))

#### ⭐️ New Features

- Add support for Ruby 3.3. ([#91](https://github.com/yuki24/artemis/pull/91))
- Add support for the latest versions of the `graphql` gem. ([#92](https://github.com/yuki24/artemis/pull/92))

#### 🐞 Bug Fixes

- No bug fixes.

## [v0.9.0](https://github.com/yuki24/artemis/tree/v0.9.0)

_<sup>released at 2023-09-18 01:08:34 UTC</sup>_

#### New Features

- Rails 7.1.0.beta1 is now officially supported ([<tt>f25ba29</tt>](https://github.com/yuki24/artemis/commit/f25ba296f15b26ffba7e4ec0f5b4cbeb061c97a1))

#### Fixes

- Fixes an issue where `graphql` gem `2.1.0` may not work with `graphql-client` ([<tt>b144ee2</tt>](https://github.com/yuki24/artemis/commit/b144ee2fbca2c23b4aaed8236f6fc07f65d8239d))

## [v0.8.0](https://github.com/yuki24/artemis/tree/v0.8.0)

_<sup>released at 2023-01-05 05:29:37 UTC</sup>_

#### New Features

- Ruby 3.2 is now officially supported

## [v0.7.0](https://github.com/yuki24/artemis/tree/v0.7.0)

_<sup>released at 2022-03-05 08:24:45 UTC</sup>_

#### Features

- Add support for Ruby 3.1 and Rails 7.0
- Add support for [the Multiplex query](https://graphql-ruby.org/queries/multiplex.html)
- Do not allow the usage of the `multi_domain` adapter to be nested ([<tt>9b7b520</tt>](https://github.com/yuki24/artemis/commit/9b7b5202c9fbe424d4ca22f05dc9c9759b5202c3))
- Do not require fragment files to end with `_fragment.graphql` ([<tt>3c6c0fa</tt>](https://github.com/yuki24/artemis/commit/3c6c0fa))
- Allow for overriding the namespace used for resolving graphql file paths ([<tt>bd18762</tt>](https://github.com/yuki24/artemis/commit/bd18762))

## [v0.6.0](https://github.com/yuki24/artemis/tree/v0.6.0)

_<sup>released at 2021-09-03 04:17:55 UTC</sup>_

#### Features

- Add support for Ruby 3.0 and Rails 6.0, 6.1
- Add the multi domain adapter ([<tt>744b8ea</tt>](https://github.com/yuki24/artemis/commit/744b8ea35795b4e6cc4fdc1ebb63dd9a4e9819f0))

#### Fixes

- Address warnings from Ruby 2.7 ([<tt>408adcb</tt>](https://github.com/yuki24/artemis/commit/408adcb3f39912f7afb7b3690a52f1d593662b7b))
- Avoid crashing when config/graphql.yml does not exist ([@dlackty](https://github.com/dlackty), [#76](https://github.com/yuki24/artemis/pull/76))

## [v0.5.2](https://github.com/yuki24/artemis/tree/v0.5.2)

_<sup>released at 2019-07-26 03:21:43 UTC</sup>_

#### Fixes

- Fixes an issue where fixtures can not be looked up properly when the client has two or more words in its name ([@JanStevens](https://github.com/JanStevens), issue: [#72](https://github.com/yuki24/artemis/issues/72), PR: [#73](https://github.com/yuki24/artemis/pull/73))

## [v0.5.1](https://github.com/yuki24/artemis/tree/v0.5.1)

_<sup>released at 2019-07-01 14:25:35 UTC</sup>_

#### Fixes

- Fixes an issue where callbacks are shared across all clients ([@JanStevens](https://github.com/JanStevens), issue: [#69](https://github.com/yuki24/artemis/issues/69), PR: [#70](https://github.com/yuki24/artemis/pull/70))
- Fixes an issue where fixtures with the same name cause a conflict ([@JanStevens](https://github.com/JanStevens), Issue: [#68](https://github.com/yuki24/artemis/issues/68), commit: [<tt>e1f57f4</tt>](https://github.com/yuki24/artemis/commit/e1f57f49ebb032553d7a6f70e48422fc9825c119))

## [v0.5.0](https://github.com/yuki24/artemis/tree/v0.5.0)

_<sup>released at 2019-06-02 22:01:57 UTC</sup>_

#### Features

- Add support for Rails 6.0, 4.1, and 4.0
- [<tt>6701b54</tt>](https://github.com/yuki24/artemis/commit/6701b546a143c22109c7ab30018acf96d67067d1), [#62](https://github.com/yuki24/artemis/issues/62): Allow to dynamically call the operation ([@JanStevens](https://github.com/JanStevens))

#### Fixes

- [#67](https://github.com/yuki24/artemis/pull/67): Fix the wrong test version constraints in `Appraisals` ([@daemonsy](https://github.com/daemonsy))
- [#60](https://github.com/yuki24/artemis/pull/60): Fix an issue where not all adapters send required HTTP headers

## [v0.4.0](https://github.com/yuki24/artemis/tree/v0.4.0)

_<sup>released at 2019-01-30 03:42:14 UTC</sup>_

#### Features

- [<tt>48d052e</tt>](https://github.com/yuki24/artemis/commit/48d052e9819703f1cefa95fbdb431bd03928f4ed): Add an easy way to set up Rspec integration
- [<tt>0f7cd12</tt>](https://github.com/yuki24/artemis/commit/0f7cd120594a0dd2a4af2b2e5cf990891dd8de16): Make Artemis' Railtie configurable
- [<tt>6bd15e2</tt>](https://github.com/yuki24/artemis/commit/6bd15e20779e5a6f898e1aacf8237c94c8c46aba): Add the ability to use ERB in test fixtures
- [#49](https://github.com/yuki24/artemis/pull/49): Expose the TestAdapter as a public API

#### Bug fixes

- [<tt>b7ad4a4</tt>](https://github.com/yuki24/artemis/commit/b7ad4a481a43cadd9193076c0e44938e05e6d44b): Require `graphl >= 1.8` to fix a bug in the generator
- [#48](https://github.com/yuki24/artemis/pull/48): Do not transform keys of query variables ([@erikdstock](https://github.com/erikdstock))
- [#47](https://github.com/yuki24/artemis/pull/47): Fixes an issue where errors thrown from `config/graphql.yml` get swallowed

## [v0.2.0: New generators and small improvements](https://github.com/yuki24/artemis/tree/v0.2.0)

_<sup>released at 2018-10-30 02:09:59 UTC</sup>_

#### Features

- [#43](https://github.com/yuki24/artemis/pull/43): Keep persistent connections open for 30 minutes
- [#42](https://github.com/yuki24/artemis/pull/42): Allow for setting up the test adapter without `url`
- [#41](https://github.com/yuki24/artemis/pull/41): Add a mutation generator
- [#40](https://github.com/yuki24/artemis/pull/40): Add a query generator
- [#39](https://github.com/yuki24/artemis/pull/39): Installer now adds a new service if `config/graphql.yml` is present

## [v0.1.0: First release!](https://github.com/yuki24/artemis/tree/v0.1.0)

_<sup>released at 2018-10-16 20:57:51 UTC</sup>_

First release of Artemis! 🎉



================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.

## Our Standards

Examples of behavior that contributes to creating a positive environment
include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
  address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
  professional setting

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.

## Scope

This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at yk.nishijima@gmail.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]

[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/


================================================
FILE: Gemfile
================================================
source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

# Specify your gem's dependencies in artemis.gemspec
gemspec

gem "bigdecimal"
gem "drb"
gem "irb"
gem "logger"
gem "mutex_m"
gem "reline"
gem "curb", ">= 0.9.6"
gem "minitest"
gem "webrick"


================================================
FILE: LICENSE.txt
================================================
The MIT License (MIT)

Copyright (c) 2018 Yuki Nishijima

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: README.md
================================================
# Artemis [![build](https://github.com/yuki24/artemis/actions/workflows/ruby.yml/badge.svg)](https://github.com/yuki24/artemis/actions/workflows/ruby.yml) [![Gem Version](https://badge.fury.io/rb/artemis.svg)](https://rubygems.org/gems/artemis)

Artemis is a GraphQL client that is designed to fit well on Rails.

 * **Convention over Configuration**: You'll never have to make trivial decisions or spend time on boring setup. Start
  making a GraphQL request in literally 30s.
 * **Performant by default**: You can't do wrong when it comes to performance. All GraphQL files are pre-loaded only
  once in production and it'll never affect runtime performance. Comes with options that enable persistent connections
   and even HTTP/2, the next-gen high-performance protocol.
 * **First-class support for testing**: Testing and stubbing GraphQL requests couldn't be simpler. No need to add
  external dependencies to test well.

<img width="24" height="24" src="https://avatars1.githubusercontent.com/u/541332?s=48&amp;v=4"> Battle-tested at [Artsy](https://www.artsy.net)

![graphql-client vs Artemis](https://raw.githubusercontent.com/yuki24/artemis/master/banner.png "graphql-client vs Artemis")

## Getting started

Add this line to your application's Gemfile:

```ruby
gem 'artemis'
```

Once you run `bundle install` on your Rails app, run the install command:


```sh
$ rails g artemis:install artsy https://metaphysics-production.artsy.net/

# or if you need to specify the `--authorization` header:
$ rails g artemis:install github https://api.github.com/graphql --authorization 'token ...'
```

### Generating your first query

Artemis comes with a query generator. For exmaple, you could use the query generator to generate a query stub for `artist`:

```sh
$ rails g artemis:query artist
```

Then this will generate:

```graphql
# app/operations/artist.graphql
query($id: String!) {
  artist(id: $id) {
    # Add fields here...
  }
}
```

Then you could call the class method that has the matching name `artist`:

```ruby
Artsy.artist(id: "pablo-picasso")
# => makes a GraphQL query that's in app/operations/artist.graphql
```

You can also specify a file name:

```sh
$ rails g artemis:query artist artist_details_on_artwork
# => generates app/operations/artist_details_on_artwork.graphql
```

Then you can make a query in `artist_details_on_artwork.graphql` with:

```ruby
Artsy.artist_details_on_artwork(id: "pablo-picasso")
```

## The convention

Artemis assumes that the files related to GraphQL are organized in a certain way. For example, a service that talks to Artsy's GraphQL API could have the following structure:

```
├──app/operations
│   ├── artsy
│   │   ├── _artist_fragment.graphql
│   │   ├── artwork.graphql
│   │   ├── artist.graphql
│   │   └── artists.graphql
│   └── artsy.rb
├──config/graphql.yml
├──test/fixtures/graphql
│   └── artsy
│       ├── artwork.yml
│       ├── artist.yml
│       └── artists.yml
└──vendor/graphql/schema/artsy.json
```

### Fragments
Fragments are defined in defined in a standard way in a file named `_artwork_fragment.graphql` with the standard convention:

```graphql
fragment on Artwork {
  id,
  name,
  artist_id
  # other artwork fields here
}
```

The way of calling an Artemis fragment on other queries or models is with a **Rails convention**. Let us suppose we have the Artist model and its corresponding artwork. The way of nesting or calling the artwork on the artist model would look like this:

```graphql
fragment on Artist {
  id,
  name,
  artworks {
    ...Artsy::ArtworkFragment
  }
}
```

Where `Artsy` is the name of the folder/module.

## Callbacks

You can use the `before_execute` callback to intercept outgoing requests and the `after_execute` callback to observe the
response. A common operation that's done in the `before_execute` hook is assigning a token to the header:

```ruby
class Artsy < Artemis::Client
  before_execute do |document, operation_name, variables, context|
    context[:headers] = {
      Authorization: "token ..."
    }
  end
end
```

Here the `:headers` key is a special context type. The hash object assigned to the `context[:headers]` will be sent as
the HTTP headers of the request.

Another common thing when receiving a response is to check if there's any error in the response and throw and error
accordingly:

```ruby
class Artsy < Artemis::Client
  after_execute do |data, errors, extensions|
    raise "GraphQL error: #{errors.to_json}" if errors.present?
  end
end
```

## Multi domain support

Services like Shopify provide
[a different endpoint per customer](https://shopify.dev/api/admin/graphql/reference#graphql-endpoint) (e.g.
`https://{shop}.myshopify.com`). In order to switch the endpoint on a per-request basis, you will have to use the
`:multi_domain` adapter. This is a wrapper adapter that relies on an actual HTTP adapter such as `:net_http` and
`:curb` so that e.g. it can maintain multiple connections for each endpoint if necessary. This could be configured
as shown below:

```yaml
default: &default
  # Specify the :multi_domain adapter:
  adapter: :multi_domain

  # Other configurations such as `timeout` and `pool_size` are passed down to the underlying adapter:
  timeout: 10
  pool_size: 25

  # Additional adapter-specific configurations could be configured as `adapter_options`:
  adapter_options:
    # Here you can configure the actual adapter to use. By default, it is set to :net_http. Available adapters are
    # :net_http, :net_http_persistent, :curb, and :test. You can not nest the use of the `:multi_domain` adapter.
    adapter: :net_http

development:
  shopify:
    <<: *default

...
```

Upon making a request you will also have to specify the `url` option:

```ruby
# Makes a request to https://myawesomeshop.myshopify.com:
Shopify.with_context(url: "https://myawesomeshop.myshopify.com").product(id: "...")
```

## Configuration

You can configure the GraphQL client using the following options. Those configurations are found in the
`config/graphql.yml`.

| Name          | Required? | Default     | Description |
| ------------- | --------- | ------------| ----------- |
| `adapter`     | No        | `:net_http` | The underlying client library that actually makes an HTTP request. See Adapters for available options.
| `pool_size`   | No        | 25          | The number of keep-alive connections. The `:net_http` adapter will ignore this option.
| `schema_path` | No        | See above   | The path to the GrapQL schema. Setting an empty value to this will force the client to download the schema upon the first request.
| `timeout`     | No        | 10          | HTTP timeout set for the adapter in seconds. This will be set to both `read_timeout` and `write_timeout` and there is no way to configure them with a different value as of writing (PRs welcome!)
| `url`         | Yes       | N/A         | The URL for the GraphQL endpoint.

### Adapters

There are four adapter options available. Choose the adapter that best fits on your use case.

| Adapter                | Protocol                 | Keep-alive  | Performance | Dependencies |
| ---------------------- | ------------------------ | ----------- | ----------- | ------------ |
| `:curb`                | HTTP/1.1, **HTTP/2**     | **Yes**     | **Fastest** | [`curb 0.9.6+`][curb]<br>[`libcurl 7.64.0+`][curl]<br>[`nghttp2 1.0.0+`][nghttp]
| `:net_http` (default)  | HTTP/1.1 only            | No          | Slow        | **None**
| `:net_http_persistent` | HTTP/1.1 only            | **Yes**     | **Fast**    | [`net-http-persistent 3.0.0+`][nhp]
| `:multi_domain`        | See [multi domain support](#multi-domain-support)
| `:test`                | See [Testing](#testing)

#### Third-party adapters

This is a comminuty-maintained adapter. Want to add yours? Send us a pull request!

| Adapter                | Description |
| ---------------------- | ------------|
| [`:net_http_hmac`](https://github.com/JanStevens/artemis-api-auth/tree/master)      | provides a new Adapter for the Artemis GraphQL ruby client to support HMAC Authentication using [ApiAuth](https://github.com/mgomes/api_auth). |

#### Writing your own adapter

When the built-in adapters do not satisfy your needs, you may want to implement your own adapter. You could do so by following the steps below. Let's implement the [`:net_http_hmac`](https://github.com/JanStevens/artemis-api-auth/tree/master) adapter as an example.

 1. Define `NetHttpHmacAdapter` under the `Artemis::Adapters` namespace and implement [the `#execute` method](https://github.com/github/graphql-client/blob/master/guides/remote-queries.md):

    ```ruby
    # lib/artemis/adapters/net_http_hmac_adapter.rb
    module Artemis::Adapters
      class NetHttpHmacAdapter
        def execute(document:, operation_name: nil, variables: {}, context: {})
          # Makes an HTTP request for GraphQL query.
        end
      end
    end
    ```

 2. Load the adapter in `config/initializers/artemis.rb` (or any place that gets loaded before Rails runs initializers):

    ```ruby
    require 'artemis/adapters/net_http_hmac_adapter'
    ```

 3. Specify the adapter name in `config/graphql.yml`:

    ```yml
    default: &default
      adapter: :net_http_hmac
    ```

## Rake tasks

Artemis also adds a useful `rake graphql:schema:update` rake task that downloads the GraphQL schema using the
`Introspection` query.

### `graphql:schema:update`

Downloads and saves the GraphQL schema.

| Option Name        | Description |
| ------------------ | ------------|
| `SERVICE`          | Service name the schema is downloaded from.| 
| `AUTHORIZATION`    | HTTP `Authorization` header value used to download the schema with.|


#### Examples

```
$ rake graphql:schema:update
# => downloads schema from the service. fails if there are multiple services in config/graphql.yml.

$ rake graphql:schema:update SERVICE=github AUTHORIZATION="token ..."
# => downloads schema from the `github` service using the HTTP header "AUTHORIZATION: token ..."
```

## Testing

Given that you have `app/operations/artsy/artist.graphql` and fixture file for the `artist.yml`:

```yml
# test/fixtures/graphql/artist.yml:
leonardo_da_vinci:
  data:
    artist:
      name: Leonardo da Vinci
      birthday: 1452/04/15

yayoi_kusama:
  data:
    artist:
      name: Yayoi Kusama
      birthday: 1929/03/22
```

Then you can stub the request with the `stub_graphql` DSL:

```ruby
stub_graphql(Artsy, :artist, id: "yayoi-kusama").to_return(:yayoi_kusama)
stub_graphql(Artsy, :artist, id: "leonardo-da-vinci").to_return(:leonardo_da_vinci)

yayoi_kusama = Artsy.artist(id: "yayoi-kusama")
yayoi_kusama.data.artist.name     # => "Yayoi Kusama"
yayoi_kusama.data.artist.birthday # => "1452/04/15"

da_vinci = Artsy.artist(id: "leonardo-da-vinci")
da_vinci.data.artist.name     # => "Leonardo da Vinci"
da_vinci.data.artist.birthday # => "1452/04/15"
```

You can also use JSON instead of YAML. See [example fixtures](https://github.com/yuki24/artemis/tree/master/spec/fixtures/responses)
and [test cases](https://github.com/yuki24/artemis/blob/master/spec/test_helper_spec.rb#L16-L51).

### MiniTest

Setting up the test helper with Artemis is very easy and simple. Just add the following code to the
`test/test_helper.rb` in your app:

```ruby
# spec/test_helper.rb
require 'artemis/test_helper'

class ActiveSupport::TestCase
  setup do
    graphql_requests.clear
    graphql_responses.clear
  end
end
```

### RSpec

Artemis also comes with a script that wires up helper methods on Rspec. Because it is more common to use the `spec/`
directory to organize spec files in RSpec, the `config.artemis.fixture_path` config needs to point to
`spec/fixtures/graphql`. Other than that, it is very straightforward to set it up:

```ruby
# config/application.rb
config.artemis.fixture_path = 'spec/fixtures/graphql'
```

```ruby
# Add this to your spec/rails_helper.rb or spec_helper.rb if you don't have rails_helper.rb
require 'artemis/rspec'
```

## Development

After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).

## Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/yuki24/artemis. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.

## License

The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).

## Code of Conduct

Everyone interacting in the Artemis project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/artemis/blob/master/CODE_OF_CONDUCT.md).

[curb]: https://rubygems.org/gems/curb
[curl]: https://curl.haxx.se/docs/http2.html
[nghttp]: https://nghttp2.org/
[nhp]: https://rubygems.org/gems/net-http-persistent


================================================
FILE: Rakefile
================================================
require "bundler/gem_tasks"
require "rake/testtask"

TESTS_IN_ISOLATION = ['test/railtie_test.rb', 'test/rake_tasks_test.rb']

Rake::TestTask.new(:test) do |t|
  t.libs << "test"
  t.test_files = FileList['test/**/*_test.rb'] - TESTS_IN_ISOLATION
  t.warning    = false
end

Rake::TestTask.new('test:isolated') do |t|
  t.libs << "test"
  t.test_files = TESTS_IN_ISOLATION
  t.warning    = false
end

task default: ['test', 'test:isolated']


================================================
FILE: artemis.gemspec
================================================

lib = File.expand_path("../lib", __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require "artemis/version"

Gem::Specification.new do |spec|
  spec.name          = "artemis"
  spec.version       = Artemis::VERSION
  spec.authors       = ["Jon Allured", "Yuki Nishijima"]
  spec.email         = ["jon.allured@gmail.com", "yk.nishijima@gmail.com"]
  spec.summary       = %q{GraphQL on Rails}
  spec.description   = %q{GraphQL client on Rails + Convention over Configuration = ❤️}
  spec.homepage      = "https://github.com/yuki24/artemis"
  spec.license       = "MIT"
  spec.files         = `git ls-files -z`.split("\x0").reject {|f| f.match(%r{^(test)/}) }
  spec.require_paths = ["lib"]

  spec.add_dependency "activesupport", ">= 4.0.0"
  spec.add_dependency "railties", ">= 4.0.0"
  spec.add_dependency "graphql"
  spec.add_dependency "graphql-client", ">= 0.13.0"

  spec.add_development_dependency "appraisal", ">= 2.2"
  spec.add_development_dependency "bundler", ">= 1.16"
  spec.add_development_dependency "net-http-persistent", ">= 3.0"
  spec.add_development_dependency "rake", ">= 10.0"
end


================================================
FILE: bin/console
================================================
#!/usr/bin/env ruby

require "bundler/setup"
require "artemis"

Artemis::Client.query_paths = [File.join(__dir__, '../spec/fixtures')]
Artemis::GraphQLEndpoint.register!(:github, adapter: :test, url: '', schema_path: 'spec/fixtures/github/schema.json')
Artemis::GraphQLEndpoint.lookup(:github).load_schema!
require_relative '../spec/fixtures/github'

require "pry"
Pry.start


================================================
FILE: bin/setup
================================================
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
set -vx

bundle install

# Do any other automated setup that you need to do here


================================================
FILE: gemfiles/.bundle/config
================================================
---
BUNDLE_RETRY: "1"


================================================
FILE: gemfiles/graphql_2_0.gemfile
================================================
# This file was generated by Appraisal

source "https://rubygems.org"

gem "bigdecimal"
gem "drb"
gem "irb"
gem "logger"
gem "mutex_m"
gem "reline"
gem "curb", ">= 0.9.6"
gem "minitest"
gem "webrick"
gem "rails", "~> 7.1.0"
gem "railties", "~> 7.1.0"
gem "activesupport", "~> 7.1.0"
gem "rackup"
gem "graphql", "< 2.1"

gemspec path: "../"


================================================
FILE: gemfiles/rails_70.gemfile
================================================
# This file was generated by Appraisal

source "https://rubygems.org"

gem "bigdecimal"
gem "drb"
gem "irb"
gem "logger"
gem "mutex_m"
gem "reline"
gem "curb", ">= 0.9.6"
gem "minitest"
gem "webrick"
gem "rails", "~> 7.0.0"
gem "railties", "~> 7.0.0"
gem "activesupport", "~> 7.0.0"

gemspec path: "../"


================================================
FILE: gemfiles/rails_71.gemfile
================================================
# This file was generated by Appraisal

source "https://rubygems.org"

gem "bigdecimal"
gem "drb"
gem "irb"
gem "logger"
gem "mutex_m"
gem "reline"
gem "curb", ">= 0.9.6"
gem "minitest"
gem "webrick"
gem "rails", "~> 7.1.0"
gem "railties", "~> 7.1.0"
gem "activesupport", "~> 7.1.0"
gem "rackup"

gemspec path: "../"


================================================
FILE: gemfiles/rails_72.gemfile
================================================
# This file was generated by Appraisal

source "https://rubygems.org"

gem "bigdecimal"
gem "drb"
gem "irb"
gem "logger"
gem "mutex_m"
gem "reline"
gem "curb", ">= 0.9.6"
gem "minitest"
gem "webrick"
gem "rails", "~> 7.2.0"
gem "railties", "~> 7.2.0"
gem "activesupport", "~> 7.2.0"
gem "rackup"

gemspec path: "../"


================================================
FILE: gemfiles/rails_80.gemfile
================================================
# This file was generated by Appraisal

source "https://rubygems.org"

gem "bigdecimal"
gem "drb"
gem "irb"
gem "logger"
gem "mutex_m"
gem "reline"
gem "curb", ">= 0.9.6"
gem "minitest"
gem "webrick"
gem "rails", "~> 8.0.0"
gem "railties", "~> 8.0.0"
gem "activesupport", "~> 8.0.0"
gem "rackup"

gemspec path: "../"


================================================
FILE: gemfiles/rails_81.gemfile
================================================
# This file was generated by Appraisal

source "https://rubygems.org"

gem "pry"
gem "pry-byebug", platforms: :mri
gem "curb", ">= 0.9.6"
gem "webrick"
gem "minitest", "< 5.25.0"
gem "rails", "~> 8.1.0"
gem "railties", "~> 8.1.0"
gem "activesupport", "~> 8.1.0"
gem "rackup"

gemspec path: "../"


================================================
FILE: gemfiles/rails_edge.gemfile
================================================
# This file was generated by Appraisal

source "https://rubygems.org"

git "https://github.com/rails/rails.git" do
  gem "rails"
  gem "railties"
  gem "activesupport"
end

gem "bigdecimal"
gem "drb"
gem "irb"
gem "logger"
gem "mutex_m"
gem "reline"
gem "curb", ">= 0.9.6"
gem "minitest"
gem "webrick"
gem "rackup"

gemspec path: "../"


================================================
FILE: lib/artemis/adapters/abstract_adapter.rb
================================================
# frozen_string_literal: true

require 'active_support/core_ext/object/blank'
require 'graphql/client/http'

module Artemis
  module Adapters
    class AbstractAdapter < ::GraphQL::Client::HTTP
      attr_reader :service_name, :timeout, :pool_size

      EMPTY_HEADERS = {}.freeze

      DEFAULT_HEADERS = {
        "Accept" => "application/json",
        "Content-Type" => "application/json"
      }.freeze

      def initialize(uri, service_name: , timeout: , pool_size: , adapter_options: {})
        raise ArgumentError, "url is required (given `#{uri.inspect}')" if uri.blank?

        super(uri) # Do not pass in the block to avoid getting #headers and #connection overridden.

        @service_name = service_name.to_s
        @timeout      = timeout
        @pool_size    = pool_size
      end

      # Public: Extension point for subclasses to set custom request headers.
      #
      # Returns Hash of String header names and values.
      def headers(_context)
        _context[:headers] || EMPTY_HEADERS
      end

      # Public: Make an HTTP request for GraphQL query.
      #
      # A subclass that inherits from +AbstractAdapter+ can override this method if it needs more flexibility for how
      # it makes a request.
      #
      # For more details, see +GraphQL::Client::HTTP#execute+.
      def execute(*)
        super
      end

      # Public: Extension point for subclasses to customize the Net:HTTP client
      #
      # A subclass that inherits from +AbstractAdapter+ should returns a Net::HTTP object or an object that responds
      # to +request+ that is given a Net::HTTP request object.
      def connection
        raise "AbstractAdapter is an abstract class that can not be instantiated!"
      end
    end
  end
end


================================================
FILE: lib/artemis/adapters/curb_adapter.rb
================================================
# frozen_string_literal: true

require 'delegate'
require 'json'

require 'curb'

require 'artemis/adapters/abstract_adapter'
require 'artemis/exceptions'

module Artemis
  module Adapters
    class CurbAdapter < AbstractAdapter
      attr_reader :multi

      def initialize(uri, service_name: , timeout: , pool_size: , adapter_options: {})
        super

        @multi = Curl::Multi.new
        @multi.pipeline = Curl::CURLPIPE_MULTIPLEX if defined?(Curl::CURLPIPE_MULTIPLEX)
      end

      def multiplex(queries, context: {})
        make_request({ _json: queries }, context)
      end

      def execute(document:, operation_name: nil, variables: {}, context: {})
        body = {}
        body["query"] = document.to_query_string
        body["variables"] = variables if variables.any?
        body["operationName"] = operation_name if operation_name

        make_request(body, context)
      end

      private

      def make_request(body, context)
        easy = Curl::Easy.new(uri.to_s)

        easy.timeout   = timeout
        easy.multi     = multi
        easy.headers   = DEFAULT_HEADERS.merge(headers(context))
        easy.post_body = JSON.generate(body)

        if defined?(Curl::CURLPIPE_MULTIPLEX)
          # This ensures libcurl waits for the connection to reveal if it is
          # possible to pipeline/multiplex on before it continues.
          easy.setopt(Curl::CURLOPT_PIPEWAIT, 1)
          easy.version = Curl::HTTP_2_0
        end

        easy.http_post

        case easy.response_code
        when 200, 400
          JSON.parse(easy.body)
        when 500..599
          raise Artemis::GraphQLServerError, "Received server error status #{easy.response_code}: #{easy.body}"
        else
          { "errors" => [{ "message" => "#{easy.response_code} #{easy.body}" }] }
        end
      end
    end
  end
end


================================================
FILE: lib/artemis/adapters/multi_domain_adapter.rb
================================================
# frozen_string_literal: true

require 'artemis/adapters/abstract_adapter'

module Artemis
  module Adapters
    class MultiDomainAdapter < AbstractAdapter
      attr_reader :adapter

      def initialize(_uri, service_name: , timeout: , pool_size: , adapter_options: {})
        if adapter_options[:adapter] == :multi_domain
          raise ArgumentError, "You can not use the :multi_domain adapter with the :multi_domain adapter."
        end

        @connection_by_url    = {}
        @service_name         = service_name.to_s
        @timeout              = timeout
        @pool_size            = pool_size
        @adapter              = adapter_options[:adapter] || :net_http
        @mutex_for_connection = Mutex.new
      end

      def multiplex(queries, context: {})
        url = context[:url]

        if url.nil?
          raise ArgumentError, 'The MultiDomain adapter requires a url on every request. Please specify a url with a context: ' \
                               'Client.multiplex(url: "https://awesomeshop.domain.conm") { ... }'
        end

        connection_for_url(url).multiplex(queries, context: context)
      end

      # Makes an HTTP request for GraphQL query.
      def execute(document:, operation_name: nil, variables: {}, context: {})
        url = context[:url]

        if url.nil?
          raise ArgumentError, 'The MultiDomain adapter requires a url on every request. Please specify a url with a context: ' \
                               'Client.with_context(url: "https://awesomeshop.domain.conm")'
        end

        connection_for_url(url).execute(document: document, operation_name: operation_name, variables: variables, context: context)
      end

      def connection
        raise NotImplementedError, "Calling the #connection method without a URI is not supported. Please use the " \
                                   "#connection_for_url(uri) instead."
      end

      def connection_for_url(url)
        @connection_by_url[url.to_s] || @mutex_for_connection.synchronize do
          @connection_by_url[url.to_s] ||= ::Artemis::Adapters.lookup(adapter).new(url, service_name: service_name, timeout: timeout, pool_size: pool_size)
        end
      end
    end
  end
end


================================================
FILE: lib/artemis/adapters/net_http_adapter.rb
================================================
# frozen_string_literal: true

require 'json'
require 'net/http'

require 'artemis/adapters/abstract_adapter'
require 'artemis/exceptions'

module Artemis
  module Adapters
    class NetHttpAdapter < AbstractAdapter
      def multiplex(queries, context: {})
        make_request({ _json: queries }, context)
      end

      # Makes an HTTP request for GraphQL query.
      def execute(document:, operation_name: nil, variables: {}, context: {})
        body = {}
        body["query"] = document.to_query_string
        body["variables"] = variables if variables.any?
        body["operationName"] = operation_name if operation_name

        make_request(body, context)
      end

      # Returns a fresh Net::HTTP object that creates a new connection.
      def connection
        Net::HTTP.new(uri.host, uri.port).tap do |client|
          client.use_ssl       = uri.scheme == "https"
          client.open_timeout  = timeout
          client.read_timeout  = timeout
          client.write_timeout = timeout if client.respond_to?(:write_timeout=)
        end
      end

      private

      def make_request(body, context)
        request = Net::HTTP::Post.new(uri.request_uri)
        request.basic_auth(uri.user, uri.password) if uri.user || uri.password
        request.body = JSON.generate(body)

        DEFAULT_HEADERS.merge(headers(context)).each { |name, value| request[name] = value }

        response = connection.request(request)

        case response.code.to_i
        when 200, 400
          JSON.parse(response.body)
        when 500..599
          raise Artemis::GraphQLServerError, "Received server error status #{response.code}: #{response.body}"
        else
          { "errors" => [{ "message" => "#{response.code} #{response.message}" }] }
        end
      end
    end
  end
end


================================================
FILE: lib/artemis/adapters/net_http_persistent_adapter.rb
================================================
# frozen_string_literal: true

require 'delegate'

begin
  require "active_support/isolated_execution_state"
rescue LoadError
  # no-op... Rails 7.0 requires this.
end

begin
  require 'active_support/deprecation'
  require 'active_support/deprecator'
rescue LoadError
end

require 'active_support/core_ext/numeric/time'
require 'net/http/persistent'

require 'artemis/adapters/net_http_adapter'

module Artemis
  module Adapters
    class NetHttpPersistentAdapter < NetHttpAdapter
      attr_reader :_connection, :raw_connection

      def initialize(uri, service_name: , timeout: , pool_size: , adapter_options: {})
        super

        @raw_connection = Net::HTTP::Persistent.new(name: service_name, pool_size: pool_size)
        @raw_connection.open_timeout = timeout
        @raw_connection.read_timeout = timeout
        @raw_connection.idle_timeout = 30.minutes.to_i # TODO: Make it configurable

        @_connection = ConnectionWrapper.new(@raw_connection, uri)
      end

      # Public: Extension point for subclasses to customize the Net:HTTP client
      #
      # Returns a Net::HTTP object
      def connection
        _connection
      end

      class ConnectionWrapper < SimpleDelegator #:nodoc:
        def initialize(obj, url)
          super(obj)

          @url = url
        end

        def request(req)
          __getobj__.request(@url, req)
        end
      end

      private_constant :ConnectionWrapper
    end
  end
end


================================================
FILE: lib/artemis/adapters/test_adapter.rb
================================================
# frozen_string_literal: true

require 'active_support/core_ext/module/attribute_accessors'

module Artemis
  module Adapters
    class TestAdapter
      cattr_accessor :requests
      self.requests = []

      cattr_accessor :responses
      self.responses = []

      Request = Struct.new(:document, :operation_name, :variables, :context)
      Multiplex = Struct.new(:queries, :context)

      private_constant :Request, :Multiplex

      def initialize(*)
      end

      def multiplex(queries, context: {})
        self.requests << Multiplex.new(queries, context)

        queries.map do |query|
          result = responses.detect do |mock|
            query[:operationName] == mock.operation_name && (mock.arguments == :__unspecified__ || query[:variables] == mock.arguments)
          end

          result&.data || fake_response
        end
      end

      def execute(**arguments)
        self.requests << Request.new(*arguments.values_at(:document, :operation_name, :variables, :context))

        response = responses.detect do |mock|
          arguments[:operation_name] == mock.operation_name && (mock.arguments == :__unspecified__ || arguments[:variables] == mock.arguments)
        end

        response&.data || fake_response
      end

      private

      def fake_response
        {
          'data' => { 'test' => 'data' },
          'errors' => [],
          'extensions' => {}
        }
      end
    end
  end
end


================================================
FILE: lib/artemis/adapters.rb
================================================
# frozen-string-literal: true

require 'active_support/dependencies/autoload'

module Artemis
  module Adapters
    extend ActiveSupport::Autoload

    autoload :CurbAdapter
    autoload :MultiDomainAdapter
    autoload :NetHttpAdapter
    autoload :NetHttpPersistentAdapter
    autoload :TestAdapter

    class << self
      ##
      # Returns the constant for the specified adapter name.
      #
      #   Artemis::Adapters.lookup(:net_http)
      #   # => Artemis::Adapters::NetHttpAdapter
      def lookup(name)
        const_get("#{name.to_s.camelize}Adapter")
      end
    end
  end
end


================================================
FILE: lib/artemis/client.rb
================================================
# frozen_string_literal: true

require 'delegate'
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/hash/deep_merge'
require 'active_support/core_ext/module/delegation'
require 'active_support/core_ext/module/attribute_accessors'
require 'active_support/core_ext/string/inflections'

require 'artemis/graphql_endpoint'
require 'artemis/exceptions'

module Artemis
  class Client
    # The paths in which the Artemis client looks for files that have the +.graphql+ extension.
    # In a rails app, this value will be set to +["app/operations"]+ by Artemis' +Artemis::Railtie+.
    cattr_accessor :query_paths

    # Returns a plain +GraphQL::Client+ object. For more details please refer to the official documentation for
    # {the +graphql-client+ gem}[https://github.com/github/graphql-client].
    attr_reader :client

    # Default context that is appended to every GraphQL request for the client.
    class_attribute :default_context, default: {}

    # List of before callbacks that get invoked in every +execute+ call.
    #
    # @api private
    class_attribute :before_callbacks, default: []
    # List of after callbacks that get invoked in every +execute+ call.
    #
    # @api private
    class_attribute :after_callbacks, default: []

    # Creates a new instance of the GraphQL client for the service.
    #
    #   # app/operations/github/user.graphql
    #   query($id: String!) {
    #     user(login: $id) {
    #       name
    #     }
    #   }
    #
    #   # app/operations/github.rb
    #   class Github < Artemis::Client
    #   end
    #
    #   github = Github.new
    #   github.user(id: 'yuki24').data.user.name # => "Yuki Nishijima"
    #
    #   github = Github.new(context: { headers: { Authorization: "bearer ..." } })
    #   github.user(id: 'yuki24').data.user.name # => "Yuki Nishijima"
    #
    def initialize(context = {})
      @client = self.class.instantiate_client(context)
    end

    class << self
      # Creates a new instance of the GraphQL client for the service.
      #
      #   # app/operations/github/user.graphql
      #   query($id: String!) {
      #     user(login: $id) {
      #       name
      #     }
      #   }
      #
      #   # app/operations/github.rb
      #   class Github < Artemis::Client
      #   end
      #
      #   github = Github.new
      #   github.user(id: 'yuki24').data.user.name # => "Yuki Nishijima"
      #
      #   github = Github.new(context: { headers: { Authorization: "bearer ..." } })
      #   github.user(id: 'yuki24').data.user.name # => "Yuki Nishijima"
      #
      alias with_context new

      # Returns the registered meta data (generally present in +config/graphql.yml+) for the client.
      #
      #   # config/graphql.yml
      #   development:
      #     github:
      #       url:     https://api.github.com/graphql
      #       adapter: :net_http
      #
      #   # app/operations/github.rb
      #   class Github < Artemis::Client
      #   end
      #
      #   Github.endpoint.url     # => "https://api.github.com/graphql"
      #   Github.endpoint.adapter # => :net_http
      #
      def endpoint
        Artemis::GraphQLEndpoint.lookup(name)
      end

      # Instantiates a new instance of +GraphQL::Client+ for the service.
      #
      #   # app/operations/github/user.graphql
      #   query($id: String!) {
      #     user(login: $id) {
      #       name
      #     }
      #   }
      #
      #   # app/operations/github.rb
      #   class Github < Artemis::Client
      #   end
      #
      #   client = Github.instantiate_client
      #   client.query(Github::User, arguments: { id: 'yuki24' }) # makes a Graphql request
      #
      #   client = Github.instantiate_client(context: { headers: { Authorization: "bearer ..." } })
      #   client.query(Github::User, arguments: { id: 'yuki24' }) # makes a Graphql request with Authorization header
      #
      def instantiate_client(context = {})
        ::GraphQL::Client.new(schema: endpoint.schema, execute: connection(context))
      end

      # Defines a callback that will get called right before the
      # client's execute method is executed.
      #
      #   class Github < Artemis::Client
      #
      #     before_execute do |document, operation_name, variables, context|
      #       Analytics.log(operation_name, variables, context[:user_id])
      #     end
      #
      #     ...
      #   end
      #
      def before_execute(&block)
        self.before_callbacks += [block]
      end

      # Defines a callback that will get called right after the
      # client's execute method has finished.
      #
      #   class Github < Artemis::Client
      #
      #     after_execute do |data, errors, extensions|
      #       if errors.present?
      #         Rails.logger.error(errors.to_json)
      #       end
      #     end
      #
      #     ...
      #   end
      #
      def after_execute(&block)
        self.after_callbacks += [block]
      end

      def resolve_graphql_file_path(filename, fragment: false)
        filename = filename.to_s.underscore

        graphql_file_paths.detect do |path|
          path.end_with?("#{namespace}/#{filename}.graphql") || (fragment && path.end_with?("#{namespace}/_#{filename}.graphql"))
        end
      end

      def graphql_file_paths
        @graphql_file_paths ||= query_paths.flat_map {|path| Dir["#{path}/#{namespace}/*.graphql"] }
      end

      def namespace
        name.underscore
      end

      def preload!
        graphql_file_paths.each do |path|
          load_constant(File.basename(path, File.extname(path)).camelize)
        end
      end

      # Looks up the GraphQL file that matches the given +const_name+ and sets it to a constant.
      #
      #   # app/operations/github.rb
      #   class Github < Artemis::Client
      #   end
      #
      #   defined?(Github::User)      # => nil
      #   Github.load_constant(:User) # => loads an operation definition from app/operations/github/user.graphql
      #   defined?(Github::User)      # => 'constant'
      #
      #   Github.load_constant(:None) # => nil
      #
      def load_constant(const_name)
        graphql_file = resolve_graphql_file_path(const_name.to_s.underscore, fragment: true)

        if graphql_file
          graphql = File.open(graphql_file).read
          ast     = instantiate_client.parse(graphql)

          const_set(const_name, ast)
        end
      end
      alias load_query load_constant

      def connection(context = {})
        Executor.new(endpoint.connection, callbacks, default_context.deep_merge(context))
      end

      def execute(query, context: {}, **arguments)
        new(default_context).execute(query, context: context, **arguments)
      end

      def multiplex(**context, &block)
        queue            = MultiplexQueue.new
        wrapped_executor = Executor.new(queue, callbacks, default_context.deep_merge(context))
        api_client       = ::GraphQL::Client.new(schema: endpoint.schema, execute: wrapped_executor)

        service_client = new
        service_client.instance_variable_set(:@client, api_client)

        block.call(service_client)

        connection.multiplex(queue.queries, context: context)
      end

      private

      # Looks up the GraphQL file that matches the given +const_name+ and sets it to a constant. If the files it not
      # found it will raise an +NameError+.
      #
      #   # app/operations/github.rb
      #   class Github < Artemis::Client
      #   end
      #
      #   defined?(Github::User) # => nil
      #   Github::User           # => loads an operation definition from app/operations/github/user.graphql
      #   defined?(Github::User) # => 'constant'
      #
      #   Github::DoesNotExist   # => raises an NameError
      #
      # @api private
      def const_missing(const_name)
        load_constant(const_name) || super
      end

      # Delegates a class method call to an instance method call, which in turn looks up the GraphQL file that matches
      # the given +method_name+ and delegates the call to it.
      #
      #   # app/operations/github.rb
      #   class Github < Artemis::Client
      #   end
      #
      #   Github.user # => delegates to Github.new(default_context).user
      #
      # @api private
      def method_missing(method_name, **arguments, &block)
        if resolve_graphql_file_path(method_name)
          new(default_context).public_send(method_name, **arguments, &block)
        else
          super
        end
      end

      def respond_to_missing?(method_name, *_, &block) #:nodoc:
        resolve_graphql_file_path(method_name) || super
      end

      # Returns a +Callbacks+ collection object that implements the interface for the +Executor+ object.
      #
      # @api private
      def callbacks
        Callbacks.new(before_callbacks, after_callbacks)
      end
    end

    # Executes a given query, raises if we didn't define the operation
    #
    # @param [String] operation
    # @param [Hash] context
    # @param [Hash] arguments
    #
    # @return [GraphQL::Client::Response]
    def execute(query, context: {}, **arguments)
      if self.class.resolve_graphql_file_path(query)
        const_name = query.to_s.camelize

        # This check will be unnecessary once we drop support for Ruby 2.4 and earlier
        if !self.class.const_get(const_name).is_a?(GraphQL::Client::OperationDefinition)
          self.class.load_constant(const_name)
        end

        client.query(self.class.const_get(const_name), variables: arguments, context: context)
      else
        raise GraphQLFileNotFound.new("Query #{query}.graphql not found in: #{query_paths.join(", ")}")
      end
    end

    private

    # Delegates a method call to a GraphQL call.
    #
    #   # app/operations/github.rb
    #   class Github < Artemis::Client
    #   end
    #
    #   github = Github.new
    #   github.user # => delegates to app/operations/github/user.graphql
    #
    # @api private
    def method_missing(method_name, context: {}, **arguments)
      execute(method_name, context: context, **arguments)
    rescue GraphQLFileNotFound
      super
    end

    def respond_to_missing?(method_name, *_, &block) #:nodoc:
      self.class.resolve_graphql_file_path(method_name) || super
    end

    # Internal collection object that holds references to the callback blocks.
    #
    # @api private
    Callbacks = Struct.new(:before_callbacks, :after_callbacks) #:nodoc:

    # Wrapper object around the adapter that wires up callbacks.
    #
    # @api private
    class Executor < SimpleDelegator
      def initialize(connection, callbacks, default_context) #:nodoc:
        super(connection)

        @callbacks = callbacks
        @default_context = default_context
      end

      def execute(document:, operation_name: nil, variables: {}, context: {}) #:nodoc:
        _context = @default_context.deep_merge(context)

        @callbacks.before_callbacks.each do |callback|
          callback.call(document, operation_name, variables, _context)
        end

        response = __getobj__.execute(document: document, operation_name: operation_name, variables: variables, context: _context)

        @callbacks.after_callbacks.each do |callback|
          callback.call(response['data'], response['errors'], response['extensions'])
        end

        response
      end
    end

    class MultiplexQueue
      attr_reader :queries

      def initialize
        @queries = []
      end

      def execute(document:, operation_name: nil, variables: {}, context: {}) #:nodoc:
        @queries << {
          query: document.to_query_string,
          variables: variables.presence || {},
          operationName: operation_name,
          context: context
        }

        {}
      end
    end

    private_constant :Callbacks, :Executor, :MultiplexQueue
  end
end


================================================
FILE: lib/artemis/exceptions.rb
================================================
module Artemis
  class Error < StandardError
  end

  class EndpointNotFound < Error
  end

  class ConfigurationError < Error
  end

  class GraphQLFileNotFound < Error
  end

  class FixtureNotFound < Error
  end

  class GraphQLError < Error
  end

  class GraphQLServerError < GraphQLError
  end
end

================================================
FILE: lib/artemis/graphql_endpoint.rb
================================================
# frozen_string_literal: true

require 'active_support/core_ext/hash/deep_merge'
require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/class/attribute_accessors'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/string/inflections'
require 'graphql/client'

require 'artemis/adapters'
require 'artemis/exceptions'

module Artemis
  class GraphQLEndpoint

    # Whether or not to suppress warnings on schema load. Use it with caution.
    #
    # @private
    cattr_accessor :suppress_warnings_on_schema_load
    self.suppress_warnings_on_schema_load = false

    # Hash object that holds references to adapter instances.
    ENDPOINT_INSTANCES = {}

    private_constant :ENDPOINT_INSTANCES

    class << self
      ##
      # Provides an endpoint instance specified in the +configuration+. If the endpoint is not found in
      # +ENDPOINT_INSTANCES+, it'll raise an exception.
      def lookup(service_name)
        ENDPOINT_INSTANCES[service_name.to_s.underscore] || raise(Artemis::EndpointNotFound, "Service `#{service_name}' not registered.")
      end

      def register!(service_name, configurations)
        ENDPOINT_INSTANCES[service_name.to_s.underscore] = new(service_name.to_s, **configurations.symbolize_keys)
      end

      ##
      # Returns the registered services as an array.
      #
      def registered_services
        ENDPOINT_INSTANCES.keys
      end
    end

    attr_reader :name, :url, :adapter, :timeout, :schema_path, :pool_size, :adapter_options

    def initialize(name, url: nil, adapter: :net_http, timeout: 10, schema_path: nil, pool_size: 25, adapter_options: {})
      @name            = name.to_s
      @url             = url
      @adapter         = adapter
      @timeout         = timeout
      @schema_path     = schema_path
      @pool_size       = pool_size
      @adapter_options = adapter_options

      @mutex_for_schema     = Mutex.new
      @mutex_for_connection = Mutex.new
    end

    def schema
      org, $stderr = $stderr, File.new("/dev/null", "w") if self.class.suppress_warnings_on_schema_load

      @schema || @mutex_for_schema.synchronize do
        @schema ||= ::GraphQL::Client.load_schema(schema_path.presence || connection)
      end
    ensure
      $stderr = org if self.class.suppress_warnings_on_schema_load
    end
    alias load_schema! schema

    def connection
      @connection || @mutex_for_connection.synchronize do
        @connection ||= ::Artemis::Adapters.lookup(adapter).new(url, service_name: name, timeout: timeout, pool_size: pool_size, adapter_options: adapter_options)
      end
    end
  end
end


================================================
FILE: lib/artemis/railtie.rb
================================================
require 'active_support/file_update_checker'

module Artemis
  class Railtie < ::Rails::Railtie #:nodoc:
    config.artemis = ActiveSupport::OrderedOptions.new
    config.artemis.query_path         = "app/operations"
    config.artemis.fixture_path       = "test/fixtures/graphql"
    config.artemis.schema_path        = "vendor/graphql/schema"
    config.artemis.graphql_extentions = ["graphql"]

    initializer 'graphql.client.attach_log_subscriber' do
      if !defined?(GraphQL::Client::LogSubscriber)
        require "graphql/client/log_subscriber"
        GraphQL::Client::LogSubscriber.attach_to :graphql
      end
    end

    initializer 'graphql.client.set_query_paths' do |app|
      query_path = config.artemis.query_path

      app.paths.add query_path

      Artemis::Client.query_paths = app.paths[query_path].existent
    end

    initializer 'graphql.test_helper' do |app|
      if !Rails.env.production?
        require 'artemis/test_helper'
        Artemis::TestHelper.__graphql_fixture_path__ = app.root.join(config.artemis.fixture_path)
      end
    end

    initializer 'graphql.client.set_reloader', after: 'graphql.client.set_query_paths' do |app|
      not_on_zeitwerk = !defined?(Zeitwerk) || (app.config.respond_to?(:autoloader) && app.config.autoloader != :zeitwerk)

      if not_on_zeitwerk
        files_to_watch = Artemis::Client.query_paths.map {|path| [path, config.artemis.graphql_extentions] }.to_h

        app.reloaders << ActiveSupport::FileUpdateChecker.new([], files_to_watch) do
          Artemis.config_for_graphql(app).each_key do |endpoint_name|
            Artemis::Client.query_paths.each do |path|
              FileUtils.touch("#{path}/#{endpoint_name}.rb") if File.exist?("#{path}/#{endpoint_name}.rb")
            end
          end
        end
      end
    end

    initializer 'graphql.client.load_config' do |app|
      if Pathname.new("#{app.paths["config"].existent.first}/graphql.yml").exist?
        Artemis.config_for_graphql(app).each do |endpoint_name, options|
          Artemis::GraphQLEndpoint
            .register!(
              endpoint_name,
              schema_path: app.root.join(config.artemis.schema_path, "#{endpoint_name}.json").to_s,
              **options.symbolize_keys
            )
        end
      end
    end

    initializer 'graphql.client.preload', after: 'graphql.client.load_config' do |app|
      not_on_zeitwerk = !defined?(Zeitwerk) || (app.config.respond_to?(:autoloader) && app.config.autoloader != :zeitwerk)

      if app.config.eager_load && app.config.cache_classes && not_on_zeitwerk
        Artemis::GraphQLEndpoint.registered_services.each do |endpoint_name|
          endpoint_name.camelize.constantize.preload!
        end
      end
    end

    rake_tasks do
      load "tasks/artemis.rake"
    end
  end
end


================================================
FILE: lib/artemis/rspec.rb
================================================
require 'artemis/test_helper'

RSpec.configure do |config|
  config.include ::Artemis::TestHelper

  config.before :each do
    graphql_requests.clear
    graphql_responses.clear
  end
end


================================================
FILE: lib/artemis/test_helper.rb
================================================
# frozen_string_literal: true

require 'erb'
require 'yaml'

require 'active_support/core_ext/module/attribute_accessors'
require 'active_support/core_ext/string/inflections'

require 'artemis/exceptions'

module Artemis
  # TODO: Write documentation for +TestHelper+
  module TestHelper
    mattr_accessor :__graphql_fixture_path__

    # Creates an object that stubs a GraphQL request for the given +service+. No mock response is registered until the
    # +to_return+ method.
    #
    #   # test/fixtures/graphql/metaphysics/artist.yml
    #   leonardo_da_vinci:
    #     data:
    #       artist:
    #         name: Leonardo da Vinci
    #         birthday: 1452/04/15
    #
    #   # In a test:
    #   stub_graphql(Metaphysics, :artist).to_return(:leonardo_da_vinci)
    #
    #   response = Metaphysics.artist(id: "leonardo-da-vinci")
    #
    #   response.data.artist.name     # => "Leonardo da Vinci"
    #   response.data.artist.birthday # => "1452/04/15"
    #
    # Test responses could also be parameterized by specifying the +arguments+ argument for the query name.
    #
    #   stub_graphql(Metaphysics, :artist, id: "pablo-picasso").to_return(:pablo_picasso)
    #   stub_graphql(Metaphysics, :artist, id: "leonardo-da-vinci").to_return(:leonardo_da_vinci)
    #
    #   pablo_picasso = Metaphysics.artist(id: "pablo-picasso")
    #   da_vinci      = Metaphysics.artist(id: "leonardo-da-vinci")
    #
    #   pablo_picasso.data.artist.name # => "Pablo Picasso"
    #   da_vinci.data.artist.name      # => "Leonardo da Vinci"
    #
    def stub_graphql(service, query_name, arguments =  :__unspecified__)
      StubbingDSL.new(service.to_s, query_name, graphql_fixture_files, arguments)
    end

    # Returns out-going GraphQL requests.
    #
    def graphql_requests
      Artemis::Adapters::TestAdapter.requests
    end

    private

    def graphql_responses #:nodoc:
      Artemis::Adapters::TestAdapter.responses
    end

    def graphql_fixture_path #:nodoc:
      __graphql_fixture_path__ || raise(Artemis::ConfigurationError, "GraphQL fixture path is unset")
    end

    def graphql_fixture_files #:nodoc:
      @graphql_fixture_sets ||= Dir["#{graphql_fixture_path}/{**,*}/*.{yml,json}"]
                                  .uniq
                                  .select {|file| ::File.file?(file) }
                                  .map    {|file| GraphQLFixture.new(File.basename(file, File.extname(file)), file, read_erb_yaml(file)) }
    end

    def read_erb_yaml(path) #:nodoc:
      if YAML.method(:load).parameters.any? { |_arg_type, arg_name| arg_name == :aliases }
        YAML.load(ERB.new(File.read(path)).result, aliases: true)
      else
        YAML.load(ERB.new(File.read(path)).result)
      end
    end

    class StubbingDSL #:nodoc:
      attr_reader :service_name, :query_name, :fixture_sets, :arguments

      def initialize(service_name, query_name, fixture_sets, arguments) #:nodoc:
        @service_name, @query_name, @fixture_sets, @arguments = service_name, query_name, fixture_sets, arguments
      end

      def get(fixture_key)
        fixture_set = find_fixture_set
        fixture     = fixture_set.data[fixture_key.to_s]

        if fixture.nil?
          raise Artemis::FixtureNotFound, "Fixture `#{fixture_key}' not found in #{fixture_set.path}"
        end

        fixture
      end

      def to_return(fixture_key) #:nodoc:
        fixture_set = find_fixture_set
        fixture     = fixture_set.data[fixture_key.to_s]

        if fixture.nil?
          raise Artemis::FixtureNotFound, "Fixture `#{fixture_key}' not found in #{fixture_set.path}"
        end

        Artemis::Adapters::TestAdapter.responses <<
          TestResponse.new(
            "#{service_name}__#{fixture_set.name.to_s.camelcase}",
            arguments.respond_to?(:deep_stringify_keys) ? arguments.deep_stringify_keys : arguments,
            fixture
          )
      end

      private

      def find_fixture_set
        fixture_set = fixture_sets
                        .detect { |fixture| %r{#{service_name.underscore}/#{query_name}\.(yml|json)\z} =~ fixture.path }
        fixture_set ||= fixture_sets.detect { |fixture| fixture.name == query_name.to_s }

        if fixture_set.nil?
          raise Artemis::FixtureNotFound, "Fixture file `#{query_name}.{yml,json}' not found"
        end

        fixture_set
      end
    end

    TestResponse      = Struct.new(:operation_name, :arguments, :data) #:nodoc:
    GraphQLFixture    = Struct.new(:name, :path, :data) #:nodoc

    private_constant :GraphQLFixture, :StubbingDSL, :TestResponse
  end
end


================================================
FILE: lib/artemis/version.rb
================================================
module Artemis
  VERSION = "1.1.0"
end


================================================
FILE: lib/artemis.rb
================================================
require "artemis/version"
require "artemis/client"
require "artemis/railtie" if defined?(Rails)

module Artemis
  def self.config_for_graphql(app)
    if app.respond_to?(:config_for)
      app.config_for(:graphql)
    else
      config_for(:graphql, app: app)
    end
  end

  # backported from https://github.com/rails/rails/blob/b9ca94ca/railties/lib/rails/application.rb#L226
  # TODO: Remove once dropping Rails <= 4.1 support
  def self.config_for(name, app:, env: Rails.env)
    if name.is_a?(Pathname)
      yaml = name
    else
      yaml = Pathname.new("#{app.paths["config"].existent.first}/#{name}.yml")
    end

    if yaml.exist?
      require "erb"
      (YAML.load(ERB.new(yaml.read).result) || {})[env] || {}
    else
      raise "Could not load configuration. No such file - #{yaml}"
    end
  rescue Psych::SyntaxError => e
    raise "YAML syntax error occurred while parsing #{yaml}. " \
      "Please note that YAML must be consistently indented using spaces. Tabs are not allowed. " \
      "Error: #{e.message}"
  end
end


================================================
FILE: lib/generators/artemis/install/USAGE
================================================
Description:
    Generates a client stub and config files and downloads the schema from the given endpoint.

Example:
    rails generate artemis:install Artsy https://metaphysics-production.artsy.net

    This will create:
        app/operations/artsy.rb
        app/operations/artsy/.gitkeep
        config/graphql.yml
        vendor/graphql/schema/artsy.json


================================================
FILE: lib/generators/artemis/install/install_generator.rb
================================================
require 'graphql/client'
require 'graphql/client/http'

class Artemis::InstallGenerator < Rails::Generators::NamedBase
  source_root File.expand_path('../templates', __FILE__)

  argument :endpoint_url, type: :string, banner: "The endpoint URL for a GraphQL service"

  class_option :authorization, type: :string, default: nil, aliases: "-A"

  def generate_client
    template "client.rb", client_file_name
    create_file query_dir_gitkeep, ""
  end

  def generate_config
    in_root do
      if behavior == :invoke && !File.exist?(config_file_name)
        template "graphql.yml", config_file_name
      else
        inject_into_file config_file_name, <<-YAML, after: "development:\n"
  #{file_name}:
    <<: *default
    url: #{endpoint_url}\n
YAML

        inject_into_file config_file_name, <<-YAML, after: "test:\n", force: true
  #{file_name}:
    <<: *default
    url: #{endpoint_url}\n
YAML

        inject_into_file config_file_name, <<-YAML, after: "production:\n", force: true
  #{file_name}:
    <<: *default
    url: #{endpoint_url}\n
YAML
      end
    end
  end

  def download_schema
    say "      downloading GraphQL schema from #{endpoint_url}..."

    if options['authorization'].present?
      rake "graphql:schema:update SERVICE=#{file_name} AUTHORIZATION='#{options['authorization']}'"
    else
      rake "graphql:schema:update SERVICE=#{file_name}"
    end
  end

  private

  def file_name # :doc:
    @_file_name ||= super.underscore
  end

  def client_file_name
    if respond_to?(:mountable_engine?) && mountable_engine?
      "app/operations/#{namespaced_path}/#{file_name}.rb"
    else
      "app/operations/#{file_name}.rb"
    end
  end

  def query_dir_gitkeep
    if respond_to?(:mountable_engine?) && mountable_engine?
      "app/operations/#{namespaced_path}/#{file_name}/.gitkeep"
    else
      "app/operations/#{file_name}/.gitkeep"
    end
  end

  def config_file_name
    "config/graphql.yml"
  end
end


================================================
FILE: lib/generators/artemis/install/templates/client.rb
================================================
<% module_namespacing do -%>
class <%= class_name %> < Artemis::Client
  # If an access token needs to be assigned to every request:
  # self.default_context = {
  #   headers: {
  #     Authorization: "token ..."
  #   }
  # }
end
<% end %>

================================================
FILE: lib/generators/artemis/install/templates/graphql.yml
================================================
default: &default
  # The underlying client library that actually makes an HTTP request.
  # Available adapters are :net_http, :net_http_persistent, :curb, and :test.
  #
  # It is set to :net_http by default.
  adapter: :net_http

  # HTTP timeout set for the adapter in seconds. This will be set to both
  # `read_timeout` and `write_timeout` and there is no way to configure them
  # with a different value as of writing (PRs welcome!)
  #
  # It is set to 10 by default.
  timeout: 10

  # The number of keep-alive connections. The `:net_http` adapter will ignore
  # this option.
  #
  # It is set to 25 by default.
  pool_size: 25

development:
  <%= file_name %>:
    <<: *default
    url: <%= endpoint_url %>

test:
  <%= file_name %>:
    <<: *default
    url: <%= endpoint_url %>

production:
  <%= file_name %>:
    <<: *default
    url: <%= endpoint_url %>


================================================
FILE: lib/generators/artemis/mutation/USAGE
================================================
Description:
    Generates a mutation stub.

    rails g artemis:mutation MUTATION_TYPE [FILE_NAME]

Example:
    rails g artemis:mutation followArtist

    This will create:
        app/operations/artsy.rb
        app/operations/artsy/follow_artist.graphql

    The GraphQL file name could be specified by giving the third argument:

      rails g artemis:mutation followArtist follow_artist_on_artwork

    This will create:
        app/operations/artsy.rb
        app/operations/artsy/follow_artist_on_artwork.graphql

    If there are multiple services registered, the service could be specified with +--service+ option:

       rails g artemis:mutation createProject --service github

    This will create:
        app/operations/github.rb
        app/operations/github/create_project.graphql

================================================
FILE: lib/generators/artemis/mutation/mutation_generator.rb
================================================
# frozen_string_literal: true

require 'graphql/schema/finder'

class Artemis::MutationGenerator < Rails::Generators::Base
  source_root File.expand_path('../templates', __FILE__)

  argument :mutation_type,     type: :string, required: true,                banner: "Mutation type"
  argument :graphql_file_name, type: :string, required: false, default: nil, banner: "The name of the GraphQL file to be generated"

  class_option :service, type: :string, default: nil, aliases: "-A"

  def generate_mutation_file
    template "mutation.graphql", graphql_file_path
  end

  private

  def mutation_name
    mutation_type.underscore
  end

  def graphql_file_path
    "app/operations/#{service_name.underscore}/#{graphql_file_name.presence || mutation_name}.graphql"
  end

  def arguments
    target_mutation.arguments
  end

  def target_mutation
    schema.find("Mutation").fields[mutation_type] ||
      raise(GraphQL::Schema::Finder::MemberNotFoundError, "Could not find type `#{mutation_type}` in schema.")
  end

  def schema
    service_name.camelize.constantize.endpoint.schema
  end

  def service_name
    options['service'].presence || begin
      services = Artemis::GraphQLEndpoint.registered_services

      if services.size == 1
        services.first
      else
        fail "Please specify a service name (available services: #{services.join(", ")}):\n\n" \
             "  rails g artemis:mutation #{mutation_type} #{graphql_file_name} --service SERVICE"
      end
    end
  end
end


================================================
FILE: lib/generators/artemis/mutation/templates/mutation.graphql
================================================
mutation<%= arguments.present? && "(#{ arguments.map {|name, type| "$#{name}: #{type.type.to_type_signature}" }.join(", ") })" %> {
  <%= target_mutation.name %><%= arguments.present? && "(#{ arguments.map {|name, type| "#{name}: $#{name}" }.join(", ") })" %> {
    # Add fields here...
  }
}

================================================
FILE: lib/generators/artemis/query/USAGE
================================================
Description:
    Generates a query stub.

    rails g artemis:query QUERY_TYPE [FILE_NAME]

Example:
    rails g artemis:query artist

    This will create:
        app/operations/artsy.rb
        app/operations/artsy/artist.graphql

    The GraphQL file name could be specified by giving the third argument:

      rails g artemis:query artist artist_in_tooltip

    This will create:
        app/operations/artsy.rb
        app/operations/artsy/artist_in_tooltip.graphql

    If there are multiple services registered, the service could be specified with +--service+ option:

       rails g artemis:query repository --service github

    This will create:
        app/operations/github.rb
        app/operations/github/repository.graphql

================================================
FILE: lib/generators/artemis/query/query_generator.rb
================================================
# frozen_string_literal: true

require 'graphql/schema/finder'

class Artemis::QueryGenerator < Rails::Generators::Base
  source_root File.expand_path('../templates', __FILE__)

  argument :query_type,        type: :string, required: true,                banner: "Query type"
  argument :graphql_file_name, type: :string, required: false, default: nil, banner: "The name of the GraphQL file to be generated"

  class_option :service, type: :string, default: nil, aliases: "-A"

  def generate_query_file
    template "query.graphql", graphql_file_path
  end

  # def generate_text_fixture_file
  #   template "fixture.yml", text_fixture_path
  # end

  private

  def query_name
    query_type.underscore
  end

  def graphql_file_path
    "app/operations/#{service_name.underscore}/#{qualified_name}.graphql"
  end

  def text_fixture_path
    File.join(Artemis::Railtie.config.artemis.fixture_path, service_name.underscore, "#{qualified_name}.yml")
  end

  def arguments
    target_query.arguments
  end

  def target_query
    schema.query.fields[query_type] ||
      raise(GraphQL::Schema::Finder::MemberNotFoundError, "Could not find type `#{query_type}` in schema.")
  end

  def schema
    service_name.camelize.constantize.endpoint.schema
  end

  def service_name
    options['service'].presence || begin
      services = Artemis::GraphQLEndpoint.registered_services

      if services.size == 1
        services.first
      else
        fail "Please specify a service name (available services: #{services.join(", ")}):\n\n" \
             "  rails g artemis:query #{query_type} #{graphql_file_name} --service SERVICE"
      end
    end
  end

  def qualified_name
    graphql_file_name.presence || query_name
  end
end


================================================
FILE: lib/generators/artemis/query/templates/fixture.yml
================================================
# You can stub GraphQL queries by calling the `stub_graphql' method in test:
#
#   stub_graphql(<%= service_name.camelize %>, :<%= qualified_name.underscore %>).to_return(:<%= target_query.name %>_1)
#
# Or with a arguments matcher:
#
#   stub_graphql(<%= service_name.camelize %>, :<%= qualified_name.underscore %>, <%= arguments.map {|name, _| "#{name}: \"...\"" }.join(", ") %>).to_return(:<%= target_query.name %>_2)
#

<%= target_query.name %>_1:
  data:
  <% target_query.type.fields.values.each do |field| -%>
  <%= field.name %>: # type: <%= field.type.to_type_signature %>
  <% end %>
<%= target_query.name %>_2:
  data:
  <% target_query.type.fields.values.each do |field| -%>
  <%= field.name %>: # type: <%= field.type.to_type_signature %>
  <% end %>


================================================
FILE: lib/generators/artemis/query/templates/query.graphql
================================================
query<%= arguments.present? ? "(#{ arguments.map {|name, type| "$#{name}: #{type.type.to_type_signature}" }.join(", ") })" : "" %> {
  <%= target_query.name %><%= arguments.present? ? "(#{ arguments.map {|name, type| "#{name}: $#{name}" }.join(", ") })" : "" %> {
    # Add fields here...
  }
}

================================================
FILE: lib/tasks/artemis.rake
================================================
# frozen_string_literal: true

require 'json'

require 'active_support/core_ext/string/inflections'
require 'graphql/client'

namespace :graphql do
  namespace :schema do
    desc "Downloads and saves the GraphQL schema (options: SERVICE=service_name AUTHORIZATION='token ...')"
    task update: :environment do
      service = if ENV['SERVICE']
                  ENV['SERVICE']
                else
                  services = Artemis.config_for_graphql(Rails.application).keys

                  if services.size == 1
                    services.first
                  else
                    raise "Please specify a service name (available services: #{services.join(", ")}): rake graphql:schema:update SERVICE=service"
                  end
                end

      headers          = ENV['AUTHORIZATION'] ? { Authorization: ENV['AUTHORIZATION'] } : {}
      service_class    = service.to_s.camelize.constantize
      schema_path      = service_class.endpoint.schema_path
      schema           = service_class.connection
                           .execute(
                             document: GraphQL::Client::IntrospectionDocument,
                             operation_name: "IntrospectionQuery",
                             variables: {},
                             context: { headers: headers }
                           ).to_h

      if schema['errors'].nil? || schema['errors'].empty?
        FileUtils.mkdir_p(File.dirname(schema_path))
        File.open(schema_path, 'w') do |file|
          file.write(JSON.pretty_generate(schema))
        end

        puts "saved schema to: #{schema_path.gsub("#{Dir.pwd}/", '')}"
      else
        raise "received error from server: #{schema}\n\n"
      end
    end
  end
end


================================================
FILE: test/adapters_test.rb
================================================
require_relative 'helpers/test_helper'

require 'artemis/adapters/abstract_adapter'

class AdaptersTest < ActiveSupport::TestCase
  Artemis::Adapters::AbstractAdapter.send(:attr_writer, :uri, :timeout)

  test "NetHttpAdapter behaves like an adapter" do
    adapter = Artemis::Adapters::NetHttpAdapter.new("http://localhost:8000", service_name: nil, timeout: 0.5, pool_size: 5)

    assert_adapter adapter, Net::ReadTimeout
  end

  # Using Net::HTTP::Persistent some times gets the CI build stuck, so avoiding it in CI for now."
  # unless ENV["CI"]
  #   test "NetHttpPersistentAdapter behaves like an adapter" do
  #     adapter = Artemis::Adapters::NetHttpPersistentAdapter.new("http://localhost:8000", service_name: nil, timeout: 0.5, pool_size: 5)
  #
  #     assert_adapter adapter, Net::ReadTimeout
  #   ensure
  #     # Make sure the connection is closed otherwise the webrick server wouldn't be able to shut down.
  #     adapter.instance_variable_get(:@raw_connection)&.shutdown
  #   end
  # end

  # if RUBY_ENGINE == 'ruby'
  #   test "CurbAdapter behaves like an adapter" do
  #     adapter = Artemis::Adapters::CurbAdapter.new("http://localhost:8000", service_name: nil, timeout: 2, pool_size: 5)
  #
  #     assert_adapter adapter, Curl::Err::TimeoutError
  #   end
  # end

  test 'MultiDomainAdapter makes an actual HTTP request' do
    adapter = Artemis::Adapters::MultiDomainAdapter.new('ignored', service_name: nil, timeout: 0.5, pool_size: 5, adapter_options: { adapter: :net_http })

    response = adapter.execute(document: GraphQL::Client::IntrospectionDocument, context: { url: "http://localhost:8000/test_multi_domain" })

    assert_equal "Endpoint switched.", response['data']['body']
    assert_equal [], response['errors']
    assert_equal({}, response['extensions'])
  end

  test 'MultiDomainAdapter can make a multiplex (the graphql feature, not HTTP/2) request' do
    adapter = Artemis::Adapters::MultiDomainAdapter.new('ignored', service_name: nil, timeout: 0.5, pool_size: 5, adapter_options: { adapter: :net_http })

    response = adapter.multiplex(
      [
        {
          query: GraphQL::Client::IntrospectionDocument.to_query_string,
          operationName: 'IntrospectionQuery',
          variables: {
            id: 'yayoi-kusama'
          },
        },
      ],
      context: {
        url: "http://localhost:8000/test_multi_domain"
      }
    )

    assert_equal "Endpoint switched.", response['data']['body']
    assert_equal [], response['errors']
    assert_equal({}, response['extensions'])
  end

  test 'MultiDomainAdapter can make a multiplex request with custom HTTP headers' do
    adapter = Artemis::Adapters::MultiDomainAdapter.new('ignored', service_name: nil, timeout: 0.5, pool_size: 5, adapter_options: { adapter: :net_http })

    response = adapter.multiplex(
      [
        {
          query: GraphQL::Client::IntrospectionDocument.to_query_string,
          operationName: 'IntrospectionQuery',
        },
      ],
      context: {
        headers: {
          Authorization: "Token token",
        },
        url: "http://localhost:8000/test_multi_domain"
      }
    )

    assert_equal "token token", response['data']['headers']['AUTHORIZATION']
  end

  test 'MultiDomainAdapter raises an error when adapter_options.adapter is set to :multi domain' do
    assert_raises(ArgumentError, "You can not use the :multi_domain adapter with the :multi_domain adapter.") do
      Artemis::Adapters::MultiDomainAdapter.new('ignored', service_name: nil, timeout: 0.5, pool_size: 5, adapter_options: { adapter: :multi_domain })
    end
  end

  test 'MultiDomainAdapter raises an error when context.url is not specified' do
    adapter = Artemis::Adapters::MultiDomainAdapter.new('ignored', service_name: nil, timeout: 0.5, pool_size: 5, adapter_options: { adapter: :net_http })
    message = 'The MultiDomain adapter requires a url on every request. Please specify a ' \
              'url with a context: Client.with_context(url: "https://awesomeshop.domain.conm")'

    assert_raises(ArgumentError, message) do
      adapter.execute(document: GraphQL::Client::IntrospectionDocument)
    end
  end

  test 'MultiDomainAdapter raises an error when it receives a server error' do
    adapter = Artemis::Adapters::MultiDomainAdapter.new('ignored', service_name: nil, timeout: 0.5, pool_size: 5, adapter_options: { adapter: :net_http })

    assert_raises(Artemis::GraphQLServerError, "Received server error status 500: Server error") do
      adapter.execute(document: GraphQL::Client::IntrospectionDocument, context: { url: "http://localhost:8000/500" })
    end
  end

  test 'MultiDomainAdapter allows for overriding timeout' do
    adapter = Artemis::Adapters::MultiDomainAdapter.new('ignored', service_name: nil, timeout: 0.5, pool_size: 5, adapter_options: { adapter: :net_http })

    assert_raises(Net::ReadTimeout) do
      adapter.execute(document: GraphQL::Client::IntrospectionDocument, context: { url: "http://localhost:8000/slow_server" })
    end
  end

  private

  def assert_adapter(adapter, timeout_error)
    assert_adapter_initialization adapter
    assert_adapter_execution adapter
    assert_adapter_server_error adapter
    assert_adapter_timeout adapter, timeout_error
    # assert_adapter_multiplex adapter
    assert_adapter_multiplex_server_error adapter
    assert_adapter_multiplex_timeout adapter, timeout_error
  end

  def assert_adapter_initialization(adapter)
    assert_raises(ArgumentError, "url is required (given `nil`)") do
      adapter.class.new(nil, service_name: nil, timeout: 2, pool_size: 5)
    end
  end

  def assert_adapter_execution(adapter)
    response = adapter.execute(
      document: GraphQL::Client::IntrospectionDocument,
      operation_name: 'IntrospectionQuery',
      variables: { id: 'yayoi-kusama' },
      context: { user_id: 1 }
    )

    assert_equal GraphQL::Client::IntrospectionDocument.to_query_string, response['data']['body']['query']
    assert_equal({ 'id' => 'yayoi-kusama' }, response['data']['body']['variables'])
    assert_equal 'IntrospectionQuery', response['data']['body']['operationName']
    assert_equal 'application/json', response['data']['headers']['CONTENT_TYPE']
    assert_equal 'application/json', response['data']['headers']['ACCEPT']
    assert_equal [], response['errors']
    assert_equal({}, response['extensions'])
  end

  def assert_adapter_server_error(adapter)
    adapter.uri = URI.parse("http://localhost:8000/500")

    assert_raises(Artemis::GraphQLServerError, "Received server error status 500: Server error") do
      adapter.execute(document: GraphQL::Client::IntrospectionDocument, operation_name: 'IntrospectionQuery')
    end
  end

  def assert_adapter_timeout(adapter, timeout_error)
    adapter.uri = URI.parse("http://localhost:8000/slow_server")

    assert_raises(timeout_error) do
      adapter.execute(document: GraphQL::Client::IntrospectionDocument, operation_name: 'IntrospectionQuery')
    end
  end

  def assert_adapter_multiplex(adapter)
    response = adapter.multiplex(
      [
        {
          query: GraphQL::Client::IntrospectionDocument.to_query_string,
          operationName: 'IntrospectionQuery',
          variables: {
            id: 'yayoi-kusama'
          },
        },
      ],
      context: {
        user_id: 1
      }
    )

    introspection_query = response[0]

    assert_equal GraphQL::Client::IntrospectionDocument.to_query_string, introspection_query['data']['body']['query']
    assert_equal({ 'id' => 'yayoi-kusama' }, introspection_query['data']['body']['variables'])
    assert_equal 'IntrospectionQuery', introspection_query['data']['body']['operationName']
    assert_equal 'application/json', introspection_query['data']['headers']['CONTENT_TYPE']
    assert_equal 'application/json', introspection_query['data']['headers']['ACCEPT']
    assert_equal [], introspection_query['errors']
    assert_equal({}, introspection_query['extensions'])
  end

  def assert_adapter_multiplex_server_error(adapter)
    adapter.uri = URI.parse("http://localhost:8000/500")

    assert_raises(Artemis::GraphQLServerError, "Received server error status 500: Server error") do
      adapter.multiplex([])
    end
  end

  def assert_adapter_multiplex_timeout(adapter, timeout_error)
    adapter.uri = URI.parse("http://localhost:8000/slow_server")

    assert_raises(timeout_error) do
      adapter.multiplex([])
    end
  end
end


================================================
FILE: test/autoloading_test.rb
================================================
require_relative 'helpers/test_helper'

class AutoLoadingTest < ActiveSupport::TestCase
  test ".load_constant loads the specified constant if there is a matching graphql file" do
    Github.send(:remove_const, :User) if Github.constants.include?(:User)

    Github.load_constant(:User)

    assert_equal 'constant', defined?(Github::User)
  end

  test ".load_constant does nothing and returns nil if there is no matching file" do
    assert_nil Github.load_constant(:DoesNotExist)
  end

  test ".preload! preloads all the graphQL files in the query paths" do
    %i(User UserRepositories Repository RepositoryFields)
      .select {|const_name| Github.constants.include?(const_name) }
      .each {|const_name| Github.send(:remove_const, const_name) }

    Github.preload!

    assert_equal 'constant', defined?(Github::User)
    assert_equal 'constant', defined?(Github::Repository)
  end

  test "dynamically loads the matching GraphQL query and sets it to a constant" do
    Github.send(:remove_const, :User) if Github.constants.include?(:User)

    query = Github::User

    assert_equal <<~GRAPHQL.strip, query.document.to_query_string
      query Github__User {
        user(login: "yuki24") {
          id
          name
        }
      }
    GRAPHQL
  end

  test "dynamically loads the matching GraphQL fragment and sets it to a constant" do
    Github.send(:remove_const, :RepositoryFields) if Github.constants.include?(:RepositoryFields)

    query = Github::RepositoryFields

    assert_equal <<~GRAPHQL.strip, query.document.to_query_string
      fragment Github__RepositoryFields on Repository {
        name
        nameWithOwner
        url
        updatedAt
        languages(first: 1) {
          nodes {
            name
            color
          }
        }
      }
    GRAPHQL
  end

  test "correctly loads the matching GraphQL query even when the top-level constant with the same name exists" do
    # In Ruby <= 2.4 top-level constants can be looked up through a namespace, which turned out to be a bad practice.
    # This has been removed in 2.5, but in earlier versions still suffer from this behaviour.
    Github.send(:remove_const, :User) if Github.constants.include?(:User)
    Object.send(:remove_const, :User) if Object.constants.include?(:User)

    begin
      Object.send(:const_set, :User, 1)

      Github.user
    ensure
      Object.send(:remove_const, :User)
    end

    query = Github::User

    assert_equal <<~GRAPHQL.strip, query.document.to_query_string
      query Github__User {
        user(login: "yuki24") {
          id
          name
        }
      }
    GRAPHQL
  end

  test "raises an exception when the path was resolved but the file does not exist" do
    begin
      Github.graphql_file_paths << "github/removed.graphql"

      assert_raises(Errno::ENOENT) { Github::Removed }
    ensure
      Github.graphql_file_paths.delete("github/removed.graphql")
    end
  end

  test "raises an NameError when there is no graphql file that matches the const name" do
    assert_raises(NameError) { Github::DoesNotExist }
  end

  test "defines the query method when the matching class method gets called for the first time" do
    skip
    Github.undef_method(:user) if Github.public_instance_methods.include?(:user)

    Github.user

    expect(Github.public_instance_methods).to include(:user)
  end

  test "raises an NameError when there is no graphql file that matches the class method name" do
    assert_raises(NameError) { Github.does_not_exist }
  end

  test "raises an NameError when the class method name matches a fragment name" do
    assert_raises(NameError) { Github.repository_fields_fragment }
  end

  test "responds to a class method that has a matching graphQL file" do
    assert_respond_to Github, :user
  end

  test "does not respond to class methods that do not have a matching graphQL file" do
    assert_not_respond_to Github, :does_not_exist
  end

  test "defines the query method when the matching instance method gets called for the first time" do
    skip
    Github.undef_method(:user) if Github.public_instance_methods.include?(:user)

    Github.new.user

    expect(Github.public_instance_methods).to include(:user)
  end

  test "raises an NameError when there is no graphql file that matches the instance method name" do
    assert_raises(NameError) { Github.new.does_not_exist }
  end

  test "raises an NameError when the instance method name matches a fragment name" do
    assert_raises(NameError) { Github.new.repository_fields_fragment }
  end

  test "responds to the method that has a matching graphQL file" do
    assert_respond_to Github.new, :user
  end

  test "does not respond to methods that do not have a matching graphQL file" do
    assert_not_respond_to Github.new, :does_not_exist
  end
end

================================================
FILE: test/backport/method_call_assertions.rb
================================================
require "minitest/mock"

module MethodCallAssertions # :nodoc:
  private
  def assert_called(object, method_name, message = nil, times: 1, returns: nil)
    times_called = 0

    object.stub(method_name, proc { times_called += 1; returns }) { yield }

    error = "Expected #{method_name} to be called #{times} times, " \
      "but was called #{times_called} times"
    error = "#{message}.\n#{error}" if message
    assert_equal times, times_called, error
  end

  def assert_called_with(object, method_name, args = [], returns: nil)
    mock = Minitest::Mock.new

    if args.all? { |arg| arg.is_a?(Array) }
      args.each { |arg| mock.expect(:call, returns, arg) }
    else
      mock.expect(:call, returns, args)
    end

    object.stub(method_name, mock) { yield }

    mock.verify
  end

  def assert_not_called(object, method_name, message = nil, &block)
    assert_called(object, method_name, message, times: 0, &block)
  end

  def stub_any_instance(klass, instance: klass.new)
    klass.stub(:new, instance) { yield instance }
  end
end


================================================
FILE: test/callbacks_test.rb
================================================
require_relative 'helpers/test_helper'

require 'active_support/core_ext/module/attribute_accessors'

class CallbacksTest < ActiveSupport::TestCase
  Client = Class.new(Artemis::Client) do
    def self.name
      'Github'
    end

    mattr_accessor :before_callback, :after_callback
    self.before_callback = nil
    self.after_callback = nil

    before_execute do |document, operation_name, variables, context|
      self.before_callback = document, operation_name, variables, context
    end

    after_execute do |data, errors, extensions|
      self.after_callback = data, errors, extensions
    end
  end

  Spotify = Class.new(Artemis::Client) do
    def self.name
      'Spotify'
    end

    before_execute do
      raise "this callback should not get invoked"
    end

    after_execute do
      raise "this callback should not get invoked"
    end
  end

  test ".before_execute gets invoked before executing" do
    Client.repository(owner: "yuki24", name: "artemis", context: { user_id: 'yuki24' })

    document, operation_name, variables, context = Client.before_callback

    assert_equal Client::Repository.document, document
    assert_equal 'CallbacksTest__Client__Repository', operation_name
    assert_equal({ "name" => "artemis", "owner" => "yuki24" }, variables)
    assert_equal({ user_id: 'yuki24' }, context)
  end

  test ".after_execute gets invoked after executing" do
    Client.user

    data, errors, extensions = Client.after_callback

    assert_equal({ "test" => "data" }, data)
    assert_equal [], errors
    assert_equal({}, extensions)
  end
end


================================================
FILE: test/client_test.rb
================================================
require 'helpers/test_helper'

class ClientTest < ActiveSupport::TestCase
  setup do
    requests.clear
  end

  test ".lookup_graphql_file returns the path to the matching graph file" do
    assert_equal "#{PROJECT_DIR}/test/fixtures/github/user.graphql", Github.resolve_graphql_file_path("user")
  end

  test ".lookup_graphql_file returns nil if the file is missing" do
    assert_nil Github.resolve_graphql_file_path("does_not_exist")
  end

  test ".graphql_file_paths returns a list of GraphQL files (*.graphql) in the query_paths" do
    Github.instance_variable_set :@graphql_file_paths, nil
    original = Github.query_paths

    Github.query_paths = [File.join(PROJECT_DIR, 'tmp')]

    begin
      FileUtils.mkdir "./tmp/github" if !Dir.exist?("./tmp/github")

      with_files "./tmp/github/text.txt", "./tmp/github/sale.graphql" do
        assert_equal ["#{PROJECT_DIR}/tmp/github/sale.graphql"], Github.graphql_file_paths
      end
    ensure
      Github.instance_variable_set :@graphql_file_paths, nil
      Github.query_paths = original
    end
  end

  test "can make a GraphQL request without variables" do
    Github.user

    request = requests[0]

    assert_equal 'Github__User', request.operation_name
    assert_empty request.variables
    assert_equal({}, request.context)
    assert_equal <<~GRAPHQL.strip, request.document.to_query_string
      query Github__User {
        user(login: "yuki24") {
          id
          name
        }
      }
    GRAPHQL
  end

  test "can make a GraphQL request with variables" do
    Github.repository(owner: "yuki24", name: "artemis")

    request = requests[0]

    assert_equal 'Github__Repository', request.operation_name
    assert_equal({ "owner" => "yuki24", "name" => "artemis" }, request.variables)
    assert_equal({}, request.context)
    assert_equal <<~GRAPHQL.strip, request.document.to_query_string
      query Github__Repository($owner: String!, $name: String!) {
        repository(owner: $owner, name: $name) {
          name
          nameWithOwner
        }
      }
    GRAPHQL
  end

  test "can make a GraphQL request with a query that contains fragments" do
    Github.user_repositories(login: "yuki24", size: 10)

    request = requests[0]

    assert_equal 'Github__UserRepositories', request.operation_name
    assert_equal({ 'login' => 'yuki24', 'size' => 10 }, request.variables)
    assert_equal({}, request.context)
    assert_equal <<~GRAPHQL.strip, request.document.to_query_string
      query Github__UserRepositories($login: String!, $size: Int!) {
        user(login: $login) {
          id
          name
          repositories(first: $size) {
            nodes {
              name
              description
              ...Github__RepositoryFields
            }
          }
        }
      }

      fragment Github__RepositoryFields on Repository {
        name
        nameWithOwner
        url
        updatedAt
        languages(first: 1) {
          nodes {
            name
            color
          }
        }
      }
    GRAPHQL
  end

  test "can make a GraphQL request with #execute" do
    Github.execute(:repository, owner: "yuki24", name: "artemis")

    request = requests[0]

    assert_equal 'Github__Repository', request.operation_name
    assert_equal({ "owner" => "yuki24", "name" => "artemis" }, request.variables)
    assert_equal({}, request.context)
    assert_equal <<~GRAPHQL.strip, request.document.to_query_string
      query Github__Repository($owner: String!, $name: String!) {
        repository(owner: $owner, name: $name) {
          name
          nameWithOwner
        }
      }
    GRAPHQL
  end

  test "raises an error when the specified graphql file does not exist" do
    assert_raises Artemis::GraphQLFileNotFound, match: /Query does_not_exist\.graphql not found/ do
      Github.execute(:does_not_exist)
    end
  end

  test "assigns context to the request when provided as an argument" do
    context = { headers: { Authorization: 'bearer ...' } }

    Github.repository(owner: "yuki24", name: "artemis", context: context)

    assert_equal context, requests[0].context
  end

  test "can create a client that always assigns the provided context to the request" do
    context   = { headers: { Authorization: 'bearer ...' } }
    client    = Github.with_context(context)

    client.repository(owner: "yuki24", name: "artemis")
    client.repository(owner: "yuki24", name: "artemis")

    assert_equal context, requests[0].context
    assert_equal context, requests[1].context
  end

  test "assigns the default context to a GraphQL request if present" do
    begin
      Github.default_context = { headers: { Authorization: 'bearer ...' } }
      Github.repository(owner: "yuki24", name: "artemis")

      assert_equal({ headers: { Authorization: 'bearer ...' } }, requests[0].context)
    ensure
      Github.default_context = { }
    end
  end

  test "can make a GraphQL request with all of .default_context, with_context(...) and the :context argument" do
    begin
      Github.default_context = { headers: { 'User-Agent': 'Artemis', 'X-key': 'value', Authorization: 'token ...' } }
      Github
          .with_context({ headers: { 'X-key': 'overridden' } })
          .repository(owner: "yuki24", name: "artemis", context: { headers: { Authorization: 'bearer ...' } })

      expected = {
        headers: {
          'User-Agent': 'Artemis',
          'X-key': 'overridden',
          Authorization: 'bearer ...',
        }
      }

      assert_equal expected, requests[0].context
    ensure
      Github.default_context = { }
    end
  end

  test "can batch multiple requests using Multiplex" do
    Github.multiplex do |queue|
      queue.repository(owner: "yuki24", name: "artemis", context: { headers: { Authorization: 'bearer ...' } })
      queue.user
    end

    repository_query, user_query = requests[0].queries

    assert_equal 'Github__Repository', repository_query[:operationName]
    assert_equal({ "owner" => "yuki24", "name" => "artemis" }, repository_query[:variables])
    assert_equal({ headers: { Authorization: 'bearer ...' } }, repository_query[:context])
    assert_equal <<~GRAPHQL.strip, repository_query[:query]
      query Github__Repository($owner: String!, $name: String!) {
        repository(owner: $owner, name: $name) {
          name
          nameWithOwner
        }
      }
    GRAPHQL

    assert_equal 'Github__User', user_query[:operationName]
    assert_empty user_query[:variables]
    assert_equal({}, user_query[:context])
    assert_equal <<~GRAPHQL.strip, user_query[:query]
      query Github__User {
        user(login: "yuki24") {
          id
          name
        }
      }
    GRAPHQL
  end

  private

  def requests
    Artemis::Adapters::TestAdapter.requests
  end

  def with_files(*files)
    files.each {|file| FileUtils.touch(file) }
    yield
  ensure
    files.each {|file| File.delete(file) }
  end
end


================================================
FILE: test/endpoint_test.rb
================================================
require_relative 'helpers/test_helper'

class GraphQLEndpointTest < ActiveSupport::TestCase
  teardown do
    Artemis::GraphQLEndpoint.const_get(:ENDPOINT_INSTANCES).delete("gitlab")
  end

  test ".lookup raises an exception when the service is missing" do
    assert_raises Artemis::EndpointNotFound do
      Artemis::GraphQLEndpoint.lookup(:does_not_exit)
    end
  end

  test "can register an endpoint" do
    endpoint = Artemis::GraphQLEndpoint.register!(:gitlab, url: "https://api.gitlab.com/graphql")

    assert_equal "https://api.gitlab.com/graphql", endpoint.url
    assert_instance_of Artemis::Adapters::NetHttpAdapter, endpoint.connection
  end

  test "can look up a registered endpoint" do
    Artemis::GraphQLEndpoint.register!(:gitlab, url: "https://api.gitlab.com/graphql")

    endpoint = Artemis::GraphQLEndpoint.lookup(:gitlab)

    assert_equal "https://api.gitlab.com/graphql", endpoint.url
    assert_instance_of Artemis::Adapters::NetHttpAdapter, endpoint.connection

    # FIXME: This #schema method makes a network call.
    # assert_equal ..., endpoint.schema
  end

  test "can register an endpoint with options" do
    options = {
      adapter: :test,
      timeout: 10,
      # schema_path: nil,
      pool_size: 25,
    }

    endpoint = Artemis::GraphQLEndpoint.register!(:gitlab, url: "https://api.gitlab.com/graphql", **options)

    assert_equal "https://api.gitlab.com/graphql", endpoint.url
    assert_equal 10, endpoint.timeout
    assert_equal 25, endpoint.pool_size
    assert_instance_of Artemis::Adapters::TestAdapter, endpoint.connection

    # FIXME: needs an example schema (and specify the :schema_path option) to test this.
    # assert_equal ..., endpoint.schema
  end
end

================================================
FILE: test/fixtures/github/_repository_fields.graphql
================================================
fragment on Repository {
  name
  nameWithOwner
  url
  updatedAt
  languages(first: 1) {
    nodes {
      name
      color
    }
  }
}


================================================
FILE: test/fixtures/github/repository.graphql
================================================
query($owner: String!, $name: String!) {
  repository(owner: $owner, name: $name) {
    name
    nameWithOwner
  }
}


================================================
FILE: test/fixtures/github/schema.json
================================================
{
  "data": {
    "__schema": {
      "queryType": {
        "name": "Query"
      },
      "mutationType": {
        "name": "Mutation"
      },
      "subscriptionType": null,
      "types": [
        {
          "kind": "INPUT_OBJECT",
          "name": "AbortQueuedMigrationsInput",
          "description": "Autogenerated input type of AbortQueuedMigrations",
          "fields": null,
          "inputFields": [
            {
              "name": "ownerId",
              "description": "The ID of the organization that is running the migrations.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "ID",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "defaultValue": null
            }
          ],
          "interfaces": null,
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "OBJECT",
          "name": "AbortQueuedMigrationsPayload",
          "description": "Autogenerated return type of AbortQueuedMigrations",
          "fields": [
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "success",
              "description": "Did the operation succeed?",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "Boolean",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "inputFields": null,
          "interfaces": [

          ],
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "INPUT_OBJECT",
          "name": "AcceptEnterpriseAdministratorInvitationInput",
          "description": "Autogenerated input type of AcceptEnterpriseAdministratorInvitation",
          "fields": null,
          "inputFields": [
            {
              "name": "invitationId",
              "description": "The id of the invitation being accepted",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "ID",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "defaultValue": null
            }
          ],
          "interfaces": null,
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "OBJECT",
          "name": "AcceptEnterpriseAdministratorInvitationPayload",
          "description": "Autogenerated return type of AcceptEnterpriseAdministratorInvitation",
          "fields": [
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "invitation",
              "description": "The invitation that was accepted.",
              "args": [

              ],
              "type": {
                "kind": "OBJECT",
                "name": "EnterpriseAdministratorInvitation",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "message",
              "description": "A message confirming the result of accepting an administrator invitation.",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "inputFields": null,
          "interfaces": [

          ],
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "INPUT_OBJECT",
          "name": "AcceptTopicSuggestionInput",
          "description": "Autogenerated input type of AcceptTopicSuggestion",
          "fields": null,
          "inputFields": [
            {
              "name": "repositoryId",
              "description": "The Node ID of the repository.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "ID",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "name",
              "description": "The name of the suggested topic.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "String",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "defaultValue": null
            }
          ],
          "interfaces": null,
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "OBJECT",
          "name": "AcceptTopicSuggestionPayload",
          "description": "Autogenerated return type of AcceptTopicSuggestion",
          "fields": [
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "topic",
              "description": "The accepted topic.",
              "args": [

              ],
              "type": {
                "kind": "OBJECT",
                "name": "Topic",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "inputFields": null,
          "interfaces": [

          ],
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "INTERFACE",
          "name": "Actor",
          "description": "Represents an object which can take actions on GitHub. Typically a User or Bot.",
          "fields": [
            {
              "name": "avatarUrl",
              "description": "A URL pointing to the actor's public avatar.",
              "args": [
                {
                  "name": "size",
                  "description": "The size of the resulting square image.",
                  "type": {
                    "kind": "SCALAR",
                    "name": "Int",
                    "ofType": null
                  },
                  "defaultValue": null
                }
              ],
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "URI",
                  "ofType": null
                }
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "login",
              "description": "The username of the actor.",
              "args": [

              ],
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "String",
                  "ofType": null
                }
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "resourcePath",
              "description": "The HTTP path for this actor.",
              "args": [

              ],
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "URI",
                  "ofType": null
                }
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "url",
              "description": "The HTTP URL for this actor.",
              "args": [

              ],
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "URI",
                  "ofType": null
                }
              },
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "inputFields": null,
          "interfaces": null,
          "enumValues": null,
          "possibleTypes": [
            {
              "kind": "OBJECT",
              "name": "Bot",
              "ofType": null
            },
            {
              "kind": "OBJECT",
              "name": "EnterpriseUserAccount",
              "ofType": null
            },
            {
              "kind": "OBJECT",
              "name": "Mannequin",
              "ofType": null
            },
            {
              "kind": "OBJECT",
              "name": "Organization",
              "ofType": null
            },
            {
              "kind": "OBJECT",
              "name": "User",
              "ofType": null
            }
          ]
        },
        {
          "kind": "OBJECT",
          "name": "ActorLocation",
          "description": "Location information for an actor",
          "fields": [
            {
              "name": "city",
              "description": "City",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "country",
              "description": "Country name",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "countryCode",
              "description": "Country code",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "region",
              "description": "Region name",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "regionCode",
              "description": "Region or state code",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "inputFields": null,
          "interfaces": [

          ],
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "ENUM",
          "name": "ActorType",
          "description": "The actor's type.",
          "fields": null,
          "inputFields": null,
          "interfaces": null,
          "enumValues": [
            {
              "name": "USER",
              "description": "Indicates a user actor.",
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "TEAM",
              "description": "Indicates a team actor.",
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "possibleTypes": null
        },
        {
          "kind": "INPUT_OBJECT",
          "name": "AddAssigneesToAssignableInput",
          "description": "Autogenerated input type of AddAssigneesToAssignable",
          "fields": null,
          "inputFields": [
            {
              "name": "assignableId",
              "description": "The id of the assignable object to add assignees to.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "ID",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "assigneeIds",
              "description": "The id of users to add as assignees.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "LIST",
                  "name": null,
                  "ofType": {
                    "kind": "NON_NULL",
                    "name": null,
                    "ofType": {
                      "kind": "SCALAR",
                      "name": "ID",
                      "ofType": null
                    }
                  }
                }
              },
              "defaultValue": null
            },
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "defaultValue": null
            }
          ],
          "interfaces": null,
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "OBJECT",
          "name": "AddAssigneesToAssignablePayload",
          "description": "Autogenerated return type of AddAssigneesToAssignable",
          "fields": [
            {
              "name": "assignable",
              "description": "The item that was assigned.",
              "args": [

              ],
              "type": {
                "kind": "INTERFACE",
                "name": "Assignable",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "inputFields": null,
          "interfaces": [

          ],
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "INPUT_OBJECT",
          "name": "AddCommentInput",
          "description": "Autogenerated input type of AddComment",
          "fields": null,
          "inputFields": [
            {
              "name": "subjectId",
              "description": "The Node ID of the subject to modify.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "ID",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "body",
              "description": "The contents of the comment.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "String",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "defaultValue": null
            }
          ],
          "interfaces": null,
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "OBJECT",
          "name": "AddCommentPayload",
          "description": "Autogenerated return type of AddComment",
          "fields": [
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "commentEdge",
              "description": "The edge from the subject's comment connection.",
              "args": [

              ],
              "type": {
                "kind": "OBJECT",
                "name": "IssueCommentEdge",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "subject",
              "description": "The subject",
              "args": [

              ],
              "type": {
                "kind": "INTERFACE",
                "name": "Node",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "timelineEdge",
              "description": "The edge from the subject's timeline connection.",
              "args": [

              ],
              "type": {
                "kind": "OBJECT",
                "name": "IssueTimelineItemEdge",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "inputFields": null,
          "interfaces": [

          ],
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "INPUT_OBJECT",
          "name": "AddDiscussionCommentInput",
          "description": "Autogenerated input type of AddDiscussionComment",
          "fields": null,
          "inputFields": [
            {
              "name": "discussionId",
              "description": "The Node ID of the discussion to comment on.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "ID",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "replyToId",
              "description": "The Node ID of the discussion comment within this discussion to reply to.",
              "type": {
                "kind": "SCALAR",
                "name": "ID",
                "ofType": null
              },
              "defaultValue": null
            },
            {
              "name": "body",
              "description": "The contents of the comment.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "String",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "defaultValue": null
            }
          ],
          "interfaces": null,
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "OBJECT",
          "name": "AddDiscussionCommentPayload",
          "description": "Autogenerated return type of AddDiscussionComment",
          "fields": [
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "comment",
              "description": "The newly created discussion comment.",
              "args": [

              ],
              "type": {
                "kind": "OBJECT",
                "name": "DiscussionComment",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "inputFields": null,
          "interfaces": [

          ],
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "INPUT_OBJECT",
          "name": "AddDiscussionPollVoteInput",
          "description": "Autogenerated input type of AddDiscussionPollVote",
          "fields": null,
          "inputFields": [
            {
              "name": "pollOptionId",
              "description": "The Node ID of the discussion poll option to vote for.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "ID",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "defaultValue": null
            }
          ],
          "interfaces": null,
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "OBJECT",
          "name": "AddDiscussionPollVotePayload",
          "description": "Autogenerated return type of AddDiscussionPollVote",
          "fields": [
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "pollOption",
              "description": "The poll option that a vote was added to.",
              "args": [

              ],
              "type": {
                "kind": "OBJECT",
                "name": "DiscussionPollOption",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "inputFields": null,
          "interfaces": [

          ],
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "INPUT_OBJECT",
          "name": "AddEnterpriseOrganizationMemberInput",
          "description": "Autogenerated input type of AddEnterpriseOrganizationMember",
          "fields": null,
          "inputFields": [
            {
              "name": "enterpriseId",
              "description": "The ID of the enterprise which owns the organization.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "ID",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "organizationId",
              "description": "The ID of the organization the users will be added to.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "ID",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "userIds",
              "description": "The IDs of the enterprise members to add.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "LIST",
                  "name": null,
                  "ofType": {
                    "kind": "NON_NULL",
                    "name": null,
                    "ofType": {
                      "kind": "SCALAR",
                      "name": "ID",
                      "ofType": null
                    }
                  }
                }
              },
              "defaultValue": null
            },
            {
              "name": "role",
              "description": "The role to assign the users in the organization",
              "type": {
                "kind": "ENUM",
                "name": "OrganizationMemberRole",
                "ofType": null
              },
              "defaultValue": null
            },
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "defaultValue": null
            }
          ],
          "interfaces": null,
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "OBJECT",
          "name": "AddEnterpriseOrganizationMemberPayload",
          "description": "Autogenerated return type of AddEnterpriseOrganizationMember",
          "fields": [
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "users",
              "description": "The users who were added to the organization.",
              "args": [

              ],
              "type": {
                "kind": "LIST",
                "name": null,
                "ofType": {
                  "kind": "NON_NULL",
                  "name": null,
                  "ofType": {
                    "kind": "OBJECT",
                    "name": "User",
                    "ofType": null
                  }
                }
              },
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "inputFields": null,
          "interfaces": [

          ],
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "INPUT_OBJECT",
          "name": "AddEnterpriseSupportEntitlementInput",
          "description": "Autogenerated input type of AddEnterpriseSupportEntitlement",
          "fields": null,
          "inputFields": [
            {
              "name": "enterpriseId",
              "description": "The ID of the Enterprise which the admin belongs to.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "ID",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "login",
              "description": "The login of a member who will receive the support entitlement.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "String",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "defaultValue": null
            }
          ],
          "interfaces": null,
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "OBJECT",
          "name": "AddEnterpriseSupportEntitlementPayload",
          "description": "Autogenerated return type of AddEnterpriseSupportEntitlement",
          "fields": [
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "message",
              "description": "A message confirming the result of adding the support entitlement.",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "inputFields": null,
          "interfaces": [

          ],
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "INPUT_OBJECT",
          "name": "AddLabelsToLabelableInput",
          "description": "Autogenerated input type of AddLabelsToLabelable",
          "fields": null,
          "inputFields": [
            {
              "name": "labelableId",
              "description": "The id of the labelable object to add labels to.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "ID",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "labelIds",
              "description": "The ids of the labels to add.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "LIST",
                  "name": null,
                  "ofType": {
                    "kind": "NON_NULL",
                    "name": null,
                    "ofType": {
                      "kind": "SCALAR",
                      "name": "ID",
                      "ofType": null
                    }
                  }
                }
              },
              "defaultValue": null
            },
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "defaultValue": null
            }
          ],
          "interfaces": null,
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "OBJECT",
          "name": "AddLabelsToLabelablePayload",
          "description": "Autogenerated return type of AddLabelsToLabelable",
          "fields": [
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "labelable",
              "description": "The item that was labeled.",
              "args": [

              ],
              "type": {
                "kind": "INTERFACE",
                "name": "Labelable",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "inputFields": null,
          "interfaces": [

          ],
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "INPUT_OBJECT",
          "name": "AddProjectCardInput",
          "description": "Autogenerated input type of AddProjectCard",
          "fields": null,
          "inputFields": [
            {
              "name": "projectColumnId",
              "description": "The Node ID of the ProjectColumn.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "ID",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "contentId",
              "description": "The content of the card. Must be a member of the ProjectCardItem union",
              "type": {
                "kind": "SCALAR",
                "name": "ID",
                "ofType": null
              },
              "defaultValue": null
            },
            {
              "name": "note",
              "description": "The note on the card.",
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "defaultValue": null
            },
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "defaultValue": null
            }
          ],
          "interfaces": null,
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "OBJECT",
          "name": "AddProjectCardPayload",
          "description": "Autogenerated return type of AddProjectCard",
          "fields": [
            {
              "name": "cardEdge",
              "description": "The edge from the ProjectColumn's card connection.",
              "args": [

              ],
              "type": {
                "kind": "OBJECT",
                "name": "ProjectCardEdge",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "projectColumn",
              "description": "The ProjectColumn",
              "args": [

              ],
              "type": {
                "kind": "OBJECT",
                "name": "ProjectColumn",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "inputFields": null,
          "interfaces": [

          ],
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "INPUT_OBJECT",
          "name": "AddProjectColumnInput",
          "description": "Autogenerated input type of AddProjectColumn",
          "fields": null,
          "inputFields": [
            {
              "name": "projectId",
              "description": "The Node ID of the project.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "ID",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "name",
              "description": "The name of the column.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "String",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "defaultValue": null
            }
          ],
          "interfaces": null,
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "OBJECT",
          "name": "AddProjectColumnPayload",
          "description": "Autogenerated return type of AddProjectColumn",
          "fields": [
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "columnEdge",
              "description": "The edge from the project's column connection.",
              "args": [

              ],
              "type": {
                "kind": "OBJECT",
                "name": "ProjectColumnEdge",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "project",
              "description": "The project",
              "args": [

              ],
              "type": {
                "kind": "OBJECT",
                "name": "Project",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "inputFields": null,
          "interfaces": [

          ],
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "INPUT_OBJECT",
          "name": "AddProjectV2DraftIssueInput",
          "description": "Autogenerated input type of AddProjectV2DraftIssue",
          "fields": null,
          "inputFields": [
            {
              "name": "projectId",
              "description": "The ID of the Project to add the draft issue to.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "ID",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "title",
              "description": "The title of the draft issue. A project item can also be created by providing the URL of an Issue or Pull Request if you have access.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "String",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "body",
              "description": "The body of the draft issue.",
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "defaultValue": null
            },
            {
              "name": "assigneeIds",
              "description": "The IDs of the assignees of the draft issue.",
              "type": {
                "kind": "LIST",
                "name": null,
                "ofType": {
                  "kind": "NON_NULL",
                  "name": null,
                  "ofType": {
                    "kind": "SCALAR",
                    "name": "ID",
                    "ofType": null
                  }
                }
              },
              "defaultValue": null
            },
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "defaultValue": null
            }
          ],
          "interfaces": null,
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "OBJECT",
          "name": "AddProjectV2DraftIssuePayload",
          "description": "Autogenerated return type of AddProjectV2DraftIssue",
          "fields": [
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "projectItem",
              "description": "The draft issue added to the project.",
              "args": [

              ],
              "type": {
                "kind": "OBJECT",
                "name": "ProjectV2Item",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "inputFields": null,
          "interfaces": [

          ],
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "INPUT_OBJECT",
          "name": "AddProjectV2ItemByIdInput",
          "description": "Autogenerated input type of AddProjectV2ItemById",
          "fields": null,
          "inputFields": [
            {
              "name": "projectId",
              "description": "The ID of the Project to add the item to.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "ID",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "contentId",
              "description": "The id of the Issue or Pull Request to add.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "ID",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "defaultValue": null
            }
          ],
          "interfaces": null,
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "OBJECT",
          "name": "AddProjectV2ItemByIdPayload",
          "description": "Autogenerated return type of AddProjectV2ItemById",
          "fields": [
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "item",
              "description": "The item added to the project.",
              "args": [

              ],
              "type": {
                "kind": "OBJECT",
                "name": "ProjectV2Item",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "inputFields": null,
          "interfaces": [

          ],
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "INPUT_OBJECT",
          "name": "AddPullRequestReviewCommentInput",
          "description": "Autogenerated input type of AddPullRequestReviewComment",
          "fields": null,
          "inputFields": [
            {
              "name": "pullRequestId",
              "description": "The node ID of the pull request reviewing\n\n**Upcoming Change on 2023-10-01 UTC**\n**Description:** `pullRequestId` will be removed. use addPullRequestReviewThread or addPullRequestReviewThreadReply instead\n**Reason:** We are deprecating the addPullRequestReviewComment mutation\n",
              "type": {
                "kind": "SCALAR",
                "name": "ID",
                "ofType": null
              },
              "defaultValue": null
            },
            {
              "name": "pullRequestReviewId",
              "description": "The Node ID of the review to modify.\n\n**Upcoming Change on 2023-10-01 UTC**\n**Description:** `pullRequestReviewId` will be removed. use addPullRequestReviewThread or addPullRequestReviewThreadReply instead\n**Reason:** We are deprecating the addPullRequestReviewComment mutation\n",
              "type": {
                "kind": "SCALAR",
                "name": "ID",
                "ofType": null
              },
              "defaultValue": null
            },
            {
              "name": "commitOID",
              "description": "The SHA of the commit to comment on.\n\n**Upcoming Change on 2023-10-01 UTC**\n**Description:** `commitOID` will be removed. use addPullRequestReviewThread or addPullRequestReviewThreadReply instead\n**Reason:** We are deprecating the addPullRequestReviewComment mutation\n",
              "type": {
                "kind": "SCALAR",
                "name": "GitObjectID",
                "ofType": null
              },
              "defaultValue": null
            },
            {
              "name": "body",
              "description": "The text of the comment. This field is required\n\n**Upcoming Change on 2023-10-01 UTC**\n**Description:** `body` will be removed. use addPullRequestReviewThread or addPullRequestReviewThreadReply instead\n**Reason:** We are deprecating the addPullRequestReviewComment mutation\n",
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "defaultValue": null
            },
            {
              "name": "path",
              "description": "The relative path of the file to comment on.\n\n**Upcoming Change on 2023-10-01 UTC**\n**Description:** `path` will be removed. use addPullRequestReviewThread or addPullRequestReviewThreadReply instead\n**Reason:** We are deprecating the addPullRequestReviewComment mutation\n",
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "defaultValue": null
            },
            {
              "name": "position",
              "description": "The line index in the diff to comment on.\n\n**Upcoming Change on 2023-10-01 UTC**\n**Description:** `position` will be removed. use addPullRequestReviewThread or addPullRequestReviewThreadReply instead\n**Reason:** We are deprecating the addPullRequestReviewComment mutation\n",
              "type": {
                "kind": "SCALAR",
                "name": "Int",
                "ofType": null
              },
              "defaultValue": null
            },
            {
              "name": "inReplyTo",
              "description": "The comment id to reply to.\n\n**Upcoming Change on 2023-10-01 UTC**\n**Description:** `inReplyTo` will be removed. use addPullRequestReviewThread or addPullRequestReviewThreadReply instead\n**Reason:** We are deprecating the addPullRequestReviewComment mutation\n",
              "type": {
                "kind": "SCALAR",
                "name": "ID",
                "ofType": null
              },
              "defaultValue": null
            },
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "defaultValue": null
            }
          ],
          "interfaces": null,
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "OBJECT",
          "name": "AddPullRequestReviewCommentPayload",
          "description": "Autogenerated return type of AddPullRequestReviewComment",
          "fields": [
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "comment",
              "description": "The newly created comment.",
              "args": [

              ],
              "type": {
                "kind": "OBJECT",
                "name": "PullRequestReviewComment",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "commentEdge",
              "description": "The edge from the review's comment connection.",
              "args": [

              ],
              "type": {
                "kind": "OBJECT",
                "name": "PullRequestReviewCommentEdge",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "inputFields": null,
          "interfaces": [

          ],
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "INPUT_OBJECT",
          "name": "AddPullRequestReviewInput",
          "description": "Autogenerated input type of AddPullRequestReview",
          "fields": null,
          "inputFields": [
            {
              "name": "pullRequestId",
              "description": "The Node ID of the pull request to modify.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "ID",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "commitOID",
              "description": "The commit OID the review pertains to.",
              "type": {
                "kind": "SCALAR",
                "name": "GitObjectID",
                "ofType": null
              },
              "defaultValue": null
            },
            {
              "name": "body",
              "description": "The contents of the review body comment.",
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "defaultValue": null
            },
            {
              "name": "event",
              "description": "The event to perform on the pull request review.",
              "type": {
                "kind": "ENUM",
                "name": "PullRequestReviewEvent",
                "ofType": null
              },
              "defaultValue": null
            },
            {
              "name": "comments",
              "description": "The review line comments.\n\n**Upcoming Change on 2023-10-01 UTC**\n**Description:** `comments` will be removed. use the `threads` argument instead\n**Reason:** We are deprecating comment fields that use diff-relative positioning\n",
              "type": {
                "kind": "LIST",
                "name": null,
                "ofType": {
                  "kind": "INPUT_OBJECT",
                  "name": "DraftPullRequestReviewComment",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "threads",
              "description": "The review line comment threads.",
              "type": {
                "kind": "LIST",
                "name": null,
                "ofType": {
                  "kind": "INPUT_OBJECT",
                  "name": "DraftPullRequestReviewThread",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "defaultValue": null
            }
          ],
          "interfaces": null,
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "OBJECT",
          "name": "AddPullRequestReviewPayload",
          "description": "Autogenerated return type of AddPullRequestReview",
          "fields": [
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "pullRequestReview",
              "description": "The newly created pull request review.",
              "args": [

              ],
              "type": {
                "kind": "OBJECT",
                "name": "PullRequestReview",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "reviewEdge",
              "description": "The edge from the pull request's review connection.",
              "args": [

              ],
              "type": {
                "kind": "OBJECT",
                "name": "PullRequestReviewEdge",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "inputFields": null,
          "interfaces": [

          ],
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "INPUT_OBJECT",
          "name": "AddPullRequestReviewThreadInput",
          "description": "Autogenerated input type of AddPullRequestReviewThread",
          "fields": null,
          "inputFields": [
            {
              "name": "path",
              "description": "Path to the file being commented on.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "String",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "body",
              "description": "Body of the thread's first comment.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "String",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "pullRequestId",
              "description": "The node ID of the pull request reviewing",
              "type": {
                "kind": "SCALAR",
                "name": "ID",
                "ofType": null
              },
              "defaultValue": null
            },
            {
              "name": "pullRequestReviewId",
              "description": "The Node ID of the review to modify.",
              "type": {
                "kind": "SCALAR",
                "name": "ID",
                "ofType": null
              },
              "defaultValue": null
            },
            {
              "name": "line",
              "description": "The line of the blob to which the thread refers, required for line-level threads. The end of the line range for multi-line comments.",
              "type": {
                "kind": "SCALAR",
                "name": "Int",
                "ofType": null
              },
              "defaultValue": null
            },
            {
              "name": "side",
              "description": "The side of the diff on which the line resides. For multi-line comments, this is the side for the end of the line range.",
              "type": {
                "kind": "ENUM",
                "name": "DiffSide",
                "ofType": null
              },
              "defaultValue": "RIGHT"
            },
            {
              "name": "startLine",
              "description": "The first line of the range to which the comment refers.",
              "type": {
                "kind": "SCALAR",
                "name": "Int",
                "ofType": null
              },
              "defaultValue": null
            },
            {
              "name": "startSide",
              "description": "The side of the diff on which the start line resides.",
              "type": {
                "kind": "ENUM",
                "name": "DiffSide",
                "ofType": null
              },
              "defaultValue": "RIGHT"
            },
            {
              "name": "subjectType",
              "description": "The level at which the comments in the corresponding thread are targeted, can be a diff line or a file",
              "type": {
                "kind": "ENUM",
                "name": "PullRequestReviewThreadSubjectType",
                "ofType": null
              },
              "defaultValue": "LINE"
            },
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "defaultValue": null
            }
          ],
          "interfaces": null,
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "OBJECT",
          "name": "AddPullRequestReviewThreadPayload",
          "description": "Autogenerated return type of AddPullRequestReviewThread",
          "fields": [
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "thread",
              "description": "The newly created thread.",
              "args": [

              ],
              "type": {
                "kind": "OBJECT",
                "name": "PullRequestReviewThread",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "inputFields": null,
          "interfaces": [

          ],
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "INPUT_OBJECT",
          "name": "AddPullRequestReviewThreadReplyInput",
          "description": "Autogenerated input type of AddPullRequestReviewThreadReply",
          "fields": null,
          "inputFields": [
            {
              "name": "pullRequestReviewId",
              "description": "The Node ID of the pending review to which the reply will belong.",
              "type": {
                "kind": "SCALAR",
                "name": "ID",
                "ofType": null
              },
              "defaultValue": null
            },
            {
              "name": "pullRequestReviewThreadId",
              "description": "The Node ID of the thread to which this reply is being written.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "ID",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "body",
              "description": "The text of the reply.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "String",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "defaultValue": null
            }
          ],
          "interfaces": null,
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "OBJECT",
          "name": "AddPullRequestReviewThreadReplyPayload",
          "description": "Autogenerated return type of AddPullRequestReviewThreadReply",
          "fields": [
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "comment",
              "description": "The newly created reply.",
              "args": [

              ],
              "type": {
                "kind": "OBJECT",
                "name": "PullRequestReviewComment",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "inputFields": null,
          "interfaces": [

          ],
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "INPUT_OBJECT",
          "name": "AddReactionInput",
          "description": "Autogenerated input type of AddReaction",
          "fields": null,
          "inputFields": [
            {
              "name": "subjectId",
              "description": "The Node ID of the subject to modify.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "ID",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "content",
              "description": "The name of the emoji to react with.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "ENUM",
                  "name": "ReactionContent",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "defaultValue": null
            }
          ],
          "interfaces": null,
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "OBJECT",
          "name": "AddReactionPayload",
          "description": "Autogenerated return type of AddReaction",
          "fields": [
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "reaction",
              "description": "The reaction object.",
              "args": [

              ],
              "type": {
                "kind": "OBJECT",
                "name": "Reaction",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "reactionGroups",
              "description": "The reaction groups for the subject.",
              "args": [

              ],
              "type": {
                "kind": "LIST",
                "name": null,
                "ofType": {
                  "kind": "NON_NULL",
                  "name": null,
                  "ofType": {
                    "kind": "OBJECT",
                    "name": "ReactionGroup",
                    "ofType": null
                  }
                }
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "subject",
              "description": "The reactable subject.",
              "args": [

              ],
              "type": {
                "kind": "INTERFACE",
                "name": "Reactable",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "inputFields": null,
          "interfaces": [

          ],
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "INPUT_OBJECT",
          "name": "AddStarInput",
          "description": "Autogenerated input type of AddStar",
          "fields": null,
          "inputFields": [
            {
              "name": "starrableId",
              "description": "The Starrable ID to star.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "ID",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "defaultValue": null
            }
          ],
          "interfaces": null,
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "OBJECT",
          "name": "AddStarPayload",
          "description": "Autogenerated return type of AddStar",
          "fields": [
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "starrable",
              "description": "The starrable.",
              "args": [

              ],
              "type": {
                "kind": "INTERFACE",
                "name": "Starrable",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "inputFields": null,
          "interfaces": [

          ],
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "INPUT_OBJECT",
          "name": "AddUpvoteInput",
          "description": "Autogenerated input type of AddUpvote",
          "fields": null,
          "inputFields": [
            {
              "name": "subjectId",
              "description": "The Node ID of the discussion or comment to upvote.",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "ID",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "defaultValue": null
            }
          ],
          "interfaces": null,
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "OBJECT",
          "name": "AddUpvotePayload",
          "description": "Autogenerated return type of AddUpvote",
          "fields": [
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "subject",
              "description": "The votable subject.",
              "args": [

              ],
              "type": {
                "kind": "INTERFACE",
                "name": "Votable",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "inputFields": null,
          "interfaces": [

          ],
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "INPUT_OBJECT",
          "name": "AddVerifiableDomainInput",
          "description": "Autogenerated input type of AddVerifiableDomain",
          "fields": null,
          "inputFields": [
            {
              "name": "ownerId",
              "description": "The ID of the owner to add the domain to",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "ID",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "domain",
              "description": "The URL of the domain",
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "URI",
                  "ofType": null
                }
              },
              "defaultValue": null
            },
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "defaultValue": null
            }
          ],
          "interfaces": null,
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "OBJECT",
          "name": "AddVerifiableDomainPayload",
          "description": "Autogenerated return type of AddVerifiableDomain",
          "fields": [
            {
              "name": "clientMutationId",
              "description": "A unique identifier for the client performing the mutation.",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "domain",
              "description": "The verifiable domain that was added.",
              "args": [

              ],
              "type": {
                "kind": "OBJECT",
                "name": "VerifiableDomain",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "inputFields": null,
          "interfaces": [

          ],
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "OBJECT",
          "name": "AddedToMergeQueueEvent",
          "description": "Represents an 'added_to_merge_queue' event on a given pull request.",
          "fields": [
            {
              "name": "actor",
              "description": "Identifies the actor who performed the event.",
              "args": [

              ],
              "type": {
                "kind": "INTERFACE",
                "name": "Actor",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "createdAt",
              "description": "Identifies the date and time when the object was created.",
              "args": [

              ],
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "DateTime",
                  "ofType": null
                }
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "enqueuer",
              "description": "The user who added this Pull Request to the merge queue",
              "args": [

              ],
              "type": {
                "kind": "OBJECT",
                "name": "User",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "id",
              "description": null,
              "args": [

              ],
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "ID",
                  "ofType": null
                }
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "mergeQueue",
              "description": "The merge queue where this pull request was added to.",
              "args": [

              ],
              "type": {
                "kind": "OBJECT",
                "name": "MergeQueue",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "pullRequest",
              "description": "PullRequest referenced by event.",
              "args": [

              ],
              "type": {
                "kind": "OBJECT",
                "name": "PullRequest",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "inputFields": null,
          "interfaces": [
            {
              "kind": "INTERFACE",
              "name": "Node",
              "ofType": null
            }
          ],
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "OBJECT",
          "name": "AddedToProjectEvent",
          "description": "Represents a 'added_to_project' event on a given issue or pull request.",
          "fields": [
            {
              "name": "actor",
              "description": "Identifies the actor who performed the event.",
              "args": [

              ],
              "type": {
                "kind": "INTERFACE",
                "name": "Actor",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "createdAt",
              "description": "Identifies the date and time when the object was created.",
              "args": [

              ],
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "DateTime",
                  "ofType": null
                }
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "databaseId",
              "description": "Identifies the primary key from the database.",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "Int",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "id",
              "description": null,
              "args": [

              ],
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "ID",
                  "ofType": null
                }
              },
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "inputFields": null,
          "interfaces": [
            {
              "kind": "INTERFACE",
              "name": "Node",
              "ofType": null
            }
          ],
          "enumValues": null,
          "possibleTypes": null
        },
        {
          "kind": "INTERFACE",
          "name": "AnnouncementBanner",
          "description": "Represents an announcement banner.",
          "fields": [
            {
              "name": "announcement",
              "description": "The text of the announcement",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "announcementExpiresAt",
              "description": "The expiration date of the announcement, if any",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "DateTime",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "announcementUserDismissible",
              "description": "Whether the announcement can be dismissed by the user",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "Boolean",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            }
          ],
          "inputFields": null,
          "interfaces": null,
          "enumValues": null,
          "possibleTypes": [
            {
              "kind": "OBJECT",
              "name": "Enterprise",
              "ofType": null
            },
            {
              "kind": "OBJECT",
              "name": "Organization",
              "ofType": null
            }
          ]
        },
        {
          "kind": "OBJECT",
          "name": "App",
          "description": "A GitHub App.",
          "fields": [
            {
              "name": "createdAt",
              "description": "Identifies the date and time when the object was created.",
              "args": [

              ],
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "DateTime",
                  "ofType": null
                }
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "databaseId",
              "description": "Identifies the primary key from the database.",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "Int",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "description",
              "description": "The description of the app.",
              "args": [

              ],
              "type": {
                "kind": "SCALAR",
                "name": "String",
                "ofType": null
              },
              "isDeprecated": false,
              "deprecationReason": null
            },
            {
              "name": "id",
              "description": null,
              "args": [

              ],
              "type": {
                "kind": "NON_NULL",
                "name": null,
                "ofType": {
                  "kind": "SCALAR",
                  "name": "ID",
                  "ofType": null
                }
              },
              "isDe
Download .txt
gitextract_iw48rug4/

├── .github/
│   └── workflows/
│       └── ruby.yml
├── .gitignore
├── .rspec
├── Appraisals
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── artemis.gemspec
├── bin/
│   ├── console
│   └── setup
├── gemfiles/
│   ├── .bundle/
│   │   └── config
│   ├── graphql_2_0.gemfile
│   ├── rails_70.gemfile
│   ├── rails_71.gemfile
│   ├── rails_72.gemfile
│   ├── rails_80.gemfile
│   ├── rails_81.gemfile
│   └── rails_edge.gemfile
├── lib/
│   ├── artemis/
│   │   ├── adapters/
│   │   │   ├── abstract_adapter.rb
│   │   │   ├── curb_adapter.rb
│   │   │   ├── multi_domain_adapter.rb
│   │   │   ├── net_http_adapter.rb
│   │   │   ├── net_http_persistent_adapter.rb
│   │   │   └── test_adapter.rb
│   │   ├── adapters.rb
│   │   ├── client.rb
│   │   ├── exceptions.rb
│   │   ├── graphql_endpoint.rb
│   │   ├── railtie.rb
│   │   ├── rspec.rb
│   │   ├── test_helper.rb
│   │   └── version.rb
│   ├── artemis.rb
│   ├── generators/
│   │   └── artemis/
│   │       ├── install/
│   │       │   ├── USAGE
│   │       │   ├── install_generator.rb
│   │       │   └── templates/
│   │       │       ├── client.rb
│   │       │       └── graphql.yml
│   │       ├── mutation/
│   │       │   ├── USAGE
│   │       │   ├── mutation_generator.rb
│   │       │   └── templates/
│   │       │       └── mutation.graphql
│   │       └── query/
│   │           ├── USAGE
│   │           ├── query_generator.rb
│   │           └── templates/
│   │               ├── fixture.yml
│   │               └── query.graphql
│   └── tasks/
│       └── artemis.rake
└── test/
    ├── adapters_test.rb
    ├── autoloading_test.rb
    ├── backport/
    │   └── method_call_assertions.rb
    ├── callbacks_test.rb
    ├── client_test.rb
    ├── endpoint_test.rb
    ├── fixtures/
    │   ├── github/
    │   │   ├── _repository_fields.graphql
    │   │   ├── repository.graphql
    │   │   ├── schema.json
    │   │   ├── user.graphql
    │   │   └── user_repositories.graphql
    │   ├── github.rb
    │   └── responses/
    │       ├── github/
    │       │   ├── repository.yml
    │       │   └── user.json
    │       └── spotify_client/
    │           └── artist.yml
    ├── generators/
    │   ├── install_generator_test.rb
    │   ├── mutation_generator_test.rb
    │   └── query_generator_test.rb
    ├── helpers/
    │   ├── fake_server.rb
    │   ├── isolated_test_helper.rb
    │   └── test_helper.rb
    ├── railtie_test.rb
    ├── rake_tasks_test.rb
    └── test_helper_test.rb
Download .txt
SYMBOL INDEX (183 symbols across 32 files)

FILE: lib/artemis.rb
  type Artemis (line 5) | module Artemis
    function config_for_graphql (line 6) | def self.config_for_graphql(app)
    function config_for (line 16) | def self.config_for(name, app:, env: Rails.env)

FILE: lib/artemis/adapters.rb
  type Artemis (line 5) | module Artemis
    type Adapters (line 6) | module Adapters
      function lookup (line 21) | def lookup(name)

FILE: lib/artemis/adapters/abstract_adapter.rb
  type Artemis (line 6) | module Artemis
    type Adapters (line 7) | module Adapters
      class AbstractAdapter (line 8) | class AbstractAdapter < ::GraphQL::Client::HTTP
        method initialize (line 18) | def initialize(uri, service_name: , timeout: , pool_size: , adapte...
        method headers (line 31) | def headers(_context)
        method execute (line 41) | def execute(*)
        method connection (line 49) | def connection

FILE: lib/artemis/adapters/curb_adapter.rb
  type Artemis (line 11) | module Artemis
    type Adapters (line 12) | module Adapters
      class CurbAdapter (line 13) | class CurbAdapter < AbstractAdapter
        method initialize (line 16) | def initialize(uri, service_name: , timeout: , pool_size: , adapte...
        method multiplex (line 23) | def multiplex(queries, context: {})
        method execute (line 27) | def execute(document:, operation_name: nil, variables: {}, context...
        method make_request (line 38) | def make_request(body, context)

FILE: lib/artemis/adapters/multi_domain_adapter.rb
  type Artemis (line 5) | module Artemis
    type Adapters (line 6) | module Adapters
      class MultiDomainAdapter (line 7) | class MultiDomainAdapter < AbstractAdapter
        method initialize (line 10) | def initialize(_uri, service_name: , timeout: , pool_size: , adapt...
        method multiplex (line 23) | def multiplex(queries, context: {})
        method execute (line 35) | def execute(document:, operation_name: nil, variables: {}, context...
        method connection (line 46) | def connection
        method connection_for_url (line 51) | def connection_for_url(url)

FILE: lib/artemis/adapters/net_http_adapter.rb
  type Artemis (line 9) | module Artemis
    type Adapters (line 10) | module Adapters
      class NetHttpAdapter (line 11) | class NetHttpAdapter < AbstractAdapter
        method multiplex (line 12) | def multiplex(queries, context: {})
        method execute (line 17) | def execute(document:, operation_name: nil, variables: {}, context...
        method connection (line 27) | def connection
        method make_request (line 38) | def make_request(body, context)

FILE: lib/artemis/adapters/net_http_persistent_adapter.rb
  type Artemis (line 22) | module Artemis
    type Adapters (line 23) | module Adapters
      class NetHttpPersistentAdapter (line 24) | class NetHttpPersistentAdapter < NetHttpAdapter
        method initialize (line 27) | def initialize(uri, service_name: , timeout: , pool_size: , adapte...
        method connection (line 41) | def connection
        class ConnectionWrapper (line 45) | class ConnectionWrapper < SimpleDelegator #:nodoc:
          method initialize (line 46) | def initialize(obj, url)
          method request (line 52) | def request(req)

FILE: lib/artemis/adapters/test_adapter.rb
  type Artemis (line 5) | module Artemis
    type Adapters (line 6) | module Adapters
      class TestAdapter (line 7) | class TestAdapter
        method initialize (line 19) | def initialize(*)
        method multiplex (line 22) | def multiplex(queries, context: {})
        method execute (line 34) | def execute(**arguments)
        method fake_response (line 46) | def fake_response

FILE: lib/artemis/client.rb
  type Artemis (line 13) | module Artemis
    class Client (line 14) | class Client
      method initialize (line 54) | def initialize(context = {})
      method endpoint (line 95) | def endpoint
      method instantiate_client (line 118) | def instantiate_client(context = {})
      method before_execute (line 134) | def before_execute(&block)
      method after_execute (line 152) | def after_execute(&block)
      method resolve_graphql_file_path (line 156) | def resolve_graphql_file_path(filename, fragment: false)
      method graphql_file_paths (line 164) | def graphql_file_paths
      method namespace (line 168) | def namespace
      method preload! (line 172) | def preload!
      method load_constant (line 190) | def load_constant(const_name)
      method connection (line 202) | def connection(context = {})
      method execute (line 206) | def execute(query, context: {}, **arguments)
      method multiplex (line 210) | def multiplex(**context, &block)
      method const_missing (line 239) | def const_missing(const_name)
      method method_missing (line 253) | def method_missing(method_name, **arguments, &block)
      method respond_to_missing? (line 261) | def respond_to_missing?(method_name, *_, &block) #:nodoc:
      method callbacks (line 268) | def callbacks
      method execute (line 280) | def execute(query, context: {}, **arguments)
      method method_missing (line 307) | def method_missing(method_name, context: {}, **arguments)
      method respond_to_missing? (line 313) | def respond_to_missing?(method_name, *_, &block) #:nodoc:
      class Executor (line 325) | class Executor < SimpleDelegator
        method initialize (line 326) | def initialize(connection, callbacks, default_context) #:nodoc:
        method execute (line 333) | def execute(document:, operation_name: nil, variables: {}, context...
      class MultiplexQueue (line 350) | class MultiplexQueue
        method initialize (line 353) | def initialize
        method execute (line 357) | def execute(document:, operation_name: nil, variables: {}, context...

FILE: lib/artemis/exceptions.rb
  type Artemis (line 1) | module Artemis
    class Error (line 2) | class Error < StandardError
    class EndpointNotFound (line 5) | class EndpointNotFound < Error
    class ConfigurationError (line 8) | class ConfigurationError < Error
    class GraphQLFileNotFound (line 11) | class GraphQLFileNotFound < Error
    class FixtureNotFound (line 14) | class FixtureNotFound < Error
    class GraphQLError (line 17) | class GraphQLError < Error
    class GraphQLServerError (line 20) | class GraphQLServerError < GraphQLError

FILE: lib/artemis/graphql_endpoint.rb
  type Artemis (line 13) | module Artemis
    class GraphQLEndpoint (line 14) | class GraphQLEndpoint
      method lookup (line 31) | def lookup(service_name)
      method register! (line 35) | def register!(service_name, configurations)
      method registered_services (line 42) | def registered_services
      method initialize (line 49) | def initialize(name, url: nil, adapter: :net_http, timeout: 10, sche...
      method schema (line 62) | def schema
      method connection (line 73) | def connection

FILE: lib/artemis/railtie.rb
  type Artemis (line 3) | module Artemis
    class Railtie (line 4) | class Railtie < ::Rails::Railtie #:nodoc:

FILE: lib/artemis/test_helper.rb
  type Artemis (line 11) | module Artemis
    type TestHelper (line 13) | module TestHelper
      function stub_graphql (line 45) | def stub_graphql(service, query_name, arguments =  :__unspecified__)
      function graphql_requests (line 51) | def graphql_requests
      function graphql_responses (line 57) | def graphql_responses #:nodoc:
      function graphql_fixture_path (line 61) | def graphql_fixture_path #:nodoc:
      function graphql_fixture_files (line 65) | def graphql_fixture_files #:nodoc:
      function read_erb_yaml (line 72) | def read_erb_yaml(path) #:nodoc:
      class StubbingDSL (line 80) | class StubbingDSL #:nodoc:
        method initialize (line 83) | def initialize(service_name, query_name, fixture_sets, arguments) ...
        method get (line 87) | def get(fixture_key)
        method to_return (line 98) | def to_return(fixture_key) #:nodoc:
        method find_fixture_set (line 116) | def find_fixture_set

FILE: lib/artemis/version.rb
  type Artemis (line 1) | module Artemis

FILE: lib/generators/artemis/install/install_generator.rb
  class Artemis::InstallGenerator (line 4) | class Artemis::InstallGenerator < Rails::Generators::NamedBase
    method generate_client (line 11) | def generate_client
    method generate_config (line 16) | def generate_config
    method download_schema (line 42) | def download_schema
    method file_name (line 54) | def file_name # :doc:
    method client_file_name (line 58) | def client_file_name
    method query_dir_gitkeep (line 66) | def query_dir_gitkeep
    method config_file_name (line 74) | def config_file_name

FILE: lib/generators/artemis/mutation/mutation_generator.rb
  class Artemis::MutationGenerator (line 5) | class Artemis::MutationGenerator < Rails::Generators::Base
    method generate_mutation_file (line 13) | def generate_mutation_file
    method mutation_name (line 19) | def mutation_name
    method graphql_file_path (line 23) | def graphql_file_path
    method arguments (line 27) | def arguments
    method target_mutation (line 31) | def target_mutation
    method schema (line 36) | def schema
    method service_name (line 40) | def service_name

FILE: lib/generators/artemis/query/query_generator.rb
  class Artemis::QueryGenerator (line 5) | class Artemis::QueryGenerator < Rails::Generators::Base
    method generate_query_file (line 13) | def generate_query_file
    method query_name (line 23) | def query_name
    method graphql_file_path (line 27) | def graphql_file_path
    method text_fixture_path (line 31) | def text_fixture_path
    method arguments (line 35) | def arguments
    method target_query (line 39) | def target_query
    method schema (line 44) | def schema
    method service_name (line 48) | def service_name
    method qualified_name (line 61) | def qualified_name

FILE: test/adapters_test.rb
  class AdaptersTest (line 5) | class AdaptersTest < ActiveSupport::TestCase
    method assert_adapter (line 122) | def assert_adapter(adapter, timeout_error)
    method assert_adapter_initialization (line 132) | def assert_adapter_initialization(adapter)
    method assert_adapter_execution (line 138) | def assert_adapter_execution(adapter)
    method assert_adapter_server_error (line 155) | def assert_adapter_server_error(adapter)
    method assert_adapter_timeout (line 163) | def assert_adapter_timeout(adapter, timeout_error)
    method assert_adapter_multiplex (line 171) | def assert_adapter_multiplex(adapter)
    method assert_adapter_multiplex_server_error (line 198) | def assert_adapter_multiplex_server_error(adapter)
    method assert_adapter_multiplex_timeout (line 206) | def assert_adapter_multiplex_timeout(adapter, timeout_error)

FILE: test/autoloading_test.rb
  class AutoLoadingTest (line 3) | class AutoLoadingTest < ActiveSupport::TestCase

FILE: test/backport/method_call_assertions.rb
  type MethodCallAssertions (line 3) | module MethodCallAssertions # :nodoc:
    function assert_called (line 5) | def assert_called(object, method_name, message = nil, times: 1, return...
    function assert_called_with (line 16) | def assert_called_with(object, method_name, args = [], returns: nil)
    function assert_not_called (line 30) | def assert_not_called(object, method_name, message = nil, &block)
    function stub_any_instance (line 34) | def stub_any_instance(klass, instance: klass.new)

FILE: test/callbacks_test.rb
  class CallbacksTest (line 5) | class CallbacksTest < ActiveSupport::TestCase
    method name (line 7) | def self.name
    method name (line 25) | def self.name

FILE: test/client_test.rb
  class ClientTest (line 3) | class ClientTest < ActiveSupport::TestCase
    method requests (line 218) | def requests
    method with_files (line 222) | def with_files(*files)

FILE: test/endpoint_test.rb
  class GraphQLEndpointTest (line 3) | class GraphQLEndpointTest < ActiveSupport::TestCase

FILE: test/fixtures/github.rb
  class Github (line 1) | class Github < Artemis::Client

FILE: test/generators/install_generator_test.rb
  class InstallGeneratorTest (line 6) | class InstallGeneratorTest < Rails::Generators::TestCase
    method assert_mock (line 107) | def assert_mock(mock)

FILE: test/generators/mutation_generator_test.rb
  class MutationGeneratorTest (line 6) | class MutationGeneratorTest < Rails::Generators::TestCase

FILE: test/generators/query_generator_test.rb
  class QueryGeneratorTest (line 6) | class QueryGeneratorTest < Rails::Generators::TestCase

FILE: test/helpers/fake_server.rb
  function start_server (line 67) | def start_server
  function teardown_server (line 84) | def teardown_server(server_thread)

FILE: test/helpers/isolated_test_helper.rb
  type Paths (line 25) | module Paths
    function app_template_path (line 26) | def app_template_path
    function tmp_path (line 30) | def tmp_path(*args)
    function app_path (line 35) | def app_path(*args)
  type Generation (line 40) | module Generation
    function build_app (line 45) | def build_app(options = {})
    function teardown_app (line 70) | def teardown_app
    function add_to_config (line 75) | def add_to_config(str)
    function initialize_app (line 84) | def self.initialize_app

FILE: test/railtie_test.rb
  class RailtieTest (line 6) | class RailtieTest < ActiveSupport::TestCase
    method app (line 217) | def app
    method boot_rails (line 221) | def boot_rails

FILE: test/rake_tasks_test.rb
  type ApplicationTests (line 5) | module ApplicationTests
    type RakeTests (line 6) | module RakeTests
      class RakeRoutesTest (line 7) | class RakeRoutesTest < ActiveSupport::TestCase
        method run_rake (line 137) | def run_rake(task)

FILE: test/test_helper_test.rb
  class TestHelperTest (line 4) | class TestHelperTest < ActiveSupport::TestCase
    method graphql_fixture_path (line 7) | def graphql_fixture_path
Condensed preview — 72 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (5,710K chars).
[
  {
    "path": ".github/workflows/ruby.yml",
    "chars": 3631,
    "preview": "name: build\n\non:\n  push:\n  pull_request:\n\njobs:\n  mri:\n    strategy:\n      fail-fast: false\n      matrix:\n        ruby_v"
  },
  {
    "path": ".gitignore",
    "chars": 118,
    "preview": "/.bundle/\n/.yardoc\n/_yardoc/\n/coverage/\n/doc/\n/pkg/\n/spec/reports/\n/tmp/\nGemfile.lock\ngemfiles/*.lock\n.byebug_history\n"
  },
  {
    "path": ".rspec",
    "chars": 30,
    "preview": "--require spec_helper\n--colour"
  },
  {
    "path": "Appraisals",
    "chars": 1442,
    "preview": "appraise \"rails_edge\" do\n  git 'https://github.com/rails/rails.git' do\n    gem \"rails\"\n    gem \"railties\"\n    gem \"activ"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 7055,
    "preview": "## Unreleased\n\n#### 🚨 Breaking Changes\n\n- No changes.\n\n#### ⭐️ New Features\n\n- Add support for Rails 8.0 ([<tt>#96</tt>]"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 3230,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
  },
  {
    "path": "Gemfile",
    "chars": 292,
    "preview": "source \"https://rubygems.org\"\n\ngit_source(:github) {|repo_name| \"https://github.com/#{repo_name}\" }\n\n# Specify your gem'"
  },
  {
    "path": "LICENSE.txt",
    "chars": 1081,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2018 Yuki Nishijima\n\nPermission is hereby granted, free of charge, to any person ob"
  },
  {
    "path": "README.md",
    "chars": 13374,
    "preview": "# Artemis [![build](https://github.com/yuki24/artemis/actions/workflows/ruby.yml/badge.svg)](https://github.com/yuki24/a"
  },
  {
    "path": "Rakefile",
    "chars": 441,
    "preview": "require \"bundler/gem_tasks\"\nrequire \"rake/testtask\"\n\nTESTS_IN_ISOLATION = ['test/railtie_test.rb', 'test/rake_tasks_test"
  },
  {
    "path": "artemis.gemspec",
    "chars": 1120,
    "preview": "\nlib = File.expand_path(\"../lib\", __FILE__)\n$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)\nrequire \"artemis/ver"
  },
  {
    "path": "bin/console",
    "chars": 375,
    "preview": "#!/usr/bin/env ruby\n\nrequire \"bundler/setup\"\nrequire \"artemis\"\n\nArtemis::Client.query_paths = [File.join(__dir__, '../sp"
  },
  {
    "path": "bin/setup",
    "chars": 131,
    "preview": "#!/usr/bin/env bash\nset -euo pipefail\nIFS=$'\\n\\t'\nset -vx\n\nbundle install\n\n# Do any other automated setup that you need "
  },
  {
    "path": "gemfiles/.bundle/config",
    "chars": 22,
    "preview": "---\nBUNDLE_RETRY: \"1\"\n"
  },
  {
    "path": "gemfiles/graphql_2_0.gemfile",
    "chars": 340,
    "preview": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"bigdecimal\"\ngem \"drb\"\ngem \"irb\"\ngem \"logger\""
  },
  {
    "path": "gemfiles/rails_70.gemfile",
    "chars": 304,
    "preview": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"bigdecimal\"\ngem \"drb\"\ngem \"irb\"\ngem \"logger\""
  },
  {
    "path": "gemfiles/rails_71.gemfile",
    "chars": 317,
    "preview": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"bigdecimal\"\ngem \"drb\"\ngem \"irb\"\ngem \"logger\""
  },
  {
    "path": "gemfiles/rails_72.gemfile",
    "chars": 317,
    "preview": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"bigdecimal\"\ngem \"drb\"\ngem \"irb\"\ngem \"logger\""
  },
  {
    "path": "gemfiles/rails_80.gemfile",
    "chars": 317,
    "preview": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"bigdecimal\"\ngem \"drb\"\ngem \"irb\"\ngem \"logger\""
  },
  {
    "path": "gemfiles/rails_81.gemfile",
    "chars": 296,
    "preview": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngem \"pry\"\ngem \"pry-byebug\", platforms: :mri\ngem \""
  },
  {
    "path": "gemfiles/rails_edge.gemfile",
    "chars": 336,
    "preview": "# This file was generated by Appraisal\n\nsource \"https://rubygems.org\"\n\ngit \"https://github.com/rails/rails.git\" do\n  gem"
  },
  {
    "path": "lib/artemis/adapters/abstract_adapter.rb",
    "chars": 1755,
    "preview": "# frozen_string_literal: true\n\nrequire 'active_support/core_ext/object/blank'\nrequire 'graphql/client/http'\n\nmodule Arte"
  },
  {
    "path": "lib/artemis/adapters/curb_adapter.rb",
    "chars": 1847,
    "preview": "# frozen_string_literal: true\n\nrequire 'delegate'\nrequire 'json'\n\nrequire 'curb'\n\nrequire 'artemis/adapters/abstract_ada"
  },
  {
    "path": "lib/artemis/adapters/multi_domain_adapter.rb",
    "chars": 2232,
    "preview": "# frozen_string_literal: true\n\nrequire 'artemis/adapters/abstract_adapter'\n\nmodule Artemis\n  module Adapters\n    class M"
  },
  {
    "path": "lib/artemis/adapters/net_http_adapter.rb",
    "chars": 1806,
    "preview": "# frozen_string_literal: true\n\nrequire 'json'\nrequire 'net/http'\n\nrequire 'artemis/adapters/abstract_adapter'\nrequire 'a"
  },
  {
    "path": "lib/artemis/adapters/net_http_persistent_adapter.rb",
    "chars": 1453,
    "preview": "# frozen_string_literal: true\n\nrequire 'delegate'\n\nbegin\n  require \"active_support/isolated_execution_state\"\nrescue Load"
  },
  {
    "path": "lib/artemis/adapters/test_adapter.rb",
    "chars": 1440,
    "preview": "# frozen_string_literal: true\n\nrequire 'active_support/core_ext/module/attribute_accessors'\n\nmodule Artemis\n  module Ada"
  },
  {
    "path": "lib/artemis/adapters.rb",
    "chars": 594,
    "preview": "# frozen-string-literal: true\n\nrequire 'active_support/dependencies/autoload'\n\nmodule Artemis\n  module Adapters\n    exte"
  },
  {
    "path": "lib/artemis/client.rb",
    "chars": 11908,
    "preview": "# frozen_string_literal: true\n\nrequire 'delegate'\nrequire 'active_support/core_ext/class/attribute'\nrequire 'active_supp"
  },
  {
    "path": "lib/artemis/exceptions.rb",
    "chars": 303,
    "preview": "module Artemis\n  class Error < StandardError\n  end\n\n  class EndpointNotFound < Error\n  end\n\n  class ConfigurationError <"
  },
  {
    "path": "lib/artemis/graphql_endpoint.rb",
    "chars": 2638,
    "preview": "# frozen_string_literal: true\n\nrequire 'active_support/core_ext/hash/deep_merge'\nrequire 'active_support/core_ext/hash/k"
  },
  {
    "path": "lib/artemis/railtie.rb",
    "chars": 2816,
    "preview": "require 'active_support/file_update_checker'\n\nmodule Artemis\n  class Railtie < ::Rails::Railtie #:nodoc:\n    config.arte"
  },
  {
    "path": "lib/artemis/rspec.rb",
    "chars": 189,
    "preview": "require 'artemis/test_helper'\n\nRSpec.configure do |config|\n  config.include ::Artemis::TestHelper\n\n  config.before :each"
  },
  {
    "path": "lib/artemis/test_helper.rb",
    "chars": 4606,
    "preview": "# frozen_string_literal: true\n\nrequire 'erb'\nrequire 'yaml'\n\nrequire 'active_support/core_ext/module/attribute_accessors"
  },
  {
    "path": "lib/artemis/version.rb",
    "chars": 39,
    "preview": "module Artemis\n  VERSION = \"1.1.0\"\nend\n"
  },
  {
    "path": "lib/artemis.rb",
    "chars": 1044,
    "preview": "require \"artemis/version\"\nrequire \"artemis/client\"\nrequire \"artemis/railtie\" if defined?(Rails)\n\nmodule Artemis\n  def se"
  },
  {
    "path": "lib/generators/artemis/install/USAGE",
    "chars": 361,
    "preview": "Description:\n    Generates a client stub and config files and downloads the schema from the given endpoint.\n\nExample:\n  "
  },
  {
    "path": "lib/generators/artemis/install/install_generator.rb",
    "chars": 1950,
    "preview": "require 'graphql/client'\nrequire 'graphql/client/http'\n\nclass Artemis::InstallGenerator < Rails::Generators::NamedBase\n "
  },
  {
    "path": "lib/generators/artemis/install/templates/client.rb",
    "chars": 241,
    "preview": "<% module_namespacing do -%>\nclass <%= class_name %> < Artemis::Client\n  # If an access token needs to be assigned to ev"
  },
  {
    "path": "lib/generators/artemis/install/templates/graphql.yml",
    "chars": 869,
    "preview": "default: &default\n  # The underlying client library that actually makes an HTTP request.\n  # Available adapters are :net"
  },
  {
    "path": "lib/generators/artemis/mutation/USAGE",
    "chars": 797,
    "preview": "Description:\n    Generates a mutation stub.\n\n    rails g artemis:mutation MUTATION_TYPE [FILE_NAME]\n\nExample:\n    rails "
  },
  {
    "path": "lib/generators/artemis/mutation/mutation_generator.rb",
    "chars": 1500,
    "preview": "# frozen_string_literal: true\n\nrequire 'graphql/schema/finder'\n\nclass Artemis::MutationGenerator < Rails::Generators::Ba"
  },
  {
    "path": "lib/generators/artemis/mutation/templates/mutation.graphql",
    "chars": 292,
    "preview": "mutation<%= arguments.present? && \"(#{ arguments.map {|name, type| \"$#{name}: #{type.type.to_type_signature}\" }.join(\", "
  },
  {
    "path": "lib/generators/artemis/query/USAGE",
    "chars": 739,
    "preview": "Description:\n    Generates a query stub.\n\n    rails g artemis:query QUERY_TYPE [FILE_NAME]\n\nExample:\n    rails g artemis"
  },
  {
    "path": "lib/generators/artemis/query/query_generator.rb",
    "chars": 1730,
    "preview": "# frozen_string_literal: true\n\nrequire 'graphql/schema/finder'\n\nclass Artemis::QueryGenerator < Rails::Generators::Base\n"
  },
  {
    "path": "lib/generators/artemis/query/templates/fixture.yml",
    "chars": 764,
    "preview": "# You can stub GraphQL queries by calling the `stub_graphql' method in test:\n#\n#   stub_graphql(<%= service_name.cameliz"
  },
  {
    "path": "lib/generators/artemis/query/templates/query.graphql",
    "chars": 294,
    "preview": "query<%= arguments.present? ? \"(#{ arguments.map {|name, type| \"$#{name}: #{type.type.to_type_signature}\" }.join(\", \") }"
  },
  {
    "path": "lib/tasks/artemis.rake",
    "chars": 1741,
    "preview": "# frozen_string_literal: true\n\nrequire 'json'\n\nrequire 'active_support/core_ext/string/inflections'\nrequire 'graphql/cli"
  },
  {
    "path": "test/adapters_test.rb",
    "chars": 8449,
    "preview": "require_relative 'helpers/test_helper'\n\nrequire 'artemis/adapters/abstract_adapter'\n\nclass AdaptersTest < ActiveSupport:"
  },
  {
    "path": "test/autoloading_test.rb",
    "chars": 4806,
    "preview": "require_relative 'helpers/test_helper'\n\nclass AutoLoadingTest < ActiveSupport::TestCase\n  test \".load_constant loads the"
  },
  {
    "path": "test/backport/method_call_assertions.rb",
    "chars": 1050,
    "preview": "require \"minitest/mock\"\n\nmodule MethodCallAssertions # :nodoc:\n  private\n  def assert_called(object, method_name, messag"
  },
  {
    "path": "test/callbacks_test.rb",
    "chars": 1587,
    "preview": "require_relative 'helpers/test_helper'\n\nrequire 'active_support/core_ext/module/attribute_accessors'\n\nclass CallbacksTes"
  },
  {
    "path": "test/client_test.rb",
    "chars": 6926,
    "preview": "require 'helpers/test_helper'\n\nclass ClientTest < ActiveSupport::TestCase\n  setup do\n    requests.clear\n  end\n\n  test \"."
  },
  {
    "path": "test/endpoint_test.rb",
    "chars": 1722,
    "preview": "require_relative 'helpers/test_helper'\n\nclass GraphQLEndpointTest < ActiveSupport::TestCase\n  teardown do\n    Artemis::G"
  },
  {
    "path": "test/fixtures/github/_repository_fields.graphql",
    "chars": 137,
    "preview": "fragment on Repository {\n  name\n  nameWithOwner\n  url\n  updatedAt\n  languages(first: 1) {\n    nodes {\n      name\n      c"
  },
  {
    "path": "test/fixtures/github/repository.graphql",
    "chars": 117,
    "preview": "query($owner: String!, $name: String!) {\n  repository(owner: $owner, name: $name) {\n    name\n    nameWithOwner\n  }\n}\n"
  },
  {
    "path": "test/fixtures/github/schema.json",
    "chars": 5074982,
    "preview": "{\n  \"data\": {\n    \"__schema\": {\n      \"queryType\": {\n        \"name\": \"Query\"\n      },\n      \"mutationType\": {\n        \"n"
  },
  {
    "path": "test/fixtures/github/user.graphql",
    "chars": 56,
    "preview": "query {\n  user(login: \"yuki24\") {\n    id\n    name\n  }\n}\n"
  },
  {
    "path": "test/fixtures/github/user_repositories.graphql",
    "chars": 213,
    "preview": "query($login: String!, $size: Int!) {\n  user(login: $login) {\n    id\n    name\n    repositories(first: $size) {\n      nod"
  },
  {
    "path": "test/fixtures/github.rb",
    "chars": 34,
    "preview": "class Github < Artemis::Client\nend"
  },
  {
    "path": "test/fixtures/responses/github/repository.yml",
    "chars": 309,
    "preview": "yuki24_artemis:\n  data:\n    repository:\n      name: artemis\n      nameWithOwner: yuki24/artemis\n\nyuki24_rambulance:\n  da"
  },
  {
    "path": "test/fixtures/responses/github/user.json",
    "chars": 123,
    "preview": "{\n  \"yuki24\": {\n    \"data\": {\n      \"user\": {\n        \"id\": \"foobar\",\n        \"name\": \"Yuki Nishijima\"\n      }\n    }\n  }"
  },
  {
    "path": "test/fixtures/responses/spotify_client/artist.yml",
    "chars": 83,
    "preview": "yoshiki:\n  data:\n    artist:\n      id: pianist-yoshiki\n      name: Pianist Yoshiki\n"
  },
  {
    "path": "test/generators/install_generator_test.rb",
    "chars": 2995,
    "preview": "require_relative '../helpers/test_helper'\nrequire 'rails/generators/test_case'\n\nrequire 'generators/artemis/install/inst"
  },
  {
    "path": "test/generators/mutation_generator_test.rb",
    "chars": 2124,
    "preview": "require_relative '../helpers/test_helper'\nrequire 'rails/generators/test_case'\n\nrequire 'generators/artemis/mutation/mut"
  },
  {
    "path": "test/generators/query_generator_test.rb",
    "chars": 2733,
    "preview": "require_relative '../helpers/test_helper'\nrequire 'rails/generators/test_case'\n\nrequire 'generators/artemis/query/query_"
  },
  {
    "path": "test/helpers/fake_server.rb",
    "chars": 2467,
    "preview": "require 'json'\nrequire 'rack'\nrequire 'webrick'\nrequire 'net/http'\n\nRACK_SERVER = begin\n                require 'rackup/"
  },
  {
    "path": "test/helpers/isolated_test_helper.rb",
    "chars": 3667,
    "preview": "$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)\n\nrequire \"active_support\"\nrequire 'active_support/core_ext/ke"
  },
  {
    "path": "test/helpers/test_helper.rb",
    "chars": 1773,
    "preview": "$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)\n\nrequire \"active_support\"\nrequire 'active_support/core_ext/ke"
  },
  {
    "path": "test/railtie_test.rb",
    "chars": 5980,
    "preview": "require \"rack/test\"\nrequire \"rails/version\"\n\nrequire_relative \"helpers/isolated_test_helper\"\n\nclass RailtieTest < Active"
  },
  {
    "path": "test/rake_tasks_test.rb",
    "chars": 4674,
    "preview": "# frozen_string_literal: true\n\nrequire_relative \"helpers/isolated_test_helper\"\n\nmodule ApplicationTests\n  module RakeTes"
  },
  {
    "path": "test/test_helper_test.rb",
    "chars": 3335,
    "preview": "require_relative 'helpers/test_helper'\nrequire 'artemis/test_helper'\n\nclass TestHelperTest < ActiveSupport::TestCase\n  i"
  }
]

About this extraction

This page contains the full source code of the yuki24/artemis GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 72 files (5.0 MB), approximately 1.3M tokens, and a symbol index with 183 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!