@ -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); | |||
} | |||
} |