@@ -286,6 +286,73 @@ def send_token(document_id, recipient:, channel:)
286286 end
287287 end
288288
289+ # List tags attached to a document.
290+ #
291+ # @param document_id [String]
292+ # @param account_id_override [String, nil]
293+ # @return [Array<Hash>]
294+ # @see GET /accounts/{account_id}/documents/{document_id}/tags
295+ def list_tags ( document_id , account_id_override = nil )
296+ acc_id = account_id ( account_id_override )
297+ doc_id = require_id ( document_id , 'Document ID' )
298+
299+ call ( 'Failed to list document tags' ) do
300+ http_get ( "accounts/#{ acc_id } /documents/#{ doc_id } /tags" )
301+ end
302+ end
303+
304+ # Replace the document's full tag set. Passing an empty array detaches
305+ # all tags from the document.
306+ #
307+ # @param document_id [String]
308+ # @param tags [Array<String>] tag names
309+ # @param account_id_override [String, nil]
310+ # @return [Array<Hash>]
311+ # @see PUT /accounts/{account_id}/documents/{document_id}/tags
312+ def replace_tags ( document_id , tags , account_id_override = nil )
313+ acc_id = account_id ( account_id_override )
314+ doc_id = require_id ( document_id , 'Document ID' )
315+
316+ call ( 'Failed to replace document tags' ) do
317+ http_put ( "accounts/#{ acc_id } /documents/#{ doc_id } /tags" ,
318+ body_params ( tags : tag_names ( tags , allow_empty : true ) ) )
319+ end
320+ end
321+
322+ # Attach additional tags to a document without removing existing tags.
323+ #
324+ # @param document_id [String]
325+ # @param tags [Array<String>] tag names
326+ # @param account_id_override [String, nil]
327+ # @return [Array<Hash>]
328+ # @see POST /accounts/{account_id}/documents/{document_id}/tags
329+ def append_tags ( document_id , tags , account_id_override = nil )
330+ acc_id = account_id ( account_id_override )
331+ doc_id = require_id ( document_id , 'Document ID' )
332+
333+ call ( 'Failed to append document tags' ) do
334+ http_post ( "accounts/#{ acc_id } /documents/#{ doc_id } /tags" ,
335+ body_params ( tags : tag_names ( tags ) ) )
336+ end
337+ end
338+
339+ # Detach a single tag from a document. The tag itself is not deleted.
340+ #
341+ # @param document_id [String]
342+ # @param tag_id [String]
343+ # @param account_id_override [String, nil]
344+ # @return [Hash]
345+ # @see DELETE /accounts/{account_id}/documents/{document_id}/tags/{tag_id}
346+ def detach_tag ( document_id , tag_id , account_id_override = nil )
347+ acc_id = account_id ( account_id_override )
348+ doc_id = require_id ( document_id , 'Document ID' )
349+ tid = require_id ( tag_id , 'Tag ID' )
350+
351+ call ( 'Failed to detach document tag' ) do
352+ http_delete ( "accounts/#{ acc_id } /documents/#{ doc_id } /tags/#{ tid } " )
353+ end
354+ end
355+
289356 # Convenience: true when the document is `certificated`, or when the
290357 # embedded assignment summary reports all signers complete.
291358 #
@@ -372,6 +439,24 @@ def template_body(signers_or_payload, options = {})
372439 body_params ( body )
373440 end
374441
442+ def tag_names ( tags , allow_empty : false )
443+ unless tags . is_a? ( Array )
444+ raise ValidationError . new ( 'Tags must be an Array' )
445+ end
446+
447+ if tags . empty? && !allow_empty
448+ raise ValidationError . new ( 'Tags must be a non-empty Array' )
449+ end
450+
451+ tags . each do |tag |
452+ next unless tag . to_s . strip . empty?
453+
454+ raise ValidationError . new ( 'Tag names cannot be empty' )
455+ end
456+
457+ tags
458+ end
459+
375460 def artifact_type ( artifact_name )
376461 value = require_id ( artifact_name , 'Artifact name' ) . to_s
377462 return value if ARTIFACT_TYPES . include? ( value )
0 commit comments