All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fi.evolver.basics.spring.triggerable.TriggerController Maven / Gradle / Ivy

package fi.evolver.basics.spring.triggerable;

import static fi.evolver.basics.spring.http.HttpInterceptor.addMetadata;
import static fi.evolver.basics.spring.http.HttpInterceptor.setMessageType;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.zip.GZIPOutputStream;

import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.CountingOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import fi.evolver.basics.spring.http.MessageType;
import fi.evolver.basics.spring.http.exception.HttpBadRequestException;
import fi.evolver.basics.spring.http.exception.HttpInternalServerErrorException;
import fi.evolver.basics.spring.job.ResultState;
import fi.evolver.utils.GzipUtils;
import fi.evolver.utils.arg.Arg;
import fi.evolver.utils.arg.StringArg;
import fi.evolver.utils.http.MultipartStream;
import fi.evolver.utils.http.Part;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.servlet.http.HttpServletRequest;

@RestController
public class TriggerController {
	private static final Logger LOG = LoggerFactory.getLogger(TriggerController.class);

	public static final StringArg ARG_USER = new StringArg("User", null);

	private static final String PARAM_USER = "user";


	private final TriggerService triggerService;


	@Autowired
	public TriggerController(TriggerService triggerService) {
		this.triggerService = triggerService;
	}


	@Operation(summary = "List available triggers and their arguments")
	@ApiResponses(value = {
			@ApiResponse(responseCode = "200", description = "Triggers and their arguments"),
			@ApiResponse(responseCode = "500", description = "Internal server error", content = @Content)
	})
	@MessageType("Triggers")
	@GetMapping("/trigger")
	public Map>> listTriggers() {
		Map>> triggers = new LinkedHashMap<>();
		for (String name: triggerService.listTriggerNames())
			triggers.put(name, triggerService.getTriggerable(name).getArgs());
		return triggers;
	}


	@Operation(summary = "Trigger a task by name")
	@ApiResponses(value = {
			@ApiResponse(responseCode = "200", description = "Triggers and their arguments"),
			@ApiResponse(responseCode = "400", description = "Bad request", content = @Content),
			@ApiResponse(responseCode = "500", description = "Internal server error", content = @Content)
	})
	@MessageType("Trigger/?")
	@PostMapping("/trigger/{name}")
	public String trigger(@PathVariable String name, @RequestParam Map parameters, HttpServletRequest request) {
		Map args;
		try {
			setMessageType("Trigger/" + name);
			args = parseArgs(triggerService.getTriggerable(name).getArgs(), parameters, request);
		}
		catch (Exception e) {
			LOG.error("Failed parsing trigger arguments", e);
			throw new HttpBadRequestException("Invalid parameters: %s", e.getMessage());
		}

		try {
			ResultState state = triggerService.trigger(name, args);
			return state.getMessage();
		}
		catch (Exception e) {
			LOG.error("TASK {} FAILED", name, e);
			throw new HttpInternalServerErrorException("Task failed: %s", e.getMessage());
		}
	}


	@Operation(summary = "Trigger a task by name asyncronously")
	@ApiResponses(value = {
			@ApiResponse(responseCode = "200", description = "Triggers and their arguments"),
			@ApiResponse(responseCode = "400", description = "Bad request", content = @Content),
			@ApiResponse(responseCode = "500", description = "Internal server error", content = @Content)
	})
	@MessageType("Trigger/?")
	@PostMapping("/trigger/{name}/async")
	public Long triggerAsync(@PathVariable String name, @RequestParam Map parameters, HttpServletRequest request) {
		Map args;
		try {
			setMessageType("Trigger/" + name);
			args = parseArgs(triggerService.getTriggerable(name).getArgs(), parameters, request);
		}
		catch (Exception e) {
			LOG.error("Failed parsing trigger arguments", e);
			throw new HttpBadRequestException("Invalid parameters: %s", e.getMessage());
		}

		try {
			Optional taskStatusId = triggerService.triggerAsync(name, args);
			taskStatusId.ifPresent(id -> addMetadata("TaskStatusId", id));
			return taskStatusId.orElse(0L);
		}
		catch (Exception e) {
			LOG.error("TASK {} FAILED", name, e);
			throw new HttpInternalServerErrorException("Task failed: %s", e.getMessage());
		}
	}

	public static Map parseArgs(List> args, Map parameters, HttpServletRequest request) throws IOException {
		Map params = new LinkedHashMap<>();
		parameters.forEach((k, v) -> {
			if (k != null && v != null) params.put(k, GzipUtils.zip(v, StandardCharsets.UTF_8));
		});

		params.putAll(parseMultipartValues(request));

		Map results = Arg.parseZipValues(args, params);
		if (ARG_USER.get(results) == null && params.containsKey(PARAM_USER))
			results.put(ARG_USER.getName(), GzipUtils.unzip(params.get(PARAM_USER), StandardCharsets.UTF_8));
		return results;
	}


	private static Map parseMultipartValues(HttpServletRequest request) throws IOException {
		if (request.getContentType() == null || !request.getContentType().matches("(?i)multipart/form-data\\s*;.*"))
			return Collections.emptyMap();

		Map values = new LinkedHashMap<>();

		MultipartStream stream = new MultipartStream(request.getContentType(), request.getInputStream());
		while (stream.hasNext()) {
			Part part = stream.next();
			String key = part.getName();

			ByteArrayOutputStream bout = new ByteArrayOutputStream();
			CountingOutputStream counter = new CountingOutputStream(new GZIPOutputStream(bout));
			try (OutputStream out = counter; InputStream in = part.getInputStream()) {
				IOUtils.copy(in, out);
			}

			Optional filename = Optional.ofNullable(part.getHeaderAttribute("Content-Disposition", "filename")).filter(n -> !n.isEmpty());
			if (filename.isPresent() || counter.getByteCount() > 0)
				values.put(key, bout.toByteArray());

			filename.ifPresent(n -> values.put(key + ":filename", GzipUtils.zip(n, StandardCharsets.UTF_8)));
		}
		return values;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy