osh 3 years ago
commit ed256ad346

@ -0,0 +1,338 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>grafana-mgt</artifactId>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>io.entgra.analytics.mgt.grafana.proxy.api</artifactId>
<packaging>war</packaging>
<name>Entgra - Grafana Proxy API</name>
<description>Entgra - Grafana Proxy API</description>
<url>http://entgra.io</url>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<packagingExcludes>WEB-INF/lib/*cxf*.jar</packagingExcludes>
<warName>api#grafana-mgt#v1.0</warName>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>deploy</id>
<build>
<defaultGoal>compile</defaultGoal>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<copy todir="${basedir}/../../../repository/deployment/server/webapps" overwrite="true">
<fileset dir="${basedir}/target">
<include name="api#grafana-mgt#v1.0.war" />
</fileset>
</copy>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<dependencies>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-bundle</artifactId>
<version>3.0.0-milestone2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-bundle-jaxrs</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-httpclient.wso2</groupId>
<artifactId>commons-httpclient</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.utils</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.framework</groupId>
<artifactId>org.wso2.carbon.user.mgt</artifactId>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.logging</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.inbound.auth.oauth2</groupId>
<artifactId>org.wso2.carbon.identity.oauth.stub</artifactId>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.apache.axis2.wso2</groupId>
<artifactId>axis2-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.json.wso2</groupId>
<artifactId>json</artifactId>
</dependency>
<dependency>
<groupId>commons-codec.wso2</groupId>
<artifactId>commons-codec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>org.wso2.carbon.certificate.mgt.core</artifactId>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>org.wso2.carbon.device.mgt.core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-core</artifactId>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.wso2.orbit.com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-jaxrs</artifactId>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
<groupId>org.wso2.orbit.com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>org.wso2.carbon.apimgt.annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wso2.orbit.com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon.commons</groupId>
<artifactId>org.wso2.carbon.application.mgt.stub</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon.analytics</groupId>
<artifactId>org.wso2.carbon.analytics.api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>org.wso2.carbon.identity.jwt.client.extension</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.registry.core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon.registry</groupId>
<artifactId>org.wso2.carbon.registry.resource</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.framework</groupId>
<artifactId>org.wso2.carbon.identity.user.store.count</artifactId>
<version>${carbon.identity.framework.version}</version>
<scope>provided</scope>
</dependency>
<!--<dependency>-->
<!--<groupId>org.wso2.carbon.analytics</groupId>-->
<!--<artifactId>org.wso2.carbon.analytics.dataservice.commons</artifactId>-->
<!--<scope>provided</scope>-->
<!--</dependency>-->
<dependency>
<groupId>org.wso2.carbon.analytics-common</groupId>
<artifactId>org.wso2.carbon.event.receiver.stub</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon.analytics-common</groupId>
<artifactId>org.wso2.carbon.event.stream.stub</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon.analytics-common</groupId>
<artifactId>org.wso2.carbon.event.publisher.stub</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon.analytics-common</groupId>
<artifactId>org.wso2.carbon.event.stream.persistence.stub</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-testng</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>org.wso2.carbon.device.mgt.common</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>org.wso2.carbon.device.mgt.extensions</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.framework</groupId>
<artifactId>org.wso2.carbon.identity.claim.metadata.mgt</artifactId>
<version>${carbon.identity.framework.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>io.entgra.analytics.mgt.grafana.proxy.core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>org.wso2.carbon.device.mgt.core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>io.entgra.analytics.mgt.grafana.proxy.common</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

@ -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 = "POST",
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 = "POST",
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;
}

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

@ -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<Object>, MessageBodyReader<Object> {
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<Object> objectClass, Type type, Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, String> 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<String, Object> stringObjectMultivaluedMap, OutputStream entityStream)
throws IOException, WebApplicationException {
OutputStreamWriter writer = new OutputStreamWriter(entityStream, UTF_8);
try {
getGson().toJson(object, type, writer);
} finally {
writer.close();
}
}
}

@ -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<Message> {
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<Object> 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 <T> 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<ConstraintViolation<T>> violations = methodValidator.validateParameters(instance,
method, arguments);
if (!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
}
public <T> void validate(final T object) {
if (validator == null) {
log.warn("Bean Validation provider could be found, no validation will be performed");
return;
}
Set<ConstraintViolation<T>> violations = validator.validate(object);
if (!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
}
public void handleFault(org.apache.cxf.message.Message messageParam) {
}
}

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

@ -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<ErrorListItem> 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<ErrorListItem> getErrorItems() {
return errorItems;
}
public void setErrorItems(List<ErrorListItem> 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<ErrorListItem> 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;
}
}
}

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

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

