didkit 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7744ae1dc9ba4ca678d641139ffa94931130ee7f91fa93d3ee89e3d6e196ba8d
4
- data.tar.gz: 4111e824027b646aa8b3a21fb3032dfdba26019b6fecaf3f40f2120dfa417e52
3
+ metadata.gz: 0170f70f452400a399264cccd61a96a0ef96d570caca53547b2794eb8067d4bd
4
+ data.tar.gz: 68197fe06b766fbe69d09d6d85b8abf59467f09560cb600b9af34bb900efaf6b
5
5
  SHA512:
6
- metadata.gz: ed96e325c2d0e8152b1db1f6a46e34afdbec049e0442f2298a89a2bbd07afb96e3ca166fca40adf1ff90d0da02997b825addd20163eb858c6074771d9ea229ce
7
- data.tar.gz: 9237183a427ad4f5380504334abcfb16a965b0e95ce39693e9aa8d58d24c6acc6c00d7150bd86be18332e02da3cbd379eb516ccb60716a3b09bf11efc155c26c
6
+ metadata.gz: 367f5acd94953397d5ca72edec0a43f2a96e8dfef3615d16210edc203abe14b1875c1e78316efe351e223c881cbc80d2fd6cf7314753c78caea6c9ccb7ed78d0
7
+ data.tar.gz: 15ade27998fbc1881f28bc07fe7d625f310e686be7f101508460db48df7ca263adfc6731bf9f8bb935363fb23e51f33b1f560183595ba624d1fd7ad4e8dcf00f
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## [0.2.0] - 2024-03-19
2
+
3
+ - added `PLCImporter` class, which lets you import operations from PLC in pages of 1000 through the "export" API
4
+ - implemented parsing of all services from DID doc & operations, not only `atproto_pds` (specifically labeller endpoints)
5
+ - allow setting the nameserver in `Resolver` initializer
6
+
1
7
  ## [0.1.0] - 2024-03-12
2
8
 
3
9
  - rejecting handles from disallowed domains like `.arpa` or `.test`
