La API de Google Calendar v3 siempre devuelve BadRequest al crear eventos
Creé un calendario compartido y quiero agregar eventos al calendario.
Creé un proyecto y configuré una cuenta de servicio [email protected]
.
Luego compartí el calendario con la cuenta de servicio como propietario .
Entonces me di cuenta
La cuenta de servicio debe agregar manualmente el calendario compartido
como se describe aquí https://stackoverflow.com/a/62232361/298430 y https://issuetracker.google.com/issues/148804709
Entonces escribí un código:
@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)
}
Funciona perfectamente. Cuando se llama por primera vez, puedo ver que calList1
está vacío y calList2
contiene algo.
Luego inserto manualmente un evento en el calendario (con la interfaz de usuario WEB del calendario de Google), quiero verificar si puedo recuperar el 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)
}
}
También funciona.
{
"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"
}
Luego quiero insertar algo mediante programación, como muestra el ejemplo de 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)
}
Puedo ver que el cliente realmente POST en el punto final de Google:

El cuerpo es:
{
"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"
}
Pero Google solo respondió 400 BadRequest, sin más descripción:
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"
}
}
Estoy usando la misma instancia de calendario , puedo addCalendarToServiceAccount()
(como owner
) y listEvents()
. Pero, ¿qué sale mal al insertar un evento? ¿Yo me perdí algo?
Otros campos se inicializan de la siguiente manera:
@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()
}
Ambientes:
<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>
Respuestas
Responder:
Necesita usar start.dateTime
y en end.dateTime
lugar de start.date
yend.date
Reparar:
Según la documentación :
end.date
: La fecha, en el formato "aaaa-mm-dd", si se trata de un evento de todo el día.
end.dateTime
: La hora, como un valor combinado de fecha y hora (formateado de acuerdo con RFC3339). Se requiere un desplazamiento de zona horaria a menos que se especifique explícitamente una zona horaria en timeZone .
start.date
: La fecha, en el formato "aaaa-mm-dd", si se trata de un evento de todo el día.
start.dateTime
: La hora, como un valor combinado de fecha y hora (formateado de acuerdo con RFC3339). Se requiere un desplazamiento de zona horaria a menos que se especifique explícitamente una zona horaria en timeZone .
Por lo tanto, debe cambiar su método de configuración de fecha y hora de:
EventDateTime().setDate(DateTime(fromDate, TimeZone.getTimeZone(zoneId))))
a:
EventDateTime().setDateTime(DateTime(fromDate, TimeZone.getTimeZone(zoneId))))
Lo que cambiará el cuerpo de la solicitud a:
{
"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"
}
Puede ver la documentación de este método aquí .
¡Espero que esto te sea útil!
Referencias:
- Eventos: insertar | API de calendario | Desarrolladores de Google
- RFC 3339 - Fecha y hora en Internet: marcas de tiempo
- EventDateTime (API de calendario v3-rev20200610-1.30.10)