# typed: strict require 'json' require 'httparty' require_relative '../structs/signature' require_relative '../structs/task' module Researchable module FreedcampApi module Sessions class SignedSession < Session extend T::Sig extend T::Helpers BASE = T.let('https://freedcamp.com/api/v1'.freeze, String) HEADERS = T.let({ 'Content-Type' => 'application/x-www-form-urlencoded' }.freeze, T::Hash[T.untyped, T.untyped]) # rubocop:disable Style/AccessorGrouping sig { returns(String) } attr_reader :public_key sig { returns(String) } attr_reader :secret_key sig { returns(T::Boolean) } attr_reader :use_cache # rubocop:enable Style/AccessorGrouping sig { params(public_key: String, secret_key: String, use_cache: T::Boolean).void } def initialize(public_key: ENV['PUBLIC_KEY'], secret_key: ENV['SECRET_KEY'], use_cache: true) @public_key = public_key @secret_key = secret_key @use_cache = use_cache end sig { override.params(path: String).returns(T::Hash[T.untyped, T.untyped]) } def get(path) call_url(path, :get) end # (3) sig do override.params( path: String, data: T.any( Researchable::FreedcampApi::Structs::Task, Researchable::FreedcampApi::Structs::Comment, T::Hash[T.untyped, T.untyped] ) ).returns(T.untyped) end def post(path, data) call_url(path, :post, data) end private sig do params( path: String, method: Symbol, data: T.any( Researchable::FreedcampApi::Structs::Task, Researchable::FreedcampApi::Structs::Comment, T::Hash[T.untyped, T.untyped] ) ).returns(T::Hash[T.untyped, T.untyped]) end def call_url(path, method, data = {}) cache_url = "output/#{Digest::SHA1.hexdigest(path + method.to_s)}" content = if data == {} && File.exist?(cache_url) && @use_cache render_cache(cache_url) else render_call(path, method, data, cache_url) end content = JSON.parse(content) if content content end def render_cache(cache_url) file = File.open(cache_url) content = file.read end def render_call(path, method, data, cache_url) signature = generate_signature url = make_url(path, signature) data = serialize_data(data) content = HTTParty.send(method, url, body: data, headers: HEADERS).body File.open(cache_url, 'w') { |f| f.write content } content end def serialize_data(data = {}) return {} if data == {} data = data.serialize if data.is_a? T::Struct data = { data: data.to_json } end sig { params(path: String, signature: Signature).returns(String) } def make_url(path, signature) result = BASE + path sep = result.include?('?') ? '&' : '?' result + "#{sep}api_key=#{signature.api_key}×tamp=#{signature.timestamp}&hash=#{signature.calculated_hash}" end sig { returns(Signature) } def generate_signature time = Time.new.to_i * 1000 data = "#{@public_key}#{time}" signature = OpenSSL::HMAC.hexdigest('sha1', @secret_key, data) Signature.new( timestamp: time, api_key: @public_key, calculated_hash: signature ) end end end end end