Renamed; added root, config, version calls
This commit is contained in:
parent
d0706840db
commit
ea09e38f67
|
@ -1,7 +1,7 @@
|
|||
(defsystem "ipfs-gno"
|
||||
(defsystem "cl-ipfs-api²"
|
||||
:version "0.1"
|
||||
:author "Jaidyn Ann <jadedctrl@teknik.io>"
|
||||
:license "Cooperative Software License"
|
||||
:license "AGPLv3"
|
||||
:depends-on ("drakma" "yason" "arnesi")
|
||||
:components ((:file "package")
|
||||
(:file "main")))
|
247
main.lisp
247
main.lisp
|
@ -1,59 +1,240 @@
|
|||
(in-package :ipfs-gno)
|
||||
(in-package :cl-ipfs-api2)
|
||||
|
||||
(defparameter *api-host* "http://127.0.0.1:5001")
|
||||
(defparameter *api-root* "/api/v0/")
|
||||
|
||||
;; STRING LIST … → (STRING/VARYING RETURN-CODE HEADER-LIST …)
|
||||
(defun api-call (call arguments &key (method :get) (parameters nil)
|
||||
(want-stream nil))
|
||||
(let ((call-url (string+ *api-host* "/api/v0/" call))
|
||||
(first-arg T))
|
||||
|
||||
;; STRING LIST [:LIST :BOOLEAN :SYMBOL] → STRING | HASHTABLE | (NIL STRING)
|
||||
(defun ipfs-call (call arguments &key (parameters nil) (want-stream nil)
|
||||
(method :GET))
|
||||
"Make an IPFS HTTP API call. Quite commonly used.
|
||||
Some calls return strings/raw data, and others return JSON.
|
||||
When strings/arbitrary data are recieved, they're returned verbatim.
|
||||
But, when JSON is returned, it is parsed into a hashtable.
|
||||
If the JSON is 'error JSON', I.E., it signals that an error has been
|
||||
recieved, two values are returned: NIL and the string-error-message."
|
||||
(let ((result
|
||||
(drakma:http-request
|
||||
(make-call-url *api-host* *api-root* call arguments)
|
||||
:method method
|
||||
:parameters parameters
|
||||
:want-stream want-stream)))
|
||||
|
||||
(cond (want-stream result)
|
||||
((stringp result) result)
|
||||
((vectorp result)
|
||||
(let ((json (yason:parse (flexi-streams:octets-to-string result))))
|
||||
(cond ((equal "error" (gethash "Type" json))
|
||||
(values nil (gethash "Message" json)))
|
||||
('T json)))))))
|
||||
|
||||
|
||||
;; STRING STRING LIST → STRING
|
||||
(defun make-call-url (host root call arguments)
|
||||
"Create the URL of an API call, as per the given arguments.
|
||||
Symbols are assumed to be something like 'T (so boolean), nil likewise.
|
||||
Arguments should look like this:
|
||||
(('recursive' nil)('name' 'xabbu'))"
|
||||
(let ((call-url (string+ host root call))
|
||||
(first-arg 'T))
|
||||
(mapcar (lambda (arg-pair)
|
||||
(format t "FIRST: ~A~%" first-arg)
|
||||
(if arg-pair
|
||||
(progn (setq call-url
|
||||
(string+ call-url (if first-arg "?" "&")
|
||||
(car arg-pair) "=" (cadr arg-pair)))
|
||||
(setq first-arg nil))))
|
||||
(when arg-pair
|
||||
(setq call-url
|
||||
(string+
|
||||
call-url
|
||||
(if first-arg "?" "&")
|
||||
(first arg-pair) "="
|
||||
(cond ((not (second arg-pair))
|
||||
"false")
|
||||
((symbolp (second arg-pair))
|
||||
"true")
|
||||
('T (second arg-pair))))))
|
||||
(setq first-arg nil))
|
||||
arguments)
|
||||
|
||||
(drakma:http-request call-url :method method :parameters parameters
|
||||
:want-stream want-stream)))
|
||||
call-url))
|
||||
|
||||
|
||||
;; STRING :NUMBER :NUMBER → STRING
|
||||
;; FORM FORM → FORM
|
||||
(defmacro bind-api-result (call form)
|
||||
"Wrap around an #'ipfs-call form; if #'call returns an error, then
|
||||
return NIL and the error message-- (NIL STRING)-- otherwise, execute
|
||||
#'form.
|
||||
Binds the result of the API call to… you guessed it, the variable 'result'.
|
||||
The error message is assigned to 'message', if such a thing exists."
|
||||
`(multiple-value-bind (result message)
|
||||
,call
|
||||
(if message
|
||||
(values nil message)
|
||||
,form)))
|
||||
|
||||
|
||||
|
||||
;; -------------------------------------
|
||||
;; / CALLS
|
||||
|
||||
;; PATHNAME → (HASH-STRING SIZE-NUMBER) || (NIL STRING)
|
||||
(defun add (pathname &key (pin 't) (only-hash nil))
|
||||
"Add a file to IPFS, return it's hash.
|
||||
http://127.0.0.1:8080/ipns/docs.ipfs.io/reference/api/http/#api-v0-add"
|
||||
(bind-api-result
|
||||
(ipfs-call "add" `(("pin" ,pin) ("only-hash" ,only-hash))
|
||||
:method :post :parameters `(("file" . ,pathname)))
|
||||
|
||||
(values (gethash "Hash" result)
|
||||
(parse-integer (gethash "Size" result))
|
||||
(gethash "Name" result))))
|
||||
|
||||
;; STRING :NUMBER :NUMBER → STRING || (NIL STRING)
|
||||
(defun cat (ipfs-path &key (offset nil) (length nil))
|
||||
"Return a string of the data at the given IPFS path."
|
||||
(api-call "cat"
|
||||
`(("arg" ,ipfs-path)
|
||||
,(if offset `("offset" ,offset))
|
||||
,(if length `("length" ,length)))))
|
||||
"Return a string of the data at the given IPFS path.
|
||||
/ipns/docs.ipfs.io/reference/api/http/#api-v0-cat"
|
||||
(bind-api-result
|
||||
(ipfs-call "cat"
|
||||
`(("arg" ,ipfs-path)
|
||||
,(if offset `("offset" ,offset))
|
||||
,(if length `("length" ,length))))
|
||||
result))
|
||||
|
||||
;; STRING [:BOOLEAN :BOOLEAN] → (LIST LIST LIST LIST) || (NIL STRING)
|
||||
(defun ls (ipfs-path &key (resolve-type 't) (size 't))
|
||||
"Returns all sub-objects (IPFS hashes) under a given IPFS/IPNS directory
|
||||
path. Returns four lists; hashes, sizes, names, types. They are related
|
||||
positionally-- aka, item #2 in the hash-list, will have a size of #2 in the
|
||||
size-list, etc.
|
||||
/ipns/docs.ipfs.io/reference/api/http/#api-v0-ls"
|
||||
(bind-api-result
|
||||
(ipfs-call "ls" `(("arg" ,ipfs-path)
|
||||
("resolve-type" ,resolve-type) ("size" ,size)))
|
||||
|
||||
;; STRING PATHNAME --> NIL
|
||||
(let ((links (gethash "Links"
|
||||
(car (gethash "Objects" result)))))
|
||||
(values (mapcar (lambda (link) (gethash "Hash" link)) links)
|
||||
(mapcar (lambda (link) (gethash "Size" link)) links)
|
||||
(mapcar (lambda (link) (gethash "Name" link)) links)
|
||||
(mapcar (lambda (link) (gethash "Type" link)) links)))))
|
||||
|
||||
;; STRING PATHNAME → NIL
|
||||
(defun dl (ipfs-path out-file)
|
||||
"Write an IPFS file directly to a file on the local file-system.
|
||||
Non-recursive, in the case of directories.
|
||||
(Thanks to this thread ♥) https://stackoverflow.com/a/12607423"
|
||||
Non-recursive, in the case of directories… for now.
|
||||
(Thanks to this thread ♥: https://stackoverflow.com/a/12607423)
|
||||
Is a general replacement for the 'get' API call, but actually just uses
|
||||
the 'cat' call, due to some issues with using 'get'.
|
||||
Will not actually return NIL when an error is reached (like other functions)
|
||||
with an error-message, it'lll just write the error JSON to the file.
|
||||
Whoops."
|
||||
(with-open-file (out-stream out-file :direction :output
|
||||
:element-type '(unsigned-byte 8)
|
||||
:if-exists :overwrite :if-does-not-exist :create)
|
||||
(let ((in-stream
|
||||
(api-call "cat" `(("arg" ,ipfs-path)) :want-stream 'T)))
|
||||
(ipfs-call "cat" `(("arg" ,ipfs-path)) :want-stream 'T)))
|
||||
|
||||
(awhile (read-byte in-stream nil nil)
|
||||
(write-byte it out-stream))
|
||||
(close in-stream))))
|
||||
|
||||
;; -----------------
|
||||
|
||||
;; PATHNAME → STRING
|
||||
(defun add (file-path)
|
||||
"Add a file to IPFS, return it's hash. Does not work recursively."
|
||||
(gethash "Hash"
|
||||
(yason:parse
|
||||
(flexi-streams:octets-to-string
|
||||
(api-call "add" '() :method :post
|
||||
:parameters `(("file" . ,file-path)))))))
|
||||
;; [STRING] → (STRING LIST STRING STRING STRING)
|
||||
(defun id (&key (peer-id nil))
|
||||
"Return info on a node by ID. Has multiple return-values, as follows:
|
||||
public-key, address-list, agent-version, protocol-version, and peer ID.
|
||||
The last might be redundant if it was specified as an argument; otherwise,
|
||||
it tells you your own node's ID.
|
||||
/ipns/docs.ipfs.io/reference/api/http/#api-v0-id"
|
||||
(bind-api-result
|
||||
(ipfs-call "id" `(,(if peer-id (list "arg" peer-id))))
|
||||
|
||||
(values (gethash "PublicKey" result)
|
||||
(gethash "Addresses" result)
|
||||
(gethash "AgentVersion" result)
|
||||
(gethash "ProtocolVersion" result)
|
||||
(gethash "ID" result))))
|
||||
|
||||
;; -----------------
|
||||
|
||||
;; STRING → (STRING || (NIL STRING)
|
||||
(defun dns (domain &key (recursive 't))
|
||||
"Resolve a domain into a path (usually /ipfs/).
|
||||
http://127.0.0.1:8080/ipns/docs.ipfs.io/reference/api/http/#api-v0-dns"
|
||||
(bind-api-result
|
||||
(ipfs-call "dns" `(("arg" ,domain) ("recursive" ,recursive)))
|
||||
(gethash "Path" result)))
|
||||
|
||||
;; STRING [:BOOLEAN :NUMBER :NUMBER] → STRING || (NIL STRING)
|
||||
(defun resolve (ipfs-path &key (recursive 't) (dht-record-count nil)
|
||||
(dht-timeout 30))
|
||||
"Resolve a given name to an IPFS path."
|
||||
(bind-api-result
|
||||
(ipfs-call "resolve" `(("arg" ,ipfs-path) ("recursive" ,recursive)
|
||||
,(if dht-record-count
|
||||
(list "dht-record-count" dht-record-count))
|
||||
("dht-timeout" ,(string+ dht-timeout "s"))))
|
||||
(gethash "Path" result)))
|
||||
|
||||
;; -----------------
|
||||
|
||||
;; NIL → NIL
|
||||
(defun shutdown ()
|
||||
"Shut down the connected IPFS node.
|
||||
/ipns/docs.ipfs.io/reference/api/http/#api-v0-shutdown"
|
||||
(ipfs-call "shutdown" '()))
|
||||
|
||||
|
||||
|
||||
;; -------------------------------------
|
||||
;; / CONFIG CALLS
|
||||
|
||||
;; STRING [:STRING :BOOLEAN :BOOLEAN] → STRING || (NIL STRING)
|
||||
(defun config (key &key (value nil) (bool nil) (json nil))
|
||||
"Get/set a config key's value.
|
||||
http://127.0.0.1:8080/ipns/docs.ipfs.io/reference/api/http/#api-v0-config"
|
||||
(bind-api-result
|
||||
(ipfs-call "config" `(("arg" ,key) ,(if value (list "value" value))
|
||||
("bool" ,bool) ("json" ,json)))
|
||||
`(,(gethash "Key" result) ,(gethash "Value" result))))
|
||||
|
||||
;; NIL → HASH-TABLE
|
||||
(defun config/show ()
|
||||
"Return the config file's contents, in a Yason, JSON-parsed hashtable.
|
||||
Doesn't quite line up with #api-v0-config-show"
|
||||
(bind-api-result
|
||||
(ipfs-call "config/show" '())
|
||||
result))
|
||||
|
||||
|
||||
|
||||
;; -------------------------------------
|
||||
;; / VERSION CALLS
|
||||
|
||||
;; NIL → (STRING STRING STRING STRING STRING)
|
||||
(defun version ()
|
||||
"Return versioning information on this IPFS node
|
||||
/ipns/docs.ipfs.io/reference/api/http/#api-v0-version"
|
||||
(bind-api-result
|
||||
(ipfs-call "version" '())
|
||||
(values (gethash "Version" result)
|
||||
(gethash "Commit" result)
|
||||
(gethash "Repo" result)
|
||||
(gethash "System" result)
|
||||
(gethash "Golang" result))))
|
||||
|
||||
;; NIL → (STRING STRING STRING STRING)
|
||||
(defun version/deps ()
|
||||
"Return info about dependencies used for build.
|
||||
/ipns/docs.ipfs.io/reference/api/http/#api-v0-version"
|
||||
(bind-api-result
|
||||
(ipfs-call "version/deps" '())
|
||||
(values (gethash "Version" result)
|
||||
(gethash "Path" result)
|
||||
(gethash "ReplacedBy" result)
|
||||
(gethash "Sum" result))))
|
||||
|
||||
|
||||
|
||||
|
||||
;; -------------------------------------
|
||||
;; UTIL
|
||||
|
||||
;; STRING-A STRING-B … STRING-N → STRING
|
||||
(defun string+ (&rest strings)
|
||||
|
|
23
package.lisp
23
package.lisp
|
@ -1,9 +1,26 @@
|
|||
(defpackage :ipfs-gno
|
||||
(defpackage :cl-ipfs-api²
|
||||
(:use :cl :arnesi)
|
||||
(:nicknames :cl-ipfs :ipfs :cl-ipfs-api2)
|
||||
(:export
|
||||
*api-host*
|
||||
*api-root*
|
||||
|
||||
;; / calls
|
||||
:dl
|
||||
:cat
|
||||
:add))
|
||||
:add
|
||||
:dns
|
||||
:id
|
||||
:ls
|
||||
:resolve
|
||||
:shutdown
|
||||
|
||||
(in-package :ipfs-gno)
|
||||
;; / config calls
|
||||
:config
|
||||
config/show
|
||||
|
||||
;; /version calls
|
||||
:version
|
||||
:version/deps))
|
||||
|
||||
(in-package :cl-ipfs-api²)
|
||||
|
|
Ŝarĝante…
Reference in New Issue