Phần 2: Tìm hiểu về Google Calendar API. Hướng dẫn viết bộ wrapper bằng Java để gọi Restful API
Tác giả: Phạm Huy Hoàng
Mục đích:
· Giới thiệu sơ lược về cơ chế Oauth của google.
· Giới thiệu 1 số concept của Google Calendar.
· Giới thiệu về Restful Service của Google Calendar API.
· Hướng dẫn tạo tài khoản Google, lấy clientID, clientSecrect.
· Hướng dẫn viết bộ Java wrapper, gọi Restful Service API.
Yêu cầu về kiến thức cơ bản:
· Nắm vững khái niệm về ngôn ngữ lập trình Java, lập trình thao tác hướng đối tượng
· Hiểu được các method của HTTP, có hiểu biết cơ bản về Restful Web Service.
(Xem thêm ở đây: http://www.kieutrongkhanh.net/2016/08/gioi-thieu-ve-restful-web-services-cong.html )
· Có khái niệm cơ bản về JSON, biết cách đọc hiểu JSON.
Lý thuyết phần này khá dài, do đó quý vị có thể đọc sơ qua, làm theo demo, sau đó quay lại đọc để hiểu rõ hơn
1. Cơ chế Oauth của google
Khi ứng dụng của bạn muốn truy cập/chỉnh sửa thông tin từ phía người dùng, nó sẽ đưa ra 1 url, yêu cầu người dùng xác nhận (Tương tự như khi bạn xác nhận 1 app của facebook).
Khi người dùng bấm nút “Chấp nhận”, google sẽ gửi trả lại ứng dụng 1 chuỗi chữ số gọi là Authoration Code.
Chương trình sẽ đưa chuỗi Authoration Code này lên server, để lấy về 1 danh sách các Token như sau:
{
"access_token":"1/fFAGRNJru1FTz70BzhT3Zg",
"expires_in":3920,
"token_type":"Bearer",
"refresh_token":"1/xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
}
Mỗi khi ứng dụng của chúng ta muốn gửi request đến Google API, nó phải gửi kèm Access Token trong request để authorize.
Thời gian sống của AccessToken thường là 1 tiếng, do đó để tiện lợi, chúng ta lưu trữ RefreshToken.
Khi muốn gọi API, nhưng access token đã expired, chúng ta có thể gửi refresh token đến server, yêu cầu cấp Access Token mới. Xin phép vẽ lại diagram ban đầu như sau.
Quý vị có thể tìm hiểu thêm về cơ chế Oauth của google ở đây:
https://developers.google.com/accounts/docs/OAuth2
Chi tiết: https://developers.google.com/accounts/docs/OAuth2WebServer
2. Một số khái niệm trong Google Calendar
CalendarList: Một danh sách các lịch của user, được hiển thị trong Google Calendar
Calendar: Thể hiện 1 lịch, chứa các thông tin như: Người tạo, nội dung….
Event: Một sự kiện được đánh dấu trong lịch, chứa các thông tin như : Nội dung, thời gian bắt đầu, kết thúc, người tham gia.
Google hỗ trợ 3 loại event:
Timed Event: Event bắt đầu và kết thúc giữa 2 khoảng thời gian (VD: Ăn trưa 10:00-12:00)
All-Days Event: Event kéo dài nguyên ngày
Recurring Event: Event lặp đi lặp lại (VD: Đi học 8:00 hàng ngày)
Thông tin chi tiết quý vị có thể tìm hiểu thêm ở đây:
https://developers.google.com/google-apps/calendar/concepts
3. Giới thiệu về Restful Service của Google CalendarAPI
Google Calendar API được implement dựa trên nền tảng RESTful service, với những Url tương tự phía dưới
https://www.googleapis.com/calendar/v3/users/me/calendarList?parameters
https://www.googleapis.com/calendar/v3/calendars/calendarID/events/eventID?parameters
(Kiến thức cơ bản về Web-service: http://kieutrongkhanh.net/index.php/java-web-service-x/79-gii-thiu-v-restful-web-services)
Kiểu trả về của service này là JSON. http://en.wikipedia.org/wiki/JSON. Json là 1 cách thể hiện data đơn giản, language-independent (Ko bị phụ thuộc vào ngôn ngữ). JSON và XML là 2 kiểu trả về thường thấy của Restful Service.
1 chuỗi JSON có syntax như sau:
{
"id": 1,
"name": "Foo",
"price": 123,
"tags": [ "Bar", "Eek" ],
"stock": {
"warehouse": 300,
"retail": 20
}
}
Có nhiều bộ Parser hỗ trợ JSON trên gần như toàn bộ mọi ngôn ngữ: C#, Java, Python ….
NOTE: Lý do chúng ta tiếp cận Googgle CalendarAPI thông qua Restful Service.
Trên trang chủ, google có cung cấp 1 số bộ API cho các ngôn ngữ tại đây:
https://developers.google.com/google-apps/calendar/firstapp#rest
Tuy nhiên các bộ API này có những khuyết điểm sau:
· Số lượng reference cần add nhiều, phức tạp ( >20 file .dll cho API C#, >10 file .jar cho API java).
· Google khá tắc trách trong việc cập nhật document, sample code. Vì vậy các API phiên bản mới sẽ không chạy với sample code cũ, gây khó khăn trong việc tìm hiểu + sử dụng.
Mục tiêu của chúng ta trong bài tutorial này là:
· Tự implement 1 bộ Wrapper Java để gọi API của Google.
· Việc này giúp chúng ta hiểu rõ hơn cơ chế hoạt động, kiểu dữ liệu gửi đi, gửi về, giao tiếp giữa client và google server (Những bộ API đã encapsulate những phần này đi).
Lý thuyết đã tạm xong, giờ chúng ta bắt đầu implement nào .
Bước 1: Trước khi bước vào code, chúng ta cần setup 1 project trên google, đồng thời lấy clientID + key trên google console.
Vào trang google console, bấm Add Project để tạo 1 project
https://cloud.google.com/console#/project
Bấm Create, click vào project vừa tạo
Bấm vào API & Auth. Tìm tới CalendarAPI và bấm On
Bấm tiếp qua Tab: Registered App. Bấm Register App
Sau khi Register App, bấm vào khung Oauth2.0 ClientID
Nhớ ghi lại 2 giá trị ClientID và ClientSecret. Phần RedirectUrl ta add tạm http://localhost (Sau này sẽ quay lại).
Ta có thể vào consent screen để sửa đổi thông tin, đây là màn hình sẽ hiện lên khi yêu cầu xác nhận của user.
Sau khi đã có clientID và clientSecret, coi như chúng ta hoàn thành xong bước 1. Quý vị có thể nghỉ ngơi 5 phút trước khi bắt đầu code ở bước 2.
Bước 2: Code bộ Java Wrapper API
Lần này chúng ta sẽ tạo 1 Java Console App mới, hoàn toàn tách biệt với project cũ. Việc tích hợp bộ wrapper vào Project cũ sẽ được hướng dẫn trong phần 3.
Chúng ta cần 2 bộ thư viện:
Gson 2.2.4: http://code.google.com/p/google-gson/downloads/list
Apache HttpComponent bản 4.3.1: http://hc.apache.org/downloads.cgi
Giải nén các file zip, add library những file sau:
· gson-2.2.4.jar
· commons-logging-1.1.3.jar
· fluent-hc-4.3.1.jar
· httpclient-4.3.1.jar
· httpcore-4.3.jar
Đầu tiên, chúng ta viết class WebUtils, hỗ trợ việc kết nối web, gửi và nhận dữ liệu
Hàm GetResult, dùng để gọi method GET của giao thức HTTP, dùng cho các hàm Get List.
Hàm DeleteResult, dùng để gọi method DELETE của giao thức HTTP
Hai hàm PostResult, một hàm để post dưới dạng NameValue, 1 hàm để post chuỗi json.
Hàm PutResult, sử dụng method PUT, dùng cho các hàm Update
Giải thích: Khi gọi API từ 1 url, ta phải đính kèm Access Token vào header, do đó các hàm luôn có url và Access Token.
GET /oauth2/v1/userinfo HTTP/1.1
Authorization: Bearer 1/fFBGRNJru1FQd44AzqT3Zg
Host: googleapis.com
Kết quả được service trả về dưới dạng JSON, do đó các hàm trả kết quả dưới dạng string, sau đó ta sẽ dùng gson để parse chuỗi JSON qua object.
Ta bắt đầu viết 1 số hàm của bộ wrapper. Trong quá trình viết, chúng ta sẽ vừa viết vừa test.
Quý vị tự dùng ClientID và ClientSecret có được từ bước 1, vui lòng đừng dùng ID của chúng tôi.
Hàm getAuthUrl dùng để tạo chuỗi AuthUrl. https://developers.google.com/accounts/docs/OAuth2WebServer
Chuỗi Scope: Url này đòi hỏi user cho phép app truy cập, quản lý calendar.
Access_type = Offline: Phải có dòng này để trong kết quả trả về có kèm refresh token.
Redirect_Uri: Sau khi user đồng ý, authorize code sẽ được trả về địa chỉ này. Địa chỉ này sẽ được sửa khi tích hợp, hiện tại ta để localhost.
Từ authorize code, chúng ta gửi request lên server để lấy refresh token.
Chuỗi json trả về có dạng như sau:
{
"access_token":"1/fFAGRNJru1FTz70BzhT3Zg",
"expires_in":3920,
"token_type":"Bearer",
"refresh_token":"1/xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
}
Do đó, chúng ta tạo class GoogleToken để deserialize chuỗi này.
Từ refresh token đã lưu, chúng ta gửi lên google server để lấy access token về, sử dụng mỗi khi gọi API.
Test thử chương trình:
Copy địa chỉ url, dán vào trình duyệt
Bấm Chấp nhận
Cắt lấy chuỗi authorize code, tạm thời lưu trữ lại.
Từ authorize code, ta request refresh token
Tiếp tục lưu trữ lại refreshToken (Giá trị refresh token này sẽ được lưu trong database, sau này những khi cần dùng thì lấy ta).
Ta tạo 1 APIWrapper, giá trị access token sẽ được khởi tạo dựa trên refresh token.
Access Token này sẽ được gắn vào request mỗi khi ta gọi API.
Nếu quá trình test thành công, chúc mừng quý vị, quý vị đã vượt qua 50% khó khăn nhất của bài demo.
Danh sách API của Google Calendar: https://developers.google.com/google-apps/calendar/v3/reference/
https://developers.google.com/google-apps/calendar/v3/reference/calendarList/list
Chúng ta tiếp tục viết 1 số hàm tương tác Calendar.
Để lấy danh sách calendar của user, chúng ta gọi API Calendar List. Kết quả trả về sẽ bao gồm 1 list có nhiều calendar.
Chuỗi Json của list
{
"kind": "calendar#calendarList",
"etag": etag,
"nextPageToken": string,
"items": [
calendarList Resource
]
}
Chuỗi json của calendar
{
"kind": "calendar#calendar",
"etag": etag,
"id": string,
"summary": string,
"description": string,
"location": string,
"timeZone": string
}
Chúng ta viết class Mapping như sau
Annotation Expose: Khi muốn add 1 calendar, ta chỉ cần 1 chuỗi json chứa summary, do đó ta chỉ serialize 1 trường duy nhất là summary
Các hàm trong API Wrapper.
Chạy thử các hàm vừa tạo trong class Main
Debug
Chuỗi calendarJson là chuỗi Json được gửi lên server
Result lấy về:
"{ "kind": "calendar#calendar", "etag": "\"zDYNyduc5vEaXlXz7scQOpE_a8Y/nfEulc1ZRqzvhfy85cWQ2taE2Qw\"", "id": "a9jsti3584npro07lk7recdl94@group.calendar.google.com", "summary": "New Calendar" } "
Chuỗi được parse thành class calendar.
Sau khi refresh lại google calendar
Output
Chúng ta tiếp tục test hàm getCalendarList
Chuỗi json được gửi về
"{ "items": [ { "id": "a9jsti3584npro07lk7recdl94@group.calendar.google.com", "summary": "New Calendar", "timeZone": "UTC" }, { "id": "huyhoang8a5@gmail.com", "summary": "huyhoang8a5@gmail.com", "timeZone": "Asia/Saigon" }, { "id": "#contacts@group.v.calendar.google.com", "summary": "Sự kiện và ngày sinh nhật của người liên hệ", "timeZone": "Asia/Saigon" }, { "id": "vi.vietnamese#holiday@group.v.calendar.google.com", "summary": "Các ngày lễ của Việt Nam", "timeZone": "Asia/Saigon" } ] } "
Các object sau khi parse.
Output.
Chúng ta tiếp tục viết hàm tương tác với phần event.
Chuỗi json trả về của phần Event: https://developers.google.com/google-apps/calendar/v3/reference/events#resource
Mapping class có phần phức tạp hơn, vì giá trị start, end của event cũng xem như 1 class.
Ta phải lấy thêm giá trị sequence, vì mỗi khi muốn update event, phải tăng giá trị sequence hiện tại lên 1, gửi về server.
Các hàm trong API Wrapper
Ta phải setDateFormat cho Gson để chuỗi Json format ra đúng chuẩn của Google. Số +7:00 để giờ được set phù hợp với múi giờ Việt Nam.
Bắt đầu test các hàm vừa viết.
Tạo 1 số event trong calendar mới
Debug ta được:
Test chức năng insert.
Chuỗi json được gửi lên server
"{"summary":"InsertEvt","start":{"dateTime":"2013-10-23T00:19:39.000+07:00"},"end":{"dateTime":"2013-10-23T02:19:39.000+07:00"},"sequence":0}"
Chuỗi json trả về:
"{ "kind": "calendar#event", "etag": "\"zDYNyduc5vEaXlXz7scQOpE_a8Y/MTM4MjQ2MjQ5MDcwOTAwMA\"", "id": "24ocdtir1fuklnt72v7n4tqj64", "status": "confirmed", "htmlLink": "https://www.google.com/calendar/event?eid=MjRvY2R0aXIxZnVrbG50NzJ2N240dHFqNjQgYTlqc3RpMzU4NG5wcm8wN2xrN3JlY2RsOTRAZw", "created": "2013-10-22T17:21:30.000Z", "updated": "2013-10-22T17:21:30.709Z", "summary": "InsertEvt", "creator": { "email": "huyhoang8a5@gmail.com", "displayName": "Pham Hoang" }, "organizer": { "email": "a9jsti3584npro07lk7recdl94@group.calendar.google.com", "displayName": "New Calendar", "self": true }, "start": { "dateTime": "2013-10-22T17:19:39Z" }, "end": { "dateTime": "2013-10-22T19:19:39Z" }, "iCalUID": "24ocdtir1fuklnt72v7n4tqj64@google.com", "sequence": 0, "reminders": { "useDefault": true } } "
Kết quả console
Kết quả trên google calendar
Test hàm delete, delete event ta vừa thêm
Kết quả:
Các hàm update quý vị tự test và tìm hiểu.
Chúc mừng quý vị đã hoàn thành phần khó nhất trong series 3 bài. Đây là 1 bài tutorial khá khó, đòi hỏi kiến thức về Restful Service, HTTP, JSON. Trong quá trình làm có thể sẽ gặp 1 số lỗi, quý vị có thể post lỗi lên để chúng tôi giúp đỡ.
Hẹn gặp lại quý vị ở bài kế tiếp, cũng là bài cuối cùng: Tích hợp API vừa viết vào ứng dụng đã tạo, để đồng bộ lịch của chúng ta với Google Calendar.
Không có nhận xét nào:
Đăng nhận xét