Tempory merge conflict resolution

feature/traccar-sync
Mohamed Rashd 3 years ago
commit aa2fa45a5f

@ -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.6-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 = "GET",
value = "Grafana dashboard details API proxy",
tags = "Analytics",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = SCOPE, value = "perm:grafana:api:view")
})
}
)
Response getDashboard(@Context HttpHeaders headers, @Context UriInfo requestUriInfo) throws ClassNotFoundException;
@GET
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Path("/annotations")
@ApiOperation(
produces = MediaType.APPLICATION_JSON,
httpMethod = "GET",
value = "Grafana annotations API proxy",
tags = "Analytics",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = SCOPE, value = "perm:grafana:api:view")
})
}
)
Response getAnnotations(@Context HttpHeaders headers, @Context UriInfo requestUriInfo) throws ClassNotFoundException;
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/alerts/states-for-dashboard")
@ApiOperation(
produces = MediaType.APPLICATION_JSON,
httpMethod = "GET",
value = "Get Grafana alert states for dashboard details API proxy",
tags = "Analytics",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = SCOPE, value = "perm:grafana:api:view")
})
}
)
Response getAlertStateForDashboards(@Context HttpHeaders headers, @Context UriInfo requestUriInfo) throws ClassNotFoundException;
}

@ -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,167 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package io.entgra.analytics.mgt.grafana.proxy.api.service.impl.util;
import com.google.gson.JsonObject;
import io.entgra.analytics.mgt.grafana.proxy.api.service.bean.ErrorResponse;
import io.entgra.analytics.mgt.grafana.proxy.api.service.exception.RefererNotValid;
import io.entgra.analytics.mgt.grafana.proxy.common.exception.GrafanaManagementException;
import io.entgra.analytics.mgt.grafana.proxy.core.bean.GrafanaPanelIdentifier;
import io.entgra.analytics.mgt.grafana.proxy.core.exception.GrafanaEnvVariablesNotDefined;
import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaConstants;
import io.entgra.analytics.mgt.grafana.proxy.core.util.GrafanaUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.wso2.carbon.device.mgt.core.common.util.HttpUtil;
import org.wso2.carbon.device.mgt.core.report.mgt.Constants;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
public class GrafanaRequestHandlerUtil {
private static final Log log = LogFactory.getLog(GrafanaRequestHandlerUtil.class);
public static Response proxyPassGetRequest(UriInfo requestUriInfo, String orgId) throws GrafanaEnvVariablesNotDefined {
HttpGet grafanaGetReq = new HttpGet();
return forwardRequestToGrafanaEndpoint(grafanaGetReq, requestUriInfo, orgId);
}
public static Response proxyPassPostRequest(JsonObject body, UriInfo requestUriInfo, String orgId)
throws GrafanaEnvVariablesNotDefined {
HttpPost grafanaPostReq = new HttpPost();
try {
setRequestEntity(grafanaPostReq, body);
} catch (UnsupportedEncodingException e) {
String errorMsg = "Error occurred while parsing body";
log.error(errorMsg, e);
return Response.status(Response.Status.BAD_REQUEST).entity(
new ErrorResponse.ErrorResponseBuilder().setMessage(errorMsg).build()).build();
}
return forwardRequestToGrafanaEndpoint(grafanaPostReq, requestUriInfo, orgId);
}
private static Response forwardRequestToGrafanaEndpoint(HttpRequestBase requestBase, UriInfo requestUriInfo, String orgId)
throws GrafanaEnvVariablesNotDefined {
URI grafanaUri = generateGrafanaUri(requestUriInfo);
requestBase.setURI(grafanaUri);
requestBase.setHeader(GrafanaConstants.X_GRAFANA_ORG_ID_HEADER, orgId);
try(CloseableHttpClient client = HttpClients.createDefault()) {
HttpResponse grafanaResponse = invokeGrafanaAPI(client, requestBase);
String grafanaResponseBody = HttpUtil.getResponseString(grafanaResponse);
return Response.status(Response.Status.OK).entity(grafanaResponseBody).
header(HttpHeaders.CONTENT_TYPE, HttpUtil.getContentType(grafanaResponse)).build();
} catch (IOException e) {
String msg = "Error occurred while calling Grafana API";
log.error(msg, e);
return Response.serverError().entity(
new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build();
} catch (GrafanaManagementException e) {
String err = "Error occurred while retrieving Grafana configuration";
log.error(err, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
new ErrorResponse.ErrorResponseBuilder().setMessage(err).build()).build();
}
}
public static HttpResponse invokeGrafanaAPI(HttpClient client, HttpRequestBase request) throws IOException, GrafanaManagementException {
setBasicAuthHeader(request);
return client.execute(request);
}
public static void setBasicAuthHeader(HttpRequestBase request) throws GrafanaManagementException {
String basicAuth = GrafanaUtil.getBasicAuthBase64Header();
request.setHeader(HttpHeaders.AUTHORIZATION, basicAuth);
}
public static URI generateGrafanaUri(UriInfo requestUriInfo) throws GrafanaEnvVariablesNotDefined {
String base = GrafanaUtil.getGrafanaHTTPBase(requestUriInfo.getRequestUri().getScheme());
String grafanaRequestPath = getGrafanaRequestPathWQuery(requestUriInfo);
return HttpUtil.createURI(GrafanaUtil.generateGrafanaUrl(grafanaRequestPath, base));
}
public static String getGrafanaRequestPathWQuery(UriInfo requestUriInfo) {
String path = requestUriInfo.getPath();
String queryParam = requestUriInfo.getRequestUri().getRawQuery();
if (queryParam != null) {
path += Constants.URI_QUERY_SEPARATOR + queryParam;
}
return path;
}
public static GrafanaPanelIdentifier getPanelIdentifier(HttpHeaders headers) throws RefererNotValid {
String referer = headers.getHeaderString(GrafanaConstants.REFERER_HEADER);
if(referer == null) {
String errMsg = "Request does not contain Referer header";
log.error(errMsg);
throw new RefererNotValid(errMsg);
}
GrafanaPanelIdentifier panelIdentifier = GrafanaUtil.getPanelIdentifierFromReferer(referer);
if(panelIdentifier.getDashboardId() == null ||
panelIdentifier.getPanelId() == null || panelIdentifier.getOrgId() == null) {
String errMsg = "Referer must contain dashboardId, panelId and orgId";
log.error(errMsg);
throw new RefererNotValid(errMsg);
}
return panelIdentifier;
}
public static void setRequestEntity(HttpPost postRequest, JsonObject body) throws UnsupportedEncodingException {
StringEntity bodyEntity = new StringEntity(body.toString(), StandardCharsets.UTF_8);
bodyEntity.setContentType(MediaType.APPLICATION_JSON);
postRequest.setEntity(bodyEntity);
}
public static Response constructInvalidReferer() {
String errorMsg = "Request does not contain a valid Referer header";
return Response.status(Response.Status.BAD_REQUEST).entity(
new ErrorResponse.ErrorResponseBuilder().setMessage(errorMsg).build()).build();
}
public static Response constructInternalServerError(Exception e, String errMsg) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
new ErrorResponse.ErrorResponseBuilder().setMessage(errMsg).build()).build();
}
public static void copyHeadersToGrafanaRequest(HttpRequestBase grafanaRequest, HttpHeaders headers) {
Map<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.6-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.6-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 = "orgId";
public static final String ID_KEY = "id";
public static final String WS_LIVE_API = "/api/live/ws";
public static final String DASHBOARD_API = "/api/dashboards/uid";
public static final String DATASOURCE_API = "/api/datasources";
public static final String HTTP_HOST_ENV_VAR = "iot.grafana.http.host";
public static final String HTTPS_HOST_ENV_VAR = "iot.grafana.https.host";
}

@ -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.6-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.6-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.6-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.6-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.6-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -62,11 +62,13 @@ public interface APIManagementProviderService {
* @param applicationName Application Name
* @param tokenType Token Type
* @param validityPeriod Validity Period
* @param username Name of the user to create the token. If null, set as carbon context user
* @return {@link String} Access Token
* @throws APIManagerException if error occurred while getting the access token for given scopes,
* validity period etc.
*/
AccessTokenInfo getAccessToken(String scopes, String[] tags, String applicationName, String tokenType, String validityPeriod)
AccessTokenInfo getAccessToken(String scopes, String[] tags, String applicationName, String
tokenType, String validityPeriod, String username)
throws APIManagerException;
}

@ -233,7 +233,7 @@ public class APIManagementProviderServiceImpl implements APIManagementProviderSe
@Override
public AccessTokenInfo getAccessToken(String scopes, String[] tags, String applicationName, String tokenType,
String validityPeriod) throws APIManagerException {
String validityPeriod, String username) throws APIManagerException {
try {
String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(true);
ApiApplicationKey clientCredentials = getClientCredentials(tenantDomain, tags, applicationName, tokenType,
@ -245,15 +245,22 @@ public class APIManagementProviderServiceImpl implements APIManagementProviderSe
throw new APIManagerException(msg);
}
String user =
PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername() + "@" + PrivilegedCarbonContext
if (username == null || username.isEmpty()) {
username =
PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername() + "@" + PrivilegedCarbonContext
.getThreadLocalCarbonContext().getTenantDomain(true);
} else {
if (!username.contains("@")) {
username += "@" + PrivilegedCarbonContext
.getThreadLocalCarbonContext().getTenantDomain(true);
}
}
JWTClientManagerService jwtClientManagerService = APIApplicationManagerExtensionDataHolder.getInstance()
.getJwtClientManagerService();
JWTClient jwtClient = jwtClientManagerService.getJWTClient();
AccessTokenInfo accessTokenForAdmin = jwtClient
.getAccessToken(clientCredentials.getConsumerKey(), clientCredentials.getConsumerSecret(), user,
.getAccessToken(clientCredentials.getConsumerKey(), clientCredentials.getConsumerSecret(), username,
scopes);
return accessTokenForAdmin;

@ -22,7 +22,7 @@
<parent>
<artifactId>apimgt-extensions</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.6-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.6-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.6-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.6-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.6-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -23,12 +23,14 @@ import java.sql.Timestamp;
public class DeviceSubscriptionData {
private int subId;
private String action;
private long actionTriggeredTimestamp;
private String actionTriggeredBy;
private String actionType;
private String status;
private Device device;
private String currentInstalledVersion;
public String getAction() {
return action;
@ -77,4 +79,16 @@ public class DeviceSubscriptionData {
public void setDevice(Device device) {
this.device = device;
}
public String getCurrentInstalledVersion() { return currentInstalledVersion; }
public void setCurrentInstalledVersion(String currentInstalledVersion) { this.currentInstalledVersion = currentInstalledVersion; }
public int getSubId() {
return subId;
}
public void setSubId(int subId) {
this.subId = subId;
}
}

@ -108,6 +108,11 @@ public class Filter {
*/
private String appReleaseState;
/**
* Username of whose favourite apps to be retrieved
*/
private String favouredBy;
public int getLimit() {
return limit;
}
@ -195,4 +200,12 @@ public class Filter {
public String getAppReleaseType() { return appReleaseType; }
public void setAppReleaseType(String appReleaseType) { this.appReleaseType = appReleaseType; }
public String getFavouredBy() {
return favouredBy;
}
public void setFavouredBy(String favouredBy) {
this.favouredBy = favouredBy;
}
}

@ -0,0 +1,32 @@
package io.entgra.application.mgt.common;
public class OperationStatusBean {
private int operationId;
private String status;
private String operationCode;
public int getOperationId() {
return operationId;
}
public void setOperationId(int operationId) {
this.operationId = operationId;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getOperationCode() {
return operationCode;
}
public void setOperationCode(String operationCode) {
this.operationCode = operationCode;
}
}

@ -96,6 +96,10 @@ public class ApplicationDTO {
value = "package name of the application")
private String packageName;
@ApiModelProperty(name = "isFavourite",
value = "if the app is favoured by the user")
private boolean isFavourite;
public String getPackageName() {
return packageName;
}
@ -189,4 +193,12 @@ public class ApplicationDTO {
public double getAppRating() { return appRating; }
public void setAppRating(double appRating) { this.appRating = appRating; }
public boolean isFavourite() {
return isFavourite;
}
public void setFavourite(boolean favourite) {
isFavourite = favourite;
}
}

@ -103,6 +103,10 @@ public class Application {
value = "package name of the application")
private String packageName;
@ApiModelProperty(name = "isFavourite",
value = "if the app is favoured by the user")
private boolean isFavourite;
public String getPackageName() {
return packageName;
}
@ -181,4 +185,12 @@ public class Application {
public String getInstallerName() { return installerName; }
public void setInstallerName(String installerName) { this.installerName = installerName; }
public boolean isFavourite() {
return isFavourite;
}
public void setFavourite(boolean favourite) {
isFavourite = favourite;
}
}

@ -128,6 +128,26 @@ public interface ApplicationManager {
* @throws ApplicationManagementException if any error occurred while creating the application
*/
<T> Application createApplication(T app) throws ApplicationManagementException;
/**
* Add an application to favourites
* @param appId id of the application
* @throws ApplicationManagementException Catch all other throwing exceptions and throw {@link ApplicationManagementException}
*/
void addAppToFavourites(int appId) throws ApplicationManagementException;
/**
* Remove an application from favourites
* @param appId id of the application
* @throws ApplicationManagementException Catch all other throwing exceptions and throw {@link ApplicationManagementException}
*/
void removeAppFromFavourites(int appId) throws ApplicationManagementException;
/**
* Check if an application is a favourite app
* @param appId id of the application
* @throws ApplicationManagementException Catch all other throwing exceptions and throw {@link ApplicationManagementException}
*/
boolean isFavouriteApp(int appId) throws ApplicationManagementException;
/**
* Check the existence of an application for given application name and the device type.
@ -175,6 +195,8 @@ public interface ApplicationManager {
*/
void deleteApplicationRelease(String releaseUuid) throws ApplicationManagementException;
ApplicationList getFavouriteApplications(Filter filter) throws ApplicationManagementException;
/**
* Use to delete application artifact files (For example this is useful to delete application release artifacts
* (I.E screenshots) when an application is deleted)
@ -290,7 +312,18 @@ public interface ApplicationManager {
*/
ApplicationRelease changeLifecycleState(String releaseUuid, LifecycleChanger lifecycleChanger)
throws ApplicationManagementException;
/**
* To get all the releases of a particular ApplicationDTO.
*
* @param applicationReleaseDTO of the ApplicationDTO Release.
* @param lifecycleChanger Lifecycle changer that contains the action and the reason for the change.
* @throws ApplicationManagementException ApplicationDTO Management Exception.
* @return
*/
ApplicationRelease changeLifecycleState(ApplicationReleaseDTO applicationReleaseDTO, LifecycleChanger lifecycleChanger)
throws ApplicationManagementException;
/**
* To update release images such as icons, banner and screenshots.
*
@ -470,7 +503,9 @@ public interface ApplicationManager {
*/
boolean checkSubDeviceIdsForOperations(int operationId, int deviceId) throws ApplicationManagementException;
void updateSubsStatus (int deviceId, int operationId, String status) throws ApplicationManagementException;
void updateSubStatus(int deviceId, List<Integer> operationId, String status) throws ApplicationManagementException;
void updateSubsStatus(int deviceId, int operationId, String status) throws ApplicationManagementException;
/**
* Get plist content to download and install the application.
@ -482,5 +517,4 @@ public interface ApplicationManager {
String getPlistArtifact(String uuid) throws ApplicationManagementException;
List<ApplicationReleaseDTO> getReleaseByPackageNames(List<String> packageIds) throws ApplicationManagementException;
}

@ -34,6 +34,16 @@ import java.util.Properties;
*/
public interface SubscriptionManager {
/**
* Use to update status of a subscription
*
* @param deviceId Id of the device
* @param subId subscription id
* @param status status to be changed
*/
void updateSubscriptionStatus(int deviceId, int subId, String status)
throws SubscriptionManagementException;
/**
* Performs bulk subscription operation for a given application and a subscriber list.
* @param applicationUUID UUID of the application to subscribe/unsubscribe
@ -175,12 +185,13 @@ public interface SubscriptionManager {
* @param actionStatus status of the operation.
* @param action action related to the device.
* @param appUUID application release UUID
* @param installedVersion installed version
* @return {@link PaginationResult}
* @throws ApplicationManagementException if offset or limit contains incorrect values, if it couldn't find an
* application release for given UUID, if an error occurred while getting device details of subscribed device ids,
* if an error occurred while getting subscription details of given application release UUID.
*/
PaginationResult getAppSubscriptionDetails(PaginationRequest request, String appUUID, String actionStatus, String action)
PaginationResult getAppSubscriptionDetails(PaginationRequest request, String appUUID, String actionStatus, String action, String installedVersion)
throws ApplicationManagementException;
/***

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

@ -39,6 +39,33 @@ public interface ApplicationDAO {
*/
int createApplication(ApplicationDTO applicationDTO, int tenantId) throws ApplicationManagementDAOException;
/**
* Use to add an application to favourites for given appId, username and tenantId
*
* @param appId id of the application
* @param userName currently logged-in user
* @throws ApplicationManagementDAOException if error occurred wile executing query to insert data into database.
*/
void addAppToFavourite(int appId, String userName, int tenantId) throws ApplicationManagementDAOException;
/**
* Use to remove an application from favourites for given appId, username and tenantId
*
* @param appId id of the application
* @param userName currently logged-in username
* @throws ApplicationManagementDAOException if error occurred wile executing query to delete data from database.
*/
void removeAppFromFavourite(int appId, String userName, int tenantId) throws ApplicationManagementDAOException;
/**
* Use to check if an app is favourite for given username and tenantId
* @param appId id of the application
* @param userName currently logged-in username
* @return If application is favourite
* @throws ApplicationManagementDAOException if error occurred wile executing query to check if application is a favourite
*/
boolean isFavouriteApp(int appId, String userName, int tenantId) throws ApplicationManagementDAOException;
/**
* To add tags for a particular application.
*

@ -101,6 +101,10 @@ public interface SubscriptionDAO {
List<Integer> getDeviceSubIds(List<Integer> deviceIds, int applicationReleaseId, int tenantId)
throws ApplicationManagementDAOException;
int getDeviceIdForSubId(int subId, int tenantId) throws ApplicationManagementDAOException;
List<Integer> getOperationIdsForSubId(int subId, int tenantId) throws ApplicationManagementDAOException;
List<Integer> getDeviceSubIdsForOperation(int operationId, int deviceID, int tenantId)
throws ApplicationManagementDAOException;
@ -239,4 +243,13 @@ public interface SubscriptionDAO {
*/
List<Integer> getAppSubscribedDevicesForGroups(int appReleaseId, String subtype, int tenantId)
throws ApplicationManagementDAOException;
/**
* This method is used to get the currently installed version for given app release id
* @param appId id of the application
* @param deviceIdList id list of devices
* @return Map with device id as a key and currently installed version as value
* @throws {@link ApplicationManagementDAOException} if connections establishment fails.
*/
Map<Integer,String> getCurrentInstalledAppVersion(int appId, List<Integer> deviceIdList, String installedVersion) throws ApplicationManagementDAOException;
}

@ -135,10 +135,14 @@ public class GenericApplicationDAOImpl extends AbstractDAOImpl implements Applic
+ "AP_APP_RELEASE.CURRENT_STATE AS RELEASE_CURRENT_STATE, "
+ "AP_APP_RELEASE.RATED_USERS AS RATED_USER_COUNT, "
+ "NEW_AP_APP_LIFECYCLE_STATE.UPDATED_AT AS LATEST_UPDATE "
+ "FROM AP_APP "
+ "LEFT JOIN AP_APP_RELEASE ON "
+ "AP_APP.ID = AP_APP_RELEASE.AP_APP_ID "
+ "INNER JOIN (SELECT AP_APP.ID FROM AP_APP ";
+ "FROM AP_APP ";
if (StringUtils.isNotEmpty(filter.getFavouredBy())) {
sql += "INNER JOIN AP_APP_FAVOURITES ON "
+ "AP_APP.ID = AP_APP_FAVOURITES.AP_APP_ID ";
}
sql += "LEFT JOIN AP_APP_RELEASE ON "
+ "AP_APP.ID = AP_APP_RELEASE.AP_APP_ID "
+ "INNER JOIN (SELECT AP_APP.ID FROM AP_APP ";
if (StringUtils.isNotEmpty(filter.getVersion()) || StringUtils.isNotEmpty(filter.getAppReleaseState())
|| StringUtils.isNotEmpty(filter.getAppReleaseType())) {
sql += "LEFT JOIN AP_APP_RELEASE ON AP_APP.ID = AP_APP_RELEASE.AP_APP_ID ";
@ -189,8 +193,11 @@ public class GenericApplicationDAOImpl extends AbstractDAOImpl implements Applic
+ "FROM AP_APP_LIFECYCLE_STATE "
+ "GROUP BY AP_APP_LIFECYCLE_STATE.AP_APP_RELEASE_ID)) AS NEW_AP_APP_LIFECYCLE_STATE "
+ "ON AP_APP_RELEASE.ID = NEW_AP_APP_LIFECYCLE_STATE.AP_APP_RELEASE_ID "
+ "WHERE AP_APP.TENANT_ID = ? "
+ "ORDER BY AP_APP.ID, LATEST_UPDATE DESC";
+ "WHERE AP_APP.TENANT_ID = ? ";
if (StringUtils.isNotEmpty(filter.getFavouredBy())) {
sql += "AND AP_APP_FAVOURITES.USER_NAME = ? ";
}
sql += "ORDER BY AP_APP.ID, LATEST_UPDATE DESC";
try {
Connection conn = this.getDBConnection();
@ -229,7 +236,10 @@ public class GenericApplicationDAOImpl extends AbstractDAOImpl implements Applic
stmt.setInt(paramIndex++, filter.getLimit());
stmt.setInt(paramIndex++, filter.getOffset());
}
stmt.setInt(paramIndex, tenantId);
stmt.setInt(paramIndex++, tenantId);
if (StringUtils.isNotEmpty(filter.getFavouredBy())) {
stmt.setString(paramIndex, filter.getFavouredBy());
}
try (ResultSet rs = stmt.executeQuery()) {
return DAOUtil.loadApplications(rs);
}
@ -732,6 +742,95 @@ public class GenericApplicationDAOImpl extends AbstractDAOImpl implements Applic
}
}
@Override
public void addAppToFavourite(int appId, String userName, int tenantId) throws ApplicationManagementDAOException {
if (log.isDebugEnabled()) {
log.debug("Request received in DAO Layer to add app with the id " + appId + " favourites");
}
String sql = "INSERT INTO AP_APP_FAVOURITES "
+ "(AP_APP_ID, USER_NAME, "
+ " TENANT_ID) "
+ "VALUES (?, ?, ?)";
try {
Connection conn = this.getDBConnection();
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, appId);
stmt.setString(2, userName);
stmt.setInt(3, tenantId);
stmt.addBatch();
stmt.executeUpdate();
}
} catch (DBConnectionException e) {
String msg = "Error occurred while obtaining the DB connection when adding app to favourites";
log.error(msg, e);
throw new ApplicationManagementDAOException(msg, e);
} catch (SQLException e) {
String msg = "SQL Error occurred while adding app with the id " + appId + " to favourites. Executed Query: " + sql;
log.error(msg, e);
throw new ApplicationManagementDAOException(msg, e);
}
}
@Override
public void removeAppFromFavourite(int appId, String userName, int tenantId) throws ApplicationManagementDAOException {
if (log.isDebugEnabled()) {
log.debug("Request received in DAO Layer to add app with the id " + appId + " favourites");
}
String sql = "DELETE FROM AP_APP_FAVOURITES "
+ "WHERE AP_APP_ID = ? "
+ "AND USER_NAME = ? "
+ "AND TENANT_ID = ?";
try {
Connection conn = this.getDBConnection();
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, appId);
stmt.setString(2, userName);
stmt.setInt(3, tenantId);
stmt.addBatch();
stmt.executeUpdate();
}
} catch (DBConnectionException e) {
String msg = "Error occurred while obtaining the DB connection when removing app from favourites";
log.error(msg, e);
throw new ApplicationManagementDAOException(msg, e);
} catch (SQLException e) {
String msg = "SQL Error occurred while removing app with the id " + appId + " from favourites. Executed Query: " + sql;
log.error(msg, e);
throw new ApplicationManagementDAOException(msg, e);
}
}
@Override
public boolean isFavouriteApp(int appId, String userName, int tenantId) throws ApplicationManagementDAOException {
String sql = "SELECT AP_APP_ID "
+ "FROM AP_APP_FAVOURITES "
+ "WHERE "
+ "AP_APP_ID = ? AND "
+ "USER_NAME = ? AND "
+ "TENANT_ID = ?";
try {
Connection conn = this.getDBConnection();
try (PreparedStatement stmt = conn.prepareStatement(sql)){
stmt.setInt(1, appId);
stmt.setString(2, userName);
stmt.setInt(3, tenantId);
try (ResultSet rs = stmt.executeQuery()){
return rs.next();
}
}
} catch (DBConnectionException e) {
String msg = "Error occurred while obtaining the DB connection to check whether the application with the id "
+ appId + " is a favourite app";
log.error(msg, e);
throw new ApplicationManagementDAOException(msg, e);
} catch (SQLException e) {
String msg = "SQL Error occurred while checking whether the application with the id "
+ appId + " is a favourite app. executed query " + sql;
log.error(msg, e);
throw new ApplicationManagementDAOException(msg, e);
}
}
@Override
public void addTags(List<String> tags, int tenantId) throws ApplicationManagementDAOException {
if (log.isDebugEnabled()) {

@ -82,10 +82,14 @@ public class OracleApplicationDAOImpl extends GenericApplicationDAOImpl {
+ "AP_APP_RELEASE.CURRENT_STATE AS RELEASE_CURRENT_STATE, "
+ "AP_APP_RELEASE.RATED_USERS AS RATED_USER_COUNT, "
+ "NEW_AP_APP_LIFECYCLE_STATE.UPDATED_AT AS LATEST_UPDATE "
+ "FROM AP_APP "
+ "LEFT JOIN AP_APP_RELEASE ON "
+ "AP_APP.ID = AP_APP_RELEASE.AP_APP_ID "
+ "INNER JOIN (SELECT AP_APP.ID FROM AP_APP ORDER BY ID ";
+ "FROM AP_APP ";
if (StringUtils.isNotEmpty(filter.getFavouredBy())) {
sql += "INNER JOIN AP_APP_FAVOURITES ON "
+ "AP_APP.ID = AP_APP_FAVOURITES.AP_APP_ID ";
}
sql += "LEFT JOIN AP_APP_RELEASE ON "
+ "AP_APP.ID = AP_APP_RELEASE.AP_APP_ID "
+ "INNER JOIN (SELECT AP_APP.ID FROM AP_APP ";
if (StringUtils.isNotEmpty(filter.getVersion()) || StringUtils.isNotEmpty(filter.getAppReleaseState())
|| StringUtils.isNotEmpty(filter.getAppReleaseType())) {
sql += "LEFT JOIN AP_APP_RELEASE ON AP_APP.ID = AP_APP_RELEASE.AP_APP_ID ";

@ -81,8 +81,12 @@ public class SQLServerApplicationDAOImpl extends GenericApplicationDAOImpl {
+ "AP_APP_RELEASE.CURRENT_STATE AS RELEASE_CURRENT_STATE, "
+ "AP_APP_RELEASE.RATED_USERS AS RATED_USER_COUNT, "
+ "NEW_AP_APP_LIFECYCLE_STATE.UPDATED_AT AS LATEST_UPDATE "
+ "FROM AP_APP "
+ "LEFT JOIN AP_APP_RELEASE ON "
+ "FROM AP_APP ";
if (StringUtils.isNotEmpty(filter.getFavouredBy())) {
sql += "INNER JOIN AP_APP_FAVOURITES ON "
+ "AP_APP.ID = AP_APP_FAVOURITES.AP_APP_ID ";
}
sql += "LEFT JOIN AP_APP_RELEASE ON "
+ "AP_APP.ID = AP_APP_RELEASE.AP_APP_ID "
+ "INNER JOIN (SELECT AP_APP.ID FROM AP_APP ";
if (StringUtils.isNotEmpty(filter.getVersion()) || StringUtils.isNotEmpty(filter.getAppReleaseState())

@ -691,6 +691,68 @@ public class GenericSubscriptionDAOImpl extends AbstractDAOImpl implements Subsc
}
}
@Override
public int getDeviceIdForSubId(int subId, int tenantId) throws ApplicationManagementDAOException {
try {
Connection conn = this.getDBConnection();
String sql = "SELECT DM_DEVICE_ID "
+ "FROM AP_DEVICE_SUBSCRIPTION "
+ "WHERE ID = ? AND "
+ "TENANT_ID = ?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, subId);
stmt.setInt(2, tenantId);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
return rs.getInt("DM_DEVICE_ID");
}
}
return -1;
}
} catch (DBConnectionException e) {
String msg = "Error occurred while obtaining the DB connection to get app operation ids for given "
+ "subscription id.";
log.error(msg, e);
throw new ApplicationManagementDAOException(msg, e);
} catch (SQLException e) {
String msg = "Error occurred when processing SQL to get operation ids for given subscription id.";
log.error(msg, e);
throw new ApplicationManagementDAOException(msg, e);
}
}
@Override
public List<Integer> getOperationIdsForSubId(int subId, int tenantId) throws ApplicationManagementDAOException {
try {
Connection conn = this.getDBConnection();
List<Integer> operationIds = new ArrayList<>();
String sql = "SELECT OPERATION_ID "
+ "FROM AP_APP_SUB_OP_MAPPING "
+ "WHERE AP_DEVICE_SUBSCRIPTION_ID = ? AND "
+ "TENANT_ID = ?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, subId);
stmt.setInt(2, tenantId);
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
operationIds.add(rs.getInt("OPERATION_ID"));
}
}
return operationIds;
}
} catch (DBConnectionException e) {
String msg = "Error occurred while obtaining the DB connection to get app operation ids for given "
+ "subscription id.";
log.error(msg, e);
throw new ApplicationManagementDAOException(msg, e);
} catch (SQLException e) {
String msg = "Error occurred when processing SQL to get operation ids for given subscription id.";
log.error(msg, e);
throw new ApplicationManagementDAOException(msg, e);
}
}
@Override
public List<Integer> getDeviceSubIdsForOperation(int operationId, int deviceId, int tenantId)
throws ApplicationManagementDAOException {
@ -1204,7 +1266,7 @@ public class GenericSubscriptionDAOImpl extends AbstractDAOImpl implements Subsc
Connection conn = this.getDBConnection();
List<String> subscribedGroups = new ArrayList<>();
String sql = "SELECT "
+ "GS.GROUP_NAME AS GROUPS "
+ "GS.GROUP_NAME AS APP_GROUPS "
+ "FROM AP_GROUP_SUBSCRIPTION GS "
+ "WHERE "
+ "AP_APP_RELEASE_ID = ? AND TENANT_ID = ? LIMIT ? OFFSET ?";
@ -1215,7 +1277,7 @@ public class GenericSubscriptionDAOImpl extends AbstractDAOImpl implements Subsc
ps.setInt(4, offsetValue);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
subscribedGroups.add(rs.getString("GROUPS"));
subscribedGroups.add(rs.getString("APP_GROUPS"));
}
}
return subscribedGroups;
@ -1244,7 +1306,7 @@ public class GenericSubscriptionDAOImpl extends AbstractDAOImpl implements Subsc
try {
Connection conn = this.getDBConnection();
String sql = "SELECT "
+ "COUNT(GS.GROUP_NAME) AS GROUPS "
+ "COUNT(GS.GROUP_NAME) AS APP_GROUPS_COUNT "
+ "FROM AP_GROUP_SUBSCRIPTION GS "
+ "WHERE "
+ "AP_APP_RELEASE_ID = ? AND TENANT_ID = ?";
@ -1254,7 +1316,7 @@ public class GenericSubscriptionDAOImpl extends AbstractDAOImpl implements Subsc
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
return rs.getInt("GROUPS");
return rs.getInt("APP_GROUPS_COUNT");
}
}
return 0;
@ -1311,4 +1373,66 @@ public class GenericSubscriptionDAOImpl extends AbstractDAOImpl implements Subsc
throw new ApplicationManagementDAOException(msg, e);
}
}
@Override
public Map<Integer,String> getCurrentInstalledAppVersion(int appId, List<Integer> deviceIdList, String installedVersion ) throws ApplicationManagementDAOException {
if (log.isDebugEnabled()) {
log.debug("Request received in DAO Layer to get current installed version of the app for " +
"given app release id.");
}
try {
Map<Integer,String> installedVersionsMap = new HashMap<>();
Connection conn = this.getDBConnection();
int index = 1;
boolean isInstalledVersionAvailable = false;
StringJoiner joiner = new StringJoiner(",",
" SELECT DM_DEVICE_ID AS DEVICE,VERSION FROM " +
" (SELECT AP_APP.ID, VERSION FROM AP_APP_RELEASE AP_APP " +
" WHERE ID IN (SELECT ID FROM AP_APP_RELEASE " +
" WHERE AP_APP_ID = ?) " +
" ) AP_APP_V" +
" INNER JOIN " +
" (SELECT AP_APP_RELEASE_ID, DM_DEVICE_ID FROM AP_DEVICE_SUBSCRIPTION AP_DEV_1 " +
" INNER JOIN (" +
" SELECT MAX(ID) AS ID FROM AP_DEVICE_SUBSCRIPTION " +
" WHERE STATUS = 'COMPLETED' AND DM_DEVICE_ID IN (",
") GROUP BY DM_DEVICE_ID " +
") AP_DEV_2 " +
"ON AP_DEV_2.ID = AP_DEV_1.ID ) AP_APP_R " +
"ON AP_APP_R.AP_APP_RELEASE_ID = AP_APP_V.ID");
deviceIdList.stream().map(ignored -> "?").forEach(joiner::add);
String query = joiner.toString();
if(installedVersion != null && !installedVersion.isEmpty()){
query += " WHERE VERSION = ? ";
isInstalledVersionAvailable = true;
}
try (PreparedStatement ps = conn.prepareStatement(query)) {
ps.setInt(index++, appId);
for (int deviceId : deviceIdList) {
ps.setInt(index++, deviceId);
}
if(isInstalledVersionAvailable){
ps.setString(index++, installedVersion);
}
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
installedVersionsMap.put(rs.getInt("DEVICE"),rs.getString("VERSION"));
}
}
return installedVersionsMap;
}
}catch (DBConnectionException e) {
String msg = "Error occurred while obtaining the DB connection to get current installed version of the app for " +
"given app id.";
log.error(msg, e);
throw new ApplicationManagementDAOException(msg, e);
} catch (SQLException e) {
String msg = "SQL Error occurred while getting current installed version of the app for given " +
"app id.";
log.error(msg, e);
throw new ApplicationManagementDAOException(msg, e);
}
}
}

