didkit 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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