@@ -0,0 +1,14 @@
1
+ module DIDKit
2
+ module AtHandles
3
+ class FormatError < StandardError
4
+ end
5
+
6
+ def parse_also_known_as(aka)
7
+ raise FormatError, "Invalid alsoKnownAs: #{aka.inspect}" unless aka.is_a?(Array)
8
+ raise FormatError, "Invalid alsoKnownAs: #{aka.inspect}" unless aka.all? { |x| x.is_a?(String) }
9
+ raise FormatError, "Invalid alsoKnownAs: #{aka.inspect}" unless aka.all? { |x| x =~ %r(\Aat://[^/]+\z) }
10
+
11
+ aka.map { |x| x.gsub('at://', '') }
12
+ end
13
+ end
14
+ end
@@ -1,11 +1,17 @@
1
+ require_relative 'at_handles'
1
2
  require_relative 'resolver'
3
+ require_relative 'service_record'
4
+ require_relative 'services'
2
5
 
3
6
  module DIDKit
4
7
  class Document
5
8
  class FormatError < StandardError
6
9
  end
7
10
 
8
- attr_reader :json, :did, :pds_endpoint, :handles
11
+ include AtHandles
12
+ include Services
13
+
14
+ attr_reader :json, :did, :handles, :services
9
15
 
10
16
  def initialize(did, json)
11
17
  raise FormatError, "Missing id field" if json['id'].nil?
@@ -19,25 +25,20 @@ module DIDKit
19
25
  raise FormatError, "Missing service key" if service.nil?
20
26
  raise FormatError, "Invalid service data" unless service.is_a?(Array) && service.all? { |x| x.is_a?(Hash) }
21
27
 
22
- if pds = service.detect { |x| x['id'] == '#atproto_pds' }
23
- raise FormatError, "Missing PDS type" unless pds['type']
24
- raise FormatError, "Invalid PDS type" unless pds['type'] == 'AtprotoPersonalDataServer'
25
- raise FormatError, "Missing PDS endpoint" unless pds['serviceEndpoint']
26
- raise FormatError, "Invalid PDS endpoint" unless pds['serviceEndpoint'].is_a?(String)
27
- raise FormatError, "Invalid PDS endpoint" unless pds['serviceEndpoint'] =~ %r(://)
28
-
29
- @pds_endpoint = pds['serviceEndpoint']
30
- end
31
-
32
- if aka = json['alsoKnownAs']
33
- raise FormatError, "Invalid alsoKnownAs" unless aka.is_a?(Array)
34
- raise FormatError, "Invalid alsoKnownAs" unless aka.all? { |x| x.is_a?(String) }
35
- raise FormatError, "Invalid alsoKnownAs" unless aka.all? { |x| x =~ %r(\Aat://[^/]+\z) }
36
-
37
- @handles = aka.map { |x| x.gsub('at://', '') }
38
- else
39
- @handles = []
40
- end
28
+ @services = service.map { |x|
29
+ id, type, endpoint = x.values_at('id', 'type', 'serviceEndpoint')
30
+
31
+ raise FormatError, "Missing service id" unless id
32
+ raise FormatError, "Invalid service id: #{id.inspect}" unless id.is_a?(String) && id.start_with?('#')
33
+ raise FormatError, "Missing service type" unless type
34
+ raise FormatError, "Invalid service type: #{type.inspect}" unless type.is_a?(String)
35
+ raise FormatError, "Missing service endpoint" unless endpoint
36
+ raise FormatError, "Invalid service endpoint: #{endpoint.inspect}" unless endpoint.is_a?(String)
37
+
38
+ ServiceRecord.new(id.gsub(/^#/, ''), type, endpoint)
39
+ }
40
+
41
+ @handles = parse_also_known_as(json['alsoKnownAs'] || [])
41
42
  end
42
43
 
43
44
  def get_validated_handle
@@ -1,5 +1,4 @@
1
1
  require 'json'
2
- require 'net/http'
3
2
  require 'open-uri'
4
3
  require 'time'
5
4
 
@@ -8,21 +7,42 @@ require_relative 'plc_operation'
8
7
  module DIDKit
9
8
  class PLCImporter
10
9
  PLC_SERVICE = 'plc.directory'
10
+ MAX_PAGE = 1000
11
11
 
12
- attr_accessor :ignore_errors
12
+ attr_accessor :ignore_errors, :last_date
13
+
14
+ def initialize(since: nil)
15
+ if since.to_s == 'beginning'
16
+ @last_date = nil
17
+ elsif since.is_a?(String)
18
+ @last_date = Time.parse(since)
19
+ elsif since
20
+ @last_date = since
21
+ else
22
+ @last_date = Time.now
23
+ @eof = true
24
+ end
13
25
 
14
- def initialize(last_date = Time.now)
15
- @last_date = last_date
16
26
  @ignore_errors = false
17
27
  end
18
28
 
19
- def fetch
20
- url = URI("https://#{PLC_SERVICE}/export")
21
- url.query = URI.encode_www_form(:after => @last_date.utc.iso8601(6)) if @last_date
22
- request_time = Time.now
29
+ def plc_service
30
+ PLC_SERVICE
31
+ end
32
+
33
+ def get_export(args = {})
34
+ url = URI("https://#{plc_service}/export")
35
+ url.query = URI.encode_www_form(args)
23
36
 
24
37
  data = URI.open(url).read
25
- rows = data.lines.map(&:strip).reject(&:empty?).map { |x| JSON.parse(x) }
38
+ data.lines.map(&:strip).reject(&:empty?).map { |x| JSON.parse(x) }
39
+ end
40
+
41
+ def fetch_page
42
+ request_time = Time.now
43
+
44
+ query = @last_date ? { :after => @last_date.utc.iso8601(6) } : {}
45
+ rows = get_export(query)
26
46
 
27
47
  operations = rows.filter_map do |json|
28
48
  begin
@@ -32,14 +52,22 @@ module DIDKit
32
52
  end
33
53
  end
34
54
 
35
- block.call(operations)
55
+ @last_date = operations.last&.created_at || request_time
56
+ @eof = (rows.length < MAX_PAGE)
36
57
 
37
- if rows.length == 1000
38
- @last_date = operations.last.created_at || request_time
39
- run_update
40
- else
41
- @last_date = request_time
58
+ operations
59
+ end
60
+
61
+ def fetch(&block)
62
+ loop do
63
+ operations = fetch_page
64
+ block.call(operations)
65
+ break if eof?
42
66
  end
43
67
  end
68
+
69
+ def eof?
70
+ !!@eof
71
+ end
44
72
  end
45
73
  end
@@ -1,56 +1,57 @@
1
1
  require 'time'
2
2
 
3
+ require_relative 'at_handles'
4
+ require_relative 'service_record'
5
+ require_relative 'services'
6
+
3
7
  module DIDKit
4
8
  class PLCOperation
5
9
  class FormatError < StandardError
6
10
  end
7
11
 
8
- attr_reader :did, :created_at, :type, :pds_endpoint, :handles
12
+ include AtHandles
13
+ include Services
14
+
15
+ attr_reader :json, :did, :created_at, :type, :handles, :services
9
16
 
10
17
  def initialize(json)
18
+ @json = json
11
19
  @did = json['did']
12
- raise FormatError, "Missing DID" if @did.nil?
13
- raise FormatError, "Invalid DID" unless @did.is_a?(String) && @did.start_with?('did:')
20
+ raise FormatError, "Missing DID: #{json}" if @did.nil?
21
+ raise FormatError, "Invalid DID: #{@did}" unless @did.is_a?(String) && @did.start_with?('did:')
14
22
 
15
23
  timestamp = json['createdAt']
16
- raise FormatError, "Missing createdAt" if timestamp.nil?
17
- raise FormatError, "Invalid createdAt" unless timestamp.is_a?(String)
24
+ raise FormatError, "Missing createdAt: #{json}" if timestamp.nil?
25
+ raise FormatError, "Invalid createdAt: #{timestamp.inspect}" unless timestamp.is_a?(String)
18
26
 
19
27
  @created_at = Time.parse(timestamp)
20
28
 
21
29
  operation = json['operation']
22
- raise FormatError, "Missing operation key" if operation.nil?
23
- raise FormatError, "Invalid operation data" unless operation.is_a?(Hash)
30
+ raise FormatError, "Missing operation key: #{json}" if operation.nil?
31
+ raise FormatError, "Invalid operation data: #{operation.inspect}" unless operation.is_a?(Hash)
24
32
 
25
33
  type = operation['type']
26
- raise FormatError, "Missing type" if type.nil?
34
+ raise FormatError, "Missing operation type: #{json}" if type.nil?
27
35
 
28
36
  @type = type.to_sym
29
37
  return unless @type == :plc_operation
30
38
 
31
39
  services = operation['services']
32
- raise FormatError, "Missing services key" if services.nil?
33
- raise FormatError, "Invalid services data" unless services.is_a?(Hash)
34
-
35
- if pds = services['atproto_pds']
36
- raise FormatError, "Invalid PDS data" unless pds.is_a?(Hash)
37
- raise FormatError, "Missing PDS type" unless pds['type']
38
- raise FormatError, "Invalid PDS type" unless pds['type'] == 'AtprotoPersonalDataServer'
39
- raise FormatError, "Missing PDS endpoint" unless pds['endpoint']
40
- raise FormatError, "Invalid PDS endpoint" unless pds['endpoint'].is_a?(String) && pds['endpoint'] =~ %r(://)
41
-
42
- @pds_endpoint = pds['endpoint']
43
- end
44
-
45
- if aka = operation['alsoKnownAs']
46
- raise FormatError, "Invalid alsoKnownAs" unless aka.is_a?(Array)
47
- raise FormatError, "Invalid alsoKnownAs" unless aka.all? { |x| x.is_a?(String) }
48
- raise FormatError, "Invalid alsoKnownAs" unless aka.all? { |x| x =~ %r(\Aat://[^/]+\z) }
49
-
50
- @handles = aka.map { |x| x.gsub('at://', '') }
51
- else
52
- @handles = []
53
- end
40
+ raise FormatError, "Missing services key: #{json}" if services.nil?
41
+ raise FormatError, "Invalid services data: #{services}" unless services.is_a?(Hash)
42
+
43
+ @services = services.map { |k, x|
44
+ type, endpoint = x.values_at('type', 'endpoint')
45
+
46
+ raise FormatError, "Missing service type" unless type
47
+ raise FormatError, "Invalid service type: #{type.inspect}" unless type.is_a?(String)
48
+ raise FormatError, "Missing service endpoint" unless endpoint
49
+ raise FormatError, "Invalid service endpoint: #{endpoint.inspect}" unless endpoint.is_a?(String)
50
+
51
+ ServiceRecord.new(k, type, endpoint)
52
+ }
53
+
54
+ @handles = parse_also_known_as(operation['alsoKnownAs'] || [])
54
55
  end
55
56
  end
56
57
  end
@@ -13,6 +13,10 @@ module DIDKit
13
13
 
14
14
  attr_accessor :nameserver
15
15
 
16
+ def initialize(options = {})
17
+ @nameserver = options[:nameserver]
18
+ end
19
+
16
20
  def resolve_handle(handle)
17
21
  domain = handle.gsub(/^@/, '')
18
22
 
@@ -0,0 +1,20 @@
1
+ require 'uri'
2
+ require_relative 'errors'
3
+
4
+ module DIDKit
5
+ class ServiceRecord
6
+ attr_reader :key, :type, :endpoint
7
+
8
+ def initialize(key, type, endpoint)
9
+ begin
10
+ uri = URI(endpoint)
11
+ rescue URI::Error
12
+ raise FormatError, "Invalid service endpoint: #{endpoint.inspect}"
13
+ end
14
+
15
+ @key = key
16
+ @type = type
17
+ @endpoint = endpoint
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,15 @@
1
+ module DIDKit
2
+ module Services
3
+ def get_service(key, type)
4
+ @services&.detect { |s| s.key == key && s.type == type }
5
+ end
6
+
7
+ def pds_endpoint
8
+ @pds_endpoint ||= get_service('atproto_pds', 'AtprotoPersonalDataServer')&.endpoint
9
+ end
10
+
11
+ def labeler_endpoint
12
+ @labeler_endpoint ||= get_service('atproto_labeler', 'AtprotoLabeler')&.endpoint
13
+ end
14
+ end
15
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DIDKit
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/didkit.rb CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  require_relative "didkit/did"
4
4
  require_relative "didkit/document"
5
+ require_relative "didkit/plc_importer"
6
+ require_relative "didkit/plc_operation"
7
+ require_relative "didkit/resolver"
5
8
  require_relative "didkit/version"
6
9
 
7
10
  module DIDKit
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: didkit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kuba Suder
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-03-12 00:00:00.000000000 Z
11
+ date: 2024-03-19 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -21,12 +21,15 @@ files:
21
21
  - LICENSE.txt
22
22
  - README.md
23
23
  - lib/didkit.rb
24
+ - lib/didkit/at_handles.rb
24
25
  - lib/didkit/did.rb
25
26
  - lib/didkit/document.rb
26
27
  - lib/didkit/errors.rb
27
28
  - lib/didkit/plc_importer.rb
28
29
  - lib/didkit/plc_operation.rb
29
30
  - lib/didkit/resolver.rb
31
+ - lib/didkit/service_record.rb
32
+ - lib/didkit/services.rb
30
33
  - lib/didkit/version.rb
31
34
  - sig/didkit.rbs
32
35
  homepage: https://github.com/mackuba/didkit