Skip to content

Commit 5778f2e

Browse files
authored
Merge branch 'master' into eg_sheets
2 parents 7398b86 + eb66766 commit 5778f2e

17 files changed

+641
-70
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@ EIFGENs
22
*.swp
33
*.rc
44

5+
system.log
6+
token.access
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
note
2+
description: "Summary description for {OAUTH_20_GOOGLE_API}."
3+
date: "$Date: 2020-08-04 12:05:41 -0300 (Tue, 04 Aug 2020) $"
4+
revision: "$Revision: 104570 $"
5+
EIS: "name=OAuth2 google apis", "src=https://developers.google.com/accounts/docs/OAuth2", "protocol=uri"
6+
7+
class
8+
OAUTH_20_GOOGLE_API
9+
10+
inherit
11+
12+
OAUTH_20_API
13+
redefine
14+
access_token_extractor,
15+
access_token_verb
16+
end
17+
18+
feature -- Access
19+
20+
access_token_extractor: ACCESS_TOKEN_EXTRACTOR
21+
do
22+
create {JSON_TOKEN_EXTRACTOR} Result
23+
end
24+
25+
access_token_verb: STRING_8
26+
-- <Precursor>
27+
do
28+
Result := "POST"
29+
end
30+
31+
access_token_endpoint: STRING_8
32+
-- Url that receives the access token request
33+
do
34+
create {STRING_8} Result.make_from_string ("https://oauth2.googleapis.com/token")
35+
end
36+
37+
authorization_url (config: OAUTH_CONFIG): detachable STRING_8
38+
-- Url where you should redirect your users to authneticate
39+
do
40+
if attached config.scope as l_scope then
41+
create Result.make_from_string (TEMPLATE_AUTHORIZE_URL + SCOPED_AUTHORIZE_URL)
42+
Result.replace_substring_all ("$CLIENT_ID", config.api_key.as_string_8)
43+
if attached config.callback as l_callback then
44+
Result.replace_substring_all ("$REDIRECT_URI", (create {OAUTH_ENCODER}).encoded_string (l_callback.as_string_8))
45+
end
46+
Result.replace_substring_all ("$SCOPE", (create {OAUTH_ENCODER}).encoded_string (l_scope.as_string_8))
47+
else
48+
create Result.make_from_string (TEMPLATE_AUTHORIZE_URL)
49+
Result.replace_substring_all ("$CLIENT_ID", config.api_key.as_string_8)
50+
if attached config.callback as l_callback then
51+
Result.replace_substring_all ("$REDIRECT_URI", (create {OAUTH_ENCODER}).encoded_string (l_callback.as_string_8))
52+
end
53+
end
54+
end
55+
56+
feature -- Implementation
57+
58+
Template_authorize_url: STRING_8 = "https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=$CLIENT_ID&redirect_uri=$REDIRECT_URI"
59+
60+
Scoped_authorize_url: STRING = "&scope=$SCOPE"
61+
62+
note
63+
copyright: "2013-2020, Javier Velilla, Jocelyn Fiat, Eiffel Software and others"
64+
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
65+
source: "[
66+
Eiffel Software
67+
5949 Hollister Ave., Goleta, CA 93117 USA
68+
Telephone 805-685-1006, Fax 805-685-6869
69+
Website http://www.eiffel.com
70+
Customer support http://support.eiffel.com
71+
]"
72+
end

sheets/Readme.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
Eiffel API for Google Sheets API
2-
32
https://developers.google.com/sheets/api/reference/rest
43

54

