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.thinkbiganalytics.feedmgr.rest.model.IconColor; import com.thinkbiganalytics.feedmgr.rest.support.SystemNamingService; import com.thinkbiganalytics.feedmgr.service.UIService; import com.thinkbiganalytics.json.ObjectMapperSerializer; import com.thinkbiganalytics.scheduler.util.CronExpressionUtil; import com.thinkbiganalytics.spring.FileResourceService; import org.apache.commons.lang3.StringUtils; import org.quartz.CronExpression; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.annotation.Nonnull; import javax.inject.Inject; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; 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; @Api(tags = "Feed Manager - Utilities", produces = "application/json") @Path("/v1/feedmgr/util") @Component public class UtilityRestController { private static final Logger log = LoggerFactory.getLogger(UtilityRestController.class); @Inject Environment env; @Inject FileResourceService fileResourceService; @GET @Path("/cron-expression/validate") @Produces(MediaType.APPLICATION_JSON) @ApiOperation("Validates the specified cron expression.") @ApiResponses( @ApiResponse(code = 200, message = "Returns the result.", response = Map.class) ) public Response validateCronExpression(@QueryParam("cronExpression") String cronExpression) { boolean valid = CronExpression.isValidExpression(cronExpression); return Response.ok("{\"valid\":" + valid + "}").build(); } @GET @Path("/cron-expression/preview") @Produces(MediaType.APPLICATION_JSON) @ApiOperation("Gets the next matching times of the cron expression.") @ApiResponses( @ApiResponse(code = 200, message = "Returns the times.", response = String.class, responseContainer = "List") ) public Response previewCronExpression(@QueryParam("cronExpression") String cronExpression, @QueryParam("number") @DefaultValue("3") Integer number) { List<Date> dates = new ArrayList<>(); List<String> dateStrings = new ArrayList<>(); SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy hh:mm:ss a"); try { dates = CronExpressionUtil.getNextFireTimes(cronExpression, number); for (Date date : dates) { dateStrings.add(format.format(date)); } } catch (ParseException e) { throw new RuntimeException(e); } return Response.ok(dateStrings).build(); } @GET @Path("/system-name") @Produces(MediaType.TEXT_PLAIN) @ApiOperation("Generates a system name from the specified name.") @ApiResponses( @ApiResponse(code = 200, message = "Returns the system name.", response = String.class) ) public Response generateSystemName(@QueryParam("name") String name) { String systemName = SystemNamingService.generateSystemName(name); return Response.ok(systemName).build(); } @GET @Path("/codemirror-types") @Produces(MediaType.APPLICATION_JSON) @ApiOperation("Gets the languages supported by CodeMirror.") @ApiResponses( @ApiResponse(code = 200, message = "Returns a mime-type to language mapping.", response = Map.class) ) public Response codeMirrorTypes() { Map<String, String> types = UIService.getInstance().getCodeMirrorTypes(); return Response.ok(types).build(); } @GET @Path("/icon-colors") @Produces(MediaType.APPLICATION_JSON) @ApiOperation("Gets the list of available icon colors.") @ApiResponses( @ApiResponse(code = 200, message = "Returns the icon colors.", response = Map.class, responseContainer = "List") ) public Response iconColors() { String colorsJson = fileResourceService.getResourceAsString("classpath:/icon-colors.json"); List<IconColor> colors = null; if (StringUtils.isNotBlank(colorsJson)) { //attempt to convert it to a list try { colors = Arrays.asList(ObjectMapperSerializer.deserialize(colorsJson, IconColor[].class)); } catch (Exception e) { log.error( "Unable to parse JSON for icon-colors.json file. Reverting to using default colors. Please check the icon-colors.json file in the /conf directory for propery JSON format Error: {}", e.getMessage()); } } if (colors == null || colors.isEmpty() || StringUtils.isBlank(colorsJson)) { colorsJson = "[{\"name\":\"Purple\",\"color\":\"#AB47BC\"},{\"name\":\"Orange\",\"color\":\"#FFCA28\"},{\"name\":\"Deep Orange\",\"color\":\"#FF8A65\"},{\"name\":\"Red\",\"color\":\"#FF5252\"},{\"name\":\"Blue\",\"color\":\"#90CAF9\"},{\"name\":\"Green\",\"color\":\"#66BB6A\"},{\"name\":\"Blue Grey\",\"color\":\"#90A4AE\"},{\"name\":\"Teal\",\"color\":\"#80CBC4\"},{\"name\":\"Pink\",\"color\":\"#F06292\"},{\"name\":\"Yellow\",\"color\":\"#FFF176\"}]"; colors = Arrays.asList(ObjectMapperSerializer.deserialize(colorsJson, IconColor[].class)); } return Response.ok(colors).build(); } @GET @Path("/icons") @Produces(MediaType.APPLICATION_JSON) @ApiOperation("Gets the list of available icons.") @ApiResponses( @ApiResponse(code = 200, message = "Returns the icons.", response = String.class, responseContainer = "List") ) public Response icons() { String iconJson = fileResourceService.getResourceAsString("classpath:/icons.json"); List<String> icons = null; if (StringUtils.isNotBlank(iconJson)) { //attempt to convert it to a list try { String[] iconArray = ObjectMapperSerializer.deserialize(iconJson, String[].class); icons = Arrays.asList(iconArray); } catch (Exception e) { log.error("Unable to parse JSON for icon.json file. Reverting to using default icons. Please check the icon.json file in the /conf directory for propery JSON format. Error: {}", e.getMessage()); } } if (icons == null || StringUtils.isBlank(iconJson)) { iconJson = "[\"local_airport\",\"phone_android\",\"web\",\"forward\",\"star\",\"attach_money\",\"location_city\",\"style\",\"insert_chart\",\"merge_type\",\"local_dining\",\"people\",\"directions_run\",\"traffic\",\"format_paint\",\"email\",\"cloud\",\"build\",\"favorite\",\"face\",\"http\",\"info\",\"input\",\"lock\",\"message\",\"highlight\",\"computer\",\"toys\",\"security\"]"; icons = Arrays.asList(ObjectMapperSerializer.deserialize(iconJson, String[].class)); } return Response.ok(icons).build(); } /** * Gets the list of functions that can be used to produce partition values. * * @return an HTTP response containing the list of formulas */ @GET @Path("/partition-functions") @Produces(MediaType.APPLICATION_JSON) @ApiOperation(value = "Gets the list of partition functions.", notes = "These functions can be used to produce partition values.") @ApiResponses( @ApiResponse(code = 200, message = "Returns the partition functions.", response = String.class, responseContainer = "Set") ) @Nonnull public Response partitionFunctions() { final Stream<String> kyloFunctions = Stream.of("val", "to_date", "year", "month", "day", "hour", "minute"); final Stream<String> userFunctions = Arrays.stream(env.getProperty("kylo.metadata.udfs", "").split(",")).map(String::trim).filter(StringUtils::isNotEmpty); return Response.ok(Stream.concat(kyloFunctions, userFunctions).collect(Collectors.toSet())).build(); } }