@ -0,0 +1,23 @@ | |||||
<!DOCTYPE html> | |||||
<!-- | |||||
This file is auto-generated by Vaadin. | |||||
--> | |||||
<html> | |||||
<head> | |||||
<meta charset="UTF-8" /> | |||||
<meta name="viewport" content="width=device-width, initial-scale=1" /> | |||||
<style> | |||||
body, #outlet { | |||||
height: 100vh; | |||||
width: 100%; | |||||
margin: 0; | |||||
} | |||||
</style> | |||||
<!-- index.ts is included here automatically (either by the dev server or during the build) --> | |||||
</head> | |||||
<body> | |||||
<!-- This outlet div is where the views are rendered --> | |||||
<div id="outlet"></div> | |||||
</body> | |||||
</html> |
@ -0,0 +1,75 @@ | |||||
package com.example.application.api; | |||||
import com.nimbusds.jose.shaded.gson.*; | |||||
import java.net.URI; | |||||
import java.net.http.HttpClient; | |||||
import java.net.http.HttpRequest; | |||||
import java.net.http.HttpResponse; | |||||
import java.util.*; | |||||
public class ApiRedmine { | |||||
private static final String REDMINE_URL = "http://localhost:3000"; | |||||
private static final String API_KEY = "cf3be6168e66c99892c6212ea0bc64e8ab1c6848"; | |||||
public static final Gson GSON = new Gson(); | |||||
public static String createIssue(Map<String, String> issueDetails) { | |||||
Map<String, Object> payload = new HashMap<>(); | |||||
payload.put("issue", issueDetails); | |||||
String jsonPayload = GSON.toJson(payload); | |||||
try { | |||||
//Crear la solicitud HTTP POST | |||||
HttpRequest request = HttpRequest.newBuilder() | |||||
.uri(URI.create(REDMINE_URL + "/issues.json")) | |||||
.header("Content-Type", "application/json") | |||||
.header("X-Redmine-API-Key", API_KEY) | |||||
.POST(HttpRequest.BodyPublishers.ofString(jsonPayload)) | |||||
.build(); | |||||
//Enviar la solicitud | |||||
HttpClient client = HttpClient.newHttpClient(); | |||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); | |||||
return response.body(); | |||||
} catch (Exception e) { | |||||
e.printStackTrace(); | |||||
return "Error: "+e.getMessage(); | |||||
} | |||||
} | |||||
public List<String> getTicketTypes() { | |||||
List<String> ticketTypes = new ArrayList<>(); | |||||
HttpClient client = HttpClient.newHttpClient(); | |||||
HttpRequest request = HttpRequest.newBuilder() | |||||
.uri(URI.create(REDMINE_URL + "/trackers.json")) | |||||
.header("Content-Type", "application/json") | |||||
.header("X-Redmine-API-Key", API_KEY) | |||||
.build(); | |||||
try { | |||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); | |||||
if (response.statusCode() == 200) { | |||||
String responseBody = response.body(); | |||||
ticketTypes = parseTicketTypes(responseBody); | |||||
} else { | |||||
System.err.println("Error en la respuesta: " + response.statusCode()); | |||||
} | |||||
} catch (Exception e) { | |||||
e.printStackTrace(); | |||||
} | |||||
return ticketTypes; | |||||
} | |||||
private List<String> parseTicketTypes(String json) { | |||||
List<String> names = new ArrayList<>(); | |||||
JsonObject jsonObject = JsonParser.parseString(json).getAsJsonObject(); | |||||
JsonArray trackers = jsonObject.getAsJsonArray("trackers"); | |||||
for (JsonElement trackerElement : trackers) { | |||||
JsonObject tracker = trackerElement.getAsJsonObject(); | |||||
String name = tracker.get("name").getAsString(); | |||||
names.add(name); | |||||
} | |||||
return names; | |||||
} | |||||
} |
@ -0,0 +1,56 @@ | |||||
package com.example.application.api; | |||||
import com.example.application.views.login.LoginView; | |||||
import com.vaadin.flow.component.UI; | |||||
import com.vaadin.flow.server.VaadinServletRequest; | |||||
import com.vaadin.flow.spring.security.VaadinWebSecurity; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.context.annotation.Configuration; | |||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; | |||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; | |||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; | |||||
import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider; | |||||
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; | |||||
@Configuration | |||||
@EnableWebSecurity | |||||
public class SecurityConfig extends VaadinWebSecurity { | |||||
private static final String LOGOUT_SUCCESS_URL = "/"; | |||||
@Override | |||||
protected void configure(HttpSecurity http) throws Exception { | |||||
super.configure(http); | |||||
setLoginView(http, LoginView.class); | |||||
} | |||||
@Autowired | |||||
public void configure(AuthenticationManagerBuilder auth) throws Exception { | |||||
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider("JUMAPACELAYA.GOB.MX", "ldap://172.16.0.1"); | |||||
provider.setConvertSubErrorCodesToExceptions(true); | |||||
provider.setUseAuthenticationRequestCredentials(true); | |||||
auth.authenticationProvider(provider); | |||||
} | |||||
public void logout() { | |||||
UI.getCurrent().getPage().setLocation(LOGOUT_SUCCESS_URL); | |||||
SecurityContextLogoutHandler logoutHandler = new SecurityContextLogoutHandler(); | |||||
logoutHandler.logout( | |||||
VaadinServletRequest.getCurrent().getHttpServletRequest(), null, null | |||||
); | |||||
} | |||||
} | |||||
/* | |||||
@Bean | |||||
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) { | |||||
LdapPasswordComparisonAuthenticationManagerFactory factory = new LdapPasswordComparisonAuthenticationManagerFactory(contextSource, new BCryptPasswordEncoder()); | |||||
factory.setUserDnPatterns("DC=JUMAPACELAYA,DC=GOB,DC=MX"); | |||||
factory.setPasswordAttribute("pwd"); //pwd | |||||
return factory.createAuthenticationManager(); | |||||
} | |||||
*/ | |||||
@ -1,34 +0,0 @@ | |||||
package com.example.application.views.about; | |||||
import com.example.application.views.MainLayout; | |||||
import com.vaadin.flow.component.html.H2; | |||||
import com.vaadin.flow.component.html.Image; | |||||
import com.vaadin.flow.component.html.Paragraph; | |||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout; | |||||
import com.vaadin.flow.router.PageTitle; | |||||
import com.vaadin.flow.router.Route; | |||||
import com.vaadin.flow.theme.lumo.LumoUtility.Margin; | |||||
@PageTitle("About") | |||||
@Route(value = "about", layout = MainLayout.class) | |||||
public class AboutView extends VerticalLayout { | |||||
public AboutView() { | |||||
setSpacing(false); | |||||
Image img = new Image("images/empty-plant.png", "placeholder plant"); | |||||
img.setWidth("200px"); | |||||
add(img); | |||||
H2 header = new H2("This place intentionally left empty"); | |||||
header.addClassNames(Margin.Top.XLARGE, Margin.Bottom.MEDIUM); | |||||
add(header); | |||||
add(new Paragraph("It’s a place where you can grow your own UI 🤗")); | |||||
setSizeFull(); | |||||
setJustifyContentMode(JustifyContentMode.CENTER); | |||||
setDefaultHorizontalComponentAlignment(Alignment.CENTER); | |||||
getStyle().set("text-align", "center"); | |||||
} | |||||
} |
@ -0,0 +1,103 @@ | |||||
package com.example.application.views.crearnuevoticket; | |||||
import com.example.application.api.ApiRedmine; | |||||
import com.example.application.views.MainLayout; | |||||
import com.nimbusds.jose.shaded.gson.Gson; | |||||
import com.nimbusds.jose.shaded.gson.JsonObject; | |||||
import com.nimbusds.jose.shaded.gson.JsonParser; | |||||
import com.vaadin.flow.component.button.Button; | |||||
import com.vaadin.flow.component.button.ButtonVariant; | |||||
import com.vaadin.flow.component.combobox.ComboBox; | |||||
import com.vaadin.flow.component.html.H2; | |||||
import com.vaadin.flow.component.notification.Notification; | |||||
import com.vaadin.flow.component.notification.NotificationVariant; | |||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout; | |||||
import com.vaadin.flow.component.textfield.TextArea; | |||||
import com.vaadin.flow.component.textfield.TextField; | |||||
import com.vaadin.flow.router.Route; | |||||
import jakarta.annotation.security.PermitAll; | |||||
import java.util.HashMap; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
@Route(value="home", layout = MainLayout.class) | |||||
@PermitAll | |||||
public class CrearnuevoTicketView extends VerticalLayout { | |||||
public CrearnuevoTicketView() { | |||||
TextField projectId = new TextField("Project ID"); | |||||
projectId.setReadOnly(true); | |||||
//Combo de los tipos de tickets | |||||
ComboBox<String> tipoTickets = new ComboBox<>("Tipo de ticket"); | |||||
ApiRedmine api = new ApiRedmine(); | |||||
List<String> types = api.getTicketTypes(); | |||||
tipoTickets.setItems(types); | |||||
//Campo de texto para el asunto | |||||
TextField asunto = new TextField("Asunto"); | |||||
asunto.setWidth("700px"); | |||||
//Campo de texto para la descripcion | |||||
TextArea descripcion = new TextArea("Descripcion"); | |||||
descripcion.setWidth("1000px"); | |||||
descripcion.setHeight("300px"); | |||||
//Respuestas Json para verificar posibles errores al enviar los nuevos tickets no visibles en la interfaz | |||||
TextArea jsonOutput = new TextArea("JSON Output"); | |||||
jsonOutput.setReadOnly(true); | |||||
TextArea responseField = new TextArea("Response"); | |||||
responseField.setReadOnly(true); | |||||
Button createButton = new Button("Enviar ticket", event -> { | |||||
Map<String, String> issueDetails = new HashMap<>(); | |||||
issueDetails.put("project_id", "proyecto-de-prueba"); | |||||
issueDetails.put("subject", asunto.getValue()); | |||||
issueDetails.put("description", descripcion.getValue()); | |||||
String response = ApiRedmine.createIssue(issueDetails); | |||||
if (response.startsWith("{\"issue\":")) { | |||||
//Aqui extraigo el numero del ticket de la respuesta json | |||||
JsonObject jsonResponse = JsonParser.parseString(response).getAsJsonObject(); | |||||
int issueNumber = jsonResponse.getAsJsonObject("issue").get("id").getAsInt(); | |||||
String notificationMessage = "¡Su ticket se ha enviado corrrectamente! Numero de ticket: #" + issueNumber; | |||||
Notification issueNtf = new Notification(notificationMessage, 5000, Notification.Position.MIDDLE); | |||||
issueNtf.addThemeVariants(NotificationVariant.LUMO_SUCCESS); | |||||
issueNtf.open(); | |||||
//Limpiando los campos | |||||
asunto.clear(); | |||||
descripcion.clear(); | |||||
tipoTickets.clear(); | |||||
} else { | |||||
Notification errNtf = Notification.show("Ha ocurrido un error al enviar el ticket: "+response, 5000, Notification.Position.MIDDLE); | |||||
errNtf.addThemeVariants(NotificationVariant.LUMO_ERROR); | |||||
} | |||||
}); | |||||
createButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY); | |||||
VerticalLayout fieldsLayout = new VerticalLayout(asunto, descripcion); | |||||
fieldsLayout.setAlignItems(Alignment.CENTER); | |||||
VerticalLayout buttonLayout = new VerticalLayout(createButton); | |||||
buttonLayout.setAlignItems(Alignment.END); | |||||
buttonLayout.setMargin(true); | |||||
add(new H2("Crear nuevo ticket"),projectId, tipoTickets, fieldsLayout,buttonLayout/*,jsonOutput,responseField*/); | |||||
} | |||||
//Metodo para convertir un Map a json usando la libreria GSON | |||||
private static String mapToJson(Map<String, String> map) { | |||||
Gson gson = new Gson(); | |||||
return gson.toJson(map); | |||||
} | |||||
} |
@ -1,35 +0,0 @@ | |||||
package com.example.application.views.helloworld; | |||||
import com.example.application.views.MainLayout; | |||||
import com.vaadin.flow.component.Key; | |||||
import com.vaadin.flow.component.button.Button; | |||||
import com.vaadin.flow.component.notification.Notification; | |||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout; | |||||
import com.vaadin.flow.component.textfield.TextField; | |||||
import com.vaadin.flow.router.PageTitle; | |||||
import com.vaadin.flow.router.Route; | |||||
import com.vaadin.flow.router.RouteAlias; | |||||
@PageTitle("Hello World") | |||||
@Route(value = "hello", layout = MainLayout.class) | |||||
@RouteAlias(value = "", layout = MainLayout.class) | |||||
public class HelloWorldView extends HorizontalLayout { | |||||
private TextField name; | |||||
private Button sayHello; | |||||
public HelloWorldView() { | |||||
name = new TextField("Your name"); | |||||
sayHello = new Button("Say hello"); | |||||
sayHello.addClickListener(e -> { | |||||
Notification.show("Hello " + name.getValue()); | |||||
}); | |||||
sayHello.addClickShortcut(Key.ENTER); | |||||
setMargin(true); | |||||
setVerticalComponentAlignment(Alignment.END, name, sayHello); | |||||
add(name, sayHello); | |||||
} | |||||
} |
@ -0,0 +1,29 @@ | |||||
package com.example.application.views.login; | |||||
import com.vaadin.flow.component.html.H1; | |||||
import com.vaadin.flow.component.login.LoginForm; | |||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout; | |||||
import com.vaadin.flow.router.Route; | |||||
import com.vaadin.flow.server.auth.AnonymousAllowed; | |||||
@Route("login") | |||||
@AnonymousAllowed | |||||
public class LoginView extends VerticalLayout { | |||||
public LoginView() { | |||||
setSizeFull(); | |||||
setAlignItems(Alignment.CENTER); | |||||
setJustifyContentMode(JustifyContentMode.CENTER); | |||||
//Formulario de login | |||||
var login = new LoginForm(); | |||||
login.setAction("login"); | |||||
add( | |||||
new H1("Soporte tecnico T.I"), | |||||
login | |||||
); | |||||
} | |||||
} | |||||
@ -0,0 +1,26 @@ | |||||
package com.example.application.views.tickets; | |||||
import com.example.application.views.MainLayout; | |||||
import com.vaadin.flow.component.html.Div; | |||||
import com.vaadin.flow.component.html.H2; | |||||
import com.vaadin.flow.component.orderedlayout.VerticalLayout; | |||||
import com.vaadin.flow.router.Route; | |||||
import com.vaadin.flow.server.auth.AnonymousAllowed; | |||||
@Route(value="mytickets", layout = MainLayout.class) | |||||
@AnonymousAllowed | |||||
public class MisTicketsView extends VerticalLayout { | |||||
public MisTicketsView() { | |||||
setSizeFull(); | |||||
Div banner = new Div(); | |||||
banner.setText("Aqui es donde el usuario vera sus tickets creados"); | |||||
banner.getStyle() | |||||
.set("display", "flex") | |||||
.set("justify-content", "center") | |||||
.set("align-items", "center") | |||||
.set("height", "100%"); | |||||
add(new H2("Mis tickets"), banner); | |||||
} | |||||
} |