Skip to content

Commit 9214a0e

Browse files
committed
Merge: Intro of nitrestful a RESTful API generator
This tool generate a Nit module which implements `Action::answer` to redirect request to a static Nit method. It checks the presence of args, their types, deserialize objects as needed and calls the target method. Missing arguments or arguments with errors (wrong type, failed deserialization, etc.) are replaced by `null` when the corresponding parameter is `nullable`. If the parameter is non-nullable, or if there is any other error, the generated code calls `super` from `answer` for the user code to handle exceptions and errors. With the `restful` annotation we can write a normal method with static types such as: (from the example) ~~~ # User code class MyAction super RestfulAction # Method answering requests like `foo?s=some_string&i=42&b=true` fun foo(s: String, i: Int, b: Bool): HttpResponse is restful do var resp = new HttpResponse(200) resp.body = "foo {s} {i} {b}" return resp end ... ~~~ And nitrestful will generate for use the wrapper extracting the args from the request and call the method `foo`: ~~~ # Generated code by `nitrestful` redef class MyAction redef fun answer(request, truncated_uri) do var verbs = truncated_uri.split("/") if verbs.not_empty and verbs.first.is_empty then verbs.shift if verbs.length != 1 then return super var verb = verbs.first if verb == "foo" then var in_s = request.string_arg("s") var out_s = in_s var in_i = request.string_arg("i") var out_i = deserialize_arg(in_i) var in_b = request.string_arg("b") var out_b = deserialize_arg(in_b) if not out_s isa String or not out_i isa Int or not out_b isa Bool then return super end return foo(out_s, out_i, out_b) end return super end end ~~~ This is an early version of this tool. More work is needed to test different types, different usages, error management, and improve the docs and examples. close #1852 Pull-Request: #1863 Reviewed-by: Jean Privat <jean@pryen.org> Reviewed-by: Romain Chanoir <romain.chanoir@viacesi.fr> Reviewed-by: Jean-Philippe Caissy <jpcaissy@piji.ca>
2 parents aa72915 + f5714f5 commit 9214a0e

File tree

17 files changed

+473
-43
lines changed

17 files changed

+473
-43
lines changed

contrib/jwrapper/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ src/javap_test_parser.nit: ../nitcc/src/nitcc grammar/javap.sablecc
1212
mv javap* gen/
1313

1414
src/serial.nit: $(shell ../../bin/nitls -M src/jwrapper.nit)
15-
../../bin/nitserial -o src/serial.nit -d package src/jwrapper.nit
15+
../../bin/nitserial -o src/serial.nit src/jwrapper.nit
1616

1717
bin/jwrapper: src/javap_test_parser.nit src/serial.nit $(shell ../../bin/nitls -M src/jwrapper.nit) ../../bin/nitc
1818
mkdir -p bin

contrib/jwrapper/src/model.nit

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ class NitType
157157
var identifier: String
158158

159159
# If this NitType was found in `lib/android`, contains the module name to import
160-
var mod: nullable NitModule
160+
var mod: nullable NitModuleRef
161161

162162
# Is this type known, wrapped and available in Nit?
163163
var is_known: Bool = true
@@ -183,7 +183,7 @@ class JavaClass
183183
var constructors = new Array[JavaConstructor]
184184

185185
# Importations from this class
186-
var imports = new HashSet[NitModule]
186+
var imports = new HashSet[NitModuleRef]
187187

188188
# Interfaces implemented by this class
189189
var implements = new HashSet[JavaType]
@@ -482,15 +482,15 @@ class JavaConstructor
482482
end
483483

484484
# A Nit module, use to import the referenced extern classes
485-
class NitModule
485+
class NitModuleRef
486486
# Relative path to the module
487487
var path: String
488488

489489
# Name of the module
490490
var name: String is lazy do return path.basename(".nit")
491491

492492
redef fun to_s do return self.name
493-
redef fun ==(other) do return other isa NitModule and self.path == other.path
493+
redef fun ==(other) do return other isa NitModuleRef and self.path == other.path
494494
redef fun hash do return self.path.hash
495495
end
496496

@@ -501,7 +501,7 @@ redef class Sys
501501
# * The value is the corresponding `NitType`.
502502
var find_extern_class: DefaultMap[String, nullable NitType] is lazy do
503503
var map = new DefaultMap[String, nullable NitType](null)
504-
var modules = new HashMap[String, NitModule]
504+
var modules = new HashMap[String, NitModuleRef]
505505

506506
var lib_paths = opt_libs.value
507507
if lib_paths == null then lib_paths = new Array[String]
@@ -543,7 +543,7 @@ redef class Sys
543543

544544
var mod = modules.get_or_null(path)
545545
if mod == null then
546-
mod = new NitModule(path)
546+
mod = new NitModuleRef(path)
547547
modules[path] = mod
548548
end
549549

lib/gen_nit.nit

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
# Support to generate and otherwise manipulate Nit code
1616
module gen_nit
1717

