Skip to content

Commit

Permalink
#26002 Initial commit for handling the sync process of languages
Browse files Browse the repository at this point in the history
  • Loading branch information
jgambarios committed Sep 20, 2023
1 parent d21e4ac commit f7eb10d
Show file tree
Hide file tree
Showing 20 changed files with 1,400 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.dotcms.model.push;

import com.dotcms.model.annotation.ValueType;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import java.io.File;
import java.util.Optional;
import org.immutables.value.Value;

@ValueType
@Value.Immutable
@JsonDeserialize(as = PushAnalysisResult.class)
@JsonIgnoreProperties(ignoreUnknown = true)
public interface AbstractPushAnalysisResult<T> {

PushAction action();

Optional<T> serverContent();

Optional<File> localFile();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.dotcms.model.push;

import com.dotcms.model.annotation.ValueType;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import org.immutables.value.Value;

@ValueType
@Value.Immutable
@JsonDeserialize(as = PushOptions.class)
@JsonIgnoreProperties(ignoreUnknown = true)
public interface AbstractPushOptions {

boolean allowRemove();

boolean failFast();

boolean dryRun();

int maxRetryAttempts();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.dotcms.model.push;

public enum PushAction {
ADD, UPDATE, REMOVE, NO_ACTION
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.dotcms.api.client.push;

import java.util.List;
import java.util.Optional;

/**
* Interface for comparing content of type T.
*
* @param <T> the type of content to be compared
*/
public interface ContentComparator<T> {


/**
* Retrieves the type parameter of the class.
*
* @return the type parameter of the class
*/
Class<T> type();

/**
* Finds matching server content based on local content and a list of server contents.
*
* @param localContent the local content to compare against server contents
* @param serverContents the list of server contents to search for matches
* @return an Optional containing the matching server content if found, otherwise an empty
* Optional.
*/
Optional<T> findMatchingServerContent(T localContent, List<T> serverContents);

/**
* Checks if the given server content is contained within the list of local contents.
*
* @param serverContent the server content to check for containment
* @param localContents the list of local contents to search for containment
* @return an Optional containing the matching local content if found, or an empty Optional if
* not found.
*/
Optional<T> localContains(T serverContent, List<T> localContents);

/**
* Checks if the given local content and server content are equal.
*
* @param localContent the local content to compare
* @param serverContent the server content to compare
* @return true if the local content is equal to the server content, false otherwise
*/
boolean contentEquals(T localContent, T serverContent);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.dotcms.api.client.push;

import java.util.List;

/**
* The ContentFetcher interface provides a contract for classes that can fetch content of type T.
*
* @param <T> The type of content to fetch.
*/
public interface ContentFetcher<T> {

/**
* Fetches a list of elements of type T.
*
* @return The fetched list of elements.
*/
List<T> fetch();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package com.dotcms.api.client.push;

import com.dotcms.api.client.push.exception.PushException;
import com.dotcms.model.push.PushAnalysisResult;
import java.util.Iterator;
import java.util.List;
import javax.enterprise.context.Dependent;

/**
* The {@code FormatStatus} class is responsible for formatting the status of a push operation. It
* provides methods for formatting the results of a push analysis into a user-friendly format.
*
* <p>This class is meant to be used in conjunction with a {@link PushHandler} which provides
* additional functionality for handling the push operation.
*
* @see PushAnalysisResult
* @see PushHandler
*/
@Dependent
public class FormatStatus {

private final String COLOR_NEW = "green";
private final String COLOR_MODIFIED = "cyan";
private final String COLOR_DELETED = "red";

private final String REGULAR_FORMAT = "%s";
private final String PUSH_NEW_FORMAT = "@|bold," + COLOR_NEW + " %s \u2795|@";
private final String PUSH_MODIFIED_FORMAT = "@|bold," + COLOR_MODIFIED + " %s \u270E|@";
private final String PUSH_DELETE_FORMAT = "@|bold," + COLOR_DELETED + " %s \u2716|@";

/**
* Formats the push analysis results using the specified push handler.
*
* @param results the list of push analysis results
* @param pushHandler the push handler to use for formatting
* @return a StringBuilder containing the formatted push analysis results
*/
public <T> StringBuilder format(final List<PushAnalysisResult<T>> results,
PushHandler<T> pushHandler) {

var outputBuilder = new StringBuilder();

outputBuilder.append(String.format(" %s:", pushHandler.title())).append("\n");

Iterator<PushAnalysisResult<T>> iterator = results.iterator();
while (iterator.hasNext()) {
PushAnalysisResult<T> result = iterator.next();
boolean isLast = !iterator.hasNext();
outputBuilder.append(formatResult(" ", result, pushHandler, isLast));
}

return outputBuilder;
}

/**
* Formats a single push analysis result using the specified prefix, result, push handler, and
* lastElement indicator.
*
* @param prefix the prefix to use for indentation
* @param result the push analysis result to format
* @param pushHandler the push handler to use for formatting
* @param lastElement indicates whether the result is the last element in the list
* @param <T> z the type of the push analysis result
* @return a StringBuilder containing the formatted push analysis result
*/
private <T> StringBuilder formatResult(final String prefix,
final PushAnalysisResult<T> result, final PushHandler<T> pushHandler,
final boolean lastElement) {

var outputBuilder = new StringBuilder();

String contentFormat;
String contentName;
switch (result.action()) {
case ADD:
contentFormat = PUSH_NEW_FORMAT;
contentName = result.localFile().get().getName();
break;
case UPDATE:
contentFormat = PUSH_MODIFIED_FORMAT;
contentName = result.localFile().get().getName();
break;
case REMOVE:
contentFormat = PUSH_DELETE_FORMAT;
contentName = pushHandler.contentSimpleDisplay(result.serverContent().get());
break;
case NO_ACTION:
contentFormat = REGULAR_FORMAT;
contentName = result.localFile().get().getName();
break;
default:
throw new PushException("Unknown action: " + result.action());
}

outputBuilder.append(prefix).
append(lastElement ? "└── " : "├── ").
append(String.format(contentFormat, contentName)).
append("\n");

return outputBuilder;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.dotcms.api.client.push;

import com.dotcms.api.client.push.exception.MappingException;
import java.io.File;

/**
* MapperService is an interface that provides a method to map a File to an object of a given
* class.
*/
public interface MapperService {

/**
* Maps the contents of a file to an instance of the given class.
*
* @param file the file to read and map
* @param clazz the class to map the file contents to
* @return the mapped instance of the given class
* @throws MappingException if there is an error during the mapping process
*/
<T> T map(final File file, Class<T> clazz) throws MappingException;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.dotcms.api.client.push;

import com.dotcms.api.client.push.exception.MappingException;
import com.dotcms.api.provider.ClientObjectMapper;
import com.dotcms.api.provider.YAMLMapperSupplier;
import com.dotcms.cli.common.InputOutputFormat;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.quarkus.arc.DefaultBean;
import java.io.File;
import java.io.IOException;
import javax.enterprise.context.Dependent;
import javax.enterprise.context.control.ActivateRequestContext;
import javax.inject.Inject;
import org.jboss.logging.Logger;

/**
* The {@code MapperServiceImpl} class implements the {@code MapperService} interface and provides
* methods for mapping data from a file to an object using different formats.
* <p>
* This class is annotated with {@code @DefaultBean} and {@code @Dependent} to indicate that it is
* the default implementation of the {@code MapperService} interface and that it belongs to the
* dependent scope.
*/
@DefaultBean
@Dependent
public class MapperServiceImpl implements MapperService {

@Inject
Logger logger;

/**
* Maps the given file to an object of the specified class.
*
* @param file The file to be mapped.
* @param clazz The class of the object to be mapped to.
* @return The mapped object.
* @throws MappingException If there is an error mapping the file.
*/
@ActivateRequestContext
public <T> T map(final File file, Class<T> clazz) throws MappingException {

if (null == file) {
var message = String.format("Trying to map empty file for type [%s]", clazz.getName());
logger.error(message);
throw new MappingException(message);
}

try {
ObjectMapper objectMapper;
InputOutputFormat inputOutputFormat;

if (isJSONFile(file)) {
inputOutputFormat = InputOutputFormat.JSON;
} else {
inputOutputFormat = InputOutputFormat.YML;
}

if (inputOutputFormat == InputOutputFormat.JSON) {
objectMapper = new ClientObjectMapper().getContext(null);
} else {
objectMapper = new YAMLMapperSupplier().get();
}

return objectMapper.readValue(file, clazz);
} catch (IOException e) {

var message = String.format("Error mapping file [%s] for type [%s]",
file.getAbsolutePath(), clazz.getName());
logger.error(message, e);

throw new MappingException(message, e);
}
}

/**
* Checks if the given file is a JSON file.
*
* @param file The file to be checked.
* @return True if the file is a JSON file, false otherwise.
*/
private boolean isJSONFile(final File file) {
return file.getName().toLowerCase().endsWith(".json");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.dotcms.api.client.push;

import com.dotcms.model.push.PushAnalysisResult;
import java.io.File;
import java.util.List;

/**
* Service interface for performing push analysis on a local file or folder.
*/
public interface PushAnalysisService {

/**
* Analyzes a local file or folder and generates a list of push analysis results.
*
* @param localFileOrFolder the local file or folder to analyze
* @param provider the content fetcher used to retrieve content
* @param comparator the content comparator used to compare content
* @return a list of push analysis results
*/
<T> List<PushAnalysisResult<T>> analyze(File localFileOrFolder,
ContentFetcher<T> provider,
ContentComparator<T> comparator);

}

Loading

0 comments on commit f7eb10d

Please sign in to comment.