@ -0,0 +1,166 @@
/*
* 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.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());
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<String, List<String>> 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);
}
}
}
}
}

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<!-- This file contains the list of permissions that are associated with URL end points
of the web app. Each permission should contain the name, permission path ,API path
(URL) , HTTP method and OAUTH2 authorization scope (not-required).
When defining dynamic paths for APIs, path variables are denoted by '*' notation.
For ex:
Actual API endpoint: devicemgt_admin/1.0.0/devices/{device-id}
URL to be represented here: /devices/*
NOTE: All the endpoints of the web app should be available in this file. Otherwise
it will result 403 error at the runtime.
-->
<PermissionConfiguration>
<APIVersion></APIVersion>
</PermissionConfiguration>

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
* 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.
-->
<!--
This file defines class loading policy of the whole container. But this behaviour can be overridden by individual webapps by putting this file into the META-INF/ directory.
-->
<Classloading xmlns="http://wso2.org/projects/as/classloading">
<!-- Parent-first or child-first. Default behaviour is child-first.-->
<ParentFirst>false</ParentFirst>
<!--
Default environments that contains provides to all the webapps. This can be overridden by individual webapps by specifing required environments
Tomcat environment is the default and every webapps gets it even if they didn't specify it.
e.g. If a webapps requires CXF, they will get both Tomcat and CXF.
-->
<Environments>CXF3,Carbon</Environments>
</Classloading>

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxrs="http://cxf.apache.org/jaxrs" xmlns:cxf="http://cxf.apache.org/core"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd">
<jaxrs:server id="services" address="/">
<jaxrs:serviceBeans>
<ref bean="grafanaAPIProxyService"/>
<ref bean="swaggerResource"/>
</jaxrs:serviceBeans>
<jaxrs:providers>
<ref bean="jsonProvider"/>
<ref bean="swaggerWriter"/>
</jaxrs:providers>
</jaxrs:server>
<bean id="swaggerConfig" class="io.swagger.jaxrs.config.BeanConfig">
<property name="resourcePackage" value="io.entgra.analytics.mgt.grafana.proxy.api.service"/>
<property name="version" value="1.0"/>
<property name="host" value="localhost:9443"/>
<property name="schemes" value="https" />
<property name="basePath" value="/api/grafana-mgt/v1.0"/>
<property name="title" value="Device Management Admin Service API Definitions"/>
<property name="contact" value="dev@entgra.io"/>
<property name="license" value="Apache 2.0"/>
<property name="licenseUrl" value="http://www.apache.org/licenses/LICENSE-2.0.html"/>
<property name="scan" value="true"/>
</bean>
<bean id="grafanaAPIProxyService" class="io.entgra.analytics.mgt.grafana.proxy.api.service.impl.GrafanaAPIProxyServiceImpl"/>
<bean id="swaggerWriter" class="io.swagger.jaxrs.listing.SwaggerSerializers" />
<bean id="swaggerResource" class="io.swagger.jaxrs.listing.ApiListingResource" />
<bean id="jsonProvider" class="io.entgra.analytics.mgt.grafana.proxy.api.service.addons.GsonMessageBodyHandler"/>
<bean id="io.entgra.analytics.mgt.grafana.proxy.api.service.addons.ValidationInterceptor" class="io.entgra.analytics.mgt.grafana.proxy.api.service.addons.ValidationInterceptor"/>
<cxf:bus>
<cxf:inInterceptors>
<ref bean="io.entgra.analytics.mgt.grafana.proxy.api.service.addons.ValidationInterceptor"/>
</cxf:inInterceptors>
</cxf:bus>
</beans>

@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<display-name>Grafana-API-Proxy-Webapp</display-name>
<servlet>
<description>JAX-WS/JAX-RS Grafana API Management Endpoint</description>
<display-name>JAX-WS/JAX-RS Servlet</display-name>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>
org.apache.cxf.transport.servlet.CXFServlet
</servlet-class>
<!-- configure a security filter -->
<init-param>
<param-name>swagger.security.filter</param-name>
<param-value>ApiAuthorizationFilterImpl</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>60</session-timeout>
</session-config>
<context-param>
<param-name>doAuthentication</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>nonSecuredEndPoints</param-name>
<param-value></param-value>
</context-param>
<!--publish to apim-->
<context-param>
<param-name>managed-api-enabled</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>managed-api-owner</param-name>
<param-value>admin</param-value>
</context-param>
<context-param>
<param-name>isSharedWithAllTenants</param-name>
<param-value>true</param-value>
</context-param>
<filter>
<filter-name>ApiOriginFilter</filter-name>
<filter-class>io.entgra.analytics.mgt.grafana.proxy.api.service.addons.ApiOriginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ApiOriginFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>HttpHeaderSecurityFilter</filter-name>
<filter-class>org.apache.catalina.filters.HttpHeaderSecurityFilter</filter-class>
<init-param>
<param-name>hstsEnabled</param-name>
<param-value>false</param-value>
</init-param>
</filter>
<filter>
<filter-name>ContentTypeBasedCachePreventionFilter</filter-name>
<filter-class>org.wso2.carbon.ui.filters.cache.ContentTypeBasedCachePreventionFilter</filter-class>
<init-param>
<param-name>patterns</param-name>
<param-value>text/html" ,application/json" ,text/plain</param-value>
</init-param>
<init-param>
<param-name>filterAction</param-name>
<param-value>enforce</param-value>
</init-param>
<init-param>
<param-name>httpHeaders</param-name>
<param-value>Cache-Control: no-store, no-cache, must-revalidate, private</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>HttpHeaderSecurityFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>ContentTypeBasedCachePreventionFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>

@ -0,0 +1,107 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>grafana-mgt</artifactId>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>io.entgra.analytics.mgt.grafana.proxy.common</artifactId>
<packaging>bundle</packaging>
<name>Entgra - Grafana API Handler Common</name>
<description>Entgra - Grafana API Handler Common</description>
<url>http://entgra.io</url>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
<Bundle-Name>${project.artifactId}</Bundle-Name>
<Bundle-Version>${carbon.device.mgt.version}</Bundle-Version>
<Bundle-Description>Grafana Management Common Bundle</Bundle-Description>
<Export-Package>
io.entgra.analytics.mgt.grafana.proxy.common.*
</Export-Package>
<Import-Package>
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
</Import-Package>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<configuration>
<destFile>${basedir}/target/coverage-reports/jacoco-unit.exec</destFile>
</configuration>
<executions>
<execution>
<id>jacoco-initialize</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>jacoco-site</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<dataFile>${basedir}/target/coverage-reports/jacoco-unit.exec</dataFile>
<outputDirectory>${basedir}/target/coverage-reports/site</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wso2.orbit.com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
</dependencies>
</project>

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

@ -0,0 +1,329 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>grafana-mgt</artifactId>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>io.entgra.analytics.mgt.grafana.proxy.core</artifactId>
<packaging>bundle</packaging>
<name>Entgra - Grafana API Handler Core</name>
<description>Entgra - Grafana API Handler Core</description>
<url>http://entgra.io</url>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-scr-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<configuration>
<destFile>${basedir}/target/coverage-reports/jacoco-unit.exec</destFile>
</configuration>
<executions>
<execution>
<id>jacoco-initialize</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>jacoco-site</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<dataFile>${basedir}/target/coverage-reports/jacoco-unit.exec</dataFile>
<outputDirectory>${basedir}/target/coverage-reports/site</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
<Bundle-Name>${project.artifactId}</Bundle-Name>
<Bundle-Version>${carbon.device.mgt.version}</Bundle-Version>
<Bundle-Description>Grafana API Management Core Bundle</Bundle-Description>
<Private-Package>io.entgra.analytics.mgt.grafana.proxy.core.internal</Private-Package>
<Import-Package>
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.*
</Import-Package>
<Export-Package>
!org.wso2.carbon.email.sender.core.internal,
io.entgra.analytics.mgt.grafana.proxy.core.*
</Export-Package>
<Embed-Dependency>
scribe;scope=compile|runtime;inline=false,
</Embed-Dependency>
<DynamicImport-Package>*</DynamicImport-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.eclipse.osgi</groupId>
<artifactId>org.eclipse.osgi</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.osgi</groupId>
<artifactId>org.eclipse.osgi.services</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.orbit.org.scannotation</groupId>
<artifactId>scannotation</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>org.wso2.carbon.device.mgt.common</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.utils</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.logging</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>io.entgra.analytics.mgt.grafana.proxy.common</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>org.wso2.carbon.device.mgt.core</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>io.entgra.application.mgt.core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.wso2</groupId>
<artifactId>httpclient</artifactId>
<version>4.1.1.wso2v1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>org.wso2.carbon.identity.jwt.client.extension</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.ndatasource.core</artifactId>
<exclusions>
<exclusion>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>commons-codec.wso2</groupId>
<artifactId>commons-codec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.h2database.wso2</groupId>
<artifactId>h2-database-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-testng</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.user.core</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.user.api</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.registry.api</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.registry.core</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.wso2</groupId>
<artifactId>jdbc-pool</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.base</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.governance</groupId>
<artifactId>org.wso2.carbon.governance.api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.axis2.transport</groupId>
<artifactId>axis2-transport-mail</artifactId>
</dependency>
<dependency>
<groupId>org.apache.ws.commons.axiom.wso2</groupId>
<artifactId>axiom</artifactId>
</dependency>
<!--dependency>
<groupId>org.apache.ws.commons.axiom.wso2</groupId>
<artifactId>axiom-impl</artifactId>
</dependency>
<dependency>
<groupId>org.apache.ws.commons.axiom.wso2</groupId>
<artifactId>axiom</artifactId>
</dependency-->
<dependency>
<groupId>org.apache.axis2.wso2</groupId>
<artifactId>axis2</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.inbound.auth.oauth2</groupId>
<artifactId>org.wso2.carbon.identity.oauth.stub</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.tomcat</groupId>
<artifactId>tomcat</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.tomcat</groupId>
<artifactId>tomcat-servlet-api</artifactId>
</dependency>
<!--Ntask dependencies-->
<dependency>
<groupId>org.wso2.carbon.commons</groupId>
<artifactId>org.wso2.carbon.ntask.core</artifactId>
</dependency>
<dependency>
<groupId>commons-collections.wso2</groupId>
<artifactId>commons-collections</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>org.wso2.carbon.email.sender.core</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>org.wso2.carbon.apimgt.annotations</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.event-processing</groupId>
<artifactId>org.wso2.carbon.event.processor.stub</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.multitenancy</groupId>
<artifactId>org.wso2.carbon.tenant.mgt</artifactId>
</dependency>
<dependency>
<groupId>commons-validator</groupId>
<artifactId>commons-validator</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>io.entgra.server.bootup.heartbeat.beacon</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
</dependencies>
</project>

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

@ -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<CacheConfiguration> 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<CacheConfiguration> 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<CacheConfiguration> caches) {
this.caches = caches;
}
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -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<Integer, Datasource> datasourceAPICache;
private Cache<QueryTemplateCacheKey, String> queryTemplateAPICache;
private Cache<String, String> encodedQueryCache;
private CacheManager() {
initCache();
}
private static final class CacheManagerHolder {
static final CacheManager cacheManager = new CacheManager();
}
public static CacheManager getInstance() {
return CacheManagerHolder.cacheManager;
}
public Cache<String, String> getEncodedQueryCache() {
return encodedQueryCache;
}
public Cache<QueryTemplateCacheKey, String> getQueryTemplateAPICache() {
return queryTemplateAPICache;
}
public Cache<Integer, Datasource> 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 <K, V> Cache<K, V> buildDatasourceCache() {
return CacheBuilder.newBuilder().build();
}
private <K , V> Cache<K, V> 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);
}
}
}

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

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

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

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

@ -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<String> 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<String> 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 + "'";
}
}

@ -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<String> parameters;
public PreparedQuery(String preparedSQL, List<String> parameters) {
this.preparedSQL = preparedSQL;
this.parameters = parameters;
}
public String getPreparedSQL() {
return preparedSQL;
}
public void setPreparedSQL(String preparedSQL) {
this.preparedSQL = preparedSQL;
}
public List<String> getParameters() {
return parameters;
}
public void setParameters(List<String> parameters) {
this.parameters = parameters;
}
}

@ -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<String> parameters) throws SQLException {
List<String> 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<String> escapeParameters(List<String> parameters) {
List<String> escapedParams = new ArrayList<>();
for (String param : parameters) {
String escapedParam = StringEscapeUtils.escapeSql(param);
escapedParams.add(escapedParam);
}
return escapedParams;
}
}

@ -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<String> 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=<query>; Delegate=com.mysql.cj.jdbc.ClientPreparedStatement: <query>]
// 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;
}
}

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

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

@ -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 = "panelId";
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";
}

@ -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<String, List<String>> queryMap = HttpUtil.getQueryMap(iframeURL);
return HttpUtil.getFirstQueryValue(GrafanaConstants.PANEL_ID_QUERY_PARAM, queryMap);
}
public static String getOrgId(URI iframeURL) {
Map<String, List<String>> 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);
}
}

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>analytics-mgt</artifactId>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>grafana-mgt</artifactId>
<packaging>pom</packaging>
<name>Entgra - Grafana API Handler Component</name>
<url>http://entgra.io</url>
<modules>
<module>io.entgra.analytics.mgt.grafana.proxy.core</module>
<module>io.entgra.analytics.mgt.grafana.proxy.common</module>
<module>io.entgra.analytics.mgt.grafana.proxy.api</module>
</modules>
</project>

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>carbon-devicemgt</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>analytics-mgt</artifactId>
<packaging>pom</packaging>
<name>Entgra - Analytics Management Component</name>
<url>http://entgra.io</url>
<modules>
<module>grafana-mgt</module>
</modules>
</project>

@ -22,7 +22,7 @@
<parent>
<artifactId>apimgt-extensions</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -21,7 +21,7 @@
<parent>
<artifactId>apimgt-extensions</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<artifactId>apimgt-extensions</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<artifactId>apimgt-extensions</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>carbon-devicemgt</artifactId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

@ -20,7 +20,7 @@
<parent>
<artifactId>application-mgt</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<artifactId>application-mgt</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -21,7 +21,7 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>application-mgt</artifactId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -21,7 +21,7 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>application-mgt</artifactId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<artifactId>application-mgt</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<artifactId>application-mgt</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>carbon-devicemgt</artifactId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<artifactId>certificate-mgt</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<artifactId>certificate-mgt</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -38,7 +38,7 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>certificate-mgt</artifactId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>carbon-devicemgt</artifactId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<artifactId>device-mgt-extensions</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<artifactId>device-mgt-extensions</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<artifactId>device-mgt-extensions</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<artifactId>device-mgt-extensions</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<artifactId>device-mgt-extensions</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<artifactId>device-mgt-extensions</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<artifactId>carbon-devicemgt</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<artifactId>device-mgt</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<artifactId>device-mgt</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -1899,6 +1899,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)

@ -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(

@ -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;
@ -1186,6 +1187,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<DeviceStatus> 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<DeviceStatus> 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) {

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

@ -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";

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

@ -21,7 +21,7 @@
<parent>
<artifactId>device-mgt</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

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

@ -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(

@ -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 {

@ -154,4 +154,6 @@ public interface OperationManager {
*/
boolean isOperationExist(DeviceIdentifier deviceId, int operationId) throws OperationManagementException;
List<Activity> getActivities(List<String> deviceTypes, String operationCode, long updatedSince, String operationStatus)
throws OperationManagementException;
}

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