18+
import template
19+
1820
redef class Sys
1921
# Reserved keywords in the Nit language
2022
var keywords: Set[String] is lazy do return new HashSet[String].from([
@@ -39,3 +41,37 @@ redef class Sys
3941
var methods_in_pointer: Array[String] is lazy do return methods_in_object + [
4042
"free"]
4143
end
44+
45+
# Template of a Nit module to generate Nit code
46+
class NitModule
47+
super Template
48+
49+
# Header on top of the module, usually the documentation
50+
var header: nullable Writable = null is writable
51+
52+
# The module's name
53+
var name: Writable is writable
54+
55+
# Imports from this module
56+
var imports = new Array[Writable]
57+
58+
# Main content of this module
59+
var content = new Array[Writable]
60+
61+
redef fun rendering
62+
do
63+
var header = header
64+
if header != null then add header
65+
66+
var name = name
67+
add "module {name}\n\n"
68+
69+
for i in imports do add "import {i}\n"
70+
add "\n"
71+
72+
for l in content do
73+
add l
74+
add "\n"
75+
end
76+
end
77+
end

lib/nitcorn/examples/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
src/restful_annot_gen.nit

lib/nitcorn/examples/Makefile

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
1-
all:
1+
all: bin/restful_annot
22
mkdir -p bin/
33
../../../bin/nitc --dir bin src/nitcorn_hello_world.nit src/simple_file_server.nit
44

55
xymus.net:
66
mkdir -p bin/
77
../../../bin/nitc --dir bin/ src/xymus_net.nit
8+
9+
pre-build: src/restful_annot_gen.nit
10+
src/restful_annot_gen.nit:
11+
../../../bin/nitrestful -o $@ src/restful_annot.nit
12+
13+
bin/restful_annot: src/restful_annot_gen.nit
14+
mkdir -p bin/
15+
../../../bin/nitc -o $@ src/restful_annot_gen.nit
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# This file is part of NIT ( http://www.nitlanguage.org ).
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import nitcorn::restful
16+
17+
class MyAction
18+
super RestfulAction
19+
20+
# Method answering requests like `foo?s=some_string&i=42&b=true`
21+
fun foo(s: String, i: Int, b: Bool): HttpResponse
22+
is restful do
23+
var resp = new HttpResponse(200)
24+
resp.body = "foo {s} {i} {b}"
25+
return resp
26+
end
27+
28+
# Method answering requests like `bar?s=these_arguments_are_optional`
29+
fun bar(s: nullable String, i: nullable Int, b: nullable Bool): HttpResponse
30+
is restful do
31+
var resp = new HttpResponse(200)
32+
resp.body = "bar {s or else "null"} {i or else "null"} {b or else "null"}"
33+
return resp
34+
end
35+
end
36+
37+
var vh = new VirtualHost("localhost:8080")
38+
39+
# Serve everything with our restful action
40+
vh.routes.add new Route(null, new MyAction)
41+
42+
# Avoid executing when running tests
43+
if "NIT_TESTING".environ == "true" then exit 0
44+
45+
var factory = new HttpFactory.and_libevent
46+
factory.config.virtual_hosts.add vh
47+
factory.run

lib/nitcorn/restful.nit

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# This file is part of NIT ( http://www.nitlanguage.org ).
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# Support module for the `nitrestful` tool and the `restful` annotation
16+
module restful is new_annotation(restful)
17+
18+
import nitcorn
19+
import json::serialization
20+
21+
# Action with `restful` methods
22+
class RestfulAction
23+
super Action
24+
25+
redef fun answer(request, truncated_uri) do return new HttpResponse(400)
26+
27+
# Service to deserialize arguments from JSON
28+
#
29+
# Accepts `nullable String` for convenience, but returns `null` when `val == null`.
30+
#
31+
# This method is called by the code generated by `nitrestful`.
32+
# It can be specialized to customize its behavior.
33+
protected fun deserialize_arg(val: nullable String): nullable Object
34+
do
35+
if val == null then return null
36+
37+
var deserializer = new JsonDeserializer(val)
38+
if deserializer.errors.not_empty then
39+
print_error deserializer.errors.join("\n")
40+
return null
41+
end
42+
43+
return deserializer.deserialize
44+
end
45+
end

share/man/nitrestful.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# NAME
2+
3+
nitrestful - generates boilerplate code to relay RESTful request to static Nit methods
4+
5+
# SYNOPSIS
6+
7+
nitrestful [*options*]... FILE
8+
9+
# OPTIONS
10+
11+
### `-o`, `--output`
12+
Output file (can also be 'stdout').
13+
14+
### `--dir`
15+
Output directory.
16+
17+
# SEE ALSO
18+
19+
The Nit language documentation and the source code of its tools and libraries may be downloaded from <http://nitlanguage.org>

src/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
NITCOPT=--semi-global
1818
OLDNITCOPT=--semi-global
19-
OBJS=nitc nitpick nit nitdoc nitls nitunit nitpretty nitmetrics nitx nitlight nitdbg_client nitserial
19+
OBJS=nitc nitpick nit nitdoc nitls nitunit nitpretty nitmetrics nitx nitlight nitdbg_client nitserial nitrestful
2020
SRCS=$(patsubst %,%.nit,$(OBJS))
2121
BINS=$(patsubst %,../bin/%,$(OBJS))
2222

src/frontend/serialization_phase.nit

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ redef class ADefinition
5656
end
5757
end
5858

59-
# TODO add annotations on attributes (volatile, sensitive or do_not_serialize?)
6059
private class SerializationPhasePreModel
6160
super Phase
6261

0 commit comments

Comments
 (0)