├── .github └── workflows │ └── rspec.yml ├── .gitignore ├── CHANGELOG.md ├── Gemfile ├── LICENSE.txt ├── Makefile ├── README.md ├── Rakefile ├── bin └── qiita ├── images └── cli.png ├── lib ├── qiita.rb └── qiita │ ├── arguments.rb │ ├── client.rb │ ├── command_builder.rb │ ├── commands │ ├── base.rb │ ├── error.rb │ └── request.rb │ ├── resource_based_methods.rb │ ├── response.rb │ ├── response_renderer.rb │ └── version.rb ├── qiita.gemspec ├── script └── generate └── spec ├── qiita └── client_spec.rb └── spec_helper.rb /.github/workflows/rspec.yml: -------------------------------------------------------------------------------- 1 | name: RSpec 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | rspec: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | - uses: ruby/setup-ruby@v1 13 | with: 14 | ruby-version: '3.0.0' 15 | bundler-cache: true 16 | - run: gem install bundler -v '< 2.0' 17 | - run: bundle install 18 | - run: bundle exec rake spec 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | lib/bundler/man 11 | pkg 12 | rdoc 13 | spec/reports 14 | test/tmp 15 | test/version_tmp 16 | tmp 17 | /vendor/ 18 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Unreleased 2 | 3 | ## v1.5.0 4 | 5 | - Require Faraday v2 6 | 7 | ## v1.4.0 8 | 9 | - Migrate CI/CD platform from Travis CI to GitHub Actions 10 | - Require Ruby 3.0.0 or higher 11 | 12 | ## v1.3.5 13 | 14 | - Add project reaction methods 15 | 16 | ## v1.3.4 17 | 18 | - Add new methods about emoji reactions 19 | 20 | ## v1.3.3 21 | 22 | - Add `#get_item_like` method 23 | - Add `#list_item_likes` method 24 | - Change method name from `#patch_project` to `#update_project` 25 | - Fix broken `#delete_item` 26 | 27 | ## v1.3.2 28 | 29 | - Fix broken client methods 30 | 31 | ## v1.3.1 32 | 33 | - Fix client methods that have templated path 34 | 35 | ## v1.3.0 36 | 37 | - Update methods and their descriptions 38 | 39 | ## v1.2.0 40 | 41 | - Update method names and docs about likes API 42 | 43 | ## v1.1.2 44 | 45 | - Slop v4 is not supported for now 46 | 47 | ## v1.1.1 48 | 49 | - Support --host with --team option 50 | 51 | ## v1.1.0 52 | 53 | - Add --no-ssl-verification option 54 | 55 | ## v1.0.7 56 | 57 | - Add list_authenticated_user_items method 58 | 59 | ## v1.0.6 60 | 61 | - Add create_tagging and delete_tagging methods 62 | 63 | ## v1.0.5 64 | 65 | - Add client methods about following, LGTM, and stock 66 | 67 | ## v1.0.4 68 | 69 | - Add list_teams method 70 | 71 | ## v1.0.3 72 | 73 | - Escape request URL path 74 | 75 | ## v1.0.2 76 | 77 | - Add a new API: list_item_stockers(item_id) 78 | 79 | ## v1.0.1 80 | 81 | - Fix response body existence check logic 82 | 83 | ## v1.0.0 84 | 85 | - Support Qiita API v2 86 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in qiita.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Ryo Nakamura 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: test doc 2 | 3 | doc: 4 | git grep -h "^\s\+# \?" lib/qiita/response.rb | sed -E 's; +# ?;;' > doc/response.md 5 | git grep -h "^\s\+# \?" lib/qiita/client.rb | sed -E 's; +# ?;;' > doc/client.md 6 | git grep -h "^\s\+# \?" lib/qiita/resource_based_methods.rb | sed -E 's; +# ?;;' >> doc/client.md 7 | 8 | release: 9 | bundle exec rake release 10 | 11 | setup: 12 | gem install bundler 13 | bundle install 14 | 15 | test: 16 | bundle exec rspec 17 | 18 | .PHONY: doc release setup test 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Qiita 2 | 3 | [![Gem Version](https://badge.fury.io/rb/qiita.svg)](https://rubygems.org/gems/qiita) 4 | [![Build Status](https://travis-ci.org/increments/qiita-rb.svg?branch=master)](https://travis-ci.org/increments/qiita-rb) 5 | 6 | Qiita API v2 client library and CLI tool, written in Ruby. 7 | 8 | ## Install 9 | 10 | Note: requires Ruby 3.0.0 or higher. 11 | 12 | ```bash 13 | gem install qiita 14 | ``` 15 | 16 | ## Library 17 | 18 | ```ruby 19 | require "qiita" 20 | 21 | client = Qiita::Client.new(access_token: "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcd") 22 | client.list_items 23 | client.list_users 24 | client.list_user_items("r7kamura") 25 | client.get_user("r7kamura") 26 | client.get_user("r7kamura").status 27 | client.get_user("r7kamura").headers 28 | client.get_user("r7kamura").body 29 | ``` 30 | 31 | ## CLI 32 | 33 | `qiita` executable calls `Qiita::Client`'s methods. 34 | 35 | ``` 36 | $ qiita [headers|params] [options] 37 | | | | | | 38 | | | | | `-- -H, --host 39 | | | | | -a, --access-token 40 | | | | | -c, --color 41 | | | | | -h, --help 42 | | | | | --header 43 | | | | | --no-body 44 | | | | | --no-ssl-verification 45 | | | | | -t, --team 46 | | | | | 47 | | | | `------------ key=value or key:=value 48 | | | | 49 | | | `------------------- Key:value 50 | | | 51 | | `------------------------------ required arguments for the method 52 | | 53 | `----------------------------------------- method name 54 | 55 | $ qiita list_users 56 | $ qiita get_user r7kamura 57 | $ qiita list_user_items r7kamura 58 | ``` 59 | 60 | ### Method and Arguments 61 | 62 | Pass `Qiita::Client` method name and required arguments. 63 | 64 | ### Access token 65 | 66 | Accepts access token via `-a, --access-token` or `QIITA_ACCESS_TOKEN` environment variable. 67 | 68 | ### Headers 69 | 70 | To set custom request headers, use `Key:value` syntax. 71 | 72 | ``` 73 | $ qiita list_items "Authorization:Bearer 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcd" 74 | ``` 75 | 76 | ### Params 77 | 78 | Params are used for query string in GET method, or for request body in other methods. 79 | You can set params by `key=value` or `key:=value` syntax. 80 | `key=value` is parsed into String value, 81 | while `key:=value` is parsed into JSON value (e.g. key:=17 will be `{"key":17}`). 82 | `qiita` also accepts params via STDIN. 83 | 84 | ``` 85 | $ qiita list_items page=2 per_page=10 86 | $ qiita create_item < params.json 87 | ``` 88 | 89 | ![](images/cli.png) 90 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | require "rspec/core/rake_task" 3 | 4 | RSpec::Core::RakeTask.new("spec") 5 | task :default => :spec 6 | -------------------------------------------------------------------------------- /bin/qiita: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__) 3 | require "qiita" 4 | 5 | builder = Qiita::CommandBuilder.new(ARGV) 6 | command = builder.call 7 | command.call 8 | -------------------------------------------------------------------------------- /images/cli.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/increments/qiita-rb/41aa0fc6d9320cf64630336af3d8bafb8213cc29/images/cli.png -------------------------------------------------------------------------------- /lib/qiita.rb: -------------------------------------------------------------------------------- 1 | require "active_support/core_ext/object/blank" 2 | require "active_support/core_ext/string/inflections" 3 | require "active_support/core_ext/string/strip" 4 | require "faraday" 5 | require "json" 6 | require "rack/utils" 7 | require "rainbow" 8 | require "rouge" 9 | require "slop" 10 | 11 | require "qiita/arguments" 12 | require "qiita/client" 13 | require "qiita/command_builder" 14 | require "qiita/commands/base" 15 | require "qiita/commands/error" 16 | require "qiita/commands/request" 17 | require "qiita/resource_based_methods" 18 | require "qiita/response" 19 | require "qiita/response_renderer" 20 | require "qiita/version" 21 | -------------------------------------------------------------------------------- /lib/qiita/arguments.rb: -------------------------------------------------------------------------------- 1 | module Qiita 2 | class Arguments 3 | def initialize(argv) 4 | @argv = argv 5 | end 6 | 7 | def access_token 8 | slop_options["access-token"] || ENV["QIITA_ACCESS_TOKEN"] 9 | end 10 | 11 | def arguments 12 | parsed_argv_data[:arguments] 13 | end 14 | 15 | def color 16 | slop_options["color"] 17 | end 18 | 19 | def error_message 20 | slop_options.to_s 21 | end 22 | 23 | def headers 24 | parsed_argv_data[:headers] 25 | end 26 | 27 | def host 28 | slop_options["host"] 29 | end 30 | 31 | def ssl 32 | !slop_options["no-ssl-verification"] 33 | end 34 | 35 | def method_name 36 | @argv[0] 37 | end 38 | 39 | def params 40 | params_from_stdin.merge(parsed_argv_data[:params]) 41 | end 42 | 43 | def show_body 44 | !slop_options["no-body"] 45 | end 46 | 47 | def show_header 48 | slop_options["header"] 49 | end 50 | 51 | def team 52 | slop_options["team"] 53 | end 54 | 55 | def valid? 56 | has_valid_slop_options? && has_valid_method_name? && has_valid_arguments? && !has_invalid_json_input? 57 | end 58 | 59 | private 60 | 61 | def has_input_from_stdin? 62 | has_pipe_input? || has_redirect_input? 63 | end 64 | 65 | def has_invalid_json_input? 66 | params_from_stdin 67 | false 68 | rescue JSON::ParserError 69 | true 70 | end 71 | 72 | def has_pipe_input? 73 | File.pipe?(STDIN) 74 | end 75 | 76 | def has_redirect_input? 77 | File.select([STDIN], [], [], 0) != nil 78 | end 79 | 80 | def has_valid_arguments? 81 | -(Client.instance_method(method_name).arity) - 1 == arguments.length 82 | end 83 | 84 | def has_valid_method_name? 85 | !method_name.nil? && Client.instance_methods.include?(method_name.to_sym) 86 | end 87 | 88 | def has_valid_slop_options? 89 | !slop_options["help"] 90 | rescue 91 | false 92 | end 93 | 94 | def params_from_stdin 95 | @params_from_stdin ||= begin 96 | if has_input_from_stdin? 97 | JSON.parse(STDIN.read) 98 | else 99 | {} 100 | end 101 | end 102 | end 103 | 104 | def parsed_argv_data 105 | @parsed_argv_data ||= begin 106 | params = {} 107 | headers = {} 108 | arguments = [] 109 | @argv[1..-1].each do |section| 110 | case 111 | when /(?.+):(?[^=]+)/ =~ section 112 | headers[key] = value 113 | when /(?.+):=(?.+)/ =~ section 114 | params[key] = JSON.parse(%<{"key":#{value}}>)["key"] 115 | when /(?.+)=(?.+)/ =~ section 116 | params[key] = value 117 | else 118 | arguments << section 119 | end 120 | end 121 | { 122 | arguments: arguments, 123 | headers: headers, 124 | params: params, 125 | } 126 | end 127 | end 128 | 129 | def slop_options 130 | @slop_options ||= Slop.parse!(@argv) do 131 | banner "Usage: qiita [arguments] [headers|params] [options]" 132 | on "H", "host=", "Change API server's host" 133 | on "a", "access-token=", "Access token" 134 | on "c", "color", "Color output" 135 | on "h", "help", "Display help message" 136 | on "header", "Show response header" 137 | on "no-body", "Hide response body" 138 | on "no-ssl-verification", "Ignore SSL verification" 139 | on "t", "team=", "Team name to be used as subdomain" 140 | end 141 | end 142 | end 143 | end 144 | -------------------------------------------------------------------------------- /lib/qiita/client.rb: -------------------------------------------------------------------------------- 1 | require "qiita/resource_based_methods" 2 | require "qiita/version" 3 | 4 | ## Qiita::Client 5 | # A class for API client to send HTTP request. 6 | # 7 | module Qiita 8 | class Client 9 | DEFAULT_ACCEPT = "application/json" 10 | 11 | DEFAULT_HOST = "qiita.com" 12 | 13 | DEFAULT_USER_AGENT = "Qiita Ruby Gem #{Qiita::VERSION}" 14 | 15 | DEFAULT_HEADERS = { 16 | "Accept" => DEFAULT_ACCEPT, 17 | "User-Agent" => DEFAULT_USER_AGENT, 18 | } 19 | 20 | include ResourceBasedMethods 21 | 22 | # ### Qiita::Client.new(options = {}) 23 | # Creates a new instance of `Qiita::Client` class. 24 | # `options` can have following key-values: 25 | # 26 | # * `access_token` - (String) Access token issued to authenticate and authorize user. 27 | # * `host` - (String) Hostname where this client accesses to. 28 | # * `ssl` - (Boolean) Use SSL verification. (default: true) 29 | # * `team` - (String) Team name to be used as subdomain. 30 | # 31 | # ```rb 32 | # Qiita::Client.new 33 | # Qiita::Client.new(access_token: "...") 34 | # Qiita::Client.new(host: "my-team-name.qiita.com") 35 | # Qiita::Client.new(team: "my-team-name") 36 | # ``` 37 | # 38 | def initialize(access_token: nil, host: nil, ssl: true, team: nil) 39 | @access_token = access_token 40 | @host = host 41 | @ssl = ssl 42 | @team = team 43 | end 44 | 45 | # ### Qiita::Client#get(path, params = nil, headers = nil) 46 | # Sends GET request with given parameters, then returns a `Qiita::Response`. 47 | # `params` are url-encoded and used as URI query string. 48 | # 49 | # ```rb 50 | # client.get("/api/v2/items", page: 2) 51 | # ``` 52 | # 53 | def get(path, params = nil, headers = nil) 54 | process(:get, path, params, headers) 55 | end 56 | 57 | # ### Qiita::Client#post(path, params = nil, headers = nil) 58 | # Sends POST request with given parameters, then returns a Qiita::Response. 59 | # `params` are JSON-encoded and used as request body. 60 | # 61 | # ```rb 62 | # client.post("/api/v2/items", title: "...", body: "...") 63 | # ``` 64 | # 65 | def post(path, params = nil, headers = nil) 66 | process(:post, path, params, headers) 67 | end 68 | 69 | # ### Qiita::Client#patch(path, params = nil, headers = nil) 70 | # Sends PATCH request with given parameters, then returns a Qiita::Response. 71 | # `params` are JSON-encoded and used as request body. 72 | # 73 | # ```rb 74 | # client.patch("/api/v2/items/543efd13001e30837319", title: "...", body: "...") 75 | # ``` 76 | # 77 | def patch(path, params = nil, headers = nil) 78 | process(:patch, path, params, headers) 79 | end 80 | 81 | # ### Qiita::Client#put(path, params = nil, headers = nil) 82 | # Sends PUT request, then returns a Qiita::Response. 83 | # `params` are JSON-encoded and used as request body. 84 | # 85 | # ```rb 86 | # client.put("/api/v2/items/543efd13001e30837319/stock") 87 | # ``` 88 | # 89 | def put(path, params = nil, headers = nil) 90 | process(:put, path, params, headers) 91 | end 92 | 93 | # ### Qiita::Client#delete(path, params = nil, headers = nil) 94 | # Sends DELETE request, then returns a Qiita::Response. 95 | # `params` are url-encoded and used as URI query string. 96 | # 97 | # ```rb 98 | # client.delete("/api/v2/items/543efd13001e30837319/stock") 99 | # ``` 100 | # 101 | def delete(path, params = nil, headers = nil) 102 | process(:delete, path, params, headers) 103 | end 104 | 105 | # ### Qiita::Client#connection 106 | # Returns a Faraday::Connection to customize by your favorite middlewares. 107 | # 108 | # ```rb 109 | # client.connection.response :logger 110 | # ``` 111 | # 112 | def connection 113 | @connection ||= Faraday.new(faraday_client_options) do |connection| 114 | connection.request :json 115 | connection.response :json 116 | connection.adapter Faraday.default_adapter 117 | end 118 | end 119 | 120 | private 121 | 122 | def base_host 123 | @host || DEFAULT_HOST 124 | end 125 | 126 | def default_headers 127 | headers = DEFAULT_HEADERS.clone 128 | headers["Authorization"] = "Bearer #{@access_token}" if @access_token 129 | headers 130 | end 131 | 132 | def faraday_client_options 133 | { 134 | headers: default_headers, 135 | ssl: { 136 | verify: @ssl, 137 | }, 138 | url: url_prefix, 139 | } 140 | end 141 | 142 | def host 143 | if @team 144 | "#{@team}.#{base_host}" 145 | else 146 | base_host 147 | end 148 | end 149 | 150 | def process(request_method, path, params, headers) 151 | Qiita::Response.new( 152 | connection.send( 153 | request_method, 154 | path, 155 | params, 156 | headers, 157 | ) 158 | ) 159 | end 160 | 161 | def url_prefix 162 | "https://#{host}" 163 | end 164 | end 165 | end 166 | -------------------------------------------------------------------------------- /lib/qiita/command_builder.rb: -------------------------------------------------------------------------------- 1 | # ## Qiita::CommandBuilder 2 | # Creates a new command object from given ARGV. 3 | # 4 | # ```ruby 5 | # builder = Qiita::CommandBuilder.new(ARGV) 6 | # builder.call 7 | # ``` 8 | # 9 | module Qiita 10 | class CommandBuilder 11 | ### Qiita::CommandBuilder.new(argv) 12 | # Creates a new instance of Qiita::CommandBuilder from given command line arguments. 13 | # 14 | def initialize(argv) 15 | @argv = argv 16 | end 17 | 18 | ### Qiita::CommandBuilder#call 19 | # Returns a new instance of command class that inherited from Qiita::Commands::Base. 20 | # 21 | def call 22 | if arguments.valid? 23 | Qiita::Commands::Request.new(arguments) 24 | else 25 | Qiita::Commands::Error.new(arguments) 26 | end 27 | end 28 | 29 | private 30 | 31 | def arguments 32 | @arguments ||= Arguments.new(@argv) 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/qiita/commands/base.rb: -------------------------------------------------------------------------------- 1 | module Qiita 2 | module Commands 3 | class Base 4 | def initialize(arguments) 5 | @arguments = arguments 6 | end 7 | 8 | def call 9 | raise NotImplementedError 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/qiita/commands/error.rb: -------------------------------------------------------------------------------- 1 | module Qiita 2 | module Commands 3 | class Error < Base 4 | def call 5 | abort(@arguments.error_message) 6 | end 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/qiita/commands/request.rb: -------------------------------------------------------------------------------- 1 | module Qiita 2 | module Commands 3 | class Request < Base 4 | def call 5 | response = client.send( 6 | @arguments.method_name, 7 | *@arguments.arguments, 8 | @arguments.params, 9 | @arguments.headers, 10 | ) 11 | print ResponseRenderer.new( 12 | response, 13 | color: @arguments.color, 14 | show_body: @arguments.show_body, 15 | show_header: @arguments.show_header, 16 | ) 17 | end 18 | 19 | private 20 | 21 | def client 22 | Client.new( 23 | access_token: @arguments.access_token, 24 | host: @arguments.host, 25 | ssl: @arguments.ssl, 26 | team: @arguments.team, 27 | ) 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/qiita/resource_based_methods.rb: -------------------------------------------------------------------------------- 1 | module Qiita 2 | module ResourceBasedMethods 3 | # ### Qiita::Client#create_access_token(params = nil, headers = nil) 4 | # Create a new access token. 5 | # 6 | def create_access_token(params = nil, headers = nil) 7 | post("/api/v2/access_tokens", params, headers) 8 | end 9 | 10 | # ### Qiita::Client#delete_access_token(access_token, params = nil, headers = nil) 11 | # Deactivate an access token. 12 | # 13 | def delete_access_token(access_token, params = nil, headers = nil) 14 | delete("/api/v2/access_tokens/#{access_token}", params, headers) 15 | end 16 | 17 | # ### Qiita::Client#get_authenticated_user(params = nil, headers = nil) 18 | # Get a user associated to the current access token. 19 | # 20 | def get_authenticated_user(params = nil, headers = nil) 21 | get("/api/v2/authenticated_user", params, headers) 22 | end 23 | 24 | # ### Qiita::Client#delete_comment(comment_id, params = nil, headers = nil) 25 | # Delete a comment. 26 | # 27 | def delete_comment(comment_id, params = nil, headers = nil) 28 | delete("/api/v2/comments/#{comment_id}", params, headers) 29 | end 30 | 31 | # ### Qiita::Client#get_comment(comment_id, params = nil, headers = nil) 32 | # Get a comment. 33 | # 34 | def get_comment(comment_id, params = nil, headers = nil) 35 | get("/api/v2/comments/#{comment_id}", params, headers) 36 | end 37 | 38 | # ### Qiita::Client#update_comment(comment_id, params = nil, headers = nil) 39 | # Update a comment. 40 | # 41 | def update_comment(comment_id, params = nil, headers = nil) 42 | patch("/api/v2/comments/#{comment_id}", params, headers) 43 | end 44 | 45 | # ### Qiita::Client#unthank_comment(comment_id, params = nil, headers = nil) 46 | # Delete a thank from a comment. 47 | # 48 | def unthank_comment(comment_id, params = nil, headers = nil) 49 | delete("/api/v2/comments/#{comment_id}/thank", params, headers) 50 | end 51 | 52 | # ### Qiita::Client#thank_comment(comment_id, params = nil, headers = nil) 53 | # Send a thank to a comment. 54 | # 55 | def thank_comment(comment_id, params = nil, headers = nil) 56 | put("/api/v2/comments/#{comment_id}/thank", params, headers) 57 | end 58 | 59 | # ### Qiita::Client#list_item_comments(item_id, params = nil, headers = nil) 60 | # List comments on an item in newest order. 61 | # 62 | def list_item_comments(item_id, params = nil, headers = nil) 63 | get("/api/v2/items/#{item_id}/comments", params, headers) 64 | end 65 | 66 | # ### Qiita::Client#create_item_comment(item_id, params = nil, headers = nil) 67 | # Post a comment on an item. 68 | # 69 | def create_item_comment(item_id, params = nil, headers = nil) 70 | post("/api/v2/items/#{item_id}/comments", params, headers) 71 | end 72 | 73 | # ### Qiita::Client#create_expanded_template(params = nil, headers = nil) 74 | # Get a template where its variables are expanded. 75 | # 76 | def create_expanded_template(params = nil, headers = nil) 77 | post("/api/v2/expanded_templates", params, headers) 78 | end 79 | 80 | # ### Qiita::Client#list_authenticated_user_items(params = nil, headers = nil) 81 | # List the authenticated user's items in newest order 82 | # 83 | def list_authenticated_user_items(params = nil, headers = nil) 84 | get("/api/v2/authenticated_user/items", params, headers) 85 | end 86 | 87 | # ### Qiita::Client#list_items(params = nil, headers = nil) 88 | # List items. 89 | # 90 | def list_items(params = nil, headers = nil) 91 | get("/api/v2/items", params, headers) 92 | end 93 | 94 | # ### Qiita::Client#create_item(params = nil, headers = nil) 95 | # Create an item. 96 | # 97 | def create_item(params = nil, headers = nil) 98 | post("/api/v2/items", params, headers) 99 | end 100 | 101 | # ### Qiita::Client#delete_item(item_id, params = nil, headers = nil) 102 | # Delete an item. 103 | # 104 | def delete_item(item_id, params = nil, headers = nil) 105 | delete("/api/v2/items/#{item_id}", params, headers) 106 | end 107 | 108 | # ### Qiita::Client#get_item(item_id, params = nil, headers = nil) 109 | # Get an item. 110 | # 111 | def get_item(item_id, params = nil, headers = nil) 112 | get("/api/v2/items/#{item_id}", params, headers) 113 | end 114 | 115 | # ### Qiita::Client#update_item(item_id, params = nil, headers = nil) 116 | # Update an item. 117 | # 118 | def update_item(item_id, params = nil, headers = nil) 119 | patch("/api/v2/items/#{item_id}", params, headers) 120 | end 121 | 122 | # ### Qiita::Client#unlike_item(item_id, params = nil, headers = nil) 123 | # Unlike an item (only available on Qiita:Team). 124 | # 125 | def unlike_item(item_id, params = nil, headers = nil) 126 | delete("/api/v2/items/#{item_id}/like", params, headers) 127 | end 128 | 129 | # ### Qiita::Client#like_item(item_id, params = nil, headers = nil) 130 | # Like an item (only available on Qiita:Team). 131 | # 132 | def like_item(item_id, params = nil, headers = nil) 133 | put("/api/v2/items/#{item_id}/like", params, headers) 134 | end 135 | 136 | # ### Qiita::Client#stock_item(item_id, params = nil, headers = nil) 137 | # Stock an item. 138 | # 139 | def stock_item(item_id, params = nil, headers = nil) 140 | put("/api/v2/items/#{item_id}/stock", params, headers) 141 | end 142 | 143 | # ### Qiita::Client#unstock_item(item_id, params = nil, headers = nil) 144 | # Unstock an item. 145 | # 146 | def unstock_item(item_id, params = nil, headers = nil) 147 | delete("/api/v2/items/#{item_id}/stock", params, headers) 148 | end 149 | 150 | # ### Qiita::Client#get_item_stock(item_id, params = nil, headers = nil) 151 | # Check if you stocked an item. 152 | # 153 | def get_item_stock(item_id, params = nil, headers = nil) 154 | get("/api/v2/items/#{item_id}/stock", params, headers) 155 | end 156 | 157 | # ### Qiita::Client#get_item_like(item_id, params = nil, headers = nil) 158 | # Check if you liked an item. 159 | # 160 | def get_item_like(item_id, params = nil, headers = nil) 161 | get("/api/v2/items/#{item_id}/like", params, headers) 162 | end 163 | 164 | # ### Qiita::Client#stock_item(item_id, params = nil, headers = nil) 165 | # Stock an item. 166 | # 167 | def stock_item(item_id, params = nil, headers = nil) 168 | put("/api/v2/items/#{item_id}/stock", params, headers) 169 | end 170 | 171 | # ### Qiita::Client#list_tag_items(tag_id, params = nil, headers = nil) 172 | # List tagged items in recently-tagged order. 173 | # 174 | def list_tag_items(tag_id, params = nil, headers = nil) 175 | get("/api/v2/tags/#{tag_id}/items", params, headers) 176 | end 177 | 178 | # ### Qiita::Client#list_user_items(user_id, params = nil, headers = nil) 179 | # List a user's items in newest order. 180 | # 181 | def list_user_items(user_id, params = nil, headers = nil) 182 | get("/api/v2/users/#{user_id}/items", params, headers) 183 | end 184 | 185 | # ### Qiita::Client#list_user_stocks(user_id, params = nil, headers = nil) 186 | # List a user's stocked items in recently-stocked order. 187 | # 188 | def list_user_stocks(user_id, params = nil, headers = nil) 189 | get("/api/v2/users/#{user_id}/stocks", params, headers) 190 | end 191 | 192 | # ### Qiita::Client#list_item_likes(item_id, params = nil, headers = nil) 193 | # List likes in newest order. 194 | # 195 | def list_item_likes(item_id, params = nil, headers = nil) 196 | get("/api/v2/items/#{item_id}/likes", params, headers) 197 | end 198 | 199 | # ### Qiita::Client#list_projects(params = nil, headers = nil) 200 | # List projects in newest order. 201 | # 202 | def list_projects(params = nil, headers = nil) 203 | get("/api/v2/projects", params, headers) 204 | end 205 | 206 | # ### Qiita::Client#create_project(params = nil, headers = nil) 207 | # Create a new project. 208 | # 209 | def create_project(params = nil, headers = nil) 210 | post("/api/v2/projects", params, headers) 211 | end 212 | 213 | # ### Qiita::Client#delete_project(project_id, params = nil, headers = nil) 214 | # Delete a project. 215 | # 216 | def delete_project(project_id, params = nil, headers = nil) 217 | delete("/api/v2/projects/#{project_id}", params, headers) 218 | end 219 | 220 | # ### Qiita::Client#get_project(project_id, params = nil, headers = nil) 221 | # Get a project. 222 | # 223 | def get_project(project_id, params = nil, headers = nil) 224 | get("/api/v2/projects/#{project_id}", params, headers) 225 | end 226 | 227 | # ### Qiita::Client#update_project(project_id, params = nil, headers = nil) 228 | # Update a project 229 | # 230 | def update_project(project_id, params = nil, headers = nil) 231 | patch("/api/v2/projects/#{project_id}", params, headers) 232 | end 233 | 234 | # ### Qiita::Client#create_comment_reaction(comment_id, params = nil, headers = nil) 235 | # Add an emoji reaction to a comment. 236 | # 237 | def create_comment_reaction(comment_id, params = nil, headers = nil) 238 | post("/api/v2/comments/#{comment_id}/reactions", params, headers) 239 | end 240 | 241 | # ### Qiita::Client#create_item_reaction(item_id, params = nil, headers = nil) 242 | # Add an emoji reaction to an item. 243 | # 244 | def create_item_reaction(item_id, params = nil, headers = nil) 245 | post("/api/v2/items/#{item_id}/reactions", params, headers) 246 | end 247 | 248 | # ### Qiita::Client#create_project_reaction(project_id, params = nil, headers = nil) 249 | # Add an emoji reaction to an project. 250 | # 251 | def create_project_reaction(project_id, params = nil, headers = nil) 252 | post("/api/v2/projects/#{project_id}/reactions", params, headers) 253 | end 254 | 255 | # ### Qiita::Client#delete_comment_reaction(comment_id, reaction_name, params = nil, headers = nil) 256 | # Delete an emoji reaction from a comment. 257 | # 258 | def delete_comment_reaction(comment_id, reaction_name, params = nil, headers = nil) 259 | delete("/api/v2/comments/#{comment_id}/reactions/#{reaction_name}", params, headers) 260 | end 261 | 262 | # ### Qiita::Client#delete_item_reaction(item_id, reaction_name, params = nil, headers = nil) 263 | # Delete an emoji reaction from an item. 264 | # 265 | def delete_item_reaction(item_id, reaction_name, params = nil, headers = nil) 266 | delete("/api/v2/items/#{item_id}/reactions/#{reaction_name}", params, headers) 267 | end 268 | 269 | # ### Qiita::Client#delete_project_reaction(project_id, reaction_name, params = nil, headers = nil) 270 | # Delete an emoji reaction from an project. 271 | # 272 | def delete_project_reaction(project_id, reaction_name, params = nil, headers = nil) 273 | delete("/api/v2/projects/#{project_id}/reactions/#{reaction_name}", params, headers) 274 | end 275 | 276 | # ### Qiita::Client#list_comment_reactions(comment_id, params = nil, headers = nil) 277 | # List emoji reactions of an comment in recently-created order. 278 | # 279 | def list_comment_reactions(comment_id, params = nil, headers = nil) 280 | get("/api/v2/comments/#{comment_id}/reactions", params, headers) 281 | end 282 | 283 | # ### Qiita::Client#list_item_reactions(item_id, params = nil, headers = nil) 284 | # List emoji reactions of an item in recently-created order. 285 | # 286 | def list_item_reactions(item_id, params = nil, headers = nil) 287 | get("/api/v2/items/#{item_id}/reactions", params, headers) 288 | end 289 | 290 | # ### Qiita::Client#list_project_reactions(project_id, params = nil, headers = nil) 291 | # List emoji reactions of an project in recently-created order. 292 | # 293 | def list_project_reactions(project_id, params = nil, headers = nil) 294 | get("/api/v2/projects/#{project_id}/reactions", params, headers) 295 | end 296 | 297 | # ### Qiita::Client#list_tags(params = nil, headers = nil) 298 | # List tags in newest order. 299 | # 300 | def list_tags(params = nil, headers = nil) 301 | get("/api/v2/tags", params, headers) 302 | end 303 | 304 | # ### Qiita::Client#get_tag(tag_id, params = nil, headers = nil) 305 | # Get a tag. 306 | # 307 | def get_tag(tag_id, params = nil, headers = nil) 308 | get("/api/v2/tags/#{tag_id}", params, headers) 309 | end 310 | 311 | # ### Qiita::Client#list_user_following_tags(user_id, params = nil, headers = nil) 312 | # List tags a user is following to in recently-tagged order. 313 | # 314 | def list_user_following_tags(user_id, params = nil, headers = nil) 315 | get("/api/v2/users/#{user_id}/following_tags", params, headers) 316 | end 317 | 318 | # ### Qiita::Client#unfollow_tag(tag_id, params = nil, headers = nil) 319 | # Unfollow a tag. 320 | # 321 | def unfollow_tag(tag_id, params = nil, headers = nil) 322 | delete("/api/v2/tags/#{tag_id}/following", params, headers) 323 | end 324 | 325 | # ### Qiita::Client#get_tag_following(tag_id, params = nil, headers = nil) 326 | # Check if you are following a tag or not. 327 | # 328 | def get_tag_following(tag_id, params = nil, headers = nil) 329 | get("/api/v2/tags/#{tag_id}/following", params, headers) 330 | end 331 | 332 | # ### Qiita::Client#follow_tag(tag_id, params = nil, headers = nil) 333 | # Follow a tag. 334 | # 335 | def follow_tag(tag_id, params = nil, headers = nil) 336 | put("/api/v2/tags/#{tag_id}/following", params, headers) 337 | end 338 | 339 | # ### Qiita::Client#create_tagging(item_id, params = nil, headers = nil) 340 | # Add a tag to an item (only available on Qiita:Team) 341 | # 342 | def create_tagging(item_id, params = nil, headers = nil) 343 | post("/api/v2/items/#{item_id}/taggings", params, headers) 344 | end 345 | 346 | # ### Qiita::Client#delete_tagging(item_id, tagging_id, params = nil, headers = nil) 347 | # Remove a tag from an item (only available on Qiita:Team) 348 | # 349 | def delete_tagging(item_id, tagging_id, params = nil, headers = nil) 350 | delete("/api/v2/items/#{item_id}/taggings/#{tagging_id}", params, headers) 351 | end 352 | 353 | # ### Qiita::Client#list_teams(params = nil, headers = nil) 354 | # List teams the user belongs to in newest order. 355 | # 356 | def list_teams(params = nil, headers = nil) 357 | get("/api/v2/teams", params, headers) 358 | end 359 | 360 | # ### Qiita::Client#list_templates(params = nil, headers = nil) 361 | # List templates in a team. 362 | # 363 | def list_templates(params = nil, headers = nil) 364 | get("/api/v2/templates", params, headers) 365 | end 366 | 367 | # ### Qiita::Client#delete_template(template_id, params = nil, headers = nil) 368 | # Delete a template. 369 | # 370 | def delete_template(template_id, params = nil, headers = nil) 371 | delete("/api/v2/templates/#{template_id}", params, headers) 372 | end 373 | 374 | # ### Qiita::Client#get_template(template_id, params = nil, headers = nil) 375 | # Get a template. 376 | # 377 | def get_template(template_id, params = nil, headers = nil) 378 | get("/api/v2/templates/#{template_id}", params, headers) 379 | end 380 | 381 | # ### Qiita::Client#create_template(params = nil, headers = nil) 382 | # Create a new template. 383 | # 384 | def create_template(params = nil, headers = nil) 385 | post("/api/v2/templates", params, headers) 386 | end 387 | 388 | # ### Qiita::Client#update_template(template_id, params = nil, headers = nil) 389 | # Update a template. 390 | # 391 | def update_template(template_id, params = nil, headers = nil) 392 | patch("/api/v2/templates/#{template_id}", params, headers) 393 | end 394 | 395 | # ### Qiita::Client#list_item_stockers(item_id, params = nil, headers = nil) 396 | # List users who stocked an item in recent-stocked order. 397 | # 398 | def list_item_stockers(item_id, params = nil, headers = nil) 399 | get("/api/v2/items/#{item_id}/stockers", params, headers) 400 | end 401 | 402 | # ### Qiita::Client#list_users(params = nil, headers = nil) 403 | # List users in newest order. 404 | # 405 | def list_users(params = nil, headers = nil) 406 | get("/api/v2/users", params, headers) 407 | end 408 | 409 | # ### Qiita::Client#get_user(user_id, params = nil, headers = nil) 410 | # Get a user. 411 | # 412 | def get_user(user_id, params = nil, headers = nil) 413 | get("/api/v2/users/#{user_id}", params, headers) 414 | end 415 | 416 | # ### Qiita::Client#list_user_followees(user_id, params = nil, headers = nil) 417 | # List users a user is following. 418 | # 419 | def list_user_followees(user_id, params = nil, headers = nil) 420 | get("/api/v2/users/#{user_id}/followees", params, headers) 421 | end 422 | 423 | # ### Qiita::Client#list_user_followers(user_id, params = nil, headers = nil) 424 | # List users who are following a user. 425 | # 426 | def list_user_followers(user_id, params = nil, headers = nil) 427 | get("/api/v2/users/#{user_id}/followers", params, headers) 428 | end 429 | 430 | # ### Qiita::Client#unfollow_user(user_id, params = nil, headers = nil) 431 | # Unfollow a user. 432 | # 433 | def unfollow_user(user_id, params = nil, headers = nil) 434 | delete("/api/v2/users/#{user_id}/following", params, headers) 435 | end 436 | 437 | # ### Qiita::Client#get_user_following(user_id, params = nil, headers = nil) 438 | # Check if the current user is following a user. 439 | # 440 | def get_user_following(user_id, params = nil, headers = nil) 441 | get("/api/v2/users/#{user_id}/following", params, headers) 442 | end 443 | 444 | # ### Qiita::Client#follow_user(user_id, params = nil, headers = nil) 445 | # Follow a user. 446 | # 447 | def follow_user(user_id, params = nil, headers = nil) 448 | put("/api/v2/users/#{user_id}/following", params, headers) 449 | end 450 | end 451 | end 452 | -------------------------------------------------------------------------------- /lib/qiita/response.rb: -------------------------------------------------------------------------------- 1 | # ## Qiita::Response 2 | # A class for response data returned from API. 3 | # 4 | module Qiita 5 | class Response 6 | def initialize(faraday_response) 7 | @raw_body = faraday_response.body 8 | @raw_headers = faraday_response.headers 9 | @raw_status = faraday_response.status 10 | end 11 | 12 | # ### Qiita::Response#body 13 | # Returns response body returned from API as a `Hash` or an `Array` of `Hash`. 14 | # 15 | # ```rb 16 | # response.body #=> { ... } 17 | # ``` 18 | # 19 | def body 20 | @raw_body 21 | end 22 | 23 | # ### Qiita::Response#first_page_url 24 | # Returns first page URL or nil. 25 | # 26 | def first_page_url 27 | links_table["first"] 28 | end 29 | 30 | # ### Qiita::Response#last_page_url 31 | # Returns last page URL or nil. 32 | # 33 | def last_page_url 34 | links_table["last"] 35 | end 36 | 37 | # ### Qiita::Response#next_page_url 38 | # Returns next page URL or nil. 39 | # 40 | def next_page_url 41 | links_table["next"] 42 | end 43 | 44 | # ### Qiita::Response#previous_page_url 45 | # Returns previous page URL or nil. 46 | # 47 | def previous_page_url 48 | links_table["prev"] 49 | end 50 | 51 | # ### Qiita::Response#headers 52 | # Returns response headers returned from API as a `Hash`. 53 | # 54 | # ```rb 55 | # response.headers #=> { "Content-Type" => "application/json" } 56 | # ``` 57 | # 58 | def headers 59 | @headers ||= @raw_headers.inject({}) do |result, (key, value)| 60 | result.merge(key.split("-").map(&:capitalize).join("-") => value) 61 | end 62 | end 63 | 64 | # ### Qiita::Response#status 65 | # Returns response status code returned from API as a `Fixnum`. 66 | # 67 | # ```rb 68 | # response.status #=> 200 69 | # ``` 70 | # 71 | def status 72 | @raw_status 73 | end 74 | 75 | def to_s 76 | @to_s ||= ResponseRenderer.new(self, show_body: true, show_header: true).to_s 77 | end 78 | 79 | def status_message 80 | Rack::Utils::HTTP_STATUS_CODES[status] 81 | end 82 | 83 | private 84 | 85 | def links_table 86 | @links_table ||= (headers["Link"] || "").split(", ").inject({}) do |table, section| 87 | url, rel = section.match(/\A<(.+)>; rel="(.+)"\z/)[1..2] 88 | table.merge(rel => url) 89 | end 90 | end 91 | end 92 | end 93 | -------------------------------------------------------------------------------- /lib/qiita/response_renderer.rb: -------------------------------------------------------------------------------- 1 | module Qiita 2 | class ResponseRenderer 3 | def initialize(response, color: nil, show_header: nil, show_body: nil) 4 | @response = response 5 | @color = color 6 | @show_body = show_body 7 | @show_header = show_header 8 | end 9 | 10 | def to_s 11 | template % { 12 | status: status, 13 | headers: headers, 14 | body: body, 15 | } 16 | end 17 | 18 | private 19 | 20 | def template 21 | str = "" 22 | str << <<-EOS.strip_heredoc if @show_header 23 | HTTP/1.1 %{status} 24 | %{headers} 25 | EOS 26 | if has_body? && @show_body 27 | str << "\n" if @show_header 28 | str << "%{body}" 29 | end 30 | str 31 | end 32 | 33 | def headers 34 | @response.headers.sort_by do |key, value| 35 | key 36 | end.map do |key, value| 37 | "%{key}: %{value}" % { 38 | key: Rainbow(key.split("-").map(&:camelize).join("-")).underline, 39 | value: Rainbow(value).green, 40 | } 41 | end.join("\n") 42 | end 43 | 44 | def status 45 | Rainbow("#{@response.status} #{@response.status_message}").bright 46 | end 47 | 48 | def body 49 | if @color 50 | Rouge::Formatters::Terminal256.format( 51 | Rouge::Lexers::Ruby.new.lex(plain_body), 52 | theme: "github" 53 | ) 54 | else 55 | plain_body 56 | end 57 | end 58 | 59 | def plain_body 60 | if has_body? 61 | JSON.pretty_generate(@response.body) + "\n" 62 | else 63 | "" 64 | end 65 | end 66 | 67 | def Rainbow(str) 68 | if @color 69 | super 70 | else 71 | Rainbow::NullPresenter.new(str.to_s) 72 | end 73 | end 74 | 75 | def has_body? 76 | @response.status != 204 77 | end 78 | end 79 | end 80 | -------------------------------------------------------------------------------- /lib/qiita/version.rb: -------------------------------------------------------------------------------- 1 | module Qiita 2 | VERSION = "1.5.0" 3 | end 4 | -------------------------------------------------------------------------------- /qiita.gemspec: -------------------------------------------------------------------------------- 1 | lib = File.expand_path("../lib", __FILE__) 2 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 3 | require "qiita/version" 4 | 5 | Gem::Specification.new do |spec| 6 | spec.name = "qiita" 7 | spec.version = Qiita::VERSION 8 | spec.authors = ["Ryo Nakamura"] 9 | spec.email = ["r7kamura@gmail.com"] 10 | spec.summary = "A client library for Qiita API v2 written in Ruby." 11 | spec.homepage = "https://github.com/increments/qiita-rb" 12 | 13 | spec.files = `git ls-files`.split($/) 14 | spec.executables = spec.files.grep(%r{^bin/}).map{ |f| File.basename(f) } 15 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 16 | spec.require_paths = ["lib"] 17 | 18 | spec.add_dependency "activesupport" 19 | spec.add_dependency 'faraday', '>= 2', '< 3' 20 | spec.add_dependency "rack" 21 | spec.add_dependency "rainbow" 22 | spec.add_dependency "rouge" 23 | spec.add_dependency "slop", "< 4.0.0" 24 | spec.add_development_dependency "bundler", "~> 2.2" 25 | spec.add_development_dependency "json_schema" 26 | spec.add_development_dependency "pry" 27 | spec.add_development_dependency "rake" 28 | spec.add_development_dependency "rspec" 29 | spec.add_development_dependency "webmock" 30 | end 31 | -------------------------------------------------------------------------------- /script/generate: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # # script/generate 3 | # Generates source code text of Qiita::ResourceBasedMethods from given JSON Schema. 4 | # 5 | # ## Usage 6 | # Run this script with a path to JSON Schema of Qiita API v2, available only for staff. 7 | # 8 | # ```sh 9 | # bundle exec ./script/generate /path/to/json/schema > lib/qiita/resource_based_methods.rb 10 | # ``` 11 | # 12 | require "active_support/core_ext/object/try" 13 | require "active_support/core_ext/string/indent" 14 | require "active_support/core_ext/string/strip" 15 | require "json_schema" 16 | require "ostruct" 17 | require "yaml" 18 | 19 | # View object class to generate String representation of each method. 20 | method_class = Class.new(OpenStruct) do 21 | def to_s 22 | <<-EOS.strip_heredoc 23 | # ### Qiita::Client##{method_name}(#{arguments}) 24 | # #{description} 25 | # 26 | def #{method_name}(#{arguments}) 27 | #{request_method}(#{path}, params, headers) 28 | end 29 | EOS 30 | end 31 | 32 | private 33 | 34 | def arguments 35 | [required_argument_names + optional_arguments].join(", ") 36 | end 37 | 38 | def optional_arguments 39 | ["params = nil", "headers = nil"] 40 | end 41 | 42 | def path 43 | ?" + href.gsub(%r<:(\w+)(?=/|$)>) { '#{' + $1.split("/").last + "}" } + ?" 44 | end 45 | end 46 | 47 | path_to_json_schema = ARGV[0] 48 | hash = YAML.load_file(path_to_json_schema) 49 | schema = JsonSchema.parse!(hash).tap(&:expand_references!) 50 | links = schema.properties.values.map(&:links).flatten 51 | methods = links.map do |link| 52 | method_class.new( 53 | available_parameters: (link.schema.try(:properties) || {}).map do |name, property| 54 | { 55 | description: property.description, 56 | name: name, 57 | type: property.type, 58 | } 59 | end, 60 | description: link.description, 61 | href: link.href, 62 | method_name: link.title, 63 | request_method: link.method.downcase, 64 | required_argument_names: link.href.scan(%r<:([^/]+)>).flatten.map do |element| 65 | element.split("/").last 66 | end, 67 | ) 68 | end 69 | 70 | puts <<-EOS 71 | module Qiita 72 | module ResourceBasedMethods 73 | #{methods.join("\n").indent(4).strip} 74 | end 75 | end 76 | EOS 77 | -------------------------------------------------------------------------------- /spec/qiita/client_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe Qiita::Client do 4 | def stub_api_request 5 | stub_request(request_method, requested_url).with( 6 | body: request_body, 7 | headers: requested_headers, 8 | ).to_return( 9 | body: response_body, 10 | headers: response_headers, 11 | status: status_code, 12 | ) 13 | end 14 | 15 | let(:status_code) do 16 | 200 17 | end 18 | 19 | let(:response_body) do 20 | response_hash.to_json 21 | end 22 | 23 | let(:response_hash) do 24 | { "dummy" => "dummy" } 25 | end 26 | 27 | let(:response_headers) do 28 | { "Content-Type" => "application/json" } 29 | end 30 | 31 | let(:requested_headers) do 32 | Qiita::Client::DEFAULT_HEADERS.clone 33 | end 34 | 35 | let(:request_body) do 36 | params.nil? || [:get, :delete].include?(request_method) ? nil : params 37 | end 38 | 39 | let(:requested_url) do 40 | "https://#{requested_host}#{path}" 41 | end 42 | 43 | let(:requested_host) do 44 | Qiita::Client::DEFAULT_HOST 45 | end 46 | 47 | let(:host) do 48 | Qiita::Client::DEFAULT_HOST 49 | end 50 | 51 | let(:client) do 52 | described_class.new(**options) 53 | end 54 | 55 | let(:options) do 56 | {} 57 | end 58 | 59 | let(:arguments) do 60 | [path, params, headers] 61 | end 62 | 63 | let(:path) do 64 | "/api/v2/dummy" 65 | end 66 | 67 | let(:params) do 68 | end 69 | 70 | let(:headers) do 71 | {} 72 | end 73 | 74 | shared_examples_for "returns a Qiita::Response" do 75 | it "returns a Qiita::Response" do 76 | should be_a Qiita::Response 77 | subject.body.should eq response_hash 78 | subject.headers.should eq response_headers 79 | subject.status.should eq status_code 80 | end 81 | end 82 | 83 | shared_examples_for "valid condition" do 84 | context "with valid condition" do 85 | include_examples "returns a Qiita::Response" 86 | end 87 | end 88 | 89 | shared_examples_for "sends request with JSON-encoded body" do 90 | context "with params" do 91 | let(:params) do 92 | { key1: "value1", key2: "value2" } 93 | end 94 | 95 | it "sends request with JSON-encoded body" do 96 | should be_a Qiita::Response 97 | end 98 | end 99 | end 100 | 101 | shared_examples_for "sends request with URL query" do 102 | context "with params" do 103 | let(:params) do 104 | { key1: "value1", key2: "value2" } 105 | end 106 | 107 | let(:requested_url) do 108 | super() + "?key1=value1&key2=value2" 109 | end 110 | 111 | it "sends request with URL query" do 112 | should be_a Qiita::Response 113 | end 114 | end 115 | end 116 | 117 | describe ".new" do 118 | subject do 119 | described_class.new(**options) 120 | end 121 | 122 | let(:options) do 123 | {} 124 | end 125 | 126 | shared_examples_for "returns a Qiita::Client" do 127 | it "returns a Qiita::Client" do 128 | should be_a described_class 129 | end 130 | end 131 | 132 | context "without any options" do 133 | let(:options) do 134 | {} 135 | end 136 | include_examples "returns a Qiita::Client" 137 | end 138 | 139 | context "with :access_token option" do 140 | let(:options) do 141 | { access_token: "dummy" } 142 | end 143 | include_examples "returns a Qiita::Client" 144 | end 145 | 146 | context "with :host option" do 147 | let(:options) do 148 | { host: "dummy" } 149 | end 150 | include_examples "returns a Qiita::Client" 151 | end 152 | end 153 | 154 | describe "#get" do 155 | before do 156 | stub_api_request 157 | end 158 | 159 | subject do 160 | client.get(*arguments) 161 | end 162 | 163 | let(:request_method) do 164 | :get 165 | end 166 | 167 | include_examples "valid condition" 168 | include_examples "sends request with URL query" 169 | 170 | context "without headers" do 171 | let(:arguments) do 172 | [path, params] 173 | end 174 | include_examples "returns a Qiita::Response" 175 | end 176 | 177 | context "without params nor headers" do 178 | let(:arguments) do 179 | [path] 180 | end 181 | include_examples "returns a Qiita::Response" 182 | end 183 | 184 | context "with a Qiita::Client created with :host option" do 185 | before do 186 | options[:host] = host 187 | end 188 | 189 | let(:host) do 190 | "example.com" 191 | end 192 | 193 | let(:requested_host) do 194 | host 195 | end 196 | 197 | it "sends request to configured host" do 198 | should be_a Qiita::Response 199 | end 200 | end 201 | 202 | context "with :host and :team options" do 203 | before do 204 | options[:host] = host 205 | options[:team] = team 206 | end 207 | 208 | let(:host) do 209 | "example.com" 210 | end 211 | 212 | let(:team) do 213 | "test" 214 | end 215 | 216 | let(:requested_host) do 217 | [team, host].join(".") 218 | end 219 | 220 | it "sends request to configured host including team as subdomain" do 221 | should be_a Qiita::Response 222 | end 223 | end 224 | 225 | context "with a Qiita::Client created with :access_token option" do 226 | before do 227 | options[:access_token] = access_token 228 | end 229 | 230 | let(:access_token) do 231 | "dummy-access-token" 232 | end 233 | 234 | let(:requested_headers) do 235 | super().merge("Authorization" => "Bearer #{access_token}") 236 | end 237 | 238 | it "sends request with configured access token" do 239 | should be_a Qiita::Response 240 | end 241 | end 242 | 243 | context "with headers" do 244 | let(:headers) do 245 | { 246 | "KEY1" => "value1", 247 | "KEY2" => "value2", 248 | } 249 | end 250 | 251 | let(:requested_headers) do 252 | super().merge(headers) 253 | end 254 | 255 | it "sends request with given headers" do 256 | should be_a Qiita::Response 257 | end 258 | end 259 | 260 | context "with params" do 261 | let(:params) do 262 | { key1: "value1", key2: "value2" } 263 | end 264 | 265 | let(:requested_url) do 266 | super() + "?key1=value1&key2=value2" 267 | end 268 | 269 | it "sends request with URL query, encoded from given params" do 270 | should be_a Qiita::Response 271 | end 272 | end 273 | 274 | context "with multibyte path" do 275 | let(:path) do 276 | "" 277 | end 278 | 279 | let(:requested_url) do 280 | "https://#{requested_host}#{CGI.escape(path)}" 281 | end 282 | 283 | it "sends request with escaped path" do 284 | should be_a Qiita::Response 285 | end 286 | end 287 | end 288 | 289 | describe "#post" do 290 | before do 291 | stub_api_request 292 | end 293 | 294 | subject do 295 | client.post(*arguments) 296 | end 297 | 298 | let(:request_method) do 299 | :post 300 | end 301 | 302 | let(:status_code) do 303 | 201 304 | end 305 | 306 | include_examples "valid condition" 307 | include_examples "sends request with JSON-encoded body" 308 | end 309 | 310 | describe "#patch" do 311 | before do 312 | stub_api_request 313 | end 314 | 315 | subject do 316 | client.patch(*arguments) 317 | end 318 | 319 | let(:request_method) do 320 | :patch 321 | end 322 | 323 | include_examples "valid condition" 324 | include_examples "sends request with JSON-encoded body" 325 | end 326 | 327 | describe "#put" do 328 | before do 329 | stub_api_request 330 | end 331 | 332 | subject do 333 | client.put(*arguments) 334 | end 335 | 336 | let(:request_method) do 337 | :put 338 | end 339 | 340 | let(:status_code) do 341 | 204 342 | end 343 | 344 | let(:response_hash) do 345 | nil 346 | end 347 | 348 | include_examples "valid condition" 349 | include_examples "sends request with JSON-encoded body" 350 | end 351 | 352 | describe "#delete" do 353 | before do 354 | stub_api_request 355 | end 356 | 357 | subject do 358 | client.delete(*arguments) 359 | end 360 | 361 | let(:request_method) do 362 | :delete 363 | end 364 | 365 | let(:status_code) do 366 | 204 367 | end 368 | 369 | let(:response_hash) do 370 | nil 371 | end 372 | 373 | include_examples "valid condition" 374 | include_examples "sends request with URL query" 375 | end 376 | end 377 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require "qiita" 2 | require "webmock/rspec" 3 | 4 | RSpec.configure do |config| 5 | config.treat_symbols_as_metadata_keys_with_true_values = true 6 | config.run_all_when_everything_filtered = true 7 | config.filter_run :focus 8 | end 9 | --------------------------------------------------------------------------------