@ -22,7 +22,7 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>device-mgt</artifactId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -338,6 +338,10 @@
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>io.entgra.server.bootup.heartbeat.beacon</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
</dependencies>
</project>

@ -0,0 +1,135 @@
/*
* Copyright (c) 2020, 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.common.util;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.entity.ContentType;
import org.apache.http.util.EntityUtils;
import org.wso2.carbon.device.mgt.core.report.mgt.Constants;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HttpUtil {
public static URI createURI(String uriString) {
uriString = uriString.replace(" ", "%20");
return URI.create(uriString);
}
public static String getRequestSubPathFromEnd(URI requestUri, int position) {
if (requestUri.getPath() != null) {
String[] pathList = requestUri.getPath().split("/");
if (pathList.length - 1 >= position) {
return pathList[pathList.length - 1 - position];
}
}
return null;
}
public static String getRequestSubPath(String fullPath, int position) {
String[] pathList = fullPath.split("/");
if (pathList.length - 1 > position) {
String path = pathList[position + 1];
if(path.contains(Constants.URI_QUERY_SEPARATOR)) {
path = path.substring(0, path.indexOf(Constants.URI_QUERY_SEPARATOR));
}
return path;
}
return null;
}
public static String getResponseString(HttpResponse response) throws IOException {
return EntityUtils.toString(response.getEntity());
}
public static boolean isQueryParamExist(String param, URI request) {
Map<String, List<String>> queryMap = getQueryMap(request);
return queryMap.containsKey(param);
}
public static String getFirstQueryValue(String param, Map<String, List<String>> queryMap) {
List<String> valueList = queryMap.get(param);
String firstValue = null;
if(valueList != null) {
firstValue = valueList.get(0);
}
return firstValue;
}
public static Map<String, List<String>> getQueryMap(String uri) {
String query = getQueryFromURIPath(uri);
Map<String, List<String>> map = new HashMap<>();
if (query != null) {
String[] params = query.split("&");
for (String param : params) {
String[] paramArr = param.split("=");
if (paramArr.length == 2) {
String name = paramArr[0];
String value = paramArr[1];
if (!map.containsKey(name)) {
List<String> valueList = new ArrayList<>();
map.put(name, valueList);
}
map.get(name).add(value);
}
}
}
return map;
}
public static Map<String, List<String>> getQueryMap(URI request) {
String query = request.getQuery();
Map<String, List<String>> map = new HashMap<>();
if (query != null) {
String[] params = query.split("&");
for (String param : params) {
String[] paramArr = param.split("=");
if (paramArr.length == 2) {
String name = paramArr[0];
String value = paramArr[1];
if (!map.containsKey(name)) {
List<String> valueList = new ArrayList<>();
map.put(name, valueList);
}
map.get(name).add(value);
}
}
}
return map;
}
public static String getQueryFromURIPath(String uri) {
String query = null;
if (uri.length() > "?".length() && uri.contains("?")) {
query = uri.substring(uri.lastIndexOf("?") + "?".length());
}
if (StringUtils.isEmpty(query)) {
query = null;
}
return query;
}
public static String getContentType(HttpResponse response) {
ContentType contentType = ContentType.getOrDefault(response.getEntity());
return contentType.getMimeType();
}
}

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

@ -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<String> 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<String> getDeviceTypes() {
return deviceTypes;
}
@XmlElementWrapper(name = "DeviceTypes", required = true)
@XmlElement(name = "DeviceType", required = true)
public void setDeviceTypes(List<String> 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;
}
}

@ -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<OperationTimeout> operationTimeoutList;
public List<OperationTimeout> getOperationTimeoutList() {
return operationTimeoutList;
}
@XmlElementWrapper(name = "OperationTimeouts", required = true)
@XmlElement(name = "OperationTimeout", required = false)
public void setOperationTimeoutList(List<OperationTimeout> operationTimeoutList) {
this.operationTimeoutList = operationTimeoutList;
}
}

@ -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) {

@ -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<DeviceStatus> getStatus(int deviceId, int tenantId) throws DeviceManagementDAOException;
List<DeviceStatus> getStatus(int deviceId, int tenantId, Date fromDate, Date toDate) throws DeviceManagementDAOException;
List<DeviceStatus> getStatus(int enrolmentId) throws DeviceManagementDAOException;
List<DeviceStatus> getStatus(int enrolmentId, Date fromDate, Date toDate) throws DeviceManagementDAOException;
}

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

@ -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<DeviceStatus> getStatus(int id, Date fromDate, Date toDate, boolean isDeviceId) throws DeviceManagementDAOException {
List<DeviceStatus> 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<DeviceStatus> getStatus(int enrolmentId, Date fromDate, Date toDate) throws DeviceManagementDAOException {
return getStatus(enrolmentId, fromDate, toDate, false);
}
@Override
public List<DeviceStatus> getStatus(int deviceId, int tenantId) throws DeviceManagementDAOException {
return getStatus(deviceId, tenantId, null, null);
}
@Override
public List<DeviceStatus> getStatus(int deviceId, int tenantId, Date fromDate, Date toDate) throws DeviceManagementDAOException {
return getStatus(deviceId, fromDate, toDate, true);
}
@Override
public List<DeviceStatus> getStatus(int enrolmentId) throws DeviceManagementDAOException {
return getStatus(enrolmentId, null, null);
}
private Connection getConnection() throws SQLException {
return DeviceManagementDAOFactory.getConnection();
}
}

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

@ -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 {

@ -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<DeviceType, DeviceStatusTaskPluginConfig> 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;
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save