@ -3,6 +3,8 @@ package mx.gob.jumapacelaya.api;
import com.google.gson.* ;
import com.google.gson.* ;
import mx.gob.jumapacelaya.models.RedmineUser ;
import mx.gob.jumapacelaya.models.RedmineUser ;
import mx.gob.jumapacelaya.models.Ticket ;
import mx.gob.jumapacelaya.models.Ticket ;
import org.slf4j.Logger ;
import org.slf4j.LoggerFactory ;
import org.springframework.beans.factory.annotation.Value ;
import org.springframework.beans.factory.annotation.Value ;
import org.springframework.stereotype.Component ;
import org.springframework.stereotype.Component ;
@ -12,6 +14,9 @@ import java.net.http.HttpClient;
import java.net.http.HttpRequest ;
import java.net.http.HttpRequest ;
import java.net.http.HttpResponse ;
import java.net.http.HttpResponse ;
import java.time.LocalDate ;
import java.time.LocalDate ;
import java.time.LocalDateTime ;
import java.time.OffsetDateTime ;
import java.time.ZoneId ;
import java.time.format.DateTimeFormatter ;
import java.time.format.DateTimeFormatter ;
import java.time.format.DateTimeParseException ;
import java.time.format.DateTimeParseException ;
import java.util.ArrayList ;
import java.util.ArrayList ;
@ -21,11 +26,11 @@ import java.util.Map;
@Component
@Component
public class RedmineClient {
public class RedmineClient {
private static final Logger log = LoggerFactory . getLogger ( RedmineClient . class ) ;
private static final int PAGE_SIZE = 25 ;
/ / private static final int PAGE_SIZE = 25 ;
static String REDMINE_URL ;
static String REDMINE_URL ;
static String API_KEY ;
static String API_KEY ;
public static final Gson GSON = new Gson ( ) ;
public RedmineClient ( @Value ( "${redmine.url}" ) String redmineUrl , @Value ( "${redmine.api_key}" ) String apiKey ) {
public RedmineClient ( @Value ( "${redmine.url}" ) String redmineUrl , @Value ( "${redmine.api_key}" ) String apiKey ) {
REDMINE_URL = redmineUrl ;
REDMINE_URL = redmineUrl ;
@ -52,7 +57,9 @@ public class RedmineClient {
try {
try {
HttpResponse < String > response = client . send ( request , HttpResponse . BodyHandlers . ofString ( ) ) ;
HttpResponse < String > response = client . send ( request , HttpResponse . BodyHandlers . ofString ( ) ) ;
if ( response . statusCode ( ) = = 200 ) {
if ( response . statusCode ( ) = = 200 ) {
tickets . addAll ( parseTickets ( response . body ( ) ) ) ;
String responseBody = response . body ( ) ;
log . info ( responseBody ) ;
tickets . addAll ( parseTickets ( responseBody ) ) ;
} else {
} else {
System . err . println ( "Error en la respuesta: " + response . statusCode ( ) ) ;
System . err . println ( "Error en la respuesta: " + response . statusCode ( ) ) ;
}
}
@ -133,16 +140,12 @@ public class RedmineClient {
for ( JsonElement issueElement : issues ) {
for ( JsonElement issueElement : issues ) {
JsonObject issue = issueElement . getAsJsonObject ( ) ;
JsonObject issue = issueElement . getAsJsonObject ( ) ;
/ / Verifica y obtiene el ID
/ / ID , subject , descripción
int id = issue . has ( "id" ) & & ! issue . get ( "id" ) . isJsonNull ( ) ? issue . get ( "id" ) . getAsInt ( ) : 0 ;
int id = issue . has ( "id" ) & & ! issue . get ( "id" ) . isJsonNull ( ) ? issue . get ( "id" ) . getAsInt ( ) : 0 ;
/ / Verifica y obtiene el subject
String subject = issue . has ( "subject" ) & & ! issue . get ( "subject" ) . isJsonNull ( ) ? issue . get ( "subject" ) . getAsString ( ) : "" ;
String subject = issue . has ( "subject" ) & & ! issue . get ( "subject" ) . isJsonNull ( ) ? issue . get ( "subject" ) . getAsString ( ) : "" ;
/ / Verifica y obtiene la descripción
String description = issue . has ( "description" ) & & ! issue . get ( "description" ) . isJsonNull ( ) ? issue . get ( "description" ) . getAsString ( ) : "" ;
String description = issue . has ( "description" ) & & ! issue . get ( "description" ) . isJsonNull ( ) ? issue . get ( "description" ) . getAsString ( ) : "" ;
/ / Verifica y obtiene el s tatus
/ / S tatus
String status = "Unknown" ;
String status = "Unknown" ;
if ( issue . has ( "status" ) & & ! issue . get ( "status" ) . isJsonNull ( ) ) {
if ( issue . has ( "status" ) & & ! issue . get ( "status" ) . isJsonNull ( ) ) {
JsonObject statusObject = issue . getAsJsonObject ( "status" ) ;
JsonObject statusObject = issue . getAsJsonObject ( "status" ) ;
@ -151,57 +154,21 @@ public class RedmineClient {
}
}
}
}
/ / Verifica y obtiene la fecha de creación
String dateString = issue . has ( "created_on" ) & & ! issue . get ( "created_on" ) . isJsonNull ( ) ? issue . get ( "created_on" ) . getAsString ( ) : "" ;
LocalDate dateCreate = null ;
if ( ! dateString . isEmpty ( ) ) {
try {
DateTimeFormatter formatter = DateTimeFormatter . ISO_DATE_TIME ;
dateCreate = LocalDate . parse ( dateString , formatter ) ;
} catch ( DateTimeParseException e ) {
System . err . println ( "Error al parsear la fecha: " + dateString ) ;
e . printStackTrace ( ) ;
}
}
/ / Verifica y obtiene la fecha de cierre
String closeDateString = issue . has ( "closed_on" ) & & ! issue . get ( "closed_on" ) . isJsonNull ( ) ? issue . get ( "closed_on" ) . getAsString ( ) : "" ;
LocalDate dateClose = null ;
if ( ! closeDateString . isEmpty ( ) ) {
try {
DateTimeFormatter formatter = DateTimeFormatter . ISO_DATE_TIME ;
dateClose = LocalDate . parse ( closeDateString , formatter ) ;
} catch ( DateTimeParseException e ) {
System . err . println ( "Error al parsear la fecha de cierre: " + closeDateString ) ;
e . printStackTrace ( ) ;
}
}
/ / Verifica y obtiene la fecha de actualizacion
String updateDateString = issue . has ( "updated_on" ) & & ! issue . get ( "updated_on" ) . isJsonNull ( ) ? issue . get ( "updated_on" ) . getAsString ( ) : "" ;
LocalDate dateUpdate = null ;
if ( ! updateDateString . isEmpty ( ) ) {
try {
DateTimeFormatter formatter = DateTimeFormatter . ISO_DATE_TIME ;
dateUpdate = LocalDate . parse ( updateDateString , formatter ) ;
} catch ( DateTimeParseException e ) {
System . err . println ( "Error al parsear la fecha de actualización: " + updateDateString ) ;
e . printStackTrace ( ) ;
}
}
/ / Parse fechas correctamente
LocalDateTime dateCreate = parseDateTime ( issue , "created_on" ) ;
LocalDateTime dateUpdate = parseDateTime ( issue , "updated_on" ) ;
LocalDateTime dateClose = parseDateTime ( issue , "closed_on" ) ;
/ / Autor
Ticket . User autor = null ;
Ticket . User autor = null ;
if ( issue . has ( "author" ) & & ! issue . get ( "author" ) . isJsonNull ( ) ) {
if ( issue . has ( "author" ) & & ! issue . get ( "author" ) . isJsonNull ( ) ) {
JsonObject authorObj = issue . getAsJsonObject ( "author" ) ;
JsonObject authorObj = issue . getAsJsonObject ( "author" ) ;
if ( authorObj . has ( "name" ) & & ! authorObj . get ( "name" ) . isJsonNull ( ) ) {
if ( authorObj . has ( "name" ) & & ! authorObj . get ( "name" ) . isJsonNull ( ) ) {
String authorName = authorObj . get ( "name" ) . getAsString ( ) ;
autor = new Ticket . User ( authorName ) ;
autor = new Ticket . User ( authorObj . get ( "name" ) . getAsString ( ) ) ;
}
}
}
}
/ / Verifica y obtiene el ID del tipo de ticket
/ / Tracker ID
Integer trackerId = null ;
Integer trackerId = null ;
if ( issue . has ( "tracker" ) & & ! issue . get ( "tracker" ) . isJsonNull ( ) ) {
if ( issue . has ( "tracker" ) & & ! issue . get ( "tracker" ) . isJsonNull ( ) ) {
JsonObject trackerObject = issue . getAsJsonObject ( "tracker" ) ;
JsonObject trackerObject = issue . getAsJsonObject ( "tracker" ) ;
@ -210,14 +177,24 @@ public class RedmineClient {
}
}
}
}
/ / Agrega el ticket a la lista
tickets . add ( new Ticket ( id , subject , description , status ,
/ / Crear ticket
Ticket ticketObj = new Ticket (
id ,
subject ,
description ,
status ,
dateCreate ! = null ? dateCreate . toString ( ) : "" ,
dateCreate ! = null ? dateCreate . toString ( ) : "" ,
dateClose ! = null ? dateClose . toString ( ) : "" ,
dateClose ! = null ? dateClose . toString ( ) : "" ,
dateUpdate ! = null ? dateUpdate . toString ( ) : "" ,
dateUpdate ! = null ? dateUpdate . toString ( ) : "" ,
autor ,
autor ,
trackerId , "Tipo Desconocido" ) ) ;
trackerId ,
"Tipo Desconocido"
) ;
/ / Log para verificar JSON de ticket
/ / System . out . println ( GSON . toJson ( ticketObj ) ) ;
tickets . add ( ticketObj ) ;
}
}
} else {
} else {
System . out . println ( "La respuesta JSON no contiene la clave 'issues'" ) ;
System . out . println ( "La respuesta JSON no contiene la clave 'issues'" ) ;
@ -229,6 +206,7 @@ public class RedmineClient {
return tickets ;
return tickets ;
}
}
public RedmineUser getMyAccount ( String username ) {
public RedmineUser getMyAccount ( String username ) {
HttpRequest request = HttpRequest . newBuilder ( )
HttpRequest request = HttpRequest . newBuilder ( )
. uri ( URI . create ( REDMINE_URL + "/my/account.json" ) )
. uri ( URI . create ( REDMINE_URL + "/my/account.json" ) )
@ -306,7 +284,7 @@ public class RedmineClient {
public RedmineUser getUserByUsername ( String username ) {
public RedmineUser getUserByUsername ( String username ) {
HttpClient client = HttpClient . newHttpClient ( ) ;
HttpClient client = HttpClient . newHttpClient ( ) ;
HttpRequest request = HttpRequest . newBuilder ( )
HttpRequest request = HttpRequest . newBuilder ( )
. uri ( URI . create ( REDMINE_URL + "/users.json?name=" + username ) )
. uri ( URI . create ( REDMINE_URL + "/users.json?name=" + username ) )
. header ( "Content-Type" , "application/json" )
. header ( "Content-Type" , "application/json" )
. header ( "X-Redmine-API-Key" , API_KEY )
. header ( "X-Redmine-API-Key" , API_KEY )
. build ( ) ;
. build ( ) ;
@ -369,4 +347,37 @@ public class RedmineClient {
System . err . println ( response . body ( ) ) ;
System . err . println ( response . body ( ) ) ;
}
}
}
}
private static final Gson GSON = new GsonBuilder ( )
. registerTypeAdapter ( LocalDateTime . class ,
( JsonSerializer < LocalDateTime > ) ( src , typeOfSrc , context ) - >
new JsonPrimitive ( src . toString ( ) ) )
. registerTypeAdapter ( LocalDateTime . class ,
( JsonDeserializer < LocalDateTime > ) ( json , typeOf , context ) - >
LocalDateTime . parse ( json . getAsString ( ) ) )
. setPrettyPrinting ( )
. create ( ) ;
/ / Método auxiliar para parsear echas con hora
private LocalDateTime parseDateTime ( JsonObject issue , String fieldName ) {
if ( ! issue . has ( fieldName ) | | issue . get ( fieldName ) . isJsonNull ( ) ) {
return null ;
}
String dateStr = issue . get ( fieldName ) . getAsString ( ) ;
try {
/ / Si viene con 'Z' al final ( UTC ) , convertir a LocalDateTime
if ( dateStr . endsWith ( "Z" ) ) {
return OffsetDateTime . parse ( dateStr )
. atZoneSameInstant ( ZoneId . systemDefault ( ) )
. toLocalDateTime ( ) ;
} else {
return LocalDateTime . parse ( dateStr ) ;
}
} catch ( DateTimeParseException ex ) {
System . err . println ( "Error al parsear fecha '" + fieldName + "': " + dateStr ) ;
return null ;
}
}
}
}