@ -316,6 +316,116 @@ public class ApplicationManagerImpl implements ApplicationManager {
return applicationDTO;
}
@Override
public void addAppToFavourites(int appId) throws ApplicationManagementException {
validateAddAppToFavouritesRequest(appId);
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true);
String userName = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername();
try {
ConnectionManagerUtil.beginDBTransaction();
applicationDAO.addAppToFavourite(appId, userName, tenantId);
ConnectionManagerUtil.commitDBTransaction();
} catch (TransactionManagementException e) {
String msg = "Error occurred while staring transaction to add applicationId: "
+ appId + " to favourites";
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
} catch (DBConnectionException e) {
String msg = "Error occurred while adding application id " + appId + " to favourites ";
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
} catch (ApplicationManagementDAOException e) {
ConnectionManagerUtil.rollbackDBTransaction();
String msg = "Error occurred while adding application with the id: " + appId + " to favourites ";
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
} finally {
ConnectionManagerUtil.closeDBConnection();
}
}
@Override
public void removeAppFromFavourites(int appId) throws ApplicationManagementException {
validateRemoveAppFromFavouritesRequest(appId);
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true);
String userName = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername();
try {
ConnectionManagerUtil.beginDBTransaction();
applicationDAO.removeAppFromFavourite(appId, userName, tenantId);
ConnectionManagerUtil.commitDBTransaction();
} catch (TransactionManagementException e) {
String msg = "Error occurred while staring transaction to remove applicationId: "
+ appId + " from favourites";
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
} catch (DBConnectionException e) {
String msg = "Error occurred while removing application id " + appId + " from favourites ";
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
} catch (ApplicationManagementDAOException e) {
ConnectionManagerUtil.rollbackDBTransaction();
String msg = "Error occurred while removing application with the id: " + appId + " from favourites ";
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
} finally {
ConnectionManagerUtil.closeDBConnection();
}
}
@Override
public boolean isFavouriteApp(int appId) throws ApplicationManagementException{
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true);
String userName = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername();
try {
ConnectionManagerUtil.openDBConnection();
return applicationDAO.isFavouriteApp(appId, userName, tenantId);
} catch (DBConnectionException e) {
String msg = "Error occurred while getting DB connection to check is app with the id " + appId
+ " is a favourite app";
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
} catch (ApplicationManagementDAOException e) {
String msg = "Error occurred while checking app with the id " + appId + " is a favourite app.";
log.error(msg);
throw new ApplicationManagementException(msg, e);
} finally {
ConnectionManagerUtil.closeDBConnection();
}
}
/**
* Use to check if the requested application id is valid before removing from favourites
*
* @param appId ID of the application
* @throws ApplicationManagementException if ID is not valid or errors while validating
*/
private void validateRemoveAppFromFavouritesRequest(int appId) throws ApplicationManagementException {
if (!isFavouriteApp(appId)) {
String msg = "Provided appId " + appId + " is not a favourite app in order remove from favourites";
throw new BadRequestException(msg);
}
}
/**
* Use to check if the requested application id is valid before adding to favourites
*
* @param appId ID of the application
* @throws ApplicationManagementException if ID is not valid or errors while validating
*/
private void validateAddAppToFavouritesRequest(int appId) throws ApplicationManagementException {
try {
getApplication(appId);
} catch (NotFoundException e) {
String msg = " No application exists for the provided appId " + appId;
throw new BadRequestException(msg);
}
if (isFavouriteApp(appId)) {
String msg = "Provided appId " + appId + " is already a favourite app";
throw new BadRequestException(msg);
}
}
/**
* Upload enterprise application release artifact into file system.
*
@ -817,9 +927,17 @@ public class ApplicationManagerImpl implements ApplicationManager {
return applicationReleaseDTO;
}
@Override
public ApplicationList getFavouriteApplications(Filter filter) throws ApplicationManagementException {
String userName = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername();
filter.setFavouredBy(userName);
return getApplications(filter);
}
@Override
public ApplicationList getApplications(Filter filter) throws ApplicationManagementException {
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
String userName = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername();
ApplicationList applicationList = new ApplicationList();
List<Application> applications = new ArrayList<>();
DeviceType deviceType;
@ -845,6 +963,7 @@ public class ApplicationManagerImpl implements ApplicationManager {
.setUnrestrictedRoles(visibilityDAO.getUnrestrictedRoles(applicationDTO.getId(), tenantId));
applicationDTO.setAppCategories(applicationDAO.getAppCategories(applicationDTO.getId(), tenantId));
applicationDTO.setTags(applicationDAO.getAppTags(applicationDTO.getId(), tenantId));
applicationDTO.setFavourite(applicationDAO.isFavouriteApp(applicationDTO.getId(), userName, tenantId));
if (isFilteringApp(applicationDTO, filter)) {
boolean isHideableApp = isHideableApp(applicationDTO.getApplicationReleaseDTOs());
@ -2019,6 +2138,61 @@ public class ApplicationManagerImpl implements ApplicationManager {
ConnectionManagerUtil.closeDBConnection();
}
}
public ApplicationRelease changeLifecycleState(ApplicationReleaseDTO applicationReleaseDTO, LifecycleChanger lifecycleChanger) throws ApplicationManagementException {
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true);
String userName = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername();
if (lifecycleChanger == null || StringUtils.isEmpty(lifecycleChanger.getAction())) {
String msg = "The Action is null or empty. Please verify the request.";
log.error(msg);
throw new BadRequestException(msg);
}
try{
if (lifecycleStateManager
.isValidStateChange(applicationReleaseDTO.getCurrentState(), lifecycleChanger.getAction(), userName,
tenantId)) {
if (lifecycleStateManager.isInstallableState(lifecycleChanger.getAction()) && applicationReleaseDAO
.hasExistInstallableAppRelease(applicationReleaseDTO.getUuid(),
lifecycleStateManager.getInstallableState(), tenantId)) {
String msg = "Installable application release is already registered for the application. "
+ "Therefore it is not permitted to change the lifecycle state from "
+ applicationReleaseDTO.getCurrentState() + " to " + lifecycleChanger.getAction();
log.error(msg);
throw new ForbiddenException(msg);
}
LifecycleState lifecycleState = new LifecycleState();
lifecycleState.setCurrentState(lifecycleChanger.getAction());
lifecycleState.setPreviousState(applicationReleaseDTO.getCurrentState());
lifecycleState.setUpdatedBy(userName);
lifecycleState.setReasonForChange(lifecycleChanger.getReason());
applicationReleaseDTO.setCurrentState(lifecycleChanger.getAction());
if (this.applicationReleaseDAO.updateRelease(applicationReleaseDTO, tenantId) == null) {
String msg = "Application release updating is failed/.";
log.error(msg);
throw new ApplicationManagementException(msg);
}
this.lifecycleStateDAO.addLifecycleState(lifecycleState, applicationReleaseDTO.getId(), tenantId);
return APIUtil.releaseDtoToRelease(applicationReleaseDTO);
} else {
String msg = "Invalid lifecycle state transition from '" + applicationReleaseDTO.getCurrentState() + "'"
+ " to '" + lifecycleChanger.getAction() + "'";
log.error(msg);
throw new ApplicationManagementException(msg);
}
} catch (ApplicationManagementDAOException e) {
String msg = "Error occurred when accessing application release data of application release which has the "
+ "application release UUID: " + applicationReleaseDTO.getUuid();
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
} catch (LifeCycleManagementDAOException e) {
String msg = "Failed to add lifecycle state for Application release UUID: " + applicationReleaseDTO.getUuid();
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
}
}
@Override
public void addApplicationCategories(List<String> categories) throws ApplicationManagementException {
@ -3651,7 +3825,40 @@ public class ApplicationManagerImpl implements ApplicationManager {
}
@Override
public void updateSubsStatus (int deviceId, int operationId, String status) throws ApplicationManagementException {
public void updateSubStatus(int deviceId, List<Integer> operationIds, String status) throws ApplicationManagementException {
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
try {
ConnectionManagerUtil.beginDBTransaction();
for (int operationId : operationIds) {
List<Integer> deviceSubIds = subscriptionDAO.getDeviceSubIdsForOperation(operationId, deviceId, tenantId);
if (!subscriptionDAO.updateDeviceSubStatus(deviceId, deviceSubIds, status, tenantId)){
ConnectionManagerUtil.rollbackDBTransaction();
String msg = "Didn't update an any app subscription of device for operation Id: " + operationId;
log.error(msg);
throw new ApplicationManagementException(msg);
}
}
ConnectionManagerUtil.commitDBTransaction();
} catch (ApplicationManagementDAOException e) {
String msg = "Error occured while updating app subscription status of the device.";
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
} catch (DBConnectionException e) {
String msg = "Error occurred while obersving the database connection to update aoo subscription status of "
+ "device.";
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
} catch (TransactionManagementException e) {
String msg = "Error occurred while executing database transaction";
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
} finally {
ConnectionManagerUtil.closeDBConnection();
}
}
@Override
public void updateSubsStatus(int deviceId, int operationId, String status) throws ApplicationManagementException {
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
try {
ConnectionManagerUtil.beginDBTransaction();

@ -20,6 +20,7 @@ package io.entgra.application.mgt.core.impl;
import com.google.gson.Gson;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethodBase;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.lang.StringUtils;
@ -1189,18 +1190,62 @@ public class SubscriptionManagerImpl implements SubscriptionManager {
}
}
@Override
public void updateSubscriptionStatus(int deviceId, int subId, String status)
throws SubscriptionManagementException {
try {
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true);
List<Integer> operationIds = getOperationIdsForSubId(subId, tenantId);
APIUtil.getApplicationManager().updateSubStatus(deviceId, operationIds, status);
} catch (DBConnectionException e) {
String msg = "Error occurred while observing the database connection to get operation Ids for " + subId;
log.error(msg, e);
throw new SubscriptionManagementException(msg, e);
} catch (ApplicationManagementException e) {
String msg = "Error occurred while updating subscription status with the id: "
+ subId;
log.error(msg, e);
throw new SubscriptionManagementException(msg, e);
}
}
private List<Integer> getOperationIdsForSubId(int subId, int tenantId) throws SubscriptionManagementException {
try {
ConnectionManagerUtil.openDBConnection();
return subscriptionDAO.getOperationIdsForSubId(subId, tenantId);
} catch (ApplicationManagementDAOException e) {
String msg = "Error occurred while getting operation Ids for subId" + subId;
log.error(msg, e);
throw new SubscriptionManagementException(msg, e);
} catch (DBConnectionException e) {
String msg = "Error occurred while observing the database connection to get operation Ids for " + subId;
log.error(msg, e);
throw new SubscriptionManagementException(msg, e);
} finally {
ConnectionManagerUtil.closeDBConnection();
}
}
private int invokeIOTCoreAPI(HttpMethodBase request) throws UserStoreException, APIManagerException, IOException {
HttpClient httpClient;
String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain();
ApiApplicationKey apiApplicationKey = OAuthUtils.getClientCredentials(tenantDomain);
String username =
PrivilegedCarbonContext.getThreadLocalCarbonContext().getUserRealm().getRealmConfiguration()
.getAdminUserName() + Constants.ApplicationInstall.AT + tenantDomain;
AccessTokenInfo tokenInfo = OAuthUtils.getOAuthCredentials(apiApplicationKey, username);
request.addRequestHeader(Constants.ApplicationInstall.AUTHORIZATION,
Constants.ApplicationInstall.AUTHORIZATION_HEADER_VALUE + tokenInfo.getAccessToken());
httpClient = new HttpClient();
httpClient.executeMethod(request);
return request.getStatusCode();
}
public int installEnrollmentApplications(ApplicationPolicyDTO applicationPolicyDTO)
throws ApplicationManagementException {
HttpClient httpClient;
PostMethod request;
try {
String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain();
ApiApplicationKey apiApplicationKey = OAuthUtils.getClientCredentials(tenantDomain);
String username =
PrivilegedCarbonContext.getThreadLocalCarbonContext().getUserRealm().getRealmConfiguration()
.getAdminUserName() + Constants.ApplicationInstall.AT + tenantDomain;
AccessTokenInfo tokenInfo = OAuthUtils.getOAuthCredentials(apiApplicationKey, username);
String requestUrl = Constants.ApplicationInstall.ENROLLMENT_APP_INSTALL_PROTOCOL + System
.getProperty(Constants.ApplicationInstall.IOT_CORE_HOST) + Constants.ApplicationInstall.COLON
+ System.getProperty(Constants.ApplicationInstall.IOT_CORE_PORT)
@ -1210,14 +1255,9 @@ public class SubscriptionManagerImpl implements SubscriptionManager {
StringRequestEntity requestEntity = new StringRequestEntity(payload, MediaType.APPLICATION_JSON,
Constants.ApplicationInstall.ENCODING);
httpClient = new HttpClient();
request = new PostMethod(requestUrl);
request.addRequestHeader(Constants.ApplicationInstall.AUTHORIZATION,
Constants.ApplicationInstall.AUTHORIZATION_HEADER_VALUE + tokenInfo.getAccessToken());
request.setRequestEntity(requestEntity);
httpClient.executeMethod(request);
return request.getStatusCode();
return invokeIOTCoreAPI(request);
} catch (UserStoreException e) {
String msg = "Error while accessing user store for user with Android device.";
log.error(msg, e);
@ -1240,6 +1280,13 @@ public class SubscriptionManagerImpl implements SubscriptionManager {
}
}
private String getIOTCoreBaseUrl() {
return Constants.HTTPS_PROTOCOL + Constants.SCHEME_SEPARATOR + System
.getProperty(Constants.IOT_CORE_HOST) + Constants.COLON
+ System.getProperty(Constants.IOT_CORE_HTTPS_PORT);
}
@Override
public PaginationResult getAppInstalledDevices(PaginationRequest request, String appUUID)
throws ApplicationManagementException {
@ -1361,7 +1408,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager {
@Override
public PaginationResult getAppSubscriptionDetails(PaginationRequest request, String appUUID, String actionStatus,
String action) throws ApplicationManagementException {
String action, String installedVersion) throws ApplicationManagementException {
int limitValue = request.getRowCount();
int offsetValue = request.getStartIndex();
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true);
@ -1395,6 +1442,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager {
}
List<Integer> deviceIdList = deviceSubscriptionDTOS.stream().map(DeviceSubscriptionDTO::getDeviceId)
.collect(Collectors.toList());
Map<Integer,String> currentVersionsMap = subscriptionDAO.getCurrentInstalledAppVersion(applicationDTO.getId(),deviceIdList, installedVersion);
try {
//pass the device id list to device manager service method
PaginationResult paginationResult = deviceManagementProviderService.getAppSubscribedDevices
@ -1404,7 +1452,15 @@ public class SubscriptionManagerImpl implements SubscriptionManager {
if (!paginationResult.getData().isEmpty()) {
List<Device> devices = (List<Device>) paginationResult.getData();
for (Device device : devices) {
if(installedVersion != null && !installedVersion.isEmpty() && !currentVersionsMap.containsKey(device.getId())){
continue;
}
DeviceSubscriptionData deviceSubscriptionData = new DeviceSubscriptionData();
if(currentVersionsMap.containsKey(device.getId())){
deviceSubscriptionData.setCurrentInstalledVersion(currentVersionsMap.get(device.getId()));
}else{
deviceSubscriptionData.setCurrentInstalledVersion("-");
}
for (DeviceSubscriptionDTO subscription : deviceSubscriptionDTOS) {
if (subscription.getDeviceId() == device.getId()) {
deviceSubscriptionData.setDevice(device);
@ -1421,6 +1477,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager {
}
deviceSubscriptionData.setActionType(subscription.getActionTriggeredFrom());
deviceSubscriptionData.setStatus(subscription.getStatus());
deviceSubscriptionData.setSubId(subscription.getId());
deviceSubscriptionDataList.add(deviceSubscriptionData);
break;
}

@ -414,6 +414,7 @@ public class APIUtil {
application.setTags(applicationDTO.getTags());
application.setUnrestrictedRoles(applicationDTO.getUnrestrictedRoles());
application.setRating(applicationDTO.getAppRating());
application.setFavourite(applicationDTO.isFavourite());
if (applicationDTO.getApplicationReleaseDTOs() != null && !applicationDTO.getApplicationReleaseDTOs().isEmpty()) {
application.setInstallerName(applicationDTO.getApplicationReleaseDTOs().get(0).getInstallerName());
}

@ -49,6 +49,9 @@ public class Constants {
public static final String IOT_CORE_HTTPS_PORT = "iot.core.https.port";
public static final String HTTPS_PROTOCOL = "https";
public static final String HTTP_PROTOCOL = "http";
public static final String SCHEME_SEPARATOR = "://";
public static final String OPERATION_STATUS_UPDATE_API_BASE = "/api/device-mgt/v1.0/devices";
public static final String OPERATION_STATUS_UPDATE_API_URI = "/operation";
public static final String LIMIT_QUERY_PARAM = "limit";
public static final String OFFSET_QUERY_PARAM = "offset";
@ -59,6 +62,7 @@ public class Constants {
public static final String IS_APP_RELEASE_TYPE = "stable";
public static final String IS_APP_DEFAULT_PAYMENT_CURRENCY = "$";
public static final String IS_APP_DEFAULT_VERSION = "1.0";
public static final String COLON = ":";
public static final String FORWARD_SLASH = "/";
public static final String URI_QUERY_SEPARATOR = "?";
public static final String QUERY_STRING_SEPARATOR = "&";

@ -120,7 +120,6 @@ public class ApplicationManagementTest extends BaseTestCase {
screenshots.put("shot3", new FileInputStream(new File("src/test/resources/samples/app1/shot3.png")));
applicationArtifact.setScreenshots(screenshots);
ApplicationManager manager = new ApplicationManagerImpl();
manager.createApplication(applicationWrapper);
}
@ -139,7 +138,12 @@ public class ApplicationManagementTest extends BaseTestCase {
public static Object[][] uuidDataProvider() {
return new Object[][] {{"TEST_APP_UUID"}};
}
@Test(enabled = false)
public void createApplicationAndPublish(ApplicationWrapper applicationWrapper, ApplicationArtifact applicationArtifact, boolean isPublish) throws ApplicationManagementException {
}
@Test(enabled = false)
public void updateApplication(int applicationId, ApplicationUpdateWrapper applicationUpdateWrapper) throws ApplicationManagementException {

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

@ -208,7 +208,7 @@ public class ApplicationManagementPublisherAPIImpl implements ApplicationManagem
return Response.status(Response.Status.BAD_REQUEST).entity(msg).build();
}
}
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Path("/public-app")

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

@ -0,0 +1,43 @@
package io.entgra.application.mgt.store.api.beans;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
/**
* This is used to map the status of subscription.
*/
@ApiModel(
value = "SubscriptionStatusBean",
description = "This class carries all information related map statuses of the subscription."
)
public class SubscriptionStatusBean {
@ApiModelProperty(
name = "sub id",
value = "Subscription Id.",
required = true
)
private int subId;
@ApiModelProperty(
name = "status",
value = "Status of the subscription.",
required = true
)
private String status;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public int getSubId() {
return subId;
}
public void setSubId(int subId) {
this.subId = subId;
}
}

