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