Skip to content

Commit

Permalink
Merge af625fe into 49d242f
Browse files Browse the repository at this point in the history
  • Loading branch information
svetlyak40wt committed Nov 12, 2022
2 parents 49d242f + af625fe commit 08c1f4d
Show file tree
Hide file tree
Showing 17 changed files with 94 additions and 54 deletions.
22 changes: 11 additions & 11 deletions client/core.lisp
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
(uiop:define-package #:openrpc-client/core
(:use #:cl)
(:import-from #:40ants-doc
#:defsection
#:defsection-copy)
(:import-from #:kebab
#:to-lisp-case)
(:import-from #:jsonrpc)
(:import-from #:log)
(:import-from #:yason)
(:import-from #:jsonrpc/class)
(:import-from #:str)
(:import-from #:dexador)
Expand All @@ -17,15 +15,10 @@
#:connection-refused-error)
(:import-from #:openrpc-client/error
#:rpc-error)
(:export #:@index
#:@readme
#:generate-client))
(:export #:generate-client))
(in-package #:openrpc-client/core)


(defsection @example (:title "Example chapter")
"Please, fill this documentation with real docs.")


(eval-when (:compile-toplevel :load-toplevel :execute)
(defun generate-method-description (spec)
Expand Down Expand Up @@ -283,10 +276,17 @@
:message message
:func-name func-name
:func-arguments arguments)))))
(jsonrpc:call client func-name arguments))))
(jsonrpc/class:call client func-name arguments))))


(defmacro generate-client (class-name url-or-path &key (export-symbols t))
"Generates Common Lisp client by OpenRPC spec.
CLASS-NAME is the name of a API class. Also, a corresponding MAKE-<CLASS-NAME> function
is created.
URL-OR-PATH argument could be a string with HTTP URL of a spec, or a pathname
if a spec should be read from the disc."
(let* ((spec (retrieve-spec (eval url-or-path)))
(client-class (generate-client-class class-name spec :export-symbols export-symbols))
(object-classes
Expand Down
7 changes: 5 additions & 2 deletions client/docs.lisp
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
(uiop:define-package #:openrpc-client/docs
(:use #:cl)
(:import-from #:40ants-doc
#:defsection))
#:defsection)
(:import-from #:openrpc-client
#:generate-client))
(in-package #:openrpc-client/docs)


Expand Down Expand Up @@ -127,4 +129,5 @@ OPENRPC-EXAMPLE/CLIENT> (funcall #v167:1)
Now this is the last page and there is now a closure to retrieve the next page. Learn more how
to implement pagination on server-side in the OPENRPC-SERVER/DOCS::@PAGINATION section.
")
"
(generate-client macro))
7 changes: 6 additions & 1 deletion docs/changelog.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,16 @@

(defchangelog (:ignore-words ("40ANTS-DOC"
"ASDF"
"API"
"CL"
"OSX"))
(0.4.0 2022-11-07
"- Fixed usage of default API when api is not specified to define-rpc-method macro.
- Fixed most imports.")
(0.3.0 2022-10-30
"- Method and its params now support such metadata as :summary :description and :deprecated.
- Schemas for CL classes can have :description if documentation id defined for class or its slots.
- Function OPENRPC-CLIENT:GENERATE now exports methods, classes and their slot readers by default.
- Macro OPENRPC-CLIENT:GENERATE-CLIENT now exports methods, classes and their slot readers by default.
- All methods, their arguments and object keys now use underscore instead of dash to make them more
convenient to use from other languages.")
(0.2.0 2022-10-25
Expand Down
2 changes: 2 additions & 0 deletions docs/index.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
"URL"
"CL"
"REPL"
"MAKE"
"HTTP"
"OPENRPC-CLIENT"
"ASDF"))
"
Expand Down
1 change: 1 addition & 0 deletions example/client.lisp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
(uiop:define-package #:openrpc-example/client
(:use #:cl)
(:import-from #:jsonrpc)
(:import-from #:openrpc-client
#:generate-client))
(in-package #:openrpc-example/client)
Expand Down
1 change: 0 additions & 1 deletion example/server.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#:return-error
#:transform-result
#:type-to-schema)
(:import-from #:clack.handler.hunchentoot)
(:import-from #:serapeum
#:dict)
(:import-from #:openrpc-server/clack
Expand Down
1 change: 1 addition & 0 deletions openrpc-client.asd
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@
:source-control (:git "https://github.com/40ants/openrpc")
:in-order-to ((test-op (test-op openrpc-tests))))

(register-system-packages "log4cl" '(#:log))
5 changes: 2 additions & 3 deletions openrpc-example.asd
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@
:class :40ants-asdf-system
:defsystem-depends-on ("40ants-asdf-system")
:pathname "example"
:depends-on ("openrpc-example/server"
:depends-on ("clack-handler-hunchentoot"
"openrpc-example/server"
"openrpc-example/client")
:description "Example JSON-RPC server and client."
:homepage "https://40ants.com/openrpc/"
:source-control (:git "https://github.com/40ants/openrpc")
:in-order-to ((test-op (test-op openrpc-tests))))

(register-system-packages "clack-handler-hunchentoot" '(#:clack.handler.hunchentoot))
4 changes: 3 additions & 1 deletion openrpc-server.asd
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
:class :40ants-asdf-system
:defsystem-depends-on ("40ants-asdf-system")
:pathname "server"
:depends-on ("openrpc-server/server")
:depends-on ("openrpc-server/server"
"openrpc-server/class"
"openrpc-server/discovery")
:description "OpenRPC server implementation for Common Lisp."
:homepage "https://40ants.com/openrpc/"
:source-control (:git "https://github.com/40ants/openrpc")
Expand Down
6 changes: 3 additions & 3 deletions qlfile.lock
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
("quicklisp" .
(:class qlot/source/dist:source-dist
:initargs (:distribution "http://beta.quicklisp.org/dist/quicklisp.txt" :%version :latest)
:version "2022-07-08"))
:version "2022-11-07"))
("ultralisp" .
(:class qlot/source/dist:source-dist
:initargs (:distribution "http://dist.ultralisp.org" :%version :latest)
:version "20221026155000"))
:version "20221112192001"))
("bordeaux-threads" .
(:class qlot/source/github:source-github
:initargs (:repos "svetlyak40wt/bordeaux-threads" :ref nil :branch "fix-apiv2-for-no-threads" :tag nil)
:version "github-29a839d1db25d86b18c0caad50036da9481b064b"))
("jsonrpc" .
(:class qlot/source/github:source-github
:initargs (:repos "svetlyak40wt/jsonrpc" :ref nil :branch "separate-clack-builder" :tag nil)
:version "github-62be10e477bb9ad2047e1c06afeb70aed2dcbd99"))
:version "github-12bc0abfa33497fff0b9974f7c23b7df25b6a979"))
7 changes: 5 additions & 2 deletions server/clack.lisp
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
(uiop:define-package #:openrpc-server/clack
(:use #:cl)
(:import-from #:jsonrpc)
(:import-from #:log)
(:import-from #:yason)
(:import-from #:lack.request)
(:import-from #:jsonrpc/class
#:bind-server-to-transport)
(:import-from #:jsonrpc/transport/websocket
Expand All @@ -19,6 +20,8 @@
#:default-api)
(:import-from #:openrpc-server/method
#:method-thunk)
(:import-from #:websocket-driver
#:websocket-p)
(:export
#:make-clack-app))
(in-package #:openrpc-server/clack)
Expand All @@ -34,7 +37,7 @@
(defun process-request (env server websocket websocket-app http http-app indent-json)
(cond
((and websocket
(wsd:websocket-p env))
(websocket-p env))
(funcall websocket-app env))
((and (string-equal (getf env :path-info)
"/openrpc.json")
Expand Down
2 changes: 1 addition & 1 deletion server/discovery.lisp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
(uiop:define-package #:openrpc-server/discovery
(:use #:cl)
(:import-from #:jsonrpc)
(:import-from #:jsonrpc/mapper)
(:import-from #:lack.request)
(:import-from #:openrpc-server/vars
#:*current-request*)
Expand Down
66 changes: 44 additions & 22 deletions server/docs.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,11 @@ Now we can define an RPC method to create a new pet:
```
(openrpc-server:define-rpc-method create-pet (name tag)
(:param name string)
(:param tag string)
(:summary \"Short method docstring.\")
(:description \"Lengthy description of the method.\")
(:param name string \"Pet's name\"
:description \"This is a long description of the parameter.\")
(:param tag string \"Old param, don't use it anymore.\" :deprecated t)
(:result pet)
(let* ((new-id (get-new-id))
(pet (make-instance 'pet
Expand All @@ -67,33 +70,52 @@ Now we can define an RPC method to create a new pet:
Here we should explicitly specify type for each parameter and result's type.
But to make this work with our pet class, we have to define a two more methods.
First one will tell framework to which JSON-SCHEMA should be mapped objects of this type:
Pay attention, the result type is PET class. OPENRPC-SERVER takes care on serializing
objects and you can retrieve an OpenRPC spec for any type, using TYPE-TO-SCHEMA generic-function:
```
(defmethod type-to-schema ((type (eql 'pet)))
(dict \"type\" \"object\"
\"properties\" (dict \"id\" (type-to-schema 'integer)
\"name\" (type-to-schema 'string)
\"tag\" (type-to-schema 'string))
\"required\" (list \"id\" \"name\" \"tag\")
\"x-cl-class\" (symbol-name type)
\"x-cl-package\" (package-name (symbol-package type))))
CL-USER> (serapeum:toggle-pretty-print-hash-table)
T
CL-USER> (openrpc-server:type-to-schema 'pet)
(SERAPEUM:DICT
\"type\" \"object\"
\"properties\" (SERAPEUM:DICT
\"id\" (SERAPEUM:DICT
\"type\" \"integer\"
)
\"name\" (SERAPEUM:DICT
\"type\" \"string\"
)
\"tag\" (SERAPEUM:DICT
\"type\" \"string\"
)
)
\"required\" '(\"tag\" \"name\" \"id\")
\"x-cl-class\" \"PET\"
\"x-cl-package\" \"COMMON-LISP-USER\"
)
```
And second method should transform pet instance into simple datastructures
according to scheme. Later result of this transformation will be serialized
to JSON:
This method is used to render response requests to `/openrpc.json` handle of your API.
There is also a second generic-function which transform class instance into simple datastructures
according to a scheme. For example, here is how we can serialize our pet:
```
(defmethod transform-result ((obj pet))
(dict \"id\" (pet-id obj)
\"name\" (pet-name obj)
\"tag\" (pet-tag obj)))
CL-USER> (openrpc-server:transform-result
(make-instance 'pet :name \"Bobik\"))
(SERAPEUM:DICT
\"name\" \"Bobik\"
)
CL-USER> (openrpc-server:transform-result
(make-instance 'pet
:name \"Bobik\"
:tag \"the dog\"))
(SERAPEUM:DICT
\"name\" \"Bobik\"
\"tag\" \"the dog\"
)
```
Probably, someday framework will generate these methods automatically, using
types from DEFCLASS form.
")


Expand Down
1 change: 1 addition & 0 deletions server/interface.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
(:use #:cl)
(:import-from #:serapeum
#:dict)
(:import-from #:jsonrpc)
(:import-from #:alexandria
#:length=)
(:import-from #:openrpc-server/api
Expand Down
9 changes: 6 additions & 3 deletions server/method.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
(:use #:cl)
(:import-from #:log)
(:import-from #:serapeum
#:soft-list-of
#:fmt
#:dict)
(:import-from #:alexandria
Expand All @@ -26,6 +27,7 @@
#:api-server)
(:import-from #:log4cl-extras/error
#:with-log-unhandled)
(:import-from #:jsonrpc)
(:import-from #:jsonrpc/errors
#:jsonrpc-callback-error)
(:import-from #:openrpc-server/errors
Expand All @@ -39,10 +41,11 @@

(defclass parameter ()
((name :initarg :name
:type string
:type symbol
:reader parameter-name)
(type :initarg :type
:type string
:type (or symbol
(soft-list-of symbol))
:reader parameter-type)
(required :initarg :required
:initform nil
Expand Down Expand Up @@ -302,7 +305,7 @@
Also, there should be one (:result type) form in the BODY."
(destructuring-bind (api name)
(etypecase name
(symbol (list default-api name))
(symbol (list 'default-api name))
(list name))
(with-destructured-lambda-list (:required required-args
:optional optional-args
Expand Down
2 changes: 0 additions & 2 deletions server/server.lisp
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
(uiop:define-package #:openrpc-server
(:use #:cl)
(:import-from #:openrpc-server/discovery)
(:import-from #:openrpc-server/class)
(:import-from #:openrpc-server/method
#:define-rpc-method)
(:import-from #:openrpc-server/interface
Expand Down
5 changes: 3 additions & 2 deletions t/petshop.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,12 @@
(testing-app "Checking PetShop"
(make-clack-app)
(let* ((url (localhost "/openrpc.json"))
(test-package (make-package "test-package1" :use (list :cl))))
(test-package (make-package "test-package1" :use (list :cl)))
(api-symbol (intern "PETSHOP" test-package)))
(unwind-protect
(let* ((*package* test-package))
(testing "Client classes creation"
(eval `(generate-client petshop ,url))
(eval `(generate-client ,api-symbol ,url))

(let ((client (uiop:symbol-call test-package "MAKE-PETSHOP")))
(jsonrpc:client-connect client :url (localhost "/") :mode :http)
Expand Down

0 comments on commit 08c1f4d

Please sign in to comment.