@ -35,8 +35,10 @@ import io.entgra.application.mgt.common.ApplicationList;
import javax.validation.Valid;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
@ -70,6 +72,13 @@ import javax.ws.rs.core.Response;
key = "perm:app:store:view",
roles = {"Internal/devicemgt-user"},
permissions = {"/app-mgt/store/application/view"}
),
@Scope(
name = "Modify Application",
description = "Modify application state",
key = "perm:app:store:modify",
roles = {"Internal/devicemgt-user"},
permissions = {"/app-mgt/store/application/modify"}
)
}
)
@ -81,6 +90,120 @@ public interface ApplicationManagementAPI {
String SCOPE = "scope";
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Path("/favourite/{appId}")
@ApiOperation(
consumes = MediaType.APPLICATION_JSON,
produces = MediaType.APPLICATION_JSON,
httpMethod = "POST",
value = "add application to favourites",
notes = "This will add application to favourites",
tags = "Application Management",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = SCOPE, value = "perm:app:store:modify")
})
}
)
@ApiResponses(
value = {
@ApiResponse(
code = 200,
message = "OK. \n Successfully added application to favourites.",
response = ApplicationList.class),
@ApiResponse(
code = 400,
message = "Bad Request. \n " +
"Payload contains unacceptable or vulnerable data"),
@ApiResponse(
code = 500,
message = "Internal Server Error. \n Error occurred while adding the application to favourites.",
response = ErrorResponse.class)
})
Response addAppToFavourite(
@ApiParam(
name = "appId",
value = "id of the application",
required = true)
@PathParam("appId") int appId);
@DELETE
@Consumes(MediaType.APPLICATION_JSON)
@Path("/favourite/{appId}")
@ApiOperation(
consumes = MediaType.APPLICATION_JSON,
produces = MediaType.APPLICATION_JSON,
httpMethod = "DELETE",
value = "remove application from favourites",
notes = "This will removing an application from favourites",
tags = "Application Management",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = SCOPE, value = "perm:app:store:modify")
})
}
)
@ApiResponses(
value = {
@ApiResponse(
code = 200,
message = "OK. \n Successfully removed application from favourites.",
response = ApplicationList.class),
@ApiResponse(
code = 400,
message = "Bad Request. \n " +
"Payload contains unacceptable or vulnerable data"),
@ApiResponse(
code = 500,
message = "Internal Server Error. \n Error occurred while removing the application from favourites.",
response = ErrorResponse.class)
})
Response removeAppFromFavourite(
@ApiParam(
name = "appId",
value = "id of the application",
required = true)
@PathParam("appId") int appId);
@POST
@Path("/favourite")
@Consumes(MediaType.APPLICATION_JSON)
@ApiOperation(
consumes = MediaType.APPLICATION_JSON,
produces = MediaType.APPLICATION_JSON,
httpMethod = "GET",
value = "get all favourite applications",
notes = "This will get all favourite applications",
tags = "Application Management",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = SCOPE, value = "perm:app:store:view")
})
}
)
@ApiResponses(
value = {
@ApiResponse(
code = 200,
message = "OK. \n Successfully got application list.",
response = ApplicationList.class),
@ApiResponse(
code = 400,
message = "Bad Request. \n " +
"Application retrieving request payload contains unacceptable or vulnerable data"),
@ApiResponse(
code = 500,
message = "Internal Server Error. \n Error occurred while getting the application list.",
response = ErrorResponse.class)
})
Response getFavouriteApplications(
@ApiParam(
name = "filter",
value = "Application filtering data",
required = true)
@Valid Filter filter);
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)

