Thứ Tư, 31 tháng 8, 2016

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

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

Your application sends a token request to the Google Authorization Server, receives an authorization code, 
exchanges the code for a token, and uses the token to call a Google API endpoint.

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