7b8a4078bd92a8261b992a5974eaeb39fcbfee78
[nit.git] / lib / nitcorn / examples / src / restful_annot.nit
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 # Example for the `restful` annotation documented at `lib/nitcorn/restful.nit`
16 module restful_annot
17
18 import nitcorn::restful
19 import nitcorn::pthreads
20
21 # An action root to its `restful` methods
22 class MyAction
23 super RestfulAction
24
25 # Method answering requests such as `foo?s=some_string&i=42&b=true`
26 #
27 # By default, the name of the HTTP resource is the name of the method.
28 # Responds to all HTTP methods, including GET, POST, PUT and DELETE.
29 #
30 # All arguments are deserialized from a JSON format,
31 # except for strings that are used as is.
32 fun foo(s: String, i: Int, b: Bool): HttpResponse
33 is restful do
34 var resp = new HttpResponse(200)
35 resp.body = "foo {s} {i} {b}"
36 return resp
37 end
38
39 # Method answering requests such as `api_name?s=these_arguments_are_optional`
40 #
41 # This method is available as both `api_name` and `alt_name` in HTTP.
42 # Responds only to the GET and PUT HTTP method.
43 fun bar(s: nullable String, i: nullable Int, b: nullable Bool): HttpResponse
44 is restful("api_name", "alt_name", GET, PUT) do
45
46 var resp = new HttpResponse(200)
47 resp.body = "bar {s or else "null"} {i or else "null"} {b or else "null"}"
48 return resp
49 end
50
51 # Asynchronous method answering requests such as `async_service?str=some_string`
52 #
53 # This method is executed by the `thread_pool` attribute of this class.
54 # Be careful when using the `async` argument to follow all the good
55 # concurrent programming pratices.
56 fun async_service(str: String): HttpResponse
57 is restful(async) do
58
59 # "Work" for 2 seconds
60 2.0.sleep
61
62 # Answer
63 var resp = new HttpResponse(200)
64 resp.body = "async_service {str}"
65 return resp
66 end
67
68 # Method with two complex parameters answering requests such as
69 # `complex_args?array=["a","b"]&data={"str":"asdf","more":{"str":"ASDF"}}`
70 #
71 # Collections and other classes can also be used as parameters,
72 # they will be deserialized from JSON format.
73 # By default, the JSON objects will be parsed as the type of the parameter.
74 # In the example above, the argument passed as `data` is deserialized as a `MyData`.
75 # However, you can use metadata in the JSON object to deserialize it
76 # as a subclass of `MyData`, as in this request where `data` is a `MyOtherData`:
77 #
78 # `complex_args?array=["a","b"]&data={"__class":"MyOtherData","str":"asdf","i":1234}`
79 #
80 # See the `json` package documentation for more information on JSON
81 # deserialization and the metadata values.
82 fun complex_args(array: Array[String], data: MyData): HttpResponse
83 is restful do
84 var resp = new HttpResponse(200)
85 resp.body = "complex_args {array} {data}"
86 return resp
87 end
88
89 # Catch all other request
90 redef fun answer(request, turi)
91 do
92 var resp = new HttpResponse(404)
93 resp.body = "Fallback answer"
94 return resp
95 end
96 end
97
98 # Simple data structure for `MyAction::complex_args`
99 class MyData
100 serialize
101
102 # Some string
103 var str: String
104
105 # Some more data
106 var more: nullable MyData
107
108 redef fun to_s do return "<MyData str:{str} more:{more or else "null"}>"
109 end
110
111 # Another data structure, subclass to `MyData`
112 class MyOtherData
113 super MyData
114 serialize
115
116 # An integer
117 var i: Int
118
119 redef fun to_s do return "<MyOtherData str:{str} more:{more or else "null"} i:{i}>"
120 end
121
122 var vh = new VirtualHost("localhost:8080")
123
124 # Set `rest_path` as the root for an instance of `MyAction`, so:
125 # * `MyClass::foo` is available as `localhost:8080/rest_path/foo?s=s&i=12&b=true`,
126 # * `MyClass::bar` is available as both `localhost:8080/rest_path/api_name?s=s`
127 # and `localhost:8080/rest_path/alt_name?...`.
128 # * `MyClass::async_service` is available as `localhost:8080/rest_path/async_service?str=str`
129 # * `MyClass::complex_args` is available as
130 # `localhost:8080/rest_path/complex_args?array=["a","b"]&data={"str":"asdf"}`
131 vh.routes.add new Route("rest_path", new MyAction)
132
133 var factory = new HttpFactory.and_libevent
134 factory.config.virtual_hosts.add vh
135 factory.run