87 lines
2.9 KiB
Ruby
87 lines
2.9 KiB
Ruby
|
|
require_relative "config"
|
||
|
|
|
||
|
|
module Backlog
|
||
|
|
# Resolves a board name to its Planka board record, caching the resolved
|
||
|
|
# id as `board_id=<id>` in the repo's `.cc-os/config` (issue #22).
|
||
|
|
#
|
||
|
|
# The tracker's board *name* stays the source of truth; the id is only a
|
||
|
|
# cache. The cached id is honored only when the config's tracker is
|
||
|
|
# exactly `planka:<board_name>`, and a cached `boards.get(id)` hit counts
|
||
|
|
# only if the returned board's name still matches — a 404 or a rename
|
||
|
|
# falls back to the projects+boards name lookup and rewrites the cache.
|
||
|
|
# Any cache failure (unreadable config, unwritable config, stale id)
|
||
|
|
# degrades to the name lookup; it never fails the command.
|
||
|
|
#
|
||
|
|
# Filesystem access is injected: `read_config` returns the raw
|
||
|
|
# `.cc-os/config` contents (or nil), `write_config` persists updated
|
||
|
|
# contents. Cache writes go through Config.set, the comment-preserving
|
||
|
|
# writer — never Config.merge.
|
||
|
|
class BoardResolver
|
||
|
|
# @param client [Planka::Client] an already-authenticated client
|
||
|
|
# @param read_config [#call] -> String, nil — raw .cc-os/config contents
|
||
|
|
# @param write_config [#call] (String) -> void — persist updated contents
|
||
|
|
def initialize(client:, read_config:, write_config:)
|
||
|
|
@client = client
|
||
|
|
@read_config = read_config
|
||
|
|
@write_config = write_config
|
||
|
|
end
|
||
|
|
|
||
|
|
# A resolver with no config access: always name-lookup, never caches.
|
||
|
|
def self.null(client:)
|
||
|
|
new(client: client, read_config: -> {}, write_config: ->(_) {})
|
||
|
|
end
|
||
|
|
|
||
|
|
# @param name [String] the board name (source of truth)
|
||
|
|
# @return [Planka::Types::Board]
|
||
|
|
# @raise [RuntimeError] if no board with that name exists
|
||
|
|
def board_for(name)
|
||
|
|
cached = cached_board(name)
|
||
|
|
return cached if cached
|
||
|
|
|
||
|
|
board = lookup_by_name!(name)
|
||
|
|
cache_id(name, board.id)
|
||
|
|
board
|
||
|
|
end
|
||
|
|
|
||
|
|
private
|
||
|
|
|
||
|
|
def cached_board(name)
|
||
|
|
config = safe_config
|
||
|
|
return nil unless config && config.tracker == "planka:#{name}"
|
||
|
|
|
||
|
|
id = config.board_id
|
||
|
|
return nil unless id
|
||
|
|
|
||
|
|
board = @client.boards.get(id).board
|
||
|
|
board && board.name == name ? board : nil
|
||
|
|
rescue StandardError
|
||
|
|
nil
|
||
|
|
end
|
||
|
|
|
||
|
|
def lookup_by_name!(name)
|
||
|
|
@client.projects.list.each do |project|
|
||
|
|
board = @client.boards.list(project.id).find { |b| b.name == name }
|
||
|
|
return board if board
|
||
|
|
end
|
||
|
|
raise "no board named #{name.inspect} found (run board-ensure first)"
|
||
|
|
end
|
||
|
|
|
||
|
|
def cache_id(name, id)
|
||
|
|
contents = @read_config.call
|
||
|
|
config = Config.new(contents)
|
||
|
|
return unless config.tracker == "planka:#{name}"
|
||
|
|
return if config.board_id == id.to_s
|
||
|
|
|
||
|
|
@write_config.call(Config.set(contents, "board_id", id))
|
||
|
|
rescue StandardError
|
||
|
|
nil # cache write failure never fails the command
|
||
|
|
end
|
||
|
|
|
||
|
|
def safe_config
|
||
|
|
Config.new(@read_config.call)
|
||
|
|
rescue StandardError
|
||
|
|
nil
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|