diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/pom.xml b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/pom.xml new file mode 100644 index 00000000000..fe919052144 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/pom.xml @@ -0,0 +1,338 @@ + + + + + + + org.wso2.carbon.devicemgt + grafana-mgt + 5.0.6-SNAPSHOT + ../pom.xml + + + 4.0.0 + io.entgra.analytics.mgt.grafana.proxy.api + war + Entgra - Grafana Proxy API + Entgra - Grafana Proxy API + http://entgra.io + + + + + maven-compiler-plugin + + 1.8 + 1.8 + + + + maven-war-plugin + + WEB-INF/lib/*cxf*.jar + api#grafana-mgt#v1.0 + + + + + + + + deploy + + compile + + + org.apache.maven.plugins + maven-antrun-plugin + 1.7 + + + compile + + run + + + + + + + + + + + + + + + + + + + + + org.testng + testng + test + + + org.springframework + spring-web + provided + + + org.apache.cxf + cxf-bundle + 3.0.0-milestone2 + test + + + org.apache.cxf + cxf-bundle-jaxrs + provided + + + commons-httpclient.wso2 + commons-httpclient + provided + + + org.wso2.carbon + org.wso2.carbon.utils + provided + + + org.wso2.carbon.identity.framework + org.wso2.carbon.user.mgt + provided + + + org.slf4j + slf4j-api + + + org.slf4j + jcl-over-slf4j + + + + + org.wso2.carbon + org.wso2.carbon.logging + provided + + + org.wso2.carbon.identity.inbound.auth.oauth2 + org.wso2.carbon.identity.oauth.stub + provided + + + org.apache.axis2.wso2 + axis2-client + + + + + org.json.wso2 + json + + + commons-codec.wso2 + commons-codec + provided + + + org.wso2.carbon.devicemgt + org.wso2.carbon.certificate.mgt.core + provided + + + org.wso2.carbon.devicemgt + org.wso2.carbon.device.mgt.core + + + + + io.swagger + swagger-annotations + + + io.swagger + swagger-core + + + com.fasterxml.jackson.module + jackson-module-jaxb-annotations + + + org.slf4j + slf4j-api + + + org.wso2.orbit.com.fasterxml.jackson.core + jackson-core + + + + + io.swagger + swagger-jaxrs + + + com.fasterxml.jackson.module + jackson-module-jaxb-annotations + + + org.slf4j + slf4j-api + + + org.wso2.orbit.com.fasterxml.jackson.core + jackson-core + + + + + javax.servlet + javax.servlet-api + provided + + + org.wso2.carbon.devicemgt + org.wso2.carbon.apimgt.annotations + provided + + + org.wso2.orbit.com.fasterxml.jackson.core + jackson-annotations + + + org.hibernate + hibernate-validator + + + javax.ws.rs + javax.ws.rs-api + provided + + + javax.ws.rs + jsr311-api + provided + + + org.wso2.carbon.commons + org.wso2.carbon.application.mgt.stub + provided + + + org.wso2.carbon.analytics + org.wso2.carbon.analytics.api + provided + + + org.wso2.carbon.devicemgt + org.wso2.carbon.identity.jwt.client.extension + provided + + + org.wso2.carbon + org.wso2.carbon.registry.core + provided + + + org.wso2.carbon.registry + org.wso2.carbon.registry.resource + provided + + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.user.store.count + ${carbon.identity.framework.version} + provided + + + + + + + + org.wso2.carbon.analytics-common + org.wso2.carbon.event.receiver.stub + provided + + + org.wso2.carbon.analytics-common + org.wso2.carbon.event.stream.stub + provided + + + org.wso2.carbon.analytics-common + org.wso2.carbon.event.publisher.stub + provided + + + org.wso2.carbon.analytics-common + org.wso2.carbon.event.stream.persistence.stub + provided + + + org.powermock + powermock-module-testng + test + + + org.powermock + powermock-api-mockito + test + + + org.wso2.carbon.devicemgt + org.wso2.carbon.device.mgt.common + provided + + + org.wso2.carbon.devicemgt + org.wso2.carbon.device.mgt.extensions + provided + + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.claim.metadata.mgt + ${carbon.identity.framework.version} + provided + + + org.wso2.carbon.devicemgt + io.entgra.analytics.mgt.grafana.proxy.core + provided + + + org.wso2.carbon.devicemgt + org.wso2.carbon.device.mgt.core + provided + + + org.wso2.carbon.devicemgt + io.entgra.analytics.mgt.grafana.proxy.common + provided + + + + diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/GrafanaAPIProxyService.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/GrafanaAPIProxyService.java new file mode 100644 index 00000000000..c154f9149c2 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/GrafanaAPIProxyService.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved. + * + * Entgra (pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.entgra.analytics.mgt.grafana.proxy.api.service; + +import com.google.gson.JsonObject; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.Extension; +import io.swagger.annotations.ExtensionProperty; +import io.swagger.annotations.Info; +import io.swagger.annotations.SwaggerDefinition; +import io.swagger.annotations.Tag; +import org.wso2.carbon.apimgt.annotations.api.Scope; +import org.wso2.carbon.apimgt.annotations.api.Scopes; +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; + +@SwaggerDefinition( + info = @Info( + version = "1.0.0", + title = "Grafana API Proxy Service", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = "name", value = "GrafanaAPIManagement"), + @ExtensionProperty(name = "context", value = "/api/grafana-mgt/v1.0/api"), + }) + } + ), + tags = { + @Tag(name = "analytics_management", description = "") + } +) +@Scopes( + scopes = { + @Scope( + name = "Using Grafana APIs required for Grafana iframes", + description = "Grafana API proxy to validate requests.", + key = "perm:grafana:api:view", + roles = {"Internal/grafanamgt-user"}, + permissions = {"/analytics-mgt/grafana-mgt/api/view"} + ) + } +) + +@Path("/api") +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.MEDIA_TYPE_WILDCARD) +@Api(value = "Grafana API Management", description = "Grafana api related operations can be found here.") +public interface GrafanaAPIProxyService { + + String SCOPE = "scope"; + + @POST + @Path("/ds/query") + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + @ApiOperation( + produces = MediaType.APPLICATION_JSON, + httpMethod = "POST", + value = "Grafana query API proxy", + tags = "Analytics", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = SCOPE, value = "perm:grafana:api:view") + }) + } + ) + Response queryDatasource(JsonObject body, @Context HttpHeaders headers, @Context UriInfo requestUriInfo); + + @POST + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + @Path("/frontend-metrics") + @ApiOperation( + produces = MediaType.APPLICATION_JSON, + httpMethod = "POST", + value = "Grafana frontend-metric API proxy", + tags = "Analytics", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = SCOPE, value = "perm:grafana:api:view") + }) + } + ) + Response frontendMetrics(JsonObject body, @Context HttpHeaders headers, @Context UriInfo requestUriInfo); + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/dashboards/uid/{uid}") + @ApiOperation( + produces = MediaType.APPLICATION_JSON, + httpMethod = "GET", + value = "Grafana dashboard details API proxy", + tags = "Analytics", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = SCOPE, value = "perm:grafana:api:view") + }) + } + ) + Response getDashboard(@Context HttpHeaders headers, @Context UriInfo requestUriInfo) throws ClassNotFoundException; + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + @Path("/annotations") + @ApiOperation( + produces = MediaType.APPLICATION_JSON, + httpMethod = "GET", + value = "Grafana annotations API proxy", + tags = "Analytics", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = SCOPE, value = "perm:grafana:api:view") + }) + } + ) + Response getAnnotations(@Context HttpHeaders headers, @Context UriInfo requestUriInfo) throws ClassNotFoundException; + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/alerts/states-for-dashboard") + @ApiOperation( + produces = MediaType.APPLICATION_JSON, + httpMethod = "GET", + value = "Get Grafana alert states for dashboard details API proxy", + tags = "Analytics", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = SCOPE, value = "perm:grafana:api:view") + }) + } + ) + Response getAlertStateForDashboards(@Context HttpHeaders headers, @Context UriInfo requestUriInfo) throws ClassNotFoundException; +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/addons/ApiOriginFilter.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/addons/ApiOriginFilter.java new file mode 100644 index 00000000000..5562c40ad0a --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/addons/ApiOriginFilter.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.entgra.analytics.mgt.grafana.proxy.api.service.addons; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +public class ApiOriginFilter implements Filter { + + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException { + HttpServletResponse res = (HttpServletResponse) response; + res.addHeader("Access-Control-Allow-Origin", "*"); + res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT"); + res.addHeader("Access-Control-Allow-Headers", "Content-Type"); + chain.doFilter(request, response); + } + + public void destroy() { + //do nothing + } + + public void init(FilterConfig filterConfig) throws ServletException { + //do nothing + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/addons/GsonMessageBodyHandler.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/addons/GsonMessageBodyHandler.java new file mode 100644 index 00000000000..f786cf645ec --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/addons/GsonMessageBodyHandler.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.entgra.analytics.mgt.grafana.proxy.api.service.addons; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import javax.ws.rs.Consumes; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyReader; +import javax.ws.rs.ext.MessageBodyWriter; +import javax.ws.rs.ext.Provider; +import java.io.*; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; + +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; + +@Provider +@Produces(APPLICATION_JSON) +@Consumes(APPLICATION_JSON) +public class GsonMessageBodyHandler implements MessageBodyWriter, MessageBodyReader { + + public static final String DATE_FORMAT = "EEE, d MMM yyyy HH:mm:ss Z"; + private Gson gson; + private static final String UTF_8 = "UTF-8"; + + public boolean isReadable(Class aClass, Type type, Annotation[] annotations, MediaType mediaType) { + return true; + } + + private Gson getGson() { + if (gson == null) { + final GsonBuilder gsonBuilder = new GsonBuilder(); + gson = gsonBuilder.setDateFormat(DATE_FORMAT).create(); + } + return gson; + } + + public Object readFrom(Class objectClass, Type type, Annotation[] annotations, MediaType mediaType, + MultivaluedMap stringStringMultivaluedMap, InputStream entityStream) + throws IOException, WebApplicationException { + + InputStreamReader reader = new InputStreamReader(entityStream, "UTF-8"); + + try { + return getGson().fromJson(reader, type); + } finally { + reader.close(); + } + } + + public boolean isWriteable(Class aClass, Type type, Annotation[] annotations, MediaType mediaType) { + return true; + } + + public long getSize(Object o, Class aClass, Type type, Annotation[] annotations, MediaType mediaType) { + return -1; + } + + public void writeTo(Object object, Class aClass, Type type, Annotation[] annotations, MediaType mediaType, + MultivaluedMap stringObjectMultivaluedMap, OutputStream entityStream) + throws IOException, WebApplicationException { + + OutputStreamWriter writer = new OutputStreamWriter(entityStream, UTF_8); + try { + getGson().toJson(object, type, writer); + } finally { + writer.close(); + } + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/addons/ValidationInterceptor.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/addons/ValidationInterceptor.java new file mode 100644 index 00000000000..3ff1928ef6b --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/addons/ValidationInterceptor.java @@ -0,0 +1,121 @@ +/* Copyright (c) 2019, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.entgra.analytics.mgt.grafana.proxy.api.service.addons; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.cxf.interceptor.Fault; +import org.apache.cxf.jaxrs.lifecycle.ResourceProvider; +import org.apache.cxf.jaxrs.model.ClassResourceInfo; +import org.apache.cxf.jaxrs.model.OperationResourceInfo; +import org.apache.cxf.message.Message; +import org.apache.cxf.message.MessageContentsList; +import org.apache.cxf.phase.AbstractPhaseInterceptor; +import org.apache.cxf.phase.Phase; + +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; +import javax.validation.executable.ExecutableValidator; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Set; + +public class ValidationInterceptor extends AbstractPhaseInterceptor { + private Log log = LogFactory.getLog(getClass()); + private Validator validator = null; //validator interface is thread-safe + + public ValidationInterceptor() { + super(Phase.PRE_INVOKE); + ValidatorFactory defaultFactory = Validation.buildDefaultValidatorFactory(); + validator = defaultFactory.getValidator(); + if (validator == null) { + log.warn("Bean Validation provider could not be found, no validation will be performed"); + } else { + log.debug("Validation In-Interceptor initialized successfully"); + } + } + + @Override + public void handleMessage(Message message) throws Fault { + final OperationResourceInfo operationResource = message.getExchange().get(OperationResourceInfo.class); + if (operationResource == null) { + log.info("OperationResourceInfo is not available, skipping validation"); + return; + } + + final ClassResourceInfo classResource = operationResource.getClassResourceInfo(); + if (classResource == null) { + log.info("ClassResourceInfo is not available, skipping validation"); + return; + } + + final ResourceProvider resourceProvider = classResource.getResourceProvider(); + if (resourceProvider == null) { + log.info("ResourceProvider is not available, skipping validation"); + return; + } + + final List arguments = MessageContentsList.getContentsList(message); + final Method method = operationResource.getAnnotatedMethod(); + final Object instance = resourceProvider.getInstance(message); + if (method != null && arguments != null) { + //validate the parameters(arguments) over the invoked method + validate(method, arguments.toArray(), instance); + + //validate the fields of each argument + for (Object arg : arguments) { + if (arg != null) + validate(arg); + } + } + + } + + public void validate(final Method method, final Object[] arguments, final T instance) { + if (validator == null) { + log.warn("Bean Validation provider could not be found, no validation will be performed"); + return; + } + + ExecutableValidator methodValidator = validator.forExecutables(); + Set> violations = methodValidator.validateParameters(instance, + method, arguments); + + if (!violations.isEmpty()) { + throw new ConstraintViolationException(violations); + } + } + + public void validate(final T object) { + if (validator == null) { + log.warn("Bean Validation provider could be found, no validation will be performed"); + return; + } + + Set> violations = validator.validate(object); + + if (!violations.isEmpty()) { + throw new ConstraintViolationException(violations); + } + } + + public void handleFault(org.apache.cxf.message.Message messageParam) { + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/bean/ErrorListItem.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/bean/ErrorListItem.java new file mode 100644 index 00000000000..aca4e22b565 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/bean/ErrorListItem.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package io.entgra.analytics.mgt.grafana.proxy.api.service.bean; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotNull; + +@ApiModel(description = "") +public class ErrorListItem { + + @NotNull + private String code = null; + @NotNull + private String message = null; + + @ApiModelProperty(required = true, value = "") + @JsonProperty("code") + public String getCode() { + return code; + } + public void setCode(String code) { + this.code = code; + } + + public ErrorListItem() {} + + public ErrorListItem(String code, String msg) { + this.code = code; + this.message = msg; + } + + + /** + * Description about individual errors occurred + **/ + @ApiModelProperty(required = true, value = "Description about individual errors occurred") + @JsonProperty("message") + public String getMessage() { + return message; + } + public void setMessage(String message) { + this.message = message; + } + + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("errorItem {\n"); + + sb.append(" code: ").append(code).append("\n"); + sb.append(" message: ").append(message).append("\n"); + sb.append("}\n"); + return sb.toString(); + } + +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/bean/ErrorResponse.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/bean/ErrorResponse.java new file mode 100644 index 00000000000..1287ce5f768 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/bean/ErrorResponse.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package io.entgra.analytics.mgt.grafana.proxy.api.service.bean; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import java.util.ArrayList; +import java.util.List; + +@ApiModel(description = "") +public class ErrorResponse { + + private Long code = null; + private String message = null; + private String description = null; + private String moreInfo = null; + private List errorItems = new ArrayList<>(); + + public ErrorResponse() { + } + + @JsonProperty(value = "code") + @ApiModelProperty(required = true, value = "") + public Long getCode() { + return code; + } + + public void setCode(Long code) { + this.code = code; + } + + @JsonProperty(value = "message") + @ApiModelProperty(required = true, value = "ErrorResponse message.") + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + @JsonProperty(value = "description") + @ApiModelProperty(value = "A detail description about the error message.") + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @JsonProperty(value = "moreInfo") + @ApiModelProperty(value = "Preferably an url with more details about the error.") + public String getMoreInfo() { + return moreInfo; + } + + public void setMoreInfo(String moreInfo) { + this.moreInfo = moreInfo; + } + + public void addErrorListItem(ErrorListItem item) { + this.errorItems.add(item); + } + + /** + * If there are more than one error list them out. \nFor example, list out validation errors by each field. + */ + @JsonProperty(value = "errorItems") + @ApiModelProperty(value = "If there are more than one error list them out. \n" + + "For example, list out validation errors by each field.") + public List getErrorItems() { + return errorItems; + } + + public void setErrorItems(List error) { + this.errorItems = error; + } + + @Override + public String toString() { + return null; + } + + public static class ErrorResponseBuilder { + + private Long code = null; + private String message = null; + private String description = null; + private String moreInfo = null; + private List error; + + + public ErrorResponseBuilder() { + this.error = new ArrayList<>(); + } + + public ErrorResponseBuilder setCode(long code) { + this.code = code; + return this; + } + + public ErrorResponseBuilder setMessage(String message) { + this.message = message; + return this; + } + + public ErrorResponseBuilder setDescription(String description) { + this.description = description; + return this; + } + + public ErrorResponseBuilder setMoreInfo(String moreInfo) { + this.moreInfo = moreInfo; + return this; + } + + public ErrorResponseBuilder addErrorItem(String code, String msg) { + ErrorListItem item = new ErrorListItem(); + item.setCode(code); + item.setMessage(msg); + this.error.add(item); + return this; + } + + public ErrorResponse build() { + ErrorResponse errorResponse = new ErrorResponse(); + errorResponse.setCode(code); + errorResponse.setMessage(message); + errorResponse.setErrorItems(error); + errorResponse.setDescription(description); + errorResponse.setMoreInfo(moreInfo); + return errorResponse; + } + } + +} + + diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/exception/RefererNotValid.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/exception/RefererNotValid.java new file mode 100644 index 00000000000..2e39f3d62e6 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/exception/RefererNotValid.java @@ -0,0 +1,7 @@ +package io.entgra.analytics.mgt.grafana.proxy.api.service.exception; + +public class RefererNotValid extends Exception { + public RefererNotValid(String msg) { + super(msg); + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/impl/GrafanaAPIProxyServiceImpl.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/impl/GrafanaAPIProxyServiceImpl.java new file mode 100644 index 00000000000..ee227b3f18e --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/impl/GrafanaAPIProxyServiceImpl.java @@ -0,0 +1,116 @@ +package io.entgra.analytics.mgt.grafana.proxy.api.service.impl; + +import com.google.gson.JsonObject; +import io.entgra.analytics.mgt.grafana.proxy.api.service.GrafanaAPIProxyService; +import io.entgra.analytics.mgt.grafana.proxy.api.service.bean.ErrorResponse; +import io.entgra.analytics.mgt.grafana.proxy.api.service.exception.RefererNotValid; +import io.entgra.analytics.mgt.grafana.proxy.api.service.impl.util.GrafanaRequestHandlerUtil; +import io.entgra.analytics.mgt.grafana.proxy.common.exception.GrafanaManagementException; +import io.entgra.analytics.mgt.grafana.proxy.core.bean.GrafanaPanelIdentifier; +import io.entgra.analytics.mgt.grafana.proxy.core.exception.MaliciousQueryAttempt; +import io.entgra.analytics.mgt.grafana.proxy.core.internal.GrafanaMgtDataHolder; +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaUtil; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.mgt.common.exceptions.DBConnectionException; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; +import java.io.IOException; +import java.sql.SQLException; + +@Path("/api") +public class GrafanaAPIProxyServiceImpl implements GrafanaAPIProxyService { + + private static final Log log = LogFactory.getLog(GrafanaAPIProxyServiceImpl.class); + + @POST + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + @Path("/ds/query") + @Override + public Response queryDatasource(JsonObject body, @Context HttpHeaders headers, @Context UriInfo requestUriInfo) { + try { + GrafanaPanelIdentifier panelIdentifier = GrafanaRequestHandlerUtil.getPanelIdentifier(headers); + GrafanaMgtDataHolder.getInstance().getGrafanaQueryService(). + buildSafeQuery(body, panelIdentifier.getDashboardId(), panelIdentifier.getPanelId(), requestUriInfo.getRequestUri()); + return GrafanaRequestHandlerUtil.proxyPassPostRequest(body, requestUriInfo, panelIdentifier.getOrgId()); + } catch (MaliciousQueryAttempt e) { + return Response.status(Response.Status.BAD_REQUEST).entity( + new ErrorResponse.ErrorResponseBuilder().setMessage(e.getMessage()).build()).build(); + } catch (GrafanaManagementException e) { + return GrafanaRequestHandlerUtil.constructInternalServerError(e, e.getMessage()); + } catch (RefererNotValid e) { + return GrafanaRequestHandlerUtil.constructInvalidReferer(); + } catch (SQLException | IOException | DBConnectionException | + io.entgra.application.mgt.common.exception.DBConnectionException e) { + log.error(e); + return GrafanaRequestHandlerUtil.constructInternalServerError(e, e.getMessage()); + } + } + + @POST + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + @Path("/frontend-metrics") + @Override + public Response frontendMetrics(JsonObject body, @Context HttpHeaders headers, @Context UriInfo requestUriInfo) { + return proxyPassPostRequest(body, headers, requestUriInfo); + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/dashboards/uid/{uid}") + @Override + public Response getDashboard(@Context HttpHeaders headers, @Context UriInfo requestUriInfo) { + return proxyPassGetRequest(headers, requestUriInfo); + } + + @GET + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + @Path("/annotations") + @Override + public Response getAnnotations(@Context HttpHeaders headers, @Context UriInfo requestUriInfo) { + return proxyPassGetRequest(headers, requestUriInfo); + } + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/alerts/states-for-dashboard") + @Override + public Response getAlertStateForDashboards(@Context HttpHeaders headers, @Context UriInfo requestUriInfo) { + return proxyPassGetRequest(headers, requestUriInfo); + } + + public Response proxyPassGetRequest(HttpHeaders headers, UriInfo requestUriInfo) { + try { + GrafanaPanelIdentifier panelIdentifier = GrafanaRequestHandlerUtil.getPanelIdentifier(headers); + return GrafanaRequestHandlerUtil.proxyPassGetRequest(requestUriInfo, panelIdentifier.getOrgId()); + } catch (RefererNotValid e) { + return GrafanaRequestHandlerUtil.constructInvalidReferer(); + } catch (GrafanaManagementException e) { + return GrafanaRequestHandlerUtil.constructInternalServerError(e, e.getMessage()); + } + } + + public Response proxyPassPostRequest(JsonObject body, HttpHeaders headers, UriInfo requestUriInfo) { + try { + GrafanaPanelIdentifier panelIdentifier = GrafanaRequestHandlerUtil.getPanelIdentifier(headers); + return GrafanaRequestHandlerUtil.proxyPassPostRequest(body, requestUriInfo, panelIdentifier.getOrgId()); + } catch (RefererNotValid e) { + return GrafanaRequestHandlerUtil.constructInvalidReferer(); + } catch (GrafanaManagementException e) { + return GrafanaRequestHandlerUtil.constructInternalServerError(e, e.getMessage()); + } + } + + +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/impl/util/GrafanaRequestHandlerUtil.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/impl/util/GrafanaRequestHandlerUtil.java new file mode 100644 index 00000000000..e4d17265a8c --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/java/io/entgra/analytics/mgt/grafana/proxy/api/service/impl/util/GrafanaRequestHandlerUtil.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package io.entgra.analytics.mgt.grafana.proxy.api.service.impl.util; + +import com.google.gson.JsonObject; +import io.entgra.analytics.mgt.grafana.proxy.api.service.bean.ErrorResponse; +import io.entgra.analytics.mgt.grafana.proxy.api.service.exception.RefererNotValid; +import io.entgra.analytics.mgt.grafana.proxy.common.exception.GrafanaManagementException; +import io.entgra.analytics.mgt.grafana.proxy.core.bean.GrafanaPanelIdentifier; +import io.entgra.analytics.mgt.grafana.proxy.core.exception.GrafanaEnvVariablesNotDefined; +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaConstants; +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaUtil; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.wso2.carbon.device.mgt.core.common.util.HttpUtil; +import org.wso2.carbon.device.mgt.core.report.mgt.Constants; + +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; + +public class GrafanaRequestHandlerUtil { + + private static final Log log = LogFactory.getLog(GrafanaRequestHandlerUtil.class); + + public static Response proxyPassGetRequest(UriInfo requestUriInfo, String orgId) throws GrafanaEnvVariablesNotDefined { + HttpGet grafanaGetReq = new HttpGet(); + return forwardRequestToGrafanaEndpoint(grafanaGetReq, requestUriInfo, orgId); + } + + public static Response proxyPassPostRequest(JsonObject body, UriInfo requestUriInfo, String orgId) + throws GrafanaEnvVariablesNotDefined { + HttpPost grafanaPostReq = new HttpPost(); + try { + setRequestEntity(grafanaPostReq, body); + } catch (UnsupportedEncodingException e) { + String errorMsg = "Error occurred while parsing body"; + log.error(errorMsg, e); + return Response.status(Response.Status.BAD_REQUEST).entity( + new ErrorResponse.ErrorResponseBuilder().setMessage(errorMsg).build()).build(); + } + return forwardRequestToGrafanaEndpoint(grafanaPostReq, requestUriInfo, orgId); + } + + private static Response forwardRequestToGrafanaEndpoint(HttpRequestBase requestBase, UriInfo requestUriInfo, String orgId) + throws GrafanaEnvVariablesNotDefined { + URI grafanaUri = generateGrafanaUri(requestUriInfo); + requestBase.setURI(grafanaUri); + requestBase.setHeader(GrafanaConstants.X_GRAFANA_ORG_ID_HEADER, orgId); + try(CloseableHttpClient client = HttpClients.createDefault()) { + HttpResponse grafanaResponse = invokeGrafanaAPI(client, requestBase); + String grafanaResponseBody = HttpUtil.getResponseString(grafanaResponse); + return Response.status(Response.Status.OK).entity(grafanaResponseBody). + header(HttpHeaders.CONTENT_TYPE, HttpUtil.getContentType(grafanaResponse)).build(); + } catch (IOException e) { + String msg = "Error occurred while calling Grafana API"; + log.error(msg, e); + return Response.serverError().entity( + new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build(); + } catch (GrafanaManagementException e) { + String err = "Error occurred while retrieving Grafana configuration"; + log.error(err, e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity( + new ErrorResponse.ErrorResponseBuilder().setMessage(err).build()).build(); + } + } + + public static HttpResponse invokeGrafanaAPI(HttpClient client, HttpRequestBase request) throws IOException, GrafanaManagementException { + setBasicAuthHeader(request); + return client.execute(request); + } + + public static void setBasicAuthHeader(HttpRequestBase request) throws GrafanaManagementException { + String basicAuth = GrafanaUtil.getBasicAuthBase64Header(); + request.setHeader(HttpHeaders.AUTHORIZATION, basicAuth); + } + + public static URI generateGrafanaUri(UriInfo requestUriInfo) throws GrafanaEnvVariablesNotDefined { + String base = GrafanaUtil.getGrafanaHTTPBase(requestUriInfo.getRequestUri().getScheme()); + String grafanaRequestPath = getGrafanaRequestPathWQuery(requestUriInfo); + return HttpUtil.createURI(GrafanaUtil.generateGrafanaUrl(grafanaRequestPath, base)); + } + + public static String getGrafanaRequestPathWQuery(UriInfo requestUriInfo) { + String path = requestUriInfo.getPath(); + String queryParam = requestUriInfo.getRequestUri().getRawQuery(); + if (queryParam != null) { + path += Constants.URI_QUERY_SEPARATOR + queryParam; + } + return path; + } + + public static GrafanaPanelIdentifier getPanelIdentifier(HttpHeaders headers) throws RefererNotValid { + String referer = headers.getHeaderString(GrafanaConstants.REFERER_HEADER); + if(referer == null) { + String errMsg = "Request does not contain Referer header"; + log.error(errMsg); + throw new RefererNotValid(errMsg); + } + GrafanaPanelIdentifier panelIdentifier = GrafanaUtil.getPanelIdentifierFromReferer(referer); + if(panelIdentifier.getDashboardId() == null || + panelIdentifier.getPanelId() == null || panelIdentifier.getOrgId() == null) { + String errMsg = "Referer must contain dashboardId, panelId and orgId"; + log.error(errMsg); + throw new RefererNotValid(errMsg); + } + return panelIdentifier; + } + + public static void setRequestEntity(HttpPost postRequest, JsonObject body) throws UnsupportedEncodingException { + StringEntity bodyEntity = new StringEntity(body.toString(), StandardCharsets.UTF_8); + bodyEntity.setContentType(MediaType.APPLICATION_JSON); + postRequest.setEntity(bodyEntity); + } + + public static Response constructInvalidReferer() { + String errorMsg = "Request does not contain a valid Referer header"; + return Response.status(Response.Status.BAD_REQUEST).entity( + new ErrorResponse.ErrorResponseBuilder().setMessage(errorMsg).build()).build(); + } + public static Response constructInternalServerError(Exception e, String errMsg) { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity( + new ErrorResponse.ErrorResponseBuilder().setMessage(errMsg).build()).build(); + } + + public static void copyHeadersToGrafanaRequest(HttpRequestBase grafanaRequest, HttpHeaders headers) { + Map> headerValues = headers.getRequestHeaders(); + for (String key : headerValues.keySet()) { + if (!key.equals(HttpHeaders.AUTHORIZATION) && !key.equals(HttpHeaders.CONTENT_LENGTH)) { + for (String value : headerValues.get(key)) { + grafanaRequest.setHeader(key, value); + } + } + } + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/webapp/META-INF/permissions.xml b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/webapp/META-INF/permissions.xml new file mode 100644 index 00000000000..190a634a123 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/webapp/META-INF/permissions.xml @@ -0,0 +1,32 @@ + + + + + + + diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/webapp/META-INF/webapp-classloading.xml b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/webapp/META-INF/webapp-classloading.xml new file mode 100644 index 00000000000..9f50930c4f7 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/webapp/META-INF/webapp-classloading.xml @@ -0,0 +1,35 @@ + + + + + + + + + false + + + CXF3,Carbon + diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/webapp/WEB-INF/cxf-servlet.xml b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/webapp/WEB-INF/cxf-servlet.xml new file mode 100644 index 00000000000..0f6ec36cf0b --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/webapp/WEB-INF/cxf-servlet.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/webapp/WEB-INF/web.xml b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..6ef280f763b --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,113 @@ + + + + Grafana-API-Proxy-Webapp + + JAX-WS/JAX-RS Grafana API Management Endpoint + JAX-WS/JAX-RS Servlet + CXFServlet + + org.apache.cxf.transport.servlet.CXFServlet + + + + swagger.security.filter + ApiAuthorizationFilterImpl + + 1 + + + CXFServlet + /* + + + 60 + + + + doAuthentication + true + + + + nonSecuredEndPoints + + + + + + managed-api-enabled + true + + + managed-api-owner + admin + + + isSharedWithAllTenants + true + + + + ApiOriginFilter + io.entgra.analytics.mgt.grafana.proxy.api.service.addons.ApiOriginFilter + + + + ApiOriginFilter + /* + + + + HttpHeaderSecurityFilter + org.apache.catalina.filters.HttpHeaderSecurityFilter + + hstsEnabled + false + + + + + ContentTypeBasedCachePreventionFilter + org.wso2.carbon.ui.filters.cache.ContentTypeBasedCachePreventionFilter + + patterns + text/html" ,application/json" ,text/plain + + + filterAction + enforce + + + httpHeaders + Cache-Control: no-store, no-cache, must-revalidate, private + + + + + HttpHeaderSecurityFilter + /* + + + + ContentTypeBasedCachePreventionFilter + /* + + + diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.common/pom.xml b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.common/pom.xml new file mode 100644 index 00000000000..5b519b30d42 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.common/pom.xml @@ -0,0 +1,107 @@ + + + + + + + org.wso2.carbon.devicemgt + grafana-mgt + 5.0.6-SNAPSHOT + ../pom.xml + + + 4.0.0 + io.entgra.analytics.mgt.grafana.proxy.common + bundle + Entgra - Grafana API Handler Common + Entgra - Grafana API Handler Common + http://entgra.io + + + + + org.apache.felix + maven-bundle-plugin + true + + + ${project.artifactId} + ${project.artifactId} + ${carbon.device.mgt.version} + Grafana Management Common Bundle + + io.entgra.analytics.mgt.grafana.proxy.common.* + + + javax.xml.bind.annotation; version="${javax.xml.bind.imp.pkg.version}", + com.fasterxml.jackson.annotation;version="${jackson-annotations.version}", + io.swagger.annotations; version="${swagger.annotations.version}"; resolution:=optional, + com.google.gson + + + + + + org.jacoco + jacoco-maven-plugin + + ${basedir}/target/coverage-reports/jacoco-unit.exec + + + + jacoco-initialize + + prepare-agent + + + + jacoco-site + test + + report + + + ${basedir}/target/coverage-reports/jacoco-unit.exec + ${basedir}/target/coverage-reports/site + + + + + + + + + + + io.swagger + swagger-annotations + provided + + + org.wso2.orbit.com.fasterxml.jackson.core + jackson-annotations + + + com.google.code.gson + gson + + + + + diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.common/src/main/java/io/entgra/analytics/mgt/grafana/proxy/common/exception/GrafanaManagementException.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.common/src/main/java/io/entgra/analytics/mgt/grafana/proxy/common/exception/GrafanaManagementException.java new file mode 100644 index 00000000000..64ec9b27313 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.common/src/main/java/io/entgra/analytics/mgt/grafana/proxy/common/exception/GrafanaManagementException.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package io.entgra.analytics.mgt.grafana.proxy.common.exception; + +public class GrafanaManagementException extends Exception{ + + private static final long serialVersionUID = -3922279312829079297L; + + public GrafanaManagementException(String msg, Exception nestedEx) { + super(msg, nestedEx); + } + public GrafanaManagementException(String msg) { + super(msg); + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/pom.xml b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/pom.xml new file mode 100644 index 00000000000..46f2116b2b3 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/pom.xml @@ -0,0 +1,329 @@ + + + + + + + org.wso2.carbon.devicemgt + grafana-mgt + 5.0.6-SNAPSHOT + ../pom.xml + + + 4.0.0 + io.entgra.analytics.mgt.grafana.proxy.core + bundle + Entgra - Grafana API Handler Core + Entgra - Grafana API Handler Core + http://entgra.io + + + + + org.apache.felix + maven-scr-plugin + + + org.jacoco + jacoco-maven-plugin + + ${basedir}/target/coverage-reports/jacoco-unit.exec + + + + jacoco-initialize + + prepare-agent + + + + jacoco-site + test + + report + + + ${basedir}/target/coverage-reports/jacoco-unit.exec + ${basedir}/target/coverage-reports/site + + + + + + org.apache.felix + maven-bundle-plugin + true + + + ${project.artifactId} + ${project.artifactId} + ${carbon.device.mgt.version} + Grafana API Management Core Bundle + io.entgra.analytics.mgt.grafana.proxy.core.internal + + io.entgra.analytics.mgt.grafana.proxy.common.*, + javax.xml.parsers;version="${javax.xml.parsers.import.pkg.version}";resolution:=optional, + javax.xml.bind.annotation, + javax.xml.bind, + org.apache.commons.lang, + org.wso2.carbon, + org.wso2.carbon.device.mgt.common.*, + org.wso2.carbon.device.mgt.core.* + io.entgra.application.mgt.core.* + + + !org.wso2.carbon.email.sender.core.internal, + io.entgra.analytics.mgt.grafana.proxy.core.* + + + scribe;scope=compile|runtime;inline=false, + + * + + + + + + + + + org.eclipse.osgi + org.eclipse.osgi + + + org.eclipse.osgi + org.eclipse.osgi.services + + + org.wso2.orbit.org.scannotation + scannotation + + + org.wso2.carbon.devicemgt + org.wso2.carbon.device.mgt.common + + + org.wso2.carbon + org.wso2.carbon.utils + + + org.wso2.carbon + org.wso2.carbon.logging + provided + + + org.wso2.carbon + org.wso2.carbon.core + provided + + + org.wso2.carbon.devicemgt + io.entgra.analytics.mgt.grafana.proxy.common + + + org.wso2.carbon.devicemgt + org.wso2.carbon.device.mgt.core + + + org.wso2.carbon.devicemgt + io.entgra.application.mgt.core + + + org.apache.httpcomponents.wso2 + httpclient + 4.1.1.wso2v1 + compile + + + org.apache.httpcomponents + httpclient + + + org.wso2.carbon.devicemgt + org.wso2.carbon.identity.jwt.client.extension + provided + + + javax.ws.rs + javax.ws.rs-api + + + javax.ws.rs + jsr311-api + + + org.javassist + javassist + + + org.powermock + powermock-api-mockito + + + org.wso2.carbon + org.wso2.carbon.ndatasource.core + + + commons-lang + commons-lang + + + + + commons-codec.wso2 + commons-codec + provided + + + com.h2database.wso2 + h2-database-engine + test + + + org.testng + testng + + + org.powermock + powermock-module-testng + test + + + org.wso2.carbon + org.wso2.carbon.user.core + + + org.wso2.carbon + org.wso2.carbon.user.api + + + org.wso2.carbon + org.wso2.carbon.registry.api + + + org.wso2.carbon + org.wso2.carbon.registry.core + + + org.apache.tomcat.wso2 + jdbc-pool + + + org.wso2.carbon + org.wso2.carbon.base + + + org.wso2.carbon.governance + org.wso2.carbon.governance.api + + + org.apache.axis2.transport + axis2-transport-mail + + + org.apache.ws.commons.axiom.wso2 + axiom + + + + org.apache.axis2.wso2 + axis2 + + + org.wso2.carbon.identity.inbound.auth.oauth2 + org.wso2.carbon.identity.oauth.stub + + + org.wso2.tomcat + tomcat + + + org.wso2.tomcat + tomcat-servlet-api + + + + + org.wso2.carbon.commons + org.wso2.carbon.ntask.core + + + + + commons-collections.wso2 + commons-collections + + + + org.wso2.carbon.devicemgt + org.wso2.carbon.email.sender.core + + + + mysql + mysql-connector-java + test + + + + com.google.code.gson + gson + + + + io.swagger + swagger-annotations + provided + + + org.wso2.carbon.devicemgt + org.wso2.carbon.apimgt.annotations + + + org.wso2.carbon.event-processing + org.wso2.carbon.event.processor.stub + + + org.wso2.carbon.multitenancy + org.wso2.carbon.tenant.mgt + + + commons-validator + commons-validator + + + org.wso2.carbon.devicemgt + io.entgra.server.bootup.heartbeat.beacon + + + com.google.guava + guava + + + + + + diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/bean/GrafanaPanelIdentifier.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/bean/GrafanaPanelIdentifier.java new file mode 100644 index 00000000000..4ff90e25b04 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/bean/GrafanaPanelIdentifier.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * you may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.entgra.analytics.mgt.grafana.proxy.core.bean; + +public class GrafanaPanelIdentifier { + + private String orgId; + private String dashboardUID; + private String panelId; + + public GrafanaPanelIdentifier(String orgId, String dashboardUID, String panelId) { + this.orgId = orgId; + this.dashboardUID = dashboardUID; + this.panelId = panelId; + } + + public String getOrgId() { + return orgId; + } + + public void setOrgId(String orgId) { + this.orgId = orgId; + } + + public String getDashboardId() { + return dashboardUID; + } + + public void setDashboardId(String dashboardId) { + this.dashboardUID = dashboardId; + } + + public String getPanelId() { + return panelId; + } + + public void setPanelId(String panelId) { + this.panelId = panelId; + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/config/GrafanaConfiguration.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/config/GrafanaConfiguration.java new file mode 100644 index 00000000000..72ab1545fe1 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/config/GrafanaConfiguration.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.entgra.analytics.mgt.grafana.proxy.core.config; + +import io.entgra.analytics.mgt.grafana.proxy.core.config.xml.bean.CacheConfiguration; +import io.entgra.analytics.mgt.grafana.proxy.core.config.xml.bean.User; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.List; + +@XmlRootElement(name = "GrafanaConfiguration") +public class GrafanaConfiguration { + + private User adminUser; + private List caches; + + @XmlElement(name = "AdminUser") + public User getAdminUser() { + return adminUser; + } + + public void setAdminUser(User user) { + this.adminUser = user; + } + + + @XmlElementWrapper(name = "Cache") + @XmlElement(name = "CacheConfiguration") + public List getCaches() { + return caches; + } + + public CacheConfiguration getCacheByName(String cacheName) { + for (CacheConfiguration cache : caches) { + if (cache.getName().equals(cacheName)) { + return cache; + } + } + return null; + } + + public void setCaches(List caches) { + this.caches = caches; + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/config/GrafanaConfigurationManager.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/config/GrafanaConfigurationManager.java new file mode 100644 index 00000000000..a8ce50fb5fb --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/config/GrafanaConfigurationManager.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * you may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.entgra.analytics.mgt.grafana.proxy.core.config; + +import io.entgra.analytics.mgt.grafana.proxy.common.exception.GrafanaManagementException; +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaConstants; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.w3c.dom.Document; +import org.wso2.carbon.utils.CarbonUtils; + +import javax.xml.XMLConstants; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.File; + +public class GrafanaConfigurationManager { + + private static final Log log = LogFactory.getLog(GrafanaConfigurationManager.class); + private static GrafanaConfigurationManager grafanaConfigurationManager; + private GrafanaConfiguration grafanaConfiguration; + private static final String GRAFANA_CONFIG_PATH = CarbonUtils.getCarbonConfigDirPath() + File.separator + + GrafanaConstants.CONFIG_XML_NAME; + + public static GrafanaConfigurationManager getInstance() { + if (grafanaConfigurationManager == null) { + synchronized (GrafanaConfigurationManager.class) { + if (grafanaConfigurationManager == null) { + grafanaConfigurationManager = new GrafanaConfigurationManager(); + } + } + } + return grafanaConfigurationManager; + } + + /** + * Initialize the Grafana Configuration through the provided configuration location + * @param configLocation has the path of the configuration file + * @throws GrafanaManagementException throws when there are any errors during the initialization of + * Grafana configuration + */ + public synchronized void initConfig(String configLocation) throws GrafanaManagementException { + try { + File smsConfig = new File(configLocation); + Document doc = convertXMLToDocument(smsConfig); + + /* Un-marshaling Grafana configuration */ + JAXBContext smsContext = JAXBContext.newInstance(GrafanaConfiguration.class); + Unmarshaller unmarshaller = smsContext.createUnmarshaller(); + this.grafanaConfiguration = (GrafanaConfiguration) unmarshaller.unmarshal(doc); + } catch (JAXBException e) { + String msg = "Error occurred while initializing Grafana config '" + configLocation + "'"; + log.error(msg, e); + throw new GrafanaManagementException(msg, e); + } + } + + /** + * Initialize the Grafana Configuration through the grafana-config.xml file in the GRAFANA_CONFIG_PATH + * @throws GrafanaManagementException throws when there are any errors during the initialization of config + */ + public void initConfig() throws GrafanaManagementException { + this.initConfig(GRAFANA_CONFIG_PATH); + } + + public GrafanaConfiguration getGrafanaConfiguration() throws GrafanaManagementException { + if (grafanaConfiguration != null) { + return grafanaConfiguration; + } + initConfig(); + return grafanaConfiguration; + } + + private static Document convertXMLToDocument(File file) throws GrafanaManagementException { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setNamespaceAware(true); + try { + factory.setFeature(GrafanaConstants.XML.FEATURES_DISALLOW_DOCTYPE_DECL, true); + factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + DocumentBuilder docBuilder = factory.newDocumentBuilder(); + return docBuilder.parse(file); + } catch (Exception e) { + String errMsg = "Error occurred while parsing file, while converting to a org.w3c.dom.Document"; + log.error(errMsg, e); + throw new GrafanaManagementException(errMsg, e); + } + } + +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/config/xml/bean/CacheConfiguration.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/config/xml/bean/CacheConfiguration.java new file mode 100644 index 00000000000..def5fb3476a --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/config/xml/bean/CacheConfiguration.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.entgra.analytics.mgt.grafana.proxy.core.config.xml.bean; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; + +public class CacheConfiguration { + + private String name; + private int capacity; + + @XmlAttribute(name = "name") + public String getName() { + return name; + } + + @XmlElement(name = "Capacity") + public int getCapacity() { + return capacity; + } + + public void setName(String name) { + this.name = name; + } + + public void setCapacity(int capacity) { + this.capacity = capacity; + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/config/xml/bean/User.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/config/xml/bean/User.java new file mode 100644 index 00000000000..3e8b6621751 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/config/xml/bean/User.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * you may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.entgra.analytics.mgt.grafana.proxy.core.config.xml.bean; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement(name="AdminUser") +public class User { + private String username; + private String password; + + @XmlElement(name="Username") + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + @XmlElement(name="Password") + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/DashboardNotFound.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/DashboardNotFound.java new file mode 100644 index 00000000000..c052d107d19 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/DashboardNotFound.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * you may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.entgra.analytics.mgt.grafana.proxy.core.exception; + +public class DashboardNotFound extends GrafanaAPIException { + + private static final long serialVersionUID = -2111271331930066297L; + + public DashboardNotFound(String errMsg) { + super(errMsg); + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/DatasourceNotFound.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/DatasourceNotFound.java new file mode 100644 index 00000000000..3d750bff35c --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/DatasourceNotFound.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * you may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.entgra.analytics.mgt.grafana.proxy.core.exception; + +public class DatasourceNotFound extends GrafanaAPIException { + + private static final long serialVersionUID = -3171333332936220227L; + + public DatasourceNotFound(String errMsg) { + super(errMsg); + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/GrafanaAPIException.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/GrafanaAPIException.java new file mode 100644 index 00000000000..df926061f08 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/GrafanaAPIException.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * you may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.entgra.analytics.mgt.grafana.proxy.core.exception; + +import io.entgra.analytics.mgt.grafana.proxy.common.exception.GrafanaManagementException; + +public class GrafanaAPIException extends GrafanaManagementException { + + private static final long serialVersionUID = -3121249462130274227L; + + public GrafanaAPIException(String errMsg) { + super(errMsg); + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/GrafanaEnvVariablesNotDefined.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/GrafanaEnvVariablesNotDefined.java new file mode 100644 index 00000000000..acb836680ce --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/GrafanaEnvVariablesNotDefined.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * you may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.entgra.analytics.mgt.grafana.proxy.core.exception; + + +import io.entgra.analytics.mgt.grafana.proxy.common.exception.GrafanaManagementException; + +public class GrafanaEnvVariablesNotDefined extends GrafanaManagementException { + + private static final long serialVersionUID = -3424429462350570237L; + + public GrafanaEnvVariablesNotDefined(String errMsg) { + super(errMsg); + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/MaliciousQueryAttempt.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/MaliciousQueryAttempt.java new file mode 100644 index 00000000000..ff91dd6a6e5 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/MaliciousQueryAttempt.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * you may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.entgra.analytics.mgt.grafana.proxy.core.exception; + + +import io.entgra.analytics.mgt.grafana.proxy.common.exception.GrafanaManagementException; + +public class MaliciousQueryAttempt extends GrafanaManagementException { + + private static final long serialVersionUID = -3171279331530570257L; + + public MaliciousQueryAttempt(String msg) { + super(msg); + } + + public MaliciousQueryAttempt(String msg, Exception nestedException) { + super(msg, nestedException); + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/PanelNotFound.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/PanelNotFound.java new file mode 100644 index 00000000000..f8cead9df8b --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/PanelNotFound.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * you may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.entgra.analytics.mgt.grafana.proxy.core.exception; + +public class PanelNotFound extends GrafanaAPIException { + + private static final long serialVersionUID = -3471379441938070287L; + public PanelNotFound(String errMsg) { + super(errMsg); + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/QueryMisMatch.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/QueryMisMatch.java new file mode 100644 index 00000000000..0d011ddd148 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/QueryMisMatch.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * you may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.entgra.analytics.mgt.grafana.proxy.core.exception; + +public class QueryMisMatch extends MaliciousQueryAttempt { + + private static final long serialVersionUID = -3171277334737076294L; + + public QueryMisMatch(String msg) { + super(msg); + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/QueryNotFound.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/QueryNotFound.java new file mode 100644 index 00000000000..1bff687b145 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/QueryNotFound.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * you may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.entgra.analytics.mgt.grafana.proxy.core.exception; + +public class QueryNotFound extends GrafanaAPIException { + + private static final long serialVersionUID = -3151259311910070297L; + public QueryNotFound(String errMsg) { + super(errMsg); + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/TemplateNotFound.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/TemplateNotFound.java new file mode 100644 index 00000000000..e5347eee101 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/exception/TemplateNotFound.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * you may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.entgra.analytics.mgt.grafana.proxy.core.exception; + +public class TemplateNotFound extends GrafanaAPIException { + + private static final long serialVersionUID = -3481878481835562209L; + public TemplateNotFound(String errMsg) { + super(errMsg); + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/internal/GrafanaManagementServiceComponent.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/internal/GrafanaManagementServiceComponent.java new file mode 100644 index 00000000000..f976e2ef510 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/internal/GrafanaManagementServiceComponent.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved. + * + * Entgra (pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.entgra.analytics.mgt.grafana.proxy.core.internal; + +import io.entgra.analytics.mgt.grafana.proxy.core.service.GrafanaAPIService; +import io.entgra.analytics.mgt.grafana.proxy.core.service.GrafanaQueryService; +import io.entgra.analytics.mgt.grafana.proxy.core.service.impl.GrafanaAPIServiceImpl; +import io.entgra.analytics.mgt.grafana.proxy.core.service.impl.GrafanaQueryServiceImpl; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.osgi.framework.BundleContext; +import org.osgi.service.component.ComponentContext; +import org.wso2.carbon.context.PrivilegedCarbonContext; + +/** + * @scr.component name="io.entgra.analytics.mgt.grafana.proxy.grafanamanagementservicecomponent" immediate="true" + */ +public class GrafanaManagementServiceComponent { + + private static Log log = LogFactory.getLog(GrafanaManagementServiceComponent.class); + + @SuppressWarnings("unused") + protected void activate(ComponentContext componentContext) { + try { + if (log.isDebugEnabled()) { + log.debug("Initializing grafana proxy management core bundle"); + } + + BundleContext bundleContext = componentContext.getBundleContext(); + + GrafanaAPIService grafanaAPIService = new GrafanaAPIServiceImpl(); + GrafanaQueryService grafanaQueryService = new GrafanaQueryServiceImpl(grafanaAPIService); + bundleContext.registerService(GrafanaAPIService.class.getName(), grafanaAPIService, null); + GrafanaMgtDataHolder.getInstance().setGrafanaAPIService(grafanaAPIService); + bundleContext.registerService(GrafanaQueryService.class.getName(), grafanaQueryService, null); + GrafanaMgtDataHolder.getInstance().setGrafanaQueryService(grafanaQueryService); + + if (log.isDebugEnabled()) { + log.debug("Grafana management core bundle has been successfully initialized"); + } + } catch (Throwable e) { + log.error("Error occurred while initializing grafana management core bundle", e); + } + } + + @SuppressWarnings("unused") + protected void deactivate(ComponentContext componentContext) { + if (log.isDebugEnabled()) { + log.debug("De-activating Grafana Management Service Component"); + } + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/internal/GrafanaMgtDataHolder.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/internal/GrafanaMgtDataHolder.java new file mode 100644 index 00000000000..10d43d7a235 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/internal/GrafanaMgtDataHolder.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved. + * + * Entgra (pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.entgra.analytics.mgt.grafana.proxy.core.internal; + +import io.entgra.analytics.mgt.grafana.proxy.core.service.GrafanaAPIService; +import io.entgra.analytics.mgt.grafana.proxy.core.service.GrafanaQueryService; + +public class GrafanaMgtDataHolder { + + private GrafanaAPIService grafanaAPIService; + private GrafanaQueryService grafanaQueryService; + + private GrafanaMgtDataHolder() { + + } + + public GrafanaAPIService getGrafanaAPIService() { + return grafanaAPIService; + } + + public void setGrafanaAPIService(GrafanaAPIService grafanaAPIService) { + this.grafanaAPIService = grafanaAPIService; + } + + public GrafanaQueryService getGrafanaQueryService() { + return grafanaQueryService; + } + + public void setGrafanaQueryService(GrafanaQueryService grafanaQueryService) { + this.grafanaQueryService = grafanaQueryService; + } + + public static class InstanceHolder { + public static GrafanaMgtDataHolder instance = new GrafanaMgtDataHolder(); + } + + public static GrafanaMgtDataHolder getInstance() { + return InstanceHolder.instance; + } + + +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/GrafanaAPIService.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/GrafanaAPIService.java new file mode 100644 index 00000000000..91776c50b9f --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/GrafanaAPIService.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * you may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.entgra.analytics.mgt.grafana.proxy.core.service; + +import com.google.gson.JsonObject; +import io.entgra.analytics.mgt.grafana.proxy.common.exception.GrafanaManagementException; +import io.entgra.analytics.mgt.grafana.proxy.core.service.bean.Datasource; + +import java.io.IOException; + +public interface GrafanaAPIService { + + /** + * Get predefined query template from grafana for given panel/variable + * @param dashboardUID + * @param panelId + * @param refId + * @param requestScheme + * @return query of the given panel/variable + * @throws IOException + * @throws GrafanaManagementException + */ + String getQueryTemplate(String dashboardUID, String panelId, String refId, + String requestScheme) throws IOException, GrafanaManagementException; + + /** + * Get panel details by dashboard uid and panel Id + * @param dashboardUID + * @param panelId + * @param requestScheme + * @return Panel details + * @throws IOException + * @throws GrafanaManagementException + */ + JsonObject getPanelDetails(String dashboardUID, String panelId, String requestScheme) throws + IOException, GrafanaManagementException; + + /** + * Get dashboard details by uid + * @param dashboardUID + * @param requestScheme + * @return Dashboard details + * @throws IOException + * @throws GrafanaManagementException + */ + JsonObject getDashboardDetails(String dashboardUID, String requestScheme) throws IOException, + GrafanaManagementException; + + /** + * Get datasource details by id + * @param datasourceId + * @param requestScheme + * @return Datasource details + * @throws IOException + * @throws GrafanaManagementException + */ + Datasource getDatasource(int datasourceId, String requestScheme) throws IOException, + GrafanaManagementException; +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/GrafanaQueryService.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/GrafanaQueryService.java new file mode 100644 index 00000000000..a1c43baf52a --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/GrafanaQueryService.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * you may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.entgra.analytics.mgt.grafana.proxy.core.service; + +import com.google.gson.JsonObject; +import io.entgra.analytics.mgt.grafana.proxy.common.exception.GrafanaManagementException; +import org.wso2.carbon.device.mgt.common.exceptions.DBConnectionException; + +import java.io.IOException; +import java.net.URI; +import java.sql.SQLException; + +public interface GrafanaQueryService { + + void buildSafeQuery(JsonObject queryRequestBody, String dashboardUID, String panelId, + URI requestUri) throws IOException, SQLException, GrafanaManagementException, + DBConnectionException, io.entgra.application.mgt.common.exception.DBConnectionException; + +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/bean/Datasource.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/bean/Datasource.java new file mode 100644 index 00000000000..43a11fdda40 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/bean/Datasource.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * you may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.entgra.analytics.mgt.grafana.proxy.core.service.bean; + +public class Datasource { + private int id; + private String url; + private String name; + private String type; + private String database; + + public Datasource(int id, String url, String name, String type, String database) { + this.id = id; + this.url = url; + this.name = name; + this.type = type; + this.database = database; + } + + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getDatabase() { + return database; + } + + public void setDatabase(String database) { + this.database = database; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } +} \ No newline at end of file diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/cache/CacheManager.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/cache/CacheManager.java new file mode 100644 index 00000000000..b56a1041bd2 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/cache/CacheManager.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2021, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.entgra.analytics.mgt.grafana.proxy.core.service.cache; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import io.entgra.analytics.mgt.grafana.proxy.common.exception.GrafanaManagementException; +import io.entgra.analytics.mgt.grafana.proxy.core.config.GrafanaConfiguration; +import io.entgra.analytics.mgt.grafana.proxy.core.config.GrafanaConfigurationManager; +import io.entgra.analytics.mgt.grafana.proxy.core.config.xml.bean.CacheConfiguration; +import io.entgra.analytics.mgt.grafana.proxy.core.service.bean.Datasource; +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaConstants; +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; + +public class CacheManager { + private static final Log log = LogFactory.getLog(CacheManager.class); + + private Cache datasourceAPICache; + private Cache queryTemplateAPICache; + private Cache encodedQueryCache; + + private CacheManager() { + initCache(); + } + + private static final class CacheManagerHolder { + static final CacheManager cacheManager = new CacheManager(); + } + + public static CacheManager getInstance() { + return CacheManagerHolder.cacheManager; + } + + public Cache getEncodedQueryCache() { + return encodedQueryCache; + } + + public Cache getQueryTemplateAPICache() { + return queryTemplateAPICache; + } + + + public Cache getDatasourceAPICache() { + return datasourceAPICache; + } + + + private void initCache() { + this.datasourceAPICache = buildDatasourceCache(); + this.queryTemplateAPICache = buildQueryCacheByName(GrafanaConstants.QUERY_API_CACHE_NAME); + this.encodedQueryCache = buildQueryCacheByName(GrafanaConstants.ENCODED_QUERY_CACHE_NAME); + } + + private Cache buildDatasourceCache() { + return CacheBuilder.newBuilder().build(); + } + + private Cache buildQueryCacheByName(String cacheName) { + int capacity = getCacheCapacity(cacheName); + return CacheBuilder.newBuilder().maximumSize(capacity).build(); + } + + + + private static int getCacheCapacity(String cacheName) { + try { + GrafanaConfiguration configuration = GrafanaConfigurationManager.getInstance().getGrafanaConfiguration(); + CacheConfiguration cacheConfig = configuration.getCacheByName(cacheName); + if (cacheConfig == null) { + log.error("CacheConfiguration config not defined for " + cacheName); + throw new GrafanaManagementException("Query API CacheConfiguration configuration not properly defined"); + } + return cacheConfig.getCapacity(); + } catch (GrafanaManagementException e) { + String errMsg = "Error occurred while initializing cache capacity for " + cacheName; + log.error(errMsg); + throw new RuntimeException(errMsg, e); + } + } + +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/cache/QueryTemplateCacheKey.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/cache/QueryTemplateCacheKey.java new file mode 100644 index 00000000000..175fe3ae288 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/cache/QueryTemplateCacheKey.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2021, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.entgra.analytics.mgt.grafana.proxy.core.service.cache; + +import java.util.Objects; + +public class QueryTemplateCacheKey { + + private final String dashboardUID; + private final String panelId; + private final String refId; + private volatile int hashCode; + + public QueryTemplateCacheKey(String dashboardUID, String panelId, String refId) { + this.dashboardUID = dashboardUID; + this.panelId = panelId; + this.refId = refId; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (obj instanceof QueryTemplateCacheKey) { + final QueryTemplateCacheKey other = (QueryTemplateCacheKey) obj; + String thisId = this.dashboardUID + this.panelId + this.refId; + String otherId = other.dashboardUID + other.panelId + other.refId; + return thisId.equals(otherId); + } + return false; + } + + @Override + public int hashCode() { + if (hashCode == 0) { + hashCode = Objects.hash(dashboardUID, panelId, refId); + } + return hashCode; + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/impl/GrafanaAPIServiceImpl.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/impl/GrafanaAPIServiceImpl.java new file mode 100644 index 00000000000..ae3e91a0c72 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/impl/GrafanaAPIServiceImpl.java @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * you may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.entgra.analytics.mgt.grafana.proxy.core.service.impl; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import io.entgra.analytics.mgt.grafana.proxy.common.exception.GrafanaManagementException; +import io.entgra.analytics.mgt.grafana.proxy.core.exception.DashboardNotFound; +import io.entgra.analytics.mgt.grafana.proxy.core.exception.DatasourceNotFound; +import io.entgra.analytics.mgt.grafana.proxy.core.exception.GrafanaEnvVariablesNotDefined; +import io.entgra.analytics.mgt.grafana.proxy.core.exception.PanelNotFound; +import io.entgra.analytics.mgt.grafana.proxy.core.exception.QueryNotFound; +import io.entgra.analytics.mgt.grafana.proxy.core.exception.TemplateNotFound; +import io.entgra.analytics.mgt.grafana.proxy.core.service.GrafanaAPIService; +import io.entgra.analytics.mgt.grafana.proxy.core.service.bean.Datasource; +import io.entgra.analytics.mgt.grafana.proxy.core.service.cache.CacheManager; +import io.entgra.analytics.mgt.grafana.proxy.core.service.cache.QueryTemplateCacheKey; +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaConstants; +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaUtil; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.wso2.carbon.device.mgt.core.common.util.HttpUtil; +import org.wso2.carbon.device.mgt.core.report.mgt.Constants; + +import javax.ws.rs.core.HttpHeaders; +import java.io.IOException; + +public class GrafanaAPIServiceImpl implements GrafanaAPIService { + + private static final Log log = LogFactory.getLog(GrafanaAPIServiceImpl.class); + + public String getQueryTemplate(String dashboardUID, String panelId, String refId, + String requestScheme) throws IOException, GrafanaManagementException { + try { + return getPanelQuery(dashboardUID, panelId, refId, requestScheme); + } catch (QueryNotFound e) { + return getTemplateQuery(dashboardUID, refId, requestScheme); + } + } + + /** + * Get predefined query template from grafana for given panel + * @param dashboardUID + * @param panelId + * @param refId + * @param requestScheme + * @return query of the given panel + * @throws IOException + * @throws GrafanaManagementException + */ + public String getPanelQuery(String dashboardUID, String panelId, String refId, + String requestScheme) throws IOException, GrafanaManagementException { + JsonObject panel = getPanelDetails(dashboardUID, panelId, requestScheme); + JsonArray queries = panel.getAsJsonArray(GrafanaConstants.DASHBOARD_PANEL_DETAIL_QUERIES_KEY); + JsonObject query = getQueryByRefId(queries, refId); + if (query == null) { + throw new QueryNotFound("No query exists for {dashboard: " + dashboardUID + + ", panelId: " + panelId + ", refId: " + refId + "}"); + } + String queryTemplate = query.get(GrafanaConstants.RAW_SQL_KEY).getAsString(); + CacheManager.getInstance().getQueryTemplateAPICache(). + put(new QueryTemplateCacheKey(dashboardUID, panelId, refId), queryTemplate); + return queryTemplate; + } + + /** + * Get predefined query template from grafana for given variable (template variable) + * Note: Here template query means the grafana template variable queries + * @param dashboardUID + * @param refId + * @param requestScheme + * @return query of grafana template variable + * @throws IOException + * @throws GrafanaManagementException + */ + // Here template query means the grafana template variable queries + public String getTemplateQuery(String dashboardUID, String refId, + String requestScheme) throws IOException, GrafanaManagementException { + JsonObject template = getTemplatingDetails(dashboardUID, refId, requestScheme); + JsonElement queryElement = template.get(GrafanaConstants.TEMPLATE_QUERY_KEY); + if (queryElement == null) { + throw new QueryNotFound("No template query exists for {dashboard: " + dashboardUID + + ", refId: " + refId + "}"); + } + String query = queryElement.getAsString(); + CacheManager.getInstance().getQueryTemplateAPICache(). + put(new QueryTemplateCacheKey(dashboardUID, null, refId), query); + return query; + } + + /** + * Get grafana template variable details of given dashboard uid + * @param dashboardUID + * @param refId + * @param requestScheme + * @return Template variable details of given dashboard uid + * @throws IOException + * @throws GrafanaManagementException + */ + public JsonObject getTemplatingDetails(String dashboardUID, String refId, String requestScheme) throws + IOException, GrafanaManagementException { + JsonObject dashboard = getDashboardDetails(dashboardUID, requestScheme); + JsonObject template = getTemplateByRefId(dashboard, refId); + if (template == null) { + String errorMsg = "Template for {refId: " + refId + ", dashboard: " + dashboardUID + "} is not found"; + log.error(errorMsg); + throw new TemplateNotFound(errorMsg); + } + return template; + } + + public JsonObject getPanelDetails(String dashboardUID, String panelId, String requestScheme) throws + IOException, GrafanaManagementException { + JsonObject dashboard = getDashboardDetails(dashboardUID, requestScheme); + JsonObject panel = getPanelById(dashboard, panelId); + if (panel == null) { + String errorMsg = "Panel " + panelId + " for dashboard " + dashboardUID + " is not found"; + log.error(errorMsg); + throw new PanelNotFound(errorMsg); + } + return panel; + } + + public JsonObject getDashboardDetails(String dashboardUID, String requestScheme) throws IOException, + GrafanaManagementException { + String dashboardAPI = generateGrafanaAPIBaseUri(GrafanaConstants.DASHBOARD_API, requestScheme) + dashboardUID; + HttpGet request = new HttpGet(dashboardAPI); + JsonObject dashboardResponseJsonBody = getGrafanaAPIJsonResponse(request); + JsonObject dashboardDetails = dashboardResponseJsonBody.getAsJsonObject(GrafanaConstants.DASHBOARD_KEY); + if (dashboardDetails == null) { + throw new DashboardNotFound("Grafana response: " + dashboardResponseJsonBody); + } + return dashboardResponseJsonBody.getAsJsonObject(GrafanaConstants.DASHBOARD_KEY); + } + + public Datasource getDatasource(int datasourceId, String requestScheme) throws IOException, GrafanaManagementException { + String datasourceAPI = generateGrafanaAPIBaseUri(GrafanaConstants.DATASOURCE_API, requestScheme) + datasourceId; + HttpGet request = new HttpGet(datasourceAPI); + JsonObject datasourceDetails = getGrafanaAPIJsonResponse(request); + if (datasourceDetails.get(GrafanaConstants.DATASOURCE_NAME_KEY) == null) { + throw new DatasourceNotFound("Grafana response: " + datasourceDetails); + } + String url = datasourceDetails.get(GrafanaConstants.DATASOURCE_URL_KEY).getAsString(); + String name = datasourceDetails.get(GrafanaConstants.DATASOURCE_NAME_KEY).getAsString(); + String type = datasourceDetails.get(GrafanaConstants.DATASOURCE_TYPE_KEY).getAsString(); + String database = datasourceDetails.get(GrafanaConstants.DATASOURCE_DB_KEY).getAsString(); + Datasource ds = new Datasource(datasourceId, url, name, type, database); + CacheManager.getInstance().getDatasourceAPICache().put(datasourceId, ds); + return ds; + } + + private String generateGrafanaAPIBaseUri(String api, String requestScheme) throws GrafanaEnvVariablesNotDefined { + return GrafanaUtil.getGrafanaHTTPBase(requestScheme) + api + Constants.URI_SEPARATOR; + } + + + private JsonObject getGrafanaAPIJsonResponse(HttpRequestBase request) throws IOException, + GrafanaManagementException { + try(CloseableHttpClient client = HttpClients.createDefault()) { + HttpResponse response = invokeAPI(client, request); + return GrafanaUtil.getJsonBody(HttpUtil.getResponseString(response)); + } + } + + private HttpResponse invokeAPI(HttpClient client, HttpRequestBase request) throws IOException, GrafanaManagementException { + setBasicAuthHeader(request); + return client.execute(request); + } + + private void setBasicAuthHeader(HttpRequestBase request) throws GrafanaManagementException { + String basicAuth = GrafanaUtil.getBasicAuthBase64Header(); + request.setHeader(HttpHeaders.AUTHORIZATION, basicAuth); + } + + private JsonObject getQueryByRefId(JsonArray queries, String refId) { + for (int i = 0; i < queries.size(); i++) { + JsonObject query = queries.get(i).getAsJsonObject(); + String queryRefId = query.get(GrafanaConstants.QUERY_REF_ID_KEY).getAsString(); + if (queryRefId.equals(refId)) { + return query; + } + } + return null; + } + + private JsonObject getTemplateByRefId(JsonObject dashboard, String refId) { + JsonArray templates = dashboard.getAsJsonObject(GrafanaConstants.TEMPLATING_KEY). + getAsJsonArray(GrafanaConstants.TEMPLATING_LIST_KEY); + for (int i = 0; i < templates.size(); i++) { + JsonObject templateObj = templates.get(i).getAsJsonObject(); + String name = templateObj.get(GrafanaConstants.TEMPLATING_NAME_KEY).getAsString(); + // RefId in query body corresponds to template name + if (refId.equals(name)) { + return templateObj; + } + } + return null; + } + + private JsonObject getPanelById(JsonObject dashboard, String panelId) { + JsonArray panels = dashboard.getAsJsonArray(GrafanaConstants.PANEL_KEY); + for (int i = 0; i < panels.size(); i++) { + JsonObject panelObj = panels.get(i).getAsJsonObject(); + String id = panelObj.get(GrafanaConstants.ID_KEY).getAsString(); + if (id.equals(panelId)) { + return panelObj; + } + } + return null; + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/impl/GrafanaQueryServiceImpl.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/impl/GrafanaQueryServiceImpl.java new file mode 100644 index 00000000000..b15ebed76ab --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/service/impl/GrafanaQueryServiceImpl.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * you may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.entgra.analytics.mgt.grafana.proxy.core.service.impl; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import io.entgra.analytics.mgt.grafana.proxy.common.exception.GrafanaManagementException; +import io.entgra.analytics.mgt.grafana.proxy.core.exception.MaliciousQueryAttempt; +import io.entgra.analytics.mgt.grafana.proxy.core.exception.QueryMisMatch; +import io.entgra.analytics.mgt.grafana.proxy.core.exception.QueryNotFound; +import io.entgra.analytics.mgt.grafana.proxy.core.service.GrafanaAPIService; +import io.entgra.analytics.mgt.grafana.proxy.core.service.GrafanaQueryService; +import io.entgra.analytics.mgt.grafana.proxy.core.service.bean.Datasource; +import io.entgra.analytics.mgt.grafana.proxy.core.service.cache.CacheManager; +import io.entgra.analytics.mgt.grafana.proxy.core.service.cache.QueryTemplateCacheKey; +import io.entgra.analytics.mgt.grafana.proxy.core.sql.query.GrafanaPreparedQueryBuilder; +import io.entgra.analytics.mgt.grafana.proxy.core.sql.query.PreparedQuery; +import io.entgra.analytics.mgt.grafana.proxy.core.sql.query.encoder.QueryEncoderFactory; +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaConstants; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.mgt.common.exceptions.DBConnectionException; + +import java.io.IOException; +import java.net.URI; +import java.sql.SQLException; + +public class GrafanaQueryServiceImpl implements GrafanaQueryService { + + private static final Log log = LogFactory.getLog(GrafanaQueryServiceImpl.class); + private final GrafanaAPIService grafanaAPIService; + + public GrafanaQueryServiceImpl(GrafanaAPIService grafanaAPIService) { + this.grafanaAPIService = grafanaAPIService; + } + + public void buildSafeQuery(JsonObject queryRequestBody, String dashboardUID, String panelId, URI requestUri) + throws IOException, SQLException, GrafanaManagementException, DBConnectionException, io.entgra.application.mgt.common.exception.DBConnectionException { + JsonArray queries = queryRequestBody.getAsJsonArray(GrafanaConstants.QUERY_BODY_QUERIES_KEY); + for (int i = 0; i < queries.size(); i++) { + JsonObject queryObj = queries.get(i).getAsJsonObject(); + JsonElement refIdJson = queryObj.get(GrafanaConstants.QUERY_REF_ID_KEY); + JsonElement rawSqlJson = queryObj.get(GrafanaConstants.RAW_SQL_KEY); + JsonElement datasourceIdJson = queryObj.get(GrafanaConstants.DATASOURCE_ID_KEY); + if (refIdJson == null || rawSqlJson == null || datasourceIdJson == null) { + String errMsg = "Query json body: refId, rawSql and datasourceId cannot be null"; + log.error(errMsg); + throw new MaliciousQueryAttempt(errMsg); + } + String refId = refIdJson.getAsString(); + String rawSql = rawSqlJson.getAsString(); + int datasourceId = datasourceIdJson.getAsInt(); + CacheManager cacheManager = CacheManager.getInstance(); + String encodedQuery = cacheManager.getEncodedQueryCache().getIfPresent(rawSql); + if (cacheManager.getEncodedQueryCache().getIfPresent(rawSql) != null) { + queryObj.addProperty(GrafanaConstants.RAW_SQL_KEY, encodedQuery); + return; + } + Datasource datasource = cacheManager.getDatasourceAPICache().getIfPresent(datasourceId); + if (datasource == null) { + datasource = grafanaAPIService.getDatasource(datasourceId, requestUri.getScheme()); + } + String queryTemplate = cacheManager.getQueryTemplateAPICache(). + getIfPresent(new QueryTemplateCacheKey(dashboardUID, panelId, refId)); + try { + if (queryTemplate != null) { + try { + encodeQuery(queryObj, datasource, queryTemplate, rawSql); + } catch (QueryMisMatch e) { + log.error("Error occurred while encoding query, " + + "retrying to encode by getting the query template from api instead of cache", e); + queryTemplate = grafanaAPIService.getQueryTemplate(dashboardUID, panelId, refId, requestUri.getScheme()); + encodeQuery(queryObj, datasource, queryTemplate, rawSql); + } + } else { + queryTemplate = grafanaAPIService.getQueryTemplate(dashboardUID, panelId, refId, requestUri.getScheme()); + encodeQuery(queryObj, datasource, queryTemplate, rawSql); + } + } catch (QueryNotFound e) { + String errMsg = "No query exists for {dashboard: " + dashboardUID + + ", panelId: " + panelId + ", refId: " + refId + "}"; + log.error(errMsg); + throw new QueryNotFound(errMsg); + } + } + } + + private void encodeQuery(JsonObject queryObj, Datasource datasource, String queryTemplate, String rawSql) + throws SQLException, GrafanaManagementException, DBConnectionException, + io.entgra.application.mgt.common.exception.DBConnectionException { + PreparedQuery pq = GrafanaPreparedQueryBuilder.build(queryTemplate, rawSql); + String encodedQuery = QueryEncoderFactory.createEncoder(datasource.getType(), datasource.getName()).encode(pq); + CacheManager.getInstance().getEncodedQueryCache().put(rawSql, encodedQuery); + if(log.isDebugEnabled()) { + log.debug("Encoded query: " + encodedQuery); + } + queryObj.addProperty(GrafanaConstants.RAW_SQL_KEY, encodedQuery); + } + +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/connection/GrafanaDatasourceConnectionFactory.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/connection/GrafanaDatasourceConnectionFactory.java new file mode 100644 index 00000000000..808b8590387 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/connection/GrafanaDatasourceConnectionFactory.java @@ -0,0 +1,66 @@ +package io.entgra.analytics.mgt.grafana.proxy.core.sql.connection; + +import io.entgra.application.mgt.core.config.Configuration; +import io.entgra.application.mgt.core.config.ConfigurationManager; +import io.entgra.application.mgt.core.util.ConnectionManagerUtil; +import org.wso2.carbon.device.mgt.common.exceptions.DBConnectionException; +import org.wso2.carbon.device.mgt.core.config.DeviceConfigurationManager; +import org.wso2.carbon.device.mgt.core.config.DeviceManagementConfig; +import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory; +import org.wso2.carbon.device.mgt.core.report.mgt.config.ReportMgtConfiguration; +import org.wso2.carbon.device.mgt.core.report.mgt.config.ReportMgtConfigurationManager; +import org.wso2.carbon.device.mgt.core.report.mgt.dao.common.ReportMgtConnectionManager; + +import java.sql.Connection; +import java.sql.SQLException; + +public class GrafanaDatasourceConnectionFactory { + + private static final ReportMgtConfiguration reportMgtConfiguration= ReportMgtConfigurationManager.getInstance(). + getConfiguration(); + private static final DeviceManagementConfig deviceManagementConfig = DeviceConfigurationManager.getInstance(). + getDeviceManagementConfig(); + + private static final Configuration applicationMgtConfig = ConfigurationManager.getInstance().getConfiguration(); + + public static Connection getConnection(String databaseName) throws SQLException, DBConnectionException, + io.entgra.application.mgt.common.exception.DBConnectionException { + if(databaseName.equals(getReportManagementDatasourceName())) { + ReportMgtConnectionManager.openDBConnection(); + return ReportMgtConnectionManager.getDBConnection(); + } else if (databaseName.equals(getDeviceManagementDatasourceName())) { + DeviceManagementDAOFactory.openConnection(); + return DeviceManagementDAOFactory.getConnection(); + } else if (databaseName.equals(getApplicationManagementDatasourceName())) { + ConnectionManagerUtil.openDBConnection(); + return ConnectionManagerUtil.getDBConnection(); + } else { + throw new RuntimeException("No such datasource with the name: " + databaseName); + } + } + public static void closeConnection(String databaseName) { + if(databaseName.equals(getReportManagementDatasourceName())) { + ReportMgtConnectionManager.closeDBConnection(); + } else if (databaseName.equals(getDeviceManagementDatasourceName())) { + DeviceManagementDAOFactory.closeConnection(); + } else if (databaseName.equals(getApplicationManagementDatasourceName())) { + ConnectionManagerUtil.closeDBConnection(); + } else { + throw new RuntimeException("No such datasource with the name: " + databaseName); + } + } + + private static String getReportManagementDatasourceName() { + return reportMgtConfiguration.getDatasourceName(); + } + + private static String getDeviceManagementDatasourceName() { + return deviceManagementConfig.getDeviceManagementConfigRepository().getDataSourceConfig(). + getJndiLookupDefinition().getJndiName(); + } + + private static String getApplicationManagementDatasourceName() { + return applicationMgtConfig.getDatasourceName(); + } + +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/GrafanaPreparedQueryBuilder.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/GrafanaPreparedQueryBuilder.java new file mode 100644 index 00000000000..8b8606dacb8 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/GrafanaPreparedQueryBuilder.java @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * you may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.entgra.analytics.mgt.grafana.proxy.core.sql.query; + +import io.entgra.analytics.mgt.grafana.proxy.core.exception.QueryMisMatch; +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaConstants; +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaUtil; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class GrafanaPreparedQueryBuilder { + + private static final Log log = LogFactory.getLog(GrafanaPreparedQueryBuilder.class); + + private static final String QUERY_MISMATCH_EXCEPTION_MESSAGE = "Grafana query api request rawSql does not match relevant query template"; + private static final String COMMA_SEPARATOR = ","; + private static final String VAR_PARAM_TEMPLATE = "$param"; + private static final String GRAFANA_QUOTED_VAR_REGEX = "('\\$(\\d|\\w|_)+')|('\\$\\{.*?\\}')|(\"\\$(\\d|\\w|_)+\")|(\"\\$\\{.*?\\}\")"; + private static final String GRAFANA_VAR_REGEX = "(\\$(\\d|\\w|_)+)|(\\$\\{.*?\\})"; + + + public static PreparedQuery build(String queryTemplate, String rawQuery) throws QueryMisMatch { + // In Grafana, a variable can coexist with a hardcoded parameter (ie: '${__from:date:YYYY-MM-DD} 00:00:00') + // hence this function templates the whole parameter in order to make valid prepared statement + queryTemplate = templateParamWGrafanaVariables(queryTemplate); + + String queryUpToVarStart = queryTemplate; + String rawQueryUpToVarStart = rawQuery; + + // Pattern matcher to get query string before the first grafana variable + Pattern toVarStart = Pattern.compile("([^\\$]+(?=\"\\$))|([^\\$]+(?='\\$))|([^\\$]+(?=\\$))"); + Matcher matchToVarStart = toVarStart.matcher(queryTemplate); + + if (matchToVarStart.find()) { + queryUpToVarStart = matchToVarStart.group(); + if(rawQuery.length() < queryUpToVarStart.length()) { + throw new QueryMisMatch(QUERY_MISMATCH_EXCEPTION_MESSAGE); + } + rawQueryUpToVarStart = rawQuery.substring(0, queryUpToVarStart.length()); + } + if(!queryUpToVarStart.equals(rawQueryUpToVarStart)){ + throw new QueryMisMatch(QUERY_MISMATCH_EXCEPTION_MESSAGE); + } + StringBuilder preparedQueryBuilder = new StringBuilder().append(queryUpToVarStart); + + String queryFromVarStart = queryTemplate.substring(queryUpToVarStart.length()); + String rawQueryFromVarStart = rawQuery.substring(queryUpToVarStart.length()); + queryTemplate = queryFromVarStart; + rawQuery = rawQueryFromVarStart; + Pattern varPattern = Pattern.compile(GRAFANA_QUOTED_VAR_REGEX + "|" + GRAFANA_VAR_REGEX); + Matcher varMatch = varPattern.matcher(queryTemplate); + List parameters = new ArrayList<>(); + while(varMatch.find()) { + String currentVar = varMatch.group(); + // Pattern matcher to get query string between grafana current variable and next variable + matchToVarStart = toVarStart.matcher(queryTemplate.substring(currentVar.length())); + + String matchToLookBehindRawQuery; + if (matchToVarStart.find()) { + matchToLookBehindRawQuery = matchToVarStart.group(); + } else { + // If next variable does not exist get query string after the current variable + matchToLookBehindRawQuery = queryTemplate.substring(currentVar.length()); + } + String currentVarInput; + if (matchToLookBehindRawQuery.isEmpty()) { + // If there is no string after the current variable, then remaining segment of rawQuery is the + // current variable input (rawQuery is sliced up to the next variable) + currentVarInput = rawQuery; + } else { + Matcher lookBehindRQ = Pattern.compile("(.*?)(?=" + Pattern.quote(matchToLookBehindRawQuery) + ")").matcher(rawQuery); + if (!lookBehindRQ.find()) { + throw new QueryMisMatch(QUERY_MISMATCH_EXCEPTION_MESSAGE); + } + currentVarInput = lookBehindRQ.group(); + } + if (isTenantIdVar(currentVar)) { + preparedQueryBuilder.append(GrafanaUtil.getTenantId()); + } else { + // Grafana variable input can be multi-valued, which are separated by comma by default + String[] varValues = splitByComma(currentVarInput); + List preparedStatementPlaceHolders = new ArrayList<>(); + for (String v : varValues) { + String param = unQuoteString(v); + if (isSafeVariableInput(param)) { + preparedStatementPlaceHolders.add(v); + } else { + parameters.add(param); + preparedStatementPlaceHolders.add(PreparedQuery.PREPARED_SQL_PARAM_PLACEHOLDER); + } + } + preparedQueryBuilder.append(String.join(COMMA_SEPARATOR, preparedStatementPlaceHolders)); + } + preparedQueryBuilder.append(matchToLookBehindRawQuery); + // Get template and raw query string from next variable + queryTemplate = queryTemplate.substring(currentVar.length() + matchToLookBehindRawQuery.length()); + rawQuery = rawQuery.substring(currentVarInput.length() + matchToLookBehindRawQuery.length()); + + varMatch = varPattern.matcher(queryTemplate); + } + if (!queryTemplate.equals(rawQuery)) { + throw new QueryMisMatch(QUERY_MISMATCH_EXCEPTION_MESSAGE); + } + return new PreparedQuery(preparedQueryBuilder.toString(), parameters); + } + + private static String[] splitByComma(String str) { + // Using regex to avoid splitting by comma inside quotes + return str.split("(\\s|\\t)*,(\\s|\\t)*(?=(?:[^'\"]*['|\"][^'\"]*['|\"])*[^'\"]*$)"); + } + + private static String templateParamWGrafanaVariables(String queryTemplate) { + // TODO: handle escaped quotes and special characters + Pattern quotedStringPattern = Pattern.compile("(\"(.+?)\")|('(.+?)')"); + Matcher quotedStringMatch = quotedStringPattern.matcher(queryTemplate); + while(quotedStringMatch.find()) { + String quotedString = quotedStringMatch.group(); + Matcher varMatcher = Pattern.compile(GRAFANA_VAR_REGEX).matcher(quotedString); + // If grafana variable exists in single quoted string + if(varMatcher.find()) { + String var = varMatcher.group(); + if (!isTenantIdVar(var)) { + String templatedQuotedString = templateQuotedString(quotedString); + // escape any special characters + templatedQuotedString = Matcher.quoteReplacement(templatedQuotedString); + queryTemplate = queryTemplate.replaceFirst(Pattern.quote(quotedString), templatedQuotedString); + } + } + } + return queryTemplate; + } + + private static String templateQuotedString(String quotedString) { + return quotedString.replaceFirst("[^']+|[^\"]+", + Matcher.quoteReplacement(VAR_PARAM_TEMPLATE)); + } + + private static boolean isSafeVariableInput(String currentVarInput) { + if (StringUtils.isEmpty(currentVarInput)) { + return true; + } + return currentVarInput.matches("\\$?[a-zA-Z0-9-_\\.]+|^\"[a-zA-Z0-9-_\\.\\s]+\"$|^'[a-zA-Z0-9-_\\.\\s]+'$"); + } + + private static String unQuoteString(String str) { + if (isQuoted(str)) { + int firstCharIndex = 0; + int lastCharIndex = str.length() - 1; + return str.substring(firstCharIndex + 1, lastCharIndex); + } + return str; + } + + private static boolean isQuoted(String str) { + if (StringUtils.isEmpty(str)) { + return false; + } + int firstCharIndex = 0; + int lastCharIndex = str.length() - 1; + return (str.charAt(firstCharIndex) == '\'' && str.charAt(lastCharIndex) == '\'') || + (str.charAt(firstCharIndex) == '"' && str.charAt(lastCharIndex) == '"'); + } + + private static boolean isTenantIdVar(String var) { + return var.equals(GrafanaConstants.TENANT_ID_VAR) || var.equals(singleQuoteString(GrafanaConstants.TENANT_ID_VAR)) + || var.equals(doubleQuoteString(GrafanaConstants.TENANT_ID_VAR)); + } + + private static String doubleQuoteString(String str) { + return "\"" + str + "\""; + } + + private static String singleQuoteString(String str) { + return "'" + str + "'"; + } + +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/PreparedQuery.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/PreparedQuery.java new file mode 100644 index 00000000000..95408410617 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/PreparedQuery.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * you may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.entgra.analytics.mgt.grafana.proxy.core.sql.query; + +import java.util.List; + +public class PreparedQuery { + + public static final String PREPARED_SQL_PARAM_PLACEHOLDER = "?"; + + private String preparedSQL; + private List parameters; + + public PreparedQuery(String preparedSQL, List parameters) { + this.preparedSQL = preparedSQL; + this.parameters = parameters; + } + + public String getPreparedSQL() { + return preparedSQL; + } + + public void setPreparedSQL(String preparedSQL) { + this.preparedSQL = preparedSQL; + } + + public List getParameters() { + return parameters; + } + + public void setParameters(List parameters) { + this.parameters = parameters; + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/encoder/GenericQueryEncoder.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/encoder/GenericQueryEncoder.java new file mode 100644 index 00000000000..7b6693689fb --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/encoder/GenericQueryEncoder.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * you may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.entgra.analytics.mgt.grafana.proxy.core.sql.query.encoder; + +import io.entgra.analytics.mgt.grafana.proxy.core.sql.query.PreparedQuery; +import org.apache.commons.lang.StringEscapeUtils; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class GenericQueryEncoder implements QueryEncoder { + + public GenericQueryEncoder() { + } + + @Override + public String encode(PreparedQuery preparedQuery) throws SQLException { + return generateEncodedSQL(preparedQuery.getPreparedSQL(), preparedQuery.getParameters()); + } + + private String generateEncodedSQL(String preparedSQL, List parameters) throws SQLException { + List escapedParams = escapeParameters(parameters); + Matcher placeHolderMatcher = Pattern.compile(Pattern.quote(PreparedQuery.PREPARED_SQL_PARAM_PLACEHOLDER)).matcher(preparedSQL); + StringBuffer sb = new StringBuffer(); + for (String param: escapedParams) { + if(placeHolderMatcher.find()) { + placeHolderMatcher.appendReplacement(sb, Matcher.quoteReplacement(param)); + } else { + String errMsg = "Given parameter count doesn't match parameters available in SQL"; + throw new SQLException(errMsg); + } + } + return sb.toString(); + } + + private List escapeParameters(List parameters) { + List escapedParams = new ArrayList<>(); + for (String param : parameters) { + String escapedParam = StringEscapeUtils.escapeSql(param); + escapedParams.add(escapedParam); + } + return escapedParams; + } + +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/encoder/MySQLQueryEncoder.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/encoder/MySQLQueryEncoder.java new file mode 100644 index 00000000000..faa6977b786 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/encoder/MySQLQueryEncoder.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * you may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.entgra.analytics.mgt.grafana.proxy.core.sql.query.encoder; + +import io.entgra.analytics.mgt.grafana.proxy.core.sql.connection.GrafanaDatasourceConnectionFactory; +import io.entgra.analytics.mgt.grafana.proxy.core.sql.query.PreparedQuery; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.mgt.common.exceptions.DBConnectionException; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.List; + +public class MySQLQueryEncoder implements QueryEncoder { + + private static final Log log = LogFactory.getLog(MySQLQueryEncoder.class); + private static final String PREPARED_STATEMENT_STRING_OBJECT_ID_SEPARATOR = ": "; + + private final String databaseName; + + public MySQLQueryEncoder(String databaseName) { + this.databaseName = databaseName; + } + + @Override + public String encode(PreparedQuery preparedQuery) throws SQLException, DBConnectionException, + io.entgra.application.mgt.common.exception.DBConnectionException { + try { + Connection con = GrafanaDatasourceConnectionFactory.getConnection(databaseName); + PreparedStatement stmt = con.prepareStatement(preparedQuery.getPreparedSQL()); + setParameters(stmt, preparedQuery.getParameters()); + return generateQueryFromPreparedStatement(stmt); + } finally { + GrafanaDatasourceConnectionFactory.closeConnection(databaseName); + } + } + + public void setParameters(PreparedStatement stmt, List parameters) + throws SQLException { + int i = 0; + for (String p : parameters) { + stmt.setObject(++i, p); + } + } + + private String generateQueryFromPreparedStatement(PreparedStatement stmt) { + String query = stmt.toString().substring(stmt.toString().indexOf(PREPARED_STATEMENT_STRING_OBJECT_ID_SEPARATOR) + + PREPARED_STATEMENT_STRING_OBJECT_ID_SEPARATOR.length()); + // Below is how generated string of prepared statement may look like + // org.apache.tomcat.jdbc.pool.StatementFacade$StatementProxy[Proxy=1138452482; Query=; Delegate=com.mysql.cj.jdbc.ClientPreparedStatement: ] + // This removes unnecessary "]" char at the end that may remain after extracting the query + if (query.charAt(query.length() - 1) == ']') { + query = query.substring(0, query.length() - 1); + } + return query; + } + +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/encoder/QueryEncoder.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/encoder/QueryEncoder.java new file mode 100644 index 00000000000..81d7735a811 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/encoder/QueryEncoder.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * you may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.entgra.analytics.mgt.grafana.proxy.core.sql.query.encoder; + +import io.entgra.analytics.mgt.grafana.proxy.core.sql.query.PreparedQuery; +import org.wso2.carbon.device.mgt.common.exceptions.DBConnectionException; + +import java.sql.SQLException; + +public interface QueryEncoder { + + String encode(PreparedQuery preparedQuery) throws SQLException, DBConnectionException, io.entgra.application.mgt.common.exception.DBConnectionException; + +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/encoder/QueryEncoderFactory.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/encoder/QueryEncoderFactory.java new file mode 100644 index 00000000000..fe426f1f20c --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/sql/query/encoder/QueryEncoderFactory.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * you may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.entgra.analytics.mgt.grafana.proxy.core.sql.query.encoder; + +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaConstants; + +public class QueryEncoderFactory { + + public static QueryEncoder createEncoder(String datasourceType, String databaseName) { + if (datasourceType.equals(GrafanaConstants.DATASOURCE_TYPE.MYSQL)) { + return new MySQLQueryEncoder(databaseName); + } else { + return new GenericQueryEncoder(); + } + + } +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/util/GrafanaConstants.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/util/GrafanaConstants.java new file mode 100644 index 00000000000..b8a33f8b329 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/util/GrafanaConstants.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * you may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.entgra.analytics.mgt.grafana.proxy.core.util; + +public class GrafanaConstants { + + public static final String QUERY_API_CACHE_NAME = "queryAPICache"; + public static final String ENCODED_QUERY_CACHE_NAME = "encodedQueryCache"; + public static final String REFERER_HEADER = "Referer"; + public static final String CONFIG_XML_NAME = "grafana-config.xml"; + public static final String X_GRAFANA_ORG_ID_HEADER = "x-org-grafana-id"; + + public static class XML { + public static final String FEATURES_DISALLOW_DOCTYPE_DECL = "http://apache.org/xml/features/disallow-doctype-decl"; + } + + public static class DATASOURCE_TYPE { + public static final String MYSQL = "mysql"; + public static final String POSTGRESQL = "postgresql"; + public static final String MICROSOFT_SQL_SERVER = "mssql"; + public static final String ORACLE = "oracle"; + } + + public static final int IFRAME_URL_DASHBOARD_UID_INDEX = 1; + + public static final String TENANT_ID_VAR_NAME = "tenantId"; + public static final String VAR_PREFIX = "$"; + public static final String TENANT_ID_VAR = VAR_PREFIX + TENANT_ID_VAR_NAME; + public static final String QUERY_PARAM_VAR_PREFIX = "var-"; + public static final String TENANT_ID_VAR_QUERY_PARAM = QUERY_PARAM_VAR_PREFIX + TENANT_ID_VAR_NAME; + public static final String API_PATH = "/api"; + public static final String DASHBOARD_KEY = "dashboard"; + public static final String DATASOURCE_TYPE_KEY = "type"; + public static final String DATASOURCE_DB_KEY = "database"; + public static final String DATASOURCE_ID_KEY = "datasourceId"; + public static final String DATASOURCE_NAME_KEY = "name"; + public static final String DATASOURCE_URL_KEY = "name"; + public static final String PANEL_KEY = "panels"; + public static final String TEMPLATING_KEY = "templating"; + public static final String TEMPLATING_NAME_KEY = "name"; + public static final String TEMPLATE_QUERY_KEY = "query"; + public static final String TEMPLATING_LIST_KEY = "list"; + public static final String RAW_SQL_KEY = "rawSql"; + public static final String QUERY_BODY_QUERIES_KEY = "queries"; + public static final String DASHBOARD_PANEL_DETAIL_QUERIES_KEY = "targets"; + public static final String QUERY_REF_ID_KEY = "refId"; + public static final String PANEL_ID_QUERY_PARAM = "panelId"; + public static final String ORG_ID_QUERY_PARAM = "orgId"; + public static final String ID_KEY = "id"; + + public static final String WS_LIVE_API = "/api/live/ws"; + public static final String DASHBOARD_API = "/api/dashboards/uid"; + public static final String DATASOURCE_API = "/api/datasources"; + public static final String HTTP_HOST_ENV_VAR = "iot.grafana.http.host"; + public static final String HTTPS_HOST_ENV_VAR = "iot.grafana.https.host"; +} diff --git a/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/util/GrafanaUtil.java b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/util/GrafanaUtil.java new file mode 100644 index 00000000000..40117cee605 --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.core/src/main/java/io/entgra/analytics/mgt/grafana/proxy/core/util/GrafanaUtil.java @@ -0,0 +1,117 @@ +/* Copyright (c) 2020, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.entgra.analytics.mgt.grafana.proxy.core.util; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import io.entgra.analytics.mgt.grafana.proxy.common.exception.GrafanaManagementException; +import io.entgra.analytics.mgt.grafana.proxy.core.bean.GrafanaPanelIdentifier; +import io.entgra.analytics.mgt.grafana.proxy.core.config.GrafanaConfiguration; +import io.entgra.analytics.mgt.grafana.proxy.core.config.GrafanaConfigurationManager; +import io.entgra.analytics.mgt.grafana.proxy.core.exception.GrafanaEnvVariablesNotDefined; +import io.entgra.analytics.mgt.grafana.proxy.core.service.GrafanaAPIService; +import io.entgra.analytics.mgt.grafana.proxy.core.service.GrafanaQueryService; +import io.entgra.analytics.mgt.grafana.proxy.core.service.impl.GrafanaAPIServiceImpl; +import io.entgra.analytics.mgt.grafana.proxy.core.service.impl.GrafanaQueryServiceImpl; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.device.mgt.core.common.util.HttpUtil; +import org.wso2.carbon.device.mgt.core.report.mgt.Constants; + +import java.net.URI; +import java.util.List; +import java.util.Map; + +public class GrafanaUtil { + + private static final Log log = LogFactory.getLog(GrafanaUtil.class); + + public static boolean isGrafanaAPI(String uri) { + return uri.contains(GrafanaConstants.API_PATH); + } + + public static String generateGrafanaUrl(String requestPath, String base) { + base += Constants.URI_SEPARATOR; + return base + requestPath; + } + public static String getGrafanaWebSocketBase(String requestScheme) throws GrafanaEnvVariablesNotDefined { + return getGrafanaBase(requestScheme, Constants.WS_PROTOCOL, Constants.WSS_PROTOCOL); + } + public static String getGrafanaHTTPBase(String requestScheme) throws GrafanaEnvVariablesNotDefined { + return getGrafanaBase(requestScheme, Constants.HTTP_PROTOCOL, Constants.HTTPS_PROTOCOL); + } + + public static String getGrafanaBase(String requestScheme, String httpProtocol, String httpsProtocol) + throws GrafanaEnvVariablesNotDefined { + String grafanaHost = System.getProperty(GrafanaConstants.HTTPS_HOST_ENV_VAR); + String scheme = httpsProtocol; + if (Constants.HTTP_PROTOCOL.equals(requestScheme) || StringUtils.isEmpty(grafanaHost)){ + grafanaHost = System.getProperty(GrafanaConstants.HTTP_HOST_ENV_VAR); + scheme = httpProtocol; + } + if(StringUtils.isEmpty(grafanaHost)) { + String errMsg = "Grafana host is not defined in the iot-server.sh properly."; + log.error(errMsg); + throw new GrafanaEnvVariablesNotDefined(errMsg); + } + return scheme + Constants.SCHEME_SEPARATOR + grafanaHost; + } + + public static String getPanelId(URI iframeURL) { + Map> queryMap = HttpUtil.getQueryMap(iframeURL); + return HttpUtil.getFirstQueryValue(GrafanaConstants.PANEL_ID_QUERY_PARAM, queryMap); + } + public static String getOrgId(URI iframeURL) { + Map> queryMap = HttpUtil.getQueryMap(iframeURL); + return HttpUtil.getFirstQueryValue(GrafanaConstants.ORG_ID_QUERY_PARAM, queryMap); + } + + public static String getDashboardUID(URI iframeURL) { + return HttpUtil.getRequestSubPathFromEnd(iframeURL, GrafanaConstants.IFRAME_URL_DASHBOARD_UID_INDEX); + } + + public static JsonObject getJsonBody(String body) { + return new Gson().fromJson(body, JsonObject.class); + } + + public static String getBasicAuthBase64Header() throws GrafanaManagementException { + GrafanaConfiguration configuration = GrafanaConfigurationManager.getInstance().getGrafanaConfiguration(); + String username = configuration.getAdminUser().getUsername(); + String password = configuration.getAdminUser().getPassword(); + return Constants.BASIC_AUTH_HEADER_PREFIX + GrafanaUtil.getBase64Encode(username, password); + } + + public static String getBase64Encode(String key, String value) { + return new String(Base64.encodeBase64((key + ":" + value).getBytes())); + } + + public static GrafanaPanelIdentifier getPanelIdentifierFromReferer(String referer) { + URI refererUri = HttpUtil.createURI(referer); + String orgId = GrafanaUtil.getOrgId(refererUri); + String dashboardUID = GrafanaUtil.getDashboardUID(refererUri); + String panelId = GrafanaUtil.getPanelId(refererUri); + return new GrafanaPanelIdentifier(orgId, dashboardUID, panelId); + } + + public static int getTenantId() { + return PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true); + } +} diff --git a/components/analytics-mgt/grafana-mgt/pom.xml b/components/analytics-mgt/grafana-mgt/pom.xml new file mode 100644 index 00000000000..c27ad6278bb --- /dev/null +++ b/components/analytics-mgt/grafana-mgt/pom.xml @@ -0,0 +1,42 @@ + + + + + + + org.wso2.carbon.devicemgt + analytics-mgt + 5.0.6-SNAPSHOT + ../pom.xml + + + 4.0.0 + grafana-mgt + pom + Entgra - Grafana API Handler Component + http://entgra.io + + + + io.entgra.analytics.mgt.grafana.proxy.core + io.entgra.analytics.mgt.grafana.proxy.common + io.entgra.analytics.mgt.grafana.proxy.api + + + diff --git a/components/analytics-mgt/pom.xml b/components/analytics-mgt/pom.xml new file mode 100644 index 00000000000..82eafa01368 --- /dev/null +++ b/components/analytics-mgt/pom.xml @@ -0,0 +1,20 @@ + + + + carbon-devicemgt + org.wso2.carbon.devicemgt + 5.0.6-SNAPSHOT + ../../pom.xml + + + 4.0.0 + analytics-mgt + pom + Entgra - Analytics Management Component + http://entgra.io + + + grafana-mgt + + + diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.annotations/pom.xml b/components/apimgt-extensions/org.wso2.carbon.apimgt.annotations/pom.xml index 7fb7e2ff3e0..270b13d80e5 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.annotations/pom.xml +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.annotations/pom.xml @@ -22,7 +22,7 @@ apimgt-extensions org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension.api/pom.xml b/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension.api/pom.xml index 58141d635e0..b0699d43b82 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension.api/pom.xml +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension.api/pom.xml @@ -21,7 +21,7 @@ apimgt-extensions org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension/pom.xml b/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension/pom.xml index 8961e7446ef..f4ee8cdd9b6 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension/pom.xml +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension/pom.xml @@ -22,7 +22,7 @@ apimgt-extensions org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension/src/main/java/org/wso2/carbon/apimgt/application/extension/APIManagementProviderService.java b/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension/src/main/java/org/wso2/carbon/apimgt/application/extension/APIManagementProviderService.java index 7405737237e..2507e4f1320 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension/src/main/java/org/wso2/carbon/apimgt/application/extension/APIManagementProviderService.java +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension/src/main/java/org/wso2/carbon/apimgt/application/extension/APIManagementProviderService.java @@ -62,11 +62,13 @@ public interface APIManagementProviderService { * @param applicationName Application Name * @param tokenType Token Type * @param validityPeriod Validity Period + * @param username Name of the user to create the token. If null, set as carbon context user * @return {@link String} Access Token * @throws APIManagerException if error occurred while getting the access token for given scopes, * validity period etc. */ - AccessTokenInfo getAccessToken(String scopes, String[] tags, String applicationName, String tokenType, String validityPeriod) + AccessTokenInfo getAccessToken(String scopes, String[] tags, String applicationName, String + tokenType, String validityPeriod, String username) throws APIManagerException; } diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension/src/main/java/org/wso2/carbon/apimgt/application/extension/APIManagementProviderServiceImpl.java b/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension/src/main/java/org/wso2/carbon/apimgt/application/extension/APIManagementProviderServiceImpl.java index 7ba23fc4cee..d6fd929e137 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension/src/main/java/org/wso2/carbon/apimgt/application/extension/APIManagementProviderServiceImpl.java +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.application.extension/src/main/java/org/wso2/carbon/apimgt/application/extension/APIManagementProviderServiceImpl.java @@ -233,7 +233,7 @@ public class APIManagementProviderServiceImpl implements APIManagementProviderSe @Override public AccessTokenInfo getAccessToken(String scopes, String[] tags, String applicationName, String tokenType, - String validityPeriod) throws APIManagerException { + String validityPeriod, String username) throws APIManagerException { try { String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(true); ApiApplicationKey clientCredentials = getClientCredentials(tenantDomain, tags, applicationName, tokenType, @@ -245,15 +245,22 @@ public class APIManagementProviderServiceImpl implements APIManagementProviderSe throw new APIManagerException(msg); } - String user = - PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername() + "@" + PrivilegedCarbonContext + if (username == null || username.isEmpty()) { + username = + PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername() + "@" + PrivilegedCarbonContext + .getThreadLocalCarbonContext().getTenantDomain(true); + } else { + if (!username.contains("@")) { + username += "@" + PrivilegedCarbonContext .getThreadLocalCarbonContext().getTenantDomain(true); + } + } JWTClientManagerService jwtClientManagerService = APIApplicationManagerExtensionDataHolder.getInstance() .getJwtClientManagerService(); JWTClient jwtClient = jwtClientManagerService.getJWTClient(); AccessTokenInfo accessTokenForAdmin = jwtClient - .getAccessToken(clientCredentials.getConsumerKey(), clientCredentials.getConsumerSecret(), user, + .getAccessToken(clientCredentials.getConsumerKey(), clientCredentials.getConsumerSecret(), username, scopes); return accessTokenForAdmin; diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/pom.xml b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/pom.xml index 415b41e932e..9f27c44e1eb 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/pom.xml +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher/pom.xml @@ -22,7 +22,7 @@ apimgt-extensions org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/apimgt-extensions/pom.xml b/components/apimgt-extensions/pom.xml index 565482a552b..144b8545045 100644 --- a/components/apimgt-extensions/pom.xml +++ b/components/apimgt-extensions/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../../pom.xml diff --git a/components/application-mgt/io.entgra.application.mgt.addons/pom.xml b/components/application-mgt/io.entgra.application.mgt.addons/pom.xml index c2f9a31f7d0..d113cfd633a 100644 --- a/components/application-mgt/io.entgra.application.mgt.addons/pom.xml +++ b/components/application-mgt/io.entgra.application.mgt.addons/pom.xml @@ -20,7 +20,7 @@ application-mgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/application-mgt/io.entgra.application.mgt.api/pom.xml b/components/application-mgt/io.entgra.application.mgt.api/pom.xml index 2c2928887d8..4fb755bab0d 100644 --- a/components/application-mgt/io.entgra.application.mgt.api/pom.xml +++ b/components/application-mgt/io.entgra.application.mgt.api/pom.xml @@ -22,7 +22,7 @@ application-mgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/application-mgt/io.entgra.application.mgt.common/pom.xml b/components/application-mgt/io.entgra.application.mgt.common/pom.xml index 276bac4b983..c18db9cdd10 100644 --- a/components/application-mgt/io.entgra.application.mgt.common/pom.xml +++ b/components/application-mgt/io.entgra.application.mgt.common/pom.xml @@ -21,7 +21,7 @@ org.wso2.carbon.devicemgt application-mgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/application-mgt/io.entgra.application.mgt.common/src/main/java/io/entgra/application/mgt/common/DeviceSubscriptionData.java b/components/application-mgt/io.entgra.application.mgt.common/src/main/java/io/entgra/application/mgt/common/DeviceSubscriptionData.java index d769a8b139e..ea1f09e6c5f 100644 --- a/components/application-mgt/io.entgra.application.mgt.common/src/main/java/io/entgra/application/mgt/common/DeviceSubscriptionData.java +++ b/components/application-mgt/io.entgra.application.mgt.common/src/main/java/io/entgra/application/mgt/common/DeviceSubscriptionData.java @@ -23,12 +23,14 @@ import java.sql.Timestamp; public class DeviceSubscriptionData { + private int subId; private String action; private long actionTriggeredTimestamp; private String actionTriggeredBy; private String actionType; private String status; private Device device; + private String currentInstalledVersion; public String getAction() { return action; @@ -77,4 +79,16 @@ public class DeviceSubscriptionData { public void setDevice(Device device) { this.device = device; } + + public String getCurrentInstalledVersion() { return currentInstalledVersion; } + + public void setCurrentInstalledVersion(String currentInstalledVersion) { this.currentInstalledVersion = currentInstalledVersion; } + + public int getSubId() { + return subId; + } + + public void setSubId(int subId) { + this.subId = subId; + } } diff --git a/components/application-mgt/io.entgra.application.mgt.common/src/main/java/io/entgra/application/mgt/common/Filter.java b/components/application-mgt/io.entgra.application.mgt.common/src/main/java/io/entgra/application/mgt/common/Filter.java index 6ce0f2b2d32..16858d7f903 100644 --- a/components/application-mgt/io.entgra.application.mgt.common/src/main/java/io/entgra/application/mgt/common/Filter.java +++ b/components/application-mgt/io.entgra.application.mgt.common/src/main/java/io/entgra/application/mgt/common/Filter.java @@ -108,6 +108,11 @@ public class Filter { */ private String appReleaseState; + /** + * Username of whose favourite apps to be retrieved + */ + private String favouredBy; + public int getLimit() { return limit; } @@ -195,4 +200,12 @@ public class Filter { public String getAppReleaseType() { return appReleaseType; } public void setAppReleaseType(String appReleaseType) { this.appReleaseType = appReleaseType; } + + public String getFavouredBy() { + return favouredBy; + } + + public void setFavouredBy(String favouredBy) { + this.favouredBy = favouredBy; + } } diff --git a/components/application-mgt/io.entgra.application.mgt.common/src/main/java/io/entgra/application/mgt/common/OperationStatusBean.java b/components/application-mgt/io.entgra.application.mgt.common/src/main/java/io/entgra/application/mgt/common/OperationStatusBean.java new file mode 100644 index 00000000000..3446d555760 --- /dev/null +++ b/components/application-mgt/io.entgra.application.mgt.common/src/main/java/io/entgra/application/mgt/common/OperationStatusBean.java @@ -0,0 +1,32 @@ +package io.entgra.application.mgt.common; + +public class OperationStatusBean { + + private int operationId; + private String status; + private String operationCode; + + public int getOperationId() { + return operationId; + } + + public void setOperationId(int operationId) { + this.operationId = operationId; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getOperationCode() { + return operationCode; + } + + public void setOperationCode(String operationCode) { + this.operationCode = operationCode; + } +} diff --git a/components/application-mgt/io.entgra.application.mgt.common/src/main/java/io/entgra/application/mgt/common/dto/ApplicationDTO.java b/components/application-mgt/io.entgra.application.mgt.common/src/main/java/io/entgra/application/mgt/common/dto/ApplicationDTO.java index 48bd1c68228..2877c02f946 100644 --- a/components/application-mgt/io.entgra.application.mgt.common/src/main/java/io/entgra/application/mgt/common/dto/ApplicationDTO.java +++ b/components/application-mgt/io.entgra.application.mgt.common/src/main/java/io/entgra/application/mgt/common/dto/ApplicationDTO.java @@ -96,6 +96,10 @@ public class ApplicationDTO { value = "package name of the application") private String packageName; + @ApiModelProperty(name = "isFavourite", + value = "if the app is favoured by the user") + private boolean isFavourite; + public String getPackageName() { return packageName; } @@ -189,4 +193,12 @@ public class ApplicationDTO { public double getAppRating() { return appRating; } public void setAppRating(double appRating) { this.appRating = appRating; } + + public boolean isFavourite() { + return isFavourite; + } + + public void setFavourite(boolean favourite) { + isFavourite = favourite; + } } diff --git a/components/application-mgt/io.entgra.application.mgt.common/src/main/java/io/entgra/application/mgt/common/response/Application.java b/components/application-mgt/io.entgra.application.mgt.common/src/main/java/io/entgra/application/mgt/common/response/Application.java index fc5251d3a8d..0d11e7bfaaa 100644 --- a/components/application-mgt/io.entgra.application.mgt.common/src/main/java/io/entgra/application/mgt/common/response/Application.java +++ b/components/application-mgt/io.entgra.application.mgt.common/src/main/java/io/entgra/application/mgt/common/response/Application.java @@ -103,6 +103,10 @@ public class Application { value = "package name of the application") private String packageName; + @ApiModelProperty(name = "isFavourite", + value = "if the app is favoured by the user") + private boolean isFavourite; + public String getPackageName() { return packageName; } @@ -181,4 +185,12 @@ public class Application { public String getInstallerName() { return installerName; } public void setInstallerName(String installerName) { this.installerName = installerName; } + + public boolean isFavourite() { + return isFavourite; + } + + public void setFavourite(boolean favourite) { + isFavourite = favourite; + } } diff --git a/components/application-mgt/io.entgra.application.mgt.common/src/main/java/io/entgra/application/mgt/common/services/ApplicationManager.java b/components/application-mgt/io.entgra.application.mgt.common/src/main/java/io/entgra/application/mgt/common/services/ApplicationManager.java index 00c5b4f9e96..12346507da7 100644 --- a/components/application-mgt/io.entgra.application.mgt.common/src/main/java/io/entgra/application/mgt/common/services/ApplicationManager.java +++ b/components/application-mgt/io.entgra.application.mgt.common/src/main/java/io/entgra/application/mgt/common/services/ApplicationManager.java @@ -128,6 +128,26 @@ public interface ApplicationManager { * @throws ApplicationManagementException if any error occurred while creating the application */ Application createApplication(T app) throws ApplicationManagementException; + /** + * Add an application to favourites + * @param appId id of the application + * @throws ApplicationManagementException Catch all other throwing exceptions and throw {@link ApplicationManagementException} + */ + void addAppToFavourites(int appId) throws ApplicationManagementException; + + /** + * Remove an application from favourites + * @param appId id of the application + * @throws ApplicationManagementException Catch all other throwing exceptions and throw {@link ApplicationManagementException} + */ + void removeAppFromFavourites(int appId) throws ApplicationManagementException; + + /** + * Check if an application is a favourite app + * @param appId id of the application + * @throws ApplicationManagementException Catch all other throwing exceptions and throw {@link ApplicationManagementException} + */ + boolean isFavouriteApp(int appId) throws ApplicationManagementException; /** * Check the existence of an application for given application name and the device type. @@ -175,6 +195,8 @@ public interface ApplicationManager { */ void deleteApplicationRelease(String releaseUuid) throws ApplicationManagementException; + ApplicationList getFavouriteApplications(Filter filter) throws ApplicationManagementException; + /** * Use to delete application artifact files (For example this is useful to delete application release artifacts * (I.E screenshots) when an application is deleted) @@ -290,7 +312,18 @@ public interface ApplicationManager { */ ApplicationRelease changeLifecycleState(String releaseUuid, LifecycleChanger lifecycleChanger) throws ApplicationManagementException; - + + /** + * To get all the releases of a particular ApplicationDTO. + * + * @param applicationReleaseDTO of the ApplicationDTO Release. + * @param lifecycleChanger Lifecycle changer that contains the action and the reason for the change. + * @throws ApplicationManagementException ApplicationDTO Management Exception. + * @return + */ + ApplicationRelease changeLifecycleState(ApplicationReleaseDTO applicationReleaseDTO, LifecycleChanger lifecycleChanger) + throws ApplicationManagementException; + /** * To update release images such as icons, banner and screenshots. * @@ -470,7 +503,9 @@ public interface ApplicationManager { */ boolean checkSubDeviceIdsForOperations(int operationId, int deviceId) throws ApplicationManagementException; - void updateSubsStatus (int deviceId, int operationId, String status) throws ApplicationManagementException; + void updateSubStatus(int deviceId, List operationId, String status) throws ApplicationManagementException; + + void updateSubsStatus(int deviceId, int operationId, String status) throws ApplicationManagementException; /** * Get plist content to download and install the application. @@ -482,5 +517,4 @@ public interface ApplicationManager { String getPlistArtifact(String uuid) throws ApplicationManagementException; List getReleaseByPackageNames(List packageIds) throws ApplicationManagementException; - } diff --git a/components/application-mgt/io.entgra.application.mgt.common/src/main/java/io/entgra/application/mgt/common/services/SubscriptionManager.java b/components/application-mgt/io.entgra.application.mgt.common/src/main/java/io/entgra/application/mgt/common/services/SubscriptionManager.java index 4fd68a490e4..7b4f99e3552 100644 --- a/components/application-mgt/io.entgra.application.mgt.common/src/main/java/io/entgra/application/mgt/common/services/SubscriptionManager.java +++ b/components/application-mgt/io.entgra.application.mgt.common/src/main/java/io/entgra/application/mgt/common/services/SubscriptionManager.java @@ -34,6 +34,16 @@ import java.util.Properties; */ public interface SubscriptionManager { + /** + * Use to update status of a subscription + * + * @param deviceId Id of the device + * @param subId subscription id + * @param status status to be changed + */ + void updateSubscriptionStatus(int deviceId, int subId, String status) + throws SubscriptionManagementException; + /** * Performs bulk subscription operation for a given application and a subscriber list. * @param applicationUUID UUID of the application to subscribe/unsubscribe @@ -175,12 +185,13 @@ public interface SubscriptionManager { * @param actionStatus status of the operation. * @param action action related to the device. * @param appUUID application release UUID + * @param installedVersion installed version * @return {@link PaginationResult} * @throws ApplicationManagementException if offset or limit contains incorrect values, if it couldn't find an * application release for given UUID, if an error occurred while getting device details of subscribed device ids, * if an error occurred while getting subscription details of given application release UUID. */ - PaginationResult getAppSubscriptionDetails(PaginationRequest request, String appUUID, String actionStatus, String action) + PaginationResult getAppSubscriptionDetails(PaginationRequest request, String appUUID, String actionStatus, String action, String installedVersion) throws ApplicationManagementException; /*** diff --git a/components/application-mgt/io.entgra.application.mgt.core/pom.xml b/components/application-mgt/io.entgra.application.mgt.core/pom.xml index bb87db35393..81b0cd151de 100644 --- a/components/application-mgt/io.entgra.application.mgt.core/pom.xml +++ b/components/application-mgt/io.entgra.application.mgt.core/pom.xml @@ -21,7 +21,7 @@ org.wso2.carbon.devicemgt application-mgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/dao/ApplicationDAO.java b/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/dao/ApplicationDAO.java index 6ffa843e5a5..c64eba8814d 100644 --- a/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/dao/ApplicationDAO.java +++ b/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/dao/ApplicationDAO.java @@ -39,6 +39,33 @@ public interface ApplicationDAO { */ int createApplication(ApplicationDTO applicationDTO, int tenantId) throws ApplicationManagementDAOException; + /** + * Use to add an application to favourites for given appId, username and tenantId + * + * @param appId id of the application + * @param userName currently logged-in user + * @throws ApplicationManagementDAOException if error occurred wile executing query to insert data into database. + */ + void addAppToFavourite(int appId, String userName, int tenantId) throws ApplicationManagementDAOException; + + /** + * Use to remove an application from favourites for given appId, username and tenantId + * + * @param appId id of the application + * @param userName currently logged-in username + * @throws ApplicationManagementDAOException if error occurred wile executing query to delete data from database. + */ + void removeAppFromFavourite(int appId, String userName, int tenantId) throws ApplicationManagementDAOException; + + /** + * Use to check if an app is favourite for given username and tenantId + * @param appId id of the application + * @param userName currently logged-in username + * @return If application is favourite + * @throws ApplicationManagementDAOException if error occurred wile executing query to check if application is a favourite + */ + boolean isFavouriteApp(int appId, String userName, int tenantId) throws ApplicationManagementDAOException; + /** * To add tags for a particular application. * diff --git a/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/dao/SubscriptionDAO.java b/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/dao/SubscriptionDAO.java index a84446d6a38..c8747547d25 100644 --- a/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/dao/SubscriptionDAO.java +++ b/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/dao/SubscriptionDAO.java @@ -101,6 +101,10 @@ public interface SubscriptionDAO { List getDeviceSubIds(List deviceIds, int applicationReleaseId, int tenantId) throws ApplicationManagementDAOException; + int getDeviceIdForSubId(int subId, int tenantId) throws ApplicationManagementDAOException; + + List getOperationIdsForSubId(int subId, int tenantId) throws ApplicationManagementDAOException; + List getDeviceSubIdsForOperation(int operationId, int deviceID, int tenantId) throws ApplicationManagementDAOException; @@ -239,4 +243,13 @@ public interface SubscriptionDAO { */ List getAppSubscribedDevicesForGroups(int appReleaseId, String subtype, int tenantId) throws ApplicationManagementDAOException; + + /** + * This method is used to get the currently installed version for given app release id + * @param appId id of the application + * @param deviceIdList id list of devices + * @return Map with device id as a key and currently installed version as value + * @throws {@link ApplicationManagementDAOException} if connections establishment fails. + */ + Map getCurrentInstalledAppVersion(int appId, List deviceIdList, String installedVersion) throws ApplicationManagementDAOException; } diff --git a/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/dao/impl/application/GenericApplicationDAOImpl.java b/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/dao/impl/application/GenericApplicationDAOImpl.java index 420afdbbde7..fe05de200ea 100644 --- a/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/dao/impl/application/GenericApplicationDAOImpl.java +++ b/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/dao/impl/application/GenericApplicationDAOImpl.java @@ -135,10 +135,14 @@ public class GenericApplicationDAOImpl extends AbstractDAOImpl implements Applic + "AP_APP_RELEASE.CURRENT_STATE AS RELEASE_CURRENT_STATE, " + "AP_APP_RELEASE.RATED_USERS AS RATED_USER_COUNT, " + "NEW_AP_APP_LIFECYCLE_STATE.UPDATED_AT AS LATEST_UPDATE " - + "FROM AP_APP " - + "LEFT JOIN AP_APP_RELEASE ON " - + "AP_APP.ID = AP_APP_RELEASE.AP_APP_ID " - + "INNER JOIN (SELECT AP_APP.ID FROM AP_APP "; + + "FROM AP_APP "; + if (StringUtils.isNotEmpty(filter.getFavouredBy())) { + sql += "INNER JOIN AP_APP_FAVOURITES ON " + + "AP_APP.ID = AP_APP_FAVOURITES.AP_APP_ID "; + } + sql += "LEFT JOIN AP_APP_RELEASE ON " + + "AP_APP.ID = AP_APP_RELEASE.AP_APP_ID " + + "INNER JOIN (SELECT AP_APP.ID FROM AP_APP "; if (StringUtils.isNotEmpty(filter.getVersion()) || StringUtils.isNotEmpty(filter.getAppReleaseState()) || StringUtils.isNotEmpty(filter.getAppReleaseType())) { sql += "LEFT JOIN AP_APP_RELEASE ON AP_APP.ID = AP_APP_RELEASE.AP_APP_ID "; @@ -189,8 +193,11 @@ public class GenericApplicationDAOImpl extends AbstractDAOImpl implements Applic + "FROM AP_APP_LIFECYCLE_STATE " + "GROUP BY AP_APP_LIFECYCLE_STATE.AP_APP_RELEASE_ID)) AS NEW_AP_APP_LIFECYCLE_STATE " + "ON AP_APP_RELEASE.ID = NEW_AP_APP_LIFECYCLE_STATE.AP_APP_RELEASE_ID " - + "WHERE AP_APP.TENANT_ID = ? " - + "ORDER BY AP_APP.ID, LATEST_UPDATE DESC"; + + "WHERE AP_APP.TENANT_ID = ? "; + if (StringUtils.isNotEmpty(filter.getFavouredBy())) { + sql += "AND AP_APP_FAVOURITES.USER_NAME = ? "; + } + sql += "ORDER BY AP_APP.ID, LATEST_UPDATE DESC"; try { Connection conn = this.getDBConnection(); @@ -229,7 +236,10 @@ public class GenericApplicationDAOImpl extends AbstractDAOImpl implements Applic stmt.setInt(paramIndex++, filter.getLimit()); stmt.setInt(paramIndex++, filter.getOffset()); } - stmt.setInt(paramIndex, tenantId); + stmt.setInt(paramIndex++, tenantId); + if (StringUtils.isNotEmpty(filter.getFavouredBy())) { + stmt.setString(paramIndex, filter.getFavouredBy()); + } try (ResultSet rs = stmt.executeQuery()) { return DAOUtil.loadApplications(rs); } @@ -732,6 +742,95 @@ public class GenericApplicationDAOImpl extends AbstractDAOImpl implements Applic } } + @Override + public void addAppToFavourite(int appId, String userName, int tenantId) throws ApplicationManagementDAOException { + if (log.isDebugEnabled()) { + log.debug("Request received in DAO Layer to add app with the id " + appId + " favourites"); + } + String sql = "INSERT INTO AP_APP_FAVOURITES " + + "(AP_APP_ID, USER_NAME, " + + " TENANT_ID) " + + "VALUES (?, ?, ?)"; + try { + Connection conn = this.getDBConnection(); + try (PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setInt(1, appId); + stmt.setString(2, userName); + stmt.setInt(3, tenantId); + stmt.addBatch(); + stmt.executeUpdate(); + } + } catch (DBConnectionException e) { + String msg = "Error occurred while obtaining the DB connection when adding app to favourites"; + log.error(msg, e); + throw new ApplicationManagementDAOException(msg, e); + } catch (SQLException e) { + String msg = "SQL Error occurred while adding app with the id " + appId + " to favourites. Executed Query: " + sql; + log.error(msg, e); + throw new ApplicationManagementDAOException(msg, e); + } + } + + @Override + public void removeAppFromFavourite(int appId, String userName, int tenantId) throws ApplicationManagementDAOException { + if (log.isDebugEnabled()) { + log.debug("Request received in DAO Layer to add app with the id " + appId + " favourites"); + } + String sql = "DELETE FROM AP_APP_FAVOURITES " + + "WHERE AP_APP_ID = ? " + + "AND USER_NAME = ? " + + "AND TENANT_ID = ?"; + try { + Connection conn = this.getDBConnection(); + try (PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setInt(1, appId); + stmt.setString(2, userName); + stmt.setInt(3, tenantId); + stmt.addBatch(); + stmt.executeUpdate(); + } + } catch (DBConnectionException e) { + String msg = "Error occurred while obtaining the DB connection when removing app from favourites"; + log.error(msg, e); + throw new ApplicationManagementDAOException(msg, e); + } catch (SQLException e) { + String msg = "SQL Error occurred while removing app with the id " + appId + " from favourites. Executed Query: " + sql; + log.error(msg, e); + throw new ApplicationManagementDAOException(msg, e); + } + } + + @Override + public boolean isFavouriteApp(int appId, String userName, int tenantId) throws ApplicationManagementDAOException { + String sql = "SELECT AP_APP_ID " + + "FROM AP_APP_FAVOURITES " + + "WHERE " + + "AP_APP_ID = ? AND " + + "USER_NAME = ? AND " + + "TENANT_ID = ?"; + try { + Connection conn = this.getDBConnection(); + try (PreparedStatement stmt = conn.prepareStatement(sql)){ + stmt.setInt(1, appId); + stmt.setString(2, userName); + stmt.setInt(3, tenantId); + try (ResultSet rs = stmt.executeQuery()){ + return rs.next(); + } + } + } catch (DBConnectionException e) { + String msg = "Error occurred while obtaining the DB connection to check whether the application with the id " + + appId + " is a favourite app"; + log.error(msg, e); + throw new ApplicationManagementDAOException(msg, e); + } catch (SQLException e) { + String msg = "SQL Error occurred while checking whether the application with the id " + + appId + " is a favourite app. executed query " + sql; + log.error(msg, e); + throw new ApplicationManagementDAOException(msg, e); + } + } + @Override public void addTags(List tags, int tenantId) throws ApplicationManagementDAOException { if (log.isDebugEnabled()) { diff --git a/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/dao/impl/application/OracleApplicationDAOImpl.java b/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/dao/impl/application/OracleApplicationDAOImpl.java index afc6a8cf412..6f4c31f1b9f 100644 --- a/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/dao/impl/application/OracleApplicationDAOImpl.java +++ b/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/dao/impl/application/OracleApplicationDAOImpl.java @@ -82,10 +82,14 @@ public class OracleApplicationDAOImpl extends GenericApplicationDAOImpl { + "AP_APP_RELEASE.CURRENT_STATE AS RELEASE_CURRENT_STATE, " + "AP_APP_RELEASE.RATED_USERS AS RATED_USER_COUNT, " + "NEW_AP_APP_LIFECYCLE_STATE.UPDATED_AT AS LATEST_UPDATE " - + "FROM AP_APP " - + "LEFT JOIN AP_APP_RELEASE ON " - + "AP_APP.ID = AP_APP_RELEASE.AP_APP_ID " - + "INNER JOIN (SELECT AP_APP.ID FROM AP_APP ORDER BY ID "; + + "FROM AP_APP "; + if (StringUtils.isNotEmpty(filter.getFavouredBy())) { + sql += "INNER JOIN AP_APP_FAVOURITES ON " + + "AP_APP.ID = AP_APP_FAVOURITES.AP_APP_ID "; + } + sql += "LEFT JOIN AP_APP_RELEASE ON " + + "AP_APP.ID = AP_APP_RELEASE.AP_APP_ID " + + "INNER JOIN (SELECT AP_APP.ID FROM AP_APP "; if (StringUtils.isNotEmpty(filter.getVersion()) || StringUtils.isNotEmpty(filter.getAppReleaseState()) || StringUtils.isNotEmpty(filter.getAppReleaseType())) { sql += "LEFT JOIN AP_APP_RELEASE ON AP_APP.ID = AP_APP_RELEASE.AP_APP_ID "; diff --git a/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/dao/impl/application/SQLServerApplicationDAOImpl.java b/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/dao/impl/application/SQLServerApplicationDAOImpl.java index 0212de34290..0ddbe3c3339 100644 --- a/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/dao/impl/application/SQLServerApplicationDAOImpl.java +++ b/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/dao/impl/application/SQLServerApplicationDAOImpl.java @@ -81,8 +81,12 @@ public class SQLServerApplicationDAOImpl extends GenericApplicationDAOImpl { + "AP_APP_RELEASE.CURRENT_STATE AS RELEASE_CURRENT_STATE, " + "AP_APP_RELEASE.RATED_USERS AS RATED_USER_COUNT, " + "NEW_AP_APP_LIFECYCLE_STATE.UPDATED_AT AS LATEST_UPDATE " - + "FROM AP_APP " - + "LEFT JOIN AP_APP_RELEASE ON " + + "FROM AP_APP "; + if (StringUtils.isNotEmpty(filter.getFavouredBy())) { + sql += "INNER JOIN AP_APP_FAVOURITES ON " + + "AP_APP.ID = AP_APP_FAVOURITES.AP_APP_ID "; + } + sql += "LEFT JOIN AP_APP_RELEASE ON " + "AP_APP.ID = AP_APP_RELEASE.AP_APP_ID " + "INNER JOIN (SELECT AP_APP.ID FROM AP_APP "; if (StringUtils.isNotEmpty(filter.getVersion()) || StringUtils.isNotEmpty(filter.getAppReleaseState()) diff --git a/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/dao/impl/subscription/GenericSubscriptionDAOImpl.java b/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/dao/impl/subscription/GenericSubscriptionDAOImpl.java index 521981849d1..c814392ae0b 100644 --- a/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/dao/impl/subscription/GenericSubscriptionDAOImpl.java +++ b/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/dao/impl/subscription/GenericSubscriptionDAOImpl.java @@ -691,6 +691,68 @@ public class GenericSubscriptionDAOImpl extends AbstractDAOImpl implements Subsc } } + @Override + public int getDeviceIdForSubId(int subId, int tenantId) throws ApplicationManagementDAOException { + try { + Connection conn = this.getDBConnection(); + String sql = "SELECT DM_DEVICE_ID " + + "FROM AP_DEVICE_SUBSCRIPTION " + + "WHERE ID = ? AND " + + "TENANT_ID = ?"; + try (PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setInt(1, subId); + stmt.setInt(2, tenantId); + try (ResultSet rs = stmt.executeQuery()) { + if (rs.next()) { + return rs.getInt("DM_DEVICE_ID"); + } + } + return -1; + } + } catch (DBConnectionException e) { + String msg = "Error occurred while obtaining the DB connection to get app operation ids for given " + + "subscription id."; + log.error(msg, e); + throw new ApplicationManagementDAOException(msg, e); + } catch (SQLException e) { + String msg = "Error occurred when processing SQL to get operation ids for given subscription id."; + log.error(msg, e); + throw new ApplicationManagementDAOException(msg, e); + } + } + + @Override + public List getOperationIdsForSubId(int subId, int tenantId) throws ApplicationManagementDAOException { + try { + Connection conn = this.getDBConnection(); + List operationIds = new ArrayList<>(); + String sql = "SELECT OPERATION_ID " + + "FROM AP_APP_SUB_OP_MAPPING " + + "WHERE AP_DEVICE_SUBSCRIPTION_ID = ? AND " + + "TENANT_ID = ?"; + try (PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setInt(1, subId); + stmt.setInt(2, tenantId); + try (ResultSet rs = stmt.executeQuery()) { + while (rs.next()) { + operationIds.add(rs.getInt("OPERATION_ID")); + } + } + return operationIds; + } + } catch (DBConnectionException e) { + String msg = "Error occurred while obtaining the DB connection to get app operation ids for given " + + "subscription id."; + log.error(msg, e); + throw new ApplicationManagementDAOException(msg, e); + } catch (SQLException e) { + String msg = "Error occurred when processing SQL to get operation ids for given subscription id."; + log.error(msg, e); + throw new ApplicationManagementDAOException(msg, e); + } + } + + @Override public List getDeviceSubIdsForOperation(int operationId, int deviceId, int tenantId) throws ApplicationManagementDAOException { @@ -1204,7 +1266,7 @@ public class GenericSubscriptionDAOImpl extends AbstractDAOImpl implements Subsc Connection conn = this.getDBConnection(); List subscribedGroups = new ArrayList<>(); String sql = "SELECT " - + "GS.GROUP_NAME AS GROUPS " + + "GS.GROUP_NAME AS APP_GROUPS " + "FROM AP_GROUP_SUBSCRIPTION GS " + "WHERE " + "AP_APP_RELEASE_ID = ? AND TENANT_ID = ? LIMIT ? OFFSET ?"; @@ -1215,7 +1277,7 @@ public class GenericSubscriptionDAOImpl extends AbstractDAOImpl implements Subsc ps.setInt(4, offsetValue); try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { - subscribedGroups.add(rs.getString("GROUPS")); + subscribedGroups.add(rs.getString("APP_GROUPS")); } } return subscribedGroups; @@ -1244,7 +1306,7 @@ public class GenericSubscriptionDAOImpl extends AbstractDAOImpl implements Subsc try { Connection conn = this.getDBConnection(); String sql = "SELECT " - + "COUNT(GS.GROUP_NAME) AS GROUPS " + + "COUNT(GS.GROUP_NAME) AS APP_GROUPS_COUNT " + "FROM AP_GROUP_SUBSCRIPTION GS " + "WHERE " + "AP_APP_RELEASE_ID = ? AND TENANT_ID = ?"; @@ -1254,7 +1316,7 @@ public class GenericSubscriptionDAOImpl extends AbstractDAOImpl implements Subsc try (ResultSet rs = stmt.executeQuery()) { if (rs.next()) { - return rs.getInt("GROUPS"); + return rs.getInt("APP_GROUPS_COUNT"); } } return 0; @@ -1311,4 +1373,66 @@ public class GenericSubscriptionDAOImpl extends AbstractDAOImpl implements Subsc throw new ApplicationManagementDAOException(msg, e); } } + + @Override + public Map getCurrentInstalledAppVersion(int appId, List deviceIdList, String installedVersion ) throws ApplicationManagementDAOException { + if (log.isDebugEnabled()) { + log.debug("Request received in DAO Layer to get current installed version of the app for " + + "given app release id."); + } + try { + + Map installedVersionsMap = new HashMap<>(); + Connection conn = this.getDBConnection(); + int index = 1; + boolean isInstalledVersionAvailable = false; + StringJoiner joiner = new StringJoiner(",", + " SELECT DM_DEVICE_ID AS DEVICE,VERSION FROM " + + " (SELECT AP_APP.ID, VERSION FROM AP_APP_RELEASE AP_APP " + + " WHERE ID IN (SELECT ID FROM AP_APP_RELEASE " + + " WHERE AP_APP_ID = ?) " + + " ) AP_APP_V" + + " INNER JOIN " + + " (SELECT AP_APP_RELEASE_ID, DM_DEVICE_ID FROM AP_DEVICE_SUBSCRIPTION AP_DEV_1 " + + " INNER JOIN (" + + " SELECT MAX(ID) AS ID FROM AP_DEVICE_SUBSCRIPTION " + + " WHERE STATUS = 'COMPLETED' AND DM_DEVICE_ID IN (", + ") GROUP BY DM_DEVICE_ID " + + ") AP_DEV_2 " + + "ON AP_DEV_2.ID = AP_DEV_1.ID ) AP_APP_R " + + "ON AP_APP_R.AP_APP_RELEASE_ID = AP_APP_V.ID"); + deviceIdList.stream().map(ignored -> "?").forEach(joiner::add); + String query = joiner.toString(); + if(installedVersion != null && !installedVersion.isEmpty()){ + query += " WHERE VERSION = ? "; + isInstalledVersionAvailable = true; + } + try (PreparedStatement ps = conn.prepareStatement(query)) { + ps.setInt(index++, appId); + for (int deviceId : deviceIdList) { + ps.setInt(index++, deviceId); + } + if(isInstalledVersionAvailable){ + ps.setString(index++, installedVersion); + } + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + installedVersionsMap.put(rs.getInt("DEVICE"),rs.getString("VERSION")); + } + } + return installedVersionsMap; + } + + }catch (DBConnectionException e) { + String msg = "Error occurred while obtaining the DB connection to get current installed version of the app for " + + "given app id."; + log.error(msg, e); + throw new ApplicationManagementDAOException(msg, e); + } catch (SQLException e) { + String msg = "SQL Error occurred while getting current installed version of the app for given " + + "app id."; + log.error(msg, e); + throw new ApplicationManagementDAOException(msg, e); + } + } } diff --git a/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/impl/ApplicationManagerImpl.java b/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/impl/ApplicationManagerImpl.java index fc928a1e977..375924a0366 100644 --- a/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/impl/ApplicationManagerImpl.java +++ b/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/impl/ApplicationManagerImpl.java @@ -316,6 +316,116 @@ public class ApplicationManagerImpl implements ApplicationManager { return applicationDTO; } + @Override + public void addAppToFavourites(int appId) throws ApplicationManagementException { + validateAddAppToFavouritesRequest(appId); + int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true); + String userName = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername(); + try { + ConnectionManagerUtil.beginDBTransaction(); + applicationDAO.addAppToFavourite(appId, userName, tenantId); + ConnectionManagerUtil.commitDBTransaction(); + } catch (TransactionManagementException e) { + String msg = "Error occurred while staring transaction to add applicationId: " + + appId + " to favourites"; + log.error(msg, e); + throw new ApplicationManagementException(msg, e); + } catch (DBConnectionException e) { + String msg = "Error occurred while adding application id " + appId + " to favourites "; + log.error(msg, e); + throw new ApplicationManagementException(msg, e); + } catch (ApplicationManagementDAOException e) { + ConnectionManagerUtil.rollbackDBTransaction(); + String msg = "Error occurred while adding application with the id: " + appId + " to favourites "; + log.error(msg, e); + throw new ApplicationManagementException(msg, e); + } finally { + ConnectionManagerUtil.closeDBConnection(); + } + } + + @Override + public void removeAppFromFavourites(int appId) throws ApplicationManagementException { + validateRemoveAppFromFavouritesRequest(appId); + int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true); + String userName = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername(); + try { + ConnectionManagerUtil.beginDBTransaction(); + applicationDAO.removeAppFromFavourite(appId, userName, tenantId); + ConnectionManagerUtil.commitDBTransaction(); + } catch (TransactionManagementException e) { + String msg = "Error occurred while staring transaction to remove applicationId: " + + appId + " from favourites"; + log.error(msg, e); + throw new ApplicationManagementException(msg, e); + } catch (DBConnectionException e) { + String msg = "Error occurred while removing application id " + appId + " from favourites "; + log.error(msg, e); + throw new ApplicationManagementException(msg, e); + } catch (ApplicationManagementDAOException e) { + ConnectionManagerUtil.rollbackDBTransaction(); + String msg = "Error occurred while removing application with the id: " + appId + " from favourites "; + log.error(msg, e); + throw new ApplicationManagementException(msg, e); + } finally { + ConnectionManagerUtil.closeDBConnection(); + } + } + + @Override + public boolean isFavouriteApp(int appId) throws ApplicationManagementException{ + int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true); + String userName = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername(); + try { + ConnectionManagerUtil.openDBConnection(); + return applicationDAO.isFavouriteApp(appId, userName, tenantId); + } catch (DBConnectionException e) { + String msg = "Error occurred while getting DB connection to check is app with the id " + appId + + " is a favourite app"; + log.error(msg, e); + throw new ApplicationManagementException(msg, e); + } catch (ApplicationManagementDAOException e) { + String msg = "Error occurred while checking app with the id " + appId + " is a favourite app."; + log.error(msg); + throw new ApplicationManagementException(msg, e); + } finally { + ConnectionManagerUtil.closeDBConnection(); + } + + } + + /** + * Use to check if the requested application id is valid before removing from favourites + * + * @param appId ID of the application + * @throws ApplicationManagementException if ID is not valid or errors while validating + */ + private void validateRemoveAppFromFavouritesRequest(int appId) throws ApplicationManagementException { + if (!isFavouriteApp(appId)) { + String msg = "Provided appId " + appId + " is not a favourite app in order remove from favourites"; + throw new BadRequestException(msg); + } + } + + /** + * Use to check if the requested application id is valid before adding to favourites + * + * @param appId ID of the application + * @throws ApplicationManagementException if ID is not valid or errors while validating + */ + private void validateAddAppToFavouritesRequest(int appId) throws ApplicationManagementException { + try { + getApplication(appId); + } catch (NotFoundException e) { + String msg = " No application exists for the provided appId " + appId; + throw new BadRequestException(msg); + } + if (isFavouriteApp(appId)) { + String msg = "Provided appId " + appId + " is already a favourite app"; + throw new BadRequestException(msg); + } + } + /** * Upload enterprise application release artifact into file system. * @@ -817,9 +927,17 @@ public class ApplicationManagerImpl implements ApplicationManager { return applicationReleaseDTO; } + @Override + public ApplicationList getFavouriteApplications(Filter filter) throws ApplicationManagementException { + String userName = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername(); + filter.setFavouredBy(userName); + return getApplications(filter); + } + @Override public ApplicationList getApplications(Filter filter) throws ApplicationManagementException { int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(); + String userName = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername(); ApplicationList applicationList = new ApplicationList(); List applications = new ArrayList<>(); DeviceType deviceType; @@ -845,6 +963,7 @@ public class ApplicationManagerImpl implements ApplicationManager { .setUnrestrictedRoles(visibilityDAO.getUnrestrictedRoles(applicationDTO.getId(), tenantId)); applicationDTO.setAppCategories(applicationDAO.getAppCategories(applicationDTO.getId(), tenantId)); applicationDTO.setTags(applicationDAO.getAppTags(applicationDTO.getId(), tenantId)); + applicationDTO.setFavourite(applicationDAO.isFavouriteApp(applicationDTO.getId(), userName, tenantId)); if (isFilteringApp(applicationDTO, filter)) { boolean isHideableApp = isHideableApp(applicationDTO.getApplicationReleaseDTOs()); @@ -2019,6 +2138,61 @@ public class ApplicationManagerImpl implements ApplicationManager { ConnectionManagerUtil.closeDBConnection(); } } + + public ApplicationRelease changeLifecycleState(ApplicationReleaseDTO applicationReleaseDTO, LifecycleChanger lifecycleChanger) throws ApplicationManagementException { + + int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true); + String userName = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername(); + if (lifecycleChanger == null || StringUtils.isEmpty(lifecycleChanger.getAction())) { + String msg = "The Action is null or empty. Please verify the request."; + log.error(msg); + throw new BadRequestException(msg); + } + + try{ + if (lifecycleStateManager + .isValidStateChange(applicationReleaseDTO.getCurrentState(), lifecycleChanger.getAction(), userName, + tenantId)) { + if (lifecycleStateManager.isInstallableState(lifecycleChanger.getAction()) && applicationReleaseDAO + .hasExistInstallableAppRelease(applicationReleaseDTO.getUuid(), + lifecycleStateManager.getInstallableState(), tenantId)) { + String msg = "Installable application release is already registered for the application. " + + "Therefore it is not permitted to change the lifecycle state from " + + applicationReleaseDTO.getCurrentState() + " to " + lifecycleChanger.getAction(); + log.error(msg); + throw new ForbiddenException(msg); + } + LifecycleState lifecycleState = new LifecycleState(); + lifecycleState.setCurrentState(lifecycleChanger.getAction()); + lifecycleState.setPreviousState(applicationReleaseDTO.getCurrentState()); + lifecycleState.setUpdatedBy(userName); + lifecycleState.setReasonForChange(lifecycleChanger.getReason()); + applicationReleaseDTO.setCurrentState(lifecycleChanger.getAction()); + if (this.applicationReleaseDAO.updateRelease(applicationReleaseDTO, tenantId) == null) { + String msg = "Application release updating is failed/."; + log.error(msg); + throw new ApplicationManagementException(msg); + } + this.lifecycleStateDAO.addLifecycleState(lifecycleState, applicationReleaseDTO.getId(), tenantId); + return APIUtil.releaseDtoToRelease(applicationReleaseDTO); + } else { + String msg = "Invalid lifecycle state transition from '" + applicationReleaseDTO.getCurrentState() + "'" + + " to '" + lifecycleChanger.getAction() + "'"; + log.error(msg); + throw new ApplicationManagementException(msg); + } + } catch (ApplicationManagementDAOException e) { + String msg = "Error occurred when accessing application release data of application release which has the " + + "application release UUID: " + applicationReleaseDTO.getUuid(); + log.error(msg, e); + throw new ApplicationManagementException(msg, e); + } catch (LifeCycleManagementDAOException e) { + String msg = "Failed to add lifecycle state for Application release UUID: " + applicationReleaseDTO.getUuid(); + log.error(msg, e); + throw new ApplicationManagementException(msg, e); + } + } + @Override public void addApplicationCategories(List categories) throws ApplicationManagementException { @@ -3651,7 +3825,40 @@ public class ApplicationManagerImpl implements ApplicationManager { } @Override - public void updateSubsStatus (int deviceId, int operationId, String status) throws ApplicationManagementException { + public void updateSubStatus(int deviceId, List operationIds, String status) throws ApplicationManagementException { + int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(); + try { + ConnectionManagerUtil.beginDBTransaction(); + for (int operationId : operationIds) { + List deviceSubIds = subscriptionDAO.getDeviceSubIdsForOperation(operationId, deviceId, tenantId); + if (!subscriptionDAO.updateDeviceSubStatus(deviceId, deviceSubIds, status, tenantId)){ + ConnectionManagerUtil.rollbackDBTransaction(); + String msg = "Didn't update an any app subscription of device for operation Id: " + operationId; + log.error(msg); + throw new ApplicationManagementException(msg); + } + } + ConnectionManagerUtil.commitDBTransaction(); + } catch (ApplicationManagementDAOException e) { + String msg = "Error occured while updating app subscription status of the device."; + log.error(msg, e); + throw new ApplicationManagementException(msg, e); + } catch (DBConnectionException e) { + String msg = "Error occurred while obersving the database connection to update aoo subscription status of " + + "device."; + log.error(msg, e); + throw new ApplicationManagementException(msg, e); + } catch (TransactionManagementException e) { + String msg = "Error occurred while executing database transaction"; + log.error(msg, e); + throw new ApplicationManagementException(msg, e); + } finally { + ConnectionManagerUtil.closeDBConnection(); + } + } + + @Override + public void updateSubsStatus(int deviceId, int operationId, String status) throws ApplicationManagementException { int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(); try { ConnectionManagerUtil.beginDBTransaction(); diff --git a/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/impl/SubscriptionManagerImpl.java b/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/impl/SubscriptionManagerImpl.java index a6ceff06d8e..b6bec326553 100644 --- a/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/impl/SubscriptionManagerImpl.java +++ b/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/impl/SubscriptionManagerImpl.java @@ -20,6 +20,7 @@ package io.entgra.application.mgt.core.impl; import com.google.gson.Gson; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpException; +import org.apache.commons.httpclient.HttpMethodBase; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.StringRequestEntity; import org.apache.commons.lang.StringUtils; @@ -1189,18 +1190,62 @@ public class SubscriptionManagerImpl implements SubscriptionManager { } } + @Override + public void updateSubscriptionStatus(int deviceId, int subId, String status) + throws SubscriptionManagementException { + try { + int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true); + List operationIds = getOperationIdsForSubId(subId, tenantId); + APIUtil.getApplicationManager().updateSubStatus(deviceId, operationIds, status); + } catch (DBConnectionException e) { + String msg = "Error occurred while observing the database connection to get operation Ids for " + subId; + log.error(msg, e); + throw new SubscriptionManagementException(msg, e); + } catch (ApplicationManagementException e) { + String msg = "Error occurred while updating subscription status with the id: " + + subId; + log.error(msg, e); + throw new SubscriptionManagementException(msg, e); + } + } + + private List getOperationIdsForSubId(int subId, int tenantId) throws SubscriptionManagementException { + try { + ConnectionManagerUtil.openDBConnection(); + return subscriptionDAO.getOperationIdsForSubId(subId, tenantId); + } catch (ApplicationManagementDAOException e) { + String msg = "Error occurred while getting operation Ids for subId" + subId; + log.error(msg, e); + throw new SubscriptionManagementException(msg, e); + } catch (DBConnectionException e) { + String msg = "Error occurred while observing the database connection to get operation Ids for " + subId; + log.error(msg, e); + throw new SubscriptionManagementException(msg, e); + } finally { + ConnectionManagerUtil.closeDBConnection(); + } + } + + private int invokeIOTCoreAPI(HttpMethodBase request) throws UserStoreException, APIManagerException, IOException { + HttpClient httpClient; + String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); + ApiApplicationKey apiApplicationKey = OAuthUtils.getClientCredentials(tenantDomain); + String username = + PrivilegedCarbonContext.getThreadLocalCarbonContext().getUserRealm().getRealmConfiguration() + .getAdminUserName() + Constants.ApplicationInstall.AT + tenantDomain; + AccessTokenInfo tokenInfo = OAuthUtils.getOAuthCredentials(apiApplicationKey, username); + request.addRequestHeader(Constants.ApplicationInstall.AUTHORIZATION, + Constants.ApplicationInstall.AUTHORIZATION_HEADER_VALUE + tokenInfo.getAccessToken()); + httpClient = new HttpClient(); + httpClient.executeMethod(request); + return request.getStatusCode(); + } + public int installEnrollmentApplications(ApplicationPolicyDTO applicationPolicyDTO) throws ApplicationManagementException { - HttpClient httpClient; PostMethod request; try { - String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(); - ApiApplicationKey apiApplicationKey = OAuthUtils.getClientCredentials(tenantDomain); - String username = - PrivilegedCarbonContext.getThreadLocalCarbonContext().getUserRealm().getRealmConfiguration() - .getAdminUserName() + Constants.ApplicationInstall.AT + tenantDomain; - AccessTokenInfo tokenInfo = OAuthUtils.getOAuthCredentials(apiApplicationKey, username); String requestUrl = Constants.ApplicationInstall.ENROLLMENT_APP_INSTALL_PROTOCOL + System .getProperty(Constants.ApplicationInstall.IOT_CORE_HOST) + Constants.ApplicationInstall.COLON + System.getProperty(Constants.ApplicationInstall.IOT_CORE_PORT) @@ -1210,14 +1255,9 @@ public class SubscriptionManagerImpl implements SubscriptionManager { StringRequestEntity requestEntity = new StringRequestEntity(payload, MediaType.APPLICATION_JSON, Constants.ApplicationInstall.ENCODING); - httpClient = new HttpClient(); request = new PostMethod(requestUrl); - request.addRequestHeader(Constants.ApplicationInstall.AUTHORIZATION, - Constants.ApplicationInstall.AUTHORIZATION_HEADER_VALUE + tokenInfo.getAccessToken()); request.setRequestEntity(requestEntity); - httpClient.executeMethod(request); - return request.getStatusCode(); - + return invokeIOTCoreAPI(request); } catch (UserStoreException e) { String msg = "Error while accessing user store for user with Android device."; log.error(msg, e); @@ -1240,6 +1280,13 @@ public class SubscriptionManagerImpl implements SubscriptionManager { } } + private String getIOTCoreBaseUrl() { + return Constants.HTTPS_PROTOCOL + Constants.SCHEME_SEPARATOR + System + .getProperty(Constants.IOT_CORE_HOST) + Constants.COLON + + System.getProperty(Constants.IOT_CORE_HTTPS_PORT); + } + + @Override public PaginationResult getAppInstalledDevices(PaginationRequest request, String appUUID) throws ApplicationManagementException { @@ -1361,7 +1408,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager { @Override public PaginationResult getAppSubscriptionDetails(PaginationRequest request, String appUUID, String actionStatus, - String action) throws ApplicationManagementException { + String action, String installedVersion) throws ApplicationManagementException { int limitValue = request.getRowCount(); int offsetValue = request.getStartIndex(); int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true); @@ -1395,6 +1442,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager { } List deviceIdList = deviceSubscriptionDTOS.stream().map(DeviceSubscriptionDTO::getDeviceId) .collect(Collectors.toList()); + Map currentVersionsMap = subscriptionDAO.getCurrentInstalledAppVersion(applicationDTO.getId(),deviceIdList, installedVersion); try { //pass the device id list to device manager service method PaginationResult paginationResult = deviceManagementProviderService.getAppSubscribedDevices @@ -1404,7 +1452,15 @@ public class SubscriptionManagerImpl implements SubscriptionManager { if (!paginationResult.getData().isEmpty()) { List devices = (List) paginationResult.getData(); for (Device device : devices) { + if(installedVersion != null && !installedVersion.isEmpty() && !currentVersionsMap.containsKey(device.getId())){ + continue; + } DeviceSubscriptionData deviceSubscriptionData = new DeviceSubscriptionData(); + if(currentVersionsMap.containsKey(device.getId())){ + deviceSubscriptionData.setCurrentInstalledVersion(currentVersionsMap.get(device.getId())); + }else{ + deviceSubscriptionData.setCurrentInstalledVersion("-"); + } for (DeviceSubscriptionDTO subscription : deviceSubscriptionDTOS) { if (subscription.getDeviceId() == device.getId()) { deviceSubscriptionData.setDevice(device); @@ -1421,6 +1477,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager { } deviceSubscriptionData.setActionType(subscription.getActionTriggeredFrom()); deviceSubscriptionData.setStatus(subscription.getStatus()); + deviceSubscriptionData.setSubId(subscription.getId()); deviceSubscriptionDataList.add(deviceSubscriptionData); break; } diff --git a/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/util/APIUtil.java b/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/util/APIUtil.java index e85e57dfd6e..0821341aa2c 100644 --- a/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/util/APIUtil.java +++ b/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/util/APIUtil.java @@ -414,6 +414,7 @@ public class APIUtil { application.setTags(applicationDTO.getTags()); application.setUnrestrictedRoles(applicationDTO.getUnrestrictedRoles()); application.setRating(applicationDTO.getAppRating()); + application.setFavourite(applicationDTO.isFavourite()); if (applicationDTO.getApplicationReleaseDTOs() != null && !applicationDTO.getApplicationReleaseDTOs().isEmpty()) { application.setInstallerName(applicationDTO.getApplicationReleaseDTOs().get(0).getInstallerName()); } diff --git a/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/util/Constants.java b/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/util/Constants.java index f4ad8013e45..a69f28e5df5 100644 --- a/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/util/Constants.java +++ b/components/application-mgt/io.entgra.application.mgt.core/src/main/java/io/entgra/application/mgt/core/util/Constants.java @@ -49,6 +49,9 @@ public class Constants { public static final String IOT_CORE_HTTPS_PORT = "iot.core.https.port"; public static final String HTTPS_PROTOCOL = "https"; public static final String HTTP_PROTOCOL = "http"; + public static final String SCHEME_SEPARATOR = "://"; + public static final String OPERATION_STATUS_UPDATE_API_BASE = "/api/device-mgt/v1.0/devices"; + public static final String OPERATION_STATUS_UPDATE_API_URI = "/operation"; public static final String LIMIT_QUERY_PARAM = "limit"; public static final String OFFSET_QUERY_PARAM = "offset"; @@ -59,6 +62,7 @@ public class Constants { public static final String IS_APP_RELEASE_TYPE = "stable"; public static final String IS_APP_DEFAULT_PAYMENT_CURRENCY = "$"; public static final String IS_APP_DEFAULT_VERSION = "1.0"; + public static final String COLON = ":"; public static final String FORWARD_SLASH = "/"; public static final String URI_QUERY_SEPARATOR = "?"; public static final String QUERY_STRING_SEPARATOR = "&"; diff --git a/components/application-mgt/io.entgra.application.mgt.core/src/test/java/io/entgra/application/mgt/core/management/ApplicationManagementTest.java b/components/application-mgt/io.entgra.application.mgt.core/src/test/java/io/entgra/application/mgt/core/management/ApplicationManagementTest.java index 7cb9e9491e6..9d51887beb7 100644 --- a/components/application-mgt/io.entgra.application.mgt.core/src/test/java/io/entgra/application/mgt/core/management/ApplicationManagementTest.java +++ b/components/application-mgt/io.entgra.application.mgt.core/src/test/java/io/entgra/application/mgt/core/management/ApplicationManagementTest.java @@ -120,7 +120,6 @@ public class ApplicationManagementTest extends BaseTestCase { screenshots.put("shot3", new FileInputStream(new File("src/test/resources/samples/app1/shot3.png"))); applicationArtifact.setScreenshots(screenshots); - ApplicationManager manager = new ApplicationManagerImpl(); manager.createApplication(applicationWrapper); } @@ -139,7 +138,12 @@ public class ApplicationManagementTest extends BaseTestCase { public static Object[][] uuidDataProvider() { return new Object[][] {{"TEST_APP_UUID"}}; } - + + @Test(enabled = false) + public void createApplicationAndPublish(ApplicationWrapper applicationWrapper, ApplicationArtifact applicationArtifact, boolean isPublish) throws ApplicationManagementException { + + } + @Test(enabled = false) public void updateApplication(int applicationId, ApplicationUpdateWrapper applicationUpdateWrapper) throws ApplicationManagementException { diff --git a/components/application-mgt/io.entgra.application.mgt.publisher.api/pom.xml b/components/application-mgt/io.entgra.application.mgt.publisher.api/pom.xml index b63528235de..e1d4400d792 100644 --- a/components/application-mgt/io.entgra.application.mgt.publisher.api/pom.xml +++ b/components/application-mgt/io.entgra.application.mgt.publisher.api/pom.xml @@ -22,7 +22,7 @@ application-mgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/application-mgt/io.entgra.application.mgt.publisher.api/src/main/java/io/entgra/application/mgt/publisher/api/services/impl/ApplicationManagementPublisherAPIImpl.java b/components/application-mgt/io.entgra.application.mgt.publisher.api/src/main/java/io/entgra/application/mgt/publisher/api/services/impl/ApplicationManagementPublisherAPIImpl.java index 561a65b7674..f17e2e973de 100644 --- a/components/application-mgt/io.entgra.application.mgt.publisher.api/src/main/java/io/entgra/application/mgt/publisher/api/services/impl/ApplicationManagementPublisherAPIImpl.java +++ b/components/application-mgt/io.entgra.application.mgt.publisher.api/src/main/java/io/entgra/application/mgt/publisher/api/services/impl/ApplicationManagementPublisherAPIImpl.java @@ -208,7 +208,7 @@ public class ApplicationManagementPublisherAPIImpl implements ApplicationManagem return Response.status(Response.Status.BAD_REQUEST).entity(msg).build(); } } - + @POST @Consumes(MediaType.APPLICATION_JSON) @Path("/public-app") diff --git a/components/application-mgt/io.entgra.application.mgt.store.api/pom.xml b/components/application-mgt/io.entgra.application.mgt.store.api/pom.xml index 9b46f642fc7..4adb35bdf34 100644 --- a/components/application-mgt/io.entgra.application.mgt.store.api/pom.xml +++ b/components/application-mgt/io.entgra.application.mgt.store.api/pom.xml @@ -22,7 +22,7 @@ application-mgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/application-mgt/io.entgra.application.mgt.store.api/src/main/java/io/entgra/application/mgt/store/api/beans/SubscriptionStatusBean.java b/components/application-mgt/io.entgra.application.mgt.store.api/src/main/java/io/entgra/application/mgt/store/api/beans/SubscriptionStatusBean.java new file mode 100644 index 00000000000..99d71f3d5f7 --- /dev/null +++ b/components/application-mgt/io.entgra.application.mgt.store.api/src/main/java/io/entgra/application/mgt/store/api/beans/SubscriptionStatusBean.java @@ -0,0 +1,43 @@ +package io.entgra.application.mgt.store.api.beans; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +/** + * This is used to map the status of subscription. + */ +@ApiModel( + value = "SubscriptionStatusBean", + description = "This class carries all information related map statuses of the subscription." +) +public class SubscriptionStatusBean { + @ApiModelProperty( + name = "sub id", + value = "Subscription Id.", + required = true + ) + private int subId; + + @ApiModelProperty( + name = "status", + value = "Status of the subscription.", + required = true + ) + private String status; + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public int getSubId() { + return subId; + } + + public void setSubId(int subId) { + this.subId = subId; + } +} diff --git a/components/application-mgt/io.entgra.application.mgt.store.api/src/main/java/io/entgra/application/mgt/store/api/services/ApplicationManagementAPI.java b/components/application-mgt/io.entgra.application.mgt.store.api/src/main/java/io/entgra/application/mgt/store/api/services/ApplicationManagementAPI.java index eb11be7a409..4eeab57384d 100644 --- a/components/application-mgt/io.entgra.application.mgt.store.api/src/main/java/io/entgra/application/mgt/store/api/services/ApplicationManagementAPI.java +++ b/components/application-mgt/io.entgra.application.mgt.store.api/src/main/java/io/entgra/application/mgt/store/api/services/ApplicationManagementAPI.java @@ -35,8 +35,10 @@ import io.entgra.application.mgt.common.ApplicationList; import javax.validation.Valid; import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; +import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; @@ -70,6 +72,13 @@ import javax.ws.rs.core.Response; key = "perm:app:store:view", roles = {"Internal/devicemgt-user"}, permissions = {"/app-mgt/store/application/view"} + ), + @Scope( + name = "Modify Application", + description = "Modify application state", + key = "perm:app:store:modify", + roles = {"Internal/devicemgt-user"}, + permissions = {"/app-mgt/store/application/modify"} ) } ) @@ -81,6 +90,120 @@ public interface ApplicationManagementAPI { String SCOPE = "scope"; + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Path("/favourite/{appId}") + @ApiOperation( + consumes = MediaType.APPLICATION_JSON, + produces = MediaType.APPLICATION_JSON, + httpMethod = "POST", + value = "add application to favourites", + notes = "This will add application to favourites", + tags = "Application Management", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = SCOPE, value = "perm:app:store:modify") + }) + } + ) + @ApiResponses( + value = { + @ApiResponse( + code = 200, + message = "OK. \n Successfully added application to favourites.", + response = ApplicationList.class), + @ApiResponse( + code = 400, + message = "Bad Request. \n " + + "Payload contains unacceptable or vulnerable data"), + @ApiResponse( + code = 500, + message = "Internal Server Error. \n Error occurred while adding the application to favourites.", + response = ErrorResponse.class) + }) + Response addAppToFavourite( + @ApiParam( + name = "appId", + value = "id of the application", + required = true) + @PathParam("appId") int appId); + + @DELETE + @Consumes(MediaType.APPLICATION_JSON) + @Path("/favourite/{appId}") + @ApiOperation( + consumes = MediaType.APPLICATION_JSON, + produces = MediaType.APPLICATION_JSON, + httpMethod = "DELETE", + value = "remove application from favourites", + notes = "This will removing an application from favourites", + tags = "Application Management", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = SCOPE, value = "perm:app:store:modify") + }) + } + ) + @ApiResponses( + value = { + @ApiResponse( + code = 200, + message = "OK. \n Successfully removed application from favourites.", + response = ApplicationList.class), + @ApiResponse( + code = 400, + message = "Bad Request. \n " + + "Payload contains unacceptable or vulnerable data"), + @ApiResponse( + code = 500, + message = "Internal Server Error. \n Error occurred while removing the application from favourites.", + response = ErrorResponse.class) + }) + Response removeAppFromFavourite( + @ApiParam( + name = "appId", + value = "id of the application", + required = true) + @PathParam("appId") int appId); + + @POST + @Path("/favourite") + @Consumes(MediaType.APPLICATION_JSON) + @ApiOperation( + consumes = MediaType.APPLICATION_JSON, + produces = MediaType.APPLICATION_JSON, + httpMethod = "GET", + value = "get all favourite applications", + notes = "This will get all favourite applications", + tags = "Application Management", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = SCOPE, value = "perm:app:store:view") + }) + } + ) + @ApiResponses( + value = { + @ApiResponse( + code = 200, + message = "OK. \n Successfully got application list.", + response = ApplicationList.class), + @ApiResponse( + code = 400, + message = "Bad Request. \n " + + "Application retrieving request payload contains unacceptable or vulnerable data"), + @ApiResponse( + code = 500, + message = "Internal Server Error. \n Error occurred while getting the application list.", + response = ErrorResponse.class) + }) + Response getFavouriteApplications( + @ApiParam( + name = "filter", + value = "Application filtering data", + required = true) + @Valid Filter filter); + @POST @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) diff --git a/components/application-mgt/io.entgra.application.mgt.store.api/src/main/java/io/entgra/application/mgt/store/api/services/admin/SubscriptionManagementAdminAPI.java b/components/application-mgt/io.entgra.application.mgt.store.api/src/main/java/io/entgra/application/mgt/store/api/services/admin/SubscriptionManagementAdminAPI.java index fc7719bc632..86609750887 100644 --- a/components/application-mgt/io.entgra.application.mgt.store.api/src/main/java/io/entgra/application/mgt/store/api/services/admin/SubscriptionManagementAdminAPI.java +++ b/components/application-mgt/io.entgra.application.mgt.store.api/src/main/java/io/entgra/application/mgt/store/api/services/admin/SubscriptionManagementAdminAPI.java @@ -17,6 +17,7 @@ */ package io.entgra.application.mgt.store.api.services.admin; +import io.entgra.application.mgt.store.api.beans.SubscriptionStatusBean; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; @@ -34,6 +35,7 @@ import io.entgra.application.mgt.common.ErrorResponse; import javax.validation.constraints.Size; import javax.ws.rs.Consumes; import javax.ws.rs.GET; +import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; @@ -69,6 +71,13 @@ import java.util.List; key = "perm:admin:app:subscription:view", roles = {"Internal/devicemgt-admin"}, permissions = {"/app-mgt/store/admin/subscription/view"} + ), + @Scope( + name = "View Application Subscriptions", + description = "View Application Subscriptions.", + key = "perm:admin:app:subscription:modify", + roles = {"Internal/devicemgt-admin"}, + permissions = {"/app-mgt/store/admin/subscription/modify"} ) } ) @@ -79,6 +88,51 @@ public interface SubscriptionManagementAdminAPI { String SCOPE = "scope"; + @PUT + @Consumes(MediaType.APPLICATION_JSON) + @Path("/device/{deviceId}/status") + @ApiOperation( + consumes = MediaType.APPLICATION_JSON, + produces = MediaType.APPLICATION_JSON, + httpMethod = "PUT", + value = "Update subscription status", + notes = "This will update the subscription status that belongs to the given device id", + tags = "Subscription Management", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = SCOPE, value = "perm:admin:app:subscription:modify") + }) + } + ) + @ApiResponses( + value = { + @ApiResponse( + code = 200, + message = "OK. \n Successfully updated subscription status.", + response = List.class, + responseContainer = "List"), + @ApiResponse( + code = 404, + message = "Not Found. \n No Application found which has application release of UUID.", + response = ErrorResponse.class), + @ApiResponse( + code = 500, + message = "Internal Server Error. \n Error occurred while updating subscription status", + response = ErrorResponse.class) + }) + Response updateSubscription( + @ApiParam( + name = "deviceId", + value = "Id of the device", + required = true) + @PathParam("deviceId") int deviceId, + @ApiParam( + name = "subscription status change bean", + value = "this bean contains the information related to status change", + required = true) + SubscriptionStatusBean subscriptionStatusBean + ); + @GET @Path("/{uuid}") @Produces(MediaType.APPLICATION_JSON) @@ -139,6 +193,10 @@ public interface SubscriptionManagementAdminAPI { name = "status", value = "Provide the device status details, such as active or inactive.") @QueryParam("status") List status, + @ApiParam( + name = "installedVersion", + value = "Provide the installed version of the application.") + @QueryParam("installedVersion") String installedVersion, @ApiParam( name = "uuid", value = "uuid of the application release.", diff --git a/components/application-mgt/io.entgra.application.mgt.store.api/src/main/java/io/entgra/application/mgt/store/api/services/impl/ApplicationManagementAPIImpl.java b/components/application-mgt/io.entgra.application.mgt.store.api/src/main/java/io/entgra/application/mgt/store/api/services/impl/ApplicationManagementAPIImpl.java index 735df25c924..5f1f124d636 100644 --- a/components/application-mgt/io.entgra.application.mgt.store.api/src/main/java/io/entgra/application/mgt/store/api/services/impl/ApplicationManagementAPIImpl.java +++ b/components/application-mgt/io.entgra.application.mgt.store.api/src/main/java/io/entgra/application/mgt/store/api/services/impl/ApplicationManagementAPIImpl.java @@ -28,14 +28,15 @@ import io.entgra.application.mgt.core.exception.NotFoundException; import io.entgra.application.mgt.core.exception.UnexpectedServerErrorException; import io.entgra.application.mgt.core.util.APIUtil; import io.entgra.application.mgt.store.api.services.ApplicationManagementAPI; - import javax.validation.Valid; import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; /** @@ -47,30 +48,88 @@ public class ApplicationManagementAPIImpl implements ApplicationManagementAPI { private static final Log log = LogFactory.getLog(ApplicationManagementAPIImpl.class); + @POST + @Path("/favourite/{appId}") + @Override + @Consumes(MediaType.APPLICATION_JSON) + public Response addAppToFavourite(@PathParam("appId") int appId) { + ApplicationManager applicationManager = APIUtil.getApplicationManager(); + try { + applicationManager.addAppToFavourites(appId); + return Response.status(Response.Status.OK).build(); + } catch (BadRequestException e) { + String msg = "Invalid payload found in the request. Hence verify the request payload."; + log.error(msg, e); + return Response.status(Response.Status.BAD_REQUEST).entity(msg).build(); + } catch (ApplicationManagementException e) { + String msg = "Error occurred while adding application to favourites"; + log.error(msg, e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); + } + } + + @DELETE + @Path("/favourite/{appId}") + @Override + @Consumes(MediaType.APPLICATION_JSON) + public Response removeAppFromFavourite(@PathParam("appId") int appId) { + ApplicationManager applicationManager = APIUtil.getApplicationManager(); + try { + applicationManager.removeAppFromFavourites(appId); + return Response.status(Response.Status.OK).build(); + } catch (BadRequestException e) { + String msg = "Invalid payload found in the request. Hence verify the request payload."; + log.error(msg, e); + return Response.status(Response.Status.BAD_REQUEST).entity(msg).build(); + } catch (ApplicationManagementException e) { + String msg = "Error occurred while removing application from favourites"; + log.error(msg, e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); + } + } + + @POST + @Path("/favourite") + @Override + @Consumes(MediaType.APPLICATION_JSON) + public Response getFavouriteApplications(@Valid Filter filter) { + ApplicationManager applicationManager = APIUtil.getApplicationManager(); + try { + validateFilter(filter); + ApplicationList applications = applicationManager.getFavouriteApplications(filter); + return Response.status(Response.Status.OK).entity(applications).build(); + } catch (BadRequestException e) { + String msg = "Invalid filter payload found in the request. Hence verify the filter payload."; + log.error(msg, e); + return Response.status(Response.Status.BAD_REQUEST).entity(msg).build(); + } catch (ApplicationManagementException e) { + String msg = "Error occurred while retrieving favourite applications"; + log.error(msg, e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); + } + } + @POST @Override @Consumes("application/json") public Response getApplications(@Valid Filter filter) { ApplicationManager applicationManager = APIUtil.getApplicationManager(); try { - if (filter == null) { - String msg = "Request Payload is null"; - log.error(msg); - return Response.status(Response.Status.BAD_REQUEST).entity(msg).build(); - } + validateFilter(filter); filter.setAppReleaseState(applicationManager.getInstallableLifecycleState()); ApplicationList applications = applicationManager.getApplications(filter); return Response.status(Response.Status.OK).entity(applications).build(); } catch (BadRequestException e) { - String msg = e.getMessage(); + String msg = "Invalid request payload found in the request. Hence verify the payload."; log.error(msg, e); return Response.status(Response.Status.BAD_REQUEST).entity(msg).build(); } catch (UnexpectedServerErrorException e) { - String msg = e.getMessage(); + String msg = "Unexpected Error occurred while retrieving applications"; log.error(msg); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); } catch (ApplicationManagementException e) { - String msg = e.getMessage(); + String msg = "Error occurred while retrieving applications"; + log.error(msg); log.error(msg, e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); } @@ -101,4 +160,18 @@ public class ApplicationManagementAPIImpl implements ApplicationManagementAPI { return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); } } + + /** + * This method can be used to check & validate if {@link Filter} object exist. + * + * @param filter {@link Filter} + * @throws BadRequestException if filter object doesn't exist + */ + private void validateFilter(Filter filter) throws BadRequestException { + if (filter == null) { + String msg = "Request Payload is null"; + log.error(msg); + throw new BadRequestException(msg); + } + } } diff --git a/components/application-mgt/io.entgra.application.mgt.store.api/src/main/java/io/entgra/application/mgt/store/api/services/impl/admin/SubscriptionManagementAdminAPIImpl.java b/components/application-mgt/io.entgra.application.mgt.store.api/src/main/java/io/entgra/application/mgt/store/api/services/impl/admin/SubscriptionManagementAdminAPIImpl.java index b767c39ccda..158696d65e0 100644 --- a/components/application-mgt/io.entgra.application.mgt.store.api/src/main/java/io/entgra/application/mgt/store/api/services/impl/admin/SubscriptionManagementAdminAPIImpl.java +++ b/components/application-mgt/io.entgra.application.mgt.store.api/src/main/java/io/entgra/application/mgt/store/api/services/impl/admin/SubscriptionManagementAdminAPIImpl.java @@ -18,6 +18,8 @@ package io.entgra.application.mgt.store.api.services.impl.admin; +import io.entgra.application.mgt.common.exception.SubscriptionManagementException; +import io.entgra.application.mgt.store.api.beans.SubscriptionStatusBean; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -34,10 +36,12 @@ import org.wso2.carbon.device.mgt.common.PaginationResult; import javax.ws.rs.Consumes; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; +import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.util.List; @@ -50,6 +54,30 @@ public class SubscriptionManagementAdminAPIImpl implements SubscriptionManagemen private static final Log log = LogFactory.getLog(SubscriptionManagementAdminAPIImpl.class); + @Override + @PUT + @Consumes(MediaType.APPLICATION_JSON) + @Path("/device/{deviceId}/status") + public Response updateSubscription( + @PathParam("deviceId") int deviceId, + SubscriptionStatusBean subscriptionStatusBean + ) { + try { + RequestValidationUtil.validateSubscriptionStatus(subscriptionStatusBean.getStatus()); + SubscriptionManager subscriptionManager = APIUtil.getSubscriptionManager(); + subscriptionManager.updateSubscriptionStatus(deviceId, subscriptionStatusBean.getSubId(), + subscriptionStatusBean.getStatus()); + return Response.status(Response.Status.OK).entity("Subscription status updated successfully").build(); + } catch (BadRequestException e) { + return Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage()).build(); + } catch (SubscriptionManagementException e) { + String msg = "Error occurred while changing subscription status of the subscription with the id " + + subscriptionStatusBean.getSubId(); + log.error(msg, e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); + } + } + @GET @Consumes("application/json") @Produces("application/json") @@ -60,6 +88,7 @@ public class SubscriptionManagementAdminAPIImpl implements SubscriptionManagemen @QueryParam("action") String action, @QueryParam("actionStatus") String actionStatus, @QueryParam("status") List status, + @QueryParam("installedVersion") String installedVersion, @PathParam("uuid") String uuid, @DefaultValue("0") @QueryParam("offset") int offset, @@ -97,7 +126,7 @@ public class SubscriptionManagementAdminAPIImpl implements SubscriptionManagemen } SubscriptionManager subscriptionManager = APIUtil.getSubscriptionManager(); PaginationResult subscriptionData = subscriptionManager.getAppSubscriptionDetails - (request, uuid, actionStatus, action); + (request, uuid, actionStatus, action, installedVersion); return Response.status(Response.Status.OK).entity(subscriptionData).build(); } catch (NotFoundException e) { String msg = "Application with application release UUID: " + uuid + " is not found"; diff --git a/components/application-mgt/io.entgra.application.mgt.store.api/src/main/java/io/entgra/application/mgt/store/api/services/impl/util/RequestValidationUtil.java b/components/application-mgt/io.entgra.application.mgt.store.api/src/main/java/io/entgra/application/mgt/store/api/services/impl/util/RequestValidationUtil.java index aeff5fe9f81..642107afc19 100644 --- a/components/application-mgt/io.entgra.application.mgt.store.api/src/main/java/io/entgra/application/mgt/store/api/services/impl/util/RequestValidationUtil.java +++ b/components/application-mgt/io.entgra.application.mgt.store.api/src/main/java/io/entgra/application/mgt/store/api/services/impl/util/RequestValidationUtil.java @@ -18,12 +18,16 @@ */ package io.entgra.application.mgt.store.api.services.impl.util; +import org.apache.commons.lang3.EnumUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import io.entgra.application.mgt.core.exception.BadRequestException; import io.entgra.application.mgt.store.api.util.Constants; +import org.wso2.carbon.device.mgt.common.operation.mgt.ActivityStatus; import java.util.List; +import java.util.StringJoiner; public class RequestValidationUtil { @@ -113,4 +117,18 @@ public class RequestValidationUtil { throw new BadRequestException(msg); } } + + /** + * Checks if user requested subscription status is valid. + * + */ + public static void validateSubscriptionStatus(String status) throws BadRequestException{ + if (!EnumUtils.isValidEnum(ActivityStatus.Status.class, status)) { + List validStatuses = EnumUtils.getEnumList(ActivityStatus.Status.class); + String validStatusesString = StringUtils.join(validStatuses, " | "); + String msg = "Invalid status type: " + status + ". \nValid status types are " + validStatusesString; + log.error(msg); + throw new BadRequestException(msg); + } + } } diff --git a/components/application-mgt/pom.xml b/components/application-mgt/pom.xml index 8dc8ef23dcb..62150762208 100644 --- a/components/application-mgt/pom.xml +++ b/components/application-mgt/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../../pom.xml diff --git a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.api/pom.xml b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.api/pom.xml index 19d9eba0f58..7f468c0d95c 100644 --- a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.api/pom.xml +++ b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.api/pom.xml @@ -22,7 +22,7 @@ certificate-mgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api/pom.xml b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api/pom.xml index 0cf1becb84c..c0228dd4506 100644 --- a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api/pom.xml +++ b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api/pom.xml @@ -22,7 +22,7 @@ certificate-mgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api/src/main/java/org/wso2/carbon/certificate/mgt/cert/jaxrs/api/CertificateManagementAdminService.java b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api/src/main/java/org/wso2/carbon/certificate/mgt/cert/jaxrs/api/CertificateManagementAdminService.java index 5204205dc1a..80dd79617db 100644 --- a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api/src/main/java/org/wso2/carbon/certificate/mgt/cert/jaxrs/api/CertificateManagementAdminService.java +++ b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api/src/main/java/org/wso2/carbon/certificate/mgt/cert/jaxrs/api/CertificateManagementAdminService.java @@ -310,8 +310,6 @@ public interface CertificateManagementAdminService { @HeaderParam("If-Modified-Since") String ifModifiedSince); @DELETE - @Path("/{serialNumber}") - @Consumes(MediaType.WILDCARD) @ApiOperation( consumes = MediaType.WILDCARD, produces = MediaType.APPLICATION_JSON, @@ -348,7 +346,7 @@ public interface CertificateManagementAdminService { "NOTE: Make sure that a certificate with the serial number you provide exists in the server. If not, first add a certificate.", required = true, defaultValue = "12438035315552875930") - @PathParam("serialNumber") String serialNumber); + @QueryParam("serialNumber") String serialNumber); /** * Verify Certificate for the API security filter diff --git a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api/src/main/java/org/wso2/carbon/certificate/mgt/cert/jaxrs/api/impl/CertificateManagementAdminServiceImpl.java b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api/src/main/java/org/wso2/carbon/certificate/mgt/cert/jaxrs/api/impl/CertificateManagementAdminServiceImpl.java index d898e632ae1..be0539c9462 100644 --- a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api/src/main/java/org/wso2/carbon/certificate/mgt/cert/jaxrs/api/impl/CertificateManagementAdminServiceImpl.java +++ b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api/src/main/java/org/wso2/carbon/certificate/mgt/cert/jaxrs/api/impl/CertificateManagementAdminServiceImpl.java @@ -151,9 +151,7 @@ public class CertificateManagementAdminServiceImpl implements CertificateManagem } @DELETE - @Path("/{serialNumber}") - @Consumes(MediaType.WILDCARD) - public Response removeCertificate(@PathParam("serialNumber") String serialNumber) { + public Response removeCertificate(@QueryParam("serialNumber") String serialNumber) { RequestValidationUtil.validateSerialNumber(serialNumber); CertificateManagementService certificateService = CertificateMgtAPIUtils.getCertificateManagementService(); diff --git a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/pom.xml b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/pom.xml index 0aac7f27a24..ab7d21fe476 100644 --- a/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/pom.xml +++ b/components/certificate-mgt/org.wso2.carbon.certificate.mgt.core/pom.xml @@ -38,7 +38,7 @@ org.wso2.carbon.devicemgt certificate-mgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/certificate-mgt/pom.xml b/components/certificate-mgt/pom.xml index db23fa09fe6..d5816dac404 100644 --- a/components/certificate-mgt/pom.xml +++ b/components/certificate-mgt/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../../pom.xml diff --git a/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.device.type.deployer/pom.xml b/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.device.type.deployer/pom.xml index fffb7bebde4..5488bf8a1ad 100644 --- a/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.device.type.deployer/pom.xml +++ b/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.device.type.deployer/pom.xml @@ -22,7 +22,7 @@ device-mgt-extensions org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.pull.notification/pom.xml b/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.pull.notification/pom.xml index b00f13f3ff4..15904f573b3 100644 --- a/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.pull.notification/pom.xml +++ b/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.pull.notification/pom.xml @@ -22,7 +22,7 @@ device-mgt-extensions org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.fcm/pom.xml b/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.fcm/pom.xml index ac1089637c1..779b6f0dc81 100644 --- a/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.fcm/pom.xml +++ b/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.fcm/pom.xml @@ -22,7 +22,7 @@ device-mgt-extensions org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.http/pom.xml b/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.http/pom.xml index 8c264062631..c8c38d83b24 100644 --- a/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.http/pom.xml +++ b/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.http/pom.xml @@ -22,7 +22,7 @@ device-mgt-extensions org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.mqtt/pom.xml b/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.mqtt/pom.xml index f8d0edf3c56..4657158241e 100644 --- a/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.mqtt/pom.xml +++ b/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.mqtt/pom.xml @@ -22,7 +22,7 @@ device-mgt-extensions org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.xmpp/pom.xml b/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.xmpp/pom.xml index d59e6fb6e9b..867a524d3ce 100644 --- a/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.xmpp/pom.xml +++ b/components/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.xmpp/pom.xml @@ -22,7 +22,7 @@ device-mgt-extensions org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/device-mgt-extensions/pom.xml b/components/device-mgt-extensions/pom.xml index f19e334e91f..e1645847c7a 100644 --- a/components/device-mgt-extensions/pom.xml +++ b/components/device-mgt-extensions/pom.xml @@ -22,7 +22,7 @@ carbon-devicemgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../../pom.xml diff --git a/components/device-mgt/io.entgra.carbon.device.mgt.config.api/pom.xml b/components/device-mgt/io.entgra.carbon.device.mgt.config.api/pom.xml index 0501fdc3943..74a8f7cd34f 100644 --- a/components/device-mgt/io.entgra.carbon.device.mgt.config.api/pom.xml +++ b/components/device-mgt/io.entgra.carbon.device.mgt.config.api/pom.xml @@ -22,7 +22,7 @@ device-mgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/pom.xml b/components/device-mgt/org.wso2.carbon.device.mgt.api/pom.xml index 0f0d22e4753..1bb61cc59a1 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/pom.xml +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/pom.xml @@ -22,7 +22,7 @@ device-mgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/ActivityInfoProviderService.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/ActivityInfoProviderService.java index 5185379ad9a..5a40e11ba57 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/ActivityInfoProviderService.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/ActivityInfoProviderService.java @@ -48,6 +48,7 @@ import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import java.util.List; /** * Activity related REST-API implementation. @@ -461,7 +462,7 @@ public interface ActivityInfoProviderService { name = "deviceId", value = "Device Id to filter" ) - @QueryParam("deviceId") String deviceId, + @QueryParam("deviceId") List deviceIds, @ApiParam( name = "type", value = "Operation type to filter" diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/DeviceManagementService.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/DeviceManagementService.java index eb1d9524916..d5d52446944 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/DeviceManagementService.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/DeviceManagementService.java @@ -1825,6 +1825,141 @@ public interface DeviceManagementService { @Size(max = 45) String id); + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/{type}/{id}/getstatushistory") + @ApiOperation( + produces = MediaType.APPLICATION_JSON, + httpMethod = "GET", + value = "Get Device status history", + notes = "Get a list of status history associated with the device type and id", + tags = "Device Management", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = Constants.SCOPE, value = "perm:devices:view") + }) + } + ) + @ApiResponses( + value = { + @ApiResponse( + code = 200, + message = "OK. \n Successfully fetched the status history of matching devices.", + response = List.class, + responseHeaders = { + @ResponseHeader( + name = "Content-Type", + description = "The content type of the body"), + @ResponseHeader( + name = "ETag", + description = "Entity Tag of the response resource.\n" + + "Used by caches, or in conditional requests."), + @ResponseHeader( + name = "Last-Modified", + description = "Date and time the resource was last modified.\n" + + "Used by caches, or in conditional requests."), + }), + @ApiResponse( + code = 304, + message = "Not Modified. Empty body because the client already has the latest version" + + " of the requested resource.\n"), + @ApiResponse( + code = 400, + message = "Bad Request. \n Invalid request or validation error.", + response = ErrorResponse.class), + @ApiResponse( + code = 404, + message = "Not Found. \n A device with the specified device type and id was not found.", + response = ErrorResponse.class), + @ApiResponse( + code = 500, + message = "Internal Server Error. \n " + + "Server error occurred while retrieving the device details.", + response = ErrorResponse.class) + }) + Response getDeviceStatusHistory( + @ApiParam( + name = "type", + value = "The device type, such as ios, android, or windows.", + required = true) + @PathParam("type") + @Size(max = 45) + String type, + @ApiParam( + name = "id", + value = "Device ID.", + required = true) + @PathParam("id") + @Size(max = 45) + String id); + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/{type}/{id}/getenrolmentstatushistory") + @ApiOperation( + produces = MediaType.APPLICATION_JSON, + httpMethod = "GET", + value = "Get Device Current Enrolment status history", + notes = "Get a list of status history associated with the device type and id for the current enrolment", + tags = "Device Management", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = Constants.SCOPE, value = "perm:devices:view") + }) + } + ) + @ApiResponses( + value = { + @ApiResponse( + code = 200, + message = "OK. \n Successfully fetched the status history of matching devices.", + response = List.class, + responseHeaders = { + @ResponseHeader( + name = "Content-Type", + description = "The content type of the body"), + @ResponseHeader( + name = "ETag", + description = "Entity Tag of the response resource.\n" + + "Used by caches, or in conditional requests."), + @ResponseHeader( + name = "Last-Modified", + description = "Date and time the resource was last modified.\n" + + "Used by caches, or in conditional requests."), + }), + @ApiResponse( + code = 304, + message = "Not Modified. Empty body because the client already has the latest version" + + " of the requested resource.\n"), + @ApiResponse( + code = 400, + message = "Bad Request. \n Invalid request or validation error.", + response = ErrorResponse.class), + @ApiResponse( + code = 404, + message = "Not Found. \n A device with the specified device type and id was not found.", + response = ErrorResponse.class), + @ApiResponse( + code = 500, + message = "Internal Server Error. \n " + + "Server error occurred while retrieving the device details.", + response = ErrorResponse.class) + }) + Response getCurrentEnrolmentDeviceStatusHistory( + @ApiParam( + name = "type", + value = "The device type, such as ios, android, or windows.", + required = true) + @PathParam("type") + @Size(max = 45) + String type, + @ApiParam( + name = "id", + value = "Device ID.", + required = true) + @PathParam("id") + @Size(max = 45) + String id); + @PUT @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/ReportManagementService.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/ReportManagementService.java index ee3e9354dde..cbec6b47c98 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/ReportManagementService.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/ReportManagementService.java @@ -17,6 +17,7 @@ */ package org.wso2.carbon.device.mgt.jaxrs.service.api; +import com.google.gson.JsonObject; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; @@ -37,12 +38,16 @@ import org.wso2.carbon.device.mgt.jaxrs.util.Constants; import javax.ws.rs.Consumes; import javax.ws.rs.GET; +import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; import java.util.List; @SwaggerDefinition( diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/ActivityProviderServiceImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/ActivityProviderServiceImpl.java index 8b74afc8c48..9c253c4fa6e 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/ActivityProviderServiceImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/ActivityProviderServiceImpl.java @@ -225,7 +225,7 @@ public class ActivityProviderServiceImpl implements ActivityInfoProviderService @QueryParam("initiatedBy") String initiatedBy, @QueryParam("operationCode") String operationCode, @QueryParam("deviceType") String deviceType, - @QueryParam("deviceId") String deviceId, + @QueryParam("deviceId") List deviceIds, @QueryParam("type") String type, @QueryParam("status") String status, @HeaderParam("If-Modified-Since") String ifModifiedSince, @@ -297,8 +297,8 @@ public class ActivityProviderServiceImpl implements ActivityInfoProviderService if (deviceType != null && !deviceType.isEmpty()) { activityPaginationRequest.setDeviceType(deviceType); } - if (deviceId != null && !deviceId.isEmpty()) { - activityPaginationRequest.setDeviceId(deviceId); + if (deviceIds != null && !deviceIds.isEmpty()) { + activityPaginationRequest.setDeviceIds(deviceIds); } if (type != null && !type.isEmpty()) { activityPaginationRequest.setType(Operation.Type.valueOf(type.toUpperCase())); diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/DeviceManagementServiceImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/DeviceManagementServiceImpl.java index 804029a13cd..ca9df6cb88e 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/DeviceManagementServiceImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/DeviceManagementServiceImpl.java @@ -85,6 +85,7 @@ import org.wso2.carbon.device.mgt.common.policy.mgt.monitor.NonComplianceData; import org.wso2.carbon.device.mgt.common.policy.mgt.monitor.PolicyComplianceException; import org.wso2.carbon.device.mgt.common.search.PropertyMap; import org.wso2.carbon.device.mgt.common.search.SearchContext; +import org.wso2.carbon.device.mgt.common.type.mgt.DeviceStatus; import org.wso2.carbon.device.mgt.core.app.mgt.ApplicationManagementProviderService; import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceDetailsMgtException; import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceInformationManager; @@ -1141,6 +1142,70 @@ public class DeviceManagementServiceImpl implements DeviceManagementService { } } + /** + * List device status history + * + * @param type Device type + * @param id Device id + * @return {@link Response} object + */ + @GET + @Path("/{type}/{id}/getstatushistory") + public Response getDeviceStatusHistory(@PathParam("type") @Size(max = 45) String type, + @PathParam("id") @Size(max = 45) String id) { + //TODO check authorization for this + RequestValidationUtil.validateDeviceIdentifier(type, id); + DeviceManagementProviderService deviceManagementProviderService = + DeviceMgtAPIUtils.getDeviceManagementService(); + try { + DeviceIdentifier deviceIdentifier = new DeviceIdentifier(id, type); + Device persistedDevice = deviceManagementProviderService.getDevice(deviceIdentifier, false); + if (persistedDevice == null) { + return Response.status(Response.Status.NOT_FOUND).build(); + } + List deviceStatusHistory = deviceManagementProviderService.getDeviceStatusHistory(persistedDevice); + return Response.status(Response.Status.OK).entity(deviceStatusHistory).build(); + } catch (DeviceManagementException e) { + String msg = "Error occurred while retreiving device status history for device of type : " + type + " and " + + "device id : " + id; + log.error(msg); + return Response.status(Response.Status.BAD_REQUEST).entity( + new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build(); + } + } + + /** + * List device status history for the current enrolment + * + * @param type Device type + * @param id Device id + * @return {@link Response} object + */ + @GET + @Path("/{type}/{id}/getenrolmentstatushistory") + public Response getCurrentEnrolmentDeviceStatusHistory(@PathParam("type") @Size(max = 45) String type, + @PathParam("id") @Size(max = 45) String id) { + //TODO check authorization for this or current enrolment should be based on for the enrolment associated with the user + RequestValidationUtil.validateDeviceIdentifier(type, id); + DeviceManagementProviderService deviceManagementProviderService = + DeviceMgtAPIUtils.getDeviceManagementService(); + try { + DeviceIdentifier deviceIdentifier = new DeviceIdentifier(id, type); + Device persistedDevice = deviceManagementProviderService.getDevice(deviceIdentifier, false); + if (persistedDevice == null) { + return Response.status(Response.Status.NOT_FOUND).build(); + } + List deviceStatusHistory = deviceManagementProviderService.getDeviceCurrentEnrolmentStatusHistory(persistedDevice); + return Response.status(Response.Status.OK).entity(deviceStatusHistory).build(); + } catch (DeviceManagementException e) { + String msg = "Error occurred while retreiving device status history for device of type : " + type + " and " + + "device id : " + id; + log.error(msg); + return Response.status(Response.Status.BAD_REQUEST).entity( + new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build(); + } + } + @POST @Path("/{type}/operations") public Response addOperation(@PathParam("type") String type, @Valid OperationRequest operationRequest) { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/util/RequestValidationUtil.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/util/RequestValidationUtil.java index 116c715eb26..1308dfa9700 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/util/RequestValidationUtil.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/util/RequestValidationUtil.java @@ -610,10 +610,12 @@ public class RequestValidationUtil { || Constants.OperationStatus.NOTNOW.toUpperCase().equals(status.get(i)) || Constants.OperationStatus.REPEATED.toUpperCase().equals(status.get(i)) || Constants.OperationStatus.PENDING.toUpperCase().equals(status.get(i)) - || Constants.OperationStatus.IN_PROGRESS.toUpperCase().equals(status.get(i))) { + || Constants.OperationStatus.IN_PROGRESS.toUpperCase().equals(status.get(i)) + || Constants.OperationStatus.REQUIRED_CONFIRMATION.toUpperCase().equals(status.get(i)) + || Constants.OperationStatus.CONFIRMED.toUpperCase().equals(status.get(i))) { } else { String msg = "Invalid status type: " + status + ". \nValid status types are COMPLETED | ERROR | " + - "IN_PROGRESS | NOTNOW | PENDING | REPEATED"; + "IN_PROGRESS | NOTNOW | PENDING | REPEATED | REQUIRED_CONFIRMATION | CONFIRMED"; log.error(msg); throw new InputValidationException(new ErrorResponse.ErrorResponseBuilder() .setCode(HttpStatus.SC_BAD_REQUEST) diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/Constants.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/Constants.java index 7042bb86106..d7462993a92 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/Constants.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/Constants.java @@ -68,6 +68,8 @@ public class Constants { public static final String PENDING = "pending"; public static final String NOTNOW = "notnow"; public static final String REPEATED = "repeated"; + public static final String REQUIRED_CONFIRMATION = "required_confirmation"; + public static final String CONFIRMED = "confirmed"; } public static final String DEVICES = "devices"; public static final String ATTRIBUTE_DISPLAY_NAME = "DisplayName"; diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/DeviceMgtAPIUtils.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/DeviceMgtAPIUtils.java index c26a1f49a4b..8861833f5aa 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/DeviceMgtAPIUtils.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/DeviceMgtAPIUtils.java @@ -959,9 +959,15 @@ public class DeviceMgtAPIUtils { case Constants.OperationStatus.REPEATED: operation.setStatus(Operation.Status.REPEATED); break; + case Constants.OperationStatus.REQUIRED_CONFIRMATION: + operation.setStatus(Operation.Status.REQUIRED_CONFIRMATION); + break; + case Constants.OperationStatus.CONFIRMED: + operation.setStatus(Operation.Status.CONFIRMED); + break; default: String msg = "Invalid operation status. Valid operations: " + - "[IN_PROGRESS, PENDING, COMPLETED, ERROR, REPEATED, NOTNOW]"; + "[IN_PROGRESS, PENDING, COMPLETED, ERROR, REPEATED, NOTNOW, REQUIRED_CONFIRMATION, CONFIRMED]"; log.error(msg); throw new BadRequestException(msg); } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/pom.xml b/components/device-mgt/org.wso2.carbon.device.mgt.common/pom.xml index 5ae45654efa..0a8dde45f39 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/pom.xml +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/pom.xml @@ -21,7 +21,7 @@ device-mgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/ActivityPaginationRequest.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/ActivityPaginationRequest.java index 08cc89093a3..b45d6c5083b 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/ActivityPaginationRequest.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/ActivityPaginationRequest.java @@ -20,6 +20,8 @@ package org.wso2.carbon.device.mgt.common; import org.wso2.carbon.device.mgt.common.operation.mgt.Operation; +import java.util.List; + /** * This class holds required parameters for a querying a paginated activity response. */ @@ -28,7 +30,7 @@ public class ActivityPaginationRequest { private int offset; private int limit; private String deviceType; - private String deviceId; + private List deviceIds; private String operationCode; private String initiatedBy; private long since; @@ -66,12 +68,12 @@ public class ActivityPaginationRequest { this.deviceType = deviceType; } - public String getDeviceId() { - return deviceId; + public List getDeviceIds() { + return deviceIds; } - public void setDeviceId(String deviceId) { - this.deviceId = deviceId; + public void setDeviceIds(List deviceIds) { + this.deviceIds = deviceIds; } public String getOperationCode() { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/DeviceManagementConstants.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/DeviceManagementConstants.java index 62590a821c1..154594678c7 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/DeviceManagementConstants.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/DeviceManagementConstants.java @@ -142,4 +142,9 @@ public final class DeviceManagementConstants { public static final String GEOFENCE = "GEOFENCE"; } + public static final class MaintenanceProperties { + private MaintenanceProperties() {throw new AssertionError();} + public static final String MAINTENANCE_USER = "system"; + } + } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/ActivityStatus.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/ActivityStatus.java index 234aa084c22..c07621a6104 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/ActivityStatus.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/ActivityStatus.java @@ -30,7 +30,7 @@ import java.util.List; public class ActivityStatus { public enum Status { - IN_PROGRESS, PENDING, COMPLETED, ERROR, REPEATED, INVALID, UNAUTHORIZED, NOTNOW + IN_PROGRESS, PENDING, COMPLETED, ERROR, REPEATED, INVALID, UNAUTHORIZED, NOTNOW, REQUIRED_CONFIRMATION, CONFIRMED } @ApiModelProperty( diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/Operation.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/Operation.java index b3b863a5f35..4c6dd9256ea 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/Operation.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/Operation.java @@ -37,7 +37,7 @@ public class Operation implements Serializable { } public enum Status { - IN_PROGRESS, PENDING, COMPLETED, ERROR, REPEATED, NOTNOW + IN_PROGRESS, PENDING, COMPLETED, ERROR, REPEATED, NOTNOW, REQUIRED_CONFIRMATION, CONFIRMED } public enum Control { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/OperationManager.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/OperationManager.java index 63333de165c..ec00986a47b 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/OperationManager.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/OperationManager.java @@ -154,4 +154,6 @@ public interface OperationManager { */ boolean isOperationExist(DeviceIdentifier deviceId, int operationId) throws OperationManagementException; + List getActivities(List deviceTypes, String operationCode, long updatedSince, String operationStatus) + throws OperationManagementException; } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/policy/mgt/ui/Column.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/policy/mgt/ui/Column.java index 4ecdb313ae9..34e8bc1c865 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/policy/mgt/ui/Column.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/policy/mgt/ui/Column.java @@ -32,6 +32,7 @@ public class Column { private Input input; private Upload upload; private Checkbox checkbox; + private boolean isRequired; @XmlAttribute(name = "name", required = true) public String getName() { return name; } @@ -96,4 +97,13 @@ public class Column { public void setCheckbox(Checkbox checkbox) { this.checkbox = checkbox; } + + @XmlElement(name = "RequiredItem") + public boolean isRequired() { + return isRequired; + } + + public void setRequired(boolean required) { + isRequired = required; + } } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/policy/mgt/ui/Item.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/policy/mgt/ui/Item.java index 43927eb8761..aac25bc0d20 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/policy/mgt/ui/Item.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/policy/mgt/ui/Item.java @@ -31,6 +31,7 @@ public class Item { private String key; private String value; private boolean isRequired; + private boolean isNoneEmptyTable; private String subTitle; private List conditions; private Checkbox checkbox; @@ -103,6 +104,15 @@ public class Item { isRequired = required; } + @XmlElement(name = "NoneEmptyTable") + public boolean isNoneEmptyTable() { + return isNoneEmptyTable; + } + + public void setNoneEmptyTable(boolean noneEmptyTable) { + isNoneEmptyTable = noneEmptyTable; + } + @XmlElement(name = "SubTitle") public String getSubTitle() { return subTitle; } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/type/mgt/DeviceStatus.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/type/mgt/DeviceStatus.java new file mode 100644 index 00000000000..a91c4947b11 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/type/mgt/DeviceStatus.java @@ -0,0 +1,61 @@ +package org.wso2.carbon.device.mgt.common.type.mgt; + +import org.wso2.carbon.device.mgt.common.EnrolmentInfo; + +import java.util.Date; + +public class DeviceStatus { + private EnrolmentInfo.Status status; + private Date updateTime; + private int enrolmentId; + private int deviceId; + private String changedBy; + + public DeviceStatus(int enrolmentId, int deviceId, EnrolmentInfo.Status status, Date updateTime, String changedBy) { + this.status = status; + this.updateTime = updateTime; + this.enrolmentId = enrolmentId; + this.deviceId = deviceId; + this.changedBy = changedBy; + } + + public String getChangedBy() { + return changedBy; + } + + public void setChangedBy(String changedBy) { + this.changedBy = changedBy; + } + + public EnrolmentInfo.Status getStatus() { + return status; + } + + public void setStatus(EnrolmentInfo.Status status) { + this.status = status; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + public int getEnrolmentId() { + return enrolmentId; + } + + public void setEnrolmentId(int enrolmentId) { + this.enrolmentId = enrolmentId; + } + + public int getDeviceId() { + return deviceId; + } + + public void setDeviceId(int deviceId) { + this.deviceId = deviceId; + } +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/pom.xml b/components/device-mgt/org.wso2.carbon.device.mgt.core/pom.xml index 04b1e73c793..8007415ca73 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/pom.xml +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt device-mgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml @@ -338,6 +338,10 @@ org.wso2.carbon.devicemgt io.entgra.server.bootup.heartbeat.beacon + + com.google.guava + guava + diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/DeviceManagementConfig.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/DeviceManagementConfig.java index 4b8adcc3570..ebdfa35bfc3 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/DeviceManagementConfig.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/DeviceManagementConfig.java @@ -24,6 +24,7 @@ import org.wso2.carbon.device.mgt.core.config.archival.ArchivalConfiguration; import org.wso2.carbon.device.mgt.core.config.cache.CertificateCacheConfiguration; import org.wso2.carbon.device.mgt.core.config.cache.DeviceCacheConfiguration; import org.wso2.carbon.device.mgt.core.config.cache.GeoFenceCacheConfiguration; +import org.wso2.carbon.device.mgt.core.config.operation.timeout.OperationTimeoutConfiguration; import org.wso2.carbon.device.mgt.core.event.config.EventOperationTaskConfiguration; import org.wso2.carbon.device.mgt.core.config.geo.location.GeoLocationConfiguration; import org.wso2.carbon.device.mgt.core.config.identity.IdentityConfigurations; @@ -66,6 +67,7 @@ public final class DeviceManagementConfig { private ArchivalConfiguration archivalConfiguration; private EnrollmentNotificationConfiguration enrollmentNotificationConfiguration; private DefaultRoles defaultRoles; + private OperationTimeoutConfiguration operationTimeoutConfiguration; @XmlElement(name = "ManagementRepository", required = true) public DeviceManagementConfigRepository getDeviceManagementConfigRepository() { @@ -243,5 +245,14 @@ public final class DeviceManagementConfig { public DefaultRoles getDefaultRoles() { return defaultRoles; } public void setDefaultRoles(DefaultRoles defaultRoles) { this.defaultRoles = defaultRoles; } + + @XmlElement(name = "OperationTimeoutConfigurations", required = true) + public OperationTimeoutConfiguration getOperationTimeoutConfiguration() { + return operationTimeoutConfiguration; + } + + public void setOperationTimeoutConfiguration(OperationTimeoutConfiguration operationTimeoutConfiguration) { + this.operationTimeoutConfiguration = operationTimeoutConfiguration; + } } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/operation/timeout/OperationTimeout.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/operation/timeout/OperationTimeout.java new file mode 100644 index 00000000000..f9f9302770e --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/operation/timeout/OperationTimeout.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2022, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.carbon.device.mgt.core.config.operation.timeout; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.List; + +@XmlRootElement(name = "OperationTimeout") +public class OperationTimeout { + + private String code; + private int timeout; + private List deviceTypes; + private String initialStatus; + private String nextStatus; + + public String getCode() { + return code; + } + + @XmlElement(name = "Code", required = true) + public void setCode(String code) { + this.code = code; + } + + public int getTimeout() { + return timeout; + } + + @XmlElement(name = "Timeout", required = true) + public void setTimeout(int timeout) { + this.timeout = timeout; + } + + public List getDeviceTypes() { + return deviceTypes; + } + + @XmlElementWrapper(name = "DeviceTypes", required = true) + @XmlElement(name = "DeviceType", required = true) + public void setDeviceTypes(List deviceTypes) { + this.deviceTypes = deviceTypes; + } + + public String getInitialStatus() { + return initialStatus; + } + + @XmlElement(name = "InitialStatus", required = true) + public void setInitialStatus(String initialStatus) { + this.initialStatus = initialStatus; + } + + public String getNextStatus() { + return nextStatus; + } + + @XmlElement(name = "NextStatus", required = true) + public void setNextStatus(String nextStatus) { + this.nextStatus = nextStatus; + } +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/operation/timeout/OperationTimeoutConfiguration.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/operation/timeout/OperationTimeoutConfiguration.java new file mode 100644 index 00000000000..a2777eda404 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/config/operation/timeout/OperationTimeoutConfiguration.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.carbon.device.mgt.core.config.operation.timeout; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.List; + +@XmlRootElement(name = "OperationTimeoutConfigurations") +public class OperationTimeoutConfiguration { + private List operationTimeoutList; + + public List getOperationTimeoutList() { + return operationTimeoutList; + } + + @XmlElementWrapper(name = "OperationTimeouts", required = true) + @XmlElement(name = "OperationTimeout", required = false) + public void setOperationTimeoutList(List operationTimeoutList) { + this.operationTimeoutList = operationTimeoutList; + } +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceManagementDAOFactory.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceManagementDAOFactory.java index c5c778ca23b..a07989e3385 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceManagementDAOFactory.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceManagementDAOFactory.java @@ -26,11 +26,8 @@ import org.wso2.carbon.device.mgt.common.exceptions.TransactionManagementExcepti import org.wso2.carbon.device.mgt.common.exceptions.UnsupportedDatabaseEngineException; import org.wso2.carbon.device.mgt.core.config.datasource.DataSourceConfig; import org.wso2.carbon.device.mgt.core.config.datasource.JNDILookupDefinition; -import org.wso2.carbon.device.mgt.core.dao.impl.ApplicationDAOImpl; -import org.wso2.carbon.device.mgt.core.dao.impl.DeviceTypeDAOImpl; -import org.wso2.carbon.device.mgt.core.dao.impl.EnrollmentDAOImpl; +import org.wso2.carbon.device.mgt.core.dao.impl.*; import org.wso2.carbon.device.mgt.core.dao.impl.event.GenericEventConfigDAOImpl; -import org.wso2.carbon.device.mgt.core.dao.impl.GeofenceDAOImpl; import org.wso2.carbon.device.mgt.core.dao.impl.device.GenericDeviceDAOImpl; import org.wso2.carbon.device.mgt.core.dao.impl.device.OracleDeviceDAOImpl; import org.wso2.carbon.device.mgt.core.dao.impl.device.PostgreSQLDeviceDAOImpl; @@ -127,7 +124,9 @@ public class DeviceManagementDAOFactory { public static EnrollmentDAO getEnrollmentDAO() { return new EnrollmentDAOImpl(); } - + public static DeviceStatusDAO getDeviceStatusDAO() { + return new DeviceStatusDAOImpl(); + } public static ApplicationDAO getApplicationDAO() { if (databaseEngine != null) { switch (databaseEngine) { @@ -152,27 +151,6 @@ public class DeviceManagementDAOFactory { return new PrivacyComplianceDAOImpl(); } - public static GeofenceDAO getGeofenceDAO() { - return new GeofenceDAOImpl(); - } - - public static EventConfigDAO getEventConfigDAO() { - if (databaseEngine != null) { - switch (databaseEngine) { - case DeviceManagementConstants.DataBaseTypes.DB_TYPE_POSTGRESQL: - case DeviceManagementConstants.DataBaseTypes.DB_TYPE_ORACLE: - case DeviceManagementConstants.DataBaseTypes.DB_TYPE_MSSQL: - case DeviceManagementConstants.DataBaseTypes.DB_TYPE_MYSQL: - return new GenericEventConfigDAOImpl(); - case DeviceManagementConstants.DataBaseTypes.DB_TYPE_H2: - return new H2EventConfigDAOImpl(); - default: - throw new UnsupportedDatabaseEngineException("Unsupported database engine : " + databaseEngine); - } - } - throw new IllegalStateException("Database engine has not initialized properly."); - } - public static void init(DataSourceConfig config) { dataSource = resolveDataSource(config); try { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceStatusDAO.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceStatusDAO.java new file mode 100644 index 00000000000..6d9cb18e585 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceStatusDAO.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.wso2.carbon.device.mgt.core.dao; + +import org.wso2.carbon.device.mgt.common.Device; +import org.wso2.carbon.device.mgt.common.EnrolmentInfo; +import org.wso2.carbon.device.mgt.common.EnrolmentInfo.Status; +import org.wso2.carbon.device.mgt.common.type.mgt.DeviceStatus; + +import java.util.Date; +import java.util.List; + +public interface DeviceStatusDAO { + +// boolean updateStatus(int deviceId, Status status, int tenantId) throws DeviceManagementDAOException; + +// boolean updateStatus(int enrolmentId, Status status) throws DeviceManagementDAOException; + + List getStatus(int deviceId, int tenantId) throws DeviceManagementDAOException; + + List getStatus(int deviceId, int tenantId, Date fromDate, Date toDate) throws DeviceManagementDAOException; + + List getStatus(int enrolmentId) throws DeviceManagementDAOException; + + List getStatus(int enrolmentId, Date fromDate, Date toDate) throws DeviceManagementDAOException; + +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/EnrollmentDAO.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/EnrollmentDAO.java index 6613b3044a5..be621eab347 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/EnrollmentDAO.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/EnrollmentDAO.java @@ -34,8 +34,11 @@ public interface EnrollmentDAO { int removeEnrollment(int deviceId, String currentOwner, int tenantId) throws DeviceManagementDAOException; + @Deprecated boolean setStatus(String currentOwner, Status status, int tenantId) throws DeviceManagementDAOException; + boolean setStatusAllDevices(String currentOwner, Status status, int tenantId) throws DeviceManagementDAOException; + boolean setStatus(int enrolmentId, Status status, int tenantId) throws DeviceManagementDAOException; Status getStatus(int deviceId, String currentOwner, int tenantId) throws DeviceManagementDAOException; diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/EventManagementDAOFactory.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/EventManagementDAOFactory.java new file mode 100644 index 00000000000..754b5190041 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/EventManagementDAOFactory.java @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2022, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.device.mgt.core.dao; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.mgt.common.DeviceManagementConstants; +import org.wso2.carbon.device.mgt.common.exceptions.IllegalTransactionStateException; +import org.wso2.carbon.device.mgt.common.exceptions.TransactionManagementException; +import org.wso2.carbon.device.mgt.common.exceptions.UnsupportedDatabaseEngineException; +import org.wso2.carbon.device.mgt.core.config.datasource.DataSourceConfig; +import org.wso2.carbon.device.mgt.core.config.datasource.JNDILookupDefinition; +import org.wso2.carbon.device.mgt.core.dao.impl.*; +import org.wso2.carbon.device.mgt.core.dao.impl.event.GenericEventConfigDAOImpl; +import org.wso2.carbon.device.mgt.core.dao.impl.event.H2EventConfigDAOImpl; +import org.wso2.carbon.device.mgt.core.dao.util.DeviceManagementDAOUtil; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Hashtable; +import java.util.List; + +public class EventManagementDAOFactory { + private static DataSource dataSource; + private static String databaseEngine; + private static final Log log = LogFactory.getLog(EventManagementDAOFactory.class); + private static ThreadLocal currentConnection = new ThreadLocal<>(); + + + public static GeofenceDAO getGeofenceDAO() { + return new GeofenceDAOImpl(); + } + + public static EventConfigDAO getEventConfigDAO() { + if (databaseEngine != null) { + switch (databaseEngine) { + case DeviceManagementConstants.DataBaseTypes.DB_TYPE_POSTGRESQL: + case DeviceManagementConstants.DataBaseTypes.DB_TYPE_ORACLE: + case DeviceManagementConstants.DataBaseTypes.DB_TYPE_MSSQL: + case DeviceManagementConstants.DataBaseTypes.DB_TYPE_MYSQL: + return new GenericEventConfigDAOImpl(); + case DeviceManagementConstants.DataBaseTypes.DB_TYPE_H2: + return new H2EventConfigDAOImpl(); + default: + throw new UnsupportedDatabaseEngineException("Unsupported database engine : " + databaseEngine); + } + } + throw new IllegalStateException("Database engine has not initialized properly."); + } + + public static void init(DataSourceConfig config) { + dataSource = resolveDataSource(config); + try { + databaseEngine = dataSource.getConnection().getMetaData().getDatabaseProductName(); + } catch (SQLException e) { + log.error("Error occurred while retrieving config.datasource connection", e); + } + } + + public static void init(DataSource dtSource) { + dataSource = dtSource; + try { + databaseEngine = dataSource.getConnection().getMetaData().getDatabaseProductName(); + } catch (SQLException e) { + log.error("Error occurred while retrieving config.datasource connection", e); + } + } + + public static void beginTransaction() throws TransactionManagementException { + Connection conn = currentConnection.get(); + if (conn != null) { + throw new IllegalTransactionStateException("A transaction is already active within the context of " + + "this particular thread. Therefore, calling 'beginTransaction/openConnection' while another " + + "transaction is already active is a sign of improper transaction handling"); + } + try { + conn = dataSource.getConnection(); + conn.setAutoCommit(false); + currentConnection.set(conn); + } catch (SQLException e) { + throw new TransactionManagementException("Error occurred while retrieving config.datasource connection", e); + } + } + + public static void openConnection() throws SQLException { + Connection conn = currentConnection.get(); + if (conn != null) { + throw new IllegalTransactionStateException("A transaction is already active within the context of " + + "this particular thread. Therefore, calling 'beginTransaction/openConnection' while another " + + "transaction is already active is a sign of improper transaction handling"); + } + conn = dataSource.getConnection(); + currentConnection.set(conn); + } + + public static Connection getConnection() throws SQLException { + Connection conn = currentConnection.get(); + if (conn == null) { + throw new IllegalTransactionStateException("No connection is associated with the current transaction. " + + "This might have ideally been caused by not properly initiating the transaction via " + + "'beginTransaction'/'openConnection' methods"); + } + return conn; + } + + public static void commitTransaction() { + Connection conn = currentConnection.get(); + if (conn == null) { + throw new IllegalTransactionStateException("No connection is associated with the current transaction. " + + "This might have ideally been caused by not properly initiating the transaction via " + + "'beginTransaction'/'openConnection' methods"); + } + try { + conn.commit(); + } catch (SQLException e) { + log.error("Error occurred while committing the transaction", e); + } + } + + public static void rollbackTransaction() { + Connection conn = currentConnection.get(); + if (conn == null) { + throw new IllegalTransactionStateException("No connection is associated with the current transaction. " + + "This might have ideally been caused by not properly initiating the transaction via " + + "'beginTransaction'/'openConnection' methods"); + } + try { + conn.rollback(); + } catch (SQLException e) { + log.warn("Error occurred while roll-backing the transaction", e); + } + } + + public static void closeConnection() { + Connection conn = currentConnection.get(); + if (conn == null) { + throw new IllegalTransactionStateException("No connection is associated with the current transaction. " + + "This might have ideally been caused by not properly initiating the transaction via " + + "'beginTransaction'/'openConnection' methods"); + } + try { + conn.close(); + } catch (SQLException e) { + log.warn("Error occurred while close the connection"); + } + currentConnection.remove(); + } + + + /** + * Resolve data source from the data source definition + * + * @param config data source configuration + * @return data source resolved from the data source definition + */ + private static DataSource resolveDataSource(DataSourceConfig config) { + DataSource dataSource = null; + if (config == null) { + throw new RuntimeException( + "Device Management Repository data source configuration " + "is null and " + + "thus, is not initialized"); + } + JNDILookupDefinition jndiConfig = config.getJndiLookupDefinition(); + if (jndiConfig != null) { + if (log.isDebugEnabled()) { + log.debug("Initializing Device Management Repository data source using the JNDI " + + "Lookup Definition"); + } + List jndiPropertyList = + jndiConfig.getJndiProperties(); + if (jndiPropertyList != null) { + Hashtable jndiProperties = new Hashtable(); + for (JNDILookupDefinition.JNDIProperty prop : jndiPropertyList) { + jndiProperties.put(prop.getName(), prop.getValue()); + } + dataSource = DeviceManagementDAOUtil.lookupDataSource(jndiConfig.getJndiName(), jndiProperties); + } else { + dataSource = DeviceManagementDAOUtil.lookupDataSource(jndiConfig.getJndiName(), null); + } + } + return dataSource; + } +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/AbstractEventConfigDAO.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/AbstractEventConfigDAO.java index a0cc3889a09..0e5194a4a4f 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/AbstractEventConfigDAO.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/AbstractEventConfigDAO.java @@ -24,6 +24,7 @@ import org.wso2.carbon.device.mgt.common.event.config.EventConfig; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory; import org.wso2.carbon.device.mgt.core.dao.EventConfigDAO; import org.wso2.carbon.device.mgt.core.dao.EventManagementDAOException; +import org.wso2.carbon.device.mgt.core.dao.EventManagementDAOFactory; import java.sql.Connection; import java.sql.PreparedStatement; @@ -342,6 +343,6 @@ public abstract class AbstractEventConfigDAO implements EventConfigDAO { } private Connection getConnection() throws SQLException { - return DeviceManagementDAOFactory.getConnection(); + return EventManagementDAOFactory.getConnection(); } } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/DeviceStatusDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/DeviceStatusDAOImpl.java new file mode 100644 index 00000000000..0305f831145 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/DeviceStatusDAOImpl.java @@ -0,0 +1,89 @@ +package org.wso2.carbon.device.mgt.core.dao.impl; + +import org.wso2.carbon.device.mgt.common.EnrolmentInfo; +import org.wso2.carbon.device.mgt.common.type.mgt.DeviceStatus; +import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException; +import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory; +import org.wso2.carbon.device.mgt.core.dao.DeviceStatusDAO; +import org.wso2.carbon.device.mgt.core.dao.EnrollmentDAO; +import org.wso2.carbon.device.mgt.core.dao.util.DeviceManagementDAOUtil; + +import java.sql.*; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +public class DeviceStatusDAOImpl implements DeviceStatusDAO { + + private List getStatus(int id, Date fromDate, Date toDate, boolean isDeviceId) throws DeviceManagementDAOException { + List result = new ArrayList<>(); + Connection conn; + PreparedStatement stmt = null; + ResultSet rs = null; + EnrolmentInfo.Status status = null; + try { + conn = this.getConnection(); + // either we list all status values for the device using the device id or only get status values for the given enrolment id + String idType = isDeviceId ? "DEVICE_ID" : "ENROLMENT_ID"; + String sql = "SELECT ENROLMENT_ID, DEVICE_ID, UPDATE_TIME, STATUS, CHANGED_BY FROM DM_DEVICE_STATUS WHERE " + idType + " = ?"; + // filter the data based on a date range if specified + if (fromDate != null){ + sql += " AND UPDATE_TIME >= ?"; + } + if (toDate != null){ + sql += " AND UPDATE_TIME <= ?"; + } + + stmt = conn.prepareStatement(sql); + + int i = 1; + stmt.setInt(i++, id); + if (fromDate != null){ + Timestamp fromTime = new Timestamp(fromDate.getTime()); + stmt.setTimestamp(i++, fromTime); + } + if (toDate != null){ + Timestamp toTime = new Timestamp(toDate.getTime()); + stmt.setTimestamp(i++, toTime); + } + rs = stmt.executeQuery(); + + while (rs.next()) { + DeviceStatus ds = new DeviceStatus(rs.getInt("ENROLMENT_ID"), rs.getInt("DEVICE_ID"), + EnrolmentInfo.Status.valueOf(rs.getString("STATUS")), + new Date(rs.getTimestamp("UPDATE_TIME").getTime()), rs.getString("CHANGED_BY")); + result.add(ds); + } + } catch (SQLException e) { + throw new DeviceManagementDAOException("Error occurred while setting the status of device enrolment", e); + } finally { + DeviceManagementDAOUtil.cleanupResources(stmt, rs); + } + return result; + } + + @Override + public List getStatus(int enrolmentId, Date fromDate, Date toDate) throws DeviceManagementDAOException { + return getStatus(enrolmentId, fromDate, toDate, false); + } + + @Override + public List getStatus(int deviceId, int tenantId) throws DeviceManagementDAOException { + return getStatus(deviceId, tenantId, null, null); + } + + @Override + public List getStatus(int deviceId, int tenantId, Date fromDate, Date toDate) throws DeviceManagementDAOException { + return getStatus(deviceId, fromDate, toDate, true); + } + + @Override + public List getStatus(int enrolmentId) throws DeviceManagementDAOException { + return getStatus(enrolmentId, null, null); + } + + + private Connection getConnection() throws SQLException { + return DeviceManagementDAOFactory.getConnection(); + } +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/EnrollmentDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/EnrollmentDAOImpl.java index 186b3b4dd63..1472518b1cb 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/EnrollmentDAOImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/EnrollmentDAOImpl.java @@ -18,7 +18,9 @@ */ package org.wso2.carbon.device.mgt.core.dao.impl; +import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.device.mgt.common.Device; +import org.wso2.carbon.device.mgt.common.DeviceManagementConstants; import org.wso2.carbon.device.mgt.common.EnrolmentInfo; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory; @@ -39,7 +41,7 @@ public class EnrollmentDAOImpl implements EnrollmentDAO { @Override public EnrolmentInfo addEnrollment(int deviceId, EnrolmentInfo enrolmentInfo, - int tenantId) throws DeviceManagementDAOException { + int tenantId) throws DeviceManagementDAOException { Connection conn; PreparedStatement stmt = null; ResultSet rs = null; @@ -64,10 +66,12 @@ public class EnrollmentDAOImpl implements EnrollmentDAO { enrolmentInfo.setId(enrolmentId); enrolmentInfo.setDateOfEnrolment(enrollmentTime.getTime()); enrolmentInfo.setDateOfLastUpdate(enrollmentTime.getTime()); + addDeviceStatus(enrolmentId, enrolmentInfo.getStatus()); return enrolmentInfo; } return null; } catch (SQLException e) { + e.printStackTrace(); throw new DeviceManagementDAOException("Error occurred while adding enrolment configuration", e); } finally { DeviceManagementDAOUtil.cleanupResources(stmt, rs); @@ -89,7 +93,11 @@ public class EnrollmentDAOImpl implements EnrollmentDAO { stmt.setTimestamp(3, new Timestamp(new Date().getTime())); stmt.setInt(4, enrolmentInfo.getId()); stmt.setInt(5, tenantId); - return stmt.executeUpdate(); + int updatedCount = stmt.executeUpdate(); + if (updatedCount == 1){ + addDeviceStatus(enrolmentInfo.getId(), enrolmentInfo.getStatus()); + } + return updatedCount; } catch (SQLException e) { throw new DeviceManagementDAOException("Error occurred while updating enrolment configuration", e); } finally { @@ -124,6 +132,9 @@ public class EnrollmentDAOImpl implements EnrollmentDAO { } if (updateStatus > 0) { status = true; + for (EnrolmentInfo enrolmentInfo : enrolmentInfos) { + addDeviceStatus(enrolmentInfo); + } } } catch (SQLException e) { throw new DeviceManagementDAOException("Error occurred while updating enrolment status of given device-list.", e); @@ -189,23 +200,31 @@ public class EnrollmentDAOImpl implements EnrollmentDAO { @Override public boolean setStatus(String currentOwner, EnrolmentInfo.Status status, int tenantId) throws DeviceManagementDAOException { + return setStatusAllDevices(currentOwner, status, tenantId); + } + + @Override + public boolean setStatusAllDevices(String currentOwner, EnrolmentInfo.Status status, int tenantId) + throws DeviceManagementDAOException{ Connection conn; PreparedStatement stmt = null; + Timestamp updateTime = new Timestamp(new Date().getTime()); if(getCountOfDevicesOfOwner(currentOwner, tenantId) > 0){ try { conn = this.getConnection(); - String sql = "UPDATE DM_ENROLMENT SET STATUS = ? WHERE OWNER = ? AND TENANT_ID = ?"; + String sql = "UPDATE DM_ENROLMENT SET STATUS = ?, DATE_OF_LAST_UPDATE = ? WHERE OWNER = ? AND TENANT_ID = ?"; stmt = conn.prepareStatement(sql); stmt.setString(1, status.toString()); - stmt.setString(2, currentOwner); - stmt.setInt(3, tenantId); + stmt.setTimestamp(2, updateTime); + stmt.setString(3, currentOwner); + stmt.setInt(4, tenantId); stmt.executeUpdate(); } catch (SQLException e) { throw new DeviceManagementDAOException("Error occurred while setting the status of device enrolment", e); } finally { DeviceManagementDAOUtil.cleanupResources(stmt, null); } - return true; + return addDeviceStatus(currentOwner, status, tenantId); } else { return false; } @@ -215,14 +234,22 @@ public class EnrollmentDAOImpl implements EnrollmentDAO { public boolean setStatus(int enrolmentID, EnrolmentInfo.Status status, int tenantId) throws DeviceManagementDAOException { Connection conn; PreparedStatement stmt = null; + Timestamp updateTime = new Timestamp(new Date().getTime()); try { conn = this.getConnection(); - String sql = "UPDATE DM_ENROLMENT SET STATUS = ? WHERE ID = ? AND TENANT_ID = ?"; + String sql = "UPDATE DM_ENROLMENT SET STATUS = ?, DATE_OF_LAST_UPDATE = ? WHERE ID = ? AND TENANT_ID = ?"; stmt = conn.prepareStatement(sql); stmt.setString(1, status.toString()); - stmt.setInt(2, enrolmentID); - stmt.setInt(3, tenantId); - stmt.executeUpdate(); + stmt.setTimestamp(2, updateTime); + stmt.setInt(3, enrolmentID); + stmt.setInt(4, tenantId); + int updatedRowCount = stmt.executeUpdate(); + if (updatedRowCount != 1){ + throw new DeviceManagementDAOException("Error occurred while setting the status of device enrolment: "+ + updatedRowCount + " rows were updated instead of one row!!!"); + } + // save the device status history + addDeviceStatus(enrolmentID, status); } catch (SQLException e) { throw new DeviceManagementDAOException("Error occurred while setting the status of device enrolment", e); } finally { @@ -231,6 +258,133 @@ public class EnrollmentDAOImpl implements EnrollmentDAO { return true; } + private boolean addDeviceStatus(EnrolmentInfo config) throws DeviceManagementDAOException { + return addDeviceStatus(config.getId(), config.getStatus()); + } + + private boolean addDeviceStatus(String currentOwner, EnrolmentInfo.Status status, int tenantId) throws DeviceManagementDAOException { + Connection conn; + String changedBy = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername(); + if (changedBy == null){ + changedBy = DeviceManagementConstants.MaintenanceProperties.MAINTENANCE_USER; + } + PreparedStatement stmt = null; + ResultSet rs = null; + List enrolmentInfoList = new ArrayList<>(); + try { + conn = this.getConnection(); + String sql = "SELECT ID, DEVICE_ID, OWNER, OWNERSHIP, STATUS, IS_TRANSFERRED, DATE_OF_ENROLMENT, " + + "DATE_OF_LAST_UPDATE, TENANT_ID FROM DM_ENROLMENT WHERE OWNER = ? AND TENANT_ID = ?"; + stmt = conn.prepareStatement(sql); + stmt.setString(1, currentOwner); + stmt.setInt(2, tenantId); + rs = stmt.executeQuery(); + while (rs.next()) { + int enrolmentId = rs.getInt("ID"); + int deviceId = rs.getInt("DEVICE_ID"); + enrolmentInfoList.add(new int[]{enrolmentId, deviceId}); + } + DeviceManagementDAOUtil.cleanupResources(stmt, rs); + Timestamp updateTime = new Timestamp(new Date().getTime()); + sql = "INSERT INTO DM_DEVICE_STATUS (ENROLMENT_ID, DEVICE_ID, STATUS, UPDATE_TIME, CHANGED_BY) VALUES(?, ?, ?, ?, ?)"; + try (PreparedStatement ps = conn.prepareStatement(sql)) { + if (conn.getMetaData().supportsBatchUpdates()) { + for(int[] info: enrolmentInfoList){ + ps.setInt(1, info[0]); + ps.setInt(2, info[1]); + ps.setString(3, status.toString()); + ps.setTimestamp(4, updateTime); + ps.setString(5, changedBy); + ps.addBatch(); + } + int[] batchResult = ps.executeBatch(); + for (int i : batchResult) { + if (i == 0 || i == Statement.SUCCESS_NO_INFO || i == Statement.EXECUTE_FAILED) { + return false; + } + } + } else { + for(int[] info: enrolmentInfoList){ + ps.setInt(1, info[0]); + ps.setInt(2, info[1]); + ps.setString(3, status.toString()); + ps.setTimestamp(4, updateTime); + ps.setString(5, changedBy); + ps.execute(); + } + + } + } + + } catch (SQLException e) { + throw new DeviceManagementDAOException("Error occurred while retrieving the enrolments " + + "information of owner '" + currentOwner + "'", e); + } finally { + DeviceManagementDAOUtil.cleanupResources(stmt, rs); + } + return true; + } + + private boolean addDeviceStatus(int enrolmentId, EnrolmentInfo.Status status) throws DeviceManagementDAOException { + Connection conn; + String changedBy = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername(); + if (changedBy == null){ + changedBy = DeviceManagementConstants.MaintenanceProperties.MAINTENANCE_USER; + } + PreparedStatement stmt = null; + try { + conn = this.getConnection(); + // get the device id and last udpated status from the device status table + String sql = "SELECT DEVICE_ID, STATUS FROM DM_DEVICE_STATUS WHERE ENROLMENT_ID = ? ORDER BY UPDATE_TIME DESC LIMIT 1"; + stmt = conn.prepareStatement(sql); + stmt.setInt(1, enrolmentId); + ResultSet rs = stmt.executeQuery(); + int deviceId = -1; + EnrolmentInfo.Status previousStatus = null; + if (rs.next()) { + // if there is a record corresponding to the enrolment we save the status and the device id + previousStatus = EnrolmentInfo.Status.valueOf(rs.getString("STATUS")); + deviceId = rs.getInt("DEVICE_ID"); + } + DeviceManagementDAOUtil.cleanupResources(stmt, null); + // if there was no record for the enrolment or the previous status is not the same as the current status + // we'll add a record + if (previousStatus == null || previousStatus != status){ + if (deviceId == -1) { + // we need the device id in order to add a new record, therefore we get it from the enrolment table + sql = "SELECT DEVICE_ID FROM DM_ENROLMENT WHERE ID = ?"; + stmt = conn.prepareStatement(sql); + stmt.setInt(1, enrolmentId); + rs = stmt.executeQuery(); + if (rs.next()) { + deviceId = rs.getInt("DEVICE_ID"); + } else { + // if there were no records corresponding to the enrolment id this is a problem. i.e. enrolment + // id is invalid + throw new DeviceManagementDAOException("Error occurred while setting the status of device enrolment: no record for enrolment id " + enrolmentId); + } + DeviceManagementDAOUtil.cleanupResources(stmt, null); + } + + sql = "INSERT INTO DM_DEVICE_STATUS (ENROLMENT_ID, DEVICE_ID, STATUS, UPDATE_TIME, CHANGED_BY) VALUES(?, ?, ?, ?, ?)"; + stmt = conn.prepareStatement(sql); + Timestamp updateTime = new Timestamp(new Date().getTime()); + stmt.setInt(1, enrolmentId); + stmt.setInt(2, deviceId); + stmt.setString(3, status.toString()); + stmt.setTimestamp(4, updateTime); + stmt.setString(5, changedBy); + stmt.execute(); + } else { + // no need to update status since the last recorded status is the same as the current status + } + } catch (SQLException e) { + throw new DeviceManagementDAOException("Error occurred while setting the status of device", e); + } finally { + DeviceManagementDAOUtil.cleanupResources(stmt, null); + } + return true; + } @Override public EnrolmentInfo.Status getStatus(int deviceId, String currentOwner, int tenantId) throws DeviceManagementDAOException { @@ -323,7 +477,7 @@ public class EnrollmentDAOImpl implements EnrollmentDAO { try { conn = this.getConnection(); String sql = "SELECT ID, DEVICE_ID, OWNER, OWNERSHIP, STATUS, IS_TRANSFERRED, DATE_OF_ENROLMENT, " + - "DATE_OF_LAST_UPDATE, TENANT_ID FROM DM_ENROLMENT WHERE DEVICE_ID = ? AND OWNER = ? AND TENANT_ID = ?"; + "DATE_OF_LAST_UPDATE, TENANT_ID FROM DM_ENROLMENT WHERE DEVICE_ID = ? AND OWNER = ? AND TENANT_ID = ?"; stmt = conn.prepareStatement(sql); stmt.setInt(1, deviceId); stmt.setString(2, user); @@ -336,7 +490,7 @@ public class EnrollmentDAOImpl implements EnrollmentDAO { return enrolmentInfos; } catch (SQLException e) { throw new DeviceManagementDAOException("Error occurred while retrieving the enrolments " + - "information of user '" + user + "' upon device '" + deviceId + "'", e); + "information of user '" + user + "' upon device '" + deviceId + "'", e); } finally { DeviceManagementDAOUtil.cleanupResources(stmt, rs); } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/GeofenceDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/GeofenceDAOImpl.java index e0d1efa4d1b..e845a43da92 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/GeofenceDAOImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/GeofenceDAOImpl.java @@ -26,6 +26,7 @@ import org.wso2.carbon.device.mgt.common.event.config.EventConfig; import org.wso2.carbon.device.mgt.common.geo.service.GeofenceData; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory; +import org.wso2.carbon.device.mgt.core.dao.EventManagementDAOFactory; import org.wso2.carbon.device.mgt.core.dao.GeofenceDAO; import org.wso2.carbon.device.mgt.core.dto.event.config.GeoFenceGroupMap; @@ -308,7 +309,7 @@ public class GeofenceDAOImpl implements GeofenceDAO { } private Connection getConnection() throws SQLException { - return DeviceManagementDAOFactory.getConnection(); + return EventManagementDAOFactory.getConnection(); } private List extractGeofenceData(ResultSet rst) throws SQLException { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/GenericDeviceDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/GenericDeviceDAOImpl.java index eb3fa87f548..aff4465a04d 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/GenericDeviceDAOImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/GenericDeviceDAOImpl.java @@ -24,6 +24,7 @@ import org.apache.commons.logging.LogFactory; import org.wso2.carbon.device.mgt.common.Count; import org.wso2.carbon.device.mgt.common.Device; import org.wso2.carbon.device.mgt.common.DeviceManagementConstants; +import org.wso2.carbon.device.mgt.common.EnrolmentInfo; import org.wso2.carbon.device.mgt.common.PaginationRequest; import org.wso2.carbon.device.mgt.common.device.details.DeviceInfo; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException; @@ -901,6 +902,7 @@ public class GenericDeviceDAOImpl extends AbstractDeviceDAOImpl { String name = request.getDeviceName(); String user = request.getOwner(); String ownership = request.getOwnership(); + String query = null; try { List devices = new ArrayList<>(); if (deviceIds.isEmpty()) { @@ -936,10 +938,10 @@ public class GenericDeviceDAOImpl extends AbstractDeviceDAOImpl { + "INNER JOIN (SELECT ID, NAME FROM DM_DEVICE_TYPE) AS device_types ON " + "device_types.ID = DM_DEVICE.DEVICE_TYPE_ID " + "WHERE DM_DEVICE.ID IN (", - ") AND DM_DEVICE.TENANT_ID = ?"); + ") AND DM_DEVICE.TENANT_ID = ? AND e.STATUS != ?"); deviceIds.stream().map(ignored -> "?").forEach(joiner::add); - String query = joiner.toString(); + query = joiner.toString(); if (name != null && !name.isEmpty()) { query += " AND DM_DEVICE.NAME LIKE ?"; @@ -966,6 +968,7 @@ public class GenericDeviceDAOImpl extends AbstractDeviceDAOImpl { ps.setObject(index++, deviceId); } ps.setInt(index++, tenantId); + ps.setString(index++, EnrolmentInfo.Status.REMOVED.toString()); if (isDeviceNameProvided) { ps.setString(index++, name + "%"); } @@ -992,7 +995,7 @@ public class GenericDeviceDAOImpl extends AbstractDeviceDAOImpl { } } catch (SQLException e) { String msg = "Error occurred while retrieving information of all registered devices " + - "according to device ids and the limit area."; + "according to device ids and the limit area. Executed query " + query; log.error(msg, e); throw new DeviceManagementDAOException(msg, e); } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/event/GenericEventConfigDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/event/GenericEventConfigDAOImpl.java index ecd5a3dc59a..deea9aa84a7 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/event/GenericEventConfigDAOImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/event/GenericEventConfigDAOImpl.java @@ -21,8 +21,8 @@ package org.wso2.carbon.device.mgt.core.dao.impl.event; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wso2.carbon.device.mgt.common.event.config.EventConfig; -import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory; import org.wso2.carbon.device.mgt.core.dao.EventManagementDAOException; +import org.wso2.carbon.device.mgt.core.dao.EventManagementDAOFactory; import org.wso2.carbon.device.mgt.core.dao.impl.AbstractEventConfigDAO; import java.sql.Connection; @@ -76,6 +76,6 @@ public class GenericEventConfigDAOImpl extends AbstractEventConfigDAO { } private Connection getConnection() throws SQLException { - return DeviceManagementDAOFactory.getConnection(); + return EventManagementDAOFactory.getConnection(); } } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/event/H2EventConfigDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/event/H2EventConfigDAOImpl.java index fef946649f5..ab59d5fc83b 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/event/H2EventConfigDAOImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/event/H2EventConfigDAOImpl.java @@ -23,6 +23,7 @@ import org.apache.commons.logging.LogFactory; import org.wso2.carbon.device.mgt.common.event.config.EventConfig; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory; import org.wso2.carbon.device.mgt.core.dao.EventManagementDAOException; +import org.wso2.carbon.device.mgt.core.dao.EventManagementDAOFactory; import org.wso2.carbon.device.mgt.core.dao.impl.AbstractEventConfigDAO; import java.sql.Connection; @@ -75,6 +76,6 @@ public class H2EventConfigDAOImpl extends AbstractEventConfigDAO { } private Connection getConnection() throws SQLException { - return DeviceManagementDAOFactory.getConnection(); + return EventManagementDAOFactory.getConnection(); } } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/device/details/mgt/dao/impl/DeviceDetailsDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/device/details/mgt/dao/impl/DeviceDetailsDAOImpl.java index 6db8bd2fdd8..ff17b026f4f 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/device/details/mgt/dao/impl/DeviceDetailsDAOImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/device/details/mgt/dao/impl/DeviceDetailsDAOImpl.java @@ -74,7 +74,7 @@ public class DeviceDetailsDAOImpl implements DeviceDetailsDAO { stmt.setDouble(14, deviceInfo.getTotalRAMMemory()); stmt.setDouble(15, deviceInfo.getAvailableRAMMemory()); stmt.setBoolean(16, deviceInfo.isPluggedIn()); - stmt.setLong(17, DeviceManagementDAOUtil.getCurrentUTCTime()); + stmt.setLong(17, DeviceManagementDAOUtil.getCurrentUTCTime() * 1000L); stmt.setInt(18, enrolmentId); stmt.execute(); @@ -508,7 +508,7 @@ public class DeviceDetailsDAOImpl implements DeviceDetailsDAO { stmt.setDouble(13, newDeviceInfo.getTotalRAMMemory()); stmt.setDouble(14, newDeviceInfo.getAvailableRAMMemory()); stmt.setBoolean(15, newDeviceInfo.isPluggedIn()); - stmt.setLong(16, System.currentTimeMillis()); + stmt.setLong(16, DeviceManagementDAOUtil.getCurrentUTCTime() * 1000L); stmt.setInt(17, deviceId); stmt.setInt(18, enrollmentId); diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dto/operation/mgt/Operation.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dto/operation/mgt/Operation.java index 03acfa9a11b..74bf820ca71 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dto/operation/mgt/Operation.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dto/operation/mgt/Operation.java @@ -30,7 +30,7 @@ public class Operation implements Serializable { } public enum Status { - IN_PROGRESS, PENDING, COMPLETED, ERROR, REPEATED, NOTNOW + IN_PROGRESS, PENDING, COMPLETED, ERROR, REPEATED, NOTNOW, REQUIRED_CONFIRMATION, CONFIRMED } public enum Control { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/event/config/EventConfigurationProviderServiceImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/event/config/EventConfigurationProviderServiceImpl.java index b2fe4ede22a..7a6a362e411 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/event/config/EventConfigurationProviderServiceImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/event/config/EventConfigurationProviderServiceImpl.java @@ -27,10 +27,7 @@ import org.wso2.carbon.device.mgt.common.event.config.EventConfigurationProvider import org.wso2.carbon.device.mgt.common.event.config.EventMetaData; import org.wso2.carbon.device.mgt.common.exceptions.TransactionManagementException; import org.wso2.carbon.device.mgt.core.config.DeviceConfigurationManager; -import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException; -import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory; -import org.wso2.carbon.device.mgt.core.dao.EventConfigDAO; -import org.wso2.carbon.device.mgt.core.dao.EventManagementDAOException; +import org.wso2.carbon.device.mgt.core.dao.*; import org.wso2.carbon.device.mgt.core.dao.util.DeviceManagementDAOUtil; import org.wso2.carbon.device.mgt.core.geo.task.GeoFenceEventOperationManager; import org.wso2.carbon.device.mgt.core.internal.DeviceManagementDataHolder; @@ -49,7 +46,7 @@ public class EventConfigurationProviderServiceImpl implements EventConfiguration private final EventConfigDAO eventConfigDAO; public EventConfigurationProviderServiceImpl() { - eventConfigDAO = DeviceManagementDAOFactory.getEventConfigDAO(); + eventConfigDAO = EventManagementDAOFactory.getEventConfigDAO(); } @Override @@ -65,7 +62,7 @@ public class EventConfigurationProviderServiceImpl implements EventConfiguration } try { - DeviceManagementDAOFactory.beginTransaction(); + EventManagementDAOFactory.beginTransaction(); if (log.isDebugEnabled()) { log.debug("Creating event records of tenant " + tenantId); } @@ -75,7 +72,7 @@ public class EventConfigurationProviderServiceImpl implements EventConfiguration log.debug("Creating event group mapping for created events with group ids : " + groupIds.toString()); } eventConfigDAO.addEventGroupMappingRecords(generatedEventIds, groupIds); - DeviceManagementDAOFactory.commitTransaction(); + EventManagementDAOFactory.commitTransaction(); if (log.isDebugEnabled()) { log.debug("Event configuration added successfully for the tenant " + tenantId); } @@ -86,10 +83,10 @@ public class EventConfigurationProviderServiceImpl implements EventConfiguration } catch (EventManagementDAOException e) { String msg = "Error occurred while saving event records"; log.error(msg, e); - DeviceManagementDAOFactory.rollbackTransaction(); + EventManagementDAOFactory.rollbackTransaction(); throw new EventConfigurationException(msg, e); } finally { - DeviceManagementDAOFactory.closeConnection(); + EventManagementDAOFactory.closeConnection(); } } @@ -103,7 +100,7 @@ public class EventConfigurationProviderServiceImpl implements EventConfiguration } List eventsToAdd; try { - DeviceManagementDAOFactory.beginTransaction(); + EventManagementDAOFactory.beginTransaction(); eventsToAdd = new ArrayList<>(); List eventsToUpdate = new ArrayList<>(); List updateEventIdList = new ArrayList<>(); @@ -162,7 +159,7 @@ public class EventConfigurationProviderServiceImpl implements EventConfiguration } eventConfigDAO.deleteEventRecords(removedEventIdList); } - DeviceManagementDAOFactory.commitTransaction(); + EventManagementDAOFactory.commitTransaction(); } catch (TransactionManagementException e) { String msg = "Failed to start/open transaction to store device event configurations"; log.error(msg, e); @@ -170,10 +167,10 @@ public class EventConfigurationProviderServiceImpl implements EventConfiguration } catch (EventManagementDAOException e) { String msg = "Error occurred while saving event records"; log.error(msg, e); - DeviceManagementDAOFactory.rollbackTransaction(); + EventManagementDAOFactory.rollbackTransaction(); throw new EventConfigurationException(msg, e); } finally { - DeviceManagementDAOFactory.closeConnection(); + EventManagementDAOFactory.closeConnection(); } if (log.isDebugEnabled()) { @@ -185,7 +182,7 @@ public class EventConfigurationProviderServiceImpl implements EventConfiguration @Override public List getEvents(List createdEventIds) throws EventConfigurationException { try { - DeviceManagementDAOFactory.openConnection(); + EventManagementDAOFactory.openConnection(); return eventConfigDAO.getEventsById(createdEventIds); } catch (EventManagementDAOException e) { String msg = "Error occurred while retrieving event by IDs : " + Arrays.toString(createdEventIds.toArray()); @@ -196,14 +193,14 @@ public class EventConfigurationProviderServiceImpl implements EventConfiguration log.error(msg, e); throw new EventConfigurationException(msg, e); } finally { - DeviceManagementDAOFactory.closeConnection(); + EventManagementDAOFactory.closeConnection(); } } @Override public List getEventsSourcesOfGroup(int groupId, int tenantId) throws EventConfigurationException { try { - DeviceManagementDAOFactory.openConnection(); + EventManagementDAOFactory.openConnection(); return eventConfigDAO.getEventSourcesOfGroups(groupId, tenantId); } catch (EventManagementDAOException e) { String msg = "Error occurred while retrieving events of group " + groupId + " and tenant " + tenantId; @@ -214,14 +211,14 @@ public class EventConfigurationProviderServiceImpl implements EventConfiguration log.error(msg, e); throw new EventConfigurationException(msg, e); } finally { - DeviceManagementDAOFactory.closeConnection(); + EventManagementDAOFactory.closeConnection(); } } @Override public void deleteEvents(List events) throws EventConfigurationException { try { - DeviceManagementDAOFactory.beginTransaction(); + EventManagementDAOFactory.beginTransaction(); Set eventIdSet = new HashSet<>(); for (EventConfig eventConfig : events) { eventIdSet.add(eventConfig.getEventId()); @@ -230,13 +227,13 @@ public class EventConfigurationProviderServiceImpl implements EventConfiguration eventConfigDAO.deleteEventGroupMappingRecordsByEventIds(Lists.newArrayList(eventIdSet)); eventConfigDAO.deleteEventRecords(Lists.newArrayList(eventIdSet)); } - DeviceManagementDAOFactory.commitTransaction(); + EventManagementDAOFactory.commitTransaction(); } catch (TransactionManagementException e) { String msg = "Failed to start/open transaction to delete device event configurations"; log.error(msg, e); throw new EventConfigurationException(msg, e); } catch (EventManagementDAOException e) { - DeviceManagementDAOFactory.rollbackTransaction(); + EventManagementDAOFactory.rollbackTransaction(); String msg = "Error occurred while deleting event records"; log.error(msg, e); throw new EventConfigurationException(msg, e); diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/event/config/EventOperationExecutor.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/event/config/EventOperationExecutor.java index 937706c219d..9c2a043923b 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/event/config/EventOperationExecutor.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/event/config/EventOperationExecutor.java @@ -132,7 +132,10 @@ public class EventOperationExecutor implements Runnable { EventRevokeOperation eventRevokeOperation = new EventRevokeOperation(); eventRevokeOperation.setEventSource(eventSource); eventRevokeOperation.setId(geoFenceMeta.getId()); - operation.setPayLoad(new Gson().toJson(eventRevokeOperation)); + + List eventOperationsRevoke = new ArrayList<>(); + eventOperationsRevoke.add(eventRevokeOperation); + operation.setPayLoad(new Gson().toJson(eventOperationsRevoke)); } @Override diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/geo/service/GeoLocationProviderServiceImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/geo/service/GeoLocationProviderServiceImpl.java index fad3138d446..5f2c6c79164 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/geo/service/GeoLocationProviderServiceImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/geo/service/GeoLocationProviderServiceImpl.java @@ -53,6 +53,7 @@ import org.wso2.carbon.device.mgt.common.geo.service.GeofenceData; import org.wso2.carbon.device.mgt.core.cache.impl.GeoCacheManagerImpl; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory; +import org.wso2.carbon.device.mgt.core.dao.EventManagementDAOFactory; import org.wso2.carbon.device.mgt.core.dao.GeofenceDAO; import org.wso2.carbon.device.mgt.core.dao.util.DeviceManagementDAOUtil; import org.wso2.carbon.device.mgt.core.dto.event.config.GeoFenceGroupMap; @@ -133,7 +134,7 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic private final GeofenceDAO geofenceDAO; public GeoLocationProviderServiceImpl() { - this.geofenceDAO = DeviceManagementDAOFactory.getGeofenceDAO(); + this.geofenceDAO = EventManagementDAOFactory.getGeofenceDAO(); } public static JWTClientManagerService getJWTClientManagerService() { @@ -1269,32 +1270,32 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic } try { - DeviceManagementDAOFactory.beginTransaction(); + EventManagementDAOFactory.beginTransaction(); geofenceData = geofenceDAO.saveGeofence(geofenceData); GeoCacheManagerImpl.getInstance() .addFenceToCache(geofenceData, geofenceData.getId(), tenantId); geofenceDAO.createGeofenceGroupMapping(geofenceData, geofenceData.getGroupIds()); - DeviceManagementDAOFactory.commitTransaction(); + EventManagementDAOFactory.commitTransaction(); } catch (TransactionManagementException e) { String msg = "Failed to begin transaction for saving geofence"; log.error(msg, e); throw new GeoLocationBasedServiceException(msg, e); } catch (DeviceManagementDAOException e) { - DeviceManagementDAOFactory.rollbackTransaction(); + EventManagementDAOFactory.rollbackTransaction(); String msg = "Error occurred while saving geofence"; log.error(msg, e); throw new GeoLocationBasedServiceException(msg, e); } finally { - DeviceManagementDAOFactory.closeConnection(); + EventManagementDAOFactory.closeConnection(); } List createdEventIds; try { setEventSource(geofenceData.getEventConfig()); eventConfigService = DeviceManagementDataHolder.getInstance().getEventConfigurationService(); createdEventIds = eventConfigService.createEventsOfDeviceGroup(geofenceData.getEventConfig(), geofenceData.getGroupIds()); - DeviceManagementDAOFactory.beginTransaction(); + EventManagementDAOFactory.beginTransaction(); geofenceDAO.createGeofenceEventMapping(geofenceData.getId(), createdEventIds); - DeviceManagementDAOFactory.commitTransaction(); + EventManagementDAOFactory.commitTransaction(); } catch (EventConfigurationException e) { String msg = "Failed to store Geofence event configurations"; log.error(msg, e); @@ -1309,12 +1310,12 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic log.error(msg, e); throw new GeoLocationBasedServiceException(msg, e); } catch (DeviceManagementDAOException e) { - DeviceManagementDAOFactory.rollbackTransaction(); + EventManagementDAOFactory.rollbackTransaction(); String msg = "Error occurred while creating geofence event mapping records"; log.error(msg, e); throw new GeoLocationBasedServiceException(msg, e); } finally { - DeviceManagementDAOFactory.closeConnection(); + EventManagementDAOFactory.closeConnection(); } try { @@ -1358,7 +1359,7 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic } try { - DeviceManagementDAOFactory.openConnection(); + EventManagementDAOFactory.openConnection(); GeofenceData geofence = geofenceDAO.getGeofence(fenceId, true); if (geofence != null) { GeoCacheManagerImpl.getInstance().addFenceToCache(geofence, fenceId, tenantId); @@ -1373,7 +1374,7 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic log.error(msg, e); throw new GeoLocationBasedServiceException(msg, e); } finally { - DeviceManagementDAOFactory.closeConnection(); + EventManagementDAOFactory.closeConnection(); } } @@ -1392,7 +1393,7 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic if (log.isDebugEnabled()) { log.debug("Retrieving geofence data for the tenant " + tenantId); } - DeviceManagementDAOFactory.openConnection(); + EventManagementDAOFactory.openConnection(); return geofenceDAO.getGeoFencesOfTenant(request, tenantId); } catch (DeviceManagementDAOException e) { String msg = "Error occurred while retrieving geofence data for the tenant " + tenantId; @@ -1403,7 +1404,7 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic log.error(msg, e); throw new GeoLocationBasedServiceException(msg, e); } finally { - DeviceManagementDAOFactory.closeConnection(); + EventManagementDAOFactory.closeConnection(); } } @@ -1422,7 +1423,7 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic if (log.isDebugEnabled()) { log.debug("Retrieving geofence data for the tenant " + tenantId); } - DeviceManagementDAOFactory.openConnection(); + EventManagementDAOFactory.openConnection(); return geofenceDAO.getGeoFencesOfTenant(fenceName, tenantId); } catch (DeviceManagementDAOException e) { String msg = "Error occurred while retrieving geofence data for the tenant " + tenantId; @@ -1433,7 +1434,7 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic log.error(msg, e); throw new GeoLocationBasedServiceException(msg, e); } finally { - DeviceManagementDAOFactory.closeConnection(); + EventManagementDAOFactory.closeConnection(); } } @@ -1452,7 +1453,7 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic if (log.isDebugEnabled()) { log.debug("Retrieving all fence data for the tenant " + tenantId); } - DeviceManagementDAOFactory.openConnection(); + EventManagementDAOFactory.openConnection(); List geoFencesOfTenant = geofenceDAO.getGeoFencesOfTenant(tenantId); for (GeofenceData geofenceData : geoFencesOfTenant) { GeoCacheManagerImpl.getInstance() @@ -1469,7 +1470,7 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic log.error(msg, e); throw new GeoLocationBasedServiceException(msg, e); } finally { - DeviceManagementDAOFactory.closeConnection(); + EventManagementDAOFactory.closeConnection(); } } @@ -1488,7 +1489,7 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic } try { - DeviceManagementDAOFactory.beginTransaction(); + EventManagementDAOFactory.beginTransaction(); geofence = geofenceDAO.getGeofence(fenceId, true); if (geofence == null) { return false; @@ -1506,10 +1507,10 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic geofenceDAO.deleteGeofenceGroupMapping(groupIdsOfGeoFence, fenceId); } geofenceDAO.deleteGeofenceById(fenceId); - DeviceManagementDAOFactory.commitTransaction(); + EventManagementDAOFactory.commitTransaction(); GeoCacheManagerImpl.getInstance().removeFenceFromCache(fenceId, tenantId); } catch (DeviceManagementDAOException e) { - DeviceManagementDAOFactory.rollbackTransaction(); + EventManagementDAOFactory.rollbackTransaction(); String msg = "Error occurred while deleting geofence"; log.error(msg, e); throw new GeoLocationBasedServiceException(msg, e); @@ -1518,7 +1519,7 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic log.error(msg, e); throw new GeoLocationBasedServiceException(msg, e); } finally { - DeviceManagementDAOFactory.closeConnection(); + EventManagementDAOFactory.closeConnection(); } this.deleteGeoFenceEvents(geofence, eventsOfGeoFence); return true; @@ -1538,7 +1539,7 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic List savedGroupIds; try { - DeviceManagementDAOFactory.beginTransaction(); + EventManagementDAOFactory.beginTransaction(); int updatedRowCount = geofenceDAO.updateGeofence(geofenceData, fenceId); savedGroupIds = geofenceDAO.getGroupIdsOfGeoFence(fenceId); geofenceData.setId(fenceId); @@ -1556,7 +1557,7 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic } geofenceDAO.deleteGeofenceGroupMapping(groupIdsToDelete, fenceId); geofenceDAO.createGeofenceGroupMapping(geofenceData, groupIdsToAdd); - DeviceManagementDAOFactory.commitTransaction(); + EventManagementDAOFactory.commitTransaction(); if (updatedRowCount > 0) { GeoCacheManagerImpl.getInstance().updateGeoFenceInCache(geofenceData, fenceId, tenantId); } @@ -1570,7 +1571,7 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic log.error(msg, e); throw new GeoLocationBasedServiceException(msg, e); } finally { - DeviceManagementDAOFactory.closeConnection(); + EventManagementDAOFactory.closeConnection(); } return true; } @@ -1587,11 +1588,11 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic if (log.isDebugEnabled()) { log.debug("Deleting geofence event mapping records of geofence " + fenceId); } - DeviceManagementDAOFactory.beginTransaction(); + EventManagementDAOFactory.beginTransaction(); geofenceDAO.deleteGeofenceEventMapping(removedEventIdList); - DeviceManagementDAOFactory.commitTransaction(); + EventManagementDAOFactory.commitTransaction(); } catch (DeviceManagementDAOException e) { - DeviceManagementDAOFactory.rollbackTransaction(); + EventManagementDAOFactory.rollbackTransaction(); String msg = "Error occurred while deleting geofence event mapping of fence " + fenceId; log.error(msg, e); throw new GeoLocationBasedServiceException(msg, e); @@ -1600,7 +1601,7 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic log.error(msg, e); throw new GeoLocationBasedServiceException(msg, e); } finally { - DeviceManagementDAOFactory.closeConnection(); + EventManagementDAOFactory.closeConnection(); } List createdEventIds; @@ -1630,11 +1631,11 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic + fenceId + ". created events " + createdEventIds.toString()); } try { - DeviceManagementDAOFactory.beginTransaction(); + EventManagementDAOFactory.beginTransaction(); geofenceDAO.createGeofenceEventMapping(fenceId, createdEventIds); - DeviceManagementDAOFactory.commitTransaction(); + EventManagementDAOFactory.commitTransaction(); } catch (DeviceManagementDAOException e) { - DeviceManagementDAOFactory.rollbackTransaction(); + EventManagementDAOFactory.rollbackTransaction(); String msg = "Error occurred while creating geofence event mapping records of geofence " + fenceId; log.error(msg, e); throw new GeoLocationBasedServiceException(msg, e); @@ -1643,7 +1644,7 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic log.error(msg, e); throw new GeoLocationBasedServiceException(msg, e); } finally { - DeviceManagementDAOFactory.closeConnection(); + EventManagementDAOFactory.closeConnection(); } if (log.isDebugEnabled()) { log.debug("Update geofence event completed."); @@ -1664,7 +1665,7 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic @Override public List attachEventObjects(List geoFences) throws GeoLocationBasedServiceException { try { - DeviceManagementDAOFactory.openConnection(); + EventManagementDAOFactory.openConnection(); List fenceIds = new ArrayList<>(); for (GeofenceData geoFence : geoFences) { fenceIds.add(geoFence.getId()); @@ -1696,14 +1697,14 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic log.error(msg, e); throw new GeoLocationBasedServiceException(msg, e); } finally { - DeviceManagementDAOFactory.closeConnection(); + EventManagementDAOFactory.closeConnection(); } } @Override public List getGeoFencesOfGroup(int groupId, int tenantId, boolean requireEventData) throws GeoLocationBasedServiceException { try { - DeviceManagementDAOFactory.openConnection(); + EventManagementDAOFactory.openConnection(); List geofenceDataList = geofenceDAO.getGeoFences(groupId, tenantId); if (requireEventData) { for (GeofenceData geoFenceData : geofenceDataList) { @@ -1711,7 +1712,6 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic geoFenceData.setEventConfig(eventsOfGeoFence); } } - DeviceManagementDAOFactory.closeConnection(); return geofenceDataList; } catch (DeviceManagementDAOException e) { String msg = "Error occurred while retrieving geo fences of group " + groupId @@ -1723,13 +1723,15 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic + groupId + " and tenant " + tenantId; log.error(msg, e); throw new GeoLocationBasedServiceException(msg, e); + } finally { + EventManagementDAOFactory.closeConnection(); } } @Override public List getEventsOfGeoFence(int geoFenceId) throws GeoLocationBasedServiceException { try { - DeviceManagementDAOFactory.openConnection(); + EventManagementDAOFactory.openConnection(); return geofenceDAO.getEventsOfGeoFence(geoFenceId); } catch (SQLException e) { String msg = "Failed to obtain connection while retrieving event data of geo fence " @@ -1741,7 +1743,7 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic log.error(msg, e); throw new GeoLocationBasedServiceException(msg, e); } finally { - DeviceManagementDAOFactory.closeConnection(); + EventManagementDAOFactory.closeConnection(); } } @@ -1770,12 +1772,9 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic DeviceManagementConstants.EventServices.GEOFENCE, new GeoFenceEventMeta(geofenceData), tenantId, geofenceData.getGroupIds()); } catch (EventConfigurationException e) { - DeviceManagementDAOFactory.rollbackTransaction(); String msg = "Failed to delete Geofence event configurations"; log.error(msg, e); throw new GeoLocationBasedServiceException(msg, e); - } finally { - DeviceManagementDAOFactory.closeConnection(); } } } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceManagementDataHolder.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceManagementDataHolder.java index 818df70b490..5cb1a1c9ab6 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceManagementDataHolder.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceManagementDataHolder.java @@ -34,6 +34,7 @@ import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceInformationManag import org.wso2.carbon.device.mgt.core.dto.DeviceType; import org.wso2.carbon.device.mgt.core.dto.DeviceTypeServiceIdentifier; import org.wso2.carbon.device.mgt.core.geo.task.GeoFenceEventOperationManager; +import org.wso2.carbon.device.mgt.core.operation.timeout.task.OperationTimeoutTaskManagerService; import org.wso2.carbon.device.mgt.core.privacy.PrivacyComplianceProvider; import org.wso2.carbon.device.mgt.core.push.notification.mgt.PushNotificationProviderRepository; import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService; @@ -81,6 +82,7 @@ public class DeviceManagementDataHolder { private GeoLocationProviderService geoLocationProviderService; private GeoFenceEventOperationManager geoFenceEventOperationManager; private ExecutorService eventConfigExecutors; + private OperationTimeoutTaskManagerService operationTimeoutTaskManagerService; private final Map deviceStatusTaskPluginConfigs = Collections.synchronizedMap( new HashMap<>()); @@ -338,4 +340,13 @@ public class DeviceManagementDataHolder { public void setEventConfigExecutors(ExecutorService eventConfigExecutors) { this.eventConfigExecutors = eventConfigExecutors; } + + public OperationTimeoutTaskManagerService getOperationTimeoutTaskManagerService() { + return operationTimeoutTaskManagerService; + } + + public void setOperationTimeoutTaskManagerService( + OperationTimeoutTaskManagerService operationTimeoutTaskManagerService) { + this.operationTimeoutTaskManagerService = operationTimeoutTaskManagerService; + } } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceManagementServiceComponent.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceManagementServiceComponent.java index 859659bdf5f..4c031163aa7 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceManagementServiceComponent.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceManagementServiceComponent.java @@ -51,6 +51,7 @@ import org.wso2.carbon.device.mgt.core.config.datasource.DataSourceConfig; import org.wso2.carbon.device.mgt.core.config.tenant.PlatformConfigurationManagementServiceImpl; import org.wso2.carbon.device.mgt.core.config.ui.UIConfigurationManager; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory; +import org.wso2.carbon.device.mgt.core.dao.EventManagementDAOFactory; import org.wso2.carbon.device.mgt.core.dao.GroupManagementDAOFactory; import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceInformationManager; import org.wso2.carbon.device.mgt.core.device.details.mgt.impl.DeviceInformationManagerImpl; @@ -192,6 +193,7 @@ public class DeviceManagementServiceComponent { NotificationManagementDAOFactory.init(dsConfig); OperationManagementDAOFactory.init(dsConfig); MetadataManagementDAOFactory.init(dsConfig); + EventManagementDAOFactory.init(dsConfig); OTPManagementDAOFactory.init(dsConfig.getJndiLookupDefinition().getJndiName()); /*Initialize the device cache*/ DeviceManagerUtil.initializeDeviceCache(); diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceTaskManagerServiceComponent.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceTaskManagerServiceComponent.java index 489a29c72c2..a7cba70a35a 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceTaskManagerServiceComponent.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceTaskManagerServiceComponent.java @@ -26,7 +26,12 @@ import org.wso2.carbon.device.mgt.common.DeviceStatusTaskPluginConfig; import org.wso2.carbon.device.mgt.common.OperationMonitoringTaskConfig; import org.wso2.carbon.device.mgt.core.config.DeviceConfigurationManager; import org.wso2.carbon.device.mgt.core.config.DeviceManagementConfig; +import org.wso2.carbon.device.mgt.core.config.operation.timeout.OperationTimeout; +import org.wso2.carbon.device.mgt.core.config.operation.timeout.OperationTimeoutConfiguration; import org.wso2.carbon.device.mgt.core.dto.DeviceType; +import org.wso2.carbon.device.mgt.core.operation.timeout.task.OperationTimeoutTaskException; +import org.wso2.carbon.device.mgt.core.operation.timeout.task.OperationTimeoutTaskManagerService; +import org.wso2.carbon.device.mgt.core.operation.timeout.task.impl.OperationTimeoutTaskManagerServiceImpl; import org.wso2.carbon.device.mgt.core.status.task.DeviceStatusTaskException; import org.wso2.carbon.device.mgt.core.status.task.DeviceStatusTaskManagerService; import org.wso2.carbon.device.mgt.core.status.task.impl.DeviceStatusTaskManagerServiceImpl; @@ -66,6 +71,10 @@ public class DeviceTaskManagerServiceComponent { if (deviceManagementConfig != null && deviceManagementConfig.getDeviceStatusTaskConfig().isEnabled()) { startDeviceStatusMonitoringTask(componentContext.getBundleContext()); } + + if (deviceManagementConfig != null && deviceManagementConfig.getOperationTimeoutConfiguration() != null) { + startOperationTimeoutTask(componentContext.getBundleContext()); + } } catch (Throwable e) { log.error("Error occurred while initializing device task manager service.", e); } @@ -102,6 +111,27 @@ public class DeviceTaskManagerServiceComponent { } } + private void startOperationTimeoutTask(BundleContext bundleContext) { + OperationTimeoutTaskManagerService operationTimeoutTaskManagerService = + new OperationTimeoutTaskManagerServiceImpl(); + DeviceManagementDataHolder.getInstance().setOperationTimeoutTaskManagerService( + operationTimeoutTaskManagerService); + bundleContext.registerService(OperationTimeoutTaskManagerService.class, + operationTimeoutTaskManagerService, null); + + OperationTimeoutConfiguration configuration = deviceManagementConfig.getOperationTimeoutConfiguration(); + + for (OperationTimeout operationTimeout : configuration.getOperationTimeoutList()) { + try { + operationTimeoutTaskManagerService.startTask(operationTimeout); + } catch (OperationTimeoutTaskException e) { + log.error("Error while starting the operation timeout task for device type (s) : " + + operationTimeout.getDeviceTypes() + ", operation code : " + + operationTimeout.getInitialStatus()); + } + } + } + @SuppressWarnings("unused") protected void deactivate(ComponentContext componentContext) { try { @@ -109,6 +139,9 @@ public class DeviceTaskManagerServiceComponent { if (deviceManagementConfig != null && deviceManagementConfig.getDeviceStatusTaskConfig().isEnabled()) { stopDeviceStatusMonitoringTask(); } + if (deviceManagementConfig != null && deviceManagementConfig.getOperationTimeoutConfiguration() != null) { + stopOperationTimeoutTask(); + } } catch (Throwable e) { log.error("Error occurred while shutting down device task manager service.", e); } @@ -143,6 +176,22 @@ public class DeviceTaskManagerServiceComponent { } } + private void stopOperationTimeoutTask() { + OperationTimeoutTaskManagerService operationTimeoutTaskManagerService = + DeviceManagementDataHolder.getInstance().getOperationTimeoutTaskManagerService(); + OperationTimeoutConfiguration configuration = deviceManagementConfig.getOperationTimeoutConfiguration(); + + for (OperationTimeout operationTimeout : configuration.getOperationTimeoutList()) { + try { + operationTimeoutTaskManagerService.stopTask(operationTimeout); + } catch (OperationTimeoutTaskException e) { + log.error("Error while stopping the operation timeout task for device type (s) : " + + operationTimeout.getDeviceTypes() + ", operation code : " + + operationTimeout.getInitialStatus()); + } + } + } + protected void setTaskService(TaskService taskService) { if (log.isDebugEnabled()) { log.debug("Setting the task service."); diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/OperationManagerImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/OperationManagerImpl.java index 32615eb5e25..27882f4fbd3 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/OperationManagerImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/OperationManagerImpl.java @@ -1587,4 +1587,19 @@ public class OperationManagerImpl implements OperationManager { OperationManagementDAOFactory.closeConnection(); } } + + @Override + public List getActivities(List deviceTypes, String operationCode, long updatedSince, String operationStatus) + throws OperationManagementException { + try { + OperationManagementDAOFactory.openConnection(); + return operationDAO.getActivities(deviceTypes, operationCode, updatedSince, operationStatus); + } catch (SQLException e) { + throw new OperationManagementException("Error occurred while opening a connection to the data source.", e); + } catch (OperationManagementDAOException e) { + throw new OperationManagementException("Error occurred while getting the activity list.", e); + } finally { + OperationManagementDAOFactory.closeConnection(); + } + } } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/OperationDAO.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/OperationDAO.java index 26e4f20ef86..6d54790c02c 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/OperationDAO.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/OperationDAO.java @@ -101,6 +101,9 @@ public interface OperationDAO { Map> getOperationMappingsByStatus(Operation.Status opStatus, Operation.PushNotificationStatus pushNotificationStatus, int limit) throws OperationManagementDAOException; + List getActivities(List deviceTypes, String operationCode, long updatedSince, String operationStatus) + throws OperationManagementDAOException; + List getActivities(ActivityPaginationRequest activityPaginationRequest) throws OperationManagementDAOException; diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/impl/GenericOperationDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/impl/GenericOperationDAOImpl.java index a9e050eb83d..24e565f2d7e 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/impl/GenericOperationDAOImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/impl/GenericOperationDAOImpl.java @@ -425,7 +425,8 @@ public class GenericOperationDAOImpl implements OperationDAO { " LEFT JOIN " + " DM_DEVICE_OPERATION_RESPONSE opr ON opr.EN_OP_MAP_ID = eom.ID " + "WHERE " + - " eom.OPERATION_ID = ? AND eom.TENANT_ID = ?"; + " eom.OPERATION_ID = ? AND eom.TENANT_ID = ? " + + "ORDER BY eom.UPDATED_TIMESTAMP DESC"; stmt = conn.prepareStatement(sql); stmt.setInt(1, operationId); @@ -1173,10 +1174,10 @@ public class GenericOperationDAOImpl implements OperationDAO { Operation operation = null; try { Connection conn = OperationManagementDAOFactory.getConnection(); - String sql = "SELECT o.ID, o.TYPE, o.CREATED_TIMESTAMP, o.RECEIVED_TIMESTAMP, om.STATUS, o.OPERATION_CODE, " + - "om.ID AS OM_MAPPING_ID, " + + String sql = "SELECT o.ID, o.TYPE, o.CREATED_TIMESTAMP, o.RECEIVED_TIMESTAMP, om.STATUS, " + + "o.OPERATION_CODE, o.INITIATED_BY, om.ID AS OM_MAPPING_ID, " + "om.UPDATED_TIMESTAMP FROM (SELECT ID, TYPE, CREATED_TIMESTAMP, RECEIVED_TIMESTAMP," + - "OPERATION_CODE FROM DM_OPERATION WHERE id = ?) o INNER JOIN (SELECT * FROM " + + "OPERATION_CODE, INITIATED_BY FROM DM_OPERATION WHERE id = ?) o INNER JOIN (SELECT * FROM " + "DM_ENROLMENT_OP_MAPPING dm where dm.OPERATION_ID = ? AND dm.ENROLMENT_ID = ?) om " + "ON o.ID = om.OPERATION_ID "; stmt = conn.prepareStatement(sql); @@ -1198,6 +1199,7 @@ public class GenericOperationDAOImpl implements OperationDAO { new Timestamp((rs.getLong("UPDATED_TIMESTAMP") * 1000)).toString()); } operation.setCode(rs.getString("OPERATION_CODE")); + operation.setInitiatedBy(rs.getString("INITIATED_BY")); OperationDAOUtil.setActivityId(operation, rs.getInt("ID")); } } catch (SQLException e) { @@ -1215,11 +1217,11 @@ public class GenericOperationDAOImpl implements OperationDAO { PreparedStatement stmt = null; ResultSet rs = null; Operation operation; - List operations = new ArrayList(); + List operations = new ArrayList<>(); try { Connection conn = OperationManagementDAOFactory.getConnection(); - String sql = "SELECT o.ID, TYPE, o.CREATED_TIMESTAMP, o.RECEIVED_TIMESTAMP, o.OPERATION_CODE, om.ID AS OM_MAPPING_ID," + - "om.UPDATED_TIMESTAMP FROM DM_OPERATION o " + + String sql = "SELECT o.ID, TYPE, o.CREATED_TIMESTAMP, o.RECEIVED_TIMESTAMP, o.OPERATION_CODE, " + + "o.INITIATED_BY, om.ID AS OM_MAPPING_ID, om.UPDATED_TIMESTAMP FROM DM_OPERATION o " + "INNER JOIN (SELECT * FROM DM_ENROLMENT_OP_MAPPING dm " + "WHERE dm.ENROLMENT_ID = ? AND dm.STATUS = ?) om ON o.ID = om.OPERATION_ID ORDER BY o.CREATED_TIMESTAMP DESC"; stmt = conn.prepareStatement(sql); @@ -1239,6 +1241,7 @@ public class GenericOperationDAOImpl implements OperationDAO { new Timestamp((rs.getLong("UPDATED_TIMESTAMP") * 1000)).toString()); } operation.setCode(rs.getString("OPERATION_CODE")); + operation.setInitiatedBy(rs.getString("INITIATED_BY")); operation.setStatus(status); OperationDAOUtil.setActivityId(operation, rs.getInt("ID")); operations.add(operation); @@ -1263,7 +1266,7 @@ public class GenericOperationDAOImpl implements OperationDAO { try { Connection conn = OperationManagementDAOFactory.getConnection(); String sql = "SELECT o.ID, TYPE, o.CREATED_TIMESTAMP, o.RECEIVED_TIMESTAMP, o.OPERATION_CODE, " + - "om.ID AS OM_MAPPING_ID, om.UPDATED_TIMESTAMP FROM DM_OPERATION o " + + "o.INITIATED_BY, om.ID AS OM_MAPPING_ID, om.UPDATED_TIMESTAMP FROM DM_OPERATION o " + "INNER JOIN (SELECT * FROM DM_ENROLMENT_OP_MAPPING dm " + "WHERE dm.ENROLMENT_ID = ? AND dm.STATUS = ?) om ON o.ID = om.OPERATION_ID ORDER BY " + "o.CREATED_TIMESTAMP DESC LIMIT ?,?"; @@ -1286,6 +1289,7 @@ public class GenericOperationDAOImpl implements OperationDAO { new Timestamp((rs.getLong("UPDATED_TIMESTAMP") * 1000)).toString()); } operation.setCode(rs.getString("OPERATION_CODE")); + operation.setInitiatedBy(rs.getString("INITIATED_BY")); operation.setStatus(status); OperationDAOUtil.setActivityId(operation, rs.getInt("OM_MAPPING_ID")); operations.add(operation); @@ -1309,8 +1313,8 @@ public class GenericOperationDAOImpl implements OperationDAO { try { Connection conn = OperationManagementDAOFactory.getConnection(); String sql = "SELECT o.ID, o.TYPE, o.CREATED_TIMESTAMP, o.RECEIVED_TIMESTAMP, " + - "o.OPERATION_CODE, om.STATUS, om.ID AS OM_MAPPING_ID, om.UPDATED_TIMESTAMP FROM DM_OPERATION o " + - "INNER JOIN (SELECT * FROM DM_ENROLMENT_OP_MAPPING dm " + + "o.OPERATION_CODE, o.INITIATED_BY, om.STATUS, om.ID AS OM_MAPPING_ID, om.UPDATED_TIMESTAMP " + + "FROM DM_OPERATION o INNER JOIN (SELECT * FROM DM_ENROLMENT_OP_MAPPING dm " + "WHERE dm.ENROLMENT_ID = ?) om ON o.ID = om.OPERATION_ID " + "ORDER BY o.CREATED_TIMESTAMP DESC, o.ID DESC"; stmt = conn.prepareStatement(sql); @@ -1329,6 +1333,7 @@ public class GenericOperationDAOImpl implements OperationDAO { new Timestamp((rs.getLong("UPDATED_TIMESTAMP") * 1000)).toString()); } operation.setCode(rs.getString("OPERATION_CODE")); + operation.setInitiatedBy(rs.getString("INITIATED_BY")); operation.setStatus(Operation.Status.valueOf(rs.getString("STATUS"))); operations.add(operation); } @@ -1345,7 +1350,7 @@ public class GenericOperationDAOImpl implements OperationDAO { public List getOperationsForDevice(int enrolmentId, PaginationRequest request) throws OperationManagementDAOException { Operation operation; - List operations = new ArrayList(); + List operations = new ArrayList<>(); String createdTo = null; String createdFrom = null; DateFormat simple = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); @@ -1363,74 +1368,75 @@ public class GenericOperationDAOImpl implements OperationDAO { Long updatedTo = request.getOperationLogFilters().getUpdatedDayTo(); List operationCode = request.getOperationLogFilters().getOperationCode(); List status = request.getOperationLogFilters().getStatus(); - String sql = "SELECT " + - "o.ID, " + - "TYPE, " + - "o.CREATED_TIMESTAMP, " + - "o.RECEIVED_TIMESTAMP, " + - "o.OPERATION_CODE, " + - "om.STATUS, " + - "om.ID AS OM_MAPPING_ID, " + - "om.UPDATED_TIMESTAMP " + - "FROM " + - "DM_OPERATION o " + - "INNER JOIN " + - "(SELECT dm.OPERATION_ID, " + - "dm.ID, " + - "dm.STATUS, " + - "dm.UPDATED_TIMESTAMP " + - "FROM " + - "DM_ENROLMENT_OP_MAPPING dm " + - "WHERE " + - "dm.ENROLMENT_ID = ?"; + StringBuilder sql = new StringBuilder("SELECT " + + "o.ID, " + + "TYPE, " + + "o.CREATED_TIMESTAMP, " + + "o.RECEIVED_TIMESTAMP, " + + "o.OPERATION_CODE, " + + "o.INITIATED_BY, " + + "om.STATUS, " + + "om.ID AS OM_MAPPING_ID, " + + "om.UPDATED_TIMESTAMP " + + "FROM " + + "DM_OPERATION o " + + "INNER JOIN " + + "(SELECT dm.OPERATION_ID, " + + "dm.ID, " + + "dm.STATUS, " + + "dm.UPDATED_TIMESTAMP " + + "FROM " + + "DM_ENROLMENT_OP_MAPPING dm " + + "WHERE " + + "dm.ENROLMENT_ID = ?"); if (updatedFrom != null && updatedFrom != 0 && updatedTo != null && updatedTo != 0) { - sql = sql + " AND dm.UPDATED_TIMESTAMP BETWEEN ? AND ?"; + sql.append(" AND dm.UPDATED_TIMESTAMP BETWEEN ? AND ?"); isUpdatedDayProvided = true; } - sql = sql + ") om ON o.ID = om.OPERATION_ID "; + sql.append(") om ON o.ID = om.OPERATION_ID "); if (createdFrom != null && !createdFrom.isEmpty() && createdTo != null && !createdTo.isEmpty()) { - sql = sql + " WHERE o.CREATED_TIMESTAMP BETWEEN ? AND ?"; + sql.append(" WHERE o.CREATED_TIMESTAMP BETWEEN ? AND ?"); isCreatedDayProvided = true; } if ((isCreatedDayProvided) && (status != null && !status.isEmpty())) { int size = status.size(); - sql = sql + " AND (om.STATUS = ? "; + sql.append(" AND (om.STATUS = ? "); for (int i = 0; i < size - 1; i++) { - sql = sql + " OR om.STATUS = ?"; + sql.append(" OR om.STATUS = ?"); } - sql = sql + ")"; + sql.append(")"); isStatusProvided = true; } else if ((!isCreatedDayProvided) && (status != null && !status.isEmpty())) { int size = status.size(); - sql = sql + " WHERE (om.STATUS = ? "; + sql.append(" WHERE (om.STATUS = ? "); for (int i = 0; i < size - 1; i++) { - sql = sql + " OR om.STATUS = ?"; + sql.append(" OR om.STATUS = ?"); } - sql = sql + ")"; + sql.append(")"); isStatusProvided = true; } if ((isCreatedDayProvided || isStatusProvided) && (operationCode != null && !operationCode.isEmpty())) { int size = operationCode.size(); - sql = sql + " AND (o.OPERATION_CODE = ? "; + sql.append(" AND (o.OPERATION_CODE = ? "); for (int i = 0; i < size - 1; i++) { - sql = sql + " OR o.OPERATION_CODE = ?"; + sql.append(" OR o.OPERATION_CODE = ?"); } - sql = sql + ")"; + sql.append(")"); isOperationCodeProvided = true; } else if ((!isCreatedDayProvided && !isStatusProvided) && (operationCode != null && !operationCode.isEmpty())) { int size = operationCode.size(); - sql = sql + " WHERE (o.OPERATION_CODE = ? "; + sql.append(" WHERE (o.OPERATION_CODE = ? "); for (int i = 0; i < size - 1; i++) { - sql = sql + " OR o.OPERATION_CODE = ?"; + sql.append(" OR o.OPERATION_CODE = ?"); } - sql = sql + ")"; + sql.append(")"); isOperationCodeProvided = true; } - sql = sql + " ORDER BY o.CREATED_TIMESTAMP DESC LIMIT ?,?"; + sql.append(" ORDER BY o.CREATED_TIMESTAMP DESC LIMIT ?,?"); try { Connection conn = OperationManagementDAOFactory.getConnection(); - try (PreparedStatement stmt = conn.prepareStatement(sql)) { + try (PreparedStatement stmt = conn.prepareStatement(sql.toString())) { int paramIndex = 1; stmt.setInt(paramIndex++, enrolmentId); if (isUpdatedDayProvided) { @@ -1442,15 +1448,13 @@ public class GenericOperationDAOImpl implements OperationDAO { stmt.setString(paramIndex++, createdTo); } if (isStatusProvided) { - int size = status.size(); - for (int i = 0; i < size; i++) { - stmt.setString(paramIndex++, status.get(i)); + for (String s : status) { + stmt.setString(paramIndex++, s); } } if (isOperationCodeProvided) { - int size = operationCode.size(); - for (int i = 0; i < size; i++) { - stmt.setString(paramIndex++, operationCode.get(i)); + for (String s : operationCode) { + stmt.setString(paramIndex++, s); } } stmt.setInt(paramIndex++, request.getStartIndex()); @@ -1468,6 +1472,7 @@ public class GenericOperationDAOImpl implements OperationDAO { new Timestamp((rs.getLong("UPDATED_TIMESTAMP") * 1000)).toString()); } operation.setCode(rs.getString("OPERATION_CODE")); + operation.setInitiatedBy(rs.getString("INITIATED_BY")); operation.setStatus(Operation.Status.valueOf(rs.getString("STATUS"))); OperationDAOUtil.setActivityId(operation, rs.getInt("ID")); operations.add(operation); @@ -1594,7 +1599,7 @@ public class GenericOperationDAOImpl implements OperationDAO { try { Connection connection = OperationManagementDAOFactory.getConnection(); stmt = connection.prepareStatement("SELECT o.ID, o.TYPE, o.CREATED_TIMESTAMP, o.RECEIVED_TIMESTAMP, " + - "o.OPERATION_CODE, om.ID AS OM_MAPPING_ID, om.UPDATED_TIMESTAMP FROM DM_OPERATION o " + + "o.OPERATION_CODE, o.INITIATED_BY, om.ID AS OM_MAPPING_ID, om.UPDATED_TIMESTAMP FROM DM_OPERATION o " + "INNER JOIN (SELECT * FROM DM_ENROLMENT_OP_MAPPING dm " + "WHERE dm.ENROLMENT_ID = ? AND dm.STATUS = ?) om ON o.ID = om.OPERATION_ID " + "ORDER BY om.UPDATED_TIMESTAMP ASC, om.ID ASC LIMIT 1"); @@ -1616,6 +1621,7 @@ public class GenericOperationDAOImpl implements OperationDAO { new Timestamp((rs.getLong("UPDATED_TIMESTAMP") * 1000)).toString()); } operation.setCode(rs.getString("OPERATION_CODE")); + operation.setInitiatedBy(rs.getString("INITIATED_BY")); operation.setStatus(Operation.Status.PENDING); OperationDAOUtil.setActivityId(operation, rs.getInt("ID")); } @@ -1636,10 +1642,10 @@ public class GenericOperationDAOImpl implements OperationDAO { List operations = new ArrayList<>(); try { Connection conn = OperationManagementDAOFactory.getConnection(); - String sql = "SELECT o.ID, TYPE, o.CREATED_TIMESTAMP, o.RECEIVED_TIMESTAMP, OPERATION_CODE, om.ID AS OM_MAPPING_ID, " + - "om.UPDATED_TIMESTAMP FROM (SELECT o.ID, TYPE, CREATED_TIMESTAMP, RECEIVED_TIMESTAMP, OPERATION_CODE " + - "FROM DM_OPERATION o WHERE o.TYPE = ?) o " + - "INNER JOIN (SELECT * FROM DM_ENROLMENT_OP_MAPPING dm " + + String sql = "SELECT o.ID, TYPE, o.CREATED_TIMESTAMP, o.RECEIVED_TIMESTAMP, OPERATION_CODE, o.INITIATED_BY," + + " om.ID AS OM_MAPPING_ID, om.UPDATED_TIMESTAMP FROM " + + "(SELECT o.ID, TYPE, CREATED_TIMESTAMP, RECEIVED_TIMESTAMP, OPERATION_CODE, INITIATED_BY " + + "FROM DM_OPERATION o WHERE o.TYPE = ?) o INNER JOIN (SELECT * FROM DM_ENROLMENT_OP_MAPPING dm " + "WHERE dm.ENROLMENT_ID = ? AND dm.STATUS = ?) om ON o.ID = om.OPERATION_ID ORDER BY o.CREATED_TIMESTAMP ASC"; stmt = conn.prepareStatement(sql); @@ -1660,6 +1666,7 @@ public class GenericOperationDAOImpl implements OperationDAO { new Timestamp((rs.getLong("UPDATED_TIMESTAMP") * 1000)).toString()); } operation.setCode(rs.getString("OPERATION_CODE")); + operation.setInitiatedBy(rs.getString("INITIATED_BY")); OperationDAOUtil.setActivityId(operation, rs.getInt("ID")); operations.add(operation); } @@ -1717,6 +1724,141 @@ public class GenericOperationDAOImpl implements OperationDAO { return operationMappingsTenantMap; } + + public List getActivities(List deviceTypes, String operationCode, long updatedSince, String operationStatus) + throws OperationManagementDAOException { + try { + + Connection conn = OperationManagementDAOFactory.getConnection(); + int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(); + StringBuilder sql = new StringBuilder("SELECT " + + " eom.ENROLMENT_ID," + + " eom.CREATED_TIMESTAMP," + + " eom.UPDATED_TIMESTAMP," + + " eom.OPERATION_ID," + + " eom.OPERATION_CODE," + + " eom.INITIATED_BY," + + " eom.TYPE," + + " eom.STATUS," + + " eom.DEVICE_ID," + + " eom.DEVICE_IDENTIFICATION," + + " eom.DEVICE_TYPE," + + " opr.ID AS OP_RES_ID," + + " opr.RECEIVED_TIMESTAMP," + + " opr.OPERATION_RESPONSE," + + " opr.IS_LARGE_RESPONSE " + + "FROM " + + " DM_ENROLMENT_OP_MAPPING eom " + + "LEFT JOIN " + + " DM_DEVICE_OPERATION_RESPONSE opr ON opr.EN_OP_MAP_ID = eom.ID " + + "INNER JOIN " + + " (SELECT DISTINCT OPERATION_ID FROM DM_ENROLMENT_OP_MAPPING WHERE TENANT_ID = ? "); + + + if (deviceTypes != null && !deviceTypes.isEmpty()) { + sql.append("AND DEVICE_TYPE IN ("); + for (int i = 0; i < deviceTypes.size() - 1; i++) { + sql.append("?, "); + } + sql.append("?) "); + } + + if (operationCode != null) { + sql.append("AND OPERATION_CODE = ? "); + } + + if (updatedSince != 0) { + sql.append("AND UPDATED_TIMESTAMP < ? "); + } + + if (operationStatus != null) { + sql.append("AND STATUS = ? "); + } + + sql.append("ORDER BY OPERATION_ID ASC ) eom_ordered " + + "ON eom_ordered.OPERATION_ID = eom.OPERATION_ID WHERE eom.TENANT_ID = ? "); + + if (deviceTypes != null && !deviceTypes.isEmpty()) { + sql.append("AND DEVICE_TYPE IN ("); + for (int i = 0; i < deviceTypes.size() - 1; i++) { + sql.append("?, "); + } + sql.append("?) "); + } + + if (operationCode != null) { + sql.append("AND eom.OPERATION_CODE = ? "); + } + + if (updatedSince != 0) { + sql.append("AND eom.UPDATED_TIMESTAMP < ? "); + } + + if (operationStatus != null) { + sql.append("AND eom.STATUS = ? "); + } + + sql.append("ORDER BY eom.OPERATION_ID, eom.UPDATED_TIMESTAMP"); + + int index = 1; + try (PreparedStatement stmt = conn.prepareStatement(sql.toString())) { + stmt.setInt(index++, tenantId); + + if (deviceTypes != null && !deviceTypes.isEmpty()) { + for (String deviceId : deviceTypes) { + stmt.setString(index++, deviceId); + } + } + + if (operationCode != null) { + stmt.setString(index++, operationCode); + } + + if (updatedSince != 0) { + stmt.setLong(index++, updatedSince); + } + + if (operationStatus != null) { + stmt.setString(index++, operationStatus); + } + + stmt.setInt(index++, tenantId); + + if (deviceTypes != null && !deviceTypes.isEmpty()) { + for (String deviceId : deviceTypes) { + stmt.setString(index++, deviceId); + } + } + + if (operationCode != null) { + stmt.setString(index++, operationCode); + } + + if (updatedSince != 0) { + stmt.setLong(index++, updatedSince); + } + + if (operationStatus != null) { + stmt.setString(index, operationStatus); + } + + try (ResultSet rs = stmt.executeQuery()) { + ActivityHolder activityHolder = OperationDAOUtil.getActivityHolder(rs); + List largeResponseIDs = activityHolder.getLargeResponseIDs(); + List activities = activityHolder.getActivityList(); + if (!largeResponseIDs.isEmpty()) { + populateLargeOperationResponses(activities, largeResponseIDs); + } + return activities; + } + } + } catch (SQLException e) { + String msg = "Error occurred while getting the operation details from the database."; + log.error(msg, e); + throw new OperationManagementDAOException(msg, e); + } + } + @Override public List getActivities(ActivityPaginationRequest activityPaginationRequest) throws OperationManagementDAOException { @@ -1724,7 +1866,7 @@ public class GenericOperationDAOImpl implements OperationDAO { boolean isTimeDurationFilteringProvided = false; Connection conn = OperationManagementDAOFactory.getConnection(); int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(); - String sql = "SELECT " + + StringBuilder sql = new StringBuilder("SELECT " + " eom.ENROLMENT_ID," + " eom.CREATED_TIMESTAMP," + " eom.UPDATED_TIMESTAMP," + @@ -1745,72 +1887,82 @@ public class GenericOperationDAOImpl implements OperationDAO { "LEFT JOIN " + " DM_DEVICE_OPERATION_RESPONSE opr ON opr.EN_OP_MAP_ID = eom.ID " + "INNER JOIN " + - " (SELECT DISTINCT OPERATION_ID FROM DM_ENROLMENT_OP_MAPPING WHERE TENANT_ID = ? "; + " (SELECT DISTINCT OPERATION_ID FROM DM_ENROLMENT_OP_MAPPING WHERE TENANT_ID = ? "); if (activityPaginationRequest.getDeviceType() != null) { - sql += "AND DEVICE_TYPE = ? "; + sql.append("AND DEVICE_TYPE = ? "); } - if (activityPaginationRequest.getDeviceId() != null) { - sql += "AND DEVICE_IDENTIFICATION = ? "; + if (activityPaginationRequest.getDeviceIds() != null && !activityPaginationRequest.getDeviceIds().isEmpty()) { + sql.append("AND DEVICE_IDENTIFICATION IN ("); + for (int i = 0; i < activityPaginationRequest.getDeviceIds().size() - 1; i++) { + sql.append("?, "); + } + sql.append("?) "); } if (activityPaginationRequest.getOperationCode() != null) { - sql += "AND OPERATION_CODE = ? "; + sql.append("AND OPERATION_CODE = ? "); } if (activityPaginationRequest.getInitiatedBy() != null) { - sql += "AND INITIATED_BY = ? "; + sql.append("AND INITIATED_BY = ? "); } if (activityPaginationRequest.getSince() != 0) { - sql += "AND UPDATED_TIMESTAMP > ? "; + sql.append("AND UPDATED_TIMESTAMP > ? "); } if (activityPaginationRequest.getStartTimestamp() > 0 && activityPaginationRequest.getEndTimestamp() > 0) { isTimeDurationFilteringProvided = true; - sql += "AND CREATED_TIMESTAMP BETWEEN ? AND ? "; + sql.append("AND CREATED_TIMESTAMP BETWEEN ? AND ? "); } if (activityPaginationRequest.getType() != null) { - sql += "AND TYPE = ? "; + sql.append("AND TYPE = ? "); } if (activityPaginationRequest.getStatus() != null) { - sql += "AND STATUS = ? "; + sql.append("AND STATUS = ? "); } - sql += "ORDER BY OPERATION_ID ASC limit ? , ? ) eom_ordered " + - "ON eom_ordered.OPERATION_ID = eom.OPERATION_ID WHERE eom.TENANT_ID = ? "; + sql.append("ORDER BY OPERATION_ID ASC limit ? , ? ) eom_ordered " + + "ON eom_ordered.OPERATION_ID = eom.OPERATION_ID WHERE eom.TENANT_ID = ? "); if (activityPaginationRequest.getDeviceType() != null) { - sql += "AND eom.DEVICE_TYPE = ? "; + sql.append("AND eom.DEVICE_TYPE = ? "); } - if (activityPaginationRequest.getDeviceId() != null) { - sql += "AND eom.DEVICE_IDENTIFICATION = ? "; + if (activityPaginationRequest.getDeviceIds() != null && !activityPaginationRequest.getDeviceIds().isEmpty()) { + sql.append("AND eom.DEVICE_IDENTIFICATION IN ("); + for (int i = 0; i < activityPaginationRequest.getDeviceIds().size() - 1; i++) { + sql.append("?, "); + } + sql.append("?) "); } if (activityPaginationRequest.getOperationCode() != null) { - sql += "AND eom.OPERATION_CODE = ? "; + sql.append("AND eom.OPERATION_CODE = ? "); } if (activityPaginationRequest.getInitiatedBy() != null) { - sql += "AND eom.INITIATED_BY = ? "; + sql.append("AND eom.INITIATED_BY = ? "); } if (activityPaginationRequest.getSince() != 0) { - sql += "AND eom.UPDATED_TIMESTAMP > ? "; + sql.append("AND eom.UPDATED_TIMESTAMP > ? "); } if (isTimeDurationFilteringProvided) { - sql += "AND eom.CREATED_TIMESTAMP BETWEEN ? AND ? "; + sql.append("AND eom.CREATED_TIMESTAMP BETWEEN ? AND ? "); } if (activityPaginationRequest.getType() != null) { - sql += "AND eom.TYPE = ? "; + sql.append("AND eom.TYPE = ? "); } if (activityPaginationRequest.getStatus() != null) { - sql += "AND eom.STATUS = ? "; + sql.append("AND eom.STATUS = ? "); } - sql += "ORDER BY eom.OPERATION_ID, eom.UPDATED_TIMESTAMP"; + sql.append("ORDER BY eom.OPERATION_ID, eom.UPDATED_TIMESTAMP"); int index = 1; - try (PreparedStatement stmt = conn.prepareStatement(sql)) { + try (PreparedStatement stmt = conn.prepareStatement(sql.toString())) { stmt.setInt(index++, tenantId); if (activityPaginationRequest.getDeviceType() != null) { stmt.setString(index++, activityPaginationRequest.getDeviceType()); } - if (activityPaginationRequest.getDeviceId() != null) { - stmt.setString(index++, activityPaginationRequest.getDeviceId()); + if (activityPaginationRequest.getDeviceIds() != null && !activityPaginationRequest.getDeviceIds().isEmpty()) { + for (String deviceId : activityPaginationRequest.getDeviceIds()) { + stmt.setString(index++, deviceId); + } } if (activityPaginationRequest.getOperationCode() != null) { stmt.setString(index++, activityPaginationRequest.getOperationCode()); @@ -1839,8 +1991,10 @@ public class GenericOperationDAOImpl implements OperationDAO { if (activityPaginationRequest.getDeviceType() != null) { stmt.setString(index++, activityPaginationRequest.getDeviceType()); } - if (activityPaginationRequest.getDeviceId() != null) { - stmt.setString(index++, activityPaginationRequest.getDeviceId()); + if (activityPaginationRequest.getDeviceIds() != null && !activityPaginationRequest.getDeviceIds().isEmpty()) { + for (String deviceId : activityPaginationRequest.getDeviceIds()) { + stmt.setString(index++, deviceId); + } } if (activityPaginationRequest.getOperationCode() != null) { stmt.setString(index++, activityPaginationRequest.getOperationCode()); @@ -1886,43 +2040,49 @@ public class GenericOperationDAOImpl implements OperationDAO { boolean isTimeDurationFilteringProvided = false; Connection conn = OperationManagementDAOFactory.getConnection(); int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(); - String sql = "SELECT count(DISTINCT OPERATION_ID) AS ACTIVITY_COUNT FROM DM_ENROLMENT_OP_MAPPING " + - "WHERE TENANT_ID = ? "; + StringBuilder sql = new StringBuilder("SELECT count(DISTINCT OPERATION_ID) AS ACTIVITY_COUNT " + + "FROM DM_ENROLMENT_OP_MAPPING WHERE TENANT_ID = ? "); if (activityPaginationRequest.getDeviceType() != null) { - sql += "AND DEVICE_TYPE = ? "; + sql.append("AND DEVICE_TYPE = ? "); } - if (activityPaginationRequest.getDeviceId() != null) { - sql += "AND DEVICE_IDENTIFICATION = ? "; + if (activityPaginationRequest.getDeviceIds() != null && !activityPaginationRequest.getDeviceIds().isEmpty()) { + sql.append("AND DEVICE_IDENTIFICATION IN ("); + for (int i = 0; i < activityPaginationRequest.getDeviceIds().size() - 1; i++) { + sql.append("?, "); + } + sql.append("?) "); } if (activityPaginationRequest.getOperationCode() != null) { - sql += "AND OPERATION_CODE = ? "; + sql.append("AND OPERATION_CODE = ? "); } if (activityPaginationRequest.getInitiatedBy() != null) { - sql += "AND INITIATED_BY = ? "; + sql.append("AND INITIATED_BY = ? "); } if (activityPaginationRequest.getSince() != 0) { - sql += "AND UPDATED_TIMESTAMP > ? "; + sql.append("AND UPDATED_TIMESTAMP > ? "); } if (activityPaginationRequest.getType() != null) { - sql += "AND TYPE = ? "; + sql.append("AND TYPE = ? "); } if (activityPaginationRequest.getStatus() != null) { - sql += "AND STATUS = ? "; + sql.append("AND STATUS = ? "); } if (activityPaginationRequest.getStartTimestamp() > 0 && activityPaginationRequest.getEndTimestamp() > 0) { isTimeDurationFilteringProvided = true; - sql += "AND CREATED_TIMESTAMP BETWEEN ? AND ? "; + sql.append("AND CREATED_TIMESTAMP BETWEEN ? AND ? "); } int index = 1; - try (PreparedStatement stmt = conn.prepareStatement(sql)) { + try (PreparedStatement stmt = conn.prepareStatement(sql.toString())) { stmt.setInt(index++, tenantId); if (activityPaginationRequest.getDeviceType() != null) { stmt.setString(index++, activityPaginationRequest.getDeviceType()); } - if (activityPaginationRequest.getDeviceId() != null) { - stmt.setString(index++, activityPaginationRequest.getDeviceId()); + if (activityPaginationRequest.getDeviceIds() != null && !activityPaginationRequest.getDeviceIds().isEmpty()) { + for (String deviceId : activityPaginationRequest.getDeviceIds()) { + stmt.setString(index++, deviceId); + } } if (activityPaginationRequest.getOperationCode() != null) { stmt.setString(index++, activityPaginationRequest.getOperationCode()); diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/timeout/task/OperationTimeoutTaskException.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/timeout/task/OperationTimeoutTaskException.java new file mode 100644 index 00000000000..c6a561ca3be --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/timeout/task/OperationTimeoutTaskException.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2022, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.device.mgt.core.operation.timeout.task; + +/** + * This exception class defines the custom exceptions thrown by the OperationTimeoutTask related components. + */ +public class OperationTimeoutTaskException extends Exception { + + private static final long serialVersionUID = -31222242646464497L; + + private String errorMessage; + + public String getErrorMessage() { + return errorMessage; + } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + public OperationTimeoutTaskException(String msg, Exception nestedEx) { + super(msg, nestedEx); + setErrorMessage(msg); + } + + public OperationTimeoutTaskException(String message, Throwable cause) { + super(message, cause); + setErrorMessage(message); + } + + public OperationTimeoutTaskException(String msg) { + super(msg); + setErrorMessage(msg); + } + + public OperationTimeoutTaskException() { + super(); + } + + public OperationTimeoutTaskException(Throwable cause) { + super(cause); + } + +} + diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/timeout/task/OperationTimeoutTaskManagerService.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/timeout/task/OperationTimeoutTaskManagerService.java new file mode 100644 index 00000000000..f605fd3ee69 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/timeout/task/OperationTimeoutTaskManagerService.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2022, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.wso2.carbon.device.mgt.core.operation.timeout.task; + +import org.wso2.carbon.device.mgt.core.config.operation.timeout.OperationTimeout; + +/** + * This interface defines the methods that should be implemented by the management service of OperationTimeoutTask + */ +public interface OperationTimeoutTaskManagerService { + + /** + * This method will start the task + * @param config + * @throws OperationTimeoutTaskException + */ + void startTask(OperationTimeout config) + throws OperationTimeoutTaskException; + + /** + * This method will stop the task. + * @param config + * @throws OperationTimeoutTaskException + */ + void stopTask(OperationTimeout config) + throws OperationTimeoutTaskException; + + /** + * This will update the task frequency which it runs. + * + * @param config + * @throws OperationTimeoutTaskException + */ + void updateTask(OperationTimeout config) + throws OperationTimeoutTaskException; + + /** + * This will check weather the task is scheduled. + * @return + * @throws OperationTimeoutTaskException + */ + boolean isTaskScheduled(OperationTimeout config) throws OperationTimeoutTaskException; +} \ No newline at end of file diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/timeout/task/impl/OperationTimeoutTask.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/timeout/task/impl/OperationTimeoutTask.java new file mode 100644 index 00000000000..2ee7e320328 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/timeout/task/impl/OperationTimeoutTask.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2022, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.carbon.device.mgt.core.operation.timeout.task.impl; + +import com.google.gson.Gson; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException; +import org.wso2.carbon.device.mgt.common.operation.mgt.Activity; +import org.wso2.carbon.device.mgt.common.operation.mgt.ActivityStatus; +import org.wso2.carbon.device.mgt.common.operation.mgt.Operation; +import org.wso2.carbon.device.mgt.common.operation.mgt.OperationManagementException; +import org.wso2.carbon.device.mgt.core.config.operation.timeout.OperationTimeout; +import org.wso2.carbon.device.mgt.core.dto.DeviceType; +import org.wso2.carbon.device.mgt.core.internal.DeviceManagementDataHolder; +import org.wso2.carbon.device.mgt.core.task.impl.DynamicPartitionedScheduleTask; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class OperationTimeoutTask extends DynamicPartitionedScheduleTask { + + private static final Log log = LogFactory.getLog(OperationTimeoutTask.class); + private OperationTimeout operationTimeoutConfig; + + @Override + public void setProperties(Map properties) { + super.setProperties(properties); + String operationTimeoutTaskConfigStr = properties + .get(OperationTimeoutTaskManagerServiceImpl.OPERATION_TIMEOUT_TASK_CONFIG); + Gson gson = new Gson(); + operationTimeoutConfig = gson.fromJson(operationTimeoutTaskConfigStr, OperationTimeout.class); + } + + @Override + public String getProperty(String name) { + return super.getProperty(name); + } + + @Override + public void refreshContext() { + super.refreshContext(); + } + + @Override + protected void setup() { + + } + + @Override + protected void executeDynamicTask() { + try { + + long timeMillis = System.currentTimeMillis() - operationTimeoutConfig.getTimeout() * 60 * 1000; + List deviceTypes = new ArrayList<>(); + if (operationTimeoutConfig.getDeviceTypes().size() == 1 && + "ALL".equals(operationTimeoutConfig.getDeviceTypes().get( 0))) { + try { + List deviceTypeList = DeviceManagementDataHolder.getInstance() + .getDeviceManagementProvider().getDeviceTypes(); + for (DeviceType deviceType : deviceTypeList) { + deviceTypes.add(deviceType.getName()); + } + } catch (DeviceManagementException e) { + log.error("Error occurred while reading device types", e); + } + } else { + deviceTypes = operationTimeoutConfig.getDeviceTypes(); + } + List activities = DeviceManagementDataHolder.getInstance().getOperationManager() + .getActivities(deviceTypes, operationTimeoutConfig.getCode(), timeMillis, + operationTimeoutConfig.getInitialStatus()); + for (Activity activity : activities) { + for (ActivityStatus activityStatus : activity.getActivityStatus()) { + String operationId = activity.getActivityId().replace("ACTIVITY_", ""); + Operation operation = DeviceManagementDataHolder.getInstance().getOperationManager() + .getOperation(Integer.parseInt(operationId)); + operation.setStatus(Operation.Status.valueOf(operationTimeoutConfig.getNextStatus())); + DeviceManagementDataHolder.getInstance().getOperationManager() + .updateOperation(activityStatus.getDeviceIdentifier(), operation); + } + } + + } catch (OperationManagementException e) { + String msg = "Error occurred while retrieving operations."; + log.error(msg, e); + } + } + +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/timeout/task/impl/OperationTimeoutTaskManagerServiceImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/timeout/task/impl/OperationTimeoutTaskManagerServiceImpl.java new file mode 100644 index 00000000000..b438b9215a2 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/timeout/task/impl/OperationTimeoutTaskManagerServiceImpl.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2022, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.carbon.device.mgt.core.operation.timeout.task.impl; + +import com.google.gson.Gson; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.mgt.core.config.operation.timeout.OperationTimeout; +import org.wso2.carbon.device.mgt.core.internal.DeviceManagementDataHolder; +import org.wso2.carbon.device.mgt.core.operation.timeout.task.OperationTimeoutTaskException; +import org.wso2.carbon.device.mgt.core.operation.timeout.task.OperationTimeoutTaskManagerService; +import org.wso2.carbon.ntask.common.TaskException; +import org.wso2.carbon.ntask.core.TaskInfo; +import org.wso2.carbon.ntask.core.TaskManager; +import org.wso2.carbon.ntask.core.service.TaskService; + +import java.util.HashMap; +import java.util.Map; + +public class OperationTimeoutTaskManagerServiceImpl implements OperationTimeoutTaskManagerService { + + private static final Log log = LogFactory.getLog(OperationTimeoutTaskManagerServiceImpl.class); + + public static final String OPERATION_TIMEOUT_TASK = "OPERATION_TIMEOUT_TASK"; + static final String DEVICE_TYPES = "DEVICE_TYPES"; + static final String OPERATION_TIMEOUT_TASK_CONFIG = "OPERATION_TIMEOUT_TASK_CONFIG"; + static final String INITIAL_STATUS = "INITIAL_STATUS"; + private static final String TASK_CLASS = OperationTimeoutTask.class.getName(); + + @Override + public void startTask(OperationTimeout config) + throws OperationTimeoutTaskException { + log.info("Operation timeout task adding for device type(s) : " + config.getDeviceTypes() + + ", operation code : " + config.getInitialStatus()); + + try { + TaskService taskService = DeviceManagementDataHolder.getInstance().getTaskService(); + taskService.registerTaskType(OPERATION_TIMEOUT_TASK); + + if (log.isDebugEnabled()) { + log.debug("Operation timeout task is started for the device type(s) : " + config.getDeviceTypes() + + ", operation code : " + config.getInitialStatus()); + log.debug( + "Operation timeout task is at frequency of : " + config.getTimeout() + " minutes"); + } + + TaskManager taskManager = taskService.getTaskManager(OPERATION_TIMEOUT_TASK); + + TaskInfo.TriggerInfo triggerInfo = new TaskInfo.TriggerInfo(); + //Convert to milli seconds + triggerInfo.setIntervalMillis(config.getTimeout() * 60 * 1000); + triggerInfo.setRepeatCount(-1); + + Gson gson = new Gson(); + String operationTimeoutConfig = gson.toJson(config); + + Map properties = new HashMap<>(); + + String deviceTypes = StringUtils.join(config.getDeviceTypes(), "_"); + properties.put(DEVICE_TYPES, deviceTypes); + properties.put(INITIAL_STATUS, config.getInitialStatus()); + properties.put(OPERATION_TIMEOUT_TASK_CONFIG, operationTimeoutConfig); + + String taskName = OPERATION_TIMEOUT_TASK + "_" + config.getInitialStatus() + "_" + deviceTypes; + + if (!taskManager.isTaskScheduled(taskName)) { + TaskInfo taskInfo = new TaskInfo(taskName, TASK_CLASS, properties, triggerInfo); + taskManager.registerTask(taskInfo); + taskManager.rescheduleTask(taskInfo.getName()); + } else { + throw new OperationTimeoutTaskException( + "Operation Timeout task is already started for the device type(s) : " + config.getDeviceTypes() + + ", operation code : " + config.getInitialStatus()); + } + } catch (TaskException e) { + throw new OperationTimeoutTaskException("Error occurred while creating the Operation timeout task " + + "for the device type(s) : " + config.getDeviceTypes() + ", operation code : " + config + .getInitialStatus(), e); + } + } + + @Override + public void stopTask(OperationTimeout config) + throws OperationTimeoutTaskException { + try { + TaskService taskService = DeviceManagementDataHolder.getInstance().getTaskService(); + String deviceTypes = StringUtils.join(config.getDeviceTypes(), "_"); + String taskName = OPERATION_TIMEOUT_TASK + "_" + config.getInitialStatus() + "_" + deviceTypes; + if (taskService != null && taskService.isServerInit()) { + TaskManager taskManager = taskService.getTaskManager(OPERATION_TIMEOUT_TASK); + taskManager.deleteTask(taskName); + } + } catch (TaskException e) { + throw new OperationTimeoutTaskException("Error occurred while deleting the Operation timeout task " + + "for the device type(s) : " + config.getDeviceTypes() + ", operation code : " + config + .getInitialStatus(), e); + } + } + + @Override + public void updateTask(OperationTimeout config) + throws OperationTimeoutTaskException { + try { + TaskService taskService = DeviceManagementDataHolder.getInstance().getTaskService(); + TaskManager taskManager = taskService.getTaskManager(OPERATION_TIMEOUT_TASK); + String deviceTypes = StringUtils.join(config.getDeviceTypes(), "_"); + String taskName = OPERATION_TIMEOUT_TASK + "_" + config.getInitialStatus() + "_" + deviceTypes; + + if (taskManager.isTaskScheduled(taskName)) { + taskManager.deleteTask(taskName); + TaskInfo.TriggerInfo triggerInfo = new TaskInfo.TriggerInfo(); + triggerInfo.setIntervalMillis(config.getTimeout() * 60 * 1000); + triggerInfo.setRepeatCount(-1); + + Map properties = new HashMap<>(); + properties.put(DEVICE_TYPES, deviceTypes); + properties.put(INITIAL_STATUS, config.getInitialStatus()); + + Gson gson = new Gson(); + String deviceStatusTaskConfigs = gson.toJson(config); + properties.put(OPERATION_TIMEOUT_TASK_CONFIG, deviceStatusTaskConfigs); + + TaskInfo taskInfo = new TaskInfo(taskName, TASK_CLASS, properties, triggerInfo); + + taskManager.registerTask(taskInfo); + taskManager.rescheduleTask(taskInfo.getName()); + } else { + throw new OperationTimeoutTaskException( + "Operation timeout task has not been started for this device-type the device type(s) : " + + config.getDeviceTypes() + ", operation code : " + config.getInitialStatus() + + ". Please start the task first."); + } + + } catch (TaskException e) { + throw new OperationTimeoutTaskException("Error occurred while updating the Operation timeout task " + + "for the device type(s) : " + config.getDeviceTypes() + ", operation code : " + + config.getInitialStatus(), e); + } + } + + @Override + public boolean isTaskScheduled(OperationTimeout config) throws OperationTimeoutTaskException { + String deviceTypes = StringUtils.join(config.getDeviceTypes(), "_"); + String taskName = OPERATION_TIMEOUT_TASK + "_" + config.getInitialStatus() + "_" + deviceTypes; + TaskService taskService = DeviceManagementDataHolder.getInstance().getTaskService(); + TaskManager taskManager; + try { + taskManager = taskService.getTaskManager(OPERATION_TIMEOUT_TASK); + return taskManager.isTaskScheduled(taskName); + } catch (TaskException e) { + throw new OperationTimeoutTaskException("Error occurred while checking the task schedule status " + + "of the Operation timeout task for the device type(s) : " + config.getDeviceTypes() + + ", operation code : " + config.getInitialStatus(), e); + } + } +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/Constants.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/Constants.java index 2be36d0acfc..962082ce3ac 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/Constants.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/Constants.java @@ -18,7 +18,14 @@ package org.wso2.carbon.device.mgt.core.report.mgt; +import org.wso2.carbon.utils.CarbonUtils; + +import java.io.File; + public class Constants { + public static final String DEFAULT_CONFIG_FILE_LOCATION = CarbonUtils.getCarbonConfigDirPath() + File.separator + + Constants.REPORT_MGT_CONFIG_XML_FILE; + public static final String REPORT_MGT_CONFIG_XML_FILE = "reporting-mgt.xml"; // device types public static final String ANDROID = "android"; public static final String IOS = "ios"; @@ -29,5 +36,14 @@ public class Constants { // OS version value generating properties public static final int NUM_OF_OS_VERSION_DIGITS= 5; public static final int NUM_OF_OS_VERSION_POSITIONS = 3; + public static final String BASIC_AUTH_HEADER_PREFIX = "Basic "; + public static final String HTTP_PROTOCOL = "http"; + public static final String HTTPS_PROTOCOL = "https"; + public static final String WSS_PROTOCOL = "wss"; + public static final String WS_PROTOCOL = "ws"; + public static final String SCHEME_SEPARATOR = "://"; + public static final String URI_SEPARATOR = "/"; + public static final String URI_QUERY_SEPARATOR = "?"; + public static final String COLON = ":"; } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/config/ReportMgtConfiguration.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/config/ReportMgtConfiguration.java new file mode 100644 index 00000000000..6c736e8679d --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/config/ReportMgtConfiguration.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved. + * + * Entgra (pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.carbon.device.mgt.core.report.mgt.config; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * Represents the Application Management Configuration. + */ +@XmlRootElement(name = "ReportManagementConfiguration") +public class ReportMgtConfiguration { + + private String datasourceName; + + @XmlElement(name = "DatasourceName", required = true) + public String getDatasourceName() { + return datasourceName; + } + + public void setDatasourceName(String datasourceName) { + this.datasourceName = datasourceName; + } + +} + diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/config/ReportMgtConfigurationManager.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/config/ReportMgtConfigurationManager.java new file mode 100644 index 00000000000..e47ba7cc84e --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/config/ReportMgtConfigurationManager.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved. + * + * Entgra (pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.carbon.device.mgt.core.report.mgt.config; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.mgt.common.exceptions.InvalidConfigurationException; +import org.wso2.carbon.device.mgt.common.exceptions.ReportManagementException; +import org.wso2.carbon.device.mgt.core.report.mgt.Constants; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.Unmarshaller; +import java.io.File; + +/** + * ConfigurationManager is responsible for the managing Application Management related configurations. + */ +public class ReportMgtConfigurationManager { + + private static final Log log = LogFactory.getLog(ReportMgtConfigurationManager.class); + + private ReportMgtConfiguration configuration; + + private static String configPath; + + private static volatile ReportMgtConfigurationManager configurationManager; + + private ReportMgtConfigurationManager() { + + } + + public static ReportMgtConfigurationManager getInstance() { + if (configurationManager == null) { + synchronized (ReportMgtConfigurationManager.class) { + if (configurationManager == null) { + configurationManager = new ReportMgtConfigurationManager(); + try { + configurationManager.initConfig(); + } catch (ReportManagementException e) { + log.error(e); + } + } + } + } + return configurationManager; + } + + public static synchronized void setConfigLocation(String configPath) throws InvalidConfigurationException { + if (ReportMgtConfigurationManager.configPath == null) { + ReportMgtConfigurationManager.configPath = configPath; + } else { + throw new InvalidConfigurationException("Configuration path " + configPath + " is already defined"); + } + } + + private void initConfig() throws ReportManagementException { + try { + JAXBContext jaxbContext = JAXBContext.newInstance(ReportMgtConfiguration.class); + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + if (configPath == null) { + configPath = Constants.DEFAULT_CONFIG_FILE_LOCATION; + } + //TODO: Add validation for the configurations + this.configuration = (ReportMgtConfiguration) unmarshaller.unmarshal(new File(configPath)); + } catch (Exception e) { + log.error(e); + throw new InvalidConfigurationException("Error occurred while initializing application config: " + + configPath, e); + } + } + + public ReportMgtConfiguration getConfiguration() { + return configuration; + } + +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/dao/common/ReportMgtConnectionManager.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/dao/common/ReportMgtConnectionManager.java new file mode 100644 index 00000000000..0d11461ef01 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/report/mgt/dao/common/ReportMgtConnectionManager.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved. + * + * Entgra (pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.carbon.device.mgt.core.report.mgt.dao.common; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.mgt.common.exceptions.DBConnectionException; +import org.wso2.carbon.device.mgt.common.exceptions.IllegalTransactionStateException; +import org.wso2.carbon.device.mgt.core.report.mgt.config.ReportMgtConfigurationManager; + +import javax.naming.InitialContext; +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; + +/** + * This class intends to act as the primary entity that hides all DAO instantiation related complexities and logic so + * that the business objection handling layer doesn't need to be aware of the same providing seamless plug-ability of + * different data sources, connection acquisition mechanisms as well as different forms of DAO implementations to the + * high-level implementations that require Application management related metadata persistence. + */ +public class ReportMgtConnectionManager { + + private static final Log log = LogFactory.getLog(ReportMgtConnectionManager.class); + + private static final ThreadLocal currentConnection = new ThreadLocal<>(); + private static DataSource dataSource; + + static { + String dataSourceName = ReportMgtConfigurationManager.getInstance().getConfiguration().getDatasourceName(); + init(dataSourceName); + } + + public static void init(String datasourceName) { + resolveDataSource(datasourceName); + } + + public static String getDatabaseType() { + try { + return dataSource.getConnection().getMetaData().getDatabaseProductName(); + } catch (SQLException e) { + log.error("Error occurred while retrieving config.datasource connection", e); + } + return null; + } + + /** + * Resolve the datasource from the datasource definition. + * + * @param dataSourceName Name of the datasource + * @return DataSource resolved by the datasource name + */ + public static DataSource resolveDataSource(String dataSourceName) { + try { + dataSource = InitialContext.doLookup(dataSourceName); + } catch (Exception e) { + throw new RuntimeException("Error in looking up data source: " + e.getMessage(), e); + } + return dataSource; + } + + public static void openDBConnection() throws DBConnectionException { + Connection conn = currentConnection.get(); + if (conn != null) { + throw new IllegalTransactionStateException("Database connection has already been obtained."); + } + try { + conn = dataSource.getConnection(); + } catch (SQLException e) { + throw new DBConnectionException("Failed to get a database connection.", e); + } + currentConnection.set(conn); + } + + public static Connection getDBConnection() throws DBConnectionException { + if (dataSource == null) { + String dataSourceName = ReportMgtConfigurationManager.getInstance().getConfiguration().getDatasourceName(); + init(dataSourceName); + } + Connection conn = currentConnection.get(); + if (conn == null) { + try { + conn = dataSource.getConnection(); + currentConnection.set(conn); + } catch (SQLException e) { + throw new DBConnectionException("Failed to get database connection.", e); + } + } + return conn; + } + + + public static void closeDBConnection() { + Connection conn = currentConnection.get(); + if (conn == null) { + throw new IllegalTransactionStateException("Database connection is not active."); + } + try { + conn.close(); + } catch (SQLException e) { + log.error("Error occurred while closing the connection", e); + } + currentConnection.remove(); + } + + +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderService.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderService.java index 6265384aad9..a8f249359bc 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderService.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderService.java @@ -72,6 +72,7 @@ import org.wso2.carbon.device.mgt.common.policy.mgt.PolicyMonitoringManager; import org.wso2.carbon.device.mgt.common.pull.notification.PullNotificationExecutionFailedException; import org.wso2.carbon.device.mgt.common.push.notification.NotificationStrategy; import org.wso2.carbon.device.mgt.common.spi.DeviceManagementService; +import org.wso2.carbon.device.mgt.common.type.mgt.DeviceStatus; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException; import org.wso2.carbon.device.mgt.core.dto.DeviceType; import org.wso2.carbon.device.mgt.core.dto.DeviceTypeVersion; @@ -690,6 +691,14 @@ public interface DeviceManagementProviderService { boolean setStatus(String currentOwner, EnrolmentInfo.Status status) throws DeviceManagementException; + List getDeviceStatusHistory(Device device) throws DeviceManagementException; + + List getDeviceStatusHistory(Device device, Date fromDate, Date toDate) throws DeviceManagementException; + + List getDeviceCurrentEnrolmentStatusHistory(Device device) throws DeviceManagementException; + + List getDeviceCurrentEnrolmentStatusHistory(Device device, Date fromDate, Date toDate) throws DeviceManagementException; + void notifyOperationToDevices(Operation operation, List deviceIds) throws DeviceManagementException; diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceImpl.java index 77b7ac0e089..1e17273481a 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceImpl.java @@ -108,6 +108,7 @@ import org.wso2.carbon.device.mgt.common.pull.notification.PullNotificationExecu import org.wso2.carbon.device.mgt.common.pull.notification.PullNotificationSubscriber; import org.wso2.carbon.device.mgt.common.push.notification.NotificationStrategy; import org.wso2.carbon.device.mgt.common.spi.DeviceManagementService; +import org.wso2.carbon.device.mgt.common.type.mgt.DeviceStatus; import org.wso2.carbon.device.mgt.common.type.mgt.DeviceTypePlatformDetails; import org.wso2.carbon.device.mgt.common.type.mgt.DeviceTypePlatformVersion; import org.wso2.carbon.device.mgt.core.DeviceManagementConstants; @@ -116,12 +117,7 @@ import org.wso2.carbon.device.mgt.core.cache.DeviceCacheKey; import org.wso2.carbon.device.mgt.core.cache.impl.DeviceCacheManagerImpl; import org.wso2.carbon.device.mgt.core.config.DeviceConfigurationManager; import org.wso2.carbon.device.mgt.core.config.DeviceManagementConfig; -import org.wso2.carbon.device.mgt.core.dao.ApplicationDAO; -import org.wso2.carbon.device.mgt.core.dao.DeviceDAO; -import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException; -import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory; -import org.wso2.carbon.device.mgt.core.dao.DeviceTypeDAO; -import org.wso2.carbon.device.mgt.core.dao.EnrollmentDAO; +import org.wso2.carbon.device.mgt.core.dao.*; import org.wso2.carbon.device.mgt.core.dao.util.DeviceManagementDAOUtil; import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceDetailsMgtException; import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceInformationManager; @@ -176,6 +172,7 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv private final DeviceTypeDAO deviceTypeDAO; private final EnrollmentDAO enrollmentDAO; private final ApplicationDAO applicationDAO; + private final DeviceStatusDAO deviceStatusDAO; public DeviceManagementProviderServiceImpl() { this.pluginRepository = new DeviceManagementPluginRepository(); @@ -183,6 +180,7 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv this.applicationDAO = DeviceManagementDAOFactory.getApplicationDAO(); this.deviceTypeDAO = DeviceManagementDAOFactory.getDeviceTypeDAO(); this.enrollmentDAO = DeviceManagementDAOFactory.getEnrollmentDAO(); + this.deviceStatusDAO = DeviceManagementDAOFactory.getDeviceStatusDAO(); /* Registering a listener to retrieve events when some device management service plugin is installed after * the component is done getting initialized */ @@ -1770,6 +1768,64 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv DeviceManagementDAOFactory.closeConnection(); } } + @Override + public List getDeviceStatusHistory(Device device, Date fromDate, Date toDate) throws DeviceManagementException{ + if (log.isDebugEnabled()) { + log.debug("get status history of device: " + device.getDeviceIdentifier()); + } + try { + int tenantId = this.getTenantId(); + return deviceStatusDAO.getStatus(device.getId(), tenantId, fromDate, toDate); + } catch (DeviceManagementDAOException e) { + DeviceManagementDAOFactory.rollbackTransaction(); + String msg = "Error occurred while retrieving status history"; + log.error(msg, e); + throw new DeviceManagementException(msg, e); + } catch (Exception e) { + String msg = "Error occurred in retrieving status history for device :" + device.getDeviceIdentifier(); + log.error(msg, e); + throw new DeviceManagementException(msg, e); + } + } + + @Override + public List getDeviceCurrentEnrolmentStatusHistory(Device device, Date fromDate, Date toDate) throws DeviceManagementException{ + if (log.isDebugEnabled()) { + log.debug("get status history of device: " + device.getDeviceIdentifier()); + } + try { + int tenantId = this.getTenantId(); + EnrolmentInfo enrolmentInfo = device.getEnrolmentInfo(); + if (enrolmentInfo == null) { + enrolmentInfo = enrollmentDAO.getEnrollment(device.getId(), tenantId); + if (enrolmentInfo == null) { + String msg = "Error occurred in getting enrollment for device :" + device.getDeviceIdentifier(); + log.error(msg); + throw new DeviceManagementException(msg); + } + } + return deviceStatusDAO.getStatus(enrolmentInfo.getId(), fromDate, toDate); + } catch (DeviceManagementDAOException e) { + DeviceManagementDAOFactory.rollbackTransaction(); + String msg = "Error occurred while retrieving status history"; + log.error(msg, e); + throw new DeviceManagementException(msg, e); + } catch (Exception e) { + String msg = "Error occurred in retrieving status history for current enrolment of device : " + device.getDeviceIdentifier(); + log.error(msg, e); + throw new DeviceManagementException(msg, e); + } + } + + @Override + public List getDeviceStatusHistory(Device device) throws DeviceManagementException{ + return getDeviceStatusHistory(device, null, null); + } + + @Override + public List getDeviceCurrentEnrolmentStatusHistory(Device device) throws DeviceManagementException{ + return getDeviceCurrentEnrolmentStatusHistory(device, null, null); + } @Override public boolean setStatus(String currentOwner, EnrolmentInfo.Status status) throws DeviceManagementException { @@ -1780,7 +1836,7 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv boolean success; int tenantId = this.getTenantId(); DeviceManagementDAOFactory.beginTransaction(); - success = enrollmentDAO.setStatus(currentOwner, status, tenantId); + success = enrollmentDAO.setStatusAllDevices(currentOwner, status, tenantId); DeviceManagementDAOFactory.commitTransaction(); return success; } catch (DeviceManagementDAOException e) { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/GroupManagementProviderServiceImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/GroupManagementProviderServiceImpl.java index 6a678c1fca0..2db3bd5f584 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/GroupManagementProviderServiceImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/GroupManagementProviderServiceImpl.java @@ -994,26 +994,37 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid if (log.isDebugEnabled()) { log.debug("Group devices to the group: " + groupId); } - Device device; + int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId(); + List devicesList = null; + try { + + List deviceIdentifierList = deviceIdentifiers.stream() + .map(DeviceIdentifier::getId) + .collect(Collectors.toCollection(ArrayList::new)); + devicesList = DeviceManagementDataHolder.getInstance().getDeviceManagementProvider() + .getDeviceByIdList(deviceIdentifierList); + if (devicesList == null || devicesList.isEmpty()) { + throw new DeviceNotFoundException("Couldn't find any devices for the given deviceIdentifiers '" + deviceIdentifiers + "'"); + } + } catch (DeviceManagementException e) { + String msg = "Error occurred while retrieving devices using device identifiers"; + log.error(msg, e); + throw new GroupManagementException(msg, e); + } catch (Exception e) { + String msg = "Error occurred in addDevices for groupId " + groupId; + log.error(msg, e); + throw new GroupManagementException(msg, e); + } + try { - int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId(); GroupManagementDAOFactory.beginTransaction(); - for (DeviceIdentifier deviceIdentifier : deviceIdentifiers) { - device = DeviceManagementDataHolder.getInstance().getDeviceManagementProvider(). - getDevice(deviceIdentifier, false); - if (device == null) { - throw new DeviceNotFoundException("Device not found for id '" + deviceIdentifier.getId() + "'"); - } + for (Device device : devicesList) { if (!this.groupDAO.isDeviceMappedToGroup(groupId, device.getId(), tenantId)) { this.groupDAO.addDevice(groupId, device.getId(), tenantId); } } GroupManagementDAOFactory.commitTransaction(); createEventTask(OperationMgtConstants.OperationCodes.EVENT_CONFIG, groupId, deviceIdentifiers, tenantId); - } catch (DeviceManagementException e) { - String msg = "Error occurred while retrieving device."; - log.error(msg, e); - throw new GroupManagementException(msg, e); } catch (GroupManagementDAOException e) { GroupManagementDAOFactory.rollbackTransaction(); String msg = "Error occurred while adding device to group."; diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/util/HttpReportingUtil.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/util/HttpReportingUtil.java index 920b4e4972e..54877fdaa13 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/util/HttpReportingUtil.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/util/HttpReportingUtil.java @@ -17,6 +17,8 @@ package org.wso2.carbon.device.mgt.core.util; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; @@ -26,11 +28,11 @@ import org.apache.http.impl.client.HttpClients; import org.apache.http.protocol.HTTP; import org.wso2.carbon.device.mgt.common.exceptions.EventPublishingException; import org.wso2.carbon.device.mgt.core.DeviceManagementConstants; - import java.io.IOException; public class HttpReportingUtil { + private static final Log log = LogFactory.getLog(HttpReportingUtil.class); private static final String IS_EVENT_PUBLISHING_ENABLED = "isEventPublishingEnabled"; public static String getReportingHost() { @@ -52,6 +54,7 @@ public class HttpReportingUtil { } } + public static boolean isPublishingEnabledForTenant() { Object configuration = DeviceManagerUtil.getConfiguration(IS_EVENT_PUBLISHING_ENABLED); if (configuration != null) { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/common/BaseDeviceManagementTest.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/common/BaseDeviceManagementTest.java index d991f92957e..2fc9e4122e6 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/common/BaseDeviceManagementTest.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/common/BaseDeviceManagementTest.java @@ -158,6 +158,7 @@ public abstract class BaseDeviceManagementTest { PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(MultitenantConstants .SUPER_TENANT_DOMAIN_NAME); PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(MultitenantConstants.SUPER_TENANT_ID); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername("admin"); } protected DataSourceConfig readDataSourceConfig(String configLocation) throws DeviceManagementException { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/dao/DeviceStatusPersistenceTests.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/dao/DeviceStatusPersistenceTests.java new file mode 100644 index 00000000000..65c22e11613 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/dao/DeviceStatusPersistenceTests.java @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.wso2.carbon.device.mgt.core.dao; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.testng.Assert; +import org.testng.annotations.Test; +import org.testng.annotations.BeforeClass; +import org.wso2.carbon.device.mgt.common.Device; +import org.wso2.carbon.device.mgt.common.EnrolmentInfo; +import org.wso2.carbon.device.mgt.common.type.mgt.DeviceStatus; +import org.wso2.carbon.device.mgt.core.common.BaseDeviceManagementTest; +import org.wso2.carbon.device.mgt.core.common.TestDataHolder; + +import java.sql.SQLException; +import java.util.*; +import java.util.List; +import java.util.stream.Stream; + +import static org.wso2.carbon.device.mgt.common.EnrolmentInfo.Status.*; + +public class DeviceStatusPersistenceTests extends BaseDeviceManagementTest { + + private static final Log log = LogFactory.getLog(DeviceStatusPersistenceTests.class); + private EnrollmentDAO enrollmentDAO = DeviceManagementDAOFactory.getEnrollmentDAO(); + private DeviceStatusDAO deviceStatusDAO = DeviceManagementDAOFactory.getDeviceStatusDAO(); + + /** + * Validate that the list of statuses received match the statuses + * @param device + * @param receivedStatus + * @param statuses + */ + private void validateDeviceStatus(Device device, List receivedStatus, EnrolmentInfo.Status[] statuses){ + Assert.assertEquals(receivedStatus.size(), statuses.length); + for(int i = 0; i < statuses.length; i++) { + Assert.assertEquals(receivedStatus.get(i).getDeviceId(), device.getId()); + Assert.assertEquals(receivedStatus.get(i).getStatus(), statuses[i]); + } + } + + /** + * Validate the list of statuses corresponds to different enrolments and their statuses + * @param device + * @param receivedStatus + * @param statuses + */ + private void validateDeviceStatus(Device device, List receivedStatus, EnrolmentInfo.Status[][] statuses){ + Map> statusMap = new HashMap<>(); + for (DeviceStatus deviceStatus: receivedStatus) { + Assert.assertEquals(deviceStatus.getDeviceId(), device.getId()); + if (!statusMap.containsKey(deviceStatus.getEnrolmentId())){ + statusMap.put(deviceStatus.getEnrolmentId(), new ArrayList<>()); + } + statusMap.get(deviceStatus.getEnrolmentId()).add(deviceStatus); + } + Assert.assertEquals(statusMap.size(), statuses.length); + Integer[] keys = (new TreeSet<>(statusMap.keySet())).toArray(new Integer[]{}); + for(int i = 0; i < keys.length; i++){ + validateDeviceStatus(device, statusMap.get(keys[i]), statuses[i]); + } + } + + @Test + public void testSingleDeviceOneEnrolmentOneStatus(){ + try { + this.initDataSource(); + Device device = TestDataHolder.generateDummyDeviceData(TestDataHolder.TEST_DEVICE_TYPE); + addDevice(device); + + EnrolmentInfo.Status[] statuses = {ACTIVE}; + int enrolmentId = createNewEnrolmentAddStatuses(device, "admin", statuses); + +// DeviceManagementDAOFactory.openConnection(); + validateDeviceStatus(device, deviceStatusDAO.getStatus(enrolmentId), statuses); + validateDeviceStatus(device, deviceStatusDAO.getStatus(device.getId(), TestDataHolder.SUPER_TENANT_ID), + new EnrolmentInfo.Status[][]{statuses}); + } catch (DeviceManagementDAOException | SQLException e) { + log.error("Error occurred while getting enrolment status", e); + } catch (Exception e) { + log.error("Error occurred while initializing datasource", e); +// } finally{ +// DeviceManagementDAOFactory.closeConnection(); + } + } + + @Test + public void testSingleDeviceOneEnrolmentMultipleStatus(){ + try { + this.initDataSource(); + Device device = TestDataHolder.generateDummyDeviceData(TestDataHolder.TEST_DEVICE_TYPE); + addDevice(device); + EnrolmentInfo.Status[] statuses = {ACTIVE, ASSIGNED, CONFIGURED, READY_TO_CONNECT}; + int enrolmentId = createNewEnrolmentAddStatuses(device, "admin", statuses); +// DeviceManagementDAOFactory.openConnection(); + validateDeviceStatus(device, deviceStatusDAO.getStatus(enrolmentId),statuses); + validateDeviceStatus(device, deviceStatusDAO.getStatus(device.getId(), TestDataHolder.SUPER_TENANT_ID), + new EnrolmentInfo.Status[][]{statuses}); + } catch (DeviceManagementDAOException | SQLException e) { + log.error("Error occurred while getting enrolment status", e); + } catch (Exception e) { + log.error("Error occurred while initializing datasource", e); +// } finally{ +// DeviceManagementDAOFactory.closeConnection(); + } + } + + @Test + public void testSingleDeviceMultipleEnrolmentMultipleStatus(){ + try { + this.initDataSource(); + Device device = TestDataHolder.generateDummyDeviceData(TestDataHolder.TEST_DEVICE_TYPE); + addDevice(device); + + EnrolmentInfo.Status[] statuses1 = {ACTIVE, ASSIGNED, CONFIGURED, READY_TO_CONNECT}; + int enrolmentId1 = createNewEnrolmentAddStatuses(device, "admin1", statuses1); + + EnrolmentInfo.Status[] statuses2 = {CREATED, SUSPENDED, BLOCKED, DEFECTIVE, REMOVED, WARRANTY_REPLACED, BLOCKED}; + int enrolmentId2 = createNewEnrolmentAddStatuses(device, "admin2", statuses2); + + EnrolmentInfo.Status[] statuses3 = {READY_TO_CONNECT, ASSIGNED}; + addStatus(enrolmentId1, statuses3); + + EnrolmentInfo.Status[] statuses1_3 = Stream.concat(Arrays.stream(statuses1), Arrays.stream(statuses3)).toArray(EnrolmentInfo.Status[]::new); + + // introducing a delay so that data is committed to the database before we try to retrieve for validation +// Thread.sleep(5000); + +// DeviceManagementDAOFactory.openConnection(); + validateDeviceStatus(device, deviceStatusDAO.getStatus(enrolmentId1), statuses1_3); + validateDeviceStatus(device, deviceStatusDAO.getStatus(enrolmentId2), statuses2); + + validateDeviceStatus(device, deviceStatusDAO.getStatus(device.getId(), TestDataHolder.SUPER_TENANT_ID), + new EnrolmentInfo.Status[][]{statuses1_3, statuses2}); + } catch (DeviceManagementDAOException | SQLException e) { + log.error("Error occurred while getting enrolment status", e); + } catch (Exception e) { + log.error("Error occurred while initializing data source", e); +// } finally{ +// DeviceManagementDAOFactory.closeConnection(); + } + } + + @Test + public void testSettingAllDeviceStatusOfSingleUser(){ + try { + this.initDataSource(); + Device device1 = TestDataHolder.generateDummyDeviceData(TestDataHolder.TEST_DEVICE_TYPE); + addDevice(device1); + + Device device2 = TestDataHolder.generateDummyDeviceData(TestDataHolder.TEST_DEVICE_TYPE); + addDevice(device2); + + EnrolmentInfo.Status[] statuses1 = {ACTIVE, ASSIGNED, CONFIGURED, READY_TO_CONNECT}; + int enrolmentId1 = createNewEnrolmentAddStatuses(device1, "admin1", statuses1); + + EnrolmentInfo.Status[] statuses2 = {CREATED, SUSPENDED, BLOCKED, DEFECTIVE, REMOVED, WARRANTY_REPLACED, BLOCKED}; + int enrolmentId2 = createNewEnrolmentAddStatuses(device1, "admin2", statuses2); + + EnrolmentInfo.Status[] statuses3 = {READY_TO_CONNECT, ASSIGNED}; + int enrolmentId3 = createNewEnrolmentAddStatuses(device2, "admin1", statuses3); + + enrollmentDAO.setStatusAllDevices("admin1", REMOVED, TestDataHolder.SUPER_TENANT_ID); + + EnrolmentInfo.Status[] statuses1_ = Stream.concat(Arrays.stream(statuses1), Arrays.stream(new EnrolmentInfo.Status[] {REMOVED})).toArray(EnrolmentInfo.Status[]::new); + EnrolmentInfo.Status[] statuses3_ = Stream.concat(Arrays.stream(statuses3), Arrays.stream(new EnrolmentInfo.Status[] {REMOVED})).toArray(EnrolmentInfo.Status[]::new); + + validateDeviceStatus(device1, deviceStatusDAO.getStatus(enrolmentId1), statuses1_); + validateDeviceStatus(device2, deviceStatusDAO.getStatus(enrolmentId3), statuses3_); + + } catch (DeviceManagementDAOException | SQLException e) { + log.error("Error occurred while getting enrolment status", e); + } catch (Exception e) { + log.error("Error occurred while initializing data source", e); + } + } + private int addDevice(Device device) throws DeviceManagementDAOException { + try { + DeviceManagementDAOFactory.openConnection(); + DeviceDAO deviceDAO = DeviceManagementDAOFactory.getDeviceDAO(); + int deviceId = deviceDAO.addDevice(TestDataHolder.initialTestDeviceType.getId(), device, TestDataHolder.SUPER_TENANT_ID); + device.setId(deviceId); + return deviceId; + } catch (SQLException e) { + throw new DeviceManagementDAOException("Error while adding device", e); + }finally{ + DeviceManagementDAOFactory.closeConnection(); + } + + } + private int addEnrolment(Device device, String owner, int tenantId, EnrolmentInfo.Status initialStatus) { + int deviceId = device.getId(); + EnrolmentInfo source = new EnrolmentInfo(owner, EnrolmentInfo.OwnerShip.BYOD, initialStatus); + try { + DeviceManagementDAOFactory.openConnection(); + EnrolmentInfo config = enrollmentDAO.addEnrollment(deviceId, source, tenantId); + device.setEnrolmentInfo(config); + return config.getId(); + } catch (DeviceManagementDAOException | SQLException e) { + log.error("Error occurred while adding enrollment", e); + } finally { + DeviceManagementDAOFactory.closeConnection(); + } + return -1; + } + + private int createNewEnrolmentAddStatuses(Device device, String user, EnrolmentInfo.Status[] statuses) { + int enrolmentId = this.addEnrolment(device, user, TestDataHolder.SUPER_TENANT_ID, statuses[0]); + Assert.assertNotEquals(enrolmentId, -1); + addStatus(enrolmentId, Arrays.copyOfRange(statuses, 1, statuses.length)); + return enrolmentId; + } + + private void addStatus(int enrolmentId, EnrolmentInfo.Status[] statuses) { + for(int i = 0; i < statuses.length; i++) { + Assert.assertTrue(addStatus(enrolmentId, statuses[i], TestDataHolder.SUPER_TENANT_ID)); + } + } + + private boolean addStatus(int enrolmentId, EnrolmentInfo.Status status, int tenentId){ + try { + DeviceManagementDAOFactory.openConnection(); + return enrollmentDAO.setStatus(enrolmentId, status, tenentId); + } catch (DeviceManagementDAOException | SQLException e) { + log.error("Error occurred while setting status", e); + } finally { + DeviceManagementDAOFactory.closeConnection(); + } + return false; + } + + @BeforeClass + @Override + public void init() throws Exception { + + } +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/dao/EnrolmentPersistenceTests.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/dao/EnrolmentPersistenceTests.java index 92647cea285..9497324798e 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/dao/EnrolmentPersistenceTests.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/dao/EnrolmentPersistenceTests.java @@ -23,6 +23,7 @@ import org.apache.commons.logging.LogFactory; import org.testng.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import org.wso2.carbon.device.mgt.common.Device; import org.wso2.carbon.device.mgt.common.EnrolmentInfo; import org.wso2.carbon.device.mgt.core.common.BaseDeviceManagementTest; import org.wso2.carbon.device.mgt.core.common.TestDataHolder; @@ -36,7 +37,7 @@ public class EnrolmentPersistenceTests extends BaseDeviceManagementTest { @Test public void testAddEnrolment() { - int deviceId = TestDataHolder.initialTestDevice.getId(); + int deviceId = -1; String owner = "admin"; /* Initializing source enrolment configuration bean to be tested */ @@ -47,6 +48,10 @@ public class EnrolmentPersistenceTests extends BaseDeviceManagementTest { /* Adding dummy enrolment configuration to the device management metadata store */ try { DeviceManagementDAOFactory.openConnection(); + Device device = TestDataHolder.generateDummyDeviceData(TestDataHolder.TEST_DEVICE_TYPE); + DeviceDAO deviceDAO = DeviceManagementDAOFactory.getDeviceDAO(); + deviceId = deviceDAO.addDevice(TestDataHolder.initialTestDeviceType.getId(), device, TestDataHolder.SUPER_TENANT_ID); + device.setId(deviceId); enrollmentDAO.addEnrollment(deviceId, source, TestDataHolder.SUPER_TENANT_ID); } catch (DeviceManagementDAOException | SQLException e) { log.error("Error occurred while adding enrollment", e); @@ -54,16 +59,18 @@ public class EnrolmentPersistenceTests extends BaseDeviceManagementTest { DeviceManagementDAOFactory.closeConnection(); } /* Retrieving the enrolment associated with the given deviceId and owner */ - EnrolmentInfo target = null; - try { - target = this.getEnrolmentConfig(deviceId, owner, TestDataHolder.SUPER_TENANT_ID); - } catch (DeviceManagementDAOException e) { - String msg = "Error occurred while retrieving application info"; - log.error(msg, e); - Assert.fail(msg, e); - } + if (deviceId != -1) { + EnrolmentInfo target = null; + try { + target = this.getEnrolmentConfig(deviceId, owner, TestDataHolder.SUPER_TENANT_ID); + } catch (DeviceManagementDAOException e) { + String msg = "Error occurred while retrieving application info"; + log.error(msg, e); + Assert.fail(msg, e); + } - Assert.assertEquals(target, source, "Enrolment configuration added is not as same as what's retrieved"); + Assert.assertEquals(target, source, "Enrolment configuration added is not as same as what's retrieved"); + } } private EnrolmentInfo getEnrolmentConfig(int deviceId, String currentOwner, diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceTest.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceTest.java index 80183bbcffc..443c0f84996 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceTest.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceTest.java @@ -74,6 +74,8 @@ import org.wso2.carbon.device.mgt.core.authorization.DeviceAccessAuthorizationSe import org.wso2.carbon.device.mgt.core.common.BaseDeviceManagementTest; import org.wso2.carbon.device.mgt.core.common.TestDataHolder; import org.wso2.carbon.device.mgt.core.config.DeviceConfigurationManager; +import org.wso2.carbon.device.mgt.core.dao.DeviceDAO; +import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory; import org.wso2.carbon.device.mgt.core.device.details.mgt.dao.DeviceDetailsDAO; import org.wso2.carbon.device.mgt.core.device.details.mgt.dao.DeviceDetailsMgtDAOException; @@ -885,6 +887,28 @@ public class DeviceManagementProviderServiceTest extends BaseDeviceManagementTes public void testUpdateDevicesStatusWithDeviceID() throws DeviceManagementException { if (!isMock()) { Device device = TestDataHolder.generateDummyDeviceData("abc"); + try { + int tenantId = TestDataHolder.SUPER_TENANT_ID; + DeviceManagementDAOFactory.beginTransaction(); + DeviceDAO deviceDAO = DeviceManagementDAOFactory.getDeviceDAO(); + int deviceId = deviceDAO.addDevice(TestDataHolder.initialTestDeviceType.getId(), device, tenantId); + device.setId(deviceId); + int enrolmentId = deviceDAO.addEnrollment(device, tenantId); + device.getEnrolmentInfo().setId(enrolmentId); + DeviceManagementDAOFactory.commitTransaction(); + } catch (DeviceManagementDAOException e) { + DeviceManagementDAOFactory.rollbackTransaction(); + String msg = "Error occurred while adding '" + device.getType() + "' device with the identifier '" + + device.getDeviceIdentifier() + "'"; + log.error(msg, e); + Assert.fail(msg, e); + } catch (TransactionManagementException e) { + String msg = "Error occurred while initiating transaction"; + log.error(msg, e); + Assert.fail(msg, e); + } finally { + DeviceManagementDAOFactory.closeConnection(); + } boolean status = deviceMgtService.setStatus(device, EnrolmentInfo.Status.ACTIVE); Assert.assertTrue(status); } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/resources/sql/h2.sql b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/resources/sql/h2.sql index cc69537e903..ea5bd1dd33f 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/resources/sql/h2.sql +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/resources/sql/h2.sql @@ -99,7 +99,19 @@ CREATE TABLE IF NOT EXISTS DM_ENROLMENT ( DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT uk_dm_device_enrolment UNIQUE (DEVICE_ID, OWNER, OWNERSHIP, TENANT_ID) ); - +CREATE TABLE IF NOT EXISTS DM_DEVICE_STATUS ( + ID INTEGER AUTO_INCREMENT NOT NULL, + ENROLMENT_ID INTEGER NOT NULL, + DEVICE_ID INTEGER NOT NULL, + STATUS VARCHAR(50) DEFAULT NULL, + UPDATE_TIME TIMESTAMP DEFAULT NULL, + CHANGED_BY VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT fk_dm_device_status_device FOREIGN KEY (DEVICE_ID) REFERENCES + DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT fk_dm_device_status_enrolment FOREIGN KEY (ENROLMENT_ID) REFERENCES + DM_ENROLMENT (ID) ON DELETE NO ACTION ON UPDATE NO ACTION +); CREATE TABLE IF NOT EXISTS DM_ENROLMENT_OP_MAPPING ( ID INTEGER AUTO_INCREMENT NOT NULL, ENROLMENT_ID INTEGER NOT NULL, diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/resources/testng.xml b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/resources/testng.xml index 889a6238b71..dc0dd4d9394 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/resources/testng.xml +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/resources/testng.xml @@ -33,6 +33,8 @@ + + diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.extensions/pom.xml b/components/device-mgt/org.wso2.carbon.device.mgt.extensions/pom.xml index 6428a953679..254a0e06e54 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.extensions/pom.xml +++ b/components/device-mgt/org.wso2.carbon.device.mgt.extensions/pom.xml @@ -22,7 +22,7 @@ device-mgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.extensions/src/test/resources/sql-files/h2.sql b/components/device-mgt/org.wso2.carbon.device.mgt.extensions/src/test/resources/sql-files/h2.sql index ef684a66851..d99e112c0ca 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.extensions/src/test/resources/sql-files/h2.sql +++ b/components/device-mgt/org.wso2.carbon.device.mgt.extensions/src/test/resources/sql-files/h2.sql @@ -120,7 +120,19 @@ CREATE TABLE IF NOT EXISTS DM_ENROLMENT ( DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT uk_dm_device_enrolment UNIQUE (DEVICE_ID, OWNER, OWNERSHIP, TENANT_ID) ); - +CREATE TABLE IF NOT EXISTS DM_DEVICE_STATUS ( + ID INTEGER AUTO_INCREMENT NOT NULL, + ENROLMENT_ID INTEGER NOT NULL, + DEVICE_ID INTEGER NOT NULL, + STATUS VARCHAR(50) DEFAULT NULL, + UPDATE_TIME TIMESTAMP DEFAULT NULL, + CHANGED_BY VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT fk_dm_device_status_device FOREIGN KEY (DEVICE_ID) REFERENCES + DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT fk_dm_device_status_enrolment FOREIGN KEY (ENROLMENT_ID) REFERENCES + DM_ENROLMENT (ID) ON DELETE NO ACTION ON UPDATE NO ACTION +); CREATE TABLE IF NOT EXISTS DM_ENROLMENT_OP_MAPPING ( ID INTEGER AUTO_INCREMENT NOT NULL, ENROLMENT_ID INTEGER NOT NULL, diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.url.printer/pom.xml b/components/device-mgt/org.wso2.carbon.device.mgt.url.printer/pom.xml index a36f167e833..168e99f4858 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.url.printer/pom.xml +++ b/components/device-mgt/org.wso2.carbon.device.mgt.url.printer/pom.xml @@ -23,7 +23,7 @@ device-mgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/device-mgt/pom.xml b/components/device-mgt/pom.xml index f5c20388c5a..5e9565dd276 100644 --- a/components/device-mgt/pom.xml +++ b/components/device-mgt/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../../pom.xml diff --git a/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/pom.xml b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/pom.xml index e263aafe5a6..d842f020d9c 100644 --- a/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/pom.xml +++ b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt heartbeat-management - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/heartbeat-management/pom.xml b/components/heartbeat-management/pom.xml index 0e98a777953..00dcc3a169e 100644 --- a/components/heartbeat-management/pom.xml +++ b/components/heartbeat-management/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../../pom.xml diff --git a/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/pom.xml b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/pom.xml index 9cc0e926d75..c1b6a61b47f 100644 --- a/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/pom.xml +++ b/components/identity-extensions/org.wso2.carbon.device.mgt.oauth.extensions/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt identity-extensions - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/identity-extensions/org.wso2.carbon.identity.jwt.client.extension/pom.xml b/components/identity-extensions/org.wso2.carbon.identity.jwt.client.extension/pom.xml index ba3450e72c4..1fe76306653 100644 --- a/components/identity-extensions/org.wso2.carbon.identity.jwt.client.extension/pom.xml +++ b/components/identity-extensions/org.wso2.carbon.identity.jwt.client.extension/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt identity-extensions - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/identity-extensions/pom.xml b/components/identity-extensions/pom.xml index 89396a40c29..bea3ed30cd9 100644 --- a/components/identity-extensions/pom.xml +++ b/components/identity-extensions/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../../pom.xml diff --git a/components/policy-mgt/org.wso2.carbon.policy.decision.point/pom.xml b/components/policy-mgt/org.wso2.carbon.policy.decision.point/pom.xml index f93cf53b3b0..6397a558a00 100644 --- a/components/policy-mgt/org.wso2.carbon.policy.decision.point/pom.xml +++ b/components/policy-mgt/org.wso2.carbon.policy.decision.point/pom.xml @@ -3,7 +3,7 @@ org.wso2.carbon.devicemgt policy-mgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/policy-mgt/org.wso2.carbon.policy.information.point/pom.xml b/components/policy-mgt/org.wso2.carbon.policy.information.point/pom.xml index af52a0c72e0..0c4862a266f 100644 --- a/components/policy-mgt/org.wso2.carbon.policy.information.point/pom.xml +++ b/components/policy-mgt/org.wso2.carbon.policy.information.point/pom.xml @@ -3,7 +3,7 @@ org.wso2.carbon.devicemgt policy-mgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/policy-mgt/org.wso2.carbon.policy.mgt.common/pom.xml b/components/policy-mgt/org.wso2.carbon.policy.mgt.common/pom.xml index e2b5c457f1e..8126f754c91 100644 --- a/components/policy-mgt/org.wso2.carbon.policy.mgt.common/pom.xml +++ b/components/policy-mgt/org.wso2.carbon.policy.mgt.common/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt policy-mgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/pom.xml b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/pom.xml index 427034bc6c9..a7b2a911c0b 100644 --- a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/pom.xml +++ b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt policy-mgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/java/org/wso2/carbon/policy/mgt/core/BasePolicyManagementDAOTest.java b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/java/org/wso2/carbon/policy/mgt/core/BasePolicyManagementDAOTest.java index 450313aac0e..b790106c682 100644 --- a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/java/org/wso2/carbon/policy/mgt/core/BasePolicyManagementDAOTest.java +++ b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/java/org/wso2/carbon/policy/mgt/core/BasePolicyManagementDAOTest.java @@ -162,6 +162,8 @@ public abstract class BasePolicyManagementDAOTest { .SUPER_TENANT_DOMAIN_NAME); PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(MultitenantConstants.SUPER_TENANT_ID); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername("admin"); + } private DataSource getDataSource(DataSourceConfig config) { diff --git a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/resources/sql/CreateH2TestDB.sql b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/resources/sql/CreateH2TestDB.sql index 4c59adb953d..0c5a71aa73d 100644 --- a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/resources/sql/CreateH2TestDB.sql +++ b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/resources/sql/CreateH2TestDB.sql @@ -107,7 +107,20 @@ CREATE TABLE IF NOT EXISTS DM_ENROLMENT ( CONSTRAINT fk_dm_device_enrolment FOREIGN KEY (DEVICE_ID) REFERENCES DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION ); - +DROP TABLE IF EXISTS DM_DEVICE_STATUS; +CREATE TABLE IF NOT EXISTS DM_DEVICE_STATUS ( + ID INTEGER AUTO_INCREMENT NOT NULL, + ENROLMENT_ID INTEGER NOT NULL, + DEVICE_ID INTEGER NOT NULL, + STATUS VARCHAR(50) DEFAULT NULL, + UPDATE_TIME TIMESTAMP DEFAULT NULL, + CHANGED_BY VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT fk_dm_device_status_device FOREIGN KEY (DEVICE_ID) REFERENCES + DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT fk_dm_device_status_enrolment FOREIGN KEY (ENROLMENT_ID) REFERENCES + DM_ENROLMENT (ID) ON DELETE NO ACTION ON UPDATE NO ACTION +); DROP TABLE IF EXISTS DM_ENROLMENT_OP_MAPPING; CREATE TABLE IF NOT EXISTS DM_ENROLMENT_OP_MAPPING ( ID INTEGER AUTO_INCREMENT NOT NULL, diff --git a/components/policy-mgt/pom.xml b/components/policy-mgt/pom.xml index b653b61a19d..487e9428161 100644 --- a/components/policy-mgt/pom.xml +++ b/components/policy-mgt/pom.xml @@ -23,7 +23,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../../pom.xml diff --git a/components/transport-mgt/email-sender/org.wso2.carbon.email.sender.core/pom.xml b/components/transport-mgt/email-sender/org.wso2.carbon.email.sender.core/pom.xml index 40d44a85000..fc042037541 100644 --- a/components/transport-mgt/email-sender/org.wso2.carbon.email.sender.core/pom.xml +++ b/components/transport-mgt/email-sender/org.wso2.carbon.email.sender.core/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt email-sender - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/transport-mgt/email-sender/pom.xml b/components/transport-mgt/email-sender/pom.xml index 6bae00732f7..49b0d1b4d3d 100644 --- a/components/transport-mgt/email-sender/pom.xml +++ b/components/transport-mgt/email-sender/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt transport-mgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/transport-mgt/pom.xml b/components/transport-mgt/pom.xml index cabafa2cf53..b5ade0b0c3f 100644 --- a/components/transport-mgt/pom.xml +++ b/components/transport-mgt/pom.xml @@ -3,7 +3,7 @@ carbon-devicemgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../../pom.xml diff --git a/components/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.api/pom.xml b/components/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.api/pom.xml index 2a216e425de..408abff8782 100644 --- a/components/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.api/pom.xml +++ b/components/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.api/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt sms-handler - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.common/pom.xml b/components/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.common/pom.xml index bf6029c1d6e..786ca943c11 100644 --- a/components/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.common/pom.xml +++ b/components/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.common/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt sms-handler - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.core/pom.xml b/components/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.core/pom.xml index f1b5a2e63cf..7ce23d075e1 100644 --- a/components/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.core/pom.xml +++ b/components/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.core/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt sms-handler - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/transport-mgt/sms-handler/pom.xml b/components/transport-mgt/sms-handler/pom.xml index 4cf46974df2..7037b73e5c8 100644 --- a/components/transport-mgt/sms-handler/pom.xml +++ b/components/transport-mgt/sms-handler/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt transport-mgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/pom.xml b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/pom.xml index 4fb0ecaf799..d4f5d22e055 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/pom.xml +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/pom.xml @@ -21,7 +21,7 @@ ui-request-interceptor io.entgra.devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT 4.0.0 @@ -102,6 +102,17 @@ org.wso2.carbon.apimgt.application.extension provided + + org.wso2.carbon.devicemgt + io.entgra.analytics.mgt.grafana.proxy.core + provided + + + org.apache.httpcomponents.wso2 + httpclient + + + org.wso2.carbon.devicemgt org.wso2.carbon.device.mgt.core diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/DefaultTokenHandler.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/DefaultTokenHandler.java index 9667c250008..6c182b9e1d0 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/DefaultTokenHandler.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/DefaultTokenHandler.java @@ -140,6 +140,7 @@ public class DefaultTokenHandler extends HttpServlet { ProxyResponse proxyResponse = new ProxyResponse(); proxyResponse.setCode(HttpStatus.SC_OK); + proxyResponse.setStatus(ProxyResponse.Status.SUCCESS); proxyResponse.setData(payload); return proxyResponse; } diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/GrafanaHandler.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/GrafanaHandler.java new file mode 100644 index 00000000000..6cd8872eae0 --- /dev/null +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/GrafanaHandler.java @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2019, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.entgra.ui.request.interceptor; + +import io.entgra.analytics.mgt.grafana.proxy.common.exception.GrafanaManagementException; +import io.entgra.analytics.mgt.grafana.proxy.core.exception.GrafanaEnvVariablesNotDefined; +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaUtil; +import io.entgra.ui.request.interceptor.beans.AuthData; +import io.entgra.ui.request.interceptor.beans.ProxyResponse; +import io.entgra.ui.request.interceptor.util.GrafanaHandlerUtil; +import io.entgra.ui.request.interceptor.util.HandlerConstants; +import io.entgra.ui.request.interceptor.util.HandlerUtil; +import org.apache.commons.fileupload.FileUploadException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.HttpHeaders; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.*; +import org.apache.http.impl.client.CloseableHttpClient; +import org.wso2.carbon.device.mgt.core.common.util.HttpUtil; + +import javax.servlet.annotation.MultipartConfig; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import javax.ws.rs.ProcessingException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.nio.charset.StandardCharsets; + +@MultipartConfig +@WebServlet( + name = "GrafanaRequestHandler", + description = "This servlet intercepts the iframe requests initiated from the user interface and validate before" + + " forwarding to the backend", + urlPatterns = { + "/grafana/*" + } +) +public class GrafanaHandler extends HttpServlet { + private static final Log log = LogFactory.getLog(GrafanaHandler.class); + private static final long serialVersionUID = -6508020875358160165L; + private static AuthData authData; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) { + try { + if (validateRequest(req, resp)) { + HttpGet grafanaRequest = new HttpGet(); + HandlerUtil.copyRequestHeaders(req, grafanaRequest, true); + if (!GrafanaUtil.isGrafanaAPI(req.getRequestURI())) { + proxyPassGrafanaRequest(grafanaRequest, resp, req); + return; + } + grafanaRequest.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BEARER + authData.getAccessToken()); + ProxyResponse grafanaAPIResponse = executeGrafanaAPIRequest(grafanaRequest, req); + String keyManagerUrl = HandlerUtil.getKeyManagerUrl(req.getScheme()); + if (HandlerConstants.TOKEN_IS_EXPIRED.equals(grafanaAPIResponse.getExecutorResponse())) { + grafanaAPIResponse = HandlerUtil.retryRequestWithRefreshedToken(req, grafanaRequest, keyManagerUrl); + if (!HandlerUtil.isResponseSuccessful(grafanaAPIResponse)) { + HandlerUtil.handleError(resp, grafanaAPIResponse); + return; + } + } + if (grafanaAPIResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { + if (grafanaAPIResponse.getCode() == HttpStatus.SC_UNAUTHORIZED) { + grafanaAPIResponse = HandlerUtil.retryRequestWithRefreshedToken(req, grafanaRequest, keyManagerUrl); + if (!HandlerUtil.isResponseSuccessful(grafanaAPIResponse)) { + HandlerUtil.handleError(resp, grafanaAPIResponse); + return; + } + } else { + log.error("Error occurred while invoking the GET API endpoint."); + HandlerUtil.handleError(resp, grafanaAPIResponse); + return; + } + } + handleSuccess(resp, grafanaAPIResponse); + } + } catch (ProcessingException e) { + String msg = "Grafana server is down or invalid grafana dashboard url provided"; + log.error(msg, e); + } + catch (IOException e) { + log.error("Error occurred when processing Iframe request.", e); + } + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) { + try { + if (validateRequest(req, resp)) { + HttpPost grafanaRequest = new HttpPost(); + HandlerUtil.generateRequestEntity(req, grafanaRequest); + HandlerUtil.copyRequestHeaders(req, grafanaRequest, true); + if (!GrafanaUtil.isGrafanaAPI(req.getRequestURI())) { + proxyPassGrafanaRequest(grafanaRequest, resp, req); + return; + } + grafanaRequest.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BEARER + authData.getAccessToken()); + ProxyResponse grafanaAPIResponse = executeGrafanaAPIRequest(grafanaRequest, req); + if (HandlerConstants.TOKEN_IS_EXPIRED.equals(grafanaAPIResponse.getExecutorResponse())) { + String keyManagerUrl = HandlerUtil.getKeyManagerUrl(req.getScheme()); + grafanaAPIResponse = HandlerUtil.retryRequestWithRefreshedToken(req, grafanaRequest, keyManagerUrl); + if (!HandlerUtil.isResponseSuccessful(grafanaAPIResponse)) { + handleError(resp, grafanaAPIResponse); + return; + } + } + if (grafanaAPIResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { + log.error("Error occurred while invoking the POST API endpoint."); + handleError(resp, grafanaAPIResponse); + return; + } + handleSuccess(resp, grafanaAPIResponse); + } + } catch (FileUploadException e) { + log.error("Error occurred when processing Multipart POST request.", e); + } catch (ProcessingException e) { + String msg = "Grafana server is down or invalid grafana dashboard url provided"; + log.error(msg, e); + } catch (IOException e) { + log.error("Error occurred when processing Iframe request.", e); + } + } + + private void handleSuccess(HttpServletResponse resp, ProxyResponse grafanaAPIResponse) throws IOException{ + String contentType = HandlerUtil.getHeaderValue(HttpHeaders.CONTENT_TYPE, grafanaAPIResponse.getHeaders()); + resp.setContentType(contentType); + resp.setStatus(grafanaAPIResponse.getCode()); + addXFrameOptionsHeaders(resp); + resp.setCharacterEncoding(StandardCharsets.UTF_8.name()); + resp.getWriter().print(grafanaAPIResponse.getData()); + } + + private void handleError(HttpServletResponse resp, int errCode, String errMsg) throws IOException { + resp.sendError(errCode, errMsg); + } + + private void handleError(HttpServletResponse resp, ProxyResponse proxyResponse) throws IOException { + resp.sendError(proxyResponse.getCode()); + } + + /*** + * Validates the incoming request. + * + * @param req {@link HttpServletRequest} + * @param resp {@link HttpServletResponse} + * @return If request is a valid one, returns TRUE, otherwise return FALSE + * @throws IOException If and error occurs while witting error response to client side + */ + private boolean validateRequest(HttpServletRequest req, HttpServletResponse resp) + throws IOException { + + if (req.getMethod() == null) { + String errMsg = "Bad Request, Request method is empty"; + log.error(errMsg); + handleError(resp, HttpStatus.SC_BAD_REQUEST, errMsg); + return false; + } + if (HandlerUtil.isPropertyDefined(HandlerConstants.IOT_REPORTING_WEBAPP_HOST_ENV_VAR)) { + String errMsg = "Reporting Endpoint is not defined in the iot-server.sh properly."; + log.error(errMsg); + resp.sendError(500, errMsg); + return false; + } + + HttpSession session = req.getSession(false); + if (session == null) { + String errMsg = "Unauthorized, You are not logged in. Please log in to the portal"; + log.error(errMsg); + handleError(resp, HttpStatus.SC_UNAUTHORIZED, errMsg); + return false; + } + + authData = (AuthData) session.getAttribute(HandlerConstants.SESSION_AUTH_DATA_KEY); + if (authData == null) { + String errMsg = "Unauthorized, Access token not found in the current session"; + log.error(errMsg); + handleError(resp, HttpStatus.SC_UNAUTHORIZED, errMsg); + return false; + } + + return true; + } + + private ProxyResponse executeGrafanaAPIRequest(HttpRequestBase requestBase, HttpServletRequest request) + throws IOException { + URI grafanaUri = HttpUtil.createURI(generateGrafanaAPIUrl(request)); + requestBase.setURI(grafanaUri); + return HandlerUtil.execute(requestBase); + } + + private String generateGrafanaAPIUrl(HttpServletRequest request) { + String apiBase = generateGrafanaAPIBase(request); + String grafanaUri = getURIWithQuery(request); + return apiBase + grafanaUri; + } + + private String generateGrafanaAPIBase(HttpServletRequest request) { + return HandlerUtil.getIOTGatewayBase(request) + HandlerConstants.GRAFANA_API; + } + + private String getURIWithQuery(HttpServletRequest request) { + String uri = request.getPathInfo(); + if (request.getQueryString() != null) { + uri += HandlerConstants.URI_QUERY_SEPARATOR + request.getQueryString(); + } + return uri; + } + private void proxyPassGrafanaRequest(HttpRequestBase requestBase, HttpServletResponse response, + HttpServletRequest request) throws IOException { + try (CloseableHttpClient client = HandlerUtil.getHttpClient()) { + String grafanaUriStr = GrafanaHandlerUtil.generateGrafanaUrl(HttpUtil.createURI(getURIWithQuery(request)), + GrafanaUtil.getGrafanaHTTPBase(request.getScheme())); + URI grafanaURI = HttpUtil.createURI(grafanaUriStr); + requestBase.setURI(grafanaURI); + HttpResponse grafanaResponse = invokeGrafanaAPI(client, requestBase); + forwardGrafanaResponse(grafanaResponse, response); + } catch (GrafanaEnvVariablesNotDefined e) { + handleError(response, HttpStatus.SC_INTERNAL_SERVER_ERROR, e.getMessage()); + } catch (GrafanaManagementException e) { + String errMsg = "Error occurred while retrieving grafana user credentials"; + log.error(errMsg, e); + handleError(response, HttpStatus.SC_INTERNAL_SERVER_ERROR, errMsg); + } + } + + private HttpResponse invokeGrafanaAPI(HttpClient client, HttpRequestBase request) throws IOException, GrafanaManagementException { + setBasicAuthHeader(request); + return client.execute(request); + } + + private void setBasicAuthHeader(HttpRequestBase request) throws GrafanaManagementException { + String basicAuth = GrafanaUtil.getBasicAuthBase64Header(); + request.setHeader(HttpHeaders.AUTHORIZATION, basicAuth); + } + + private void forwardGrafanaResponse(HttpResponse grafanaResponse, HttpServletResponse response) throws IOException { + InputStream responseContent = grafanaResponse.getEntity().getContent(); + String grafanaContentType = HandlerUtil.getMemeType(grafanaResponse); + response.setHeader(HttpHeaders.CONTENT_TYPE, grafanaContentType); + addXFrameOptionsHeaders(response); + byte[] buffer = new byte[10240]; + try (InputStream input = responseContent; OutputStream output = response.getOutputStream()) { + for (int length = 0; (length = input.read(buffer)) > 0;) { + output.write(buffer, 0, length); + } + } + + } + + private void addXFrameOptionsHeaders(HttpServletResponse response) { + response.setHeader(HandlerConstants.X_FRAME_OPTIONS, HandlerConstants.X_FRAME_OPTIONS_SAMEORIGIN); + } + +} diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/InvokerHandler.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/InvokerHandler.java index 7cfe352548d..045aac0b5ad 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/InvokerHandler.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/InvokerHandler.java @@ -18,37 +18,21 @@ package io.entgra.ui.request.interceptor; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; import io.entgra.ui.request.interceptor.beans.AuthData; import io.entgra.ui.request.interceptor.util.HandlerConstants; import io.entgra.ui.request.interceptor.util.HandlerUtil; -import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadException; -import org.apache.commons.fileupload.disk.DiskFileItemFactory; -import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.HttpHeaders; import org.apache.http.HttpStatus; import org.apache.http.client.methods.HttpDelete; -import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpHead; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; -import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.cookie.SM; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.InputStreamEntity; -import org.apache.http.entity.StringEntity; -import org.apache.http.entity.mime.HttpMultipartMode; -import org.apache.http.entity.mime.MultipartEntityBuilder; -import org.apache.http.entity.mime.content.InputStreamBody; import io.entgra.ui.request.interceptor.beans.ProxyResponse; - import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; @@ -56,8 +40,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; -import java.util.Enumeration; -import java.util.List; @MultipartConfig @WebServlet( @@ -79,14 +61,15 @@ public class InvokerHandler extends HttpServlet { protected void doPost(HttpServletRequest req, HttpServletResponse resp) { try { if (validateRequest(req, resp)) { - HttpPost postRequest = new HttpPost(generateBackendRequestURL(req)); - generateRequestEntity(req, postRequest); + HttpPost postRequest = new HttpPost(HandlerUtil.generateBackendRequestURL(req, apiEndpoint)); + HandlerUtil.generateRequestEntity(req, postRequest); postRequest.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BEARER + authData.getAccessToken()); ProxyResponse proxyResponse = HandlerUtil.execute(postRequest); if (HandlerConstants.TOKEN_IS_EXPIRED.equals(proxyResponse.getExecutorResponse())) { - proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, resp, postRequest, kmManagerUrl); - if (proxyResponse == null) { + proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, postRequest, kmManagerUrl); + if (!HandlerUtil.isResponseSuccessful(proxyResponse)) { + HandlerUtil.handleError(resp, proxyResponse); return; } } @@ -108,19 +91,24 @@ public class InvokerHandler extends HttpServlet { protected void doGet(HttpServletRequest req, HttpServletResponse resp) { try { if (validateRequest(req, resp)) { - HttpGet getRequest = new HttpGet(generateBackendRequestURL(req)); - copyRequestHeaders(req, getRequest, false); + HttpGet getRequest = new HttpGet(HandlerUtil.generateBackendRequestURL(req, apiEndpoint)); + HandlerUtil.copyRequestHeaders(req, getRequest, false); getRequest.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BEARER + authData.getAccessToken()); ProxyResponse proxyResponse = HandlerUtil.execute(getRequest); if (HandlerConstants.TOKEN_IS_EXPIRED.equals(proxyResponse.getExecutorResponse())) { - proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, resp, getRequest, kmManagerUrl); - if (proxyResponse == null) { + proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, getRequest, kmManagerUrl); + if (!HandlerUtil.isResponseSuccessful(proxyResponse)) { + HandlerUtil.handleError(resp, proxyResponse); return; } } if (proxyResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { if (proxyResponse.getCode() == HttpStatus.SC_UNAUTHORIZED) { - proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, resp, getRequest, kmManagerUrl); + proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, getRequest, kmManagerUrl); + if (!HandlerUtil.isResponseSuccessful(proxyResponse)) { + HandlerUtil.handleError(resp, proxyResponse); + return; + } } else { log.error("Error occurred while invoking the GET API endpoint."); HandlerUtil.handleError(resp, proxyResponse); @@ -138,13 +126,14 @@ public class InvokerHandler extends HttpServlet { protected void doHead(HttpServletRequest req, HttpServletResponse resp) { try { if (validateRequest(req, resp)) { - HttpHead headRequest = new HttpHead(generateBackendRequestURL(req)); - copyRequestHeaders(req, headRequest, false); + HttpHead headRequest = new HttpHead(HandlerUtil.generateBackendRequestURL(req, apiEndpoint)); + HandlerUtil.copyRequestHeaders(req, headRequest, false); headRequest.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BEARER + authData.getAccessToken()); ProxyResponse proxyResponse = HandlerUtil.execute(headRequest); if (HandlerConstants.TOKEN_IS_EXPIRED.equals(proxyResponse.getExecutorResponse())) { - proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, resp, headRequest, kmManagerUrl); - if (proxyResponse == null) { + proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, headRequest, kmManagerUrl); + if (!HandlerUtil.isResponseSuccessful(proxyResponse)) { + HandlerUtil.handleError(resp, proxyResponse); return; } } @@ -164,14 +153,15 @@ public class InvokerHandler extends HttpServlet { protected void doPut(HttpServletRequest req, HttpServletResponse resp) { try { if (validateRequest(req, resp)) { - HttpPut putRequest = new HttpPut(generateBackendRequestURL(req)); - generateRequestEntity(req, putRequest); + HttpPut putRequest = new HttpPut(HandlerUtil.generateBackendRequestURL(req, apiEndpoint)); + HandlerUtil.generateRequestEntity(req, putRequest); putRequest.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BEARER + authData.getAccessToken()); ProxyResponse proxyResponse = HandlerUtil.execute(putRequest); if (HandlerConstants.TOKEN_IS_EXPIRED.equals(proxyResponse.getExecutorResponse())) { - proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, resp, putRequest, kmManagerUrl); - if (proxyResponse == null) { + proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, putRequest, kmManagerUrl); + if (!HandlerUtil.isResponseSuccessful(proxyResponse)) { + HandlerUtil.handleError(resp, proxyResponse); return; } } @@ -193,13 +183,14 @@ public class InvokerHandler extends HttpServlet { protected void doDelete(HttpServletRequest req, HttpServletResponse resp) { try { if (validateRequest(req, resp)) { - HttpDelete deleteRequest = new HttpDelete(generateBackendRequestURL(req)); - copyRequestHeaders(req, deleteRequest, false); + HttpDelete deleteRequest = new HttpDelete(HandlerUtil.generateBackendRequestURL(req, apiEndpoint)); + HandlerUtil.copyRequestHeaders(req, deleteRequest, false); deleteRequest.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BEARER + authData.getAccessToken()); ProxyResponse proxyResponse = HandlerUtil.execute(deleteRequest); if (HandlerConstants.TOKEN_IS_EXPIRED.equals(proxyResponse.getExecutorResponse())) { - proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, resp, deleteRequest, kmManagerUrl); - if (proxyResponse == null) { + proxyResponse = HandlerUtil.retryRequestWithRefreshedToken(req, deleteRequest, kmManagerUrl); + if (!HandlerUtil.isResponseSuccessful(proxyResponse)) { + HandlerUtil.handleError(resp, proxyResponse); return; } } @@ -215,83 +206,6 @@ public class InvokerHandler extends HttpServlet { } } - /** - * Generate te request entity for POST and PUT requests from the incoming request. - * - * @param req incoming {@link HttpServletRequest}. - * @param proxyRequest proxy request instance. - * @throws FileUploadException If unable to parse the incoming request for multipart content extraction. - * @throws IOException If error occurred while generating the request body. - */ - private void generateRequestEntity(HttpServletRequest req, HttpEntityEnclosingRequestBase proxyRequest) - throws FileUploadException, IOException { - if (ServletFileUpload.isMultipartContent(req)) { - ServletFileUpload servletFileUpload = new ServletFileUpload(new DiskFileItemFactory()); - List fileItemList = servletFileUpload.parseRequest(req); - MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create(); - entityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); - for (FileItem item: fileItemList) { - if (!item.isFormField()) { - entityBuilder.addPart(item.getFieldName(), new InputStreamBody(item.getInputStream(), - ContentType.create(item.getContentType()), item.getName())); - } else { - entityBuilder.addTextBody(item.getFieldName(), item.getString()); - } - } - proxyRequest.setEntity(entityBuilder.build()); - copyRequestHeaders(req, proxyRequest, false); - } else { - if (StringUtils.isNotEmpty(req.getHeader(HttpHeaders.CONTENT_LENGTH)) || - StringUtils.isNotEmpty(req.getHeader(HttpHeaders.TRANSFER_ENCODING))) { - InputStreamEntity entity = new InputStreamEntity(req.getInputStream(), - Long.parseLong(req.getHeader(HttpHeaders.CONTENT_LENGTH))); - proxyRequest.setEntity(entity); - } - copyRequestHeaders(req, proxyRequest, true); - } - } - - /** - * Generates the target URL for the proxy request. - * - * @param req incoming {@link HttpServletRequest} - * @return Target URL - */ - private String generateBackendRequestURL(HttpServletRequest req) { - StringBuilder urlBuilder = new StringBuilder(); - urlBuilder.append(apiEndpoint).append(HandlerConstants.API_COMMON_CONTEXT) - .append(req.getPathInfo().replace(" ", "%20")); - if (StringUtils.isNotEmpty(req.getQueryString())) { - urlBuilder.append("?").append(req.getQueryString()); - } - return urlBuilder.toString(); - } - - /** - * Copy incoming request headers to the proxy request. - * - * @param req incoming {@link HttpServletRequest} - * @param httpRequest proxy request instance. - * @param preserveContentType true if content type header needs to be preserved. - * This should be set to false when handling multipart requests as Http - * client will generate the Content-Type header automatically. - */ - private void copyRequestHeaders(HttpServletRequest req, HttpRequestBase httpRequest, boolean preserveContentType) { - Enumeration headerNames = req.getHeaderNames(); - while (headerNames.hasMoreElements()) { - String headerName = headerNames.nextElement(); - if (headerName.equalsIgnoreCase(HttpHeaders.CONTENT_LENGTH) || - headerName.equalsIgnoreCase(SM.COOKIE) || - (!preserveContentType && headerName.equalsIgnoreCase(HttpHeaders.CONTENT_TYPE))) { - continue; - } - Enumeration headerValues = req.getHeaders(headerName); - while (headerValues.hasMoreElements()) { - httpRequest.setHeader(headerName, headerValues.nextElement()); - } - } - } - /*** * Validates the incoming request. * @@ -305,8 +219,7 @@ public class InvokerHandler extends HttpServlet { apiEndpoint = req.getScheme() + HandlerConstants.SCHEME_SEPARATOR + System.getProperty(HandlerConstants.IOT_GW_HOST_ENV_VAR) + HandlerConstants.COLON + HandlerUtil.getGatewayPort(req.getScheme()); - kmManagerUrl = req.getScheme() + HandlerConstants.SCHEME_SEPARATOR + System.getProperty(HandlerConstants.IOT_KM_HOST_ENV_VAR) - + HandlerConstants.COLON + HandlerUtil.getKeymanagerPort(req.getScheme()); + kmManagerUrl = HandlerUtil.getKeyManagerUrl(req.getScheme()); if (HandlerConstants.REPORTS.equalsIgnoreCase(req.getHeader(HandlerConstants.APP_NAME))){ apiEndpoint = System.getProperty("iot.reporting.webapp.host"); @@ -339,107 +252,4 @@ public class InvokerHandler extends HttpServlet { return true; } - /** - * Retry request again after refreshing the access token - * - * @param req incoming {@link HttpServletRequest} - * @param resp resp {@link HttpServletResponse} - * @param httpRequest subclass of {@link HttpRequestBase} related to the current request. - * @return {@link ProxyResponse} if successful and null if failed. - * @throws IOException If an error occurs when try to retry the request. - */ - private ProxyResponse retryRequestWithRefreshedToken(HttpServletRequest req, HttpServletResponse resp, - HttpRequestBase httpRequest) throws IOException { - if (refreshToken(req, resp)) { - httpRequest.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BEARER + authData.getAccessToken()); - ProxyResponse proxyResponse = HandlerUtil.execute(httpRequest); - if (proxyResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { - log.error("Error occurred while invoking the API after refreshing the token."); - HandlerUtil.handleError(resp, proxyResponse); - return null; - } - return proxyResponse; - } - return null; - } - - /*** - * This method is responsible to get the refresh token - * - * @param req {@link HttpServletRequest} - * @param resp {@link HttpServletResponse} - * @return If successfully renew tokens, returns TRUE otherwise return FALSE - * @throws IOException If an error occurs while witting error response to client side or invoke token renewal API - */ - private static boolean refreshToken(HttpServletRequest req, HttpServletResponse resp) - throws IOException { - if (log.isDebugEnabled()) { - log.debug("refreshing the token"); - } - - HttpPost tokenEndpoint = new HttpPost( - kmManagerUrl + HandlerConstants.TOKEN_ENDPOINT); - HttpSession session = req.getSession(false); - if (session == null) { - log.error("Couldn't find a session, hence it is required to login and proceed."); - handleError(resp, HttpStatus.SC_UNAUTHORIZED); - return false; - } - - StringEntity tokenEndpointPayload = new StringEntity( - "grant_type=refresh_token&refresh_token=" + authData.getRefreshToken() + "&scope=PRODUCTION", - ContentType.APPLICATION_FORM_URLENCODED); - - tokenEndpoint.setEntity(tokenEndpointPayload); - String encodedClientApp = authData.getEncodedClientApp(); - tokenEndpoint.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BASIC + - encodedClientApp); - tokenEndpoint.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_FORM_URLENCODED.toString()); - - ProxyResponse tokenResultResponse = HandlerUtil.execute(tokenEndpoint); - if (tokenResultResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { - log.error("Error occurred while refreshing access token."); - HandlerUtil.handleError(resp, tokenResultResponse); - return false; - } - - JsonParser jsonParser = new JsonParser(); - JsonElement jTokenResult = jsonParser.parse(tokenResultResponse.getData()); - - if (jTokenResult.isJsonObject()) { - JsonObject jTokenResultAsJsonObject = jTokenResult.getAsJsonObject(); - AuthData newAuthData = new AuthData(); - - newAuthData.setAccessToken(jTokenResultAsJsonObject.get("access_token").getAsString()); - newAuthData.setRefreshToken(jTokenResultAsJsonObject.get("refresh_token").getAsString()); - newAuthData.setScope(jTokenResultAsJsonObject.get("scope").getAsString()); - newAuthData.setClientId(authData.getClientId()); - newAuthData.setClientSecret(authData.getClientSecret()); - newAuthData.setEncodedClientApp(authData.getEncodedClientApp()); - newAuthData.setUsername(authData.getUsername()); - authData = newAuthData; - session.setAttribute(HandlerConstants.SESSION_AUTH_DATA_KEY, newAuthData); - return true; - } - - log.error("Error Occurred in token renewal process."); - handleError(resp, HttpStatus.SC_INTERNAL_SERVER_ERROR); - return false; - } - - /** - * Handle error requests - * - * @param resp {@link HttpServletResponse} - * @param errorCode HTTP error status code - * @throws IOException If error occurred when trying to send the error response. - */ - private static void handleError(HttpServletResponse resp, int errorCode) - throws IOException { - ProxyResponse proxyResponse = new ProxyResponse(); - proxyResponse.setCode(errorCode); - proxyResponse.setExecutorResponse( - HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + HandlerUtil.getStatusKey(errorCode)); - HandlerUtil.handleError(resp, proxyResponse); - } } diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/LoginHandler.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/LoginHandler.java index 953fbf5a5c3..f41f94a7647 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/LoginHandler.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/LoginHandler.java @@ -123,6 +123,7 @@ public class LoginHandler extends HttpServlet { if (getTokenAndPersistInSession(req, resp, clientId, clientSecret, encodedClientApp, scopes)) { ProxyResponse proxyResponse = new ProxyResponse(); + proxyResponse.setStatus(ProxyResponse.Status.SUCCESS); proxyResponse.setCode(HttpStatus.SC_OK); HandlerUtil.handleSuccess(resp, proxyResponse); return; @@ -210,7 +211,7 @@ public class LoginHandler extends HttpServlet { + HandlerConstants.COLON + HandlerUtil.getCorePort(req.getScheme()); uiConfigUrl = iotCoreUrl + HandlerConstants.UI_CONFIG_ENDPOINT; kmManagerUrl = req.getScheme() + HandlerConstants.SCHEME_SEPARATOR + System.getProperty(HandlerConstants.IOT_KM_HOST_ENV_VAR) - + HandlerConstants.COLON + HandlerUtil.getKeymanagerPort(req.getScheme()); + + HandlerConstants.COLON + HandlerUtil.getKeyManagerPort(req.getScheme()); if (username == null || password == null) { String msg = "Invalid login request. Username or Password is not received for login request."; diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/LogoutHandler.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/LogoutHandler.java index 6cb7aed7571..bbf82164a25 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/LogoutHandler.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/LogoutHandler.java @@ -45,6 +45,7 @@ public class LogoutHandler extends HttpServlet { } ProxyResponse proxyResponse = new ProxyResponse(); + proxyResponse.setStatus(ProxyResponse.Status.SUCCESS); proxyResponse.setCode(HttpStatus.SC_OK); try { HandlerUtil.handleSuccess(resp, proxyResponse); diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/OTPInvokerHandler.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/OTPInvokerHandler.java index c852cce259c..44dbe7882e1 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/OTPInvokerHandler.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/OTPInvokerHandler.java @@ -20,37 +20,22 @@ package io.entgra.ui.request.interceptor; import io.entgra.ui.request.interceptor.util.HandlerConstants; import io.entgra.ui.request.interceptor.util.HandlerUtil; -import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadException; -import org.apache.commons.fileupload.disk.DiskFileItemFactory; -import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.http.HttpHeaders; import org.apache.http.HttpStatus; import org.apache.http.client.methods.HttpDelete; -import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpHead; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; -import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.cookie.SM; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.InputStreamEntity; -import org.apache.http.entity.mime.HttpMultipartMode; -import org.apache.http.entity.mime.MultipartEntityBuilder; -import org.apache.http.entity.mime.content.InputStreamBody; import io.entgra.ui.request.interceptor.beans.ProxyResponse; - import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; -import java.util.Enumeration; -import java.util.List; @WebServlet( name = "OTPRequestHandlerServlet", @@ -70,7 +55,7 @@ public class OTPInvokerHandler extends HttpServlet { try { if (validateRequest(req, resp)) { HttpPost postRequest = new HttpPost(HandlerUtil.generateBackendRequestURL(req, apiEndpoint)); - generateRequestEntity(req, postRequest); + HandlerUtil.generateRequestEntity(req, postRequest); ProxyResponse proxyResponse = HandlerUtil.execute(postRequest); if (proxyResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { @@ -92,7 +77,7 @@ public class OTPInvokerHandler extends HttpServlet { try { if (validateRequest(req, resp)) { HttpGet getRequest = new HttpGet(HandlerUtil.generateBackendRequestURL(req, apiEndpoint)); - copyRequestHeaders(req, getRequest, false); + HandlerUtil.copyRequestHeaders(req, getRequest, false); ProxyResponse proxyResponse = HandlerUtil.execute(getRequest); if (proxyResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { @@ -112,7 +97,7 @@ public class OTPInvokerHandler extends HttpServlet { try { if (validateRequest(req, resp)) { HttpHead headRequest = new HttpHead(HandlerUtil.generateBackendRequestURL(req, apiEndpoint)); - copyRequestHeaders(req, headRequest, false); + HandlerUtil.copyRequestHeaders(req, headRequest, false); ProxyResponse proxyResponse = HandlerUtil.execute(headRequest); if (proxyResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { @@ -132,7 +117,7 @@ public class OTPInvokerHandler extends HttpServlet { try { if (validateRequest(req, resp)) { HttpPut putRequest = new HttpPut(HandlerUtil.generateBackendRequestURL(req, apiEndpoint)); - generateRequestEntity(req, putRequest); + HandlerUtil.generateRequestEntity(req, putRequest); ProxyResponse proxyResponse = HandlerUtil.execute(putRequest); if (proxyResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { @@ -154,7 +139,7 @@ public class OTPInvokerHandler extends HttpServlet { try { if (validateRequest(req, resp)) { HttpDelete deleteRequest = new HttpDelete(HandlerUtil.generateBackendRequestURL(req, apiEndpoint)); - copyRequestHeaders(req, deleteRequest, false); + HandlerUtil.copyRequestHeaders(req, deleteRequest, false); ProxyResponse proxyResponse = HandlerUtil.execute(deleteRequest); if (proxyResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { @@ -169,67 +154,6 @@ public class OTPInvokerHandler extends HttpServlet { } } - /** - * Generate te request entity for POST and PUT requests from the incoming request. - * - * @param req incoming {@link HttpServletRequest}. - * @param proxyRequest proxy request instance. - * @throws FileUploadException If unable to parse the incoming request for multipart content extraction. - * @throws IOException If error occurred while generating the request body. - */ - private void generateRequestEntity(HttpServletRequest req, HttpEntityEnclosingRequestBase proxyRequest) - throws FileUploadException, IOException { - if (ServletFileUpload.isMultipartContent(req)) { - ServletFileUpload servletFileUpload = new ServletFileUpload(new DiskFileItemFactory()); - List fileItemList = servletFileUpload.parseRequest(req); - MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create(); - entityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); - for (FileItem item: fileItemList) { - if (!item.isFormField()) { - entityBuilder.addPart(item.getFieldName(), new InputStreamBody(item.getInputStream(), - ContentType.create(item.getContentType()), item.getName())); - } else { - entityBuilder.addTextBody(item.getFieldName(), item.getString()); - } - } - proxyRequest.setEntity(entityBuilder.build()); - copyRequestHeaders(req, proxyRequest, false); - } else { - if (StringUtils.isNotEmpty(req.getHeader(HttpHeaders.CONTENT_LENGTH)) || - StringUtils.isNotEmpty(req.getHeader(HttpHeaders.TRANSFER_ENCODING))) { - InputStreamEntity entity = new InputStreamEntity(req.getInputStream(), - Long.parseLong(req.getHeader(HttpHeaders.CONTENT_LENGTH))); - proxyRequest.setEntity(entity); - } - copyRequestHeaders(req, proxyRequest, true); - } - } - - /** - * Copy incoming request headers to the proxy request. - * - * @param req incoming {@link HttpServletRequest} - * @param httpRequest proxy request instance. - * @param preserveContentType true if content type header needs to be preserved. - * This should be set to false when handling multipart requests as Http - * client will generate the Content-Type header automatically. - */ - private void copyRequestHeaders(HttpServletRequest req, HttpRequestBase httpRequest, boolean preserveContentType) { - Enumeration headerNames = req.getHeaderNames(); - while (headerNames.hasMoreElements()) { - String headerName = headerNames.nextElement(); - if (headerName.equalsIgnoreCase(HttpHeaders.CONTENT_LENGTH) || - headerName.equalsIgnoreCase(SM.COOKIE) || - (!preserveContentType && headerName.equalsIgnoreCase(HttpHeaders.CONTENT_TYPE))) { - continue; - } - Enumeration headerValues = req.getHeaders(headerName); - while (headerValues.hasMoreElements()) { - httpRequest.setHeader(headerName, headerValues.nextElement()); - } - } - } - /*** * Validates the incoming request. * @@ -246,25 +170,10 @@ public class OTPInvokerHandler extends HttpServlet { if (StringUtils.isBlank(req.getHeader(HandlerConstants.OTP_HEADER))) { log.error("Unauthorized, Please provide OTP token."); - handleError(resp, HttpStatus.SC_UNAUTHORIZED); + HandlerUtil.handleError(resp, HttpStatus.SC_UNAUTHORIZED); return false; } return true; } - /** - * Handle error requests - * - * @param resp {@link HttpServletResponse} - * @param errorCode HTTP error status code - * @throws IOException If error occurred when trying to send the error response. - */ - private static void handleError(HttpServletResponse resp, int errorCode) - throws IOException { - ProxyResponse proxyResponse = new ProxyResponse(); - proxyResponse.setCode(errorCode); - proxyResponse.setExecutorResponse( - HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + HandlerUtil.getStatusKey(errorCode)); - HandlerUtil.handleError(resp, proxyResponse); - } } diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/PermissionScopeHandler.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/PermissionScopeHandler.java index 9ccb10c7965..b5309492c7c 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/PermissionScopeHandler.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/PermissionScopeHandler.java @@ -57,6 +57,7 @@ public class PermissionScopeHandler extends HttpServlet { JSONObject jsonObject = new JSONObject(); jsonObject.put(HandlerConstants.USER_SCOPES, authData.getScope()); proxyResponse.setCode(HttpStatus.SC_OK); + proxyResponse.setStatus(ProxyResponse.Status.SUCCESS); proxyResponse.setData(jsonObject.toString()); HandlerUtil.handleSuccess(resp, proxyResponse); return; diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/SsoLoginCallbackHandler.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/SsoLoginCallbackHandler.java index d402f4795e2..6fc5afa8efd 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/SsoLoginCallbackHandler.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/SsoLoginCallbackHandler.java @@ -53,7 +53,7 @@ public class SsoLoginCallbackHandler extends HttpServlet { String iotsCoreUrl = req.getScheme() + HandlerConstants.SCHEME_SEPARATOR + System.getProperty(HandlerConstants.IOT_CORE_HOST_ENV_VAR) + HandlerConstants.COLON + HandlerUtil.getCorePort(req.getScheme()); String keyManagerUrl = req.getScheme() + HandlerConstants.SCHEME_SEPARATOR + System.getProperty(HandlerConstants.IOT_KM_HOST_ENV_VAR) - + HandlerConstants.COLON + HandlerUtil.getKeymanagerPort(req.getScheme()); + + HandlerConstants.COLON + HandlerUtil.getKeyManagerPort(req.getScheme()); if (session == null) { String baseContextPath = req.getContextPath(); diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/SsoLoginHandler.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/SsoLoginHandler.java index f76f72a9586..f6180c0a379 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/SsoLoginHandler.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/SsoLoginHandler.java @@ -108,7 +108,7 @@ public class SsoLoginHandler extends HttpServlet { apiMgtUrl = req.getScheme() + HandlerConstants.SCHEME_SEPARATOR + System.getProperty(HandlerConstants.IOT_APIM_HOST_ENV_VAR) + HandlerConstants.COLON + HandlerUtil.getAPIManagerPort(req.getScheme()); keyManagerUrl = req.getScheme() + HandlerConstants.SCHEME_SEPARATOR + System.getProperty(HandlerConstants.IOT_KM_HOST_ENV_VAR) - + HandlerConstants.COLON + HandlerUtil.getKeymanagerPort(req.getScheme()); + + HandlerConstants.COLON + HandlerUtil.getKeyManagerPort(req.getScheme()); // Fetch ui config and persists in session String uiConfigUrl = iotsCoreUrl + HandlerConstants.UI_CONFIG_ENDPOINT; diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/SsoLogoutHandler.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/SsoLogoutHandler.java index 92d14735ae8..0e46e759f66 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/SsoLogoutHandler.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/SsoLogoutHandler.java @@ -45,6 +45,7 @@ public class SsoLogoutHandler extends HttpServlet { } removeCookie(HandlerConstants.COMMON_AUTH_ID_KEY, "/", resp); ProxyResponse proxyResponse = new ProxyResponse(); + proxyResponse.setStatus(ProxyResponse.Status.SUCCESS); proxyResponse.setCode(HttpStatus.SC_OK); HttpSession session = req.getSession(false); diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/UserHandler.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/UserHandler.java index 170030f399b..4e1f120734c 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/UserHandler.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/UserHandler.java @@ -57,7 +57,7 @@ public class UserHandler extends HttpServlet { String keymanagerUrl = req.getScheme() + HandlerConstants.SCHEME_SEPARATOR + System.getProperty(HandlerConstants.IOT_KM_HOST_ENV_VAR) - + HandlerConstants.COLON + HandlerUtil.getKeymanagerPort(req.getScheme()); + + HandlerConstants.COLON + HandlerUtil.getKeyManagerPort(req.getScheme()); HttpSession httpSession = req.getSession(false); if (httpSession == null) { HandlerUtil.sendUnAuthorizeResponse(resp); @@ -86,7 +86,11 @@ public class UserHandler extends HttpServlet { if (tokenStatus.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { if (tokenStatus.getCode() == HttpStatus.SC_UNAUTHORIZED) { - tokenStatus = HandlerUtil.retryRequestWithRefreshedToken(req, resp, tokenEndpoint, keymanagerUrl); + tokenStatus = HandlerUtil.retryRequestWithRefreshedToken(req, tokenEndpoint, keymanagerUrl); + if(!HandlerUtil.isResponseSuccessful(tokenStatus)) { + HandlerUtil.handleError(resp, tokenStatus); + return; + } } else { log.error("Error occurred while invoking the API to get token status."); HandlerUtil.handleError(resp, tokenStatus); @@ -108,6 +112,7 @@ public class UserHandler extends HttpServlet { return; } ProxyResponse proxyResponse = new ProxyResponse(); + proxyResponse.setStatus(ProxyResponse.Status.SUCCESS); proxyResponse.setCode(HttpStatus.SC_OK); proxyResponse.setData( jTokenResultAsJsonObject.get("username").getAsString().replaceAll("@carbon.super", "")); diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/beans/ProxyResponse.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/beans/ProxyResponse.java index ea1a550cfbc..32c7ecb117d 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/beans/ProxyResponse.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/beans/ProxyResponse.java @@ -17,11 +17,20 @@ */ package io.entgra.ui.request.interceptor.beans; +import org.apache.http.Header; + public class ProxyResponse { + public static class Status { + public static int SUCCESS = 1; + public static int ERROR = 0; + } + private int code; private String data; private String executorResponse; + private int status; + private Header[] headers; public int getCode() { return code; } @@ -34,4 +43,21 @@ public class ProxyResponse { public String getExecutorResponse() { return executorResponse; } public void setExecutorResponse(String executorResponse) { this.executorResponse = executorResponse; } + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + public Header[] getHeaders() { + return headers; + } + + public void setHeaders(Header[] headers) { + this.headers = headers; + } + } diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/GrafanaHandlerUtil.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/GrafanaHandlerUtil.java new file mode 100644 index 00000000000..3ba09f8d0dd --- /dev/null +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/GrafanaHandlerUtil.java @@ -0,0 +1,20 @@ +package io.entgra.ui.request.interceptor.util; + +import org.apache.commons.lang.StringUtils; + +import java.net.URI; + +public class GrafanaHandlerUtil { + public static String getGrafanaUri(URI req) { + StringBuilder grafanaUriBuilder = new StringBuilder(); + grafanaUriBuilder.append(req.getPath().replace(" ", "%20")); + if (StringUtils.isNotEmpty(req.getQuery())) { + grafanaUriBuilder.append("?").append(req.getQuery()); + } + return grafanaUriBuilder.toString(); + } + + public static String generateGrafanaUrl(URI request, String base) { + return base + GrafanaHandlerUtil.getGrafanaUri(request); + } +} diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/HandlerConstants.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/HandlerConstants.java index ac97931bf27..153ace868a8 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/HandlerConstants.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/HandlerConstants.java @@ -31,6 +31,11 @@ public class HandlerConstants { public static final String SSO_LOGIN_CALLBACK = "/ssoLoginCallback"; public static final String BASIC = "Basic "; public static final String BEARER = "Bearer "; + public static final String X_FRAME_OPTIONS = "X-Frame-Options"; + public static final String X_FRAME_OPTIONS_SAMEORIGIN = "SAMEORIGIN"; + public static final String UPGRADE = "Upgrade"; + public static final String WEB_SOCKET = "websocket"; + public static final String HTTP_UPGRADE = "HTTP/1.1"; public static final String TAGS_KEY = "tags"; public static final String APP_NAME_KEY = "applicationName"; public static final String SESSION_AUTH_DATA_KEY = "authInfo"; @@ -58,10 +63,13 @@ public class HandlerConstants { public static final String LOGIN_CACHE_CAPACITY_KEY = "loginCacheCapacity"; public static final String SCHEME_SEPARATOR = "://"; + public static final String URI_SEPARATOR = "/"; + public static final String QUERY_PARAM_KEY_VALUE_SEP = "="; public static final String COLON = ":"; public static final String HTTP_PROTOCOL = "http"; public static final String HTTPS_PROTOCOL = "https"; public static final String UNDERSCORE = "_"; + public static final String URI_QUERY_SEPARATOR = "?"; public static final int INTERNAL_ERROR_CODE = 500; public static final long TIMEOUT = 1200; @@ -71,9 +79,12 @@ public class HandlerConstants { public static final String AX_PREFIX = "ax2251:"; public static final String PAYLOADS_DIR = "repository/resources/payloads"; public static final String SOAP_ACTION_HEADER = "SOAPAction"; + public static final String REFERER_HEADER = "Referer"; public static final String WSS_PROTOCOL = "wss"; + public static final String WS_PROTOCOL = "ws"; public static final String REMOTE_SESSION_CONTEXT = "/remote/session/clients"; + public static final String GRAFANA_API = "/api/grafana-mgt/v1.0"; public static final String IOT_CORE_HOST_ENV_VAR = "iot.core.host"; public static final String IOT_CORE_HTTP_PORT_ENV_VAR = "iot.core.http.port"; @@ -89,5 +100,6 @@ public class HandlerConstants { public static final String IOT_REMOTE_SESSION_HOST_ENV_VAR = "iot.remotesession.server.host"; public static final String IOT_REMOTE_SESSION_HTTPS_PORT_ENV_VAR = "iot.remotesession.server.https.port"; public static final String IOT_GW_HTTPS_PORT_ENV_VAR = "iot.gateway.https.port"; + public static final String IOT_REPORTING_WEBAPP_HOST_ENV_VAR = "iot.reporting.webapp.host"; public static final String USER_SCOPES = "userScopes"; } diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/HandlerUtil.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/HandlerUtil.java index 48812430f47..6be995f1918 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/HandlerUtil.java +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/util/HandlerUtil.java @@ -25,27 +25,37 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import io.entgra.ui.request.interceptor.beans.AuthData; import io.entgra.ui.request.interceptor.cache.LoginCache; +import org.apache.commons.fileupload.FileItem; +import org.apache.commons.fileupload.FileUploadException; +import org.apache.commons.fileupload.disk.DiskFileItemFactory; +import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.http.Consts; -import org.apache.http.HttpHeaders; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; +import org.apache.http.*; +import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.cookie.SM; +import org.apache.http.entity.BufferedHttpEntity; import org.apache.http.entity.ContentType; +import org.apache.http.entity.InputStreamEntity; import org.apache.http.entity.StringEntity; +import org.apache.http.entity.mime.HttpMultipartMode; +import org.apache.http.entity.mime.MultipartEntityBuilder; +import org.apache.http.entity.mime.content.InputStreamBody; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.xml.serialize.OutputFormat; import org.apache.xml.serialize.XMLSerializer; +import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.w3c.dom.Document; import io.entgra.ui.request.interceptor.beans.ProxyResponse; +import org.wso2.carbon.device.mgt.core.common.util.HttpUtil; import org.xml.sax.SAXException; import javax.servlet.http.HttpServletRequest; @@ -60,6 +70,8 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.StringWriter; +import java.util.Enumeration; +import java.util.List; public class HandlerUtil { @@ -83,54 +95,64 @@ public class HandlerUtil { log.error("Received null response for http request : " + httpRequest.getMethod() + " " + httpRequest .getURI().toString()); proxyResponse.setCode(HandlerConstants.INTERNAL_ERROR_CODE); + proxyResponse.setStatus(ProxyResponse.Status.ERROR); proxyResponse.setExecutorResponse(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + getStatusKey( HandlerConstants.INTERNAL_ERROR_CODE)); return proxyResponse; } else { int statusCode = response.getStatusLine().getStatusCode(); - try (BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()))) { - StringBuilder result = new StringBuilder(); - String line; - while ((line = rd.readLine()) != null) { - result.append(line); - } - - String jsonString = result.toString(); - if (statusCode == HttpStatus.SC_OK || statusCode == HttpStatus.SC_CREATED) { + String jsonString = getResponseString(response); + if (statusCode == HttpStatus.SC_OK || statusCode == HttpStatus.SC_CREATED) { + proxyResponse.setCode(statusCode); + proxyResponse.setData(jsonString); + proxyResponse.setStatus(ProxyResponse.Status.SUCCESS); + proxyResponse.setExecutorResponse("SUCCESS"); + proxyResponse.setHeaders(response.getAllHeaders()); + return proxyResponse; + } else if (statusCode == HttpStatus.SC_UNAUTHORIZED) { + if (isTokenExpired(jsonString)) { + proxyResponse.setCode(statusCode); + proxyResponse.setStatus(ProxyResponse.Status.ERROR); + proxyResponse.setExecutorResponse(HandlerConstants.TOKEN_IS_EXPIRED); + } else { + log.error( + "Received " + statusCode + " response for http request : " + httpRequest.getMethod() + + " " + httpRequest.getURI().toString() + ". Error message: " + jsonString); proxyResponse.setCode(statusCode); proxyResponse.setData(jsonString); - proxyResponse.setExecutorResponse("SUCCESS"); - return proxyResponse; - } else if (statusCode == HttpStatus.SC_UNAUTHORIZED) { - if (jsonString.contains("Access token expired") || jsonString - .contains("Invalid input. Access token validation failed")) { - proxyResponse.setCode(statusCode); - proxyResponse.setExecutorResponse(HandlerConstants.TOKEN_IS_EXPIRED); - return proxyResponse; - } else { - log.error( - "Received " + statusCode + " response for http request : " + httpRequest.getMethod() - + " " + httpRequest.getURI().toString() + ". Error message: " + jsonString); - proxyResponse.setCode(statusCode); - proxyResponse.setData(jsonString); - proxyResponse.setExecutorResponse( - HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + getStatusKey(statusCode)); - return proxyResponse; - } + proxyResponse.setStatus(ProxyResponse.Status.ERROR); + proxyResponse.setExecutorResponse( + HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + getStatusKey(statusCode)); } - log.error("Received " + statusCode + - " response for http request : " + httpRequest.getMethod() + " " + httpRequest.getURI() - .toString() + ". Error message: " + jsonString); - proxyResponse.setCode(statusCode); - proxyResponse.setData(jsonString); - proxyResponse - .setExecutorResponse(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + getStatusKey(statusCode)); return proxyResponse; } + log.error("Received " + statusCode + + " response for http request : " + httpRequest.getMethod() + " " + httpRequest.getURI() + .toString() + ". Error message: " + jsonString); + proxyResponse.setCode(statusCode); + proxyResponse.setData(jsonString); + proxyResponse.setStatus(ProxyResponse.Status.ERROR); + proxyResponse + .setExecutorResponse(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + getStatusKey(statusCode)); + return proxyResponse; } } } + public static boolean isTokenExpired(String jsonBody) { + return jsonBody.contains("Access token expired") || jsonBody + .contains("Invalid input. Access token validation failed"); + } + + public static String getMemeType(HttpResponse response) { + String memeType = ""; + Header contentType = response.getEntity().getContentType(); + if (contentType != null) { + memeType = contentType.getValue().split(";")[0].trim(); + } + return memeType; + } + /*** * * @param statusCode Provide status code, e.g:- 400, 401, 500 etc @@ -172,6 +194,7 @@ public class HandlerUtil { } + /*** * Handle error requests. * @@ -183,6 +206,7 @@ public class HandlerUtil { Gson gson = new Gson(); if (proxyResponse == null) { proxyResponse = new ProxyResponse(); + proxyResponse.setStatus(ProxyResponse.Status.ERROR); proxyResponse.setCode(HttpStatus.SC_INTERNAL_SERVER_ERROR); proxyResponse.setExecutorResponse(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + HandlerUtil .getStatusKey(HandlerConstants.INTERNAL_ERROR_CODE)); @@ -200,17 +224,27 @@ public class HandlerUtil { /** * Handle error requests with custom error codes. * - * @param resp {@link HttpServletResponse} + * @param resp {@link HttpServletResponse} * @param errorCode HTTP error status code * @throws IOException If error occurred when trying to send the error response. */ public static void handleError(HttpServletResponse resp, int errorCode) throws IOException { + ProxyResponse proxyResponse = constructProxyResponseByErrorCode(errorCode); + HandlerUtil.handleError(resp, proxyResponse); + } + + public static ProxyResponse constructProxyResponseByErrorCode(int errorCode) { ProxyResponse proxyResponse = new ProxyResponse(); proxyResponse.setCode(errorCode); + proxyResponse.setStatus(ProxyResponse.Status.ERROR); proxyResponse.setExecutorResponse( HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + HandlerUtil.getStatusKey(errorCode)); - HandlerUtil.handleError(resp, proxyResponse); + return proxyResponse; + } + + public static boolean isResponseSuccessful(ProxyResponse response) { + return response.getStatus() == ProxyResponse.Status.SUCCESS; } /*** @@ -231,10 +265,18 @@ public class HandlerUtil { if (!StringUtils.isEmpty(responseData)) { try { - JSONObject responseDataJsonObj = new JSONObject(responseData); - response.put("data", responseDataJsonObj); + if (responseData.startsWith("{")) { + JSONObject responseDataJsonObj = new JSONObject(responseData); + response.put("data", responseDataJsonObj); + } else if (responseData.startsWith("[")) { + JSONArray responseDataJsonArr = new JSONArray(responseData); + response.put("data", responseDataJsonArr); + } else { + log.warn("Response data is not valid json string >> " + responseData); + response.put("data", responseData); + } } catch (JSONException e) { - log.debug("Response data is not valid json string"); + log.error("Response data is not passable"); response.put("data", responseData); } } @@ -265,12 +307,17 @@ public class HandlerUtil { * @param scheme https or https * @return {@link String} keymanager port */ - public static String getKeymanagerPort(String scheme) { - String keymanagerPort = System.getProperty(HandlerConstants.IOT_KM_HTTPS_PORT_ENV_VAR); + public static String getKeyManagerPort(String scheme) { + String keyManagerPort = System.getProperty(HandlerConstants.IOT_KM_HTTPS_PORT_ENV_VAR); if (HandlerConstants.HTTP_PROTOCOL.equals(scheme)) { - keymanagerPort = System.getProperty(HandlerConstants.IOT_KM_HTTP_PORT_ENV_VAR); + keyManagerPort = System.getProperty(HandlerConstants.IOT_KM_HTTP_PORT_ENV_VAR); } - return keymanagerPort; + return keyManagerPort; + } + + public static String getKeyManagerUrl(String scheme) { + return scheme + HandlerConstants.SCHEME_SEPARATOR + System.getProperty(HandlerConstants.IOT_KM_HOST_ENV_VAR) + + HandlerConstants.COLON + HandlerUtil.getKeyManagerPort(scheme); } /** @@ -324,6 +371,7 @@ public class HandlerUtil { public static void sendUnAuthorizeResponse(HttpServletResponse resp) throws IOException { ProxyResponse proxyResponse = new ProxyResponse(); + proxyResponse.setStatus(ProxyResponse.Status.ERROR); proxyResponse.setCode(HttpStatus.SC_UNAUTHORIZED); proxyResponse.setExecutorResponse( HandlerConstants.EXECUTOR_EXCEPTION_PREFIX + HandlerUtil.getStatusKey(HttpStatus.SC_UNAUTHORIZED)); @@ -333,7 +381,7 @@ public class HandlerUtil { /** * Generates the target URL for the proxy request. * - * @param req incoming {@link HttpServletRequest} + * @param req incoming {@link HttpServletRequest} * @param apiEndpoint API Endpoint URL * @return Target URL */ @@ -347,6 +395,47 @@ public class HandlerUtil { return urlBuilder.toString(); } + public static String getIOTGatewayBase(HttpServletRequest req) { + return req.getScheme() + HandlerConstants.SCHEME_SEPARATOR + System.getProperty(HandlerConstants.IOT_GW_HOST_ENV_VAR) + + HandlerConstants.COLON + HandlerUtil.getGatewayPort(req.getScheme()); + } + + /** + * Generate te request entity for POST and PUT requests from the incoming request. + * + * @param req incoming {@link HttpServletRequest}. + * @param proxyRequest proxy request instance. + * @throws FileUploadException If unable to parse the incoming request for multipart content extraction. + * @throws IOException If error occurred while generating the request body. + */ + public static void generateRequestEntity(HttpServletRequest req, HttpEntityEnclosingRequestBase proxyRequest) + throws FileUploadException, IOException { + if (ServletFileUpload.isMultipartContent(req)) { + ServletFileUpload servletFileUpload = new ServletFileUpload(new DiskFileItemFactory()); + List fileItemList = servletFileUpload.parseRequest(req); + MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create(); + entityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); + for (FileItem item : fileItemList) { + if (!item.isFormField()) { + entityBuilder.addPart(item.getFieldName(), new InputStreamBody(item.getInputStream(), + ContentType.create(item.getContentType()), item.getName())); + } else { + entityBuilder.addTextBody(item.getFieldName(), item.getString()); + } + } + proxyRequest.setEntity(new BufferedHttpEntity(entityBuilder.build())); + HandlerUtil.copyRequestHeaders(req, proxyRequest, false); + } else { + if (StringUtils.isNotEmpty(req.getHeader(HttpHeaders.CONTENT_LENGTH)) || + StringUtils.isNotEmpty(req.getHeader(HttpHeaders.TRANSFER_ENCODING))) { + InputStreamEntity entity = new InputStreamEntity(req.getInputStream(), + Long.parseLong(req.getHeader(HttpHeaders.CONTENT_LENGTH))); + proxyRequest.setEntity(new BufferedHttpEntity(entity)); + } + HandlerUtil.copyRequestHeaders(req, proxyRequest, true); + } + } + /*** * Constructs the application registration payload for DCR. * @@ -376,7 +465,7 @@ public class HandlerUtil { * @return {@link JsonObject} of UI configurations */ public static JsonObject getUIConfigAndPersistInSession(String uiConfigUrl, String gatewayUrl, HttpSession httpSession, - HttpServletResponse resp) throws IOException { + HttpServletResponse resp) throws IOException { HttpGet uiConfigEndpoint = new HttpGet(uiConfigUrl); ProxyResponse uiConfigResponse = HandlerUtil.execute(uiConfigEndpoint); String executorResponse = uiConfigResponse.getExecutorResponse(); @@ -448,7 +537,7 @@ public class HandlerUtil { log.error("Error occurred while sending the response into the socket. ", e); } catch (ParserConfigurationException e) { log.error("Error while creating the document builder."); - } catch ( SAXException e) { + } catch (SAXException e) { log.error("Error while parsing xml file.", e); } @@ -496,58 +585,78 @@ public class HandlerUtil { /** * Retry request again after refreshing the access token * - * @param req incoming {@link HttpServletRequest} - * @param resp resp {@link HttpServletResponse} + * @param req incoming {@link HttpServletRequest} * @param httpRequest subclass of {@link HttpRequestBase} related to the current request. * @return {@link ProxyResponse} if successful and null if failed. * @throws IOException If an error occurs when try to retry the request. */ - public static ProxyResponse retryRequestWithRefreshedToken(HttpServletRequest req, HttpServletResponse resp, - HttpRequestBase httpRequest, String apiEndpoint) throws IOException { - if (refreshToken(req, resp, apiEndpoint)) { + public static ProxyResponse retryRequestWithRefreshedToken(HttpServletRequest req, HttpRequestBase httpRequest, + String apiEndpoint) throws IOException { + ProxyResponse retryResponse = refreshToken(req, apiEndpoint); + if (isResponseSuccessful(retryResponse)) { HttpSession session = req.getSession(false); if (session == null) { log.error("Unauthorized, You are not logged in. Please log in to the portal"); - handleError(resp, HttpStatus.SC_UNAUTHORIZED); - return null; + return constructProxyResponseByErrorCode(HttpStatus.SC_UNAUTHORIZED); } httpRequest.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BEARER + authData.getAccessToken()); ProxyResponse proxyResponse = HandlerUtil.execute(httpRequest); if (proxyResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { log.error("Error occurred while invoking the API after refreshing the token."); - HandlerUtil.handleError(resp, proxyResponse); - return null; + return proxyResponse; } return proxyResponse; + } - return null; + return retryResponse; } /*** * This method is responsible to get the refresh token * * @param req {@link HttpServletRequest} - * @param resp {@link HttpServletResponse} * @return If successfully renew tokens, returns TRUE otherwise return FALSE * @throws IOException If an error occurs while witting error response to client side or invoke token renewal API */ - private static boolean refreshToken(HttpServletRequest req, HttpServletResponse resp, String keymanagerUrl) + private static ProxyResponse refreshToken(HttpServletRequest req, String keymanagerUrl) throws IOException { if (log.isDebugEnabled()) { log.debug("refreshing the token"); } - - HttpPost tokenEndpoint = new HttpPost(keymanagerUrl + HandlerConstants.TOKEN_ENDPOINT); + ProxyResponse tokenResultResponse; HttpSession session = req.getSession(false); if (session == null) { log.error("Couldn't find a session, hence it is required to login and proceed."); - handleError(resp, HttpStatus.SC_UNAUTHORIZED); - return false; + tokenResultResponse = constructProxyResponseByErrorCode(HttpStatus.SC_UNAUTHORIZED); +// handleError(resp, HttpStatus.SC_UNAUTHORIZED); + return tokenResultResponse; } authData = (AuthData) session.getAttribute(HandlerConstants.SESSION_AUTH_DATA_KEY); + tokenResultResponse = getTokenResult(authData, keymanagerUrl); + if (tokenResultResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { + log.error("Error occurred while refreshing access token."); +// HandlerUtil.handleError(resp, tokenResultResponse); + return tokenResultResponse; + } + + JsonParser jsonParser = new JsonParser(); + JsonElement jTokenResult = jsonParser.parse(tokenResultResponse.getData()); + + if (jTokenResult.isJsonObject()) { + setNewAuthData(constructAuthDataFromTokenResult(jTokenResult, authData), session); + return tokenResultResponse; + } + + log.error("Error Occurred in token renewal process."); + tokenResultResponse = constructProxyResponseByErrorCode(HttpStatus.SC_INTERNAL_SERVER_ERROR); +// handleError(resp, HttpStatus.SC_INTERNAL_SERVER_ERROR); + return tokenResultResponse; + } + public static ProxyResponse getTokenResult(AuthData authData, String keymanagerUrl) throws IOException { + HttpPost tokenEndpoint = new HttpPost(keymanagerUrl + HandlerConstants.TOKEN_ENDPOINT); StringEntity tokenEndpointPayload = new StringEntity( - "grant_type=refresh_token&refresh_token=" + authData.getRefreshToken() + "&scope=PRODUCTION", + "grant_type=refresh_token&refresh_token=" + authData.getRefreshToken(), ContentType.APPLICATION_FORM_URLENCODED); tokenEndpoint.setEntity(tokenEndpointPayload); @@ -555,35 +664,75 @@ public class HandlerUtil { tokenEndpoint.setHeader(HttpHeaders.AUTHORIZATION, HandlerConstants.BASIC + encodedClientApp); tokenEndpoint.setHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_FORM_URLENCODED.toString()); + return HandlerUtil.execute(tokenEndpoint); + } - ProxyResponse tokenResultResponse = HandlerUtil.execute(tokenEndpoint); - if (tokenResultResponse.getExecutorResponse().contains(HandlerConstants.EXECUTOR_EXCEPTION_PREFIX)) { - log.error("Error occurred while refreshing access token."); - HandlerUtil.handleError(resp, tokenResultResponse); - return false; + public static void setNewAuthData(AuthData newAuthData, HttpSession session) { + authData = newAuthData; + session.setAttribute(HandlerConstants.SESSION_AUTH_DATA_KEY, newAuthData); + } + + public static AuthData constructAuthDataFromTokenResult(JsonElement tokenResult, AuthData authData) { + JsonObject jTokenResultAsJsonObject = tokenResult.getAsJsonObject(); + AuthData newAuthData = new AuthData(); + newAuthData.setAccessToken(jTokenResultAsJsonObject.get("access_token").getAsString()); + newAuthData.setRefreshToken(jTokenResultAsJsonObject.get("refresh_token").getAsString()); + newAuthData.setScope(jTokenResultAsJsonObject.get("scope").getAsString()); + newAuthData.setClientId(authData.getClientId()); + newAuthData.setClientSecret(authData.getClientSecret()); + newAuthData.setEncodedClientApp(authData.getEncodedClientApp()); + newAuthData.setUsername(authData.getUsername()); + return newAuthData; + } + + /** + * Copy incoming request headers to the proxy request. + * + * @param req incoming {@link HttpServletRequest} + * @param httpRequest proxy request instance. + * @param preserveContentType true if content type header needs to be preserved. + * This should be set to false when handling multipart requests as Http + * client will generate the Content-Type header automatically. + */ + public static void copyRequestHeaders(HttpServletRequest req, HttpRequestBase httpRequest, boolean preserveContentType) { + Enumeration headerNames = req.getHeaderNames(); + while (headerNames.hasMoreElements()) { + String headerName = headerNames.nextElement(); + if (headerName.equalsIgnoreCase(HttpHeaders.CONTENT_LENGTH) || + headerName.equalsIgnoreCase(SM.COOKIE) || + (!preserveContentType && headerName.equalsIgnoreCase(HttpHeaders.CONTENT_TYPE))) { + continue; + } + Enumeration headerValues = req.getHeaders(headerName); + while (headerValues.hasMoreElements()) { + httpRequest.setHeader(headerName, headerValues.nextElement()); + } } + } - JsonParser jsonParser = new JsonParser(); - JsonElement jTokenResult = jsonParser.parse(tokenResultResponse.getData()); + public static String getHeaderValue(String headerName, Header[] headers) { + String headerValue = null; + for(Header header : headers) { + if (header.getName().equalsIgnoreCase(headerName)) { + headerValue = header.getValue(); + } + } + return headerValue; + } - if (jTokenResult.isJsonObject()) { - JsonObject jTokenResultAsJsonObject = jTokenResult.getAsJsonObject(); - AuthData newAuthData = new AuthData(); - - newAuthData.setAccessToken(jTokenResultAsJsonObject.get("access_token").getAsString()); - newAuthData.setRefreshToken(jTokenResultAsJsonObject.get("refresh_token").getAsString()); - newAuthData.setScope(jTokenResultAsJsonObject.get("scope").getAsString()); - newAuthData.setClientId(authData.getClientId()); - newAuthData.setClientSecret(authData.getClientSecret()); - newAuthData.setEncodedClientApp(authData.getEncodedClientApp()); - newAuthData.setUsername(authData.getUsername()); - authData = newAuthData; - session.setAttribute(HandlerConstants.SESSION_AUTH_DATA_KEY, newAuthData); - return true; + public static String getResponseString(HttpResponse response) throws IOException { + try (BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()))) { + StringBuilder responseBuilder = new StringBuilder(); + String line; + while ((line = rd.readLine()) != null) { + responseBuilder.append(line); + } + return responseBuilder.toString(); } + } - log.error("Error Occurred in token renewal process."); - handleError(resp, HttpStatus.SC_INTERNAL_SERVER_ERROR); - return false; + + public static boolean isPropertyDefined(String property) { + return StringUtils.isEmpty(System.getProperty(property)); } -} +} \ No newline at end of file diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/websocket/GrafanaLiveSecurityFilter.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/websocket/GrafanaLiveSecurityFilter.java new file mode 100644 index 00000000000..091a1e005bf --- /dev/null +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/websocket/GrafanaLiveSecurityFilter.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2019, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.entgra.ui.request.interceptor.websocket; + +import io.entgra.ui.request.interceptor.beans.AuthData; +import io.entgra.ui.request.interceptor.util.HandlerConstants; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; +import java.io.IOException; + +public class GrafanaLiveSecurityFilter implements Filter { + + private static final Log log = LogFactory.getLog(GrafanaLiveSecurityFilter.class); + + @Override + public void init(FilterConfig filterConfig) { + + } + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + HttpServletRequest req = (HttpServletRequest) servletRequest; + HttpSession session = req.getSession(false); + if (session == null) { + log.error("Unauthorized, You are not logged in. Please log in to the portal"); + return; + } + AuthData authData = (AuthData) session.getAttribute(HandlerConstants.SESSION_AUTH_DATA_KEY); + if (authData == null) { + log.error("Unauthorized, Access token not found in the current session"); + return; + } + filterChain.doFilter(servletRequest, servletResponse); + } + + @Override + public void destroy() { + + } +} diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/websocket/GrafanaWebSocketClient.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/websocket/GrafanaWebSocketClient.java new file mode 100644 index 00000000000..f6b157e4f71 --- /dev/null +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/websocket/GrafanaWebSocketClient.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2019, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.entgra.ui.request.interceptor.websocket; + +import io.entgra.analytics.mgt.grafana.proxy.common.exception.GrafanaManagementException; +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaUtil; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.websocket.ClientEndpoint; +import javax.websocket.ContainerProvider; +import javax.websocket.Session; +import javax.websocket.OnOpen; +import javax.websocket.OnClose; +import javax.websocket.OnError; +import javax.websocket.CloseReason; +import javax.websocket.WebSocketContainer; +import java.net.URI; + +import javax.websocket.ClientEndpointConfig; +import javax.websocket.Endpoint; +import javax.websocket.EndpointConfig; +import javax.ws.rs.core.HttpHeaders; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +@ClientEndpoint +public class GrafanaWebSocketClient extends Endpoint { + + private static final Log log = LogFactory.getLog(GrafanaWebSocketClient.class); + private Session grafanaServerSession; + private Consumer messageConsumer; + + public GrafanaWebSocketClient(URI endpointURI) { + try { + ClientEndpointConfig clientEndpointConfig = handShakeRequestConfig(); + WebSocketContainer container = ContainerProvider.getWebSocketContainer(); + container.connectToServer(this, clientEndpointConfig, endpointURI); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public ClientEndpointConfig handShakeRequestConfig() { + ClientEndpointConfig.Configurator clientEndpointConfigConfigurator = new ClientEndpointConfig.Configurator() { + @Override + public void beforeRequest(Map> headers) { + try { + headers.put(HttpHeaders.AUTHORIZATION, + Collections.singletonList(GrafanaUtil.getBasicAuthBase64Header())); + } catch (GrafanaManagementException e) { + log.error(e); + } + } + + + }; + return ClientEndpointConfig.Builder.create(). + configurator(clientEndpointConfigConfigurator).build(); + } + + @OnOpen + @Override + public void onOpen(Session grafanaServerSession, EndpointConfig endpointConfig) { + // Due to a bug (https://bz.apache.org/bugzilla/show_bug.cgi?format=multiple&id=57788) + // in the tomcat version used, this has to coded like this + grafanaServerSession.addMessageHandler(String.class, message -> messageConsumer.accept(message)); + this.grafanaServerSession = grafanaServerSession; + } + + @OnClose + @Override + public void onClose(Session session, CloseReason reason) { + log.info("Server session closed: " + reason); + this.grafanaServerSession = null; + } + + @OnError + @Override + public void onError(Session session, Throwable t) { + log.error("Error occurred in grafana server session: " + t.toString()); + t.printStackTrace(); + } + + public void sendMessageToServer(String message) { + if (grafanaServerSession.getAsyncRemote() != null) { + grafanaServerSession.getAsyncRemote().sendText(message); + } + } + + public Session getGrafanaServerSession() { + return grafanaServerSession; + } + + public void addMessageConsumer(Consumer messageConsumer) { + this.messageConsumer = messageConsumer; + } +} diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/websocket/GrafanaWebSocketHandler.java b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/websocket/GrafanaWebSocketHandler.java new file mode 100644 index 00000000000..04dff018447 --- /dev/null +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/java/io/entgra/ui/request/interceptor/websocket/GrafanaWebSocketHandler.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2019, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved. + * + * Entgra (Pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.entgra.ui.request.interceptor.websocket; + +import io.entgra.analytics.mgt.grafana.proxy.core.exception.GrafanaEnvVariablesNotDefined; +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaConstants; +import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaUtil; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.websocket.CloseReason; +import javax.websocket.server.ServerEndpoint; +import javax.websocket.Session; +import javax.websocket.OnOpen; +import javax.websocket.OnMessage; +import javax.websocket.OnClose; +import javax.websocket.OnError; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +@ServerEndpoint(value = "/grafana/api/live/ws") +public class GrafanaWebSocketHandler { + private static final Log log = LogFactory.getLog(GrafanaWebSocketHandler.class); + private GrafanaWebSocketClient grafanaClient; + + @OnOpen + public void onOpen(Session browserSession) throws GrafanaEnvVariablesNotDefined { + URI grafanaUri = browserSession.getRequestURI(); + String grafanaWebSocketUrl = getGrafanaWebSocketUrl(grafanaUri); + try { + grafanaClient = new GrafanaWebSocketClient(new URI(grafanaWebSocketUrl)); + grafanaClient.addMessageConsumer(message -> sendMessageToBrowser(browserSession, message)); + } catch (URISyntaxException e) { + log.error("Invalid web socket uri provided", e); + } + } + + @OnClose + public void onClose(CloseReason reason) throws IOException { + log.info("Browser session closed: " + reason); + if (grafanaClient.getGrafanaServerSession() != null) { + grafanaClient.getGrafanaServerSession().close(); + } + } + + @OnMessage + public void onMessage(String message) { + grafanaClient.sendMessageToServer(message); + } + + @OnError + public void onError(Throwable t) { + log.error("Error occurred in grafana browser session: " + t.toString()); + t.printStackTrace(); + } + + public void sendMessageToBrowser(Session browserSession, String message) { + try { + // Avoid grafana client sending messages when browser session is already closed + if(browserSession.isOpen()) { + browserSession.getBasicRemote().sendText(message); + } + } catch (IOException e) { + log.error("Error occurred while sending message to browser", e); + } + } + + private String getGrafanaWebSocketUrl(URI requestUri) throws GrafanaEnvVariablesNotDefined { + return GrafanaUtil.getGrafanaWebSocketBase(requestUri.getScheme()) + GrafanaConstants.WS_LIVE_API; + } +} diff --git a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/webapp/WEB-INF/web.xml b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/webapp/WEB-INF/web.xml index f5ba0ae97ac..32e9b34f98c 100644 --- a/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/webapp/WEB-INF/web.xml +++ b/components/ui-request-interceptor/io.entgra.ui.request.interceptor/src/main/webapp/WEB-INF/web.xml @@ -88,6 +88,15 @@ Cache-Control: no-store, no-cache, must-revalidate, private + + GrafanaLiveSecurityFilter + io.entgra.ui.request.interceptor.websocket.GrafanaLiveSecurityFilter + + + + GrafanaLiveSecurityFilter + /grafana/api/live/ws + HttpHeaderSecurityFilter diff --git a/components/ui-request-interceptor/pom.xml b/components/ui-request-interceptor/pom.xml index da0217aad5f..1e3917a86e7 100644 --- a/components/ui-request-interceptor/pom.xml +++ b/components/ui-request-interceptor/pom.xml @@ -21,7 +21,7 @@ carbon-devicemgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../../pom.xml diff --git a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/pom.xml b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/pom.xml index eee190b03bf..c073d222ea1 100644 --- a/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/pom.xml +++ b/components/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework/pom.xml @@ -21,7 +21,7 @@ org.wso2.carbon.devicemgt webapp-authenticator-framework - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/components/webapp-authenticator-framework/pom.xml b/components/webapp-authenticator-framework/pom.xml index 2d66edebd7b..e773a44ec67 100644 --- a/components/webapp-authenticator-framework/pom.xml +++ b/components/webapp-authenticator-framework/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../../pom.xml diff --git a/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api.feature/pom.xml b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api.feature/pom.xml new file mode 100644 index 00000000000..76e69e798e8 --- /dev/null +++ b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api.feature/pom.xml @@ -0,0 +1,120 @@ + + + + + + + org.wso2.carbon.devicemgt + grafana-mgt-feature + 5.0.6-SNAPSHOT + ../pom.xml + + + 4.0.0 + io.entgra.analytics.mgt.grafana.proxy.api.feature + pom + Entgra - Grafana Proxy API Feature + https://entgra.io + This feature contains the APIs required for Grafana + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + grafana-mgt-copy + package + + copy + + + + + org.wso2.carbon.devicemgt + io.entgra.analytics.mgt.grafana.proxy.api + ${project.version} + war + true + + ${project.build.directory}/maven-shared-archive-resources/webapps + + api#grafana-mgt#v1.0.war + + + + + + + + org.apache.maven.plugins + maven-resources-plugin + + + copy-resources + generate-resources + + copy-resources + + + src/main/resources + + + resources + + build.properties + p2.inf + + + + + + + + + org.wso2.maven + carbon-p2-plugin + + + p2-feature-generation + package + + p2-feature-gen + + + io.entgra.analytics.mgt.grafana.proxy.api + ../../../etc/feature.properties + + + + org.wso2.carbon.p2.category.type:server + + org.eclipse.equinox.p2.type.group:false + + + + + + + + + + + diff --git a/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api.feature/src/main/resources/build.properties b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api.feature/src/main/resources/build.properties new file mode 100644 index 00000000000..9c86577d768 --- /dev/null +++ b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api.feature/src/main/resources/build.properties @@ -0,0 +1 @@ +custom = true diff --git a/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api.feature/src/main/resources/p2.inf b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api.feature/src/main/resources/p2.inf new file mode 100644 index 00000000000..ca042b5b3e0 --- /dev/null +++ b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.api.feature/src/main/resources/p2.inf @@ -0,0 +1,3 @@ +instructions.configure = \ +org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../deployment/server/webapps/);\ +org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/io.entgra.analytics.mgt.grafana.proxy.api_${feature.version}/webapps/api#grafana-mgt#v1.0.war,target:${installFolder}/../../deployment/server/webapps/api#grafana-mgt#v1.0.war,overwrite:true);\ diff --git a/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/pom.xml b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/pom.xml new file mode 100644 index 00000000000..d50ddc46a45 --- /dev/null +++ b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/pom.xml @@ -0,0 +1,140 @@ + + + + + + + org.wso2.carbon.devicemgt + grafana-mgt-feature + 5.0.6-SNAPSHOT + ../pom.xml + + + 4.0.0 + io.entgra.analytics.mgt.grafana.proxy.server.feature + pom + + Entgra - Grafana Management Server Feature + https://entgra.io + This feature contains the core bundles required for Back-end Grafana Management functionality + + + + + org.wso2.carbon.devicemgt + io.entgra.analytics.mgt.grafana.proxy.common + + + org.wso2.carbon.devicemgt + io.entgra.analytics.mgt.grafana.proxy.core + + + com.googlecode.plist + dd-plist + ${googlecode.plist.version} + + + commons-validator + commons-validator + ${commons-validator.version} + + + org.apache.cxf + cxf-rt-frontend-jaxrs + ${cxf.version} + + + com.h2database.wso2 + h2-database-engine + + + + + + + maven-resources-plugin + 2.6 + + + copy-resources + generate-resources + + copy-resources + + + src/main/resources + + + resources + + build.properties + p2.inf + + + + + + + + + org.wso2.maven + carbon-p2-plugin + ${carbon.p2.plugin.version} + + + p2-feature-generation + package + + p2-feature-gen + + + io.entgra.analytics.mgt.grafana.proxy.server + ../../../etc/feature.properties + + + org.wso2.carbon.p2.category.type:server + org.eclipse.equinox.p2.type.group:false + + + + + org.wso2.carbon.devicemgt:io.entgra.analytics.mgt.grafana.proxy.common:${carbon.device.mgt.version} + + + org.wso2.carbon.devicemgt:io.entgra.analytics.mgt.grafana.proxy.core:${carbon.device.mgt.version} + + + com.googlecode.plist:dd-plist:${googlecode.plist.version} + + + commons-validator:commons-validator:${commons-validator.version} + + + + + + + + + + + + 1.6 + + diff --git a/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/src/main/resources/build.properties b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/src/main/resources/build.properties new file mode 100644 index 00000000000..9c86577d768 --- /dev/null +++ b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/src/main/resources/build.properties @@ -0,0 +1 @@ +custom = true diff --git a/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/src/main/resources/conf/grafana-config.xml b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/src/main/resources/conf/grafana-config.xml new file mode 100644 index 00000000000..09344263f47 --- /dev/null +++ b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/src/main/resources/conf/grafana-config.xml @@ -0,0 +1,35 @@ + + + + + + + + 100 + + + 100 + + + + + admin + admin + + diff --git a/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/src/main/resources/conf_templates.templates.repository.conf/grafana-config.xml.j2 b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/src/main/resources/conf_templates.templates.repository.conf/grafana-config.xml.j2 new file mode 100644 index 00000000000..09344263f47 --- /dev/null +++ b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/src/main/resources/conf_templates.templates.repository.conf/grafana-config.xml.j2 @@ -0,0 +1,35 @@ + + + + + + + + 100 + + + 100 + + + + + admin + admin + + diff --git a/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/src/main/resources/p2.inf b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/src/main/resources/p2.inf new file mode 100644 index 00000000000..f269b204c46 --- /dev/null +++ b/features/analytics-mgt/grafana-mgt/io.entgra.analytics.mgt.grafana.proxy.server.feature/src/main/resources/p2.inf @@ -0,0 +1,4 @@ +instructions.configure = \ +org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/io.entgra.analytics.mgt.grafana.proxy.server_${feature.version}/conf/grafana-config.xml,target:${installFolder}/../../conf/grafana-config.xml,overwrite:true);\ + +instructions.unconfigure = \ \ No newline at end of file diff --git a/features/analytics-mgt/grafana-mgt/pom.xml b/features/analytics-mgt/grafana-mgt/pom.xml new file mode 100644 index 00000000000..74d6ec9ecad --- /dev/null +++ b/features/analytics-mgt/grafana-mgt/pom.xml @@ -0,0 +1,40 @@ + + + + + + + org.wso2.carbon.devicemgt + analytics-mgt-feature + 5.0.6-SNAPSHOT + ../pom.xml + + + 4.0.0 + grafana-mgt-feature + pom + Entgra - Grafana Management Feature + http://entgra.io + + + io.entgra.analytics.mgt.grafana.proxy.server.feature + io.entgra.analytics.mgt.grafana.proxy.api.feature + + + diff --git a/features/analytics-mgt/pom.xml b/features/analytics-mgt/pom.xml new file mode 100644 index 00000000000..8f657bc162c --- /dev/null +++ b/features/analytics-mgt/pom.xml @@ -0,0 +1,19 @@ + + + + carbon-devicemgt + org.wso2.carbon.devicemgt + 5.0.6-SNAPSHOT + ../../pom.xml + + + 4.0.0 + analytics-mgt-feature + pom + Entgra - Analytics Management Feature + http://entgra.io + + + grafana-mgt + + diff --git a/features/apimgt-extensions/org.wso2.carbon.apimgt.application.extension.feature/pom.xml b/features/apimgt-extensions/org.wso2.carbon.apimgt.application.extension.feature/pom.xml index ff6f03085de..2be4052a599 100644 --- a/features/apimgt-extensions/org.wso2.carbon.apimgt.application.extension.feature/pom.xml +++ b/features/apimgt-extensions/org.wso2.carbon.apimgt.application.extension.feature/pom.xml @@ -21,7 +21,7 @@ org.wso2.carbon.devicemgt apimgt-extensions-feature - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/features/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher.feature/pom.xml b/features/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher.feature/pom.xml index 0436f66bd9b..4452caf2a83 100644 --- a/features/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher.feature/pom.xml +++ b/features/apimgt-extensions/org.wso2.carbon.apimgt.webapp.publisher.feature/pom.xml @@ -21,7 +21,7 @@ org.wso2.carbon.devicemgt apimgt-extensions-feature - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/features/apimgt-extensions/pom.xml b/features/apimgt-extensions/pom.xml index b27d5b62da5..604debbc1d0 100644 --- a/features/apimgt-extensions/pom.xml +++ b/features/apimgt-extensions/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../../pom.xml diff --git a/features/application-mgt/io.entgra.application.mgt.api.feature/pom.xml b/features/application-mgt/io.entgra.application.mgt.api.feature/pom.xml index c4683689654..12789094895 100644 --- a/features/application-mgt/io.entgra.application.mgt.api.feature/pom.xml +++ b/features/application-mgt/io.entgra.application.mgt.api.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt application-mgt-feature - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/features/application-mgt/io.entgra.application.mgt.server.feature/pom.xml b/features/application-mgt/io.entgra.application.mgt.server.feature/pom.xml index efdfec28b59..ad2482c6948 100644 --- a/features/application-mgt/io.entgra.application.mgt.server.feature/pom.xml +++ b/features/application-mgt/io.entgra.application.mgt.server.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt application-mgt-feature - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/features/application-mgt/io.entgra.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/h2.sql b/features/application-mgt/io.entgra.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/h2.sql index 150480f68df..6b07c6b7579 100644 --- a/features/application-mgt/io.entgra.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/h2.sql +++ b/features/application-mgt/io.entgra.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/h2.sql @@ -278,3 +278,17 @@ CREATE TABLE IF NOT EXISTS AP_SCHEDULED_SUBSCRIPTION( FOREIGN KEY (APPLICATION_UUID) REFERENCES AP_APP_RELEASE (UUID) ON DELETE NO ACTION ON UPDATE NO ACTION ); + +-- ----------------------------------------------------- +-- Table AP_APP_FAVOURITES +-- ----------------------------------------------------- +CREATE TABLE IF NOT EXISTS AP_APP_FAVOURITES( + ID INTEGER NOT NULL AUTO_INCREMENT, + AP_APP_ID INTEGER NOT NULL, + USER_NAME VARCHAR(100) NOT NULL, + TENANT_ID INTEGER NOT NULL, + PRIMARY KEY(ID), + CONSTRAINT AP_APP_FAVOURITES_AP_APP_ID_fk + FOREIGN KEY (AP_APP_ID) + REFERENCES AP_APP (ID) ON DELETE NO ACTION ON UPDATE NO ACTION +); diff --git a/features/application-mgt/io.entgra.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/mssql.sql b/features/application-mgt/io.entgra.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/mssql.sql index 5d29525a8c5..f1a41956995 100644 --- a/features/application-mgt/io.entgra.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/mssql.sql +++ b/features/application-mgt/io.entgra.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/mssql.sql @@ -278,4 +278,18 @@ CREATE TABLE AP_SCHEDULED_SUBSCRIPTION( CONSTRAINT fk_AP_SCHEDULED_SUBSCRIPTION_AP_APP_RELEASE FOREIGN KEY (APPLICATION_UUID) REFERENCES AP_APP_RELEASE (UUID) ON DELETE NO ACTION ON UPDATE NO ACTION -); \ No newline at end of file +); + +-- ----------------------------------------------------- +-- Table AP_APP_FAVOURITES +-- ----------------------------------------------------- +CREATE TABLE IF NOT EXISTS AP_APP_FAVOURITES( + ID INTEGER NOT NULL IDENTITY, + AP_APP_ID INTEGER NOT NULL, + USER_NAME VARCHAR(100) NOT NULL, + TENANT_ID INTEGER NOT NULL, + PRIMARY KEY(ID), + CONSTRAINT AP_APP_FAVOURITES_AP_APP_ID_fk + FOREIGN KEY (AP_APP_ID) + REFERENCES AP_APP (ID) +); diff --git a/features/application-mgt/io.entgra.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/mysql.sql b/features/application-mgt/io.entgra.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/mysql.sql index 8985d3637bf..2d773378821 100644 --- a/features/application-mgt/io.entgra.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/mysql.sql +++ b/features/application-mgt/io.entgra.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/mysql.sql @@ -2,311 +2,271 @@ -- Table AP_APP -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS AP_APP( - ID INTEGER NOT NULL AUTO_INCREMENT, - NAME VARCHAR(350) NOT NULL, - DESCRIPTION VARCHAR(200) NOT NULL, - TYPE VARCHAR(200) NOT NULL, - TENANT_ID INTEGER NOT NULL, - STATUS VARCHAR(45) NOT NULL DEFAULT 'ACTIVE', - SUB_TYPE VARCHAR(45) NOT NULL, - CURRENCY VARCHAR(45) NULL DEFAULT '$', - RATING DOUBLE NULL DEFAULT NULL, - DEVICE_TYPE_ID INTEGER NOT NULL, - PRIMARY KEY (ID) + ID INTEGER NOT NULL AUTO_INCREMENT, + NAME VARCHAR(350) NOT NULL, + DESCRIPTION VARCHAR(200) NOT NULL, + TYPE VARCHAR(200) NOT NULL, + TENANT_ID INTEGER NOT NULL, + STATUS VARCHAR(45) NOT NULL DEFAULT 'ACTIVE', + SUB_TYPE VARCHAR(45) NOT NULL, + CURRENCY VARCHAR(45) NULL DEFAULT '$', + RATING DOUBLE NULL DEFAULT NULL, + DEVICE_TYPE_ID INTEGER NOT NULL, + PRIMARY KEY (ID) ); - -- ----------------------------------------------------- -- Table AP_APP_RELEASE -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS AP_APP_RELEASE( - ID INTEGER NOT NULL AUTO_INCREMENT, - DESCRIPTION VARCHAR(200) NOT NULL, - VERSION VARCHAR(70) NOT NULL, - TENANT_ID INTEGER NOT NULL, - UUID VARCHAR(200) NOT NULL, - RELEASE_TYPE VARCHAR(45) NOT NULL, - PACKAGE_NAME VARCHAR(150) NOT NULL, - APP_PRICE DECIMAL(6, 2) NULL DEFAULT NULL, - INSTALLER_LOCATION VARCHAR(100) NOT NULL, - ICON_LOCATION VARCHAR(100) NOT NULL, - BANNER_LOCATION VARCHAR(100) NULL DEFAULT NULL, - SC_1_LOCATION VARCHAR(100) NOT NULL, - SC_2_LOCATION VARCHAR(100) NULL DEFAULT NULL, - SC_3_LOCATION VARCHAR(100) NULL DEFAULT NULL, - APP_HASH_VALUE VARCHAR(1000) NOT NULL, - SHARED_WITH_ALL_TENANTS BOOLEAN NOT NULL DEFAULT FALSE, - APP_META_INFO VARCHAR(150) NULL DEFAULT NULL, - SUPPORTED_OS_VERSIONS VARCHAR(45) NOT NULL, - RATING DOUBLE NULL DEFAULT NULL, - CURRENT_STATE VARCHAR(45) NOT NULL, - RATED_USERS INTEGER NULL, - AP_APP_ID INTEGER NOT NULL, - PRIMARY KEY (ID), - CONSTRAINT fk_AP_APP_RELEASE_AP_APP1 - FOREIGN KEY (AP_APP_ID) - REFERENCES AP_APP (ID) ON DELETE NO ACTION ON UPDATE NO ACTION + ID INTEGER NOT NULL AUTO_INCREMENT, + DESCRIPTION VARCHAR(200) NOT NULL, + VERSION VARCHAR(70) NOT NULL, + TENANT_ID INTEGER NOT NULL, + UUID VARCHAR(200) NOT NULL, + RELEASE_TYPE VARCHAR(45) NOT NULL, + PACKAGE_NAME VARCHAR(150) NOT NULL, + APP_PRICE DECIMAL(6, 2) NULL DEFAULT NULL, + INSTALLER_LOCATION VARCHAR(100) NOT NULL, + ICON_LOCATION VARCHAR(100) NOT NULL, + BANNER_LOCATION VARCHAR(100) NULL DEFAULT NULL, + SC_1_LOCATION VARCHAR(100) NOT NULL, + SC_2_LOCATION VARCHAR(100) NULL DEFAULT NULL, + SC_3_LOCATION VARCHAR(100) NULL DEFAULT NULL, + APP_HASH_VALUE VARCHAR(1000) NOT NULL, + SHARED_WITH_ALL_TENANTS BOOLEAN NOT NULL DEFAULT FALSE, + APP_META_INFO VARCHAR(150) NULL DEFAULT NULL, + SUPPORTED_OS_VERSIONS VARCHAR(45) NOT NULL, + RATING DOUBLE NULL DEFAULT NULL, + CURRENT_STATE VARCHAR(45) NOT NULL, + RATED_USERS INTEGER NULL, + AP_APP_ID INTEGER NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT fk_AP_APP_RELEASE_AP_APP1 FOREIGN KEY (AP_APP_ID) REFERENCES AP_APP (ID) ON DELETE NO ACTION ON UPDATE NO ACTION ); CREATE INDEX fk_AP_APP_RELEASE_AP_APP1_idx ON AP_APP_RELEASE (AP_APP_ID ASC); - -- ----------------------------------------------------- -- Table AP_APP_REVIEW -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS AP_APP_REVIEW( - ID INTEGER NOT NULL AUTO_INCREMENT, - TENANT_ID INTEGER NOT NULL, - COMMENT TEXT NOT NULL, - ROOT_PARENT_ID INTEGER NOT NULL, - IMMEDIATE_PARENT_ID INTEGER NOT NULL, - CREATED_AT BIGINT NOT NULL, - MODIFIED_AT BIGINT NOT NULL, - RATING INTEGER NULL, - USERNAME VARCHAR(45) NOT NULL, - ACTIVE_REVIEW BOOLEAN NOT NULL DEFAULT TRUE, - AP_APP_RELEASE_ID INTEGER NOT NULL, - PRIMARY KEY (ID), - CONSTRAINT fk_AP_APP_COMMENT_AP_APP_RELEASE1 - FOREIGN KEY (AP_APP_RELEASE_ID) - REFERENCES AP_APP_RELEASE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION + ID INTEGER NOT NULL AUTO_INCREMENT, + TENANT_ID INTEGER NOT NULL, + COMMENT TEXT NOT NULL, + ROOT_PARENT_ID INTEGER NOT NULL, + IMMEDIATE_PARENT_ID INTEGER NOT NULL, + CREATED_AT BIGINT NOT NULL, + MODIFIED_AT BIGINT NOT NULL, + RATING INTEGER NULL, + USERNAME VARCHAR(45) NOT NULL, + ACTIVE_REVIEW BOOLEAN NOT NULL DEFAULT TRUE, + AP_APP_RELEASE_ID INTEGER NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT fk_AP_APP_COMMENT_AP_APP_RELEASE1 FOREIGN KEY (AP_APP_RELEASE_ID) REFERENCES AP_APP_RELEASE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION ); CREATE INDEX fk_AP_APP_COMMENT_AP_APP_RELEASE1_idx ON AP_APP_REVIEW (AP_APP_RELEASE_ID ASC); - -- ----------------------------------------------------- -- Table AP_APP_LIFECYCLE_STATE -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS AP_APP_LIFECYCLE_STATE( - ID INTEGER NOT NULL AUTO_INCREMENT, - CURRENT_STATE VARCHAR(45) NOT NULL, - PREVIOUS_STATE VARCHAR(45) NOT NULL, - TENANT_ID INTEGER NOT NULL, - UPDATED_BY VARCHAR(100) NOT NULL, - UPDATED_AT BIGINT NOT NULL, - AP_APP_RELEASE_ID INTEGER NOT NULL, - REASON TEXT DEFAULT NULL, - PRIMARY KEY (ID), - CONSTRAINT fk_AP_APP_LIFECYCLE_STATE_AP_APP_RELEASE1 - FOREIGN KEY (AP_APP_RELEASE_ID) - REFERENCES AP_APP_RELEASE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION + ID INTEGER NOT NULL AUTO_INCREMENT, + CURRENT_STATE VARCHAR(45) NOT NULL, + PREVIOUS_STATE VARCHAR(45) NOT NULL, + TENANT_ID INTEGER NOT NULL, + UPDATED_BY VARCHAR(100) NOT NULL, + UPDATED_AT BIGINT NOT NULL, + AP_APP_RELEASE_ID INTEGER NOT NULL, + REASON TEXT DEFAULT NULL, + PRIMARY KEY (ID), + CONSTRAINT fk_AP_APP_LIFECYCLE_STATE_AP_APP_RELEASE1 FOREIGN KEY (AP_APP_RELEASE_ID) REFERENCES AP_APP_RELEASE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION ); -CREATE INDEX fk_AP_APP_LIFECYCLE_STATE_AP_APP_RELEASE1_idx ON AP_APP_LIFECYCLE_STATE( AP_APP_RELEASE_ID ASC); - +CREATE INDEX fk_AP_APP_LIFECYCLE_STATE_AP_APP_RELEASE1_idx ON AP_APP_LIFECYCLE_STATE(AP_APP_RELEASE_ID ASC); -- ----------------------------------------------------- -- Table AP_APP_TAG -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS AP_APP_TAG( - ID INTEGER NOT NULL AUTO_INCREMENT, - TENANT_ID INTEGER NOT NULL, - TAG VARCHAR(100) NOT NULL, - PRIMARY KEY (ID) + ID INTEGER NOT NULL AUTO_INCREMENT, + TENANT_ID INTEGER NOT NULL, + TAG VARCHAR(100) NOT NULL, + PRIMARY KEY (ID) ); - -- ----------------------------------------------------- -- Table AP_DEVICE_SUBSCRIPTION -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS AP_DEVICE_SUBSCRIPTION( - ID INTEGER NOT NULL AUTO_INCREMENT, - TENANT_ID INTEGER NOT NULL, - SUBSCRIBED_BY VARCHAR(100) NOT NULL, - SUBSCRIBED_TIMESTAMP TIMESTAMP NOT NULL, - UNSUBSCRIBED BOOLEAN NOT NULL DEFAULT false, - UNSUBSCRIBED_BY VARCHAR(100) NULL DEFAULT NULL, - UNSUBSCRIBED_TIMESTAMP TIMESTAMP NULL DEFAULT NULL, - ACTION_TRIGGERED_FROM VARCHAR(45) NOT NULL, - STATUS VARCHAR(45) NOT NULL, - DM_DEVICE_ID INTEGER NOT NULL, - AP_APP_RELEASE_ID INTEGER NOT NULL, - PRIMARY KEY (ID), - CONSTRAINT fk_AP_DEVICE_SUBSCRIPTION_AP_APP_RELEASE1 - FOREIGN KEY (AP_APP_RELEASE_ID) - REFERENCES AP_APP_RELEASE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION + ID INTEGER NOT NULL AUTO_INCREMENT, + TENANT_ID INTEGER NOT NULL, + SUBSCRIBED_BY VARCHAR(100) NOT NULL, + SUBSCRIBED_TIMESTAMP TIMESTAMP NOT NULL, + UNSUBSCRIBED BOOLEAN NOT NULL DEFAULT false, + UNSUBSCRIBED_BY VARCHAR(100) NULL DEFAULT NULL, + UNSUBSCRIBED_TIMESTAMP TIMESTAMP NULL DEFAULT NULL, + ACTION_TRIGGERED_FROM VARCHAR(45) NOT NULL, + STATUS VARCHAR(45) NOT NULL, + DM_DEVICE_ID INTEGER NOT NULL, + AP_APP_RELEASE_ID INTEGER NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT fk_AP_DEVICE_SUBSCRIPTION_AP_APP_RELEASE1 FOREIGN KEY (AP_APP_RELEASE_ID) REFERENCES AP_APP_RELEASE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION ); CREATE INDEX fk_AP_DEVICE_SUBSCRIPTION_AP_APP_RELEASE1_idx ON AP_DEVICE_SUBSCRIPTION (AP_APP_RELEASE_ID ASC); - -- ----------------------------------------------------- -- Table AP_GROUP_SUBSCRIPTION -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS AP_GROUP_SUBSCRIPTION( - ID INTEGER NOT NULL AUTO_INCREMENT, - TENANT_ID INTEGER NOT NULL, - SUBSCRIBED_BY VARCHAR(100) NOT NULL, - SUBSCRIBED_TIMESTAMP TIMESTAMP NOT NULL, - UNSUBSCRIBED BOOLEAN NOT NULL DEFAULT false, - UNSUBSCRIBED_BY VARCHAR(100) NULL DEFAULT NULL, - UNSUBSCRIBED_TIMESTAMP TIMESTAMP NULL DEFAULT NULL, - GROUP_NAME VARCHAR(100) NOT NULL, - AP_APP_RELEASE_ID INTEGER NOT NULL, - PRIMARY KEY (ID), - CONSTRAINT fk_AP_GROUP_SUBSCRIPTION_AP_APP_RELEASE1 - FOREIGN KEY (AP_APP_RELEASE_ID) - REFERENCES AP_APP_RELEASE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION + ID INTEGER NOT NULL AUTO_INCREMENT, + TENANT_ID INTEGER NOT NULL, + SUBSCRIBED_BY VARCHAR(100) NOT NULL, + SUBSCRIBED_TIMESTAMP TIMESTAMP NOT NULL, + UNSUBSCRIBED BOOLEAN NOT NULL DEFAULT false, + UNSUBSCRIBED_BY VARCHAR(100) NULL DEFAULT NULL, + UNSUBSCRIBED_TIMESTAMP TIMESTAMP NULL DEFAULT NULL, + GROUP_NAME VARCHAR(100) NOT NULL, + AP_APP_RELEASE_ID INTEGER NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT fk_AP_GROUP_SUBSCRIPTION_AP_APP_RELEASE1 FOREIGN KEY (AP_APP_RELEASE_ID) REFERENCES AP_APP_RELEASE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION ); CREATE INDEX fk_AP_GROUP_SUBSCRIPTION_AP_APP_RELEASE1_idx ON AP_GROUP_SUBSCRIPTION (AP_APP_RELEASE_ID ASC); - -- ----------------------------------------------------- -- Table AP_ROLE_SUBSCRIPTION -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS AP_ROLE_SUBSCRIPTION( - ID INTEGER NOT NULL AUTO_INCREMENT, - TENANT_ID INTEGER NOT NULL, - ROLE_NAME VARCHAR(100) NOT NULL, - SUBSCRIBED_BY VARCHAR(100) NOT NULL, - SUBSCRIBED_TIMESTAMP TIMESTAMP NOT NULL, - UNSUBSCRIBED BOOLEAN NOT NULL DEFAULT false, - UNSUBSCRIBED_BY VARCHAR(100) NULL DEFAULT NULL, - UNSUBSCRIBED_TIMESTAMP TIMESTAMP NULL DEFAULT NULL, - AP_APP_RELEASE_ID INTEGER NOT NULL, - PRIMARY KEY (ID), - CONSTRAINT fk_AP_ROLE_SUBSCRIPTION_AP_APP_RELEASE1 - FOREIGN KEY (AP_APP_RELEASE_ID) - REFERENCES AP_APP_RELEASE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION + ID INTEGER NOT NULL AUTO_INCREMENT, + TENANT_ID INTEGER NOT NULL, + ROLE_NAME VARCHAR(100) NOT NULL, + SUBSCRIBED_BY VARCHAR(100) NOT NULL, + SUBSCRIBED_TIMESTAMP TIMESTAMP NOT NULL, + UNSUBSCRIBED BOOLEAN NOT NULL DEFAULT false, + UNSUBSCRIBED_BY VARCHAR(100) NULL DEFAULT NULL, + UNSUBSCRIBED_TIMESTAMP TIMESTAMP NULL DEFAULT NULL, + AP_APP_RELEASE_ID INTEGER NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT fk_AP_ROLE_SUBSCRIPTION_AP_APP_RELEASE1 FOREIGN KEY (AP_APP_RELEASE_ID) REFERENCES AP_APP_RELEASE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION ); CREATE INDEX fk_AP_ROLE_SUBSCRIPTION_AP_APP_RELEASE1_idx ON AP_ROLE_SUBSCRIPTION (AP_APP_RELEASE_ID ASC); - -- ----------------------------------------------------- -- Table AP_UNRESTRICTED_ROLE -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS AP_UNRESTRICTED_ROLE( - ID INTEGER NOT NULL AUTO_INCREMENT, - TENANT_ID INTEGER NOT NULL, - ROLE VARCHAR(45) NOT NULL, - AP_APP_ID INTEGER NOT NULL, - PRIMARY KEY (ID), - CONSTRAINT fk_AP_APP_VISIBILITY_AP_APP1 - FOREIGN KEY (AP_APP_ID) - REFERENCES AP_APP (ID) ON DELETE NO ACTION ON UPDATE NO ACTION + ID INTEGER NOT NULL AUTO_INCREMENT, + TENANT_ID INTEGER NOT NULL, + ROLE VARCHAR(45) NOT NULL, + AP_APP_ID INTEGER NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT fk_AP_APP_VISIBILITY_AP_APP1 FOREIGN KEY (AP_APP_ID) REFERENCES AP_APP (ID) ON DELETE NO ACTION ON UPDATE NO ACTION ); CREATE INDEX fk_AP_APP_VISIBILITY_AP_APP1_idx ON AP_UNRESTRICTED_ROLE (AP_APP_ID ASC); - -- ----------------------------------------------------- -- Table AP_USER_SUBSCRIPTION -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS AP_USER_SUBSCRIPTION( - ID INTEGER NOT NULL AUTO_INCREMENT, - TENANT_ID INTEGER NOT NULL, - USER_NAME VARCHAR(100) NOT NULL, - SUBSCRIBED_BY VARCHAR(100) NOT NULL, - SUBSCRIBED_TIMESTAMP TIMESTAMP NOT NULL, - UNSUBSCRIBED BOOLEAN NOT NULL DEFAULT false, - UNSUBSCRIBED_BY VARCHAR(100) NULL DEFAULT NULL, - UNSUBSCRIBED_TIMESTAMP TIMESTAMP NULL DEFAULT NULL, - AP_APP_RELEASE_ID INTEGER NOT NULL, - PRIMARY KEY (ID), - CONSTRAINT fk_AP_USER_SUBSCRIPTION_AP_APP_RELEASE1 - FOREIGN KEY (AP_APP_RELEASE_ID) - REFERENCES AP_APP_RELEASE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION + ID INTEGER NOT NULL AUTO_INCREMENT, + TENANT_ID INTEGER NOT NULL, + USER_NAME VARCHAR(100) NOT NULL, + SUBSCRIBED_BY VARCHAR(100) NOT NULL, + SUBSCRIBED_TIMESTAMP TIMESTAMP NOT NULL, + UNSUBSCRIBED BOOLEAN NOT NULL DEFAULT false, + UNSUBSCRIBED_BY VARCHAR(100) NULL DEFAULT NULL, + UNSUBSCRIBED_TIMESTAMP TIMESTAMP NULL DEFAULT NULL, + AP_APP_RELEASE_ID INTEGER NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT fk_AP_USER_SUBSCRIPTION_AP_APP_RELEASE1 FOREIGN KEY (AP_APP_RELEASE_ID) REFERENCES AP_APP_RELEASE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION ); CREATE INDEX fk_AP_USER_SUBSCRIPTION_AP_APP_RELEASE1_idx ON AP_USER_SUBSCRIPTION (AP_APP_RELEASE_ID ASC); - -- ----------------------------------------------------- -- Table AP_APP_CATEGORY -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS AP_APP_CATEGORY( - ID INTEGER NOT NULL AUTO_INCREMENT, - TENANT_ID INTEGER NOT NULL, - CATEGORY VARCHAR(45) NOT NULL, - CATEGORY_ICON VARCHAR(45) NULL, - PRIMARY KEY (ID) + ID INTEGER NOT NULL AUTO_INCREMENT, + TENANT_ID INTEGER NOT NULL, + CATEGORY VARCHAR(45) NOT NULL, + CATEGORY_ICON VARCHAR(45) NULL, + PRIMARY KEY (ID) ); - -- ----------------------------------------------------- -- Table AP_APP_TAG_MAPPING -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS AP_APP_TAG_MAPPING( -ID INTEGER NOT NULL AUTO_INCREMENT, -TENANT_ID INTEGER NOT NULL, -AP_APP_TAG_ID INTEGER NOT NULL, -AP_APP_ID INTEGER NOT NULL, -PRIMARY KEY (ID), -CONSTRAINT fk_AP_APP_TAG_copy1_AP_APP_TAG1 - FOREIGN KEY (AP_APP_TAG_ID) - REFERENCES AP_APP_TAG (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, -CONSTRAINT fk_AP_APP_TAG_copy1_AP_APP1 - FOREIGN KEY (AP_APP_ID) - REFERENCES AP_APP (ID) ON DELETE NO ACTION ON UPDATE NO ACTION + ID INTEGER NOT NULL AUTO_INCREMENT, + TENANT_ID INTEGER NOT NULL, + AP_APP_TAG_ID INTEGER NOT NULL, + AP_APP_ID INTEGER NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT fk_AP_APP_TAG_copy1_AP_APP_TAG1 FOREIGN KEY (AP_APP_TAG_ID) REFERENCES AP_APP_TAG (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT fk_AP_APP_TAG_copy1_AP_APP1 FOREIGN KEY (AP_APP_ID) REFERENCES AP_APP (ID) ON DELETE NO ACTION ON UPDATE NO ACTION ); CREATE INDEX fk_AP_APP_TAG_copy1_AP_APP_TAG1_idx ON AP_APP_TAG_MAPPING (AP_APP_TAG_ID ASC); CREATE INDEX fk_AP_APP_TAG_copy1_AP_APP1_idx ON AP_APP_TAG_MAPPING (AP_APP_ID ASC); - -- ----------------------------------------------------- -- Table AP_APP_CATEGORY_MAPPING -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS AP_APP_CATEGORY_MAPPING( -ID INTEGER NOT NULL AUTO_INCREMENT, -TENANT_ID INTEGER NOT NULL, -AP_APP_CATEGORY_ID INTEGER NOT NULL, -AP_APP_ID INTEGER NOT NULL, -PRIMARY KEY (ID), -CONSTRAINT fk_AP_APP_CATEGORY_copy1_AP_APP_CATEGORY1 - FOREIGN KEY (AP_APP_CATEGORY_ID) - REFERENCES AP_APP_CATEGORY (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, -CONSTRAINT fk_AP_APP_CATEGORY_copy1_AP_APP1 - FOREIGN KEY (AP_APP_ID) - REFERENCES AP_APP (ID) ON DELETE NO ACTION ON UPDATE NO ACTION + ID INTEGER NOT NULL AUTO_INCREMENT, + TENANT_ID INTEGER NOT NULL, + AP_APP_CATEGORY_ID INTEGER NOT NULL, + AP_APP_ID INTEGER NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT fk_AP_APP_CATEGORY_copy1_AP_APP_CATEGORY1 FOREIGN KEY (AP_APP_CATEGORY_ID) REFERENCES AP_APP_CATEGORY (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT fk_AP_APP_CATEGORY_copy1_AP_APP1 FOREIGN KEY (AP_APP_ID) REFERENCES AP_APP (ID) ON DELETE NO ACTION ON UPDATE NO ACTION ); CREATE INDEX fk_AP_APP_CATEGORY_copy1_AP_APP_CATEGORY1_idx ON AP_APP_CATEGORY_MAPPING (AP_APP_CATEGORY_ID ASC); CREATE INDEX fk_AP_APP_CATEGORY_copy1_AP_APP1_idx ON AP_APP_CATEGORY_MAPPING (AP_APP_ID ASC); - -- ----------------------------------------------------- -- Table AP_APP_SUB_OP_MAPPING -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS AP_APP_SUB_OP_MAPPING ( - ID INTEGER NOT NULL AUTO_INCREMENT, - TENANT_ID INTEGER NOT NULL, - OPERATION_ID INTEGER NOT NULL, - AP_DEVICE_SUBSCRIPTION_ID INTEGER NOT NULL, - PRIMARY KEY (ID), - CONSTRAINT fk_AP_APP_SUB_OP_MAPPING_AP_DEVICE_SUBSCRIPTION1 - FOREIGN KEY (AP_DEVICE_SUBSCRIPTION_ID) - REFERENCES AP_DEVICE_SUBSCRIPTION (ID) ON DELETE NO ACTION ON UPDATE NO ACTION + ID INTEGER NOT NULL AUTO_INCREMENT, + TENANT_ID INTEGER NOT NULL, + OPERATION_ID INTEGER NOT NULL, + AP_DEVICE_SUBSCRIPTION_ID INTEGER NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT fk_AP_APP_SUB_OP_MAPPING_AP_DEVICE_SUBSCRIPTION1 FOREIGN KEY (AP_DEVICE_SUBSCRIPTION_ID) REFERENCES AP_DEVICE_SUBSCRIPTION (ID) ON DELETE NO ACTION ON UPDATE NO ACTION ); CREATE INDEX fk_AP_APP_SUB_OP_MAPPING_AP_DEVICE_SUBSCRIPTION1_idx ON AP_APP_SUB_OP_MAPPING (AP_DEVICE_SUBSCRIPTION_ID ASC); - -- ----------------------------------------------------- -- Table AP_SCHEDULED_SUBSCRIPTION -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS AP_SCHEDULED_SUBSCRIPTION( - ID INTEGER NOT NULL AUTO_INCREMENT, - TASK_NAME VARCHAR(100) NOT NULL, - APPLICATION_UUID VARCHAR(36) NOT NULL, - SUBSCRIBER_LIST TEXT NOT NULL, - STATUS VARCHAR(15) NOT NULL, - SCHEDULED_AT BIGINT NOT NULL, - SCHEDULED_BY VARCHAR(100) NOT NULL, - SCHEDULED_TIMESTAMP TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, - DELETED BOOLEAN, - PRIMARY KEY (ID) + ID INTEGER NOT NULL AUTO_INCREMENT, + TASK_NAME VARCHAR(100) NOT NULL, + APPLICATION_UUID VARCHAR(36) NOT NULL, + SUBSCRIBER_LIST TEXT NOT NULL, + STATUS VARCHAR(15) NOT NULL, + SCHEDULED_AT BIGINT NOT NULL, + SCHEDULED_BY VARCHAR(100) NOT NULL, + SCHEDULED_TIMESTAMP TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + DELETED BOOLEAN, + PRIMARY KEY (ID) ); - -- ----------------------------------------------------- -- Table AP_IDENTITY_SERVER -- ----------------------------------------------------- -CREATE TABLE IF NOT EXISTS AP_IDENTITY_SERVER -( - ID INT AUTO_INCREMENT - PRIMARY KEY, - NAME VARCHAR(255) NOT NULL, - DESCRIPTION VARCHAR(255) NOT NULL, - URL VARCHAR(255) NOT NULL, - SP_APPS_URI VARCHAR(255) NOT NULL, - SP_APPS_API VARCHAR(255) NULL, - TENANT_ID INT NOT NULL, - USERNAME VARCHAR(255) NOT NULL, - PASSWORD VARCHAR(255) NOT NULL -) - --- ----------------------------------------------------- +CREATE TABLE IF NOT EXISTS AP_IDENTITY_SERVER ( + ID INT AUTO_INCREMENT PRIMARY KEY, + NAME VARCHAR(255) NOT NULL, + DESCRIPTION VARCHAR(255) NOT NULL, + URL VARCHAR(255) NOT NULL, + SP_APPS_URI VARCHAR(255) NOT NULL, + SP_APPS_API VARCHAR(255) NULL, + TENANT_ID INT NOT NULL, + USERNAME VARCHAR(255) NOT NULL, + PASSWORD VARCHAR(255) NOT NULL +) -- ----------------------------------------------------- -- Table AP_IS_SP_APP_MAPPING -- -----------------------------------------------------; - CREATE TABLE IF NOT EXISTS AP_IS_SP_APP_MAPPING - ( - ID INT AUTO_INCREMENT - PRIMARY KEY, - SP_UID VARCHAR(255) NOT NULL, - AP_APP_ID INT NOT NULL, - IS_ID INT NOT NULL, - TENANT_ID INT NOT NULL, - CONSTRAINT AP_IS_SP_APP_MAPPING_AP_APP_ID_fk - FOREIGN KEY (AP_APP_ID) REFERENCES AP_APP (ID), - CONSTRAINT AP_IS_SP_APP_MAPPING_AP_IDENTITY_SERVER_ID_fk - FOREIGN KEY (IS_ID) REFERENCES AP_IDENTITY_SERVER (ID) - ); - +CREATE TABLE IF NOT EXISTS AP_IS_SP_APP_MAPPING ( + ID INT AUTO_INCREMENT PRIMARY KEY, + SP_UID VARCHAR(255) NOT NULL, + AP_APP_ID INT NOT NULL, + IS_ID INT NOT NULL, + TENANT_ID INT NOT NULL, + CONSTRAINT AP_IS_SP_APP_MAPPING_AP_APP_ID_fk FOREIGN KEY (AP_APP_ID) REFERENCES AP_APP (ID), + CONSTRAINT AP_IS_SP_APP_MAPPING_AP_IDENTITY_SERVER_ID_fk FOREIGN KEY (IS_ID) REFERENCES AP_IDENTITY_SERVER (ID) +); +-- Table AP_APP_FAVOURITES +-- ----------------------------------------------------- +CREATE TABLE IF NOT EXISTS AP_APP_FAVOURITES( + ID INTEGER NOT NULL AUTO_INCREMENT, + AP_APP_ID INTEGER NOT NULL, + USER_NAME VARCHAR(100) NOT NULL, + TENANT_ID INTEGER NOT NULL, + PRIMARY KEY(ID), + CONSTRAINT AP_APP_FAVOURITES_AP_APP_ID_fk FOREIGN KEY (AP_APP_ID) REFERENCES AP_APP (ID) +); \ No newline at end of file diff --git a/features/application-mgt/io.entgra.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/oracle.sql b/features/application-mgt/io.entgra.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/oracle.sql index edbc277ab25..b15ef930f6a 100644 --- a/features/application-mgt/io.entgra.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/oracle.sql +++ b/features/application-mgt/io.entgra.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/oracle.sql @@ -382,3 +382,15 @@ CREATE TABLE AP_SCHEDULED_SUBSCRIPTION ( REFERENCES AP_APP_RELEASE (UUID) ) / + +CREATE TABLE IF NOT EXISTS AP_APP_FAVOURITES( + ID number GENERATED BY DEFAULT AS IDENTITY(START WITH 1) NOT NULL, + AP_APP_ID INTEGER NOT NULL, + USER_NAME VARCHAR(100) NOT NULL, + TENANT_ID INTEGER NOT NULL, + PRIMARY KEY(ID), + CONSTRAINT AP_APP_FAVOURITES_AP_APP_ID_fk + FOREIGN KEY (AP_APP_ID) + REFERENCES AP_APP (ID) +) +/ diff --git a/features/application-mgt/io.entgra.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/postgresql.sql b/features/application-mgt/io.entgra.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/postgresql.sql index c799b032453..e5f83986d68 100644 --- a/features/application-mgt/io.entgra.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/postgresql.sql +++ b/features/application-mgt/io.entgra.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/postgresql.sql @@ -305,3 +305,18 @@ CREATE TABLE IF NOT EXISTS AP_SCHEDULED_SUBSCRIPTION( DELETED BOOLEAN, PRIMARY KEY (ID) ); + + +-- ----------------------------------------------------- +-- Table AP_APP_FAVOURITES +-- ----------------------------------------------------- +CREATE TABLE IF NOT EXISTS AP_APP_FAVOURITES( + ID INTEGER NOT NULL DEFAULT NEXTVAL ('AP_APP_FAVOURITES_seq'), + AP_APP_ID INTEGER NOT NULL, + USER_NAME VARCHAR(100) NOT NULL, + TENANT_ID INTEGER NOT NULL, + PRIMARY KEY(ID), + CONSTRAINT AP_APP_FAVOURITES_AP_APP_ID_fk + FOREIGN KEY (AP_APP_ID) + REFERENCES AP_APP (ID) ON DELETE NO ACTION ON UPDATE NO ACTION +); diff --git a/features/application-mgt/pom.xml b/features/application-mgt/pom.xml index 533845f0fb1..6a53d9fa88f 100644 --- a/features/application-mgt/pom.xml +++ b/features/application-mgt/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../../pom.xml diff --git a/features/certificate-mgt/org.wso2.carbon.certificate.mgt.api.feature/pom.xml b/features/certificate-mgt/org.wso2.carbon.certificate.mgt.api.feature/pom.xml index aaecb0c3a5c..a9ea4dfba2a 100644 --- a/features/certificate-mgt/org.wso2.carbon.certificate.mgt.api.feature/pom.xml +++ b/features/certificate-mgt/org.wso2.carbon.certificate.mgt.api.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt certificate-mgt-feature - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/features/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api.feature/pom.xml b/features/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api.feature/pom.xml index 1e4becdcebb..dcc3615536c 100644 --- a/features/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api.feature/pom.xml +++ b/features/certificate-mgt/org.wso2.carbon.certificate.mgt.cert.admin.api.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt certificate-mgt-feature - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/features/certificate-mgt/org.wso2.carbon.certificate.mgt.server.feature/pom.xml b/features/certificate-mgt/org.wso2.carbon.certificate.mgt.server.feature/pom.xml index f0a7162ba93..b5f340e61c5 100644 --- a/features/certificate-mgt/org.wso2.carbon.certificate.mgt.server.feature/pom.xml +++ b/features/certificate-mgt/org.wso2.carbon.certificate.mgt.server.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt certificate-mgt-feature - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/features/certificate-mgt/pom.xml b/features/certificate-mgt/pom.xml index 57fb5023429..662c4325af6 100644 --- a/features/certificate-mgt/pom.xml +++ b/features/certificate-mgt/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../../pom.xml diff --git a/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.device.type.deployer.feature/pom.xml b/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.device.type.deployer.feature/pom.xml index e86ca55b36e..c6d3b7a8567 100644 --- a/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.device.type.deployer.feature/pom.xml +++ b/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.device.type.deployer.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt device-mgt-extensions-feature - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.fcm.feature/pom.xml b/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.fcm.feature/pom.xml index f4691b0b766..b2d0d3b1530 100644 --- a/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.fcm.feature/pom.xml +++ b/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.fcm.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt device-mgt-extensions-feature - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.http.feature/pom.xml b/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.http.feature/pom.xml index 59f61d98fd3..16c161caf71 100644 --- a/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.http.feature/pom.xml +++ b/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.http.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt device-mgt-extensions-feature - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.mqtt.feature/pom.xml b/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.mqtt.feature/pom.xml index d00139fadf5..9f34db1a965 100644 --- a/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.mqtt.feature/pom.xml +++ b/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.mqtt.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt device-mgt-extensions-feature - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.xmpp.feature/pom.xml b/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.xmpp.feature/pom.xml index ca96e268196..27a6c0f30e9 100644 --- a/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.xmpp.feature/pom.xml +++ b/features/device-mgt-extensions/org.wso2.carbon.device.mgt.extensions.push.notification.provider.xmpp.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt device-mgt-extensions-feature - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/features/device-mgt-extensions/pom.xml b/features/device-mgt-extensions/pom.xml index 0badd067530..a513e38e589 100644 --- a/features/device-mgt-extensions/pom.xml +++ b/features/device-mgt-extensions/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../../pom.xml diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.api.feature/pom.xml b/features/device-mgt/org.wso2.carbon.device.mgt.api.feature/pom.xml index b89c5309066..ee84b3e83bd 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.api.feature/pom.xml +++ b/features/device-mgt/org.wso2.carbon.device.mgt.api.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt device-mgt-feature - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/pom.xml b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/pom.xml index 03f77097eaa..6420e924c80 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/pom.xml +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt device-mgt-feature - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/cdm-config.xml b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/cdm-config.xml index d15901005e5..50e3afd8245 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/cdm-config.xml +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/cdm-config.xml @@ -178,5 +178,9 @@ + + + + diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/datasources/reporting-mgt-datasources.xml b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/datasources/reporting-mgt-datasources.xml new file mode 100644 index 00000000000..4a518e77ac8 --- /dev/null +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/datasources/reporting-mgt-datasources.xml @@ -0,0 +1,48 @@ + + + + + org.wso2.carbon.ndatasource.rdbms.RDBMSDataSourceReader + + + + + + jdbc/MDM_REPORTS_DS + The datasource used for Report Management + + jdbc/MDM_REPORTS_DS + + + + jdbc:mysql://localhost:3306/MDM_REPORTS?autoReconnect=true&relaxAutoCommit=true&useSSL=false + root + root + com.mysql.cj.jdbc.Driver + 50 + 60000 + true + SELECT 1 + 30000 + + + + + + diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/mdm-ui-config.xml b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/mdm-ui-config.xml index bc6be595996..a8b15843254 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/mdm-ui-config.xml +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/mdm-ui-config.xml @@ -26,6 +26,7 @@ 10000 + analytics_management application_management device_management subscription_management @@ -34,6 +35,7 @@ true + perm:grafana:api:view perm:app:review:view perm:app:review:update perm:app:publisher:service-provider:view @@ -43,6 +45,7 @@ perm:app:publisher:view perm:app:publisher:update perm:app:store:view + perm:app:store:modify perm:app:subscription:install perm:app:subscription:uninstall perm:admin:app:review:update @@ -50,6 +53,7 @@ perm:admin:app:publisher:update perm:admin:app:review:update perm:admin:app:subscription:view + perm:admin:app:subscription:modify perm:device-types:types perm:enterprise:modify perm:enterprise:view @@ -187,6 +191,7 @@ perm:metadata:update perm:android:google-account perm:android:update-default-sim + perm:android:google-account perm:android:info diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/reporting-mgt.xml b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/reporting-mgt.xml new file mode 100644 index 00000000000..b768822971d --- /dev/null +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf/reporting-mgt.xml @@ -0,0 +1,21 @@ + + + + jdbc/MDM_REPORTS_DS + diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/cdm-config.xml.j2 b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/cdm-config.xml.j2 index 5a25843f940..19feedc4b12 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/cdm-config.xml.j2 +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/cdm-config.xml.j2 @@ -44,7 +44,7 @@ org.wso2.carbon.device.mgt.extensions.push.notification.provider.xmpp.XMPPBasedPushNotificationProvider {% if device_mgt_conf.push_notification_conf.push_notification_providers is defined %} {%- for push_notification_provider in device_mgt_conf.push_notification_conf.push_notification_providers -%} - {{push_notification_provider.name}} + {{push_notification_provider}} {% endfor %} {% endif %} @@ -311,5 +311,34 @@ + + + + + + + + + + + + {% if device_mgt_conf.operation_timeout_conf is defined %} + {% for timeout_conf in device_mgt_conf.operation_timeout_conf %} + + + {% for device_type in timeout_conf.device_types %} + {{device_type}} + {% endfor %} + + + {{timeout_conf.code}} + {{timeout_conf.initial_status}} + {{timeout_conf.timeout}} + {{timeout_conf.next_status}} + + {% endfor %} + {% endif%} + + diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/datasources/reporting-mgt-datasources.xml.j2 b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/datasources/reporting-mgt-datasources.xml.j2 new file mode 100644 index 00000000000..4a518e77ac8 --- /dev/null +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/datasources/reporting-mgt-datasources.xml.j2 @@ -0,0 +1,48 @@ + + + + + org.wso2.carbon.ndatasource.rdbms.RDBMSDataSourceReader + + + + + + jdbc/MDM_REPORTS_DS + The datasource used for Report Management + + jdbc/MDM_REPORTS_DS + + + + jdbc:mysql://localhost:3306/MDM_REPORTS?autoReconnect=true&relaxAutoCommit=true&useSSL=false + root + root + com.mysql.cj.jdbc.Driver + 50 + 60000 + true + SELECT 1 + 30000 + + + + + + diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/mdm-ui-config.xml.j2 b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/mdm-ui-config.xml.j2 index 244e90ce97c..292234023cb 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/mdm-ui-config.xml.j2 +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/mdm-ui-config.xml.j2 @@ -71,6 +71,7 @@ perm:admin:app:publisher:update perm:admin:app:review:update perm:admin:app:subscription:view + perm:admin:app:subscription:modify perm:device-types:types perm:enterprise:modify perm:enterprise:view @@ -187,6 +188,8 @@ perm:android:location perm:android:clear-password perm:android:control-camera + perm:android:update-default-sim + perm:android:google-account perm:android:enterprise-wipe perm:android:wipe perm:android:ring @@ -219,6 +222,7 @@ perm:ios:app-configurations perm:mac-os:shut-down perm:mac-os:restart + perm:android:info {% if mdm_ui_conf.scopes is defined %} {%- for scope in mdm_ui_conf.scopes -%} {{scope}} diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/reporting-mgt.xml.j2 b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/reporting-mgt.xml.j2 new file mode 100644 index 00000000000..b768822971d --- /dev/null +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/reporting-mgt.xml.j2 @@ -0,0 +1,21 @@ + + + + jdbc/MDM_REPORTS_DS + diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/h2.sql b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/h2.sql index ed9ce41c7dc..6372b87e166 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/h2.sql +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/h2.sql @@ -108,7 +108,19 @@ CREATE TABLE IF NOT EXISTS DM_ENROLMENT ( DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT uk_dm_device_enrolment UNIQUE (DEVICE_ID, OWNER, OWNERSHIP, TENANT_ID) ); - +CREATE TABLE IF NOT EXISTS DM_DEVICE_STATUS ( + ID INTEGER AUTO_INCREMENT NOT NULL, + ENROLMENT_ID INTEGER NOT NULL, + DEVICE_ID INTEGER NOT NULL, + STATUS VARCHAR(50) DEFAULT NULL, + UPDATE_TIME TIMESTAMP DEFAULT NULL, + CHANGED_BY VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT fk_dm_device_status_device FOREIGN KEY (DEVICE_ID) REFERENCES + DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT fk_dm_device_status_enrolment FOREIGN KEY (ENROLMENT_ID) REFERENCES + DM_ENROLMENT (ID) ON DELETE NO ACTION ON UPDATE NO ACTION +); CREATE TABLE IF NOT EXISTS DM_ENROLMENT_OP_MAPPING ( ID INTEGER AUTO_INCREMENT NOT NULL, ENROLMENT_ID INTEGER NOT NULL, diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mssql.sql b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mssql.sql index f0cbcf24583..bd301d6641d 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mssql.sql +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mssql.sql @@ -120,6 +120,20 @@ CREATE TABLE DM_OPERATION ( PRIMARY KEY (ID) ); +IF NOT EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[DM_DEVICE_STATUS]') AND TYPE IN (N'U')) +CREATE TABLE DM_DEVICE_STATUS ( + ID INTEGER IDENTITY(1,1) NOT NULL, + ENROLMENT_ID INTEGER NOT NULL, + DEVICE_ID INTEGER NOT NULL, + STATUS VARCHAR(50) DEFAULT NULL, + UPDATE_TIME DATETIME2 DEFAULT NULL, + CHANGED_BY VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT FK_DM_DEVICE_STATUS_DEVICE FOREIGN KEY (DEVICE_ID) REFERENCES + DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT FK_DM_DEVICE_STATUS_ENROLMENT FOREIGN KEY (ENROLMENT_ID) REFERENCES + DM_ENROLMENT (ID) ON DELETE NO ACTION ON UPDATE NO ACTION +); IF NOT EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[DM_ENROLMENT]') AND TYPE IN (N'U')) CREATE TABLE DM_ENROLMENT ( diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mysql.sql b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mysql.sql index e11919b0a0d..b191a177b77 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mysql.sql +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mysql.sql @@ -122,7 +122,19 @@ CREATE TABLE IF NOT EXISTS DM_ENROLMENT ( CONSTRAINT FK_DM_DEVICE_ENROLMENT FOREIGN KEY (DEVICE_ID) REFERENCES DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION )ENGINE = InnoDB; - +CREATE TABLE IF NOT EXISTS DM_DEVICE_STATUS ( + ID INTEGER AUTO_INCREMENT NOT NULL, + ENROLMENT_ID INTEGER NOT NULL, + DEVICE_ID INTEGER NOT NULL, + STATUS VARCHAR(50) DEFAULT NULL, + UPDATE_TIME TIMESTAMP DEFAULT NULL, + CHANGED_BY VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT FK_DM_DEVICE_STATUS_DEVICE FOREIGN KEY (DEVICE_ID) REFERENCES + DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT FK_DM_DEVICE_STATUS_ENROLMENT FOREIGN KEY (ENROLMENT_ID) REFERENCES + DM_ENROLMENT (ID) ON DELETE NO ACTION ON UPDATE NO ACTION +)ENGINE = InnoDB; CREATE INDEX IDX_ENROLMENT_FK_DEVICE_ID ON DM_ENROLMENT(DEVICE_ID); CREATE INDEX IDX_ENROLMENT_DEVICE_ID_TENANT_ID ON DM_ENROLMENT(DEVICE_ID, TENANT_ID); CREATE INDEX IDX_ENROLMENT_DEVICE_ID_TENANT_ID_STATUS ON DM_ENROLMENT(DEVICE_ID, TENANT_ID, STATUS); diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/oracle.sql b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/oracle.sql index 00adba35005..878d6095436 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/oracle.sql +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/oracle.sql @@ -197,6 +197,7 @@ WHEN (NEW.ID IS NULL) / + CREATE TABLE DM_ENROLMENT ( ID NUMBER(10) NOT NULL, DEVICE_ID NUMBER(10) NOT NULL, @@ -212,6 +213,18 @@ CREATE TABLE DM_ENROLMENT ( DM_DEVICE (ID) ) / +CREATE TABLE DM_DEVICE_STATUS ( + ID NUMBER(10) NOT NULL, + ENROLMENT_ID NUMBER(10) NOT NULL, + DEVICE_ID NUMBER(10) NOT NULL, + STATUS VARCHAR(50) DEFAULT NULL, + UPDATE_TIME TIMESTAMP(0) DEFAULT NULL, + CHANGED_BY VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT FK_DM_DEVICE_STATUS_DEVICE FOREIGN KEY (DEVICE_ID) REFERENCES DM_DEVICE (ID), + CONSTRAINT FK_DM_DEVICE_STATUS_ENROLMENT FOREIGN KEY (ENROLMENT_ID) REFERENCES DM_ENROLMENT (ID) +); +/ -- Generate ID using sequence and trigger CREATE SEQUENCE DM_ENROLMENT_seq START WITH 1 INCREMENT BY 1 NOCACHE / diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/postgresql.sql b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/postgresql.sql index d2537af3a88..a9f08b45f86 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/postgresql.sql +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/postgresql.sql @@ -102,6 +102,8 @@ CREATE TABLE IF NOT EXISTS DM_OPERATION ( PRIMARY KEY (ID) ); + + CREATE SEQUENCE DM_ENROLMENT_seq; CREATE TABLE IF NOT EXISTS DM_ENROLMENT ( @@ -118,7 +120,21 @@ CREATE TABLE IF NOT EXISTS DM_ENROLMENT ( CONSTRAINT FK_DM_DEVICE_ENROLMENT FOREIGN KEY (DEVICE_ID) REFERENCES DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION ); +CREATE SEQUENCE DM_DEVICE_STATUS_seq; +CREATE TABLE IF NOT EXISTS DM_DEVICE_STATUS ( + ID INTEGER DEFAULT NEXTVAL ('DM_DEVICE_STATUS_seq') NOT NULL, + ENROLMENT_ID INTEGER NOT NULL, + DEVICE_ID INTEGER NOT NULL, + STATUS VARCHAR(50) DEFAULT NULL, + UPDATE_TIME TIMESTAMP(0) DEFAULT NULL, + CHANGED_BY VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT FK_DM_DEVICE_STATUS_DEVICE FOREIGN KEY (DEVICE_ID) REFERENCES + DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT FK_DM_DEVICE_STATUS_ENROLMENT FOREIGN KEY (ENROLMENT_ID) REFERENCES + DM_ENROLMENT (ID) ON DELETE NO ACTION ON UPDATE NO ACTION +); CREATE INDEX IDX_ENROLMENT_FK_DEVICE_ID ON DM_ENROLMENT(DEVICE_ID); CREATE INDEX IDX_ENROLMENT_DEVICE_ID_TENANT_ID ON DM_ENROLMENT(DEVICE_ID, TENANT_ID); CREATE INDEX IDX_ENROLMENT_DEVICE_ID_TENANT_ID_STATUS ON DM_ENROLMENT(DEVICE_ID, TENANT_ID, STATUS); diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/p2.inf b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/p2.inf index 5ebad172029..178b0961b03 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/p2.inf +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/p2.inf @@ -1,7 +1,9 @@ instructions.configure = \ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.basics_${feature.version}/conf/datasources/cdm-datasources.xml,target:${installFolder}/../../conf/datasources/cdm-datasources.xml,overwrite:true);\ +org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.basics_${feature.version}/conf/datasources/reporting-mgt-datasources.xml,target:${installFolder}/../../conf/datasources/reporting-mgt-datasources.xml,overwrite:true);\ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.basics_${feature.version}/conf/cdm-config.xml,target:${installFolder}/../../conf/cdm-config.xml,overwrite:true);\ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.basics_${feature.version}/conf/mdm-ui-config.xml,target:${installFolder}/../../conf/mdm-ui-config.xml,overwrite:true);\ +org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.basics_${feature.version}/conf/reporting-mgt.xml,target:${installFolder}/../../conf/reporting-mgt.xml,overwrite:true);\ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.basics_${feature.version}/conf/license-config.xml,target:${installFolder}/../../conf/etc/license-config.xml,overwrite:true);\ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.basics_${feature.version}/conf/remote-appmanager-config.xml,target:${installFolder}/../../conf/etc/remote-appmanager-config.xml,overwrite:true);\ org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.wso2.carbon.device.mgt.basics_${feature.version}/dbscripts/cdm,target:${installFolder}/../../../dbscripts/cdm,overwrite:true);\ diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.extensions.feature/pom.xml b/features/device-mgt/org.wso2.carbon.device.mgt.extensions.feature/pom.xml index 2e116892e65..af1a3b165f9 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.extensions.feature/pom.xml +++ b/features/device-mgt/org.wso2.carbon.device.mgt.extensions.feature/pom.xml @@ -4,7 +4,7 @@ org.wso2.carbon.devicemgt device-mgt-feature - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.feature/pom.xml b/features/device-mgt/org.wso2.carbon.device.mgt.feature/pom.xml index e2730296be9..874edaca48f 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.feature/pom.xml +++ b/features/device-mgt/org.wso2.carbon.device.mgt.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt device-mgt-feature - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/pom.xml b/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/pom.xml index a059dbe4a96..9e036804718 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/pom.xml +++ b/features/device-mgt/org.wso2.carbon.device.mgt.server.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt device-mgt-feature - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/features/device-mgt/pom.xml b/features/device-mgt/pom.xml index 8232f41213e..65ab4f8708a 100644 --- a/features/device-mgt/pom.xml +++ b/features/device-mgt/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../../pom.xml diff --git a/features/heartbeat-management/io.entgra.server.heart.beat.feature/pom.xml b/features/heartbeat-management/io.entgra.server.heart.beat.feature/pom.xml index cd2992f106e..69639f26881 100644 --- a/features/heartbeat-management/io.entgra.server.heart.beat.feature/pom.xml +++ b/features/heartbeat-management/io.entgra.server.heart.beat.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt heart-beat-feature - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/features/heartbeat-management/pom.xml b/features/heartbeat-management/pom.xml index 6d4368557fc..96d62150004 100644 --- a/features/heartbeat-management/pom.xml +++ b/features/heartbeat-management/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../../pom.xml diff --git a/features/jwt-client/org.wso2.carbon.identity.jwt.client.extension.feature/pom.xml b/features/jwt-client/org.wso2.carbon.identity.jwt.client.extension.feature/pom.xml index a7e0498cfcc..06427c4750a 100644 --- a/features/jwt-client/org.wso2.carbon.identity.jwt.client.extension.feature/pom.xml +++ b/features/jwt-client/org.wso2.carbon.identity.jwt.client.extension.feature/pom.xml @@ -23,7 +23,7 @@ org.wso2.carbon.devicemgt jwt-client-feature - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/features/jwt-client/pom.xml b/features/jwt-client/pom.xml index 9cbc01241f3..1bcc6e4c9d7 100644 --- a/features/jwt-client/pom.xml +++ b/features/jwt-client/pom.xml @@ -23,7 +23,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../../pom.xml diff --git a/features/policy-mgt/org.wso2.carbon.policy.mgt.server.feature/pom.xml b/features/policy-mgt/org.wso2.carbon.policy.mgt.server.feature/pom.xml index a3b5dc52b00..612783f39f4 100644 --- a/features/policy-mgt/org.wso2.carbon.policy.mgt.server.feature/pom.xml +++ b/features/policy-mgt/org.wso2.carbon.policy.mgt.server.feature/pom.xml @@ -23,7 +23,7 @@ org.wso2.carbon.devicemgt policy-mgt-feature - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/features/policy-mgt/pom.xml b/features/policy-mgt/pom.xml index 0a225108ead..d2dfee46638 100644 --- a/features/policy-mgt/pom.xml +++ b/features/policy-mgt/pom.xml @@ -23,7 +23,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../../pom.xml diff --git a/features/transport-mgt/email-sender/org.wso2.carbon.email.sender.feature/pom.xml b/features/transport-mgt/email-sender/org.wso2.carbon.email.sender.feature/pom.xml index 4d14ab0da26..dfa2a8ebe9f 100644 --- a/features/transport-mgt/email-sender/org.wso2.carbon.email.sender.feature/pom.xml +++ b/features/transport-mgt/email-sender/org.wso2.carbon.email.sender.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt email-sender-feature - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/features/transport-mgt/email-sender/pom.xml b/features/transport-mgt/email-sender/pom.xml index 93f44cad186..c4880eab25f 100644 --- a/features/transport-mgt/email-sender/pom.xml +++ b/features/transport-mgt/email-sender/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt transport-mgt-feature - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/features/transport-mgt/pom.xml b/features/transport-mgt/pom.xml index 51f6c27a4db..be7df6c6bec 100644 --- a/features/transport-mgt/pom.xml +++ b/features/transport-mgt/pom.xml @@ -3,7 +3,7 @@ carbon-devicemgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../../pom.xml diff --git a/features/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.api.feature/pom.xml b/features/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.api.feature/pom.xml index 3a9adddc28c..efb91751ba5 100644 --- a/features/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.api.feature/pom.xml +++ b/features/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.api.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt sms-handler-feature - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/features/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.server.feature/pom.xml b/features/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.server.feature/pom.xml index 9704347dc92..c09e4d3d276 100644 --- a/features/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.server.feature/pom.xml +++ b/features/transport-mgt/sms-handler/io.entgra.transport.mgt.sms.handler.server.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt sms-handler-feature - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/features/transport-mgt/sms-handler/pom.xml b/features/transport-mgt/sms-handler/pom.xml index 4e898834caa..e8a77b147a7 100644 --- a/features/transport-mgt/sms-handler/pom.xml +++ b/features/transport-mgt/sms-handler/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt transport-mgt-feature - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/features/ui-request-interceptor/io.entgra.ui.request.interceptor.feature/pom.xml b/features/ui-request-interceptor/io.entgra.ui.request.interceptor.feature/pom.xml index 3c80a5009ce..22b6229db03 100644 --- a/features/ui-request-interceptor/io.entgra.ui.request.interceptor.feature/pom.xml +++ b/features/ui-request-interceptor/io.entgra.ui.request.interceptor.feature/pom.xml @@ -21,7 +21,7 @@ ui-request-interceptor-feature io.entgra.devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT 4.0.0 diff --git a/features/ui-request-interceptor/pom.xml b/features/ui-request-interceptor/pom.xml index 8d1f9a30d0e..bd06e13b89e 100644 --- a/features/ui-request-interceptor/pom.xml +++ b/features/ui-request-interceptor/pom.xml @@ -21,7 +21,7 @@ carbon-devicemgt org.wso2.carbon.devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../../pom.xml diff --git a/features/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework.server.feature/pom.xml b/features/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework.server.feature/pom.xml index 697b967c045..679ea4dad02 100644 --- a/features/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework.server.feature/pom.xml +++ b/features/webapp-authenticator-framework/org.wso2.carbon.webapp.authenticator.framework.server.feature/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt webapp-authenticator-framework-feature - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../pom.xml diff --git a/features/webapp-authenticator-framework/pom.xml b/features/webapp-authenticator-framework/pom.xml index 4109747f00d..27fe3146c08 100644 --- a/features/webapp-authenticator-framework/pom.xml +++ b/features/webapp-authenticator-framework/pom.xml @@ -22,7 +22,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT ../../pom.xml diff --git a/pom.xml b/pom.xml index 6ee8d4c5eea..5ae11399c55 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ org.wso2.carbon.devicemgt carbon-devicemgt pom - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT WSO2 Carbon - Device Management - Parent http://wso2.org WSO2 Connected Device Manager Components @@ -44,6 +44,7 @@ components/certificate-mgt components/ui-request-interceptor components/transport-mgt + components/analytics-mgt components/webapp-authenticator-framework features/device-mgt features/apimgt-extensions @@ -55,6 +56,7 @@ features/jwt-client features/device-mgt-extensions features/transport-mgt + features/analytics-mgt features/webapp-authenticator-framework @@ -287,6 +289,18 @@ org.wso2.carbon.apimgt.application.extension.api ${carbon.device.mgt.version} + + org.wso2.carbon.devicemgt + io.entgra.analytics.mgt.grafana.proxy.api.feature + zip + ${carbon.device.mgt.version} + + + org.wso2.carbon.devicemgt + io.entgra.analytics.mgt.grafana.proxy.server.feature + zip + ${carbon.device.mgt.version} + org.wso2.carbon.devicemgt io.entgra.application.mgt.server.feature @@ -314,6 +328,21 @@ io.entgra.transport.mgt.sms.handler.common ${carbon.device.mgt.version} + + org.wso2.carbon.devicemgt + io.entgra.analytics.mgt.grafana.proxy.api + ${carbon.device.mgt.version} + + + org.wso2.carbon.devicemgt + io.entgra.analytics.mgt.grafana.proxy.core + ${carbon.device.mgt.version} + + + org.wso2.carbon.devicemgt + io.entgra.analytics.mgt.grafana.proxy.common + ${carbon.device.mgt.version} + @@ -1259,6 +1288,11 @@ gson ${google.gson.version} + + com.google.guava + guava + ${google.guava.version} + com.squareup.okhttp3 okhttp @@ -2042,7 +2076,7 @@ 1.2.11.wso2v10 - 5.0.1-SNAPSHOT + 5.0.6-SNAPSHOT 4.7.35 @@ -2085,6 +2119,7 @@ 3.0.0.wso2v1 1.3 2.8.5 + 31.0.1-jre 4.6.0 1.13.0 9.3.1 @@ -2199,7 +2234,7 @@ 7.0.85 - 1.0 + 1.1 1.13.1 5.1.13.RELEASE