package com.thinkbiganalytics.feedmgr.rest.controller;
/*-
* #%L
* thinkbig-feed-manager-controller
* %%
* Copyright (C) 2017 ThinkBig Analytics
* %%
* Licensed 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.
* #L%
*/
import com.fasterxml.jackson.core.type.TypeReference;
import com.thinkbiganalytics.feedmgr.rest.ImportComponent;
import com.thinkbiganalytics.feedmgr.rest.model.ImportComponentOption;
import com.thinkbiganalytics.feedmgr.rest.model.ImportFeedOptions;
import com.thinkbiganalytics.feedmgr.rest.model.ImportProperty;
import com.thinkbiganalytics.feedmgr.rest.model.ImportTemplateOptions;
import com.thinkbiganalytics.feedmgr.rest.model.UploadProgress;
import com.thinkbiganalytics.feedmgr.rest.model.UserFieldCollection;
import com.thinkbiganalytics.feedmgr.service.MetadataService;
import com.thinkbiganalytics.feedmgr.service.UploadProgressService;
import com.thinkbiganalytics.feedmgr.service.feed.ExportImportFeedService;
import com.thinkbiganalytics.feedmgr.service.template.ExportImportTemplateService;
import com.thinkbiganalytics.feedmgr.util.ImportUtil;
import com.thinkbiganalytics.json.ObjectMapperSerializer;
import com.thinkbiganalytics.rest.model.RestResponseStatus;
import org.apache.commons.lang.StringUtils;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.inject.Inject;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
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;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import io.swagger.annotations.SwaggerDefinition;
import io.swagger.annotations.Tag;
/**
* REST API for administrative functions.
*/
@Api(tags = "Feed Manager - Administration", produces = "application/json")
@Path(AdminController.BASE)
@SwaggerDefinition(tags = @Tag(name = "Feed Manager - Administration", description = "administrator operations"))
public class AdminController {
public static final String BASE = "/v1/feedmgr/admin";
public static final String IMPORT_TEMPLATE = "/import-template";
public static final String IMPORT_FEED = "/import-feed";
public static final String IMPORT_TEMPLATE_NEW = "/import-template2";
public static final String IMPORT_FEED_NEW = "/import-feed2";
@Inject
ExportImportTemplateService exportImportTemplateService;
@Inject
ExportImportFeedService exportImportFeedService;
@Inject
UploadProgressService uploadProgressService;
/**
* Feed manager metadata service
*/
@Inject
MetadataService metadataService;
@GET
@Path("/export-template/{templateId}")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation("Exports the template with the specified ID.")
@ApiResponses({
@ApiResponse(code = 200, message = "Returns the template as an attachment."),
@ApiResponse(code = 400, message = "the templateId is invalid.", response = RestResponseStatus.class),
@ApiResponse(code = 500, message = "The template is not available.", response = RestResponseStatus.class)
})
public Response exportTemplate(@NotNull @Size(min = 36, max = 36, message = "Invalid templateId size")
@PathParam("templateId") String templateId) {
ExportImportTemplateService.ExportTemplate zipFile = exportImportTemplateService.exportTemplate(templateId);
return Response.ok(zipFile.getFile(), MediaType.APPLICATION_OCTET_STREAM)
.header("Content-Disposition", "attachments; filename=\"" + zipFile.getFileName() + "\"") //optional
.build();
}
@GET
@Path("/export-feed/{feedId}")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation("Exports the feed with the specified ID.")
@ApiResponses({
@ApiResponse(code = 200, message = "Returns the feed as an attachment."),
@ApiResponse(code = 400, message = "The feedId is invalid.", response = RestResponseStatus.class),
@ApiResponse(code = 404, message = "The feed is not available.", response = RestResponseStatus.class)
})
public Response exportFeed(@NotNull @Size(min = 36, max = 36, message = "Invalid feedId size")
@PathParam("feedId") String feedId) {
try {
ExportImportFeedService.ExportFeed zipFile = exportImportFeedService.exportFeed(feedId);
return Response.ok(zipFile.getFile(), MediaType.APPLICATION_OCTET_STREAM)
.header("Content-Disposition", "attachments; filename=\"" + zipFile.getFileName() + "\"") //optional
.build();
} catch (IOException e) {
throw new RuntimeException("Unable to export Feed " + e.getMessage());
}
}
@GET
@Path("/upload-status/{key}")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation("Gets thet status of a given upload/import.")
@ApiResponses({
@ApiResponse(code = 200, message = "Returns the upload status")
})
public Response uploadStatus(@NotNull @PathParam("key") String key) {
UploadProgress uploadProgress = uploadProgressService.getUploadStatus(key);
if (uploadProgress != null) {
uploadProgress.checkAndIncrementPercentage();
return Response.ok(uploadProgress).build();
} else {
return Response.ok(uploadProgress).build();
}
}
/**
* This is used for quick import via a script. The UI uses the AdminControllerV2 class
*/
@POST
@Path(IMPORT_FEED)
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation("Imports a feed zip file.")
@ApiResponses({
@ApiResponse(code = 200, message = "Returns the feed metadata.", response = ExportImportFeedService.ImportFeed.class),
@ApiResponse(code = 500, message = "There was a problem importing the feed.", response = RestResponseStatus.class)
})
public Response uploadFeed(@NotNull @FormDataParam("file") InputStream fileInputStream,
@NotNull @FormDataParam("file") FormDataContentDisposition fileMetaData,
@FormDataParam("overwrite") @DefaultValue("false") boolean overwrite,
@FormDataParam("overwriteFeedTemplate") @DefaultValue("false") boolean overwriteFeedTemplate,
@FormDataParam("categorySystemName") String categorySystemName,
@FormDataParam("importConnectingReusableFlow") @DefaultValue("NOT_SET") ImportTemplateOptions.IMPORT_CONNECTING_FLOW importConnectingFlow,
@FormDataParam("templateProperties") @DefaultValue("") String templateProperties,
@FormDataParam("feedProperties") @DefaultValue("") String feedProperties)
throws Exception {
ImportFeedOptions options = new ImportFeedOptions();
String uploadKey = uploadProgressService.newUpload();
options.setUploadKey(uploadKey);
options.findImportComponentOption(ImportComponent.FEED_DATA).setOverwrite(overwrite);
options.findImportComponentOption(ImportComponent.FEED_DATA).setUserAcknowledged(true);
options.findImportComponentOption(ImportComponent.FEED_DATA).setShouldImport(true);
options.findImportComponentOption(ImportComponent.REUSABLE_TEMPLATE).setShouldImport(importConnectingFlow.equals(ImportTemplateOptions.IMPORT_CONNECTING_FLOW.YES));
options.findImportComponentOption(ImportComponent.REUSABLE_TEMPLATE).setUserAcknowledged(!importConnectingFlow.equals(ImportTemplateOptions.IMPORT_CONNECTING_FLOW.NOT_SET));
options.findImportComponentOption(ImportComponent.REUSABLE_TEMPLATE).setOverwrite(importConnectingFlow.equals(ImportTemplateOptions.IMPORT_CONNECTING_FLOW.YES) && overwriteFeedTemplate);
options.findImportComponentOption(ImportComponent.NIFI_TEMPLATE).setOverwrite(overwriteFeedTemplate);
options.findImportComponentOption(ImportComponent.NIFI_TEMPLATE).setUserAcknowledged(true);
options.findImportComponentOption(ImportComponent.NIFI_TEMPLATE).setShouldImport(true);
options.findImportComponentOption(ImportComponent.NIFI_TEMPLATE).setContinueIfExists(!overwriteFeedTemplate);
options.findImportComponentOption(ImportComponent.TEMPLATE_DATA).setOverwrite(overwriteFeedTemplate);
options.findImportComponentOption(ImportComponent.TEMPLATE_DATA).setUserAcknowledged(true);
options.findImportComponentOption(ImportComponent.TEMPLATE_DATA).setShouldImport(true);
options.findImportComponentOption(ImportComponent.TEMPLATE_DATA).setContinueIfExists(!overwriteFeedTemplate);
options.setCategorySystemName(categorySystemName);
if(StringUtils.isNotBlank(templateProperties)) {
List<ImportProperty> properties = ObjectMapperSerializer.deserialize(templateProperties, new TypeReference<List<ImportProperty>>() {
});
options.findImportComponentOption(ImportComponent.TEMPLATE_DATA).setProperties(properties);
}
if(StringUtils.isNotBlank(feedProperties)) {
List<ImportProperty> properties = ObjectMapperSerializer.deserialize(feedProperties, new TypeReference<List<ImportProperty>>() {
});
options.findImportComponentOption(ImportComponent.FEED_DATA).setProperties(properties);
}
byte[] content = ImportUtil.streamToByteArray(fileInputStream);
ExportImportFeedService.ImportFeed importFeed = exportImportFeedService.importFeed(fileMetaData.getFileName(), content, options);
return Response.ok(importFeed).build();
}
/**
* This is used for quick import via scripts. The UI uses the AdminControllerV2 class
*/
@POST
@Path(IMPORT_TEMPLATE)
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation("Imports a template xml or zip file.")
@ApiResponses({
@ApiResponse(code = 200, message = "Returns the template metadata.", response = ExportImportTemplateService.ImportTemplate.class),
@ApiResponse(code = 500, message = "There was a problem importing the template.", response = RestResponseStatus.class)
})
public Response uploadTemplatex(@NotNull @FormDataParam("file") InputStream fileInputStream,
@NotNull @FormDataParam("file") FormDataContentDisposition fileMetaData,
@FormDataParam("overwrite") @DefaultValue("false") boolean overwrite,
@FormDataParam("importConnectingReusableFlow") @DefaultValue("NOT_SET") ImportTemplateOptions.IMPORT_CONNECTING_FLOW importConnectingFlow,
@FormDataParam("createReusableFlow") @DefaultValue("false") boolean createReusableFlow,
@FormDataParam("templateProperties") @DefaultValue("") String templateProperties)
throws Exception {
ImportTemplateOptions options = new ImportTemplateOptions();
String uploadKey = uploadProgressService.newUpload();
options.setUploadKey(uploadKey);
boolean isZip = fileMetaData.getFileName().endsWith("zip");
if (isZip) {
options.findImportComponentOption(ImportComponent.REUSABLE_TEMPLATE).setShouldImport(importConnectingFlow.equals(ImportTemplateOptions.IMPORT_CONNECTING_FLOW.YES));
options.findImportComponentOption(ImportComponent.REUSABLE_TEMPLATE).setUserAcknowledged(!importConnectingFlow.equals(ImportTemplateOptions.IMPORT_CONNECTING_FLOW.NOT_SET));
options.findImportComponentOption(ImportComponent.REUSABLE_TEMPLATE).setOverwrite(importConnectingFlow.equals(ImportTemplateOptions.IMPORT_CONNECTING_FLOW.YES));
} else {
options.findImportComponentOption(ImportComponent.REUSABLE_TEMPLATE).setShouldImport(createReusableFlow);
options.findImportComponentOption(ImportComponent.REUSABLE_TEMPLATE).setUserAcknowledged(true);
options.findImportComponentOption(ImportComponent.REUSABLE_TEMPLATE).setOverwrite(createReusableFlow);
}
options.findImportComponentOption(ImportComponent.NIFI_TEMPLATE).setOverwrite(overwrite);
options.findImportComponentOption(ImportComponent.NIFI_TEMPLATE).setUserAcknowledged(true);
options.findImportComponentOption(ImportComponent.NIFI_TEMPLATE).setShouldImport(true);
options.findImportComponentOption(ImportComponent.NIFI_TEMPLATE).setContinueIfExists(!overwrite);
options.findImportComponentOption(ImportComponent.TEMPLATE_DATA).setOverwrite(overwrite);
options.findImportComponentOption(ImportComponent.TEMPLATE_DATA).setUserAcknowledged(true);
options.findImportComponentOption(ImportComponent.TEMPLATE_DATA).setShouldImport(true);
if(StringUtils.isNotBlank(templateProperties)) {
List<ImportProperty> properties = ObjectMapperSerializer.deserialize(templateProperties, new TypeReference<List<ImportProperty>>() {
});
options.findImportComponentOption(ImportComponent.TEMPLATE_DATA).setProperties(properties);
}
byte[] content = ImportUtil.streamToByteArray(fileInputStream);
ExportImportTemplateService.ImportTemplate importTemplate = exportImportTemplateService.importTemplate(fileMetaData.getFileName(), content, options);
return Response.ok(importTemplate).build();
}
/**
* Gets the user-defined fields for all categories and feeds.
*
* @return the user-defined fields
* @since 0.4.0
*/
@GET
@Path("user-fields")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation("Gets the user-defined fields for categories and feeds.")
@ApiResponses(
@ApiResponse(code = 200, message = "Returns the user-defined fields.", response = UserFieldCollection.class)
)
@Nonnull
public Object getUserFields() {
return metadataService.getUserFields();
}
/**
* Sets the user-defined fields for all categories and feeds.
*
* @param userFields the new user-defined fields
* @since 0.4.0
*/
@POST
@Path("user-fields")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation("Sets the user-defined fields for categories and feeds.")
@ApiResponses(
@ApiResponse(code = 204, message = "The user-defined fields have been updated.")
)
public void setUserFields(@Nonnull final UserFieldCollection userFields) {
metadataService.setUserFields(userFields);
}
}