L'API di Google Calendar v3 restituisce sempre BadRequest durante la creazione di eventi
Ho creato un calendario condiviso e voglio aggiungere eventi al calendario.
Ho creato un progetto e impostato un account di servizio [email protected]
.
Quindi ho condiviso il calendario con l'account di servizio come proprietario .
Poi ho notato
L'account di servizio deve aggiungere manualmente il calendario condiviso
come descritto qui https://stackoverflow.com/a/62232361/298430 e https://issuetracker.google.com/issues/148804709
Quindi ho scritto un codice:
@Test
fun addCalendarToServiceAccount() {
val calList1: CalendarList = calendar.calendarList().list().execute()
logger.info("calList1 = {}", calList1)
val inserted = calendar.calendarList().insert(CalendarListEntry().setId(calendarId)).execute()
logger.info("inserted = {}", inserted)
val calList2: CalendarList = calendar.calendarList().list().execute()
logger.info("calList2 = {}", calList2)
}
Funziona perfettamente. Quando viene chiamato per la prima volta, vedo che calList1
è vuoto e calList2
contiene qualcosa.
Quindi inserisco manualmente un evento nel calendario (con interfaccia WEB del calendario di Google), voglio controllare se riesco a recuperare l'evento:
@Test
fun listEvents() {
val events: Events = calendar.events().list(calendarId).execute()
logger.info("events = {}", events)
events.items.forEachIndexed { index, e ->
logger.info("Event [index = {}] , event = {}", index, e)
}
}
Funziona anche.
{
"accessRole":"owner",
"defaultReminders":[
],
"etag":"\"xxx\"",
"items":[
{
"created":"2020-08-17T17:51:21.000Z",
"creator":{
"email":"[email protected]"
},
"end":{
"date":"2020-08-20"
},
"etag":"\"xxx\"",
"htmlLink":"https://www.google.com/calendar/event?eid=xxx",
"iCalUID":"[email protected]",
"id":"xxx",
"kind":"calendar#event",
"organizer":{
"displayName":"xxx",
"email":"[email protected]",
"self":true
},
"reminders":{
"useDefault":false
},
"sequence":0,
"start":{
"date":"2020-08-19"
},
"status":"confirmed",
"summary":"xxx test1",
"transparency":"transparent",
"updated":"2020-08-18T01:07:54.441Z"
}
],
"kind":"calendar#events",
"nextSyncToken":"xxx",
"summary":"xxx",
"timeZone":"Asia/Taipei",
"updated":"2020-08-18T01:07:54.688Z"
}
Quindi voglio inserire a livello di codice qualcosa, come mostra l'esempio dell'API:
@Test
fun testInsertEvent() {
val now = LocalDateTime.now().withSecond(0).withNano(0)
val zoneId = ZoneId.of("Asia/Taipei")
val fromDate = Date.from(now.atZone(zoneId).toInstant())
val endDate = Date.from(now.plusMinutes(60).atZone(zoneId).toInstant())
val event = Event()
.setSummary("Google I/O 2015")
.setLocation("800 Howard St., San Francisco, CA 94103")
.setDescription("A chance to hear more about Google's developer products.")
.setStart(EventDateTime().setDate(DateTime(fromDate, TimeZone.getTimeZone(zoneId))))
.setEnd(EventDateTime().setDate(DateTime(endDate, TimeZone.getTimeZone(zoneId))))
logger.info("before insert event : {}", event)
val eventResult: Event = calendar.events().insert(calendarId, event).execute()
logger.info("eventResult = {}", eventResult)
}
Riesco a vedere il client realmente POST all'endpoint google'e:

