# frozen_string_literal: true require "uri" require "trello" require "trellodon/entities" module Trellodon class Client class DeletedMember < Struct.new(:id) def full_name = "[DELETED]" def username = "__deleted__" end def initialize(api_key:, api_token:, logger: Config.logger) @logger = logger @client = Trello::Client.new( developer_public_key: api_key, member_token: api_token ) @members = Concurrent::Map.new end def fetch_board(board_id) retrying do board = @client.find(:boards, board_id) Board.new( id: board.id, name: board.name, lists: lists_from(board), card_ids: @client.find_many(Trello::Card, "/boards/#{board_id}/cards", fields: "id").map(&:id), last_activity_date: board.last_activity_date ) end end def fetch_card(card_id) retrying do card = @client.find(:cards, card_id) Card.new( id: card.id, name: card.name, desc: card.desc, labels: card.labels.map(&:name), list_id: card.list_id, comments: comments_from(card), attachments: attachments_from(card), checklists: checklists_from(card), last_activity_date: card.last_activity_date ) end end private attr_reader :logger def retrying(&block) attempt = 0 begin block.call rescue Trello::Error => err raise unless err.status_code == 429 attempt += 1 cooldown = 2**attempt + rand(2**attempt) - 2**(attempt - 1) logger.warn "API limit exceeded, cool down for #{cooldown}s" sleep cooldown retry end end def comments_from(card) card.comments.map do |comment| Comment.new( text: comment.data["text"], date: comment.date, creator: member_from(comment.creator_id) ) end end def attachments_from(card) card.attachments.map do |attach| Attachment.new( file_name: attach.is_upload ? URI.decode_www_form_component(attach.file_name) : attach.file_name, mime_type: attach.mime_type, bytes: attach.bytes, date: attach.date, name: attach.name, is_upload: attach.is_upload, added_by: member_from(attach.member_id), headers: attach.is_upload ? {"Authorization" => "OAuth oauth_consumer_key=\"#{@client.configuration.developer_public_key}\", oauth_token=\"#{@client.configuration.member_token}\""} : {}, url: attach.url ) end end def lists_from(board) board.lists.map do |list| List.new( id: list.id, name: list.name, board_id: list.board_id ) end end def checklists_from(card) card.checklists.map do |checklist| Checklist.new( id: checklist.id, name: checklist.name, items: checklist_items_from(checklist) ) end end def checklist_items_from(checklist) checklist.items.map do |item| ChecklistItem.new( id: item.id, name: item.name, state: item.state, member: member_from(item.member_id), due_date: item.due_date ) end end def member_from(id) return nil unless id @members.compute_if_absent(id) do member = begin @client.find(:members, id) rescue Trello::Error => err raise unless err.status_code == 404 DeletedMember.new(id) end Member.new( id: member.id, full_name: member.full_name, username: member.username ) end end end end