@ -17,6 +17,7 @@
*/
package io.entgra.application.mgt.store.api.services.admin;
import io.entgra.application.mgt.store.api.beans.SubscriptionStatusBean;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
@ -34,6 +35,7 @@ import io.entgra.application.mgt.common.ErrorResponse;
import javax.validation.constraints.Size;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
@ -69,6 +71,13 @@ import java.util.List;
key = "perm:admin:app:subscription:view",
roles = {"Internal/devicemgt-admin"},
permissions = {"/app-mgt/store/admin/subscription/view"}
),
@Scope(
name = "View Application Subscriptions",
description = "View Application Subscriptions.",
key = "perm:admin:app:subscription:modify",
roles = {"Internal/devicemgt-admin"},
permissions = {"/app-mgt/store/admin/subscription/modify"}
)
}
)
@ -79,6 +88,51 @@ public interface SubscriptionManagementAdminAPI {
String SCOPE = "scope";
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Path("/device/{deviceId}/status")
@ApiOperation(
consumes = MediaType.APPLICATION_JSON,
produces = MediaType.APPLICATION_JSON,
httpMethod = "PUT",
value = "Update subscription status",
notes = "This will update the subscription status that belongs to the given device id",
tags = "Subscription Management",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = SCOPE, value = "perm:admin:app:subscription:modify")
})
}
)
@ApiResponses(
value = {
@ApiResponse(
code = 200,
message = "OK. \n Successfully updated subscription status.",
response = List.class,
responseContainer = "List"),
@ApiResponse(
code = 404,
message = "Not Found. \n No Application found which has application release of UUID.",
response = ErrorResponse.class),
@ApiResponse(
code = 500,
message = "Internal Server Error. \n Error occurred while updating subscription status",
response = ErrorResponse.class)
})
Response updateSubscription(
@ApiParam(
name = "deviceId",
value = "Id of the device",
required = true)
@PathParam("deviceId") int deviceId,
@ApiParam(
name = "subscription status change bean",
value = "this bean contains the information related to status change",
required = true)
SubscriptionStatusBean subscriptionStatusBean
);
@GET
@Path("/{uuid}")
@Produces(MediaType.APPLICATION_JSON)
@ -139,6 +193,10 @@ public interface SubscriptionManagementAdminAPI {
name = "status",
value = "Provide the device status details, such as active or inactive.")
@QueryParam("status") List<String> status,
@ApiParam(
name = "installedVersion",
value = "Provide the installed version of the application.")
@QueryParam("installedVersion") String installedVersion,
@ApiParam(
name = "uuid",
value = "uuid of the application release.",

@ -28,14 +28,15 @@ import io.entgra.application.mgt.core.exception.NotFoundException;
import io.entgra.application.mgt.core.exception.UnexpectedServerErrorException;
import io.entgra.application.mgt.core.util.APIUtil;
import io.entgra.application.mgt.store.api.services.ApplicationManagementAPI;
import javax.validation.Valid;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
/**
@ -47,30 +48,88 @@ public class ApplicationManagementAPIImpl implements ApplicationManagementAPI {
private static final Log log = LogFactory.getLog(ApplicationManagementAPIImpl.class);
@POST
@Path("/favourite/{appId}")
@Override
@Consumes(MediaType.APPLICATION_JSON)
public Response addAppToFavourite(@PathParam("appId") int appId) {
ApplicationManager applicationManager = APIUtil.getApplicationManager();
try {
applicationManager.addAppToFavourites(appId);
return Response.status(Response.Status.OK).build();
} catch (BadRequestException e) {
String msg = "Invalid payload found in the request. Hence verify the request payload.";
log.error(msg, e);
return Response.status(Response.Status.BAD_REQUEST).entity(msg).build();
} catch (ApplicationManagementException e) {
String msg = "Error occurred while adding application to favourites";
log.error(msg, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
}
}
@DELETE
@Path("/favourite/{appId}")
@Override
@Consumes(MediaType.APPLICATION_JSON)
public Response removeAppFromFavourite(@PathParam("appId") int appId) {
ApplicationManager applicationManager = APIUtil.getApplicationManager();
try {
applicationManager.removeAppFromFavourites(appId);
return Response.status(Response.Status.OK).build();
} catch (BadRequestException e) {
String msg = "Invalid payload found in the request. Hence verify the request payload.";
log.error(msg, e);
return Response.status(Response.Status.BAD_REQUEST).entity(msg).build();
} catch (ApplicationManagementException e) {
String msg = "Error occurred while removing application from favourites";
log.error(msg, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
}
}
@POST
@Path("/favourite")
@Override
@Consumes(MediaType.APPLICATION_JSON)
public Response getFavouriteApplications(@Valid Filter filter) {
ApplicationManager applicationManager = APIUtil.getApplicationManager();
try {
validateFilter(filter);
ApplicationList applications = applicationManager.getFavouriteApplications(filter);
return Response.status(Response.Status.OK).entity(applications).build();
} catch (BadRequestException e) {
String msg = "Invalid filter payload found in the request. Hence verify the filter payload.";
log.error(msg, e);
return Response.status(Response.Status.BAD_REQUEST).entity(msg).build();
} catch (ApplicationManagementException e) {
String msg = "Error occurred while retrieving favourite applications";
log.error(msg, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
}
}
@POST
@Override
@Consumes("application/json")
public Response getApplications(@Valid Filter filter) {
ApplicationManager applicationManager = APIUtil.getApplicationManager();
try {
if (filter == null) {
String msg = "Request Payload is null";
log.error(msg);
return Response.status(Response.Status.BAD_REQUEST).entity(msg).build();
}
validateFilter(filter);
filter.setAppReleaseState(applicationManager.getInstallableLifecycleState());
ApplicationList applications = applicationManager.getApplications(filter);
return Response.status(Response.Status.OK).entity(applications).build();
} catch (BadRequestException e) {
String msg = e.getMessage();
String msg = "Invalid request payload found in the request. Hence verify the payload.";
log.error(msg, e);
return Response.status(Response.Status.BAD_REQUEST).entity(msg).build();
} catch (UnexpectedServerErrorException e) {
String msg = e.getMessage();
String msg = "Unexpected Error occurred while retrieving applications";
log.error(msg);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
} catch (ApplicationManagementException e) {
String msg = e.getMessage();
String msg = "Error occurred while retrieving applications";
log.error(msg);
log.error(msg, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
}
@ -101,4 +160,18 @@ public class ApplicationManagementAPIImpl implements ApplicationManagementAPI {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
}
}
/**
* This method can be used to check & validate if {@link Filter} object exist.
*
* @param filter {@link Filter}
* @throws BadRequestException if filter object doesn't exist
*/
private void validateFilter(Filter filter) throws BadRequestException {
if (filter == null) {
String msg = "Request Payload is null";
log.error(msg);
throw new BadRequestException(msg);
}
}
}

@ -18,6 +18,8 @@
package io.entgra.application.mgt.store.api.services.impl.admin;
import io.entgra.application.mgt.common.exception.SubscriptionManagementException;
import io.entgra.application.mgt.store.api.beans.SubscriptionStatusBean;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -34,10 +36,12 @@ import org.wso2.carbon.device.mgt.common.PaginationResult;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.List;
@ -50,6 +54,30 @@ public class SubscriptionManagementAdminAPIImpl implements SubscriptionManagemen
private static final Log log = LogFactory.getLog(SubscriptionManagementAdminAPIImpl.class);
@Override
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Path("/device/{deviceId}/status")
public Response updateSubscription(
@PathParam("deviceId") int deviceId,
SubscriptionStatusBean subscriptionStatusBean
) {
try {
RequestValidationUtil.validateSubscriptionStatus(subscriptionStatusBean.getStatus());
SubscriptionManager subscriptionManager = APIUtil.getSubscriptionManager();
subscriptionManager.updateSubscriptionStatus(deviceId, subscriptionStatusBean.getSubId(),
subscriptionStatusBean.getStatus());
return Response.status(Response.Status.OK).entity("Subscription status updated successfully").build();
} catch (BadRequestException e) {
return Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage()).build();
} catch (SubscriptionManagementException e) {
String msg = "Error occurred while changing subscription status of the subscription with the id "
+ subscriptionStatusBean.getSubId();
log.error(msg, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
}
}
@GET
@Consumes("application/json")
@Produces("application/json")
@ -60,6 +88,7 @@ public class SubscriptionManagementAdminAPIImpl implements SubscriptionManagemen
@QueryParam("action") String action,
@QueryParam("actionStatus") String actionStatus,
@QueryParam("status") List<String> status,
@QueryParam("installedVersion") String installedVersion,
@PathParam("uuid") String uuid,
@DefaultValue("0")
@QueryParam("offset") int offset,
@ -97,7 +126,7 @@ public class SubscriptionManagementAdminAPIImpl implements SubscriptionManagemen
}
SubscriptionManager subscriptionManager = APIUtil.getSubscriptionManager();
PaginationResult subscriptionData = subscriptionManager.getAppSubscriptionDetails
(request, uuid, actionStatus, action);
(request, uuid, actionStatus, action, installedVersion);
return Response.status(Response.Status.OK).entity(subscriptionData).build();
} catch (NotFoundException e) {
String msg = "Application with application release UUID: " + uuid + " is not found";

@ -18,12 +18,16 @@
*/
package io.entgra.application.mgt.store.api.services.impl.util;
import org.apache.commons.lang3.EnumUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import io.entgra.application.mgt.core.exception.BadRequestException;
import io.entgra.application.mgt.store.api.util.Constants;
import org.wso2.carbon.device.mgt.common.operation.mgt.ActivityStatus;
import java.util.List;
import java.util.StringJoiner;
public class RequestValidationUtil {
@ -113,4 +117,18 @@ public class RequestValidationUtil {
throw new BadRequestException(msg);
}
}
/**
* Checks if user requested subscription status is valid.
*
*/
public static void validateSubscriptionStatus(String status) throws BadRequestException{
if (!EnumUtils.isValidEnum(ActivityStatus.Status.class, status)) {
List<ActivityStatus.Status> validStatuses = EnumUtils.getEnumList(ActivityStatus.Status.class);
String validStatusesString = StringUtils.join(validStatuses, " | ");
String msg = "Invalid status type: " + status + ". \nValid status types are " + validStatusesString;
log.error(msg);
throw new BadRequestException(msg);
}
}
}

@ -22,7 +22,7 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>carbon-devicemgt</artifactId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.6-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.6-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.6-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -310,8 +310,6 @@ public interface CertificateManagementAdminService {
@HeaderParam("If-Modified-Since") String ifModifiedSince);
@DELETE
@Path("/{serialNumber}")
@Consumes(MediaType.WILDCARD)
@ApiOperation(
consumes = MediaType.WILDCARD,
produces = MediaType.APPLICATION_JSON,
@ -348,7 +346,7 @@ public interface CertificateManagementAdminService {
"NOTE: Make sure that a certificate with the serial number you provide exists in the server. If not, first add a certificate.",
required = true,
defaultValue = "12438035315552875930")
@PathParam("serialNumber") String serialNumber);
@QueryParam("serialNumber") String serialNumber);
/**
* Verify Certificate for the API security filter

@ -151,9 +151,7 @@ public class CertificateManagementAdminServiceImpl implements CertificateManagem
}
@DELETE
@Path("/{serialNumber}")
@Consumes(MediaType.WILDCARD)
public Response removeCertificate(@PathParam("serialNumber") String serialNumber) {
public Response removeCertificate(@QueryParam("serialNumber") String serialNumber) {
RequestValidationUtil.validateSerialNumber(serialNumber);
CertificateManagementService certificateService = CertificateMgtAPIUtils.getCertificateManagementService();

@ -38,7 +38,7 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>certificate-mgt</artifactId>
<version>5.0.1-SNAPSHOT</version>
<version>5.0.6-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.6-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.6-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.6-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.6-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.6-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

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

Loading…
Cancel
Save