Il corpo è:
{
"description":"A chance to hear more about Google's developer products.",
"end":{
"date":"2020-08-18T11:32:00.000+08:00"
},
"location":"800 Howard St., San Francisco, CA 94103",
"start":{
"date":"2020-08-18T10:32:00.000+08:00"
},
"summary":"Google I/O 2015"
}
Ma Google ha appena risposto 400 BadRequest, senza alcuna ulteriore descrizione:
2020-08-18 10:32:15.974 [main] INFO c.g.a.c.h.HttpResponse - -------------- RESPONSE --------------
HTTP/1.1 400 Bad Request
Transfer-Encoding: chunked
Alt-Svc: h3-29=":443"; ma=2592000,h3-27=":443"; ma=2592000,h3-T050=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"
Server: ESF
X-Content-Type-Options: nosniff
Pragma: no-cache
Date: Tue, 18 Aug 2020 02:32:15 GMT
X-Frame-Options: SAMEORIGIN
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Encoding: gzip
Vary: Referer
Vary: X-Origin
Vary: Origin
Expires: Mon, 01 Jan 1990 00:00:00 GMT
X-XSS-Protection: 0
Content-Type: application/json; charset=UTF-8
2020-08-18 10:32:15.980 [main] INFO c.g.a.c.u.LoggingByteArrayOutputStream - Total: 171 bytes
2020-08-18 10:32:15.980 [main] INFO c.g.a.c.u.LoggingByteArrayOutputStream - {
"error": {
"errors": [
{
"domain": "global",
"reason": "badRequest",
"message": "Bad Request"
}
],
"code": 400,
"message": "Bad Request"
}
}
Sto usando la stessa istanza di calendario , posso con successo addCalendarToServiceAccount()
(come owner
) e listEvents()
. Ma cosa non va quando si inserisce un evento? Mi sono perso qualcosa?
Gli altri campi vengono inizializzati come segue:
@Value("\${google.calendar.id}") private lateinit var calendarId: String @Value("\${google.calendar.apiKey}")
private lateinit var apiKey : String
private val httpTransport: HttpTransport by lazy {
GoogleNetHttpTransport.newTrustedTransport()
}
private val jacksonFactory: JsonFactory by lazy {
JacksonFactory.getDefaultInstance()
}
private val saCredentials: GoogleCredentials by lazy {
javaClass.getResourceAsStream("/chancer-d1de03c4c25a.json").use { iStream ->
ServiceAccountCredentials.fromStream(iStream)
.createScoped(listOf(
"https://www.googleapis.com/auth/cloud-platform",
*CalendarScopes.all().toTypedArray()
))
}.apply {
refreshIfExpired()
}
}
private val requestInitializer: HttpRequestInitializer by lazy {
HttpCredentialsAdapter(saCredentials)
}
private val calendar: Calendar by lazy {
Calendar.Builder(httpTransport, jacksonFactory, requestInitializer)
.build()
}
Ambienti:
<java.version>1.8</java.version>
<kotlin.version>1.4.0</kotlin.version>
<dependency>
<groupId>com.google.api-client</groupId>
<artifactId>google-api-client</artifactId>
<version>1.30.10</version>
</dependency>
<dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-calendar</artifactId>
<version>v3-rev20200610-1.30.10</version>
</dependency>
<dependency>
<groupId>com.google.auth</groupId>
<artifactId>google-auth-library-oauth2-http</artifactId>
<version>0.21.1</version>
</dependency>
Risposte
Risposta:
Devi usare start.dateTime
and end.dateTime
invece di start.date
eend.date
Correzione:
Come da documentazione :
end.date
: La data, nel formato "aaaa-mm-gg", se si tratta di un evento che dura tutto il giorno.
end.dateTime
: L'ora, come valore combinato di data e ora (formattato secondo RFC3339). È necessario un offset del fuso orario a meno che non venga specificato esplicitamente un fuso orario in timeZone .
start.date
: La data, nel formato "aaaa-mm-gg", se si tratta di un evento che dura tutto il giorno.
start.dateTime
: L'ora, come valore combinato di data e ora (formattato secondo RFC3339). È necessario un offset del fuso orario a meno che non venga specificato esplicitamente un fuso orario in timeZone .
Pertanto, è necessario modificare il metodo di impostazione di data e ora da:
EventDateTime().setDate(DateTime(fromDate, TimeZone.getTimeZone(zoneId))))
per:
EventDateTime().setDateTime(DateTime(fromDate, TimeZone.getTimeZone(zoneId))))
Che cambierà il corpo della richiesta in:
{
"description": "A chance to hear more about Google's developer products.",
"end": {
"dateTime": "2020-08-18T11:32:00.000+08:00" // modified
},
"location": "800 Howard St., San Francisco, CA 94103",
"start": {
"dateTime": "2020-08-18T10:32:00.000+08:00" // modified
},
"summary": "Google I/O 2015"
}
Puoi vedere la documentazione per questo metodo qui .
Spero che questo ti sia di aiuto!
Riferimenti:
- Eventi: inserire | API Calendar | Google Developers
- RFC 3339 - Data e ora su Internet: timestamp
- EventDateTime (API Calendar v3-rev20200610-1.30.10)