commit baf4860e4612fb7b1470c4b3d80946a7d1217918 Author: viletyy Date: Fri Apr 15 15:50:33 2022 +0800 init repository diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..a8b8ace --- /dev/null +++ b/Gemfile @@ -0,0 +1,7 @@ +source "https://rubygems.org" + +gem 'rest-client' + +group :test do + gem 'rake' +end \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..43b8992 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,31 @@ +GEM + remote: https://rubygems.org/ + specs: + domain_name (0.5.20190701) + unf (>= 0.0.5, < 1.0.0) + http-accept (1.7.0) + http-cookie (1.0.4) + domain_name (~> 0.5) + mime-types (3.4.1) + mime-types-data (~> 3.2015) + mime-types-data (3.2022.0105) + netrc (0.11.0) + rake (13.0.6) + rest-client (2.1.0) + http-accept (>= 1.7.0, < 2.0) + http-cookie (>= 1.0.2, < 2.0) + mime-types (>= 1.16, < 4.0) + netrc (~> 0.8) + unf (0.1.4) + unf_ext + unf_ext (0.0.8.1) + +PLATFORMS + x86_64-darwin-20 + +DEPENDENCIES + rake + rest-client + +BUNDLED WITH + 2.2.19 diff --git a/README.md b/README.md new file mode 100644 index 0000000..3639bed --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ + +# GiteaClient + +[![GiteaClient](https://img.shields.io/badge/readme%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/viletyy/gitea-client) + +Gitea Api 库 + +## 内容列表 + +- [GiteaClient](#giteaclient) + - [内容列表](#内容列表) + - [使用说明](#使用说明) + - [如何贡献](#如何贡献) + - [使用许可](#使用许可) + +## 使用说明 + +```ruby +require 'gitea-client' + +client = Gitea::Api::Client.new({domain: YOUR_GITEA_SERVER_DOMAIN, base_url: YOUR_GITEA_API_BASE_URL, token: YOUR_TOKRN, username: YOUR_USERNAME, password: YOUR_PASSWORD}) + +client.get_users_search({query: {q: 'viletyy'}}) + +``` + +## 如何贡献 + +非常欢迎你的加入![提一个 Issue](https://github.com/viletyy/gitea-client/issues/new) 或者提交一个 Pull Request。 + + +## 使用许可 + +[MIT]() © ViletYy diff --git a/bin/giteaclient b/bin/giteaclient new file mode 100644 index 0000000..2aa218c --- /dev/null +++ b/bin/giteaclient @@ -0,0 +1,8 @@ +#!/usr/bin/env ruby + +require 'rest-client' +require './lib/gitea/api' +require './lib/gitea/version' + +require "irb" +IRB.start \ No newline at end of file diff --git a/lib/gitea/api.rb b/lib/gitea/api.rb new file mode 100644 index 0000000..6c23e8a --- /dev/null +++ b/lib/gitea/api.rb @@ -0,0 +1,6 @@ +require_relative 'common' +require_relative 'api/user' +require_relative 'api/client' +require_relative 'api/config' +require_relative 'api/exception' +require_relative 'api/http' diff --git a/lib/gitea/api/client.rb b/lib/gitea/api/client.rb new file mode 100644 index 0000000..ea0291e --- /dev/null +++ b/lib/gitea/api/client.rb @@ -0,0 +1,23 @@ +module Gitea + module Api + class Client + + # 构造Api client,用于操作Api数据 + # @param opts [Hash] 构造client时的参数选项 + # @option opts [string] domain [必填] gitea服务地址 + # @option opts [string] base_url [必填] api相对路径 + # @option opts [string] admin_username [必填] 管理员账号 + # @option opts [string] admin_password [必填] 管理员密码 + # @option opts [string] username [选填] 用户账号 + # @option opts [string] password [选填] 用户密码 + # @option opts [string] gitea_token [选填] 用户token + def initialize(opts) + @config = Config.new(opts) + @http = Http.new(@config) + end + + include Gitea::Api::User + + end + end # User +end # Gitea \ No newline at end of file diff --git a/lib/gitea/api/config.rb b/lib/gitea/api/config.rb new file mode 100644 index 0000000..12453f8 --- /dev/null +++ b/lib/gitea/api/config.rb @@ -0,0 +1,34 @@ +module Gitea + module Api + + class Config < Common::Struct::Base + attrs :domain, :base_url, :username, :password, :token, :open_timeout, :read_timeout + + def initialize(opts = {}) + super(opts) + + valid! + normalize_domain + end + + private + def valid! + if @domain.nil? || @base_url.nil? + fail Exception, "@domain, @base_url not permit blank." + end + end + + def normalize_domain + uri = URI.parse(domain) + uri = URI.parse(domain) + uri = URI.parse("http://#{domain}") unless uri.scheme + + if uri.scheme != 'http' and uri.scheme != 'https' + fail ClientError, "Only HTTP and HTTPS domain are accepted." + end + + @domain = uri + end + end + end # Common +end # Gitea \ No newline at end of file diff --git a/lib/gitea/api/exception.rb b/lib/gitea/api/exception.rb new file mode 100644 index 0000000..caaac5f --- /dev/null +++ b/lib/gitea/api/exception.rb @@ -0,0 +1,25 @@ +module Gitea + module Api + class ServerError < Common::Exception + attr_reader :http_code, :error_code, :message + + def initialize(response) + @http_code = response.code + + puts response + end + + def to_s + @attrs.merge({'HTTPCode' => @http_code}).map do |k, v| + [k, v].join(": ") + end.join(", ") + end + end + + class CallBackError < ServerError + end # CallBackError + + class ClientError < Common::Exception + end # ClientError + end # Api +end # Git \ No newline at end of file diff --git a/lib/gitea/api/http.rb b/lib/gitea/api/http.rb new file mode 100644 index 0000000..c329172 --- /dev/null +++ b/lib/gitea/api/http.rb @@ -0,0 +1,112 @@ +require 'base64' + +module Gitea + module Api + class Http + DEFAULT_CONTENT_TYPE = 'application/json' + TOKEN_HEADER = 'Authorization' + OPEN_TIMEOUT = 10 + READ_TIMEOUT = 120 + + include Common::Logging + + def initialize(config) + @config = config + end + + def get_request_url(api_url) + url = @config.domain.dup + url.query = nil + url.fragment = nil + [url.to_s, @config.base_url, api_url].join('') + end + + def get(api_url = '', http_options = {}, &block) + do_request('GET', api_url, http_options, &block) + end + + def put(api_url = '', http_options = {}, &block) + do_request('PUT', api_url, http_options, &block) + end + + def post(api_url = '', http_options = {}, &block) + do_request('POST', api_url, http_options, &block) + end + + def delete(api_url = '', http_options = {}, &block) + do_request('DELETE', api_url, http_options, &block) + end + + def head(api_url = '', http_options = {}, &block) + do_request('HEAD', api_url, http_options, &block) + end + + def options(api_url = '', http_options = {}, &block) + do_request('OPTIONS', api_url, http_options, &block) + end + + private + # Do Http request + def do_request(verb, api_url, http_options = {}, &block) + + headers = http_options[:headers] || {} + headers['user-agent'] = get_user_agent + headers['date'] = Time.now.httpdate + headers['content-type'] ||= DEFAULT_CONTENT_TYPE + headers[TOKEN_HEADER] = @config.token if @config.token + + if @config.username and @config.password + headers[TOKEN_HEADER] = 'Basic ' + Base64::Encoding(@config.username + ":" + @config.password) + end + + headers[:params] = http_options[:query] || {} + + logger.debug("Send HTTP request, verb: #{verb}, http_options: #{http_options}") + + request = RestClient::Request.new( + :method => verb, + :url => get_request_url(api_url), + :headers => headers, + :payload => http_options[:body], + :open_timeout => @config.open_timeout || OPEN_TIMEOUT, + :read_timeout => @config.read_timeout || READ_TIMEOUT + ) + + + response = request.execute do |resp, &blk| + if resp.code >= 300 + e = ServerError.new(resp) + logger.error(e.to_s) + raise e + else + resp.return!(&blk) + end + end + + # If streaming read_body is used, we need to create the + # RestClient::Response ourselves + unless response.is_a?(RestClient::Response) + if response.code.to_i >= 300 + body = response.body + if RestClient::version < '2.1.0' + body = RestClient::Request.decode(response['content-encoding'], response.body) + end + response = RestClient::Response.create(body, response, request) + e = ServerError.new(response) + logger.error(e.to_s) + raise e + end + response = RestClient::Response.create(nil, response, request) + response.return! + end + + response + end + + def get_user_agent + "gitea-client/#{VERSION}" + end + + end + end # Common +end # Gitea \ No newline at end of file diff --git a/lib/gitea/api/user.rb b/lib/gitea/api/user.rb new file mode 100644 index 0000000..1ded9fc --- /dev/null +++ b/lib/gitea/api/user.rb @@ -0,0 +1,199 @@ +module Gitea + module Api + module User + + def get_user(opt={}) + @http.get("/user", opt) + end + + def get_user_applications_oauth2(opt={}) + @http.get("/user/applications/oauth2", opt) + end + + def post_user_applications_oauth2(opt={}) + @http.post("/user/applications/oauth2", opt) + end + + def get_user_applications_oauth2_by_id(id, opt={}) + @http.get("/user/applications/oauth2/#{id}", opt) + end + + def delete_user_applications_oauth2_by_id(id, opt={}) + @http.delete("/user/applications/oauth2/#{id}", opt) + end + + def patch_user_applications_oauth2_by_id(id, opt={}) + @http.patch("/user/applications/oauth2/#{id}", opt) + end + + def get_user_emails(opt={}) + @http.get("/user/emails", opt) + end + + def post_user_emails(opt={}) + @http.post("/user/emails", opt) + end + + def delete_user_emails(opt={}) + @http.delete("/user/emails", opt) + end + + def get_user_followers(opt={}) + @http.get("/user/followers", opt) + end + + def get_user_following(opt={}) + @http.get("/user/following", opt) + end + + def get_user_following_by_username(username, opt={}) + @http.get("/user/following/#{username}", opt) + end + + def put_user_following_by_username(username, opt={}) + @http.put("/user/following/#{username}", opt) + end + + def delete_user_following_by_username(username, opt={}) + @http.delete("/user/following/#{username}", opt) + end + + def get_user_gpg_key_token(opt={}) + @http.get("/user/gpg_key_token", opt) + end + + def post_user_gpg_key_verify(opt={}) + @http.get("/user/gpg_key_verify", opt) + end + + def get_user_gpg_keys(opt={}) + @http.get("/user/gpg_keys", opt) + end + + def post_user_gpg_keys(opt={}) + @http.post("/user/gpg_keys", opt) + end + + def get_user_gpg_keys_by_id(id, opt={}) + @http.get("/user/gpg_keys/#{id}", opt) + end + + def delete_user_gpg_keys_by_id(id, opt={}) + @http.delete("/user/gpg_keys/#{id}", opt) + end + + def get_user_keys(opt={}) + @http.get("/user/keys", opt) + end + + def post_user_keys(opt={}) + @http.post("/user/keys", opt) + end + + def get_user_keys_by_id(id, opt={}) + @http.get("/user/keys/#{id}", opt) + end + + def delete_user_keys_by_id(id, opt={}) + @http.delete("/user/keys/#{id}", opt) + end + + def get_user_repos(opt={}) + @http.get("/user/repos", opt) + end + + def post_user_repos(opt={}) + @http.post("/user/repos", opt) + end + + def get_user_settings(opt={}) + @http.get("/user/settings", opt) + end + + def patch_user_settings(opt={}) + @http.patch("/user/settings", opt) + end + + def get_user_starred(opt={}) + @http.get("/user/starred", opt) + end + + def get_user_starred_by_owner_repo(owner, repo, opt={}) + @http.get("/user/starred/#{owner}/#{repo}", opt) + end + + def put_user_starred_by_owner_repo(owner, repo, opt={}) + @http.put("/user/starred/#{owner}/#{repo}", opt) + end + + def delete_user_starred_by_owner_repo(owner, repo, opt={}) + @http.delete("/user/starred/#{owner}/#{repo}", opt) + end + + def get_user_stopwatches(opt={}) + @http.get("/user/stopwatches", opt) + end + + def get_user_subscriptions(opt={}) + @http.get("/user/subscriptions", opt) + end + + def get_user_teams(opt={}) + @http.get("/user/teams", opt) + end + + def get_user_times(opt={}) + @http.get("/user/times", opt) + end + + def get_users_search(opt={}) + @http.get("/users/search", opt) + end + + def get_users_following_by_follower_followee(follower, followee, opt={}) + @http.get("/users/#{follower}/following/#{followee}", opt) + end + + def get_users_followers_by_username(username, opt={}) + @http.get("/users/#{username}/followers", opt) + end + + def get_users_following_by_username(username, opt={}) + @http.get("/user/#{username}/following", opt) + end + + def get_users_gpg_keys_by_username(username, opt={}) + @http.get("/user/#{username}/gpg_keys", opt) + end + + def get_users_keys_by_username(username, opt={}) + @http.get("/user/#{username}/keys", opt) + end + + def get_users_repos_by_username(username, opt={}) + @http.get("/users/#{username}/repos", opt) + end + + def get_users_starred_by_username(username, opt={}) + @http.get("/users/#{username}/starred", opt) + end + + def get_users_subscriptions_by_username(username, opt={}) + @http.get("/users/#{username}/subscriptions", opt) + end + + def get_users_token_by_username(username, opt={}) + @http.get("/users/#{username}/tokens", opt) + end + + def post_users_token_by_username(username, opt={}) + @http.get("/users/#{username}/tokens", opt) + end + + def delete_users_token_by_username_token(username, token, opt={}) + @http.delete("/users/#{username}/tokens/#{token}", opt) + end + + end + end # Api +end # Gitea \ No newline at end of file diff --git a/lib/gitea/common.rb b/lib/gitea/common.rb new file mode 100644 index 0000000..8d5766c --- /dev/null +++ b/lib/gitea/common.rb @@ -0,0 +1,3 @@ +require_relative 'common/exception' +require_relative 'common/logging' +require_relative 'common/struct' diff --git a/lib/gitea/common/exception.rb b/lib/gitea/common/exception.rb new file mode 100644 index 0000000..478e361 --- /dev/null +++ b/lib/gitea/common/exception.rb @@ -0,0 +1,12 @@ +module Gitea + module Common + + class Exception < RuntimeError + attr_reader :message + + def initialize(message) + @message = message + end + end + end # Common +end # Gitea \ No newline at end of file diff --git a/lib/gitea/common/logging.rb b/lib/gitea/common/logging.rb new file mode 100644 index 0000000..11bb253 --- /dev/null +++ b/lib/gitea/common/logging.rb @@ -0,0 +1,62 @@ +require 'logger' + +module Gitea + module Common + + ## + # Logging support + # @example + # include Logging + # logger.info(xxx) + + module Logging + + MAX_NUM_LOG = 100 + ROTATE_SIZE = 10 * 1024 * 1024 + + # level = Logger::DEBUG | Logger::INFO | Logger::ERROR | Logger::FATAL + def self.set_log_level(level) + Logging.logger.level = level + end + + # 设置日志输出的文件 + def self.set_log_file(file) + @log_file = file + end + + # 获取logger + def logger + Logging.logger + end + + private + + def self.logger + unless @logger + @log_file ||= ENV["GITEA_CLIENT_LOG_PATH"] + @logger = Logger.new( + @log_file, MAX_NUM_LOG, ROTATE_SIZE) + @logger.level = get_env_log_level || Logger::INFO + end + @logger + end + + def self.get_env_log_level + return unless ENV["GITEA_CLIENT_LOG_LEVEL"] + case ENV["GITEA_CLIENT_LOG_LEVEL"].upcase + when "DEBUG" + Logger::DEBUG + when "WARN" + Logger::WARN + when "ERROR" + Logger::ERROR + when "FATAL" + Logger::FATAL + when "UNKNOWN" + Logger::UNKNOWN + end + end + + end + end # Common +end # Gitea \ No newline at end of file diff --git a/lib/gitea/common/struct.rb b/lib/gitea/common/struct.rb new file mode 100644 index 0000000..aeaed10 --- /dev/null +++ b/lib/gitea/common/struct.rb @@ -0,0 +1,37 @@ +module Gitea + module Common + module Struct + class Base + module AttrHelper + def attrs(*s) + define_method(:attrs) {s} + attr_reader(*s) + end + end + + extend AttrHelper + + def initialize(opts = {}) + extra_keys = opts.keys - attrs + unless extra_keys.empty? + fail Common::Exception, + "Unexpected extra keys: #{extra_keys.join(', ')}" + end + + attrs.each do |attr| + instance_variable_set("@#{attr}", opts[attr]) + end + end + + def to_s + attrs.map do |attr| + v = instance_variable_get("@#{attr}") + "#{attr.to_s}: #{v}" + end.join(", ") + end + + end # Base + end # Struct + + end # Common +end # Gitea \ No newline at end of file diff --git a/lib/gitea/version.rb b/lib/gitea/version.rb new file mode 100644 index 0000000..ae2e573 --- /dev/null +++ b/lib/gitea/version.rb @@ -0,0 +1,5 @@ +module Gitea + + VERSION = "0.0.1" + +end \ No newline at end of file