2019-06-09 17:20:40 +02:00
/ *
* This file is part of Player Analytics ( Plan ) .
*
* Plan is free software : you can redistribute it and / or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* Plan is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan . If not , see < https : //www.gnu.org/licenses/>.
* /
2020-03-01 20:24:48 +01:00
package com.djrapitops.plan.delivery.webserver.resolver.json ;
2019-06-09 17:20:40 +02:00
2023-08-20 10:56:13 +02:00
import com.djrapitops.plan.delivery.domain.auth.WebPermission ;
2023-01-22 09:18:14 +01:00
import com.djrapitops.plan.delivery.formatting.Formatter ;
2019-12-05 19:22:33 +01:00
import com.djrapitops.plan.delivery.rendering.json.graphs.GraphJSONCreator ;
2020-03-01 19:35:00 +01:00
import com.djrapitops.plan.delivery.web.resolver.Response ;
2020-03-10 09:41:49 +01:00
import com.djrapitops.plan.delivery.web.resolver.exception.BadRequestException ;
2020-03-01 19:35:00 +01:00
import com.djrapitops.plan.delivery.web.resolver.request.Request ;
2021-07-17 11:19:33 +02:00
import com.djrapitops.plan.delivery.web.resolver.request.URIQuery ;
2020-03-01 19:35:00 +01:00
import com.djrapitops.plan.delivery.web.resolver.request.WebUser ;
2021-02-10 09:04:21 +01:00
import com.djrapitops.plan.delivery.webserver.cache.AsyncJSONResolverService ;
2019-08-30 23:20:50 +02:00
import com.djrapitops.plan.delivery.webserver.cache.DataID ;
2021-02-10 09:04:21 +01:00
import com.djrapitops.plan.delivery.webserver.cache.JSONStorage ;
2019-08-30 22:14:54 +02:00
import com.djrapitops.plan.identification.Identifiers ;
2021-03-12 17:03:00 +01:00
import com.djrapitops.plan.identification.ServerUUID ;
2023-01-14 21:17:50 +01:00
import com.djrapitops.plan.utilities.dev.Untrusted ;
2022-07-13 20:21:20 +02:00
import io.swagger.v3.oas.annotations.Operation ;
import io.swagger.v3.oas.annotations.Parameter ;
import io.swagger.v3.oas.annotations.enums.ParameterIn ;
import io.swagger.v3.oas.annotations.media.Content ;
import io.swagger.v3.oas.annotations.media.ExampleObject ;
import io.swagger.v3.oas.annotations.parameters.RequestBody ;
import io.swagger.v3.oas.annotations.responses.ApiResponse ;
import jakarta.ws.rs.GET ;
import jakarta.ws.rs.Path ;
2019-06-09 17:20:40 +02:00
import javax.inject.Inject ;
import javax.inject.Singleton ;
2023-08-20 10:56:13 +02:00
import java.util.List ;
2020-03-01 19:35:00 +01:00
import java.util.Optional ;
2019-06-09 17:20:40 +02:00
/ * *
2019-12-05 18:42:20 +01:00
* Resolves / v1 / graph JSON requests .
2019-06-09 17:20:40 +02:00
*
2021-02-13 14:16:03 +01:00
* @author AuroraLS3
2019-06-09 17:20:40 +02:00
* /
@Singleton
2022-07-13 20:21:20 +02:00
@Path ( " /v1/graph " )
2023-01-22 09:18:14 +01:00
public class GraphsJSONResolver extends JSONResolver {
2019-06-09 17:20:40 +02:00
2019-07-06 14:24:01 +02:00
private final Identifiers identifiers ;
2021-02-10 09:04:21 +01:00
private final AsyncJSONResolverService jsonResolverService ;
2019-12-05 19:22:33 +01:00
private final GraphJSONCreator graphJSON ;
2019-06-09 17:20:40 +02:00
@Inject
2019-12-05 18:42:20 +01:00
public GraphsJSONResolver (
2019-07-06 14:24:01 +02:00
Identifiers identifiers ,
2023-01-22 09:18:14 +01:00
AsyncJSONResolverService jsonResolverService ,
GraphJSONCreator graphJSON
2019-06-09 17:20:40 +02:00
) {
2019-07-06 14:24:01 +02:00
this . identifiers = identifiers ;
2021-02-10 09:04:21 +01:00
this . jsonResolverService = jsonResolverService ;
2019-07-03 11:05:33 +02:00
this . graphJSON = graphJSON ;
2019-06-09 17:20:40 +02:00
}
2023-01-22 09:18:14 +01:00
@Override
public Formatter < Long > getHttpLastModifiedFormatter ( ) { return jsonResolverService . getHttpLastModifiedFormatter ( ) ; }
2019-06-09 17:20:40 +02:00
@Override
2020-03-01 19:35:00 +01:00
public boolean canAccess ( Request request ) {
2023-08-20 10:56:13 +02:00
@Untrusted String type = request . getQuery ( ) . get ( " type " )
. orElseThrow ( ( ) - > new BadRequestException ( " 'type' parameter was not defined. " ) ) ;
DataID dataID = getDataID ( type ) ;
boolean forServer = request . getQuery ( ) . get ( " server " ) . isPresent ( ) ;
List < WebPermission > requiredPermissionOptions = forServer
? getRequiredPermission ( dataID )
: getRequiredNetworkPermission ( dataID ) ;
if ( requiredPermissionOptions . isEmpty ( ) ) return true ;
WebUser user = request . getUser ( ) . orElse ( new WebUser ( " " ) ) ;
for ( WebPermission permissionOption : requiredPermissionOptions ) {
if ( user . hasPermission ( permissionOption ) ) return true ;
}
return false ;
2020-03-01 19:35:00 +01:00
}
2020-03-06 09:09:06 +01:00
/ * *
* Resolves the request .
*
* @param request HTTP request , contains all information necessary to resolve the request .
* @return JSON response .
* @throws BadRequestException If ' type ' parameter is not defined or supported .
* @throws BadRequestException If ' server ' parameter is not defined or server is not found in database .
* /
2022-07-13 20:21:20 +02:00
@GET
@Operation (
description = " Get graph data " ,
parameters = {
@Parameter ( in = ParameterIn . QUERY , name = " type " , description = " Type of the graph, see https://github.com/plan-player-analytics/Plan/blob/master/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/GraphsJSONResolver.java " , required = true , examples = {
@ExampleObject ( value = " performance " , description = " Deprecated, use optimizedPerformance " ) ,
@ExampleObject ( " optimizedPerformance " ) ,
@ExampleObject ( " playersOnline " ) ,
2023-04-09 09:10:28 +02:00
@ExampleObject ( " playersOnlineProxies " ) ,
2022-07-13 20:21:20 +02:00
@ExampleObject ( " uniqueAndNew " ) ,
@ExampleObject ( " hourlyUniqueAndNew " ) ,
@ExampleObject ( " serverCalendar " ) ,
@ExampleObject ( " worldPie " ) ,
@ExampleObject ( " activity " ) ,
@ExampleObject ( " geolocation " ) ,
@ExampleObject ( " aggregatedPing " ) ,
@ExampleObject ( " punchCard " ) ,
@ExampleObject ( " serverPie " ) ,
@ExampleObject ( " joinAddressPie " ) ,
2022-09-05 15:56:59 +02:00
@ExampleObject ( " joinAddressByDay " ) ,
2022-07-13 20:21:20 +02:00
} ) ,
@Parameter ( in = ParameterIn . QUERY , name = " server " , description = " Server identifier to get data for " , examples = {
@ExampleObject ( " Server 1 " ) ,
@ExampleObject ( " 1 " ) ,
@ExampleObject ( " 1fb39d2a-eb82-4868-b245-1fad17d823b3 " ) ,
} ) ,
@Parameter ( in = ParameterIn . QUERY , name = " timestamp " , description = " Epoch millisecond for the request, newer value is wanted " )
} ,
responses = {
@ApiResponse ( responseCode = " 200 " , description = " Graph data json " , content = @Content ( ) ) ,
@ApiResponse ( responseCode = " 400 " , description = " 'type' parameter not given " , content = @Content ( examples = {
@ExampleObject ( " { \" status \" : 400, \" error \" : \" 'type' parameter was not defined. \" } " )
} ) ) ,
} ,
requestBody = @RequestBody ( content = @Content ( examples = @ExampleObject ( ) ) )
)
2020-03-01 19:35:00 +01:00
@Override
public Optional < Response > resolve ( Request request ) {
return Optional . of ( getResponse ( request ) ) ;
}
private Response getResponse ( Request request ) {
2023-01-14 21:17:50 +01:00
@Untrusted String type = request . getQuery ( ) . get ( " type " )
2019-06-09 17:20:40 +02:00
. orElseThrow ( ( ) - > new BadRequestException ( " 'type' parameter was not defined. " ) ) ;
2019-08-15 13:37:40 +02:00
2019-08-30 23:20:50 +02:00
DataID dataID = getDataID ( type ) ;
2023-01-22 09:18:14 +01:00
JSONStorage . StoredJSON storedJSON = getGraphJSON ( request , dataID ) ;
return getCachedOrNewResponse ( request , storedJSON ) ;
2021-02-10 09:04:21 +01:00
}
2023-01-14 21:17:50 +01:00
private JSONStorage . StoredJSON getGraphJSON ( @Untrusted Request request , DataID dataID ) {
2021-07-17 11:19:33 +02:00
Optional < Long > timestamp = Identifiers . getTimestamp ( request ) ;
2021-02-10 09:04:21 +01:00
JSONStorage . StoredJSON storedJSON ;
2020-03-01 19:35:00 +01:00
if ( request . getQuery ( ) . get ( " server " ) . isPresent ( ) ) {
2021-03-12 17:03:00 +01:00
ServerUUID serverUUID = identifiers . getServerUUID ( request ) ; // Can throw BadRequestException
2021-02-10 09:04:21 +01:00
storedJSON = jsonResolverService . resolve (
timestamp , dataID , serverUUID ,
2021-07-17 11:19:33 +02:00
theServerUUID - > generateGraphDataJSONOfType ( dataID , theServerUUID , request . getQuery ( ) )
2021-02-10 09:04:21 +01:00
) ;
} else {
// Assume network
storedJSON = jsonResolverService . resolve (
2022-09-10 09:45:03 +02:00
timestamp , dataID , ( ) - > generateGraphDataJSONOfType ( dataID , request . getQuery ( ) )
2021-02-10 09:04:21 +01:00
) ;
2019-08-15 13:37:40 +02:00
}
2021-02-10 09:04:21 +01:00
return storedJSON ;
2019-06-09 17:20:40 +02:00
}
2023-01-14 21:17:50 +01:00
private DataID getDataID ( @Untrusted String type ) {
2019-06-09 17:20:40 +02:00
switch ( type ) {
2020-03-06 09:09:06 +01:00
case " performance " :
return DataID . GRAPH_PERFORMANCE ;
2021-01-24 16:40:30 +01:00
case " optimizedPerformance " :
return DataID . GRAPH_OPTIMIZED_PERFORMANCE ;
2020-03-06 09:09:06 +01:00
case " playersOnline " :
return DataID . GRAPH_ONLINE ;
2023-04-09 09:10:28 +02:00
case " playersOnlineProxies " :
return DataID . GRAPH_ONLINE_PROXIES ;
2020-03-06 09:09:06 +01:00
case " uniqueAndNew " :
return DataID . GRAPH_UNIQUE_NEW ;
2020-05-15 20:48:35 +02:00
case " hourlyUniqueAndNew " :
return DataID . GRAPH_HOURLY_UNIQUE_NEW ;
2020-03-06 09:09:06 +01:00
case " serverCalendar " :
return DataID . GRAPH_CALENDAR ;
case " worldPie " :
return DataID . GRAPH_WORLD_PIE ;
case " activity " :
return DataID . GRAPH_ACTIVITY ;
case " geolocation " :
return DataID . GRAPH_WORLD_MAP ;
case " aggregatedPing " :
return DataID . GRAPH_PING ;
case " punchCard " :
return DataID . GRAPH_PUNCHCARD ;
case " serverPie " :
return DataID . GRAPH_SERVER_PIE ;
2021-03-17 10:31:14 +01:00
case " joinAddressPie " :
2021-03-09 09:01:09 +01:00
return DataID . GRAPH_HOSTNAME_PIE ;
2022-09-05 15:56:59 +02:00
case " joinAddressByDay " :
return DataID . JOIN_ADDRESSES_BY_DAY ;
2020-03-06 09:09:06 +01:00
default :
2021-01-24 10:05:04 +01:00
throw new BadRequestException ( " unknown 'type' parameter. " ) ;
2019-08-30 23:20:50 +02:00
}
}
2023-08-20 10:56:13 +02:00
private List < WebPermission > getRequiredPermission ( DataID dataID ) {
switch ( dataID ) {
case GRAPH_PERFORMANCE :
return List . of ( WebPermission . PAGE_SERVER_PERFORMANCE_GRAPHS ) ;
case GRAPH_PING :
case GRAPH_OPTIMIZED_PERFORMANCE :
return List . of ( WebPermission . PAGE_SERVER_PERFORMANCE_GRAPHS , WebPermission . PAGE_NETWORK_PERFORMANCE ) ;
case GRAPH_ONLINE :
return List . of ( WebPermission . PAGE_SERVER_OVERVIEW_PLAYERS_ONLINE_GRAPH ) ;
case GRAPH_UNIQUE_NEW :
return List . of ( WebPermission . PAGE_SERVER_ONLINE_ACTIVITY_GRAPHS_DAY_BY_DAY ) ;
case GRAPH_HOURLY_UNIQUE_NEW :
return List . of ( WebPermission . PAGE_SERVER_ONLINE_ACTIVITY_GRAPHS_HOUR_BY_HOUR ) ;
case GRAPH_CALENDAR :
return List . of ( WebPermission . PAGE_SERVER_ONLINE_ACTIVITY_GRAPHS_CALENDAR ) ;
case GRAPH_PUNCHCARD :
return List . of ( WebPermission . PAGE_SERVER_ONLINE_ACTIVITY_GRAPHS_PUNCHCARD ) ;
case GRAPH_WORLD_PIE :
return List . of ( WebPermission . PAGE_SERVER_SESSIONS_WORLD_PIE ) ;
case GRAPH_ACTIVITY :
return List . of ( WebPermission . PAGE_SERVER_PLAYERBASE_GRAPHS ) ;
case GRAPH_WORLD_MAP :
return List . of ( WebPermission . PAGE_SERVER_GEOLOCATIONS_MAP ) ;
case GRAPH_HOSTNAME_PIE :
return List . of ( WebPermission . PAGE_SERVER_JOIN_ADDRESSES_GRAPHS_PIE ) ;
case JOIN_ADDRESSES_BY_DAY :
return List . of ( WebPermission . PAGE_SERVER_JOIN_ADDRESSES_GRAPHS_TIME ) ;
default :
return List . of ( ) ;
}
}
private List < WebPermission > getRequiredNetworkPermission ( DataID dataID ) {
switch ( dataID ) {
case GRAPH_PERFORMANCE :
case GRAPH_OPTIMIZED_PERFORMANCE :
case GRAPH_PING :
return List . of ( WebPermission . PAGE_NETWORK_PERFORMANCE ) ;
case GRAPH_ACTIVITY :
return List . of ( WebPermission . PAGE_NETWORK_PLAYERBASE_GRAPHS ) ;
case GRAPH_UNIQUE_NEW :
return List . of ( WebPermission . PAGE_NETWORK_OVERVIEW_GRAPHS_DAY_BY_DAY ) ;
case GRAPH_HOURLY_UNIQUE_NEW :
return List . of ( WebPermission . PAGE_NETWORK_OVERVIEW_GRAPHS_HOUR_BY_HOUR ) ;
2023-09-30 16:10:18 +02:00
case GRAPH_CALENDAR :
return List . of ( WebPermission . PAGE_NETWORK_OVERVIEW_GRAPHS_CALENDAR ) ;
2023-08-20 10:56:13 +02:00
case GRAPH_SERVER_PIE :
return List . of ( WebPermission . PAGE_NETWORK_SESSIONS_SERVER_PIE ) ;
case GRAPH_WORLD_MAP :
return List . of ( WebPermission . PAGE_NETWORK_GEOLOCATIONS_MAP ) ;
case GRAPH_ONLINE_PROXIES :
return List . of ( WebPermission . PAGE_NETWORK_OVERVIEW_GRAPHS_ONLINE ) ;
case GRAPH_HOSTNAME_PIE :
return List . of ( WebPermission . PAGE_NETWORK_JOIN_ADDRESSES_GRAPHS_PIE ) ;
case JOIN_ADDRESSES_BY_DAY :
return List . of ( WebPermission . PAGE_NETWORK_JOIN_ADDRESSES_GRAPHS_TIME ) ;
default :
return List . of ( ) ;
}
}
2023-01-14 21:17:50 +01:00
private Object generateGraphDataJSONOfType ( DataID id , ServerUUID serverUUID , @Untrusted URIQuery query ) {
2019-08-30 23:20:50 +02:00
switch ( id ) {
case GRAPH_PERFORMANCE :
2020-03-01 19:35:00 +01:00
return graphJSON . performanceGraphJSON ( serverUUID ) ;
2021-01-24 16:40:30 +01:00
case GRAPH_OPTIMIZED_PERFORMANCE :
2022-09-05 15:56:59 +02:00
return graphJSON . optimizedPerformanceGraphJSON ( serverUUID ) ;
2019-08-30 23:20:50 +02:00
case GRAPH_ONLINE :
2020-03-01 19:35:00 +01:00
return graphJSON . playersOnlineGraph ( serverUUID ) ;
2019-08-30 23:20:50 +02:00
case GRAPH_UNIQUE_NEW :
2020-03-01 19:35:00 +01:00
return graphJSON . uniqueAndNewGraphJSON ( serverUUID ) ;
2020-05-15 20:48:35 +02:00
case GRAPH_HOURLY_UNIQUE_NEW :
return graphJSON . hourlyUniqueAndNewGraphJSON ( serverUUID ) ;
2019-08-30 23:20:50 +02:00
case GRAPH_CALENDAR :
2020-03-01 19:35:00 +01:00
return graphJSON . serverCalendarJSON ( serverUUID ) ;
2019-08-30 23:20:50 +02:00
case GRAPH_WORLD_PIE :
2020-03-01 19:35:00 +01:00
return graphJSON . serverWorldPieJSONAsMap ( serverUUID ) ;
2021-03-15 16:29:10 +01:00
case GRAPH_HOSTNAME_PIE :
return graphJSON . playerHostnamePieJSONAsMap ( serverUUID ) ;
2019-08-30 23:20:50 +02:00
case GRAPH_ACTIVITY :
2020-03-01 19:35:00 +01:00
return graphJSON . activityGraphsJSONAsMap ( serverUUID ) ;
2019-08-30 23:20:50 +02:00
case GRAPH_WORLD_MAP :
2020-03-01 19:35:00 +01:00
return graphJSON . geolocationGraphsJSONAsMap ( serverUUID ) ;
2019-08-30 23:20:50 +02:00
case GRAPH_PING :
2020-03-01 19:35:00 +01:00
return graphJSON . pingGraphsJSON ( serverUUID ) ;
2019-08-30 23:20:50 +02:00
case GRAPH_PUNCHCARD :
2020-03-01 19:35:00 +01:00
return graphJSON . punchCardJSONAsMap ( serverUUID ) ;
2022-09-05 15:56:59 +02:00
case JOIN_ADDRESSES_BY_DAY :
try {
return graphJSON . joinAddressesByDay ( serverUUID ,
query . get ( " after " ) . map ( Long : : parseLong ) . orElse ( 0L ) ,
query . get ( " before " ) . map ( Long : : parseLong ) . orElse ( System . currentTimeMillis ( ) )
) ;
2023-01-14 21:17:50 +01:00
} catch ( @Untrusted NumberFormatException e ) {
throw new BadRequestException ( " 'after' or 'before' is not a epoch millisecond (number) " ) ;
2022-09-05 15:56:59 +02:00
}
2019-06-09 17:20:40 +02:00
default :
2023-01-14 21:17:50 +01:00
throw new BadRequestException ( " Graph type not supported with server-parameter ( " + id . name ( ) + " ) " ) ;
2019-06-09 17:20:40 +02:00
}
}
2023-01-14 21:17:50 +01:00
private Object generateGraphDataJSONOfType ( DataID id , @Untrusted URIQuery query ) {
2019-08-30 23:20:50 +02:00
switch ( id ) {
case GRAPH_ACTIVITY :
2020-03-01 19:35:00 +01:00
return graphJSON . activityGraphsJSONAsMap ( ) ;
2019-08-30 23:20:50 +02:00
case GRAPH_UNIQUE_NEW :
2020-03-01 19:35:00 +01:00
return graphJSON . uniqueAndNewGraphJSON ( ) ;
2020-05-15 20:48:35 +02:00
case GRAPH_HOURLY_UNIQUE_NEW :
return graphJSON . hourlyUniqueAndNewGraphJSON ( ) ;
2023-09-30 16:10:18 +02:00
case GRAPH_CALENDAR :
return graphJSON . networkCalendarJSON ( ) ;
2019-08-30 23:20:50 +02:00
case GRAPH_SERVER_PIE :
2020-03-01 19:35:00 +01:00
return graphJSON . serverPreferencePieJSONAsMap ( ) ;
2021-03-09 09:01:09 +01:00
case GRAPH_HOSTNAME_PIE :
return graphJSON . playerHostnamePieJSONAsMap ( ) ;
2019-08-30 23:20:50 +02:00
case GRAPH_WORLD_MAP :
2020-03-01 19:35:00 +01:00
return graphJSON . geolocationGraphsJSONAsMap ( ) ;
2023-04-09 09:10:28 +02:00
case GRAPH_ONLINE_PROXIES :
return graphJSON . proxyPlayersOnlineGraphs ( ) ;
2022-09-10 09:45:03 +02:00
case JOIN_ADDRESSES_BY_DAY :
try {
return graphJSON . joinAddressesByDay (
query . get ( " after " ) . map ( Long : : parseLong ) . orElse ( 0L ) ,
query . get ( " before " ) . map ( Long : : parseLong ) . orElse ( System . currentTimeMillis ( ) )
) ;
2023-01-14 21:17:50 +01:00
} catch ( @Untrusted NumberFormatException e ) {
throw new BadRequestException ( " 'after' or 'before' is not a epoch millisecond (number) " ) ;
2022-09-10 09:45:03 +02:00
}
2019-08-15 13:37:40 +02:00
default :
2023-01-14 21:17:50 +01:00
throw new BadRequestException ( " Graph type not supported without server-parameter ( " + id . name ( ) + " ) " ) ;
2019-08-15 13:37:40 +02:00
}
}
2019-06-09 17:20:40 +02:00
}