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