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

com.teamscale.commons.toml.TeamscaleIntegrationConfiguration Maven / Gradle / Ivy

There is a newer version: 2025.1.0
Show newest version
package com.teamscale.commons.toml;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Optional;

import org.checkerframework.checker.nullness.qual.NonNull;
import org.conqat.engine.index.shared.PublicProjectId;
import org.conqat.lib.commons.collections.Pair;
import org.conqat.lib.commons.uniformpath.UniformPath;
import org.conqat.lib.commons.uniformpath.UniformPathCompatibilityUtil;

/**
 * Representation of a configuration file or a hierarchy of configuration files.
 * The represented file if formatted according to the format described in
 * architecture-decisions/0016-ide-config-format.md or a newer version of that
 * format.
 *
 * The configuration format describes settings required to connect a local code
 * repository to a Teamscale server (excluding the Teamscale credentials). In
 * particular, it allows to map one code file/folder to a uniform path in a
 * Teamscale project.
 */
public class TeamscaleIntegrationConfiguration {
	/**
	 * The Teamscale-integration-format version number of this configuration. This
	 * is never null. Can be private since this class must provide all features from
	 * any supported version (i.e., consumers of this class need not care about the
	 * version).
	 */
	@NonNull
	private final String fileFormatVersion;
	/**
	 * Whether the parser found a configuration file with root=true (and stopped
	 * searching then).
	 */
	public final boolean isRootConfiguration;

	/**
	 * Configuration settings defining the Teamscale server that we can connect to.
	 */
	public final Server server;
	/**
	 * Configuration settings referring to a Teamscale project on {@link #server}
	 * and mapping one folder on the machine that contains this configuration to a
	 * uniform path on the server.
	 */
	public final Project project;

	/* package */ TeamscaleIntegrationConfiguration(@NonNull String fileFormatVersion, boolean isRootConfiguration,
			Server server, Project project) {
		this.fileFormatVersion = fileFormatVersion;
		this.isRootConfiguration = isRootConfiguration;
		this.server = server;
		this.project = project;
	}

	/**
	 * Validates whether this configuration contains the minimum required config
	 * items. Throws an Exception if validation fails.
	 */
	/* package */void validate(File initialFolder) throws IOException {
		if (server == null || server.url == null) {
			throw new IOException("Aggregated Teamscale Integration toml configuration for folder " + initialFolder
					+ " is missing a server section and/or server.url.");
		}
		if (project == null || project.id == null) {
			throw new IOException("Aggregated Teamscale Integration toml configuration for folder " + initialFolder
					+ " is missing a project section and/or project.id.");
		}
	}

	/**
	 * Clones this config and, if some config items have not been set, imports these
	 * items from the given other configuration.
	 *
	 * This method is used for aggregating config files from a folder hierarchy.
	 */
	/* package */ TeamscaleIntegrationConfiguration cloneAndImportMissingConfigurationItemsFrom(
			TeamscaleIntegrationConfiguration otherConfigFile) {
		boolean newIsRoot = isRootConfiguration;
		if (!newIsRoot) {
			newIsRoot = otherConfigFile.isRootConfiguration;
		}
		Server newServer;
		if (server == null) {
			newServer = otherConfigFile.server;
		} else {
			newServer = server.cloneAndImportMissingConfigurationItemsFrom(otherConfigFile.server);
		}
		Project newProject;
		if (project == null) {
			newProject = otherConfigFile.project;
		} else {
			newProject = project.cloneAndImportMissingConfigurationItemsFrom(otherConfigFile.project);
		}
		return new TeamscaleIntegrationConfiguration(fileFormatVersion, newIsRoot, newServer, newProject);
	}

	/**
	 * Configuration settings defining the Teamscale server that we can connect to.
	 */
	public static class Server {
		/**
		 * URL of the configured Teamscale server. This is never null in a valid
		 * configuration.
		 */
		private final String url;

		/* package */ Server(String url) {
			this.url = url;
		}

		/**
		 * Returns the URL of the configured Teamscale server. This is never null in a
		 * valid configuration.
		 */
		public @NonNull URL getUrl() throws MalformedURLException {
			return new URL(url);
		}

		/**
		 * Clones this config and, if some config items have not been set, imports these
		 * items from the given other configuration.
		 *
		 * This method is used for aggregating config files from a folder hierarchy.
		 */
		private Server cloneAndImportMissingConfigurationItemsFrom(Server otherServer) {
			if (otherServer == null) {
				return this;
			}
			if (url == null) {
				return new Server(otherServer.url);
			}
			return this;
		}
	}

	/**
	 * Configuration settings referring to a Teamscale project on {@link #server}
	 * and mapping one folder on the machine that contains this configuration to a
	 * uniform path on the server.
	 */
	public static class Project {
		/**
		 * Public project id of the configured project on {@link #server}. This is never
		 * null in a valid configuration.
		 */
		private final String id;

		/**
		 * The (hard-coded) branch to use for retrieving data from the server.
		 *
		 * If empty (or null), use auto-discovery of branch from VCS, fallback to
		 * Teamscale default branch. If set and non-empty, turns off auto-discovery of
		 * branch from VCS and always uses the specified branch.
		 */
		public final String branch;

		/**
		 * A Pair of a uniform path within the Teamscale project on the server and the
		 * file that defined this path. If null, no configuration file defined a value
		 * for the server.path property. In this case, the Teamscale integrations should
		 * use the Teamscale-project root as server path.
		 *
		 * For example, if (Windows) file C:\x\.teamscale.toml contains
		 * "project.path=a", that means (for example) file C:\x\y\z\foo.java is mapped
		 * to uniform path a/y/z/foo.java in the Teamscale project.
		 */
		private final Pair mappedSourcePath;

		/* package */ Project(String id, String branch, Pair mappedSourcePath) {
			this.id = id;
			this.branch = branch;
			this.mappedSourcePath = mappedSourcePath;
		}

		/**
		 *
		 * Public project id of the configured project on {@link #server}. This is never
		 * null in a valid configuration.
		 */
		public PublicProjectId getIdAsPublicProjectId() {
			return new PublicProjectId(id);
		}

		/**
		 * A Pair of a uniform path within the Teamscale project on the server and the
		 * file that defined this path. If {@link Optional#empty()}, uses the Teamscale
		 * project root.
		 *
		 * For example, if (Windows) file C:\x\.teamscale.toml contains
		 * "project.path=a", that means (for example) file C:\x\y\z\foo.java is mapped
		 * to uniform path a/z/foo.java in the Teamscale project.
		 */
		public Optional> getMappedSourcePathAsUniformPath() {
			if (mappedSourcePath == null) {
				return Optional.empty();
			}
			return Optional.of(Pair.createPair(mappedSourcePath.getFirst(),
					UniformPathCompatibilityUtil.convert(mappedSourcePath.getSecond())));
		}

		/**
		 * Clones this config and, if some config items have not been set, imports these
		 * items from the given other configuration.
		 *
		 * This method is used for aggregating config files from a folder hierarchy.
		 */
		private Project cloneAndImportMissingConfigurationItemsFrom(Project otherProject) {
			if (otherProject == null) {
				return this;
			}
			String newId = id;
			if (newId == null) {
				newId = otherProject.id;
			}
			String newBranch = branch;
			if (newBranch == null) {
				newBranch = otherProject.branch;
			}
			Pair newPath = mappedSourcePath;
			if (newPath == null) {
				newPath = otherProject.mappedSourcePath;
			}
			return new Project(newId, newBranch, newPath);
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy