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

io.datarouter.gcp.gcs.web.GcsBucketHandler Maven / Gradle / Ivy

The newest version!
/*
 * Copyright © 2009 HotPads ([email protected])
 *
 * 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.
 */
package io.datarouter.gcp.gcs.web;

import static j2html.TagCreator.a;
import static j2html.TagCreator.div;
import static j2html.TagCreator.h4;
import static j2html.TagCreator.rawHtml;
import static j2html.TagCreator.td;

import java.time.Instant;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.http.client.utils.URIBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.datarouter.gcp.gcs.DatarouterGcsClient;
import io.datarouter.gcp.gcs.client.GcsClientManager;
import io.datarouter.gcp.gcs.config.DatarouterGcpGcsPaths;
import io.datarouter.scanner.Scanner;
import io.datarouter.storage.client.ClientId;
import io.datarouter.storage.client.DatarouterClients;
import io.datarouter.storage.node.op.raw.read.DirectoryDto;
import io.datarouter.util.number.NumberFormatter;
import io.datarouter.util.string.StringTool;
import io.datarouter.web.handler.BaseHandler;
import io.datarouter.web.handler.mav.Mav;
import io.datarouter.web.handler.types.Param;
import io.datarouter.web.html.form.HtmlForm;
import io.datarouter.web.html.form.HtmlForm.HtmlFormMethod;
import io.datarouter.web.html.j2html.J2HtmlTable;
import io.datarouter.web.html.j2html.bootstrap4.Bootstrap4FormHtml;
import io.datarouter.web.html.j2html.bootstrap4.Bootstrap4PageFactory;
import io.datarouter.web.requirejs.DatarouterWebRequireJsV2;
import j2html.tags.specialized.ATag;
import j2html.tags.specialized.TableTag;
import jakarta.inject.Inject;

public class GcsBucketHandler extends BaseHandler{
	private static final Logger logger = LoggerFactory.getLogger(GcsBucketHandler.class);

	public static final String P_client = "client";
	public static final String P_bucket = "bucket";
	public static final String P_prefix = "prefix";
	private static final String P_after = "after";
	private static final String P_offset = "offset";
	private static final String P_limit = "limit";
	public static final String P_currentDirectory = "currentDirectory";
	public static final String P_delimiter = "delimiter";

	@Inject
	private Bootstrap4PageFactory pageFactory;
	@Inject
	private DatarouterClients clients;
	@Inject
	private GcsClientManager gcsClientManager;
	@Inject
	private DatarouterGcpGcsPaths paths;

