Skip to content

Instantly share code, notes, and snippets.

@CodeMonkeySteve
Created May 31, 2021 21:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CodeMonkeySteve/d7b75da979520bea8142f06d0d4b9f2a to your computer and use it in GitHub Desktop.
Save CodeMonkeySteve/d7b75da979520bea8142f06d0d4b9f2a to your computer and use it in GitHub Desktop.
IOTA API v1.5 (quick-n-dirty Ruby client)
module IOTA
class Message
attr_reader :id, :nonce
def initialize(node, id: nil, payload: nil)
@node = node
@id = id || (metadata && metadata[:messageId])
@payload = payload
@nonce = nil
end
def parents
@parents ||= metadata[:parentMessageIds].map { |id| self.class.new(node, id: id) }
end
def solid?
metadata[:isSolid]
end
def confirmed?
metadata[:ledgerInclusionState].in? %w(included noTransaction)
end
def promote?
metadata[:shouldPromote]
end
def reattach?
metadata[:shouldReattach]
end
def conflict
(metadata[:ledgerInclusionState] == 'conflicting') && metadata[:conflictReason]
end
def milestone
metadata[:milestoneIndex]
end
def ref_by_milestone
metadata[:referencedByMilestoneIndex]
end
def metadata(reload = false)
@metadata = nil if reload
@metadata ||= id.presence && node.get("api/v1/messages/#{id}/metadata")
end
def payload
@payload ||= id.presence && begin
data = node.get("api/v1/messages/#{id}")
@parents = data[:parentMessageIds].map { |id| self.class.new(node, id: id) }
@nonce = data[:nonce]
Payload.from_data(data[:payload])
end
end
def create!
raise "Message already crated" if id
raise "Message missing payload" unless payload
res = node.post('api/v1/messages', { payload: payload.to_data })
@id = res[:messageId]
self
end
protected
attr_reader :node
end
end
require 'net/http'
module IOTA
APIError = StandardError
class Node
def initialize(uri, token = nil)
@uri = URI.parse(uri)
@token = token
end
def healthy?
get('health')
end
def info
get('api/v1/info')
end
def message(id = nil)
Message.new(self, id: id)
end
def search(index)
res = get('api/v1/messages', index: IOTA.to_hex(index))
res[:messageIds].map { |id| message(id) }
end
def indexation_message(index, data)
payload = Payload::Indexation.new(index: index, data: data)
Message.new(self, payload: payload)
end
def get(path, params = nil)
api(:get, path, params)
end
def post(path, body = nil)
api(:post, path, body)
end
private
def api(method, path, body = nil)
uri = @uri.dup
path = '/' + path unless path.start_with?('/')
uri.path += path
if body && (method == :get)
uri.query = body.map { |k, v| "#{k}=#{CGI::escape(v.to_s)}" }.join('&')
end
request = Net::HTTP::const_get(method.to_s.titlecase).new(uri)
request["Authorization"] = "Bearer #{@token}" if @token.present?
if body && (method == :post)
request.content_type = 'application/json'
request.body = JSON.dump(body)
end
request_opts = { use_ssl: uri.scheme == "https" }
response = Net::HTTP.start(uri.hostname, uri.port, request_opts) { |http| http.request(request) }
raise APIError, response.inspect unless response.code&.to_i&.in?(200...300)
#return true unless response.body.present?
unless response.body.present?
return
end
data = JSON.parse(response.body).deep_symbolize_keys
raise data[:error] if data.key?(:error)
data[:data]
end
end
end
module IOTA
class Payload::Indexation < Payload
register_type 2
attr_accessor :index, :data
def initialize(index: nil, data: nil, **opts)
super(opts)
@index = index
@data = data
end
def self.from_data(payload)
new payload.merge(
index: IOTA.from_hex(payload[:index]),
data: IOTA.from_hex(payload[:data])
)
end
def to_data
super.merge(
index: IOTA.to_hex(index),
data: IOTA.to_hex(data)
)
end
end
end
module IOTA
class Payload
attr_reader :type
def initialize(type: nil)
@type = type || self.class.types.key(self.class)
end
def to_data
{ type: type }
end
def self.from_data(payload)
type = payload[:type]
if (klass = types[type])
klass.from_data(payload)
else
self.new(type: type)
end
end
protected
def self.types
@@types ||= {}
end
def self.register_type(index, klass = self)
types[index] = klass
end
end
end
require_relative 'payload/indexation'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment