com.khartec.waltz.web.WebUtilities.java Source code

Java tutorial

Introduction

Here is the source code for com.khartec.waltz.web.WebUtilities.java

Source

/*
 *  This file is part of Waltz.
 *
 *     Waltz 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, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     Waltz 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 Waltz.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.khartec.waltz.web;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JSR310Module;
import com.khartec.waltz.common.EnumUtilities;
import com.khartec.waltz.common.StringUtilities;
import com.khartec.waltz.model.*;
import com.khartec.waltz.model.user.Role;
import com.khartec.waltz.service.user.UserRoleService;
import com.khartec.waltz.web.endpoints.auth.AuthenticationUtilities;
import org.eclipse.jetty.http.MimeTypes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import spark.Request;
import spark.Response;
import spark.ResponseTransformer;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;

import static com.khartec.waltz.common.Checks.checkAll;
import static com.khartec.waltz.common.Checks.checkNotNull;
import static com.khartec.waltz.common.ObjectUtilities.firstNotNull;

public class WebUtilities {

    private static final Logger LOG = LoggerFactory.getLogger(WebUtilities.class);

    public static final String TYPE_JSON = "application/json";

    private static final MimeTypes mimeTypes = new MimeTypes();
    private static final ObjectMapper mapper;

    static {
        mapper = new ObjectMapper();
        mapper.registerModule(new JSR310Module()); // DateTime etc
        mapper.registerModule(new Jdk8Module()); // Optional etc

        // Force timestamps to be sent as ISO-8601 formatted strings
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

        mimeTypes.addMimeMapping("ttf", "application/x-font-ttf");
    }

    public static final ResponseTransformer transformer = mapper::writeValueAsString;

    /**
     * Given a vararg/array of path segments will join them
     * to make a string representing the path.  No starting or trailing
     * slashes are added to the resultant path string.
     *
     * @param segs Segments to join
     * @return String representing the path produced by joining the segments
     * @throws IllegalArgumentException If any of the segments are null
     */
    public static String mkPath(String... segs) {
        checkAll(segs, x -> StringUtilities.notEmpty(x), "Cannot convert empty segments to path");

        return String.join("/", segs);
    }

    /**
     * Will render the given object to the response object, or else report failure
     * to the given logger.
     * @param res  spark response object
     * @param obj  object to render
     * @param logger   logger to use if failure report required
     */
    public static void attemptRender(Response res, Object obj, Logger logger) {
        checkNotNull(res, "res must not be null");
        checkNotNull(obj, "obj must not be null");
        checkNotNull(logger, "logger must not be null");

        try {
            String msg = transformer.render(obj);
            res.body(msg);
        } catch (Exception e) {
            logger.warn("Failed to render {} because: {}", obj, e.getMessage());
        }
    }

    public static long getLong(Request request, String paramName) {
        checkNotNull(request, "request must not be null");
        checkNotNull(paramName, "paramName must not be null");

        return Long.parseLong(request.params(paramName));
    }

    public static long getId(Request request) {
        return getLong(request, "id");
    }

    public static void requireRole(UserRoleService userRoleService, Request request, Role... requiredRoles) {
        String user = getUsername(request);
        if (StringUtilities.isEmpty(user)) {
            LOG.warn("Required role check failed as no user, roles needed: " + Arrays.toString(requiredRoles));
            throw new IllegalArgumentException("Not logged in");
        }
        if (!userRoleService.hasRole(user, requiredRoles)) {
            LOG.warn("Required role check failed as user: " + user + ", did not have required roles: "
                    + Arrays.toString(requiredRoles));
            throw new NotAuthorizedException();
        }
    }

    public static String getUsername(Request request) {
        return AuthenticationUtilities.getUsername(request);
    }

    /**
     * Expect parameter to be called: <code>kind</code>
     * @param request
     * @return
     */
    public static EntityKind getKind(Request request) {
        checkNotNull(request, "request must not be null");

        return EntityKind.valueOf(request.params("kind"));
    }

    /**
     * Expects parameters :kind and :id
     * @param request
     * @return
     */
    public static EntityReference getEntityReference(Request request) {
        checkNotNull(request, "request must not be null");
        return ImmutableEntityReference.builder().kind(getKind(request)).id(getId(request)).build();
    }

    /**
     * Reads the body of the request and attempts to convert it into an instance of
     * the given class.
     *
     * @param request
     * @param objClass
     * @param <T>
     * @return
     * @throws IOException
     */
    public static <T> T readBody(Request request, Class<T> objClass) throws IOException {
        return mapper.readValue(request.bodyAsBytes(), objClass);
    }

    public static List<Long> readIdsFromBody(Request req) throws IOException {
        return readBody(req, List.class);
    }

    public static List<String> readStringsFromBody(Request req) throws IOException {
        return readBody(req, List.class);
    }

    public static IdSelectionOptions readIdSelectionOptionsFromBody(Request request) throws java.io.IOException {
        return readBody(request, IdSelectionOptions.class);
    }

    public static EntityIdSelectionOptions readEntityIdOptionsFromBody(Request request) throws java.io.IOException {
        return readBody(request, EntityIdSelectionOptions.class);
    }

    /**
     * Reads the body of the request and attempts to convert it into an instance of
     * the given class. If the attempt fails then return the given default object
     * instead of throwing an exception.
     *
     * @param request
     * @param objClass
     * @param dflt
     * @param <T>
     * @return
     */
    public static <T> T readBody(Request request, Class<T> objClass, T dflt) {
        try {
            return readBody(request, objClass);
        } catch (IOException ioe) {
            String msg = "Failed to ready body of request into an object of type: " + objClass.getName()
                    + ", returning default object";
            LOG.warn(msg, ioe);
            return dflt;
        }
    }

    public static <T extends Enum<T>> T readEnum(Request request, String paramName, Class<T> enumClass,
            Function<String, T> failedParseSupplier) {
        return EnumUtilities.readEnum(request.params(paramName), enumClass, failedParseSupplier);
    }

    public static void reportException(int statusCode, Optional<String> maybeErrorCode, String message,
            Response res, Logger log) {
        res.status(statusCode);
        ImmutableWebError webError = ImmutableWebError.builder().message(message).id(maybeErrorCode).build();
        attemptRender(res, webError, log);

    }

    public static void reportException(int statusCode, String errorCode, String message, Response res, Logger log) {
        reportException(statusCode, Optional.ofNullable(errorCode), message, res, log);
    }

    /**
     * Given a path will return the mime type to go with it.
     * Does this by looking at the extension of the final (non-anchor)
     * segment of the given path.  <br>
     *
     * To add new mimetypes modify the static initialiser of this class.
     *
     * @param path
     * @return
     */
    public static String getMimeType(String path) {
        return firstNotNull(mimeTypes.getMimeByExtension(path), "application/octet-stream");
    }

    public static Optional<Integer> getLimit(Request request) {
        String limitVal = request.queryParams("limit");
        return Optional.ofNullable(limitVal).map(s -> Integer.valueOf(s));
    }
}