65
OAuth2.0 Client credentials
76
To begin, obtain OAuth 2.0 client credentials from the [Browser Quickstart](https://developers.google.com/sheets/api/quickstart/js) and enable the credentials.
87

8+
9+
- EG_SHEETS_I -- it's an interface where we work with Eiffel Objects
10+
- EG_SHEETS_API -- It's the low level interface that sends and receives HTTP messages..
11+
- EG_SHEETS_JSON -- it's the implementation where we map from the HTTP messages encoded in JSON format to the Eiffel side. (and vice versa).
12+
13+
Then in the object cluster we have the Eiffel objects representing the API domain.

sheets/esheets.ecf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
2121
<library name="cypress_consumer" location="$ISE_LIBRARY\contrib\library\web\authentication\oauth\cypress\consumer\consumer.ecf" readonly="false"/>
2222
<library name="encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder.ecf"/>
23+
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http.ecf"/>
2324
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json.ecf" readonly="false"/>
25+
<library name="logging" location="$ISE_LIBRARY\library\runtime\logging\logging.ecf"/>
2426
<library name="login_with_google" location="$ISE_LIBRARY\contrib\library\web\authentication\oauth\cypress\login_with\google\login_with_google.ecf" readonly="false"/>
2527
<library name="uri" location="$ISE_LIBRARY\library\text\uri\uri.ecf"/>
2628
<cluster name="esheets_api" location=".\src\" recursive="true"/>

sheets/src/eg_sheets_api.e

Lines changed: 141 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ note
55

66
class
77
EG_SHEETS_API
8+
inherit
9+
LOGGABLE
10+
811
create
912
make
1013

@@ -41,8 +44,11 @@ feature -- Access
4144
version: STRING_8
4245
-- Google Sheets version
4346

44-
4547
feature -- Spreedsheets
48+
spreadsheet_id: detachable STRING
49+
50+
51+
feature -- Spreedsheets Operations
4652

4753
create_spreedsheet: detachable STRING
4854
-- POST /spreadsheets
@@ -58,12 +64,104 @@ feature -- Spreedsheets
5864
end
5965
end
6066

67+
get_from_id (a_spreadsheet_id: attached like spreadsheet_id): detachable like last_response.body
68+
-- POST /spreadsheets/`a_spreadsheet_id'
69+
note
70+
EIS:"name=get.spreedsheets", "src=https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/get", "protocol=uri"
71+
require
72+
not a_spreadsheet_id.is_empty
73+
local
74+
l_file: PLAIN_TEXT_FILE
75+
l_qry_params: STRING_TABLE [STRING]
76+
do
77+
78+
logger.write_information ("get_from_id-> Now getting sheet from id:" + a_spreadsheet_id)
79+
create l_qry_params.make (2)
80+
l_qry_params.extend ("true", "includeGridData") -- all content
81+
-- l_params.extend ("sheets.properties", "fields") -- properties only
82+
83+
api_get_call (sheets_url ("spreadsheets/" + a_spreadsheet_id, Void), l_qry_params)
84+
check
85+
attached last_response as l_response and then
86+
attached l_response.body as l_body
87+
then
88+
parse_last_response
89+
if l_response.status = {HTTP_STATUS_CODE}.ok then
90+
Result := l_body
91+
92+
create l_file.make_create_read_write ("/tmp/hitme_sheet_json-get_from_id.json")
93+
logger.write_information ("get_from_id->Writing body into " + l_file.path.utf_8_name)
94+
l_file.close
95+
l_file.wipe_out
96+
l_file.open_append
97+
98+
l_file.put_string (l_body)
99+
l_file.close
100+
elseif l_response.status = {HTTP_STATUS_CODE}.not_found then
101+
logger.write_error ("get_from_id-> Not found:" + l_response.status.out + " %NBody: " + l_body)
102+
else
103+
logger.write_error ("get_from_id-> Status code invalid:" + l_response.status.out + " %NBody: " + l_body)
104+
end
105+
end
106+
end
107+
108+
append (a_spreadsheet_id: attached like spreadsheet_id; a_data_line: ARRAY[STRING]): detachable like last_response.body
109+
note
110+
EIS:"name=append.spreedsheets", "src=https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/append", "protocol=uri"
111+
require
112+
not a_spreadsheet_id.is_empty
113+
local
114+
l_file: PLAIN_TEXT_FILE
115+
l_range,
116+
l_path_params_s: STRING
117+
l_qry_params: STRING_TABLE [STRING]
118+
do
119+
l_range := ""
120+
logger.write_information ("append-> spreadsheed_id:" + a_spreadsheet_id)
121+
-- path params
122+
l_path_params_s := a_spreadsheet_id
123+
l_path_params_s.append ("/values/{") -- spreadsheets/{spreadsheetId}/values/{range}:append
124+
l_path_params_s.append ("") -- range ex. A1:B2 or namedRanges TRY: last not null index could be: =index(J:J,max(row(J:J)*(J:J<>"")))
125+
l_path_params_s.append ("}:append")
126+
-- qry params
127+
create l_qry_params.make (2)
128+
l_qry_params.extend ("RAW", "valueInputOption") -- INPUT_VALUE_OPTION_UNSPECIFIED|RAW|USER_ENTERED https://developers.google.com/sheets/api/reference/rest/v4/ValueInputOption
129+
l_qry_params.extend ("INSERT_ROWS", "insertDataOption") -- OVERWRITE|INSERT_ROWS https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/append#InsertDataOption
130+
l_qry_params.extend ("true", "includeValuesInResponse") -- BOOLEAN
131+
l_qry_params.extend ("true", "responseValueRenderOption") -- FORMATTED_VALUE| https://developers.google.com/sheets/api/reference/rest/v4/ValueRenderOption
132+
133+
api_get_call (sheets_url ("spreadsheets/" + l_path_params_s, Void), l_qry_params)
134+
check
135+
attached last_response as l_response and then
136+
attached l_response.body as l_body
137+
then
138+
parse_last_response
139+
if l_response.status = {HTTP_STATUS_CODE}.ok then
140+
Result := l_body
141+
142+
create l_file.make_create_read_write ("/tmp/hitme_sheet_json-append.json")
143+
logger.write_information ("get_from_id->Writing body into " + l_file.path.utf_8_name)
144+
l_file.close
145+
l_file.wipe_out
146+
l_file.open_append
147+
148+
l_file.put_string (l_body)
149+
l_file.close
150+
elseif l_response.status = {HTTP_STATUS_CODE}.not_found then
151+
logger.write_error ("get_from_id-> Not found:" + l_response.status.out + " %NBody: " + l_body)
152+
else
153+
logger.write_error ("get_from_id-> Status code invalid:" + l_response.status.out + " %NBody: " + l_body)
154+
end
155+
end
156+
end
157+
61158
feature -- Parameters Factory
62159

63160
parameters (a_params: detachable STRING_TABLE [STRING] ): detachable ARRAY [detachable TUPLE [name: STRING; value: STRING]]
161+
-- @JV please add a call example
64162
local
65-
l_result: detachable ARRAY [detachable TUPLE [name: STRING; value: STRING]]
66-
l_tuple : detachable TUPLE [name: STRING; value: STRING]
163+
l_result: like parameters
164+
l_tuple : like parameters.item
67165
i: INTEGER
68166
do
69167
if attached a_params then
@@ -83,6 +181,43 @@ feature -- Parameters Factory
83181

84182
feature -- Error Report
85183

184+
parse_last_response
185+
require
186+
attached last_response
187+
local
188+
l_json_parser: JSON_PARSER
189+
do
190+
check
191+
attached last_response as l_response
192+
then
193+
if l_response.status = {HTTP_STATUS_CODE}.unauthorized then
194+
logger.write_error ("parse_last_response->Unauthorized status, review your authorization credentials")
195+
end
196+
if attached l_response.body as l_body then
197+
logger.write_debug ("parse_last_response->body:" + l_body)
198+
create l_json_parser.make_with_string (l_body)
199+
l_json_parser.parse_content
200+
if l_json_parser.is_valid then
201+
if attached {JSON_OBJECT} l_json_parser.parsed_json_value as l_main_jso then
202+
if attached {JSON_OBJECT} l_main_jso.item ("error") as l_error_jso then
203+
if attached {JSON_NUMBER} l_error_jso.item ("code") as l_jso then
204+
print ("parse_last_response-> error code:" + l_jso.representation + "%N")
205+
end
206+
if attached {JSON_STRING} l_error_jso.item ("message") as l_jso then
207+
print ("parse_last_response-> error message:" + l_jso.unescaped_string_8 + "%N")
208+
end
209+
if attached {JSON_STRING} l_error_jso.item ("status") as l_jso then
210+
print ("parse_last_response-> error status:" + l_jso.unescaped_string_8 + "%N")
211+
end
212+
end
213+
end
214+
else
215+
print ("parse_last_response-> Error: Invalid json body content:" + l_body + "%N")
216+
end
217+
end
218+
end
219+
end
220+
86221
has_error: BOOLEAN
87222
-- Last api call raise an error?
88223
do
@@ -152,6 +287,7 @@ feature {NONE} -- Implementation
152287
api_service: OAUTH_20_SERVICE
153288
config: OAUTH_CONFIG
154289
do
290+
logger.write_debug ("internal_api_call-> a_api_url:" + a_api_url + " method:" + a_method)
155291
-- TODO improve this, so we can check the required scopes before we
156292
-- do an api call.
157293
create config.make_default ("", "")
@@ -169,10 +305,10 @@ feature {NONE} -- Implementation
169305
create l_access_token.make_token_secret (access_token, "NOT_NEEDED")
170306
--| Todo improve access_token to create a token without a secret.
171307
if attached l_access_token as ll_access_token then
172-
print ("%NGot the Access Token!%N");
308+
logger.write_information ("internal_api_call->Got the Access Token:" + ll_access_token.token);
173309

174310
--Now let's go and check if the request is signed correcty
175-
print ("%NNow we're going to verify our credentials...%N");
311+
logger.write_information ("internal_api_call->Now we're going to verify our credentials...%N");
176312
-- Build the request and authorize it with OAuth.
177313
create request.make (a_method, a_api_url)
178314
-- Workaorund to make it work with Google API

sheets/src/errors/eg_sheet_error.e

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
note
2+
description: "Summary description for {EG_SHEET_ERROR}."
3+
author: ""
4+
date: "$Date$"
5+
revision: "$Revision$"
6+
7+
class
8+
EG_SHEET_ERROR
9+
10+
create
11+
make,
12+
make_from_separate
13+
14+
feature {NONE} -- Initialize
15+
16+
make (a_msg: like message)
17+
do
18+
message := a_msg
19+
ensure
20+
message = a_msg
21+
end
22+
23+
make_from_separate (other: separate like Current)
24+
do
25+
create message.make_from_separate (other.message)
26+
end
27+
28+
feature -- Access
29+
30+
message: STRING
31+
32+
feature -- SCOOP
33+
34+
error_from_separate (an_err: separate like Current): EG_SHEET_ERROR
35+
do
36+
create Result.make_from_separate (an_err)
37+
ensure
38+
instance_free: Class
39+
end
40+
41+
end

0 commit comments

Comments
 (0)