Added a few example based on the obsolete libraries (v0).
Updated the tutorial example. Added WSF_MESSAGE_EXECUTION.
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
note
|
||||
description: "Summary description for {DATABASE_API}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
DATABASE_API
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
create orders.make (10)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
orders: HASH_TABLE [ORDER, STRING]
|
||||
|
||||
;note
|
||||
copyright: "2011-2012, Javier Velilla and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -0,0 +1,19 @@
|
||||
note
|
||||
description: "Summary description for {SHARED_DATABASE_API}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
SHARED_DATABASE_API
|
||||
|
||||
feature -- Access
|
||||
|
||||
db_access: DATABASE_API
|
||||
once
|
||||
create Result.make
|
||||
end
|
||||
note
|
||||
copyright: "2011-2012, Javier Velilla and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
90
examples/obsolete/v0/restbucksCRUD/src/domain/item.e
Normal file
90
examples/obsolete/v0/restbucksCRUD/src/domain/item.e
Normal file
@@ -0,0 +1,90 @@
|
||||
note
|
||||
description: "Summary description for {ITEM}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
ITEM
|
||||
inherit
|
||||
ITEM_CONSTANTS
|
||||
create
|
||||
make
|
||||
feature -- Initialization
|
||||
make ( a_name : STRING_32 ; a_size:STRING_32; a_option: STRING_32; a_quantity:INTEGER_8)
|
||||
do
|
||||
set_name (a_name)
|
||||
set_size (a_size)
|
||||
set_option (a_option)
|
||||
set_quantity (a_quantity)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
name : STRING
|
||||
-- product name type of Coffee(Late, Cappuccino, Expresso)
|
||||
|
||||
option : STRING
|
||||
-- customization option Milk (skim, semi, whole)
|
||||
|
||||
size : STRING
|
||||
-- small, mediumm large
|
||||
|
||||
quantity :INTEGER
|
||||
|
||||
|
||||
|
||||
|
||||
feature -- Element Change
|
||||
set_name (a_name: STRING)
|
||||
require
|
||||
valid_name: is_valid_coffee_type (a_name)
|
||||
do
|
||||
name := a_name
|
||||
ensure
|
||||
name_assigned : name.same_string(a_name)
|
||||
end
|
||||
|
||||
set_size (a_size: STRING)
|
||||
require
|
||||
valid_size : is_valid_size_option (a_size)
|
||||
do
|
||||
size := a_size
|
||||
ensure
|
||||
size_assigned : size.same_string(a_size)
|
||||
end
|
||||
|
||||
set_option (an_option: STRING)
|
||||
require
|
||||
valid_option : is_valid_milk_type (an_option)
|
||||
do
|
||||
option := an_option
|
||||
ensure
|
||||
option_assigned : option.same_string (an_option)
|
||||
end
|
||||
|
||||
set_quantity (a_quantity: INTEGER)
|
||||
require
|
||||
valid_quantity : a_quantity > 0
|
||||
do
|
||||
quantity := a_quantity
|
||||
ensure
|
||||
quantity_assigned : quantity = a_quantity
|
||||
end
|
||||
|
||||
feature -- Report
|
||||
hash_code: INTEGER
|
||||
--Hash code value
|
||||
do
|
||||
Result := option.hash_code + name.hash_code + size.hash_code + quantity.hash_code
|
||||
end
|
||||
|
||||
|
||||
invariant
|
||||
valid_size : is_valid_size_option (size)
|
||||
valid_coffe : is_valid_coffee_type (name)
|
||||
valid_customization : is_valid_milk_type (option)
|
||||
valid_quantity : quantity > 0
|
||||
note
|
||||
copyright: "2011-2012, Javier Velilla and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -0,0 +1,54 @@
|
||||
note
|
||||
description: "Summary description for {ITEM_CONSTANTS}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
ITEM_CONSTANTS
|
||||
feature -- Access
|
||||
is_valid_coffee_type (a_type: STRING) : BOOLEAN
|
||||
--is `a_type' a valid coffee type
|
||||
do
|
||||
a_type.to_lower
|
||||
coffe_types.compare_objects
|
||||
Result := coffe_types.has (a_type)
|
||||
end
|
||||
|
||||
Coffe_types : ARRAY[STRING]
|
||||
-- List of valid Coffee types
|
||||
once
|
||||
Result := <<"late","cappuccino", "expresso">>
|
||||
end
|
||||
|
||||
is_valid_milk_type (a_type: STRING) : BOOLEAN
|
||||
--is `a_type' a valid milk type
|
||||
do
|
||||
a_type.to_lower
|
||||
milk_types.compare_objects
|
||||
Result := milk_types.has (a_type)
|
||||
end
|
||||
|
||||
Milk_types : ARRAY[STRING]
|
||||
-- List of valid Milk types
|
||||
once
|
||||
Result := <<"skim","semi", "whole">>
|
||||
end
|
||||
|
||||
is_valid_size_option (an_option: STRING) : BOOLEAN
|
||||
--is `an_option' a valid size option
|
||||
do
|
||||
an_option.to_lower
|
||||
size_options.compare_objects
|
||||
Result := size_options.has (an_option)
|
||||
end
|
||||
|
||||
Size_options : ARRAY[STRING]
|
||||
-- List of valid Size_options
|
||||
once
|
||||
Result := <<"small","mediumn", "large">>
|
||||
end
|
||||
note
|
||||
copyright: "2011-2012, Javier Velilla and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -0,0 +1,176 @@
|
||||
note
|
||||
description: "Summary description for {JSON_ORDER_CONVERTER}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
JSON_ORDER_CONVERTER
|
||||
inherit
|
||||
JSON_CONVERTER
|
||||
create
|
||||
make
|
||||
feature -- Initialization
|
||||
make
|
||||
do
|
||||
create object.make ("","","")
|
||||
end
|
||||
feature -- Access
|
||||
object : ORDER
|
||||
|
||||
|
||||
value : detachable JSON_OBJECT
|
||||
feature -- Conversion
|
||||
|
||||
from_json (j: attached like value): detachable like object
|
||||
-- Convert from JSON value. Returns Void if unable to convert
|
||||
local
|
||||
s_id, s_location, s_status: detachable STRING_32
|
||||
q: INTEGER_8
|
||||
o: ORDER
|
||||
i : ITEM
|
||||
l_array : detachable ARRAYED_LIST [JSON_VALUE]
|
||||
is_valid_from_json : BOOLEAN
|
||||
do
|
||||
is_valid_from_json := True
|
||||
|
||||
if attached {STRING_32} json.object (j.item (id_key), Void) as l_id then
|
||||
s_id := s_id
|
||||
end
|
||||
if attached {STRING_32} json.object (j.item (location_key), Void) as l_location then
|
||||
s_location := l_location
|
||||
end
|
||||
if attached {STRING_32} json.object (j.item (status_key), Void) as l_status then
|
||||
s_status := l_status
|
||||
end
|
||||
|
||||
create o.make ("", s_location, s_status)
|
||||
|
||||
if attached {JSON_ARRAY} j.item (items_key) as l_val then
|
||||
l_array := l_val.array_representation
|
||||
from
|
||||
l_array.start
|
||||
until
|
||||
l_array.after
|
||||
loop
|
||||
if attached {JSON_OBJECT} l_array.item_for_iteration as jv then
|
||||
if attached {INTEGER_8} json.object (jv.item (quantity_key), Void) as l_integer then
|
||||
q := l_integer
|
||||
else
|
||||
q := 0
|
||||
end
|
||||
if
|
||||
attached {STRING_32} json.object (jv.item (name_key), Void) as s_name and then
|
||||
attached {STRING_32} json.object (jv.item (size_key), Void) as s_key and then
|
||||
attached {STRING_32} json.object (jv.item (option_key), Void) as s_option
|
||||
then
|
||||
if is_valid_item_customization (s_name, s_key, s_option,q) then
|
||||
create i.make (s_name, s_key, s_option, q)
|
||||
o.add_item (i)
|
||||
else
|
||||
is_valid_from_json := False
|
||||
end
|
||||
else
|
||||
is_valid_from_json := False
|
||||
end
|
||||
end
|
||||
|
||||
l_array.forth
|
||||
end
|
||||
end
|
||||
if not is_valid_from_json or o.items.is_empty then
|
||||
Result := Void
|
||||
else
|
||||
Result := o
|
||||
end
|
||||
end
|
||||
|
||||
to_json (o: like object): like value
|
||||
-- Convert to JSON value
|
||||
local
|
||||
ja : JSON_ARRAY
|
||||
i : ITEM
|
||||
jv: JSON_OBJECT
|
||||
do
|
||||
create Result.make
|
||||
-- Result.put (json.value (o.id), id_key)
|
||||
Result.put (json.value (o.location), location_key)
|
||||
Result.put (json.value (o.status), status_key)
|
||||
from
|
||||
create ja.make_empty
|
||||
o.items.start
|
||||
until
|
||||
o.items.after
|
||||
loop
|
||||
i := o.items.item_for_iteration
|
||||
create jv.make
|
||||
jv.put (json.value (i.name), name_key)
|
||||
jv.put (json.value (i.size),size_key)
|
||||
jv.put (json.value (i.quantity), quantity_key)
|
||||
jv.put (json.value (i.option), option_key)
|
||||
ja.add (jv)
|
||||
o.items.forth
|
||||
end
|
||||
Result.put (ja, items_key)
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
id_key: JSON_STRING
|
||||
once
|
||||
create Result.make_from_string ("id")
|
||||
end
|
||||
|
||||
location_key: JSON_STRING
|
||||
once
|
||||
create Result.make_from_string ("location")
|
||||
end
|
||||
|
||||
status_key: JSON_STRING
|
||||
once
|
||||
create Result.make_from_string ("status")
|
||||
end
|
||||
|
||||
items_key : JSON_STRING
|
||||
once
|
||||
create Result.make_from_string ("items")
|
||||
end
|
||||
|
||||
|
||||
name_key : JSON_STRING
|
||||
|
||||
once
|
||||
create Result.make_from_string ("name")
|
||||
end
|
||||
|
||||
size_key : JSON_STRING
|
||||
|
||||
once
|
||||
create Result.make_from_string ("size")
|
||||
end
|
||||
|
||||
quantity_key : JSON_STRING
|
||||
|
||||
once
|
||||
create Result.make_from_string ("quantity")
|
||||
end
|
||||
|
||||
|
||||
option_key : JSON_STRING
|
||||
|
||||
once
|
||||
create Result.make_from_string ("option")
|
||||
end
|
||||
feature -- Validation
|
||||
|
||||
is_valid_item_customization ( name : STRING_32; size: STRING_32; option : STRING_32; quantity : INTEGER_8 ) : BOOLEAN
|
||||
local
|
||||
ic : ITEM_CONSTANTS
|
||||
do
|
||||
create ic
|
||||
Result := ic.is_valid_coffee_type (name) and ic.is_valid_milk_type (option) and ic.is_valid_size_option (size) and quantity > 0
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2015, Javier Velilla and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
114
examples/obsolete/v0/restbucksCRUD/src/domain/order.e
Normal file
114
examples/obsolete/v0/restbucksCRUD/src/domain/order.e
Normal file
@@ -0,0 +1,114 @@
|
||||
note
|
||||
description: "Summary description for {ORDER}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
ORDER
|
||||
create
|
||||
make
|
||||
feature -- Initialization
|
||||
|
||||
make ( an_id : detachable STRING_32; a_location: detachable STRING_32; a_status: detachable STRING_32)
|
||||
do
|
||||
create {ARRAYED_LIST [ITEM]} items.make (10)
|
||||
if an_id /= Void then
|
||||
set_id (an_id)
|
||||
else
|
||||
set_id ("")
|
||||
end
|
||||
if a_location /= Void then
|
||||
set_location (a_location)
|
||||
else
|
||||
set_location ("")
|
||||
end
|
||||
if a_status /= Void then
|
||||
set_status (a_status)
|
||||
else
|
||||
set_status ("")
|
||||
end
|
||||
revision := 0
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
id : STRING_32
|
||||
location : STRING_32
|
||||
items: LIST[ITEM]
|
||||
status : STRING_32
|
||||
revision : INTEGER
|
||||
|
||||
feature -- element change
|
||||
|
||||
set_id (an_id : STRING_32)
|
||||
do
|
||||
id := an_id
|
||||
ensure
|
||||
id_assigned : id.same_string (an_id)
|
||||
end
|
||||
|
||||
set_location (a_location : STRING_32)
|
||||
do
|
||||
location := a_location
|
||||
ensure
|
||||
location_assigned : location.same_string (a_location)
|
||||
end
|
||||
|
||||
set_status (a_status : STRING_32)
|
||||
do
|
||||
status := a_status
|
||||
ensure
|
||||
status_asigned : status.same_string (a_status)
|
||||
end
|
||||
|
||||
add_item (a_item : ITEM)
|
||||
require
|
||||
valid_item: a_item /= Void
|
||||
do
|
||||
items.force (a_item)
|
||||
ensure
|
||||
has_item : items.has (a_item)
|
||||
end
|
||||
|
||||
add_revision
|
||||
do
|
||||
revision := revision + 1
|
||||
ensure
|
||||
revision_incremented : old revision + 1 = revision
|
||||
end
|
||||
|
||||
feature -- Etag
|
||||
|
||||
etag : STRING_32
|
||||
-- Etag generation for Order objects
|
||||
do
|
||||
Result := hash_code.out + revision.out
|
||||
end
|
||||
|
||||
|
||||
feature -- Output
|
||||
|
||||
feature -- Report
|
||||
|
||||
hash_code: INTEGER_32
|
||||
-- Hash code value
|
||||
do
|
||||
from
|
||||
items.start
|
||||
Result := items.item.hash_code
|
||||
until
|
||||
items.off
|
||||
loop
|
||||
Result:= ((Result \\ 8388593) |<< 8) + items.item.hash_code
|
||||
items.forth
|
||||
end
|
||||
if items.count > 1 then
|
||||
Result := Result \\ items.count
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2012, Javier Velilla and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -0,0 +1,56 @@
|
||||
note
|
||||
description: "Summary description for {ORDER_TRANSITIONS}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
ORDER_VALIDATION
|
||||
feature -- Access
|
||||
|
||||
is_valid_status_state (a_status: STRING) : BOOLEAN
|
||||
--is `a_status' a valid coffee order state
|
||||
do
|
||||
a_status.to_lower
|
||||
Order_states.compare_objects
|
||||
Result := Order_states.has (a_status)
|
||||
end
|
||||
|
||||
Order_states : ARRAY[STRING]
|
||||
-- List of valid status states
|
||||
once
|
||||
Result := <<"submitted","pay","payed", "cancel","canceled","prepare","prepared","deliver","completed">>
|
||||
end
|
||||
|
||||
|
||||
is_valid_transition (order:ORDER a_status : STRING) :BOOLEAN
|
||||
-- Given the current order state, determine if the transition is valid
|
||||
do
|
||||
a_status.to_lower
|
||||
if order.status.same_string ("submitted") then
|
||||
Result := a_status.same_string ("pay") or a_status.same_string ("cancel") or order.status.same_string (a_status)
|
||||
elseif order.status.same_string ("pay") then
|
||||
Result := a_status.same_string ("payed") or order.status.same_string (a_status)
|
||||
elseif order.status.same_string ("cancel") then
|
||||
Result := a_status.same_string ("canceled") or order.status.same_string (a_status)
|
||||
elseif order.status.same_string ("payed") then
|
||||
Result := a_status.same_string ("prepared") or order.status.same_string (a_status)
|
||||
elseif order.status.same_string ("prepared") then
|
||||
Result := a_status.same_string ("deliver") or order.status.same_string (a_status)
|
||||
elseif order.status.same_string ("deliver") then
|
||||
Result := a_status.same_string ("completed") or order.status.same_string (a_status)
|
||||
end
|
||||
end
|
||||
|
||||
is_state_valid_to_update ( a_status : STRING) : BOOLEAN
|
||||
-- Given the current state `a_status' of an order, is possible to update the order?
|
||||
do
|
||||
if a_status.same_string ("submitted") or else a_status.same_string ("pay") or else a_status.same_string ("payed") then
|
||||
Result := true
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2012, Javier Velilla and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -0,0 +1,19 @@
|
||||
note
|
||||
description: "Summary description for {SHARED_ORDER_VALIDATION}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
SHARED_ORDER_VALIDATION
|
||||
|
||||
feature
|
||||
order_validation : ORDER_VALIDATION
|
||||
once
|
||||
create Result
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2012, Javier Velilla and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
@@ -0,0 +1,574 @@
|
||||
note
|
||||
description: "{ORDER_HANDLER} handle the resources that we want to expose"
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class ORDER_HANDLER
|
||||
|
||||
inherit
|
||||
|
||||
WSF_SKELETON_HANDLER
|
||||
|
||||
SHARED_DATABASE_API
|
||||
|
||||
SHARED_EJSON
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
SHARED_ORDER_VALIDATION
|
||||
|
||||
WSF_RESOURCE_HANDLER_HELPER
|
||||
rename
|
||||
execute_options as helper_execute_options,
|
||||
handle_internal_server_error as helper_handle_internal_server_error
|
||||
end
|
||||
|
||||
create
|
||||
|
||||
make_with_router
|
||||
|
||||
|
||||
feature -- Execution variables
|
||||
|
||||
Order_execution_variable: STRING = "ORDER"
|
||||
-- Execution variable used by application
|
||||
|
||||
Generated_content_execution_variable: STRING = "GENERATED_CONTENT"
|
||||
-- Execution variable used by application
|
||||
|
||||
Extracted_order_execution_variable: STRING = "EXTRACTED_ORDER"
|
||||
-- Execution variable used by application
|
||||
|
||||
feature -- Documentation
|
||||
|
||||
description: READABLE_STRING_GENERAL
|
||||
-- General description for self-generated documentation;
|
||||
-- The specific URI templates supported will be described automatically
|
||||
do
|
||||
Result := "Create, Read, Update or Delete an ORDER."
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
is_chunking (req: WSF_REQUEST): BOOLEAN
|
||||
-- Will the response to `req' using chunked transfer encoding?
|
||||
do
|
||||
-- No.
|
||||
end
|
||||
|
||||
includes_response_entity (req: WSF_REQUEST): BOOLEAN
|
||||
-- Does the response to `req' include an entity?
|
||||
-- Method will be DELETE, POST, PUT or an extension method.
|
||||
do
|
||||
Result := False
|
||||
-- At present, there is no support for this except for DELETE.
|
||||
end
|
||||
|
||||
conneg (req: WSF_REQUEST): SERVER_CONTENT_NEGOTIATION
|
||||
-- Content negotiatior for all requests
|
||||
once
|
||||
create Result.make ({HTTP_MIME_TYPES}.application_json, "en", "UTF-8", "identity")
|
||||
end
|
||||
|
||||
mime_types_supported (req: WSF_REQUEST): LIST [STRING]
|
||||
-- All values for Accept header that `Current' can serve
|
||||
do
|
||||
create {ARRAYED_LIST [STRING]} Result.make_from_array (<<{HTTP_MIME_TYPES}.application_json>>)
|
||||
Result.compare_objects
|
||||
end
|
||||
|
||||
languages_supported (req: WSF_REQUEST): LIST [STRING]
|
||||
-- All values for Accept-Language header that `Current' can serve
|
||||
do
|
||||
create {ARRAYED_LIST [STRING]} Result.make_from_array (<<"en">>)
|
||||
Result.compare_objects
|
||||
end
|
||||
|
||||
charsets_supported (req: WSF_REQUEST): LIST [STRING]
|
||||
-- All values for Accept-Charset header that `Current' can serve
|
||||
do
|
||||
create {ARRAYED_LIST [STRING]} Result.make_from_array (<<"UTF-8">>)
|
||||
Result.compare_objects
|
||||
end
|
||||
|
||||
encodings_supported (req: WSF_REQUEST): LIST [STRING]
|
||||
-- All values for Accept-Encoding header that `Current' can serve
|
||||
do
|
||||
create {ARRAYED_LIST [STRING]} Result.make_from_array (<<"identity">>)
|
||||
Result.compare_objects
|
||||
end
|
||||
|
||||
max_age (req: WSF_REQUEST): NATURAL
|
||||
-- Maximum age in seconds before response to `req` is considered stale;
|
||||
-- This is used to generate a Cache-Control: max-age header.
|
||||
-- Return 0 to indicate already expired.
|
||||
-- Return Never_expires to indicate never expires.
|
||||
do
|
||||
-- All our responses are considered stale.
|
||||
end
|
||||
|
||||
is_freely_cacheable (req: WSF_REQUEST): BOOLEAN
|
||||
-- Should the response to `req' be freely cachable in shared caches?
|
||||
-- If `True', then a Cache-Control: public header will be generated.
|
||||
do
|
||||
-- definitely not!
|
||||
end
|
||||
|
||||
private_headers (req: WSF_REQUEST): detachable LIST [READABLE_STRING_8]
|
||||
-- Header names intended for a single user.
|
||||
-- If non-Void, then a Cache-Control: private header will be generated.
|
||||
-- Returning an empty list prevents the entire response from being served from a shared cache.
|
||||
do
|
||||
create {ARRAYED_LIST [READABLE_STRING_8]} Result.make (0)
|
||||
end
|
||||
|
||||
non_cacheable_headers (req: WSF_REQUEST): detachable LIST [READABLE_STRING_8]
|
||||
-- Header names that will not be sent from a cache without revalidation;
|
||||
-- If non-Void, then a Cache-Control: no-cache header will be generated.
|
||||
-- Returning an empty list prevents the response being served from a cache
|
||||
-- without revalidation.
|
||||
do
|
||||
create {ARRAYED_LIST [READABLE_STRING_8]} Result.make (0)
|
||||
end
|
||||
|
||||
is_sensitive (req: WSF_REQUEST): BOOLEAN
|
||||
-- Is the response to `req' of a sensitive nature?
|
||||
-- If `True' then a Cache-Control: no-store header will be generated.
|
||||
do
|
||||
Result := True
|
||||
-- since it's commercial data.
|
||||
end
|
||||
|
||||
allowed_cross_origins (req: WSF_REQUEST): detachable STRING
|
||||
-- Value for Access-Control-Allow-Origin header;
|
||||
-- If supplied, should be a single URI, or the values "*" or "null".
|
||||
-- This is currently supported only for GET requests, and POSTs that functions as GET.
|
||||
do
|
||||
if req.is_get_head_request_method then
|
||||
Result := "*"
|
||||
end
|
||||
end
|
||||
|
||||
matching_etag (req: WSF_REQUEST; a_etag: READABLE_STRING_32; a_strong: BOOLEAN): BOOLEAN
|
||||
-- Is `a_etag' a match for resource requested in `req'?
|
||||
-- If `a_strong' then the strong comparison function must be used.
|
||||
local
|
||||
l_id: STRING
|
||||
l_etag_util: ETAG_UTILS
|
||||
do
|
||||
l_id := order_id_from_request (req)
|
||||
if db_access.orders.has_key (l_id) then
|
||||
check attached db_access.orders.item (l_id) as l_order then
|
||||
-- postcondition of `has_key'
|
||||
create l_etag_util
|
||||
Result := a_etag.same_string (l_etag_util.md5_digest (l_order.out).as_string_32)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
etag (req: WSF_REQUEST): detachable READABLE_STRING_8
|
||||
-- Optional Etag for `req' in the requested variant
|
||||
local
|
||||
l_etag_utils: ETAG_UTILS
|
||||
do
|
||||
create l_etag_utils
|
||||
if attached {ORDER} req.execution_variable (Order_execution_variable) as l_order then
|
||||
Result := l_etag_utils.md5_digest (l_order.out)
|
||||
end
|
||||
end
|
||||
|
||||
last_modified (req: WSF_REQUEST): detachable DATE_TIME
|
||||
-- When representation of resource selected in `req' was last modified;
|
||||
-- SHOULD be set whenever it can reasonably be determined.
|
||||
do
|
||||
end
|
||||
|
||||
modified_since (req: WSF_REQUEST; a_date_time: DATE_TIME): BOOLEAN
|
||||
-- Has resource requested in `req' been modified since `a_date_time' (UTC)?
|
||||
do
|
||||
-- We don't track this information. It is safe to always say yes.
|
||||
Result := True
|
||||
end
|
||||
|
||||
feature -- Measurement
|
||||
|
||||
content_length (req: WSF_REQUEST): NATURAL
|
||||
-- Length of entity-body of the response to `req'
|
||||
do
|
||||
check attached {READABLE_STRING_8} req.execution_variable (Generated_content_execution_variable) as l_response then
|
||||
-- postcondition generated_content_set_for_get_head of `ensure_content_available'
|
||||
-- We only call this for GET/HEAD in this example.
|
||||
Result := l_response.count.as_natural_32
|
||||
end
|
||||
end
|
||||
|
||||
allow_post_to_missing_resource (req: WSF_REQUEST): BOOLEAN
|
||||
-- The resource named in `req' does not exist, and this is a POST. Do we allow it?
|
||||
do
|
||||
-- No.
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
finished (req: WSF_REQUEST): BOOLEAN
|
||||
-- Has the last chunk been generated for `req'?
|
||||
do
|
||||
-- precondition is never met
|
||||
end
|
||||
|
||||
feature -- Execution
|
||||
|
||||
check_resource_exists (req: WSF_REQUEST; a_helper: WSF_METHOD_HELPER)
|
||||
-- Call `a_helper.set_resource_exists' to indicate that `req.path_translated'
|
||||
-- is the name of an existing resource.
|
||||
-- We also put the order into `req.execution_variable (Order_execution_variable)' for GET or HEAD responses.
|
||||
local
|
||||
l_id: STRING
|
||||
do
|
||||
if req.is_post_request_method then
|
||||
a_helper.set_resource_exists
|
||||
-- because only /order is defined to this handler for POST
|
||||
else
|
||||
-- the request is of the form /order/{orderid}
|
||||
l_id := order_id_from_request (req)
|
||||
if db_access.orders.has_key (l_id) then
|
||||
a_helper.set_resource_exists
|
||||
if req.is_get_head_request_method then
|
||||
check attached db_access.orders.item (l_id) as l_order then
|
||||
-- postcondition `item_if_found' of `has_key'
|
||||
req.set_execution_variable (Order_execution_variable, l_order)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
ensure then
|
||||
order_saved_only_for_get_head: attached {ORDER} req.execution_variable (Order_execution_variable) implies req.is_get_head_request_method
|
||||
end
|
||||
|
||||
feature -- GET/HEAD content
|
||||
|
||||
ensure_content_available (req: WSF_REQUEST)
|
||||
-- Commence generation of response text (entity-body).
|
||||
-- If not chunked, then this will create the entire entity-body so as to be available
|
||||
-- for a subsequent call to `content'.
|
||||
-- If chunked, only the first chunk will be made available to `next_chunk'. If chunk extensions
|
||||
-- are used, then this will also generate the chunk extension for the first chunk.
|
||||
-- We save the text in `req.execution_variable (Generated_content_execution_variable)'
|
||||
-- We ignore the results of content negotiation, as there is only one possible combination.
|
||||
do
|
||||
check attached {ORDER} req.execution_variable (Order_execution_variable) as l_order then
|
||||
-- precondition get_or_head and postcondition order_saved_only_for_get_head of `check_resource_exists' and
|
||||
if attached {JSON_VALUE} json.value (l_order) as jv then
|
||||
req.set_execution_variable (Generated_content_execution_variable, jv.representation)
|
||||
else
|
||||
req.set_execution_variable (Generated_content_execution_variable, "")
|
||||
end
|
||||
end
|
||||
ensure then
|
||||
generated_content_set_for_get_head: req.is_get_head_request_method implies
|
||||
attached {READABLE_STRING_8} req.execution_variable (Generated_content_execution_variable)
|
||||
end
|
||||
|
||||
content (req: WSF_REQUEST): READABLE_STRING_8
|
||||
-- Non-chunked entity body in response to `req';
|
||||
-- We only call this for GET/HEAD in this example.
|
||||
do
|
||||
check attached {READABLE_STRING_8} req.execution_variable (Generated_content_execution_variable) as l_response then
|
||||
-- postcondition generated_content_set_for_get_head of `ensure_content_available'
|
||||
Result := l_response
|
||||
end
|
||||
end
|
||||
|
||||
next_chunk (req: WSF_REQUEST): TUPLE [a_chunk: READABLE_STRING_8; a_extension: detachable READABLE_STRING_8]
|
||||
-- Next chunk of entity body in response to `req';
|
||||
-- The second field of the result is an optional chunk extension.
|
||||
do
|
||||
-- precondition `is_chunking' is never met, but we need a dummy `Result'
|
||||
-- to satisfy the compiler in void-safe mode
|
||||
Result := ["", Void]
|
||||
end
|
||||
|
||||
generate_next_chunk (req: WSF_REQUEST)
|
||||
-- Prepare next chunk (including optional chunk extension) of entity body in response to `req'.
|
||||
-- This is not called for the first chunk.
|
||||
do
|
||||
-- precondition `is_chunking' is never met
|
||||
end
|
||||
|
||||
feature -- DELETE
|
||||
|
||||
delete (req: WSF_REQUEST)
|
||||
-- Delete resource named in `req' or set an error on `req.error_handler'.
|
||||
local
|
||||
l_id: STRING
|
||||
do
|
||||
l_id := order_id_from_request (req)
|
||||
if db_access.orders.has_key (l_id) then
|
||||
if is_valid_to_delete (l_id) then
|
||||
delete_order (l_id)
|
||||
else
|
||||
req.error_handler.add_custom_error ({HTTP_STATUS_CODE}.method_not_allowed, "DELETE not valid",
|
||||
"There is conflict while trying to delete the order, the order could not be deleted in the current state")
|
||||
end
|
||||
else
|
||||
req.error_handler.add_custom_error ({HTTP_STATUS_CODE}.not_found, "DELETE not valid",
|
||||
"There is no such order to delete")
|
||||
end
|
||||
end
|
||||
|
||||
delete_queued (req: WSF_REQUEST): BOOLEAN
|
||||
-- Has resource named by `req' been queued for deletion?
|
||||
do
|
||||
-- No
|
||||
end
|
||||
|
||||
|
||||
feature -- PUT/POST
|
||||
|
||||
is_entity_too_large (req: WSF_REQUEST): BOOLEAN
|
||||
-- Is the entity stored in `req.execution_variable (Request_entity_execution_variable)' too large for the application?
|
||||
do
|
||||
-- No. We don't care for this example.
|
||||
end
|
||||
|
||||
check_content_headers (req: WSF_REQUEST)
|
||||
-- Check we can support all content headers on request entity.
|
||||
-- Set `req.execution_variable (Content_check_code_execution_variable)' to {NATURAL} zero if OK, or 415 or 501 if not.
|
||||
do
|
||||
-- We don't bother for this example. Note that this is equivalent to setting zero.
|
||||
end
|
||||
|
||||
create_resource (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Create new resource in response to a PUT request when `check_resource_exists' returns `False'.
|
||||
-- Implementor must set error code of 200 OK or 500 Server Error.
|
||||
do
|
||||
-- We don't support creating a new resource with PUT. But this can't happen
|
||||
-- with our router mappings, so we don't bother to set a 500 response.
|
||||
end
|
||||
|
||||
append_resource (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Create new resource in response to a POST request.
|
||||
-- Implementor must set error code of 200 OK or 204 No Content or 303 See Other or 500 Server Error.
|
||||
do
|
||||
if attached {ORDER} req.execution_variable (Extracted_order_execution_variable) as l_order then
|
||||
save_order (l_order)
|
||||
compute_response_post (req, res, l_order)
|
||||
else
|
||||
handle_bad_request_response ("Not a valid order", req, res)
|
||||
end
|
||||
end
|
||||
|
||||
check_conflict (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Check we can support all content headers on request entity.
|
||||
-- Set `req.execution_variable (Conflict_check_code_execution_variable)' to {NATURAL} zero if OK, or 409 if not.
|
||||
-- In the latter case, write the full error response to `res'.
|
||||
do
|
||||
if attached {ORDER} req.execution_variable (Extracted_order_execution_variable) as l_order then
|
||||
if not is_valid_to_update (l_order) then
|
||||
req.set_execution_variable (Conflict_check_code_execution_variable, {NATURAL} 409)
|
||||
handle_resource_conflict_response (l_order.out +"%N There is conflict while trying to update the order, the order could not be update in the current state", req, res)
|
||||
end
|
||||
else
|
||||
req.set_execution_variable (Conflict_check_code_execution_variable, {NATURAL} 409)
|
||||
--| This ought to be a 500, as if attached should probably be check attached. But as yet I lack a proof.
|
||||
handle_resource_conflict_response ("There is conflict while trying to update the order, the order could not be update in the current state", req, res)
|
||||
end
|
||||
end
|
||||
|
||||
check_request (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Check that the request entity is a valid request.
|
||||
-- The entity is available as `req.execution_variable (Conflict_check_code_execution_variable)'.
|
||||
-- Set `req.execution_variable (Request_check_code_execution_variable)' to {NATURAL} zero if OK, or 400 if not.
|
||||
-- In the latter case, write the full error response to `res'.
|
||||
local
|
||||
l_order: detachable ORDER
|
||||
l_id: STRING
|
||||
do
|
||||
if attached {READABLE_STRING_8} req.execution_variable (Request_entity_execution_variable) as l_request then
|
||||
l_order := extract_order_request (l_request)
|
||||
if req.is_put_request_method then
|
||||
l_id := order_id_from_request (req)
|
||||
if l_order /= Void and then db_access.orders.has_key (l_id) then
|
||||
l_order.set_id (l_id)
|
||||
req.set_execution_variable (Request_check_code_execution_variable, {NATURAL} 0)
|
||||
req.set_execution_variable (Extracted_order_execution_variable, l_order)
|
||||
else
|
||||
req.set_execution_variable (Request_check_code_execution_variable, {NATURAL} 400)
|
||||
handle_bad_request_response (l_request +"%N is not a valid ORDER, maybe the order does not exist in the system", req, res)
|
||||
end
|
||||
else
|
||||
req.set_execution_variable (Request_check_code_execution_variable, {NATURAL} 0)
|
||||
req.set_execution_variable (Extracted_order_execution_variable, l_order)
|
||||
end
|
||||
else
|
||||
req.set_execution_variable (Request_check_code_execution_variable, {NATURAL} 400)
|
||||
handle_bad_request_response ("Request is not a valid ORDER", req, res)
|
||||
end
|
||||
end
|
||||
|
||||
update_resource (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Perform the update requested in `req'.
|
||||
-- Write a response to `res' with a code of 204 or 500.
|
||||
do
|
||||
if attached {ORDER} req.execution_variable (Extracted_order_execution_variable) as l_order then
|
||||
update_order (l_order)
|
||||
compute_response_put (req, res, l_order)
|
||||
else
|
||||
handle_internal_server_error (res)
|
||||
end
|
||||
end
|
||||
|
||||
feature -- HTTP Methods
|
||||
|
||||
compute_response_put (req: WSF_REQUEST; res: WSF_RESPONSE; l_order : ORDER)
|
||||
local
|
||||
h: HTTP_HEADER
|
||||
joc : JSON_ORDER_CONVERTER
|
||||
etag_utils : ETAG_UTILS
|
||||
do
|
||||
create h.make
|
||||
create joc.make
|
||||
create etag_utils
|
||||
json.add_converter(joc)
|
||||
|
||||
create h.make
|
||||
h.put_content_type_application_json
|
||||
if attached req.request_time as time then
|
||||
h.add_header ("Date:" +time.formatted_out ("ddd,[0]dd mmm yyyy [0]hh:[0]mi:[0]ss.ff2") + " GMT")
|
||||
end
|
||||
h.add_header ("etag:" + etag_utils.md5_digest (l_order.out))
|
||||
if attached {JSON_VALUE} json.value (l_order) as jv then
|
||||
h.put_content_length (jv.representation.count)
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||||
res.put_header_text (h.string)
|
||||
res.put_string (jv.representation)
|
||||
end
|
||||
end
|
||||
|
||||
compute_response_post (req: WSF_REQUEST; res: WSF_RESPONSE; l_order : ORDER)
|
||||
local
|
||||
h: HTTP_HEADER
|
||||
l_msg : STRING
|
||||
l_location : STRING
|
||||
joc : JSON_ORDER_CONVERTER
|
||||
do
|
||||
create h.make
|
||||
|
||||
create joc.make
|
||||
json.add_converter(joc)
|
||||
|
||||
h.put_content_type_application_json
|
||||
if attached {JSON_VALUE} json.value (l_order) as jv then
|
||||
l_msg := jv.representation
|
||||
h.put_content_length (l_msg.count)
|
||||
if attached req.http_host as host then
|
||||
l_location := "http://" + host + req.request_uri + "/" + l_order.id
|
||||
h.put_location (l_location)
|
||||
end
|
||||
if attached req.request_time as time then
|
||||
h.put_utc_date (time)
|
||||
end
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.created)
|
||||
res.put_header_text (h.string)
|
||||
res.put_string (l_msg)
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- URI helper methods
|
||||
|
||||
order_id_from_request (req: WSF_REQUEST): STRING
|
||||
-- Value of "orderid" template URI variable in `req'
|
||||
require
|
||||
req_attached: req /= Void
|
||||
do
|
||||
if attached {WSF_VALUE} req.path_parameter ("orderid") as l_value then
|
||||
Result := l_value.as_string.value.as_string_8
|
||||
else
|
||||
Result := ""
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation Repository Layer
|
||||
|
||||
retrieve_order ( id : STRING) : detachable ORDER
|
||||
-- get the order by id if it exist, in other case, Void
|
||||
do
|
||||
Result := db_access.orders.item (id)
|
||||
end
|
||||
|
||||
save_order (an_order: ORDER)
|
||||
-- save the order to the repository
|
||||
local
|
||||
i : INTEGER
|
||||
do
|
||||
from
|
||||
i := 1
|
||||
until
|
||||
not db_access.orders.has_key ((db_access.orders.count + i).out)
|
||||
loop
|
||||
i := i + 1
|
||||
end
|
||||
an_order.set_id ((db_access.orders.count + i).out)
|
||||
an_order.set_status ("submitted")
|
||||
an_order.add_revision
|
||||
db_access.orders.force (an_order, an_order.id)
|
||||
end
|
||||
|
||||
|
||||
is_valid_to_delete ( an_id : STRING) : BOOLEAN
|
||||
-- Is the order identified by `an_id' in a state whre it can still be deleted?
|
||||
do
|
||||
if attached retrieve_order (an_id) as l_order then
|
||||
if order_validation.is_state_valid_to_update (l_order.status) then
|
||||
Result := True
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
is_valid_to_update (an_order: ORDER) : BOOLEAN
|
||||
-- Check if there is a conflict while trying to update the order
|
||||
do
|
||||
if attached retrieve_order (an_order.id) as l_order then
|
||||
if order_validation.is_state_valid_to_update (l_order.status) and then order_validation.is_valid_status_state (an_order.status) and then
|
||||
order_validation.is_valid_transition (l_order, an_order.status) then
|
||||
Result := True
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
update_order (an_order: ORDER)
|
||||
-- update the order to the repository
|
||||
do
|
||||
an_order.add_revision
|
||||
db_access.orders.force (an_order, an_order.id)
|
||||
end
|
||||
|
||||
delete_order (an_order: STRING)
|
||||
-- update the order to the repository
|
||||
do
|
||||
db_access.orders.remove (an_order)
|
||||
end
|
||||
|
||||
extract_order_request (l_post : STRING) : detachable ORDER
|
||||
-- extract an object Order from the request, or Void
|
||||
-- if the request is invalid
|
||||
local
|
||||
parser : JSON_PARSER
|
||||
joc : JSON_ORDER_CONVERTER
|
||||
do
|
||||
create joc.make
|
||||
json.add_converter(joc)
|
||||
create parser.make_with_string (l_post)
|
||||
parser.parse_content
|
||||
if parser.is_valid and then attached parser.parsed_json_value as jv then
|
||||
if attached {like extract_order_request} json.object (jv, "ORDER") as res then
|
||||
Result := res
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2015, Javier Velilla and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
403
examples/obsolete/v0/restbucksCRUD/src/resource/order_handler.e
Normal file
403
examples/obsolete/v0/restbucksCRUD/src/resource/order_handler.e
Normal file
@@ -0,0 +1,403 @@
|
||||
note
|
||||
description: "{ORDER_HANDLER} handle the resources that we want to expose"
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class ORDER_HANDLER
|
||||
inherit
|
||||
|
||||
WSF_URI_TEMPLATE_HANDLER
|
||||
|
||||
WSF_RESOURCE_HANDLER_HELPER
|
||||
redefine
|
||||
do_get,
|
||||
do_post,
|
||||
do_put,
|
||||
do_delete
|
||||
end
|
||||
|
||||
SHARED_DATABASE_API
|
||||
|
||||
SHARED_EJSON
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
SHARED_ORDER_VALIDATION
|
||||
|
||||
WSF_SELF_DOCUMENTED_HANDLER
|
||||
|
||||
create
|
||||
make_with_router
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make_with_router (a_router: WSF_ROUTER)
|
||||
-- Initialize `router'.
|
||||
require
|
||||
a_router_attached: a_router /= Void
|
||||
do
|
||||
router := a_router
|
||||
ensure
|
||||
router_aliased: router = a_router
|
||||
end
|
||||
|
||||
feature -- Router
|
||||
|
||||
router: WSF_ROUTER
|
||||
-- Associated router that could be used for advanced strategy
|
||||
|
||||
feature -- Execute
|
||||
|
||||
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Execute request handler
|
||||
do
|
||||
execute_methods (req, res)
|
||||
end
|
||||
|
||||
feature -- API DOC
|
||||
|
||||
api_doc : STRING = "URI:/order METHOD: POST%N URI:/order/{orderid} METHOD: GET, PUT, DELETE%N"
|
||||
|
||||
|
||||
feature -- Documentation
|
||||
|
||||
mapping_documentation (m: WSF_ROUTER_MAPPING; a_request_methods: detachable WSF_REQUEST_METHODS): WSF_ROUTER_MAPPING_DOCUMENTATION
|
||||
do
|
||||
create Result.make (m)
|
||||
if a_request_methods /= Void then
|
||||
if a_request_methods.has_method_post then
|
||||
Result.add_description ("URI:/order METHOD: POST")
|
||||
elseif
|
||||
a_request_methods.has_method_get
|
||||
or a_request_methods.has_method_put
|
||||
or a_request_methods.has_method_delete
|
||||
then
|
||||
Result.add_description ("URI:/order/{orderid} METHOD: GET, PUT, DELETE")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- HTTP Methods
|
||||
|
||||
do_get (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- <Precursor>
|
||||
local
|
||||
id: STRING
|
||||
do
|
||||
if attached req.path_info as l_path_info then
|
||||
id := get_order_id_from_path (l_path_info)
|
||||
if attached retrieve_order (id) as l_order then
|
||||
if is_conditional_get (req, l_order) then
|
||||
handle_resource_not_modified_response ("The resource" + l_path_info + "does not change", req, res)
|
||||
else
|
||||
compute_response_get (req, res, l_order)
|
||||
end
|
||||
else
|
||||
handle_resource_not_found_response ("The following resource" + l_path_info + " is not found ", req, res)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
is_conditional_get (req : WSF_REQUEST; l_order : ORDER) : BOOLEAN
|
||||
-- Check if If-None-Match is present and then if there is a representation that has that etag
|
||||
-- if the representation hasn't changed, we return TRUE
|
||||
-- then the response is a 304 with no entity body returned.
|
||||
local
|
||||
etag_util : ETAG_UTILS
|
||||
do
|
||||
if attached req.meta_string_variable ("HTTP_IF_NONE_MATCH") as if_none_match then
|
||||
create etag_util
|
||||
if if_none_match.same_string (etag_util.md5_digest (l_order.out).as_string_32) then
|
||||
Result := True
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
compute_response_get (req: WSF_REQUEST; res: WSF_RESPONSE; l_order: ORDER)
|
||||
local
|
||||
h: HTTP_HEADER
|
||||
l_msg : STRING
|
||||
etag_utils : ETAG_UTILS
|
||||
do
|
||||
create h.make
|
||||
create etag_utils
|
||||
h.put_content_type_application_json
|
||||
if attached {JSON_VALUE} json.value (l_order) as jv then
|
||||
l_msg := jv.representation
|
||||
h.put_content_length (l_msg.count)
|
||||
if attached req.request_time as time then
|
||||
h.add_header ("Date:" + time.formatted_out ("ddd,[0]dd mmm yyyy [0]hh:[0]mi:[0]ss.ff2") + " GMT")
|
||||
end
|
||||
h.add_header ("etag:" + etag_utils.md5_digest (l_order.out))
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||||
res.put_header_text (h.string)
|
||||
res.put_string (l_msg)
|
||||
end
|
||||
end
|
||||
|
||||
do_put (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Updating a resource with PUT
|
||||
-- A successful PUT request will not create a new resource, instead it will
|
||||
-- change the state of the resource identified by the current uri.
|
||||
-- If success we response with 200 and the updated order.
|
||||
-- 404 if the order is not found
|
||||
-- 400 in case of a bad request
|
||||
-- 500 internal server error
|
||||
-- If the request is a Conditional PUT, and it does not mat we response
|
||||
-- 415, precondition failed.
|
||||
local
|
||||
l_put: STRING
|
||||
l_order : detachable ORDER
|
||||
id : STRING
|
||||
do
|
||||
if attached req.path_info as l_path_info then
|
||||
id := get_order_id_from_path (l_path_info)
|
||||
l_put := retrieve_data (req)
|
||||
l_order := extract_order_request(l_put)
|
||||
if l_order /= Void and then db_access.orders.has_key (id) then
|
||||
l_order.set_id (id)
|
||||
if is_valid_to_update(l_order) then
|
||||
if is_conditional_put (req, l_order) then
|
||||
update_order( l_order)
|
||||
compute_response_put (req, res, l_order)
|
||||
else
|
||||
handle_precondition_fail_response ("", req, res)
|
||||
end
|
||||
else
|
||||
--| FIXME: Here we need to define the Allow methods
|
||||
handle_resource_conflict_response (l_put +"%N There is conflict while trying to update the order, the order could not be update in the current state", req, res)
|
||||
end
|
||||
else
|
||||
handle_bad_request_response (l_put +"%N is not a valid ORDER, maybe the order does not exist in the system", req, res)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
is_conditional_put (req : WSF_REQUEST; order : ORDER) : BOOLEAN
|
||||
-- Check if If-Match is present and then if there is a representation that has that etag
|
||||
-- if the representation hasn't changed, we return TRUE
|
||||
local
|
||||
etag_util : ETAG_UTILS
|
||||
do
|
||||
if attached retrieve_order (order.id) as l_order then
|
||||
if attached req.meta_string_variable ("HTTP_IF_MATCH") as if_match then
|
||||
create etag_util
|
||||
if if_match.same_string (etag_util.md5_digest (l_order.out).as_string_32) then
|
||||
Result := True
|
||||
end
|
||||
else
|
||||
Result := True
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
compute_response_put (req: WSF_REQUEST; res: WSF_RESPONSE; l_order : ORDER)
|
||||
local
|
||||
h: HTTP_HEADER
|
||||
joc : JSON_ORDER_CONVERTER
|
||||
etag_utils : ETAG_UTILS
|
||||
do
|
||||
create h.make
|
||||
create joc.make
|
||||
create etag_utils
|
||||
json.add_converter(joc)
|
||||
|
||||
create h.make
|
||||
h.put_content_type_application_json
|
||||
if attached req.request_time as time then
|
||||
h.add_header ("Date:" +time.formatted_out ("ddd,[0]dd mmm yyyy [0]hh:[0]mi:[0]ss.ff2") + " GMT")
|
||||
end
|
||||
h.add_header ("etag:" + etag_utils.md5_digest (l_order.out))
|
||||
if attached {JSON_VALUE} json.value (l_order) as jv then
|
||||
h.put_content_length (jv.representation.count)
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.ok)
|
||||
res.put_header_text (h.string)
|
||||
res.put_string (jv.representation)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
do_delete (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Here we use DELETE to cancel an order, if that order is in state where
|
||||
-- it can still be canceled.
|
||||
-- 200 if is ok
|
||||
-- 404 Resource not found
|
||||
-- 405 if consumer and service's view of the resouce state is inconsisent
|
||||
-- 500 if we have an internal server error
|
||||
local
|
||||
id: STRING
|
||||
do
|
||||
if attached req.path_info as l_path_info then
|
||||
id := get_order_id_from_path (l_path_info)
|
||||
if db_access.orders.has_key (id) then
|
||||
if is_valid_to_delete (id) then
|
||||
delete_order( id)
|
||||
compute_response_delete (req, res)
|
||||
else
|
||||
--| FIXME: Here we need to define the Allow methods
|
||||
handle_method_not_allowed_response (l_path_info + "%N There is conflict while trying to delete the order, the order could not be deleted in the current state", req, res)
|
||||
end
|
||||
else
|
||||
handle_resource_not_found_response (l_path_info + " not found in this server", req, res)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
compute_response_delete (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
local
|
||||
h : HTTP_HEADER
|
||||
do
|
||||
create h.make
|
||||
h.put_content_type_application_json
|
||||
if attached req.request_time as time then
|
||||
h.put_utc_date (time)
|
||||
end
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.no_content)
|
||||
res.put_header_text (h.string)
|
||||
end
|
||||
|
||||
do_post (req: WSF_REQUEST; res: WSF_RESPONSE)
|
||||
-- Here the convention is the following.
|
||||
-- POST is used for creation and the server determines the URI
|
||||
-- of the created resource.
|
||||
-- If the request post is SUCCESS, the server will create the order and will response with
|
||||
-- HTTP_RESPONSE 201 CREATED, the Location header will contains the newly created order's URI
|
||||
-- if the request post is not SUCCESS, the server will response with
|
||||
-- HTTP_RESPONSE 400 BAD REQUEST, the client send a bad request
|
||||
-- HTTP_RESPONSE 500 INTERNAL_SERVER_ERROR, when the server can deliver the request
|
||||
local
|
||||
l_post: STRING
|
||||
do
|
||||
l_post := retrieve_data (req)
|
||||
if attached extract_order_request (l_post) as l_order then
|
||||
save_order (l_order)
|
||||
compute_response_post (req, res, l_order)
|
||||
else
|
||||
handle_bad_request_response (l_post +"%N is not a valid ORDER", req, res)
|
||||
end
|
||||
end
|
||||
|
||||
compute_response_post (req: WSF_REQUEST; res: WSF_RESPONSE; l_order : ORDER)
|
||||
local
|
||||
h: HTTP_HEADER
|
||||
l_msg : STRING
|
||||
l_location : STRING
|
||||
joc : JSON_ORDER_CONVERTER
|
||||
do
|
||||
create h.make
|
||||
|
||||
create joc.make
|
||||
json.add_converter(joc)
|
||||
|
||||
h.put_content_type_application_json
|
||||
if attached {JSON_VALUE} json.value (l_order) as jv then
|
||||
l_msg := jv.representation
|
||||
h.put_content_length (l_msg.count)
|
||||
if attached req.http_host as host then
|
||||
l_location := "http://" + host + req.request_uri + "/" + l_order.id
|
||||
h.put_location (l_location)
|
||||
end
|
||||
if attached req.request_time as time then
|
||||
h.put_utc_date (time)
|
||||
end
|
||||
res.set_status_code ({HTTP_STATUS_CODE}.created)
|
||||
res.put_header_text (h.string)
|
||||
res.put_string (l_msg)
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- URI helper methods
|
||||
|
||||
get_order_id_from_path (a_path: READABLE_STRING_32) : STRING
|
||||
do
|
||||
Result := a_path.split ('/').at (3)
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation Repository Layer
|
||||
|
||||
retrieve_order ( id : STRING) : detachable ORDER
|
||||
-- get the order by id if it exist, in other case, Void
|
||||
do
|
||||
Result := db_access.orders.item (id)
|
||||
end
|
||||
|
||||
save_order (an_order: ORDER)
|
||||
-- save the order to the repository
|
||||
local
|
||||
i : INTEGER
|
||||
do
|
||||
from
|
||||
i := 1
|
||||
until
|
||||
not db_access.orders.has_key ((db_access.orders.count + i).out)
|
||||
loop
|
||||
i := i + 1
|
||||
end
|
||||
an_order.set_id ((db_access.orders.count + i).out)
|
||||
an_order.set_status ("submitted")
|
||||
an_order.add_revision
|
||||
db_access.orders.force (an_order, an_order.id)
|
||||
end
|
||||
|
||||
|
||||
is_valid_to_delete ( an_id : STRING) : BOOLEAN
|
||||
-- Is the order identified by `an_id' in a state whre it can still be deleted?
|
||||
do
|
||||
if attached retrieve_order (an_id) as l_order then
|
||||
if order_validation.is_state_valid_to_update (l_order.status) then
|
||||
Result := True
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
is_valid_to_update (an_order: ORDER) : BOOLEAN
|
||||
-- Check if there is a conflict while trying to update the order
|
||||
do
|
||||
if attached retrieve_order (an_order.id) as l_order then
|
||||
if order_validation.is_state_valid_to_update (l_order.status) and then order_validation.is_valid_status_state (an_order.status) and then
|
||||
order_validation.is_valid_transition (l_order, an_order.status) then
|
||||
Result := True
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
update_order (an_order: ORDER)
|
||||
-- update the order to the repository
|
||||
do
|
||||
an_order.add_revision
|
||||
db_access.orders.force (an_order, an_order.id)
|
||||
end
|
||||
|
||||
delete_order (an_order: STRING)
|
||||
-- update the order to the repository
|
||||
do
|
||||
db_access.orders.remove (an_order)
|
||||
end
|
||||
|
||||
extract_order_request (l_post : STRING) : detachable ORDER
|
||||
-- extract an object Order from the request, or Void
|
||||
-- if the request is invalid
|
||||
local
|
||||
parser : JSON_PARSER
|
||||
joc : JSON_ORDER_CONVERTER
|
||||
do
|
||||
create joc.make
|
||||
json.add_converter(joc)
|
||||
create parser.make_with_string (l_post)
|
||||
parser.parse_content
|
||||
if
|
||||
parser.is_valid and then
|
||||
attached parser.parsed_json_value as jv
|
||||
then
|
||||
if attached {like extract_order_request} json.object (jv, "ORDER") as res then
|
||||
Result := res
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2015, Javier Velilla and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
58
examples/obsolete/v0/restbucksCRUD/src/restbucks_server.e
Normal file
58
examples/obsolete/v0/restbucksCRUD/src/restbucks_server.e
Normal file
@@ -0,0 +1,58 @@
|
||||
note
|
||||
description : "REST Buck server"
|
||||
date : "$Date$"
|
||||
revision : "$Revision$"
|
||||
|
||||
class RESTBUCKS_SERVER
|
||||
|
||||
inherit
|
||||
|
||||
WSF_ROUTED_SKELETON_SERVICE
|
||||
undefine
|
||||
requires_proxy
|
||||
end
|
||||
|
||||
WSF_URI_TEMPLATE_HELPER_FOR_ROUTED_SERVICE
|
||||
|
||||
WSF_HANDLER_HELPER
|
||||
|
||||
WSF_DEFAULT_SERVICE
|
||||
|
||||
WSF_NO_PROXY_POLICY
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
initialize_router
|
||||
set_service_option ("port", 9090)
|
||||
make_and_launch
|
||||
end
|
||||
|
||||
setup_router
|
||||
local
|
||||
order_handler: ORDER_HANDLER
|
||||
doc: WSF_ROUTER_SELF_DOCUMENTATION_HANDLER
|
||||
do
|
||||
create order_handler.make_with_router (router)
|
||||
router.handle_with_request_methods ("/order", order_handler, router.methods_POST)
|
||||
router.handle_with_request_methods ("/order/{orderid}", order_handler, router.methods_GET + router.methods_DELETE + router.methods_PUT)
|
||||
create doc.make_hidden (router)
|
||||
router.handle_with_request_methods ("/api/doc", doc, router.methods_GET)
|
||||
end
|
||||
|
||||
|
||||
note
|
||||
copyright: "2011-2013, Javier Velilla and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
24
examples/obsolete/v0/restbucksCRUD/src/utils/etag_utils.e
Normal file
24
examples/obsolete/v0/restbucksCRUD/src/utils/etag_utils.e
Normal file
@@ -0,0 +1,24 @@
|
||||
note
|
||||
description: "Summary description for {ETAG_UTILS}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
ETAG_UTILS
|
||||
|
||||
feature -- Access
|
||||
|
||||
md5_digest (a_string: STRING): STRING
|
||||
-- Cryptographic hash function that produces a 128-bit (16-byte) hash value, based on `a_string'
|
||||
local
|
||||
md5: MD5
|
||||
do
|
||||
create md5.make
|
||||
md5.update_from_string (a_string)
|
||||
Result := md5.digest_as_string
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2014, Javier Velilla and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
end
|
||||
Reference in New Issue
Block a user