	@Handler(defaultHandler = true)
	public Mav index(
			@Param(P_client) String client,
			@Param(P_bucket) String bucket,
			@Param(P_prefix) Optional prefix,
			@Param(P_after) Optional after,
			@Param(P_offset) Optional offset,
			@Param(P_limit) Optional limit,
			@Param(P_currentDirectory) Optional currentDirectory,
			@Param(P_delimiter) Optional delimiter){

		var form = new HtmlForm(HtmlFormMethod.GET);
		form.addTextField()
				.withLabel("Client")
				.withName(P_client)
				.withPlaceholder("theClientName")
				.withValue(client);
		form.addTextField()
				.withLabel("Bucket")
				.withName(P_bucket)
				.withPlaceholder("the.bucket.name")
				.withValue(bucket);
		form.addTextField()
				.withLabel("Prefix")
				.withName(P_prefix)
				.withValue(prefix.orElse(""));
		form.addTextField()
				.withLabel("After")
				.withName(P_after)
				.withValue(after.orElse(""));
		form.addNumberField()
				.withLabel("Offset")
				.withName(P_offset)
				.withValue(offset.orElse(0));
		form.addNumberField()
				.withLabel("Limit")
				.withName(P_limit)
				.withValue(limit.orElse(100));
		form.addTextField()
				.withLabel("Delimiter")
				.withName(P_delimiter)
				.withValue(delimiter.orElse(""));
		form.addCheckboxField()
				.withLabel("currentDirectory")
				.withName(P_currentDirectory)
				.withChecked(currentDirectory.orElse(false));
		form.addButton()
				.withLabel("Submit")
				.withValue("");
		var htmlForm = Bootstrap4FormHtml.render(form)
				.withClass("card card-body bg-light");

		ClientId clientId = clients.getClientId(client);
		DatarouterGcsClient gcsClient = gcsClientManager.getClient(clientId);
		TableTag table = null;
		List objects = gcsClient.scanSubdirectories(
				bucket,
				prefix.orElse(null),
				after.orElse(null),
				params.optionalNotEmpty(P_delimiter).orElse(null),
				limit.orElse(100),
				currentDirectory.orElse(false))
				.list();
		int sizePadding = sizePadding(objects);
		table = new J2HtmlTable()
				.withClasses("sortable table table-sm table-striped my-4 border")
				.withHtmlColumn("Key", object -> {
					String name = object.name();
					if(object.isDirectory()){
						return td(makePrefixLink(client, bucket, name, "/"));
					}
					return td(name);
				})
				.withHtmlColumn("Directory", object -> {
					boolean isDirectory = object.isDirectory();
					if(isDirectory){
						String href = new URIBuilder()
								.setPath(request.getContextPath() + paths.datarouter.clients.gcpGcs.countObjects
										.toSlashedString())
								.addParameter(P_client, client)
								.addParameter(P_bucket, bucket)
								.addParameter(P_prefix, object.name())
								.toString();
						return td(a("true, view count").withHref(href));
					}
					return td(String.valueOf(isDirectory));
				})
				.withHtmlColumn("Size", object -> {
					String commas = NumberFormatter.addCommas(object.size());
					String padded = StringTool.pad(commas, ' ', sizePadding);
					String escaped = padded.replace(" ", " ");
					return td(rawHtml(escaped));
				})
				.withColumn("Last Modified", DirectoryDto::lastModified, Instant::toString)
				.withColumn("Storage Class", DirectoryDto::storageClass)
				.build(objects)
				.withStyle("font-family:monospace; font-size:.9em;");
		var content = div(
				htmlForm,
				h4(bucket),
				table)
				.withClass("container-fluid my-4");
		return pageFactory.startBuilder(request)
				.withTitle("Gcs Bucket")
				.withRequires(DatarouterWebRequireJsV2.SORTTABLE)
				.withContent(content)
				.buildMav();
	}

	@Handler
	public Mav countObjects(
			@Param(P_client) String client,
			@Param(P_bucket) String bucket,
			@Param(P_prefix) Optional prefix){
		ClientId clientId = clients.getClientId(client);
		DatarouterGcsClient gcsClient = gcsClientManager.getClient(clientId);
		var count = new AtomicLong();
		var size = new AtomicLong();
		var message = new AtomicReference();
		gcsClient.scanObjectsPaged(
				bucket,
				prefix.orElse(""))
				.concat(Scanner::of)
				.each($ -> count.incrementAndGet())
				.each(obj -> size.addAndGet(obj.getSize()))
				.sample(10_000, true)
				.map(obj -> String.format("client=%s, bucket=%s, prefix=%s, count=%s, size=%s, through=%s",
						client,
						bucket,
						prefix.orElse(null),
						NumberFormatter.addCommas(count.get()),
						NumberFormatter.addCommas(size.get()),
						obj.getName()))
				.each(message::set)
				.forEach(logger::warn);
		return pageFactory.message(request, message.get());
	}

	private ATag makePrefixLink(String client, String bucket, String prefix, String delimiter){
		String href = new URIBuilder()
				.setPath(request.getContextPath() + paths.datarouter.clients.gcpGcs.listObjects
						.toSlashedString())
				.addParameter(P_client, client)
				.addParameter(P_bucket, bucket)
				.addParameter(P_prefix, prefix)
				.addParameter(P_delimiter, delimiter)
				.toString();
		return a(prefix)
				.withHref(href);
	}

	private static int sizePadding(List objects){
		return Scanner.of(objects)
				.map(DirectoryDto::size)
				.map(NumberFormatter::addCommas)
				.map(String::length)
				.findMax(Comparator.naturalOrder())
				.orElse(0);
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy