/*
*
* * This file is part of the Hesperides distribution.
* * (https://github.com/voyages-sncf-technologies/hesperides)
* * Copyright (c) 2016 VSCT.
* *
* * Hesperides is free software: you can redistribute it and/or modify
* * it under the terms of the GNU General Public License as
* * published by the Free Software Foundation, version 3.
* *
* * Hesperides 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
* * General Public License for more details.
* *
* * You should have received a copy of the GNU General Public License
* * along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
package com.vsct.dt.hesperides.resources;
import com.codahale.metrics.annotation.Timed;
import com.google.common.collect.Sets;
import com.vsct.dt.hesperides.applications.Applications;
import com.vsct.dt.hesperides.applications.InstanceModel;
import com.vsct.dt.hesperides.applications.PlatformKey;
import com.vsct.dt.hesperides.indexation.search.ApplicationSearch;
import com.vsct.dt.hesperides.indexation.search.PlatformSearchResponse;
import com.vsct.dt.hesperides.security.model.User;
import com.vsct.dt.hesperides.templating.models.HesperidesPropertiesModel;
import com.vsct.dt.hesperides.templating.modules.ModuleKey;
import com.vsct.dt.hesperides.templating.modules.Modules;
import com.vsct.dt.hesperides.templating.platform.ApplicationData;
import com.vsct.dt.hesperides.templating.platform.PlatformData;
import com.vsct.dt.hesperides.templating.platform.TimeStampedPlatformData;
import com.vsct.dt.hesperides.util.HesperidesUtil;
import com.vsct.dt.hesperides.util.Release;
import com.vsct.dt.hesperides.util.WorkingCopy;
import com.vsct.dt.hesperides.util.converter.ApplicationConverter;
import com.vsct.dt.hesperides.util.converter.PlatformConverter;
import com.vsct.dt.hesperides.util.converter.PropertiesConverter;
import com.vsct.dt.hesperides.util.converter.TimeStampedPlatformConverter;
import com.wordnik.swagger.annotations.Api;
import com.wordnik.swagger.annotations.ApiOperation;
import io.dropwizard.auth.Auth;
import javax.validation.Valid;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import java.util.*;
import java.util.stream.Collectors;
import static com.vsct.dt.hesperides.util.CheckArgument.isNonDisplayedChar;
/**
* Created by william_montaz on 11/07/14.
*
* Modified by tidiane_sidibe on 05/08/2016 : adding the comment to event
*/
@Path("/applications")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Api("/applications")
public final class HesperidesApplicationResource extends BaseResource {
private final Applications applications;
private final Modules modules;
private final ApplicationSearch applicationSearch;
private final PlatformConverter platformConverter;
private final PropertiesConverter propertiesConverter;
final TimeStampedPlatformConverter timeStampedPlatformConverter;
private ResponseConverter<PlatformData, Platform> responsePlatformConverter;
private ResponseConverter<TimeStampedPlatformData, TimeStampedPlatform> responseTimeConverter;
private ResponseConverter<ApplicationData, Application> responseApplicationConverter;
private final HesperidesModuleResource moduleResource;
public HesperidesApplicationResource(final Applications applications, final Modules modules,
final ApplicationSearch applicationSearch,
final TimeStampedPlatformConverter timeStampedPlatformConverter,
final ApplicationConverter applicationDataConverter,
final PropertiesConverter propertiesConverter,
final HesperidesModuleResource moduleResource) {
this.applications = applications;
this.modules = modules;
this.applicationSearch = applicationSearch;
this.platformConverter = timeStampedPlatformConverter.getPlatformConverter();
this.timeStampedPlatformConverter = timeStampedPlatformConverter;
this.responsePlatformConverter = obj -> platformConverter.toPlatform(obj);
this.responseTimeConverter = obj -> timeStampedPlatformConverter.toTimeStampedPlatform(obj);
this.responseApplicationConverter = obj -> applicationDataConverter.toApplication(obj);
this.propertiesConverter = propertiesConverter;
this.moduleResource = moduleResource;
}
@Path("/using_module/{module}/{version}/{type}")
@GET
@Timed
@ApiOperation("Get application using module")
public Response getApplicationUsingModule(@Auth final User user, @PathParam("module") final String module, @PathParam("version") final String version,
@PathParam("type") final String type) {
return Response.ok(applicationSearch.getAllPlatformsUsingModules(module, version, type)).build();
}
@Path("/{application_name}/platforms/{platform_name}/global_properties_usage")
@GET
@Timed
@ApiOperation("Retrieve global properties usage in application")
public Response getGlobalPropertiesUsage(@Auth final User user, @PathParam("application_name") final String applicationName,
@PathParam("platform_name") final String platformName) {
final Optional<PlatformData> searchPlatform = this.applications.getPlatform(new PlatformKey(applicationName, platformName));
if (searchPlatform.isPresent()) {
final PlatformData ptf = searchPlatform.get();
final Map<String, Set<Map>> usage = new HashMap<>();
applications
.getProperties(new PlatformKey(applicationName, platformName), "#")
.getKeyValueProperties()
.stream()
.forEach(globalProp -> {
final Set<Map> buffer = new HashSet<>();
ptf.getModules().stream().forEach(elem -> {
final PlatformKey currentPlatformKey = new PlatformKey(applicationName, platformName);
Optional<HesperidesPropertiesModel> oModel;
HesperidesPropertiesModel model;
if (elem.isWorkingCopy()) {
oModel = modules.getModel(new ModuleKey(elem.getName(), WorkingCopy.of(elem.getVersion())));
} else {
oModel = modules.getModel(new ModuleKey(elem.getName(), Release.of(elem.getVersion())));
}
if (oModel.isPresent()) {
model = oModel.get();
if (model.getKeyValueProperties().stream().anyMatch(prop ->
prop.getName().equals(globalProp.getName()))) {
final Map b = new HashMap();
b.put("path", elem.getPropertiesPath());
b.put("inModel", true);
buffer.add(b);
}
} else {
model = null;
}
applications
.getProperties(currentPlatformKey, elem.getPropertiesPath())
.getKeyValueProperties()
.stream()
.forEach(prop -> {
if (prop.getName().equals(globalProp.getName())
|| prop.getValue().contains("{{" + globalProp.getName() + "}}")) {
if (model == null) {
final Map b = new HashMap();
b.put("path", elem.getPropertiesPath());
b.put("inModel", false);
buffer.add(b);
} else {
final boolean inModel = model.getKeyValueProperties().stream().anyMatch(mod ->
prop.getName().equals(mod.getName()));
final Map b = new HashMap();
b.put("path", elem.getPropertiesPath());
b.put("inModel", inModel);
buffer.add(b);
}
}
});
});
usage.put(globalProp.getName(), buffer);
});
return Response.ok(usage).build();
} else {
return Response.status(Status.NOT_FOUND).entity("Module application and platform not found").build();
}
}
@Path("/{application_name}")
@GET
@Timed
@ApiOperation("Get application with given name")
public Response getApplication(@Auth final User user, @PathParam("application_name") final String name) {
checkQueryParameterNotEmpty("application_name", name);
return entityWithConverterOrNotFound(applications.getApplication(name), responseApplicationConverter);
}
@Path("/perform_search")
@POST
@Timed
@ApiOperation("Get application by name")
public Set<ApplicationListItem> searchApplications(@Auth final User user, @QueryParam("name") final String name) {
checkQueryParameterNotEmpty("name", name);
return applicationSearch.getApplicationsLike(name).stream().map(applicationSearchResponse ->
new ApplicationListItem(applicationSearchResponse.getName())
).collect(Collectors.toSet());
}
@Path("/platforms/perform_search")
@POST
@Timed
@ApiOperation("Get all platform for a given application name")
public Set<PlatformListItem> searchPlatforms(@Auth final User user, @QueryParam("applicationName") final String applicationName, @QueryParam("platformName") final String platformName) {
checkQueryParameterNotEmpty("applicationName", applicationName);
return applicationSearch.getAllPlatforms(applicationName, platformName).stream().map(platformSearchResponse ->
new PlatformListItem(platformSearchResponse.getName())
).collect(Collectors.toSet());
}
@Path("/{application_name}/platforms/{platform_name}")
@GET
@Timed
@ApiOperation("Get platform for application with given name")
public Response getPlatform(@Auth final User user,
@PathParam("application_name") final String applicationName,
@PathParam("platform_name") final String platformName,
@QueryParam("timestamp") final Long timestamp) {
checkQueryParameterNotEmpty("application_name", applicationName);
checkQueryParameterNotEmpty("platform_name", platformName);
PlatformKey platformKey = PlatformKey.withName(platformName)
.withApplicationName(applicationName)
.build();
if(timestamp != null){
return entityWithConverterOrNotFound(applications.getPlatform(platformKey, timestamp), responseTimeConverter);
} else {
return entityWithConverterOrNotFound(applications.getPlatform(platformKey), responsePlatformConverter);
}
}
@Path("/{application_name}/platforms/{platform_name}")
@DELETE
@Timed
@ApiOperation("Deletes a platform on a given app")
public Response deletePlatform(@Auth final User user,
@PathParam("application_name") final String applicationName,
@PathParam("platform_name") final String platformName){
checkQueryParameterNotEmpty("application_name", applicationName);
checkQueryParameterNotEmpty("platform_name", platformName);
PlatformKey platformKey = PlatformKey.withName(platformName)
.withApplicationName(applicationName)
.build();
applications.delete(platformKey);
return Response.ok().build();
}
@Path("/{application_name}/platforms")
@POST
@Timed
@ApiOperation("Create platform for application with given name, possibly from an existing platform")
public Platform createPlatform(@Auth final User user,
@PathParam("application_name") final String applicationName,
@QueryParam("from_application") final String fromApplication,
@QueryParam("from_platform") final String fromPlatform,
@Valid final Platform platform) {
if((fromApplication == null || isNonDisplayedChar(fromApplication))
&& (fromPlatform == null || isNonDisplayedChar(fromPlatform))) {
return platformConverter.toPlatform(applications.createPlatform(platformConverter.toPlatformData(platform)));
} else {
PlatformKey fromPlatformKey = PlatformKey.withName(fromPlatform)
.withApplicationName(fromApplication)
.build();
checkQueryParameterNotEmpty("from_application", fromApplication);
checkQueryParameterNotEmpty("from_platform", fromPlatform);
return platformConverter.toPlatform(
applications.createPlatformFromExistingPlatform(
platformConverter.toPlatformData(platform),
fromPlatformKey));
}
}
@Path("/{application_name}/platforms")
@PUT
@Timed
@ApiOperation("Update platform for application with given name")
public Platform updatePlatform(@Auth final User user,
@PathParam("application_name") final String applicationName,
@QueryParam("copyPropertiesForUpgradedModules") final boolean copyPropertiesForUpgradedModules,
@Valid final Platform platform) {
return platformConverter.toPlatform(
applications.updatePlatform(
platformConverter.toPlatformData(platform), copyPropertiesForUpgradedModules));
}
@Path("/{application_name}/platforms/{platform_name}/properties")
@GET
@Timed
@ApiOperation("Get properties with the given path in a platform")
public Properties getProperties(@Auth final User user,
@PathParam("application_name") final String applicationName,
@PathParam("platform_name") final String platformName,
@QueryParam("path") final String path,
@QueryParam("timestamp") final Long timestamp) {
checkQueryParameterNotEmpty("application_name", applicationName);
checkQueryParameterNotEmpty("platform_name", platformName);
checkQueryParameterNotEmpty("path", path);
PlatformKey platformKey = PlatformKey.withName(platformName)
.withApplicationName(applicationName)
.build();
HesperidesPropertiesModel model = new HesperidesPropertiesModel(Sets.newHashSet(), Sets.newHashSet());
if (path.length() > 1) {
HesperidesUtil.ModuleInfo info = HesperidesUtil.moduleInfoFromPath(path);
model = info.isRelease() ? this.moduleResource.getReleaseModel(user, info.getName(), info.getVersion()) : this.moduleResource.getWorkingCopyModel(user, info.getName(), info.getVersion());
}
return (timestamp != null) ? propertiesConverter.toProperties(applications.getSecuredProperties(platformKey, path, timestamp, model)) : propertiesConverter.toProperties(applications.getSecuredProperties(platformKey, path, model));
}
@Path("/{application_name}/platforms/{platform_name}/properties/instance_model")
@GET
@Timed
@ApiOperation("Get properties with the given path in a platform")
public InstanceModel getInstanceModel(@Auth final User user,
@PathParam("application_name") final String applicationName,
@PathParam("platform_name") final String platformName,
@QueryParam("path") final String path) {
checkQueryParameterNotEmpty("application_name", applicationName);
checkQueryParameterNotEmpty("platform_name", platformName);
checkQueryParameterNotEmpty("path", path);
PlatformKey platformKey = PlatformKey.withName(platformName)
.withApplicationName(applicationName)
.build();
return applications.getInstanceModel(platformKey, path);
}
@Path("/{application_name}/platforms/{platform_name}/properties")
@POST
@Timed
@ApiOperation("Save properties in a platform with the given path")
public Properties saveProperties(@Auth final User user,
@PathParam("application_name") final String applicationName,
@PathParam("platform_name") final String platformName,
@QueryParam("path") final String path,
@QueryParam("platform_vid") final Long platformID, //Using Long wrapper object helps preventing Java Primitive Default value of 0
@QueryParam("comment") final String comment,
@Valid final Properties properties) {
checkQueryParameterNotEmpty("application_name", applicationName);
checkQueryParameterNotEmpty("platform_name", platformName);
checkQueryParameterNotEmpty("path", path);
checkQueryParameterNotEmpty("platform_vid", platformID);
checkQueryParameterNotEmpty("comment", comment);
PlatformKey platformKey = PlatformKey.withName(platformName)
.withApplicationName(applicationName)
.build();
//Filter properties to remove null or empty valuations
Properties propertiesCleaned;
if (path.equals("#")) {
propertiesCleaned = properties;
} else {
propertiesCleaned = properties.makeCopyWithoutNullOrEmptyValorisations();
}
return propertiesConverter.toProperties(
applications.createOrUpdatePropertiesInPlatform(platformKey, path,
propertiesConverter.toPropertiesData(propertiesCleaned), platformID, comment));
}
@Path("/{application_name}/platforms/{platform_name}/take_snapshot")
@POST
@Timed
@ApiOperation("Take a snapshot of the platform")
public Response takeSnapshot(@Auth final User user,
@PathParam("application_name") final String applicationName,
@PathParam("platform_name") final String platformName){
checkQueryParameterNotEmpty("application_name", applicationName);
checkQueryParameterNotEmpty("platform_name", platformName);
PlatformKey platformKey = PlatformKey.withName(platformName)
.withApplicationName(applicationName)
.build();
applications.takeSnapshot(platformKey);
return Response.ok().build();
}
@Path("/{application_name}/platforms/{platform_name}/restore_snapshot")
@POST
@Timed
@ApiOperation("Take a snapshot of the platform")
public Platform restaureSnapshot(@Auth final User user,
@PathParam("application_name") final String applicationName,
@PathParam("platform_name") final String platformName,
@QueryParam("timestamp") final long timestamp){
checkQueryParameterNotEmpty("application_name", applicationName);
checkQueryParameterNotEmpty("platform_name", platformName);
checkQueryParameterNotEmpty("timestamp", timestamp);
PlatformKey platformKey = PlatformKey.withName(platformName)
.withApplicationName(applicationName)
.build();
return platformConverter.toPlatform(applications.restoreSnapshot(platformKey, timestamp));
}
@Path("/{application_name}/platforms/{platform_name}/snapshots")
@GET
@Timed
@ApiOperation("Get the list of timestamps corresponding to snapshots of the platform")
public List<Long> getSnapshots(@Auth final User user,
@PathParam("application_name") final String applicationName,
@PathParam("platform_name") final String platformName){
checkQueryParameterNotEmpty("application_name", applicationName);
checkQueryParameterNotEmpty("platform_name", platformName);
PlatformKey platformKey = PlatformKey.withName(platformName)
.withApplicationName(applicationName)
.build();
List<Long> snapshots = applications.getSnapshots(platformKey);
Collections.sort(snapshots, (longA, longB) -> longB.compareTo(longA));
return snapshots;
}
}