Skip to content

Commit 661dc96

Browse files
committed
Work in progress Eiffel Google SpreadSheets.
1 parent 1f58868 commit 661dc96

18 files changed

+1191
-4
lines changed

sheets/Readme.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Eiffel API for Google Sheets API
2+
3+
https://developers.google.com/sheets/api/reference/rest
4+
5+
6+
OAuth2.0 Client credentials
7+
To begin, obtain OAuth 2.0 client credentials from the [Browser Quickstart](https://developers.google.com/sheets/api/quickstart/js) and enable the credentials.
8+

sheets/src/eg_sheets_api.e

Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
note
2+
description: "Sheets API: specify how to read and write Google Sheets data."
3+
date: "$Date$"
4+
revision: "$Revision$"
5+
6+
class
7+
EG_SHEETS_API
8+
create
9+
make
10+
11+
feature {NONE} -- Initialization
12+
13+
make (a_access_token: READABLE_STRING_32)
14+
do
15+
-- Using a code verifier
16+
access_token := a_access_token
17+
enable_version_4
18+
end
19+
20+
feature -- Reset
21+
22+
reset
23+
do
24+
create access_token.make_empty
25+
end
26+
27+
feature -- Access
28+
29+
access_token: STRING_32
30+
-- Google OAuth2 access token.
31+
32+
http_status: INTEGER
33+
-- Contains the last HTTP status code returned.
34+
35+
last_api_call: detachable STRING
36+
-- Contains the last API call.
37+
38+
last_response: detachable OAUTH_RESPONSE
39+
-- Cointains the last API response.
40+
41+
version: STRING_8
42+
-- Google Sheets version
43+
44+
45+
feature -- Spreedsheets
46+
47+
create_spreedsheet: detachable STRING
48+
-- POST /spreadsheets
49+
note
50+
EIS:"name=create.spreedsheets", "src=https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/create", "protocol=uri"
51+
do
52+
api_post_call (sheets_url ("spreadsheets", Void ), Void, Void)
53+
if
54+
attached last_response as l_response and then
55+
attached l_response.body as l_body
56+
then
57+
Result := l_body
58+
end
59+
end
60+
61+
feature -- Parameters Factory
62+
63+
parameters (a_params: detachable STRING_TABLE [STRING] ): detachable ARRAY [detachable TUPLE [name: STRING; value: STRING]]
64+
local
65+
l_result: detachable ARRAY [detachable TUPLE [name: STRING; value: STRING]]
66+
l_tuple : detachable TUPLE [name: STRING; value: STRING]
67+
i: INTEGER
68+
do
69+
if attached a_params then
70+
i := 1
71+
create l_result.make_filled (Void, 1, a_params.count)
72+
across a_params as ic
73+
loop
74+
create l_tuple.default_create
75+
l_tuple.put (ic.key, 1)
76+
l_tuple.put (ic.item, 2)
77+
l_result.force (l_tuple, i)
78+
i := i + 1
79+
end
80+
Result := l_result
81+
end
82+
end
83+
84+
feature -- Error Report
85+
86+
has_error: BOOLEAN
87+
-- Last api call raise an error?
88+
do
89+
if attached last_response as l_response then
90+
Result := l_response.status /= 200
91+
else
92+
Result := False
93+
end
94+
end
95+
96+
error_message: STRING
97+
-- Last api call error message.
98+
require
99+
has_error: has_error
100+
do
101+
if
102+
attached last_response as l_response
103+
then
104+
if
105+
attached l_response.error_message as l_error_message
106+
then
107+
Result := l_error_message
108+
else
109+
Result := l_response.status.out
110+
end
111+
else
112+
Result := "Unknown Error"
113+
end
114+
end
115+
116+
feature -- Versions
117+
118+
enable_version_4
119+
-- Enable Google sheets version v4.
120+
do
121+
version := "v4"
122+
ensure
123+
version_set: version.same_string ("v4")
124+
end
125+
126+
feature {NONE} -- Implementation
127+
128+
api_post_call (a_api_url: STRING; a_params: detachable STRING_TABLE [STRING]; a_upload_data: detachable TUPLE[data:PATH; content_type: STRING])
129+
-- POST REST API call for `a_api_url'
130+
do
131+
internal_api_call (a_api_url, "POST", a_params, a_upload_data)
132+
end
133+
134+
api_delete_call (a_api_url: STRING; a_params: detachable STRING_TABLE [STRING])
135+
-- DELETE REST API call for `a_api_url'
136+
do
137+
internal_api_call (a_api_url, "DELETE", a_params, Void)
138+
end
139+
140+
api_get_call (a_api_url: STRING; a_params: detachable STRING_TABLE [STRING])
141+
-- GET REST API call for `a_api_url'
142+
do
143+
internal_api_call (a_api_url, "GET", a_params, Void)
144+
end
145+
146+
internal_api_call (a_api_url: STRING; a_method: STRING; a_params: detachable STRING_TABLE [STRING]; a_upload_data: detachable TUPLE[filename:PATH; content_type: STRING])
147+
note
148+
EIS:"name=access token", "src=https://developers.google.com/identity/protocols/oauth2", "protocol=uri"
149+
local
150+
request: OAUTH_REQUEST
151+
l_access_token: detachable OAUTH_TOKEN
152+
api_service: OAUTH_20_SERVICE
153+
config: OAUTH_CONFIG
154+
do
155+
-- TODO improve this, so we can check the required scopes before we
156+
-- do an api call.
157+
create config.make_default ("", "")
158+
config.set_scope ("https://www.googleapis.com/auth/spreadsheets")
159+
160+
-- Initialization
161+
162+
create api_service.make (create {OAUTH_20_GOOGLE_API}, config)
163+
--| TODO improve cypress service creation procedure to make configuration optional.
164+
165+
print ("%N===Google OAuth Workflow using OAuth access token for the owner of the application ===%N")
166+
--| TODO rewrite prints as logs
167+
168+
-- Create the access token that will identifies the user making the request.
169+
create l_access_token.make_token_secret (access_token, "NOT_NEEDED")
170+
--| Todo improve access_token to create a token without a secret.
171+
if attached l_access_token as ll_access_token then
172+
print ("%NGot the Access Token!%N");
173+
174+
--Now let's go and check if the request is signed correcty
175+
print ("%NNow we're going to verify our credentials...%N");
176+
-- Build the request and authorize it with OAuth.
177+
create request.make (a_method, a_api_url)
178+
-- Workaorund to make it work with Google API
179+
-- in other case it return HTTP 411 Length Required.
180+
-- Todo check.
181+
request.add_header ("Content-length", "0")
182+
upload_data (a_method, request, a_upload_data)
183+
add_parameters (a_method, request, a_params)
184+
api_service.sign_request (ll_access_token, request)
185+
if attached {OAUTH_RESPONSE} request.execute as l_response then
186+
last_response := l_response
187+
end
188+
end
189+
last_api_call := a_api_url.string
190+
end
191+
192+
sheets_url (a_query: STRING; a_params: detachable STRING): STRING
193+
-- Sheet url endpoint
194+
note
195+
eis: "name=Sheets service endpoint", "src=https://developers.google.com/sheets/api/reference/rest", "protocol=uri"
196+
require
197+
a_query_attached: a_query /= Void
198+
do
199+
create Result.make_from_string (endpooint_sheets_url)
200+
Result.append ("/")
201+
Result.append (version)
202+
Result.append ("/")
203+
Result.append (a_query)
204+
if attached a_params then
205+
Result.append_character ('?')
206+
Result.append (a_params)
207+
end
208+
ensure
209+
Result_attached: Result /= Void
210+
end
211+
212+
url (a_base_url: STRING; a_parameters: detachable ARRAY [detachable TUPLE [name: STRING; value: STRING]]): STRING
213+
-- url for `a_base_url' and `a_parameters'
214+
require
215+
a_base_url_attached: a_base_url /= Void
216+
do
217+
create Result.make_from_string (a_base_url)
218+
append_parameters_to_url (Result, a_parameters)
219+
end
220+
221+
append_parameters_to_url (a_url: STRING; a_parameters: detachable ARRAY [detachable TUPLE [name: STRING; value: STRING]])
222+
-- Append parameters `a_parameters' to `a_url'
223+
require
224+
a_url_attached: a_url /= Void
225+
local
226+
i: INTEGER
227+
l_first_param: BOOLEAN
228+
do
229+
if a_parameters /= Void and then a_parameters.count > 0 then
230+
if a_url.index_of ('?', 1) > 0 then
231+
l_first_param := False
232+
elseif a_url.index_of ('&', 1) > 0 then
233+
l_first_param := False
234+
else
235+
l_first_param := True
236+
end
237+
from
238+
i := a_parameters.lower
239+
until
240+
i > a_parameters.upper
241+
loop
242+
if attached a_parameters[i] as a_param then
243+
if l_first_param then
244+
a_url.append_character ('?')
245+
else
246+
a_url.append_character ('&')
247+
end
248+
a_url.append_string (a_param.name)
249+
a_url.append_character ('=')
250+
a_url.append_string (a_param.value)
251+
l_first_param := False
252+
end
253+
i := i + 1
254+
end
255+
end
256+
end
257+
258+
add_parameters (a_method: STRING; request:OAUTH_REQUEST; a_params: detachable STRING_TABLE [STRING])
259+
-- add parameters 'a_params' (with key, value) to the oauth request 'request'.
260+
--| at the moment all params are added to the query_string.
261+
do
262+
add_query_parameters (request, a_params)
263+
end
264+
265+
add_query_parameters (request:OAUTH_REQUEST; a_params: detachable STRING_TABLE [STRING])
266+
do
267+
if attached a_params then
268+
across a_params as ic
269+
loop
270+
request.add_query_string_parameter (ic.key.as_string_8, ic.item)
271+
end
272+
end
273+
end
274+
275+
add_body_parameters (request:OAUTH_REQUEST; a_params: detachable STRING_TABLE [STRING])
276+
do
277+
if attached a_params then
278+
across a_params as ic
279+
loop
280+
request.add_body_parameter (ic.key.as_string_8, ic.item)
281+
end
282+
end
283+
end
284+
285+
upload_data (a_method: STRING; request:OAUTH_REQUEST; a_upload_data: detachable TUPLE[file_name:PATH; content_type: STRING])
286+
local
287+
l_raw_file: RAW_FILE
288+
do
289+
if a_method.is_case_insensitive_equal_general ("POST") and then attached a_upload_data as l_upload_data then
290+
291+
create l_raw_file.make_open_read (l_upload_data.file_name.absolute_path.name)
292+
if l_raw_file.exists then
293+
request.add_header ("Content-Type", l_upload_data.content_type)
294+
request.set_upload_filename (l_upload_data.file_name.absolute_path.name)
295+
request.add_form_parameter("source", l_upload_data.file_name.name.as_string_32)
296+
end
297+
end
298+
end
299+
300+
feature -- Service Endpoint
301+
302+
endpooint_sheets_url: STRING = "https://sheets.googleapis.com"
303+
-- base URL that specifies the network address of an API service.
304+
end
305+

sheets/src/eg_sheets_i.e

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,31 @@ note
44
revision: "$Revision$"
55
EIS: "name=Rest API GSheets", "src=https://developers.google.com/sheets/api/reference/rest?apix=true", "protocol=urihttps://developers.google.com/sheets/api/reference/rest?apix=true"
66

7-
class
7+
deferred class
88
EG_SHEETS_I
99

10+
11+
feature {NONE} -- Initialization
12+
13+
make (a_code: READABLE_STRING_32)
14+
deferred
15+
end
16+
17+
feature -- Status Report
18+
19+
last_status_code: INTEGER
20+
-- Return the HTTP status code from the last request.
21+
deferred
22+
end
23+
24+
feature -- Post
25+
26+
create_spreedsheet: EG_SPREEDSHEET
27+
-- Creates a spreadsheet, returning the newly created spreadsheet.
28+
note
29+
EIS:"name=create.spreedsheets", "src=https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/create", "protocol=uri"
30+
deferred
31+
end
32+
33+
1034
end

0 commit comments

Comments
 (0)