diff --git a/client/core.lisp b/client/core.lisp index 9c8b9a5..010fd5a 100644 --- a/client/core.lisp +++ b/client/core.lisp @@ -69,6 +69,7 @@ ((string-equal type "number") (push 'double-float type-list)) ((string-equal type "string") (push 'string type-list)) ((string-equal type "array") (push 'list type-list)) + ((string-equal type "null") (push 'null type-list)) (t (error "Type ~S is not supported yet." type))))) @@ -418,6 +419,26 @@ lambda-list a separate defmethod." (jsonrpc/class:call client func-name arguments)))) +(eval-when (:compile-toplevel :load-toplevel :execute) + (defun %generate-client (class-name spec &key (export-symbols t)) + (let* ((client-class (generate-client-class class-name spec :export-symbols export-symbols)) + (object-classes + ;; The map from package::symbol to a code which defines + ;; a class for some complex object used as argument or + ;; result in an API: + (make-hash-table :test 'equal)) + (methods (loop for method-spec in (gethash "methods" spec) + appending (generate-method class-name method-spec object-classes + :export-symbols export-symbols))) + (class-definitions + (loop for def being the hash-value of object-classes + ;; Here each def contains a list of DEFCLASS + one or more methods. + appending def))) + (values client-class + class-definitions + methods)))) + + (defmacro generate-client (class-name url-or-path &key (export-symbols t)) "Generates Common Lisp client by OpenRPC spec. @@ -426,21 +447,10 @@ lambda-list a separate defmethod." 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 - ;; The map from package::symbol to a code which defines - ;; a class for some complex object used as argument or - ;; result in an API: - (make-hash-table :test 'equal)) - (methods (loop for method-spec in (gethash "methods" spec) - appending (generate-method class-name method-spec object-classes - :export-symbols export-symbols))) - (class-definitions - (loop for def being the hash-value of object-classes - ;; Here each def contains a list of DEFCLASS + one or more methods. - appending def))) - `(eval-when (:compile-toplevel :load-toplevel :execute) - ,@client-class - ,@class-definitions - ,@methods))) + (let* ((spec (retrieve-spec (eval url-or-path)))) + (multiple-value-bind (client-class class-definitions methods) + (%generate-client class-name spec :export-symbols export-symbols) + `(eval-when (:compile-toplevel :load-toplevel :execute) + ,@client-class + ,@class-definitions + ,@methods)))) diff --git a/openrpc-tests.asd b/openrpc-tests.asd index 0f1a4d8..7fe5492 100644 --- a/openrpc-tests.asd +++ b/openrpc-tests.asd @@ -7,7 +7,8 @@ "openrpc-tests/petshop" "openrpc-tests/server/interface" "openrpc-tests/client/deserialization" - "openrpc-tests/client/generation") + "openrpc-tests/client/generation" + "openrpc-tests/client/regression") :description "Test system for OPENRPC." :perform (test-op (op c) diff --git a/qlfile.lock b/qlfile.lock index 8b59e45..9bc696f 100644 --- a/qlfile.lock +++ b/qlfile.lock @@ -5,4 +5,4 @@ ("ultralisp" . (:class qlot/source/dist:source-dist :initargs (:distribution "http://dist.ultralisp.org" :%version :latest) - :version "20230808001000")) + :version "20230911163002")) diff --git a/t/client/regress-data/multiple-types/spec.json b/t/client/regress-data/multiple-types/spec.json new file mode 100644 index 0000000..ffd1867 --- /dev/null +++ b/t/client/regress-data/multiple-types/spec.json @@ -0,0 +1,39 @@ +{ + "methods": [ + { + "name": "example", + "params": [ + { + "name": "name", + "schema": + { + "type": ["string", "null"], + "maxLength": 255 + }, + "required": true, + "summary": "User name." + } + ], + "result": { + "name": "example_result", + "schema": { + "type": ["string", "null"], + "maxLength": 255 + } + }, + "summary": "Example method.", + "paramStructure": "by-name" + } + ], + "openrpc": "1.0.0", + "info": { + "title": "Example API", + "version": "0.1.0" + }, + "servers": [ + { + "name": "default", + "url": "https://example.org/" + } + ] +} diff --git a/t/client/regression.lisp b/t/client/regression.lisp new file mode 100644 index 0000000..b81347f --- /dev/null +++ b/t/client/regression.lisp @@ -0,0 +1,32 @@ +(uiop:define-package #:openrpc-tests/client/regression + (:use #:cl) + (:import-from #:rove + #:deftest) + (:import-from #:openrpc-client/core + #:%generate-client + #:retrieve-spec)) +(in-package #:openrpc-tests/client/regression) + + +(deftest client-regression () + (let ((spec + (rove:testing "Reading spec" + (retrieve-spec + ;; TODO: repeat this test for all subfolders of #P"t/client" + (asdf:system-relative-pathname :openrpc-tests + (make-pathname :directory '(:relative "t" "client" "regress-data" "multiple-types") + :name "spec" + :type "json")))))) + (rove:ok spec) + + (multiple-value-bind (client-class class-definitions methods) + (rove:testing "Generating client" + (%generate-client 'the-class spec :export-symbols nil)) + + ;; TODO: Here we need to compare results with forms read from: + ;; - #P"t/client/regress-data/multiple-types/client-class.lisp" + ;; - #P"t/client/regress-data/multiple-types/class-definitions.lisp" + ;; - #P"t/client/regress-data/multiple-types/methods.lisp" + (rove:ok client-class) + (rove:ok class-definitions) + (rove:ok methods))))