Updated webapi system.

Added specific webapi handler classes for root, user, access token, ...
Added new queries to user profile api.
Reviewed filter setup depending on the mode (site, admin, webapi).
This commit is contained in:
Jocelyn Fiat
2017-08-25 09:20:17 +02:00
parent db585a4873
commit 053cc000cd
18 changed files with 858 additions and 213 deletions

View File

@@ -1,188 +0,0 @@
note
description: "Summary description for {CMS_CORE_MODULE_WEBAPI}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
CMS_CORE_MODULE_WEBAPI
inherit
CMS_MODULE_WEBAPI [CMS_CORE_MODULE]
redefine
permissions
end
create
make
feature -- Security
permissions: LIST [READABLE_STRING_8]
-- List of permission ids, used by this module, and declared.
do
Result := Precursor
Result.force ("admin users")
end
feature {NONE} -- Router/administration
setup_webapi_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- <Precursor>
do
a_router.handle ("", create {WSF_URI_AGENT_HANDLER}.make (agent handle_root (?, ?, a_api)), a_router.methods_get)
a_router.handle ("/access_token", create {WSF_URI_AGENT_HANDLER}.make (agent do_post_access_token (?, ?, a_api)), a_router.methods_post)
a_router.handle ("/user/{uid}", create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (agent do_get_user (?, ?, a_api)), a_router.methods_get)
end
feature -- Request handling
handle_root (req: WSF_REQUEST; res: WSF_RESPONSE; api: CMS_API)
local
rep: HM_WEBAPI_RESPONSE
do
rep := new_webapi_response (req, res, api)
rep.add_field ("site_name", api.setup.site_name)
if attached api.user as u then
add_user_links_to (u, rep)
end
rep.add_self (req.percent_encoded_path_info)
rep.execute
end
feature -- Access token
do_get_access_token (req: WSF_REQUEST; res: WSF_RESPONSE; api: CMS_API)
local
rep: HM_WEBAPI_RESPONSE
-- l_access_token: detachable READABLE_STRING_32
do
if
attached api.user as l_user and then
attached api.user_api.user_profile_item ("access_token", l_user) as l_access_token
then
-- l_access_token := new_key (40)
-- api.user_api.save_user_profile_item (l_user, "access_token", l_access_token)
rep := new_webapi_response (req, res, api)
rep.add_field ("access_token", l_access_token)
rep.add_self (req.percent_encoded_path_info)
add_user_links_to (l_user, rep)
if attached {WSF_STRING} req.item ("destination") as dest then
rep.set_redirection (dest.url_encoded_value)
end
rep.execute
else
send_access_denied (Void, req, res, api)
end
end
do_post_access_token (req: WSF_REQUEST; res: WSF_RESPONSE; api: CMS_API)
local
m: WSF_PAGE_RESPONSE
l_access_token: detachable READABLE_STRING_32
do
if attached api.user as l_user then
l_access_token := api.user_api.user_profile_item ("access_token", l_user)
l_access_token := new_key (40)
-- if l_access_token /= Void then
-- l_access_token := "Updated-" + (create {UUID_GENERATOR}).generate_uuid.out
-- else
-- l_access_token := "New-" + (create {UUID_GENERATOR}).generate_uuid.out
-- end
api.user_api.save_user_profile_item (l_user, "access_token", l_access_token)
if attached {WSF_STRING} req.item ("destination") as dest then
res.redirect_now (dest.value.to_string_8)
else
create m.make_with_body ("Ok")
res.send (m)
end
else
send_access_denied (Void, req, res, api)
end
end
feature -- Users
do_get_user (req: WSF_REQUEST; res: WSF_RESPONSE; api: CMS_API)
local
rep: HM_WEBAPI_RESPONSE
do
if attached api.user as u then
rep := new_webapi_response (req, res, api)
rep.add_field ("uid", u.id.out)
rep.add_field ("name", u.name)
if attached u.email as l_email then
rep.add_field ("email", l_email)
end
if attached u.profile_name as l_profile_name then
rep.add_field ("profile_name", l_profile_name)
end
add_user_links_to (u, rep)
rep.execute
else
-- FIXME: use specific Web API response!
send_access_denied (Void, req, res, api)
end
end
feature -- Helpers
new_webapi_response (req: WSF_REQUEST; res: WSF_RESPONSE; api: CMS_API): HM_WEBAPI_RESPONSE
do
-- create {MD_WEBAPI_RESPONSE} Result.make (req, res, api)
create {JSON_WEBAPI_RESPONSE} Result.make (req, res, api)
end
send_access_denied (m: detachable READABLE_STRING_GENERAL; req: WSF_REQUEST; res: WSF_RESPONSE; api: CMS_API)
local
rep: HM_WEBAPI_RESPONSE
do
rep := new_webapi_response (req, res, api)
if m /= Void then
rep.add_field ("error", m)
else
rep.add_field ("error", "Access denied")
end
rep.execute
end
add_user_links_to (u: CMS_USER; rep: HM_WEBAPI_RESPONSE)
do
rep.add_link ("account", "user/" + u.id.out, rep.api.webapi_path ("/user/" + u.id.out))
end
new_key (len: INTEGER): STRING_8
local
rand: RANDOM
n: INTEGER
v: NATURAL_32
do
create rand.set_seed ((create {DATE_TIME}.make_now_utc).seconds)
rand.start
create Result.make (len)
from
n := 1
until
n = len
loop
rand.forth
v := (rand.item \\ 16).to_natural_32
check 0 <= v and v <= 15 end
if v < 9 then
Result.append_code (48 + v) -- 48 '0'
else
Result.append_code (97 + v - 9) -- 97 'a'
end
n := n + 1
end
end
note
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -118,6 +118,16 @@ feature -- Access: user
Result := user_storage.user_by_name (a_username)
end
user_by_id_or_name (a_uid: READABLE_STRING_GENERAL): detachable CMS_USER
-- User by id or name `a_uid`, if any.
do
if a_uid.is_integer_64 then
Result := user_by_id (a_uid.to_integer_64)
else
Result := user_by_name (a_uid)
end
end
user_by_email (a_email: READABLE_STRING_GENERAL): detachable CMS_USER
-- User by email `a_email', if any.
do

View File

@@ -54,6 +54,15 @@ feature -- Access: profile
Result := user_profile_storage.user_profile_item (a_user, a_item_name)
end
users_with_profile_item (a_item_name: READABLE_STRING_GENERAL; a_value: detachable READABLE_STRING_GENERAL): detachable LIST [CMS_USER]
-- Users having a profile item `a_item_name:a_value`.
-- Note: if `a_value` is Void, return users having a profile item named `a_item_name`.
require
not a_item_name.is_whitespace
do
Result := user_profile_storage.users_with_profile_item (a_item_name, a_value)
end
feature -- Change: profile
save_user_profile (a_user: CMS_USER; a_user_profile: CMS_USER_PROFILE)

View File

@@ -82,7 +82,6 @@ feature -- HTTP Methods
-- <Precursor>
local
l_user: detachable CMS_USER
l_uid: INTEGER_64
do
if api.has_permission ("view user") then
-- Display existing node

View File

@@ -31,6 +31,12 @@ feature -- Access
end
end
users_with_profile_item (a_item_name: READABLE_STRING_GENERAL; a_value: detachable READABLE_STRING_GENERAL): detachable LIST [CMS_USER]
-- Users having a profile item `a_item_name:a_value`.
-- Note: if `a_value` is Void, return users having a profile item named `a_item_name`.
deferred
end
feature -- Change
save_user_profile (a_user: CMS_USER; a_profile: CMS_USER_PROFILE)
@@ -54,4 +60,7 @@ feature -- Change
save_user_profile (a_user, pf)
end
note
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -24,6 +24,11 @@ feature -- Access
do
end
users_with_profile_item (a_item_name: READABLE_STRING_GENERAL; a_value: detachable READABLE_STRING_GENERAL): detachable LIST [CMS_USER]
-- <Precursor>
do
end
feature -- Change
save_user_profile (a_user: CMS_USER; a_profile: CMS_USER_PROFILE)
@@ -31,4 +36,7 @@ feature -- Change
do
end
note
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -66,13 +66,63 @@ feature -- Access
sql_finalize
end
users_with_profile_item (a_item_name: READABLE_STRING_GENERAL; a_value: detachable READABLE_STRING_GENERAL): detachable LIST [CMS_USER]
-- Users having a profile item `a_item_name:a_value`.
-- Note: if `a_value` is Void, return users having a profile item named `a_item_name`.
local
l_parameters: STRING_TABLE [detachable ANY]
l_uids: ARRAYED_LIST [INTEGER_64]
do
reset_error
create l_parameters.make (2)
l_parameters.put (a_item_name, "key")
if a_value = Void then
sql_query (sql_select_users_with_profile_item_named, l_parameters)
else
l_parameters.put (a_value, "value")
sql_query (sql_select_users_with_profile_item, l_parameters)
end
if not has_error then
create l_uids.make (0)
from
sql_start
until
sql_after or has_error
loop
if
attached sql_read_integer_64 (1) as l_uid and then
l_uid > 0
then
l_uids.force (l_uid)
end
sql_forth
end
end
sql_finalize
if
not has_error and
l_uids /= Void and
attached api as l_cms_api
then
create {ARRAYED_LIST [CMS_USER]} Result.make (l_uids.count)
across
l_uids as ic
loop
if attached l_cms_api.user_api.user_by_id (ic.item) as u then
Result.force (u)
else
check known_user: False end
end
end
end
end
feature -- Change
save_user_profile_item (a_user: CMS_USER; a_item_name: READABLE_STRING_GENERAL; a_item_value: READABLE_STRING_GENERAL)
-- Save user profile item `a_item_name:a_item_value` for `a_user'.
local
l_parameters: STRING_TABLE [detachable ANY]
p: detachable CMS_USER_PROFILE
do
create l_parameters.make (3)
l_parameters.put (a_user.id, "uid")
@@ -142,6 +192,12 @@ feature {NONE} -- Queries
sql_select_user_profile_item: STRING = "SELECT key, value FROM user_profiles WHERE uid=:uid AND key=:key"
-- user profile items for :uid;
sql_select_users_with_profile_item: STRING = "SELECT uid FROM user_profiles WHERE key=:key and value=:value"
-- users with profile item named :key and value :value;
sql_select_users_with_profile_item_named: STRING = "SELECT uid FROM user_profiles WHERE key=:key"
-- users with profile item named :key;
sql_insert_user_profile_item: STRING = "INSERT INTO user_profiles (uid, key, value) VALUES (:uid, :key, :value);"
-- new user profile item for :uid;
@@ -149,5 +205,8 @@ feature {NONE} -- Queries
-- user profile items for :uid;
note
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,52 @@
note
description: "Summary description for {CMS_CORE_ACCESS_TOKEN_WEBAPI_AUTH_FILTER}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
CMS_CORE_ACCESS_TOKEN_WEBAPI_AUTH_FILTER
inherit
WSF_FILTER
create
make
feature {NONE} -- Initialization
make (a_api: CMS_API)
-- Initialize Current handler with `a_api'.
do
api := a_api
end
feature -- API Service
api: CMS_API
feature -- Basic operations
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute the filter.
local
tok: READABLE_STRING_GENERAL
do
if
attached req.http_authorization as l_auth and then
l_auth.starts_with_general ("Bearer ")
then
tok := l_auth.substring (8, l_auth.count)
if attached api.user_api.users_with_profile_item ("access_token", tok) as lst then
if lst.count = 1 then
api.set_user (lst.first)
end
end
end
execute_next (req, res)
end
note
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,92 @@
note
description: "Summary description for {CMS_CORE_MODULE_WEBAPI}."
author: ""
date: "$Date$"
revision: "$Revision$"
class
CMS_CORE_MODULE_WEBAPI
inherit
CMS_MODULE_WEBAPI [CMS_CORE_MODULE]
redefine
permissions,
filters
end
create
make
feature -- Security
permissions: LIST [READABLE_STRING_8]
-- List of permission ids, used by this module, and declared.
do
Result := Precursor
Result.force ("admin users")
Result.force ("view users")
end
feature {NONE} -- Router/administration
setup_webapi_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- <Precursor>
local
l_root: CMS_CORE_WEBAPI_ROOT_HANDLER
do
create l_root.make (a_api)
a_router.handle ("", l_root, a_router.methods_get)
a_router.handle ("/", l_root, a_router.methods_get)
a_router.handle ("/user/{uid}/access_token", create {CMS_CORE_WEBAPI_ACCESS_TOKEN_HANDLER}.make (a_api), a_router.methods_get_post)
a_router.handle ("/user/{uid}", create {CMS_CORE_WEBAPI_USER_HANDLER}.make (a_api), a_router.methods_get)
end
feature -- Access: filter
filters (a_api: CMS_API): detachable LIST [WSF_FILTER]
-- Possibly list of Filter's module.
do
create {ARRAYED_LIST [WSF_FILTER]} Result.make (1)
Result.extend (create {CMS_CORE_ACCESS_TOKEN_WEBAPI_AUTH_FILTER}.make (a_api))
end
--feature -- Helpers
-- new_webapi_response (req: WSF_REQUEST; res: WSF_RESPONSE; api: CMS_API): HM_WEBAPI_RESPONSE
-- do
---- create {MD_WEBAPI_RESPONSE} Result.make (req, res, api)
-- create {JSON_WEBAPI_RESPONSE} Result.make (req, res, api)
-- end
-- new_wepapi_error_response (msg: detachable READABLE_STRING_GENERAL; req: WSF_REQUEST; res: WSF_RESPONSE; api: CMS_API): HM_WEBAPI_RESPONSE
-- do
-- Result := new_webapi_response (req, res, api)
-- if msg /= Void then
-- Result.add_string_field ("error", msg)
-- else
-- Result.add_string_field ("error", "True")
-- end
-- end
-- send_access_denied (m: detachable READABLE_STRING_GENERAL; req: WSF_REQUEST; res: WSF_RESPONSE; api: CMS_API)
-- local
-- rep: HM_WEBAPI_RESPONSE
-- do
-- rep := new_webapi_response (req, res, api)
-- if m /= Void then
-- rep.add_string_field ("error", m)
-- else
-- rep.add_string_field ("error", "Access denied")
-- end
-- rep.execute
-- end
-- add_user_links_to (u: CMS_USER; rep: HM_WEBAPI_RESPONSE)
-- do
-- rep.add_link ("account", "user/" + u.id.out, rep.api.webapi_path ("/user/" + u.id.out))
-- end
note
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,177 @@
note
description: "Summary description for {CMS_CORE_WEBAPI_ACCESS_TOKEN_HANDLER}."
date: "$Date$"
revision: "$Revision$"
class
CMS_CORE_WEBAPI_ACCESS_TOKEN_HANDLER
inherit
CMS_WEBAPI_HANDLER
WSF_URI_TEMPLATE_HANDLER
create
make
feature -- Execution
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute handler for `req' and respond in `res'.
local
l_uid: READABLE_STRING_GENERAL
do
if attached {WSF_STRING} req.path_parameter ("uid") as p_uid then
l_uid := p_uid.value
if req.is_post_request_method then
post_access_token (l_uid, req, res)
elseif req.is_get_request_method then
get_access_token (l_uid, req, res)
else
send_bad_request (Void, req, res)
end
else
send_bad_request ("Missing {uid} parameter", req, res)
end
end
feature -- Helper
user_by_uid (a_uid: READABLE_STRING_GENERAL): detachable CMS_USER
do
Result := api.user_api.user_by_id_or_name (a_uid)
end
feature -- Request execution
get_access_token (a_uid: READABLE_STRING_GENERAL; req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute handler for `req' and respond in `res'.
local
rep: HM_WEBAPI_RESPONSE
do
if attached user_by_uid (a_uid) as l_user then
if attached api.user as u then
if u.same_as (l_user) or api.user_api.is_admin_user (u) then
rep := new_access_token_webapi_response (l_user, user_access_token (l_user), req, res)
if attached {WSF_STRING} req.item ("destination") as dest then
rep.set_redirection (dest.url_encoded_value)
end
rep.execute
else
-- Only admin, or current user can see its access_token!
send_access_denied (Void, req, res)
end
else
send_access_denied (Void, req, res)
end
else
send_not_found ("User not found", req, res)
end
end
post_access_token (a_uid: READABLE_STRING_GENERAL; req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute handler for `req' and respond in `res'.
local
l_access_token: detachable READABLE_STRING_32
rep: like new_webapi_response
do
if attached user_by_uid (a_uid) as l_user then
if attached api.user as u then
if u.same_as (l_user) or api.user_api.is_admin_user (u) then
if attached req.path_parameter ("application") then
end
-- l_access_token := user_access_token (l_user)
l_access_token := new_key (40)
-- if l_access_token /= Void then
-- l_access_token := "Updated-" + (create {UUID_GENERATOR}).generate_uuid.out
-- else
-- l_access_token := "New-" + (create {UUID_GENERATOR}).generate_uuid.out
-- end
set_user_access_token (l_user, l_access_token)
rep := new_access_token_webapi_response (l_user, l_access_token, req, res)
if attached {WSF_STRING} req.item ("destination") as dest then
rep.set_redirection (dest.url_encoded_value)
end
rep.execute
else
-- Only admin, or current user can create the user access_token!
send_access_denied (Void, req, res)
end
else
send_access_denied (Void, req, res)
end
else
send_not_found ("User not found", req, res)
end
end
feature {NONE} -- Implementation
user_access_token (a_user: CMS_USER): READABLE_STRING_8
do
if
attached api.user_api.user_profile_item ("access_token", a_user) as l_access_token and then
not l_access_token.is_whitespace and then
l_access_token.is_valid_as_string_8
then
Result := l_access_token.to_string_8
else
Result := new_key (40)
end
end
set_user_access_token (a_user: CMS_USER; a_access_token: READABLE_STRING_GENERAL)
do
api.user_api.save_user_profile_item (a_user, "access_token", a_access_token)
end
new_access_token_webapi_response (a_user: CMS_USER; a_access_token: READABLE_STRING_GENERAL; req: WSF_REQUEST; res: WSF_RESPONSE): like new_webapi_response
local
tb: STRING_TABLE [detachable ANY]
do
Result := new_webapi_response (req, res)
Result.add_string_field ("access_token", a_access_token)
create tb.make_equal (3)
tb.force (a_user.name, "name")
tb.force (a_user.id, "uid")
Result.add_table_iterator_field ("user", tb)
-- Result.add_string_field ("username", a_user.name)
-- Result.add_integer_64_field ("uid", a_user.id)
Result.add_self (req.percent_encoded_path_info)
add_user_links_to (a_user, Result)
end
new_key (len: INTEGER): STRING_8
local
rand: RANDOM
n: INTEGER
v: NATURAL_32
do
create rand.set_seed ((create {DATE_TIME}.make_now_utc).seconds)
rand.start
create Result.make (len)
from
n := 1
until
n = len
loop
rand.forth
v := (rand.item \\ 16).to_natural_32
check 0 <= v and v <= 15 end
if v < 9 then
Result.append_code (48 + v) -- 48 '0'
else
Result.append_code (97 + v - 9) -- 97 'a'
end
n := n + 1
end
end
note
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,36 @@
note
description: "Summary description for {CMS_CORE_WEBAPI_ROOT_HANDLER}."
date: "$Date$"
revision: "$Revision$"
class
CMS_CORE_WEBAPI_ROOT_HANDLER
inherit
CMS_WEBAPI_HANDLER
WSF_URI_HANDLER
create
make
feature -- Execution
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute handler for `req' and respond in `res'.
local
rep: HM_WEBAPI_RESPONSE
do
rep := new_webapi_response (req, res)
rep.add_string_field ("site_name", api.setup.site_name)
if attached api.user as u then
add_user_links_to (u, rep)
end
rep.add_self (req.percent_encoded_path_info)
rep.execute
end
note
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -0,0 +1,80 @@
note
description: "Summary description for {CMS_CORE_WEBAPI_USER_HANDLER}."
date: "$Date$"
revision: "$Revision$"
class
CMS_CORE_WEBAPI_USER_HANDLER
inherit
CMS_WEBAPI_HANDLER
WSF_URI_TEMPLATE_HANDLER
create
make
feature -- Execution
execute (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute handler for `req' and respond in `res'.
do
if req.is_get_request_method then
execute_get (req, res)
else
send_bad_request (Void, req, res)
end
end
execute_get (req: WSF_REQUEST; res: WSF_RESPONSE)
-- Execute handler for `req' and respond in `res'.
local
rep: HM_WEBAPI_RESPONSE
l_user: detachable CMS_USER
do
if attached api.user as u then
if attached {WSF_STRING} req.path_parameter ("uid") as p_uid then
if p_uid.is_integer then
l_user := api.user_api.user_by_id (p_uid.integer_value)
else
l_user := api.user_api.user_by_name (p_uid.value)
end
-- if l_user = Void and p_uid.is_case_insensitive_equal ("me") then
-- l_user := u
-- end
if l_user /= Void then
if l_user.same_as (u) or api.has_permissions (<<"admin users", "view users">>) then
rep := new_webapi_response (req, res)
rep.add_string_field ("uid", u.id.out)
rep.add_string_field ("name", u.name)
if attached u.email as l_email then
rep.add_string_field ("email", l_email)
end
if attached u.profile_name as l_profile_name then
rep.add_string_field ("profile_name", l_profile_name)
end
add_user_links_to (u, rep)
else
rep := new_wepapi_error_response ("denied", req, res)
rep.set_status_code ({HTTP_STATUS_CODE}.user_access_denied)
end
else
rep := new_wepapi_error_response ("Not found", req, res)
rep.set_status_code ({HTTP_STATUS_CODE}.not_found)
end
else
rep := new_wepapi_error_response ("Bad request", req, res)
rep.set_status_code ({HTTP_STATUS_CODE}.bad_request)
end
rep.execute
else
-- FIXME: use specific Web API response!
send_access_denied (Void, req, res)
end
end
note
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -19,6 +19,7 @@ inherit
execute_default,
filter_execute,
initialize,
initialize_filter,
initialize_router
end
@@ -61,6 +62,13 @@ feature {NONE} -- Initialization
create {WSF_MAINTENANCE_FILTER} filter
end
initialize_filter
-- Initialize `filter`.
do
create_filter
-- setup_filter: delayed to `initialize_execution`.
end
initialize_router
-- Initialize `router`.
do
@@ -147,11 +155,12 @@ feature -- Settings: router
configure_api_file_handler (l_router)
end
setup_router_for_webapi
setup_router_and_filter_for_webapi
local
l_api: like api
l_router: like router
l_module: CMS_MODULE
f, l_filter, l_last_filter: WSF_FILTER
do
l_api := api
l_router := router
@@ -161,6 +170,20 @@ feature -- Settings: router
-- Configure root of api handler.
l_router.set_base_url (l_api.webapi_path (Void))
-- Find insertion location for new filter
-- i.e just before the CMS_EXECUTION filter.
from
l_filter := filter
until
l_last_filter /= Void or not attached l_filter.next as l_next_filter
loop
if l_next_filter.next = Void then
l_last_filter := l_next_filter
else
l_filter := l_next_filter
end
end
-- Include routes from modules.
across
modules as ic
@@ -172,25 +195,52 @@ feature -- Settings: router
attached l_webapi.module_webapi as wapi
then
wapi.setup_router (l_router, l_api)
if attached wapi.filters (l_api) as l_filters then
across
l_filters as f_ic
loop
f := f_ic.item
l_filter.set_next (f)
f.set_next (l_last_filter)
end
end
end
end
end
setup_router_for_administration
setup_router_and_filter_for_administration
-- <Precursor>
local
l_api: like api
l_router: like router
l_module: CMS_MODULE
f, l_filter, l_last_filter: WSF_FILTER
do
l_api := api
l_router := router
l_api.logger.put_debug (generator + ".setup_router_for_administration", Void)
l_api.logger.put_debug (generator + ".setup_router_and_filter_for_administration", Void)
-- Configure root of api handler.
l_router.set_base_url (l_api.administration_path (Void))
-- Apply normal filters
setup_filter
-- Find insertion location for new filter
-- i.e just before the CMS_EXECUTION filter.
from
l_filter := filter
until
l_last_filter /= Void or not attached l_filter.next as l_next_filter
loop
if l_next_filter.next = Void then
l_last_filter := l_next_filter
else
l_filter := l_next_filter
end
end
-- Include routes from modules.
across
modules as ic
@@ -200,15 +250,24 @@ feature -- Settings: router
l_module.is_initialized
then
if
attached {CMS_ADMINISTRABLE} l_module as l_administration and then
attached {CMS_WITH_MODULE_ADMINISTRATION} l_module as l_administration and then
attached l_administration.module_administration as adm
then
adm.setup_router (l_router, l_api)
elseif
attached {CMS_WITH_WEBAPI} l_module as l_wapi and then
attached l_wapi.module_webapi as wapi
then
wapi.setup_router (l_router, l_api)
if attached adm.filters (l_api) as l_filters then
across
l_filters as f_ic
loop
f := f_ic.item
l_filter.set_next (f)
f.set_next (l_last_filter)
end
end
-- elseif
-- attached {CMS_WITH_WEBAPI} l_module as l_wapi and then
-- attached l_wapi.module_webapi as wapi
-- then
-- wapi.setup_router (l_router, l_api)
end
end
end
@@ -283,6 +342,7 @@ feature -- Request execution
do
api.switch_to_site_mode
api.initialize_execution
setup_filter
setup_router
end
@@ -291,7 +351,7 @@ feature -- Request execution
do
api.switch_to_webapi_mode
api.initialize_execution
setup_router_for_webapi
setup_router_and_filter_for_webapi
end
initialize_administration_execution
@@ -299,7 +359,7 @@ feature -- Request execution
do
api.switch_to_administration_mode
api.initialize_execution
setup_router_for_administration
setup_router_and_filter_for_administration
end
execute
@@ -341,6 +401,36 @@ feature -- Filters
-- f.set_next (l_filter)
-- l_filter := f
-- -- Include filters from modules
-- across
-- modules as ic
-- loop
-- l_module := ic.item
-- if
-- l_module.is_enabled and then
-- attached l_module.filters (l_api) as l_m_filters
-- then
-- across l_m_filters as f_ic loop
-- f := f_ic.item
-- l_filter.append (f)
---- f.set_next (l_filter)
---- l_filter := f
-- end
-- end
-- end
filter := l_filter
end
setup_filter
-- Setup `filter'.
local
f, l_filter: detachable WSF_FILTER
l_module: CMS_MODULE
l_api: like api
do
l_api := api
l_filter := filter
-- Include filters from modules
across
modules as ic
@@ -352,20 +442,15 @@ feature -- Filters
then
across l_m_filters as f_ic loop
f := f_ic.item
f.set_next (l_filter)
l_filter := f
l_filter.append (f)
-- f.set_next (l_filter)
-- l_filter := f
end
end
end
filter := l_filter
end
setup_filter
-- Setup `filter'.
do
end
feature -- Execution
handle_favicon (req: WSF_REQUEST; res: WSF_RESPONSE)

View File

@@ -15,11 +15,27 @@ feature -- Element change
deferred
end
add_field (a_name: READABLE_STRING_GENERAL; a_value: READABLE_STRING_GENERAL)
add_string_field (a_name: READABLE_STRING_GENERAL; a_value: READABLE_STRING_GENERAL)
deferred
end
add_link (rel: READABLE_STRING_8; a_attname: READABLE_STRING_8 ; a_att_href: READABLE_STRING_8)
add_boolean_field (a_name: READABLE_STRING_GENERAL; a_value: BOOLEAN)
deferred
end
add_integer_64_field (a_name: READABLE_STRING_GENERAL; a_value: INTEGER_64)
deferred
end
add_iterator_field (a_name: READABLE_STRING_GENERAL; a_value: ITERABLE [detachable ANY])
deferred
end
add_table_iterator_field (a_name: READABLE_STRING_GENERAL; a_value: TABLE_ITERABLE [detachable ANY, READABLE_STRING_GENERAL])
deferred
end
add_link (rel: READABLE_STRING_8; a_attname: READABLE_STRING_8; a_att_href: READABLE_STRING_8)
deferred
end

View File

@@ -31,14 +31,53 @@ feature -- Element change
add_self (a_href: READABLE_STRING_8)
do
add_field ("self", a_href)
add_string_field ("self", a_href)
end
add_field (a_name: READABLE_STRING_GENERAL; a_value: READABLE_STRING_GENERAL)
feature -- Fields
-- add_field (a_name: READABLE_STRING_GENERAL; a_value: detachable ANY)
-- do
-- resource.put (new_resource_item (a_value), a_name)
-- end
add_string_field (a_name: READABLE_STRING_GENERAL; a_value: READABLE_STRING_GENERAL)
do
resource.put_string (a_value, a_name)
end
add_boolean_field (a_name: READABLE_STRING_GENERAL; a_value: BOOLEAN)
do
resource.put_boolean (a_value, a_name)
end
add_integer_64_field (a_name: READABLE_STRING_GENERAL; a_value: INTEGER_64)
do
resource.put_integer (a_value, a_name)
end
add_natural_64_field (a_name: READABLE_STRING_GENERAL; a_value: NATURAL_64)
do
resource.put_natural (a_value, a_name)
end
add_real_64_field (a_name: READABLE_STRING_GENERAL; a_value: REAL_64)
do
resource.put_real (a_value, a_name)
end
add_iterator_field (a_name: READABLE_STRING_GENERAL; a_value: ITERABLE [detachable ANY])
do
resource.put (new_resource_item (a_value), a_name)
end
add_table_iterator_field (a_name: READABLE_STRING_GENERAL; a_value: TABLE_ITERABLE [detachable ANY, READABLE_STRING_GENERAL])
do
resource.put (new_resource_item (a_value), a_name)
end
feature -- Links
add_link (rel: READABLE_STRING_8; a_attname: READABLE_STRING_8 ; a_att_href: READABLE_STRING_8)
local
lnks: JSON_OBJECT
@@ -66,6 +105,68 @@ feature -- Execution
response.send (m)
end
feature {NONE} -- Implementation factory
new_resource_item (a_value: detachable ANY): JSON_VALUE
local
l_serializer: JSON_REFLECTOR_SERIALIZER
ctx: JSON_SERIALIZER_CONTEXT
do
create {JSON_NULL} Result
create l_serializer
create ctx
ctx.set_default_serializer (l_serializer)
ctx.set_is_type_name_included (False)
ctx.register_serializer (create {TABLE_ITERABLE_JSON_SERIALIZER [detachable ANY, READABLE_STRING_GENERAL]}, {TABLE_ITERABLE [detachable ANY, READABLE_STRING_GENERAL]})
ctx.register_serializer (create {ITERABLE_JSON_SERIALIZER [detachable ANY]}, {ITERABLE [detachable ANY]})
Result := l_serializer.to_json (a_value, ctx)
-- if a_value = Void then
-- create {JSON_NULL} Result
-- elseif attached {READABLE_STRING_GENERAL} a_value as s then
-- create {JSON_STRING} Result.make_from_string_general (s)
-- elseif attached {BOOLEAN} a_value as b then
-- create {JSON_BOOLEAN} Result.make (b)
-- elseif attached {NUMERIC} a_value as num then
---- if attached {INTEGER_64} num as i64 then
---- add_integer_64_field (a_name, i64)
---- elseif attached {INTEGER_32} num as i32 then
---- add_integer_64_field (a_name, i32.as_integer_64)
---- elseif attached {INTEGER_16} num as i16 then
---- add_integer_64_field (a_name, i16.as_integer_64)
---- elseif attached {INTEGER_8} num as i8 then
---- add_integer_64_field (a_name, i8.as_integer_64)
---- elseif attached {NATURAL_64} num as n64 then
---- add_natural_64_field (a_name, n64)
---- elseif attached {NATURAL_32} num as n32 then
---- add_natural_64_field (a_name, n32.as_natural_64)
---- elseif attached {NATURAL_16} num as n16 then
---- add_natural_64_field (a_name, n16.as_natural_64)
---- elseif attached {NATURAL_8} num as n8 then
---- add_natural_64_field (a_name, n8.as_natural_64)
---- elseif attached {REAL_64} num as r64 then
---- add_real_64_field (a_name, r64)
---- elseif attached {REAL_32} num as r32 then
---- add_real_64_field (a_name, r32.to_double
---- else
---- check is_basic_numeric_type: False end
---- add_string_field (a_name, num.out)
---- end
-- elseif attached {CHARACTER_8} a_value as ch8 then
---- add_string_field (a_name, ch8.out)
-- elseif attached {CHARACTER_32} a_value as ch32 then
---- add_string_field (a_name, ch32.out)
-- elseif attached {POINTER} a_value as ptr then
---- add_string_field (a_name, ptr.out)
-- elseif attached {ITERABLE [detachable ANY]} a_value as arr then
---- add_iterator_field (a_name, arr)
-- elseif attached {TABLE_ITERABLE [detachable ANY, READABLE_STRING_GENERAL]} a_value as tb then
---- add_table_iterator_field (a_name, tb)
-- else
-- check is_supported_type: False end
-- end
end
invariant
note

View File

@@ -0,0 +1,100 @@
note
description: "[
Objects that ...
]"
author: "$Author$"
date: "$Date$"
revision: "$Revision$"
deferred class
CMS_WEBAPI_HANDLER
inherit
WSF_HANDLER
CMS_API_ACCESS
CMS_ENCODERS
REFACTORING_HELPER
feature {NONE} -- Initialization
make (a_api: CMS_API)
-- Initialize Current handler with `a_api'.
do
api := a_api
end
feature -- API Service
api: CMS_API
feature -- Factory
new_webapi_response (req: WSF_REQUEST; res: WSF_RESPONSE): HM_WEBAPI_RESPONSE
do
-- create {MD_WEBAPI_RESPONSE} Result.make (req, res, api)
create {JSON_WEBAPI_RESPONSE} Result.make (req, res, api)
end
new_wepapi_error_response (msg: detachable READABLE_STRING_GENERAL; req: WSF_REQUEST; res: WSF_RESPONSE): HM_WEBAPI_RESPONSE
do
Result := new_webapi_response (req, res)
if msg /= Void then
Result.add_string_field ("error", msg)
else
Result.add_string_field ("error", "True")
end
end
send_not_found (m: detachable READABLE_STRING_GENERAL; req: WSF_REQUEST; res: WSF_RESPONSE)
local
rep: HM_WEBAPI_RESPONSE
do
if m /= Void then
rep := new_wepapi_error_response (m, req, res)
else
rep := new_wepapi_error_response ("Not found", req, res)
end
rep.set_status_code ({HTTP_STATUS_CODE}.not_found)
rep.execute
end
send_access_denied (m: detachable READABLE_STRING_GENERAL; req: WSF_REQUEST; res: WSF_RESPONSE)
local
rep: HM_WEBAPI_RESPONSE
do
if m /= Void then
rep := new_wepapi_error_response (m, req, res)
else
rep := new_wepapi_error_response ("Access denied", req, res)
end
rep.set_status_code ({HTTP_STATUS_CODE}.user_access_denied)
rep.execute
end
send_bad_request (m: detachable READABLE_STRING_GENERAL; req: WSF_REQUEST; res: WSF_RESPONSE)
local
rep: HM_WEBAPI_RESPONSE
do
if m /= Void then
rep := new_wepapi_error_response (m, req, res)
else
rep := new_wepapi_error_response ("Bad request", req, res)
end
rep.set_status_code ({HTTP_STATUS_CODE}.bad_request)
rep.execute
end
feature {NONE} -- Builder
add_user_links_to (u: CMS_USER; rep: HM_WEBAPI_RESPONSE)
do
rep.add_link ("account", "user/" + u.id.out, rep.api.webapi_path ("/user/" + u.id.out))
end
note
copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end