···1The zlib License
23-Copyright (c) 2025 Jakub Suder
45This software is provided 'as-is', without any express or implied
6warranty. In no event will the authors be held liable for any damages
···1The zlib License
23+Copyright (c) 2026 Jakub Suder
45This software is provided 'as-is', without any express or implied
6warranty. In no event will the authors be held liable for any damages
+8-4
README.md
···1314## Installation
1516-From the command line:
1718- gem install didkit
1920-Or, add this to your `Gemfile`:
002122 gem 'didkit', '~> 0.3'
23···106107## Credits
108109-Copyright ยฉ 2025 Kuba Suder ([@mackuba.eu](https://bsky.app/profile/did:plc:oio4hkxaop4ao4wz2pp3f4cr)).
110111The code is available under the terms of the [zlib license](https://choosealicense.com/licenses/zlib/) (permissive, similar to MIT).
00
···1314## Installation
1516+To use DIDKit, you need a reasonably new version of Ruby โ it should run on Ruby 2.6 and above, although it's recommended to use a version that's still getting maintainance updates, i.e. currently 3.2+. A compatible version should be preinstalled on macOS Big Sur and above and on many Linux systems. Otherwise, you can install one using tools such as [RVM](https://rvm.io), [asdf](https://asdf-vm.com), [ruby-install](https://github.com/postmodern/ruby-install) or [ruby-build](https://github.com/rbenv/ruby-build), or `rpm` or `apt-get` on Linux (see more installation options on [ruby-lang.org](https://www.ruby-lang.org/en/downloads/)).
1718+To install the gem, run in the command line:
1920+ [sudo] gem install didkit
21+22+Or add this to your app's `Gemfile`:
2324 gem 'didkit', '~> 0.3'
25···108109## Credits
110111+Copyright ยฉ 2026 Kuba Suder ([@mackuba.eu](https://bsky.app/profile/did:plc:oio4hkxaop4ao4wz2pp3f4cr)).
112113The code is available under the terms of the [zlib license](https://choosealicense.com/licenses/zlib/) (permissive, similar to MIT).
114+115+Bug reports and pull requests are welcome ๐
···6require_relative 'resolver'
78module DIDKit
00000000009 class DID
10 GENERIC_REGEXP = /\Adid\:\w+\:.+\z/
1112 include Requests
13000000000014 def self.resolve_handle(handle)
15 Resolver.new.resolve_handle(handle)
16 end
1718- attr_reader :type, :did, :resolved_by
00000000000000001920 def initialize(did, resolved_by = nil)
21 if did.is_a?(DID)
···36 @resolved_by = resolved_by
37 end
3839- alias to_s did
0004041 def document
42 @document ||= get_document
43 end
4400045 def get_document
46 Resolver.new.resolve_did(self)
47 end
480000000049 def get_verified_handle
50 Resolver.new.get_verified_handle(document)
51 end
520000053 def get_audit_log
54 if @type == :plc
55 PLCImporter.new.fetch_audit_log(self)
···58 end
59 end
60000061 def web_domain
62 did.gsub(/^did\:web\:/, '') if type == :web
63 end
6400000000065 def account_status(request_options = {})
66 doc = self.document
67 return nil if doc.pds_endpoint.nil?
···91 end
92 end
9300000094 def account_active?
95 account_status == :active
96 end
9700000098 def account_exists?
99 account_status != nil
100 end
00000101102 def ==(other)
103 if other.is_a?(String)
···6require_relative 'resolver'
78module DIDKit
9+10+ #
11+ # Represents a DID identifier (account on the ATProto network). This class serves as an entry
12+ # point to various lookup helpers. For convenience it can also be accessed as just `DID` without
13+ # the `DIDKit::` prefix.
14+ #
15+ # @example Resolving a handle
16+ # did = DID.resolve_handle('bsky.app')
17+ #
18+19 class DID
20 GENERIC_REGEXP = /\Adid\:\w+\:.+\z/
2122 include Requests
2324+ # Resolve a handle into a DID. Looks up the given ATProto domain handle using the DNS TXT method
25+ # and the HTTP .well-known method and returns a DID if one is assigned using either of the methods.
26+ #
27+ # If a DID string or a {DID} object is passed, it simply returns that DID, so you can use this
28+ # method to pass it an input string from the user which can be a DID or handle, without having to
29+ # check which one it is.
30+ #
31+ # @param handle [String, DID] a domain handle (may start with an `@`) or a DID string
32+ # @return [DID, nil] resolved DID if found, nil otherwise
33+34 def self.resolve_handle(handle)
35 Resolver.new.resolve_handle(handle)
36 end
3738+ # @return [Symbol] DID type (`:plc` or `:web`)
39+ attr_reader :type
40+41+ # @return [String] DID identifier string
42+ attr_reader :did
43+44+ # @return [Symbol, nil] `:dns` or `:http` if the DID was looked up using one of those methods
45+ attr_reader :resolved_by
46+47+ alias to_s did
48+49+50+ # Create a DID object from a DID string.
51+ #
52+ # @param did [String, DID] DID string or another DID object
53+ # @param resolved_by [Symbol, nil] optionally, how the DID was looked up (`:dns` or `:http`)
54+ # @raise [DIDError] when the DID format or type is invalid
5556 def initialize(did, resolved_by = nil)
57 if did.is_a?(DID)
···72 @resolved_by = resolved_by
73 end
7475+ # Returns or looks up the DID document with the DID's identity details from an appropriate source.
76+ # This method caches the document in a local variable if it's called again.
77+ #
78+ # @return [Document] resolved DID document
7980 def document
81 @document ||= get_document
82 end
8384+ # Looks up the DID document with the DID's identity details from an appropriate source.
85+ # @return [Document] resolved DID document
86+87 def get_document
88 Resolver.new.resolve_did(self)
89 end
9091+ # Returns the first verified handle assigned to this DID.
92+ #
93+ # Looks up the domain handles assigned to this DID in its DID document, checks if they are
94+ # verified (i.e. assigned correctly to this DID using DNS TXT or .well-known) and returns
95+ # the first handle that validates correctly, or nil if none matches.
96+ #
97+ # @return [String, nil] verified handle domain, if found
98+99 def get_verified_handle
100 Resolver.new.get_verified_handle(document)
101 end
102103+ # Fetches the PLC audit log (list of all previous operations) for a did:plc DID.
104+ #
105+ # @return [Array<PLCOperation>] list of PLC operations in the audit log
106+ # @raise [DIDError] when the DID is not a did:plc
107+108 def get_audit_log
109 if @type == :plc
110 PLCImporter.new.fetch_audit_log(self)
···113 end
114 end
115116+ # Returns the domain portion of a did:web identifier.
117+ #
118+ # @return [String, nil] DID domain if the DID is a did:web, nil for did:plc
119+120 def web_domain
121 did.gsub(/^did\:web\:/, '') if type == :web
122 end
123124+ # Checks the status of the account/repo on its own PDS using the `getRepoStatus` endpoint.
125+ #
126+ # @param request_options [Hash] request options to override
127+ # @option request_options [Integer] :timeout request timeout (default: 15)
128+ # @option request_options [Integer] :max_redirects maximum number of redirects to follow (default: 5)
129+ #
130+ # @return [Symbol, nil] `:active`, or returned inactive status, or `nil` if account is not found
131+ # @raise [APIError] when the response is invalid
132+133 def account_status(request_options = {})
134 doc = self.document
135 return nil if doc.pds_endpoint.nil?
···159 end
160 end
161162+ # Checks if the account is seen as active on its own PDS, using the `getRepoStatus` endpoint.
163+ # This is a helper which calls the {#account_status} method and checks if the status is `:active`.
164+ #
165+ # @return [Boolean] true if the returned status is active
166+ # @raise [APIError] when the response is invalid
167+168 def account_active?
169 account_status == :active
170 end
171172+ # Checks if the account exists its own PDS, using the `getRepoStatus` endpoint.
173+ # This is a helper which calls the {#account_status} method and checks if the repo is found at all.
174+ #
175+ # @return [Boolean] true if the returned status is valid, false if repo is not found
176+ # @raise [APIError] when the response is invalid
177+178 def account_exists?
179 account_status != nil
180 end
181+182+ # Compares the DID to another DID object or string.
183+ #
184+ # @param other [DID, String] other DID to compare with
185+ # @return [Boolean] true if it's the same DID
186187 def ==(other)
188 if other.is_a?(String)
+39-4
lib/didkit/document.rb
···1require_relative 'at_handles'
02require_relative 'resolver'
3require_relative 'service_record'
4require_relative 'services'
56module DIDKit
00000007 class Document
8- class FormatError < StandardError
9- end
10-11 include AtHandles
12 include Services
1314- attr_reader :json, :did, :handles, :services
0000000000000000000001516 def initialize(did, json)
17 raise FormatError, "Missing id field" if json['id'].nil?
···25 @handles = parse_also_known_as(json['alsoKnownAs'] || [])
26 end
270000000028 def get_verified_handle
29 Resolver.new.get_verified_handle(self)
30 end
03132 private
33
···1require_relative 'at_handles'
2+require_relative 'errors'
3require_relative 'resolver'
4require_relative 'service_record'
5require_relative 'services'
67module DIDKit
8+9+ #
10+ # Parsed DID document from a JSON file loaded from [plc.directory](https://plc.directory) or a did:web domain.
11+ #
12+ # Use {DID#document} or {Resolver#resolve_did} to fetch a DID document and return this object.
13+ #
14+15 class Document
00016 include AtHandles
17 include Services
1819+ # @return [Hash] the complete JSON data of the DID document
20+ attr_reader :json
21+22+ # @return [DID] the DID that this document describes
23+ attr_reader :did
24+25+ # Returns a list of handles assigned to this DID in its DID document.
26+ #
27+ # Note: the handles aren't guaranteed to be verified (validated in the other direction).
28+ # Use {#get_verified_handle} to find a handle that is correctly verified.
29+ #
30+ # @return [Array<String>]
31+ attr_reader :handles
32+33+ # @return [Array<ServiceRecords>] service records like PDS details assigned to the DID
34+ attr_reader :services
35+36+ # Creates a DID document object.
37+ #
38+ # @param did [DID] DID object
39+ # @param json [Hash] DID document JSON
40+ # @raise [FormatError] when required fields are missing or invalid.
4142 def initialize(did, json)
43 raise FormatError, "Missing id field" if json['id'].nil?
···51 @handles = parse_also_known_as(json['alsoKnownAs'] || [])
52 end
5354+ # Returns the first verified handle assigned to the DID.
55+ #
56+ # Looks up the domain handles assigned to this DID in the DID document, checks if they are
57+ # verified (i.e. assigned correctly to this DID using DNS TXT or .well-known) and returns
58+ # the first handle that validates correctly, or nil if none matches.
59+ #
60+ # @return [String, nil] verified handle domain, if found
61+62 def get_verified_handle
63 Resolver.new.get_verified_handle(self)
64 end
65+6667 private
68
+20-2
lib/didkit/errors.rb
···1module DIDKit
2- class DIDError < StandardError
3- end
40005 class APIError < StandardError
006 attr_reader :response
708 def initialize(response)
9 @response = response
10 super("APIError: #{response}")
11 end
12013 def status
14 response.code.to_i
15 end
16017 def body
18 response.body
19 end
00000000000020 end
21end
···1module DIDKit
0023+ #
4+ # Raised when an HTTP request returns a response with an error status.
5+ #
6 class APIError < StandardError
7+8+ # @return [Net::HTTPResponse] the returned HTTP response
9 attr_reader :response
1011+ # @param response [Net::HTTPResponse] the returned HTTP response
12 def initialize(response)
13 @response = response
14 super("APIError: #{response}")
15 end
1617+ # @return [Integer] HTTP status code
18 def status
19 response.code.to_i
20 end
2122+ # @return [String] HTTP response body
23 def body
24 response.body
25 end
26+ end
27+28+ #
29+ # Raised when a string is not a valid DID or not of the right type.
30+ #
31+ class DIDError < StandardError
32+ end
33+34+ #
35+ # Raised when the loaded data has some missing or invalid fields.
36+ #
37+ class FormatError < StandardError
38 end
39end
+44-4
lib/didkit/plc_operation.rb
···1require 'time'
23require_relative 'at_handles'
04require_relative 'service_record'
5require_relative 'services'
67module DIDKit
8- class PLCOperation
9- class FormatError < StandardError
10- end
1100000012 include AtHandles
13 include Services
1415- attr_reader :json, :did, :cid, :seq, :created_at, :type, :handles, :services
0000000000000000000000000000000000001617 def initialize(json)
18 @json = json
···1require 'time'
23require_relative 'at_handles'
4+require_relative 'errors'
5require_relative 'service_record'
6require_relative 'services'
78module DIDKit
000910+ #
11+ # Represents a single operation of changing a specific DID's data in the [plc.directory](https://plc.directory)
12+ # (e.g. changing assigned handles or migrating to a different PDS).
13+ #
14+15+ class PLCOperation
16 include AtHandles
17 include Services
1819+ # @return [Hash] the JSON from which the operation is parsed
20+ attr_reader :json
21+22+ # @return [String] the DID which the operation concerns
23+ attr_reader :did
24+25+ # @return [String] CID (Content Identifier) of the operation
26+ attr_reader :cid
27+28+ # Returns a sequential number of the operation (only used in the new export API).
29+ # @return [Integer, nil] sequential number of the operation
30+ attr_reader :seq
31+32+ # @return [Time] time when the operation was created
33+ attr_reader :created_at
34+35+ # Returns the `type` field of the operation (usually `"plc_operation"`).
36+ # @return [String] the operation type
37+ attr_reader :type
38+39+ # Returns a list of handles assigned to the DID in this operation.
40+ #
41+ # Note: the handles aren't guaranteed to be verified (validated in the other direction).
42+ # Use {DID#get_verified_handle} or {Document#get_verified_handle} to find a handle that is
43+ # correctly verified.
44+ #
45+ # @return [Array<String>]
46+ attr_reader :handles
47+48+ # @return [Array<ServiceRecords>] service records like PDS details assigned to the DID
49+ attr_reader :services
50+51+52+ # Creates a PLCOperation object.
53+ #
54+ # @param json [Hash] operation JSON
55+ # @raise [FormatError] when required fields are missing or invalid
5657 def initialize(json)
58 @json = json
···6require_relative 'requests'
78module DIDKit
000009 class Resolver
0010 RESERVED_DOMAINS = %w(alt arpa example internal invalid local localhost onion test)
1112 include Requests
13014 attr_accessor :nameserver
000001516 def initialize(options = {})
17 @nameserver = options[:nameserver]
18 @request_options = options.slice(:timeout, :max_redirects)
19 end
20000000000021 def resolve_handle(handle)
22 if handle.is_a?(DID) || handle =~ DID::GENERIC_REGEXP
23 return DID.new(handle)
···36 end
37 end
380000000039 def resolve_handle_by_dns(domain)
40 dns_records = Resolv::DNS.open(resolv_options) do |d|
41 d.getresources("_atproto.#{domain}", Resolv::DNS::Resource::IN::TXT)
···50 nil
51 end
520000000053 def resolve_handle_by_well_known(domain)
54 url = "https://#{domain}/.well-known/atproto-did"
55 response = get_response(url, @request_options)
···63 nil
64 end
65000000000000000000000000000000000000000000066 def resolv_options
67 options = Resolv::DNS::Config.default_config_hash.dup
68 options[:nameserver] = nameserver if nameserver
···78 text.lines.length == 1 && text =~ DID::GENERIC_REGEXP ? text : nil
79 end
8081- def resolve_did(did)
82- did = DID.new(did) if did.is_a?(String)
83-84- did.type == :plc ? resolve_did_plc(did) : resolve_did_web(did)
85- end
86-87 def resolve_did_plc(did)
88 json = get_json("https://plc.directory/#{did}", content_type: /^application\/did\+ld\+json(;.+)?$/)
89 Document.new(did, json)
···92 def resolve_did_web(did)
93 json = get_json("https://#{did.web_domain}/.well-known/did.json")
94 Document.new(did, json)
95- end
96-97- def get_verified_handle(subject)
98- document = subject.is_a?(Document) ? subject : resolve_did(subject)
99-100- first_verified_handle(document.did, document.handles)
101- end
102-103- def first_verified_handle(did, handles)
104- handles.detect { |h| resolve_handle(h) == did.to_s }
105 end
106 end
107end
···6require_relative 'requests'
78module DIDKit
9+10+ #
11+ # A class which manages resolving of handles to DIDs and DIDs to DID documents.
12+ #
13+14 class Resolver
15+ # These TLDs are not allowed in ATProto handles, so the resolver returns nil for them
16+ # without trying to look them up.
17 RESERVED_DOMAINS = %w(alt arpa example internal invalid local localhost onion test)
1819 include Requests
2021+ # @return [String, Array<String>] custom DNS nameserver(s) to use for DNS TXT lookups
22 attr_accessor :nameserver
23+24+ # @param options [Hash] resolver options
25+ # @option options [String, Array<String>] :nameserver custom DNS nameserver(s) to use (IP or an array of IPs)
26+ # @option options [Integer] :timeout request timeout in seconds (default: 15)
27+ # @option options [Integer] :max_redirects maximum number of redirects to follow (default: 5)
2829 def initialize(options = {})
30 @nameserver = options[:nameserver]
31 @request_options = options.slice(:timeout, :max_redirects)
32 end
3334+ # Resolve a handle into a DID. Looks up the given ATProto domain handle using the DNS TXT method
35+ # and the HTTP .well-known method and returns a DID if one is assigned using either of the methods.
36+ #
37+ # If a DID string or a {DID} object is passed, it simply returns that DID, so you can use this
38+ # method to pass it an input string from the user which can be a DID or handle, without having to
39+ # check which one it is.
40+ #
41+ # @param handle [String, DID] a domain handle (may start with an `@`) or a DID string
42+ # @return [DID, nil] resolved DID if found, nil otherwise
43+44 def resolve_handle(handle)
45 if handle.is_a?(DID) || handle =~ DID::GENERIC_REGEXP
46 return DID.new(handle)
···59 end
60 end
6162+ # Tries to resolve a handle into DID using the DNS TXT method.
63+ #
64+ # Checks the DNS records for a given domain for an entry `_atproto.#{domain}` whose value is
65+ # a correct DID string.
66+ #
67+ # @param domain [String] a domain handle to look up
68+ # @return [String, nil] resolved DID if found, nil otherwise
69+70 def resolve_handle_by_dns(domain)
71 dns_records = Resolv::DNS.open(resolv_options) do |d|
72 d.getresources("_atproto.#{domain}", Resolv::DNS::Resource::IN::TXT)
···81 nil
82 end
8384+ # Tries to resolve a handle into DID using the HTTP .well-known method.
85+ #
86+ # Checks the `/.well-known/atproto-did` endpoint on the given domain to see if it returns
87+ # a text file that contains a correct DID string.
88+ #
89+ # @param domain [String] a domain handle to look up
90+ # @return [String, nil] resolved DID if found, nil otherwise
91+92 def resolve_handle_by_well_known(domain)
93 url = "https://#{domain}/.well-known/atproto-did"
94 response = get_response(url, @request_options)
···102 nil
103 end
104105+ # Resolve a DID to a DID document.
106+ #
107+ # Looks up the DID document with the DID's identity details from an appropriate source, i.e. either
108+ # [plc.directory](https://plc.directory) for did:plc DIDs, or the did:web's domain for did:web DIDs.
109+ #
110+ # @param did [String, DID] DID string or object
111+ # @return [Document] resolved DID document
112+ # @raise [APIError] if an incorrect response is returned
113+114+ def resolve_did(did)
115+ did = DID.new(did) if did.is_a?(String)
116+117+ did.type == :plc ? resolve_did_plc(did) : resolve_did_web(did)
118+ end
119+120+ # Returns the first verified handle assigned to the given DID.
121+ #
122+ # Looks up the domain handles assigned to the DID in the DID document, checks if they are
123+ # verified (i.e. assigned correctly to this DID using DNS TXT or .well-known) and returns
124+ # the first handle that validates correctly, or nil if none matches.
125+ #
126+ # @param subject [String, DID, Document] a DID or its DID document
127+ # @return [String, nil] verified handle domain, if found
128+129+ def get_verified_handle(subject)
130+ document = subject.is_a?(Document) ? subject : resolve_did(subject)
131+132+ first_verified_handle(document.did, document.handles)
133+ end
134+135+ # Returns the first handle from the list that resolves back to the given DID.
136+ #
137+ # @param did [DID, String] DID to verify the handles against
138+ # @param handles [Array<String>] handles to check
139+ # @return [String, nil] a verified handle, if found
140+141+ def first_verified_handle(did, handles)
142+ handles.detect { |h| resolve_handle(h) == did.to_s }
143+ end
144+145+146+ private
147+148 def resolv_options
149 options = Resolv::DNS::Config.default_config_hash.dup
150 options[:nameserver] = nameserver if nameserver
···160 text.lines.length == 1 && text =~ DID::GENERIC_REGEXP ? text : nil
161 end
162000000163 def resolve_did_plc(did)
164 json = get_json("https://plc.directory/#{did}", content_type: /^application\/did\+ld\+json(;.+)?$/)
165 Document.new(did, json)
···168 def resolve_did_web(did)
169 json = get_json("https://#{did.web_domain}/.well-known/did.json")
170 Document.new(did, json)
0000000000171 end
172 end
173end
+21-3
lib/didkit/service_record.rb
···2require_relative 'errors'
34module DIDKit
00005 class ServiceRecord
6- class FormatError < StandardError
7- end
89- attr_reader :key, :type, :endpoint
00000000000000001011 def initialize(key, type, endpoint)
12 begin
···2require_relative 'errors'
34module DIDKit
5+6+ # A parsed service record from either a DID document's `service` field or a PLC directory
7+ # operation's `services` field.
8+9 class ServiceRecord
001011+ # Returns the service's identifier (without `#`), like "atproto_pds".
12+ # @return [String] service's identifier
13+ attr_reader :key
14+15+ # Returns the service's type field, like "AtprotoPersonalDataServer".
16+ # @return [String] service's type
17+ attr_reader :type
18+19+ # @return [String] service's endpoint URL
20+ attr_reader :endpoint
21+22+ # Create a service record from DID document fields.
23+ #
24+ # @param key [String] service identifier (without `#`)
25+ # @param type [String] service type
26+ # @param endpoint [String] service endpoint URL
27+ # @raise [FormatError] when the endpoint is not a valid URI
2829 def initialize(key, type, endpoint)
30 begin
···1require 'uri'
23module DIDKit
4+5+ #
6+ # @api private
7+ #
8+9 module Services
10+11+ # Finds a service entry matching the given key and type.
12+ #
13+ # @api public
14+ # @param key [String] service key in the DID document
15+ # @param type [String] service type identifier
16+ # @return [ServiceRecord, nil] matching service record, if found
17+18 def get_service(key, type)
19 @services&.detect { |s| s.key == key && s.type == type }
20 end
2122+ # Returns the PDS service endpoint, if present.
23+ #
24+ # If the DID has an `#atproto_pds` service declared in its `service` section,
25+ # returns the URL in its `serviceEndpoint` field. In other words, this is the URL
26+ # of the PDS assigned to a given user, which stores the user's account and repo.
27+ #
28+ # @api public
29+ # @return [String, nil] PDS service endpoint URL
30+31 def pds_endpoint
32 @pds_endpoint ||= get_service('atproto_pds', 'AtprotoPersonalDataServer')&.endpoint
33 end
34+35+ # Returns the labeler service endpoint, if present.
36+ #
37+ # If the DID has an `#atproto_labeler` service declared in its `service` section,
38+ # returns the URL in its `serviceEndpoint` field.
39+ #
40+ # @api public
41+ # @return [String, nil] labeler service endpoint URL
4243 def labeler_endpoint
44 @labeler_endpoint ||= get_service('atproto_labeler', 'AtprotoLabeler')&.endpoint
45 end
4647+ # Returns the hostname of the PDS service, if present.
48+ #
49+ # @api public
50+ # @return [String, nil] hostname of the PDS endpoint URL
51+52 def pds_host
53 pds_endpoint&.then { |x| URI(x).host }
54 end
55+56+ # Returns the hostname of the labeler service, if present.
57+ #
58+ # @api public
59+ # @return [String, nil] hostname of the labeler endpoint URL
6061 def labeler_host
62 labeler_endpoint&.then { |x| URI(x).host }
+7-7
spec/document_spec.rb
···41 it 'should raise a format error' do
42 expect {
43 subject.new(did, json)
44- }.to raise_error(DIDKit::Document::FormatError)
45 end
46 end
47···51 it 'should raise a format error' do
52 expect {
53 subject.new(did, json)
54- }.to raise_error(DIDKit::Document::FormatError)
55 end
56 end
57···61 it 'should raise a format error' do
62 expect {
63 subject.new(did, json)
64- }.to raise_error(DIDKit::Document::FormatError)
65 end
66 end
67···71 it 'should raise an AtHandles format error' do
72 expect {
73 subject.new(did, json)
74- }.to raise_error(DIDKit::AtHandles::FormatError)
75 end
76 end
77···81 it 'should raise an AtHandles format error' do
82 expect {
83 subject.new(did, json)
84- }.to raise_error(DIDKit::AtHandles::FormatError)
85 end
86 end
87···106 it 'should raise a format error' do
107 expect {
108 subject.new(did, json)
109- }.to raise_error(DIDKit::Document::FormatError)
110 end
111 end
112···116 it 'should raise a format error' do
117 expect {
118 subject.new(did, json)
119- }.to raise_error(DIDKit::Document::FormatError)
120 end
121 end
122
···41 it 'should raise a format error' do
42 expect {
43 subject.new(did, json)
44+ }.to raise_error(DIDKit::FormatError)
45 end
46 end
47···51 it 'should raise a format error' do
52 expect {
53 subject.new(did, json)
54+ }.to raise_error(DIDKit::FormatError)
55 end
56 end
57···61 it 'should raise a format error' do
62 expect {
63 subject.new(did, json)
64+ }.to raise_error(DIDKit::FormatError)
65 end
66 end
67···71 it 'should raise an AtHandles format error' do
72 expect {
73 subject.new(did, json)
74+ }.to raise_error(DIDKit::FormatError)
75 end
76 end
77···81 it 'should raise an AtHandles format error' do
82 expect {
83 subject.new(did, json)
84+ }.to raise_error(DIDKit::FormatError)
85 end
86 end
87···106 it 'should raise a format error' do
107 expect {
108 subject.new(did, json)
109+ }.to raise_error(DIDKit::FormatError)
110 end
111 end
112···116 it 'should raise a format error' do
117 expect {
118 subject.new(did, json)
119+ }.to raise_error(DIDKit::FormatError)
120 end
121 end
122
+16-16
spec/plc_operation_spec.rb
···27 let(:json) { [base_json] }
2829 it 'should raise a format error' do
30- expect { subject.new(json) }.to raise_error(DIDKit::PLCOperation::FormatError)
31 end
32 end
33···35 let(:json) { base_json.tap { |h| h.delete('did') }}
3637 it 'should raise a format error' do
38- expect { subject.new(json) }.to raise_error(DIDKit::PLCOperation::FormatError)
39 end
40 end
41···43 let(:json) { base_json.merge('did' => 123) }
4445 it 'should raise a format error' do
46- expect { subject.new(json) }.to raise_error(DIDKit::PLCOperation::FormatError)
47 end
48 end
49···51 let(:json) { base_json.merge('did' => 'foobar') }
5253 it 'should raise a format error' do
54- expect { subject.new(json) }.to raise_error(DIDKit::PLCOperation::FormatError)
55 end
56 end
57···59 let(:json) { base_json.tap { |h| h.delete('cid') }}
6061 it 'should raise a format error' do
62- expect { subject.new(json) }.to raise_error(DIDKit::PLCOperation::FormatError)
63 end
64 end
65···67 let(:json) { base_json.merge('cid' => 700) }
6869 it 'should raise a format error' do
70- expect { subject.new(json) }.to raise_error(DIDKit::PLCOperation::FormatError)
71 end
72 end
73···75 let(:json) { base_json.tap { |h| h.delete('createdAt') }}
7677 it 'should raise a format error' do
78- expect { subject.new(json) }.to raise_error(DIDKit::PLCOperation::FormatError)
79 end
80 end
81···83 let(:json) { base_json.merge('createdAt' => 123) }
8485 it 'should raise a format error' do
86- expect { subject.new(json) }.to raise_error(DIDKit::PLCOperation::FormatError)
87 end
88 end
89···91 let(:json) { base_json.tap { |h| h.delete('operation') }}
9293 it 'should raise a format error' do
94- expect { subject.new(json) }.to raise_error(DIDKit::PLCOperation::FormatError)
95 end
96 end
97···99 let(:json) { base_json.merge('operation' => 'invalid') }
100101 it 'should raise a format error' do
102- expect { subject.new(json) }.to raise_error(DIDKit::PLCOperation::FormatError)
103 end
104 end
105···107 let(:json) { base_json.tap { |h| h['operation'].delete('type') }}
108109 it 'should raise a format error' do
110- expect { subject.new(json) }.to raise_error(DIDKit::PLCOperation::FormatError)
111 end
112 end
113···152 it 'should raise an AtHandles format error' do
153 expect {
154 subject.new(json)
155- }.to raise_error(DIDKit::AtHandles::FormatError)
156 end
157 end
158···162 it 'should raise an AtHandles format error' do
163 expect {
164 subject.new(json)
165- }.to raise_error(DIDKit::AtHandles::FormatError)
166 end
167 end
168···187 let(:json) { base_json.tap { |h| h['operation'].delete('services') }}
188189 it 'should raise a format error' do
190- expect { subject.new(json) }.to raise_error(DIDKit::PLCOperation::FormatError)
191 end
192 end
193···205 }
206207 it 'should raise a format error' do
208- expect { subject.new(json) }.to raise_error(DIDKit::PLCOperation::FormatError)
209 end
210 end
211···225 }
226227 it 'should raise a format error' do
228- expect { subject.new(json) }.to raise_error(DIDKit::PLCOperation::FormatError)
229 end
230 end
231
···27 let(:json) { [base_json] }
2829 it 'should raise a format error' do
30+ expect { subject.new(json) }.to raise_error(DIDKit::FormatError)
31 end
32 end
33···35 let(:json) { base_json.tap { |h| h.delete('did') }}
3637 it 'should raise a format error' do
38+ expect { subject.new(json) }.to raise_error(DIDKit::FormatError)
39 end
40 end
41···43 let(:json) { base_json.merge('did' => 123) }
4445 it 'should raise a format error' do
46+ expect { subject.new(json) }.to raise_error(DIDKit::FormatError)
47 end
48 end
49···51 let(:json) { base_json.merge('did' => 'foobar') }
5253 it 'should raise a format error' do
54+ expect { subject.new(json) }.to raise_error(DIDKit::FormatError)
55 end
56 end
57···59 let(:json) { base_json.tap { |h| h.delete('cid') }}
6061 it 'should raise a format error' do
62+ expect { subject.new(json) }.to raise_error(DIDKit::FormatError)
63 end
64 end
65···67 let(:json) { base_json.merge('cid' => 700) }
6869 it 'should raise a format error' do
70+ expect { subject.new(json) }.to raise_error(DIDKit::FormatError)
71 end
72 end
73···75 let(:json) { base_json.tap { |h| h.delete('createdAt') }}
7677 it 'should raise a format error' do
78+ expect { subject.new(json) }.to raise_error(DIDKit::FormatError)
79 end
80 end
81···83 let(:json) { base_json.merge('createdAt' => 123) }
8485 it 'should raise a format error' do
86+ expect { subject.new(json) }.to raise_error(DIDKit::FormatError)
87 end
88 end
89···91 let(:json) { base_json.tap { |h| h.delete('operation') }}
9293 it 'should raise a format error' do
94+ expect { subject.new(json) }.to raise_error(DIDKit::FormatError)
95 end
96 end
97···99 let(:json) { base_json.merge('operation' => 'invalid') }
100101 it 'should raise a format error' do
102+ expect { subject.new(json) }.to raise_error(DIDKit::FormatError)
103 end
104 end
105···107 let(:json) { base_json.tap { |h| h['operation'].delete('type') }}
108109 it 'should raise a format error' do
110+ expect { subject.new(json) }.to raise_error(DIDKit::FormatError)
111 end
112 end
113···152 it 'should raise an AtHandles format error' do
153 expect {
154 subject.new(json)
155+ }.to raise_error(DIDKit::FormatError)
156 end
157 end
158···162 it 'should raise an AtHandles format error' do
163 expect {
164 subject.new(json)
165+ }.to raise_error(DIDKit::FormatError)
166 end
167 end
168···187 let(:json) { base_json.tap { |h| h['operation'].delete('services') }}
188189 it 'should raise a format error' do
190+ expect { subject.new(json) }.to raise_error(DIDKit::FormatError)
191 end
192 end
193···205 }
206207 it 'should raise a format error' do
208+ expect { subject.new(json) }.to raise_error(DIDKit::FormatError)
209 end
210 end
211···225 }
226227 it 'should raise a format error' do
228+ expect { subject.new(json) }.to raise_error(DIDKit::FormatError)
229 end
230 end
231