Skip to content

Commit

Permalink
Merge: Intro of nitrestful a RESTful API generator
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
privat committed Dec 4, 2015
2 parents aa72915 + f5714f5 commit 9214a0e
Show file tree
Hide file tree
Showing 17 changed files with 473 additions and 43 deletions.
2 changes: 1 addition & 1 deletion contrib/jwrapper/Makefile
Expand Up @@ -12,7 +12,7 @@ src/javap_test_parser.nit: ../nitcc/src/nitcc grammar/javap.sablecc
mv javap* gen/

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

bin/jwrapper: src/javap_test_parser.nit src/serial.nit $(shell ../../bin/nitls -M src/jwrapper.nit) ../../bin/nitc
mkdir -p bin
Expand Down
12 changes: 6 additions & 6 deletions contrib/jwrapper/src/model.nit
Expand Up @@ -157,7 +157,7 @@ class NitType
var identifier: String

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

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

# Importations from this class
var imports = new HashSet[NitModule]
var imports = new HashSet[NitModuleRef]

# Interfaces implemented by this class
var implements = new HashSet[JavaType]
Expand Down Expand Up @@ -482,15 +482,15 @@ class JavaConstructor
end

# A Nit module, use to import the referenced extern classes
class NitModule
class NitModuleRef
# Relative path to the module
var path: String

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

redef fun to_s do return self.name
redef fun ==(other) do return other isa NitModule and self.path == other.path
redef fun ==(other) do return other isa NitModuleRef and self.path == other.path
redef fun hash do return self.path.hash
end

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

var lib_paths = opt_libs.value
if lib_paths == null then lib_paths = new Array[String]
Expand Down Expand Up @@ -543,7 +543,7 @@ redef class Sys

var mod = modules.get_or_null(path)
if mod == null then
mod = new NitModule(path)
mod = new NitModuleRef(path)
modules[path] = mod
end

Expand Down
36 changes: 36 additions & 0 deletions lib/gen_nit.nit
Expand Up @@ -15,6 +15,8 @@
# Support to generate and otherwise manipulate Nit code
module gen_nit

import template

redef class Sys
# Reserved keywords in the Nit language
var keywords: Set[String] is lazy do return new HashSet[String].from([
Expand All @@ -39,3 +41,37 @@ redef class Sys
var methods_in_pointer: Array[String] is lazy do return methods_in_object + [
"free"]
end

# Template of a Nit module to generate Nit code
class NitModule
super Template

# Header on top of the module, usually the documentation
var header: nullable Writable = null is writable

# The module's name
var name: Writable is writable

# Imports from this module
var imports = new Array[Writable]

# Main content of this module
var content = new Array[Writable]

redef fun rendering
do
var header = header
if header != null then add header

var name = name
add "module {name}\n\n"

for i in imports do add "import {i}\n"
add "\n"

for l in content do
add l
add "\n"
end
end
end
1 change: 1 addition & 0 deletions lib/nitcorn/examples/.gitignore
@@ -0,0 +1 @@
src/restful_annot_gen.nit
10 changes: 9 additions & 1 deletion lib/nitcorn/examples/Makefile
@@ -1,7 +1,15 @@
all:
all: bin/restful_annot
mkdir -p bin/
../../../bin/nitc --dir bin src/nitcorn_hello_world.nit src/simple_file_server.nit

xymus.net:
mkdir -p bin/
../../../bin/nitc --dir bin/ src/xymus_net.nit

pre-build: src/restful_annot_gen.nit
src/restful_annot_gen.nit:
../../../bin/nitrestful -o $@ src/restful_annot.nit

bin/restful_annot: src/restful_annot_gen.nit
mkdir -p bin/
../../../bin/nitc -o $@ src/restful_annot_gen.nit
47 changes: 47 additions & 0 deletions lib/nitcorn/examples/src/restful_annot.nit
@@ -0,0 +1,47 @@
# This file is part of NIT ( http://www.nitlanguage.org ).
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import nitcorn::restful

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

# Method answering requests like `bar?s=these_arguments_are_optional`
fun bar(s: nullable String, i: nullable Int, b: nullable Bool): HttpResponse
is restful do
var resp = new HttpResponse(200)
resp.body = "bar {s or else "null"} {i or else "null"} {b or else "null"}"
return resp
end
end

var vh = new VirtualHost("localhost:8080")

# Serve everything with our restful action
vh.routes.add new Route(null, new MyAction)

# Avoid executing when running tests
if "NIT_TESTING".environ == "true" then exit 0

var factory = new HttpFactory.and_libevent
factory.config.virtual_hosts.add vh
factory.run
45 changes: 45 additions & 0 deletions lib/nitcorn/restful.nit
@@ -0,0 +1,45 @@
# This file is part of NIT ( http://www.nitlanguage.org ).
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Support module for the `nitrestful` tool and the `restful` annotation
module restful is new_annotation(restful)

import nitcorn
import json::serialization

# Action with `restful` methods
class RestfulAction
super Action

redef fun answer(request, truncated_uri) do return new HttpResponse(400)

# Service to deserialize arguments from JSON
#
# Accepts `nullable String` for convenience, but returns `null` when `val == null`.
#
# This method is called by the code generated by `nitrestful`.
# It can be specialized to customize its behavior.
protected fun deserialize_arg(val: nullable String): nullable Object
do
if val == null then return null

var deserializer = new JsonDeserializer(val)
if deserializer.errors.not_empty then
print_error deserializer.errors.join("\n")
return null
end

return deserializer.deserialize
end
end
19 changes: 19 additions & 0 deletions share/man/nitrestful.md
@@ -0,0 +1,19 @@
# NAME

nitrestful - generates boilerplate code to relay RESTful request to static Nit methods

# SYNOPSIS

nitrestful [*options*]... FILE

# OPTIONS

### `-o`, `--output`
Output file (can also be 'stdout').

### `--dir`
Output directory.

# SEE ALSO

The Nit language documentation and the source code of its tools and libraries may be downloaded from <http://nitlanguage.org>
2 changes: 1 addition & 1 deletion src/Makefile
Expand Up @@ -16,7 +16,7 @@

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

Expand Down
1 change: 0 additions & 1 deletion src/frontend/serialization_phase.nit
Expand Up @@ -56,7 +56,6 @@ redef class ADefinition
end
end

# TODO add annotations on attributes (volatile, sensitive or do_not_serialize?)
private class SerializationPhasePreModel
super Phase

Expand Down

0 comments on commit 9214a0e

Please sign in to comment.