Module: ActiveProject::Adapters::Fizzy::Issues

Included in:
ActiveProject::Adapters::FizzyAdapter
Defined in:
lib/active_project/adapters/fizzy/issues.rb

Instance Method Summary collapse

Instance Method Details

#create_issue(board_id, attributes) ⇒ ActiveProject::Resources::Issue

Creates a new card in a board.

Parameters:

  • The ULID of the Fizzy board.

  • Card attributes.

    • :title [String] Required. The card title.

    • :description [String] Optional. Rich text description (HTML).

    • :status [String] Optional. Initial status: published (default), drafted.

    • :tag_ids [Array<String>] Optional. Tag IDs to apply.

Returns:

  • The created issue resource.



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/active_project/adapters/fizzy/issues.rb', line 69

def create_issue(board_id, attributes)
  title = attributes[:title]
  unless title && !title.empty?
    raise ArgumentError, "Missing required attribute for Fizzy card creation: :title"
  end

  path = "boards/#{board_id}/cards.json"
  payload = {
    card: {
      title: title,
      description: attributes[:description],
      status: attributes[:status] || "published",
      tag_ids: attributes[:tag_ids]
    }.compact
  }

  # Fizzy returns 201 Created with Location header
  response = @connection.post(path) do |req|
    req.body = payload.to_json
  end

  # Extract card number from Location header and fetch it
  location = response.headers["Location"]
  if location
    card_number = location.match(%r{/cards/(\d+)})[1]
    find_issue(card_number)
  else
    # Fallback: parse response body if available
    card_data = parse_response(response)
    map_card_data(card_data, board_id)
  end
rescue Faraday::Error => e
  handle_faraday_error(e)
end

#delete_issue(card_number, context = {}) ⇒ Boolean

Deletes a card.

Parameters:

  • The card number to delete.

  • (defaults to: {})

    Optional context (not required for Fizzy).

Returns:

  • True if successfully deleted.



144
145
146
147
148
# File 'lib/active_project/adapters/fizzy/issues.rb', line 144

def delete_issue(card_number, context = {})
  path = "cards/#{card_number}.json"
  make_request(:delete, path)
  true
end

#find_issue(card_number, context = {}) ⇒ ActiveProject::Resources::Issue

Finds a specific card by its number.

Parameters:

  • The card number (sequential per account).

  • (defaults to: {})

    Optional context (not required for Fizzy).

Returns:

  • The issue resource.



52
53
54
55
56
57
58
59
# File 'lib/active_project/adapters/fizzy/issues.rb', line 52

def find_issue(card_number, context = {})
  path = "cards/#{card_number}.json"
  card_data = make_request(:get, path)
  return nil unless card_data

  board_id = card_data.dig("board", "id")
  map_card_data(card_data, board_id)
end

#list_issues(board_id, options = {}) ⇒ Array<ActiveProject::Resources::Issue>

Lists cards within a specific board.

Parameters:

  • The ULID of the Fizzy board.

  • (defaults to: {})

    Optional options for filtering.

    • :tag_ids [Array<String>] Filter by tag IDs

    • :assignee_ids [Array<String>] Filter by assignee user IDs

    • :indexed_by [String] Filter: all, closed, not_now, stalled, postponing_soon, golden

    • :sorted_by [String] Sort: latest, newest, oldest

Returns:

  • An array of issue resources.



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/active_project/adapters/fizzy/issues.rb', line 15

def list_issues(board_id, options = {})
  all_cards = []
  query = { "board_ids[]" => board_id }

  # Add optional filters
  options[:tag_ids]&.each { |id| query["tag_ids[]"] = id }
  options[:assignee_ids]&.each { |id| query["assignee_ids[]"] = id }
  query[:indexed_by] = options[:indexed_by] if options[:indexed_by]
  query[:sorted_by] = options[:sorted_by] if options[:sorted_by]

  path = "cards.json"

  loop do
    response = @connection.get(path, query)
    cards_data = parse_response(response)
    break if cards_data.empty?

    cards_data.each do |card_data|
      all_cards << map_card_data(card_data, board_id)
    end

    next_url = parse_next_link(response.headers["Link"])
    break unless next_url

    path = extract_path_from_url(next_url)
    query = {} # Query params are in the URL now
  end

  all_cards
rescue Faraday::Error => e
  handle_faraday_error(e)
end

#update_issue(card_number, attributes, context = {}) ⇒ ActiveProject::Resources::Issue

Updates an existing card.

Parameters:

  • The card number.

  • Attributes to update.

    • :title [String] The card title.

    • :description [String] Rich text description.

    • :status [Symbol] Status change: :open, :closed, :on_hold.

    • :tag_ids [Array<String>] Tag IDs to apply.

  • (defaults to: {})

    Optional context (not required for Fizzy).

Returns:

  • The updated issue resource.



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/active_project/adapters/fizzy/issues.rb', line 113

def update_issue(card_number, attributes, context = {})
  put_payload = {}
  put_payload[:title] = attributes[:title] if attributes.key?(:title)
  put_payload[:description] = attributes[:description] if attributes.key?(:description)
  put_payload[:tag_ids] = attributes[:tag_ids] if attributes.key?(:tag_ids)

  status_change_required = attributes.key?(:status)
  target_status = attributes[:status] if status_change_required

  unless !put_payload.empty? || status_change_required
    raise ArgumentError, "No attributes provided to update for FizzyAdapter#update_issue"
  end

  # Update basic fields via PUT
  unless put_payload.empty?
    put_path = "cards/#{card_number}.json"
    make_request(:put, put_path, { card: put_payload }.to_json)
  end

  # Handle status changes via separate endpoints
  if status_change_required
    handle_status_change(card_number, target_status)
  end

  find_issue(card_number, context)
end