release version 1.10.0
This commit is contained in:
@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>ru.entaxy.esb.platform.runtime.core</groupId>
|
||||
<artifactId>object-producing</artifactId>
|
||||
<version>1.9.0</version>
|
||||
<version>1.10.0</version>
|
||||
</parent>
|
||||
<groupId>ru.entaxy.esb.platform.runtime.core.object-producing</groupId>
|
||||
<artifactId>object-producer-api</artifactId>
|
||||
|
@ -2,7 +2,7 @@
|
||||
* ~~~~~~licensing~~~~~~
|
||||
* object-producer-api
|
||||
* ==========
|
||||
* Copyright (C) 2020 - 2023 EmDev LLC
|
||||
* Copyright (C) 2020 - 2024 EmDev LLC
|
||||
* ==========
|
||||
* You may not use this file except in accordance with the License Terms of the Copyright
|
||||
* Holder located at: https://entaxy.ru/eula . All copyrights, all intellectual property
|
||||
|
@ -2,7 +2,7 @@
|
||||
* ~~~~~~licensing~~~~~~
|
||||
* test-producers
|
||||
* ==========
|
||||
* Copyright (C) 2020 - 2023 EmDev LLC
|
||||
* Copyright (C) 2020 - 2024 EmDev LLC
|
||||
* ==========
|
||||
* You may not use this file except in accordance with the License Terms of the Copyright
|
||||
* Holder located at: https://entaxy.ru/eula . All copyrights, all intellectual property
|
||||
|
@ -2,7 +2,7 @@
|
||||
* ~~~~~~licensing~~~~~~
|
||||
* test-producers
|
||||
* ==========
|
||||
* Copyright (C) 2020 - 2023 EmDev LLC
|
||||
* Copyright (C) 2020 - 2024 EmDev LLC
|
||||
* ==========
|
||||
* You may not use this file except in accordance with the License Terms of the Copyright
|
||||
* Holder located at: https://entaxy.ru/eula . All copyrights, all intellectual property
|
||||
@ -25,67 +25,114 @@
|
||||
*/
|
||||
package ru.entaxy.platform.core.producer.api;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.osgi.framework.Filter;
|
||||
import org.osgi.framework.FrameworkUtil;
|
||||
import org.osgi.framework.InvalidSyntaxException;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import ru.entaxy.platform.base.objects.factory.EntaxyFactory;
|
||||
import ru.entaxy.platform.base.support.CommonUtils;
|
||||
|
||||
public interface EntaxyProducerService {
|
||||
|
||||
public static interface DIRECTIVES {
|
||||
|
||||
String LIFECYCLE = "@LIFECYCLE";
|
||||
|
||||
/*
|
||||
* usage: {"@SKIP": ['command1', 'command2-', 'command3+']}
|
||||
* where
|
||||
* 'command1' - skip command1
|
||||
* 'command2-' - skip ALL BEFORE command2
|
||||
* 'command3+' - skip ALL AFTER command3
|
||||
*/
|
||||
String SKIP = "@SKIP";
|
||||
public static interface DIRECTIVES {
|
||||
|
||||
/*
|
||||
* usage: {"@CONTINUE_ON_EXCEPTION": ['command1', 'command2-', 'command3+']}
|
||||
* where
|
||||
* 'command1' - continue after exception in command1
|
||||
* 'command2-' - continue after exception in ANY command BEFORE command2
|
||||
* 'command3+' - continue after exception in ANY command AFTER command3
|
||||
*/
|
||||
String CONTINUE_ON_EXCEPTION = "@CONTINUE_ON_EXCEPTION";
|
||||
|
||||
};
|
||||
|
||||
public static interface INSTRUCTIONS {
|
||||
|
||||
String PRINT_OUTPUT = "printOutput";
|
||||
String SKIP = "skip";
|
||||
String CONTINUE_ON_EXCEPTION = "continueOnException";
|
||||
String MAX_COUNT = "maxCount";
|
||||
String CURRENT_ITERATION = "currentCount";
|
||||
|
||||
public static interface ARTIFACT {
|
||||
String LIFECYCLE = "@LIFECYCLE";
|
||||
|
||||
public static final String VERSION = "artifact.version";
|
||||
public static final String TIMESTAMP = "artifact.timestamp";
|
||||
public static final String VERSION_POLICY = "artifact.version.policy";
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public ProducerResult produce(String configuration);
|
||||
public ProducerResult produce(JsonObject configuration);
|
||||
/*
|
||||
* usage: {"@SKIP": ['command1', 'command2-', 'command3+']}
|
||||
* where
|
||||
* 'command1' - skip command1
|
||||
* 'command2-' - skip ALL BEFORE command2
|
||||
* 'command3+' - skip ALL AFTER command3
|
||||
*/
|
||||
String SKIP = "@SKIP";
|
||||
|
||||
public ProducerResult produce(String configuration, String instructions);
|
||||
public ProducerResult produce(JsonObject configuration, String instructions);
|
||||
|
||||
public List<EntaxyProducer> getAllProducers();
|
||||
public EntaxyProducer getProducerForType(String type);
|
||||
public EntaxyFactory findFactoryById(String factoryId);
|
||||
|
||||
public List<ProducingCommandExecutor> getAvailableCommands();
|
||||
public void registerCommand(ProducingCommandExecutor commandExecutor);
|
||||
public ProducingCommandExecutor getCommandById(String id);
|
||||
/*
|
||||
* usage: {"@CONTINUE_ON_EXCEPTION": ['command1', 'command2-', 'command3+']}
|
||||
* where
|
||||
* 'command1' - continue after exception in command1
|
||||
* 'command2-' - continue after exception in ANY command BEFORE command2
|
||||
* 'command3+' - continue after exception in ANY command AFTER command3
|
||||
*/
|
||||
String CONTINUE_ON_EXCEPTION = "@CONTINUE_ON_EXCEPTION";
|
||||
|
||||
};
|
||||
|
||||
public static interface INSTRUCTIONS {
|
||||
|
||||
String PRINT_OUTPUT = "printOutput";
|
||||
String SKIP = "skip";
|
||||
String CONTINUE_ON_EXCEPTION = "continueOnException";
|
||||
String MAX_COUNT = "maxCount";
|
||||
String CURRENT_ITERATION = "currentCount";
|
||||
|
||||
public static interface ARTIFACT {
|
||||
|
||||
public static final String VERSION = "artifact.version";
|
||||
public static final String TIMESTAMP = "artifact.timestamp";
|
||||
public static final String VERSION_POLICY = "artifact.version.policy";
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public ProducerResult produce(String configuration);
|
||||
|
||||
public ProducerResult produce(JsonObject configuration);
|
||||
|
||||
public ProducerResult produce(String configuration, String instructions);
|
||||
|
||||
public ProducerResult produce(JsonObject configuration, String instructions);
|
||||
|
||||
public List<EntaxyProducer> getAllProducers();
|
||||
|
||||
public EntaxyProducer getProducerForType(String type);
|
||||
|
||||
public EntaxyFactory findFactoryById(String factoryId);
|
||||
|
||||
default public boolean isFactoryOfType(String factoryId, String type) {
|
||||
EntaxyFactory f = findFactoryById(factoryId);
|
||||
while (f != null) {
|
||||
if (f.getType().equals(type))
|
||||
return true;
|
||||
if (!CommonUtils.isValid(f.getParent()))
|
||||
return false;
|
||||
f = findFactoryById(f.getParent());
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
default public List<EntaxyFactory> findFactories(String filter) {
|
||||
try {
|
||||
Filter osgiFilter = FrameworkUtil.createFilter(filter);
|
||||
return findFactories(osgiFilter);
|
||||
} catch (InvalidSyntaxException e) {
|
||||
LoggerFactory.getLogger(EntaxyProducerService.class).error(String.format("Invalid filter [%s]", filter));
|
||||
return new ArrayList<>();
|
||||
}
|
||||
};
|
||||
|
||||
default public List<EntaxyFactory> findFactories(Filter filter) {
|
||||
List<EntaxyFactory> allFactories = new ArrayList<>();
|
||||
for (EntaxyProducer producer : getAllProducers())
|
||||
allFactories.addAll(producer.getAllFactories());
|
||||
return EntaxyProducerUtils.filterFactories(filter, allFactories);
|
||||
};
|
||||
|
||||
public List<ProducingCommandExecutor> getAvailableCommands();
|
||||
|
||||
public void registerCommand(ProducingCommandExecutor commandExecutor);
|
||||
|
||||
public void extendLifecycle(String lifecycle, ProducingCommandExecutor commandExecutor,
|
||||
Map<String, Object> parameters);
|
||||
|
||||
public ProducingCommandExecutor getCommandById(String id);
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
* ~~~~~~licensing~~~~~~
|
||||
* object-producer-api
|
||||
* ==========
|
||||
* Copyright (C) 2020 - 2023 EmDev LLC
|
||||
* Copyright (C) 2020 - 2024 EmDev LLC
|
||||
* ==========
|
||||
* You may not use this file except in accordance with the License Terms of the Copyright
|
||||
* Holder located at: https://entaxy.ru/eula . All copyrights, all intellectual property
|
||||
@ -25,123 +25,203 @@
|
||||
*/
|
||||
package ru.entaxy.platform.core.producer.api;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.osgi.framework.Filter;
|
||||
import org.osgi.framework.FrameworkUtil;
|
||||
import org.osgi.framework.InvalidSyntaxException;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import ru.entaxy.platform.base.objects.EntaxyObjectService;
|
||||
import ru.entaxy.platform.base.objects.factory.EntaxyFactory;
|
||||
import ru.entaxy.platform.base.support.JSONUtils;
|
||||
|
||||
public class EntaxyProducerUtils {
|
||||
|
||||
protected static Gson sharedGson = new Gson();
|
||||
|
||||
//INSTRUCTIONS BUILDER
|
||||
|
||||
protected static class InstructionsItemBuilder {
|
||||
|
||||
protected JsonObject instructionsJson;
|
||||
|
||||
protected InstructionsItemBuilder(JsonObject jsonObject) {
|
||||
this.instructionsJson = jsonObject;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class InstructionsBuilder extends InstructionsItemBuilder {
|
||||
protected static Gson sharedGson = new Gson();
|
||||
|
||||
protected InstructionsBuilder(JsonObject jsonObject) {
|
||||
super(jsonObject);
|
||||
}
|
||||
// INSTRUCTIONS BUILDER
|
||||
|
||||
public InstructionsBuilder lifecycle(String lifecycle) {
|
||||
if (!instructionsJson.has(EntaxyProducerService.DIRECTIVES.LIFECYCLE))
|
||||
instructionsJson.add(EntaxyProducerService.DIRECTIVES.LIFECYCLE, new JsonArray());
|
||||
JsonArray lc = instructionsJson.get(EntaxyProducerService.DIRECTIVES.LIFECYCLE).getAsJsonArray();
|
||||
lc.add(lifecycle);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandBuilder command(String commandName) {
|
||||
if (!instructionsJson.has(commandName))
|
||||
instructionsJson.add(commandName, new JsonObject());
|
||||
if ("*".equals(commandName))
|
||||
return new AnyCommandBuilder(instructionsJson, instructionsJson.get(commandName).getAsJsonObject());
|
||||
else
|
||||
return new CommandBuilder(instructionsJson, instructionsJson.get(commandName).getAsJsonObject());
|
||||
protected static class InstructionsItemBuilder {
|
||||
|
||||
}
|
||||
protected JsonObject instructionsJson;
|
||||
|
||||
public AnyCommandBuilder any() {
|
||||
return (AnyCommandBuilder)this.command("*");
|
||||
}
|
||||
|
||||
public JsonObject getInstructions() {
|
||||
return this.instructionsJson;
|
||||
}
|
||||
|
||||
public String getInstructionsString() {
|
||||
return getInstructions().toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static class CommandBuilder extends InstructionsBuilder {
|
||||
protected InstructionsItemBuilder(JsonObject jsonObject) {
|
||||
this.instructionsJson = jsonObject;
|
||||
}
|
||||
|
||||
protected JsonObject commandJson;
|
||||
|
||||
protected CommandBuilder(JsonObject instructionsObject, JsonObject commandObject) {
|
||||
super(instructionsObject);
|
||||
commandJson = commandObject;
|
||||
}
|
||||
|
||||
public CommandBuilder set(String name, Object value) {
|
||||
this.commandJson.remove(name);
|
||||
this.commandJson.add(name, sharedGson.toJsonTree(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class AnyCommandBuilder extends CommandBuilder {
|
||||
}
|
||||
|
||||
protected AnyCommandBuilder(JsonObject instructionsObject, JsonObject commandObject) {
|
||||
super(instructionsObject, commandObject);
|
||||
}
|
||||
public static class InstructionsBuilder extends InstructionsItemBuilder {
|
||||
|
||||
public AnyCommandBuilder skip(String commandExprssion) {
|
||||
if (!this.commandJson.has(EntaxyProducerService.DIRECTIVES.SKIP))
|
||||
this.commandJson.add(EntaxyProducerService.DIRECTIVES.SKIP, new JsonArray());
|
||||
this.commandJson.get(EntaxyProducerService.DIRECTIVES.SKIP).getAsJsonArray().add(commandExprssion);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static InstructionsBuilder instructions() {
|
||||
return instructions(new JsonObject());
|
||||
}
|
||||
protected InstructionsBuilder(JsonObject jsonObject) {
|
||||
super(jsonObject);
|
||||
}
|
||||
|
||||
public static InstructionsBuilder instructions(JsonObject jsonObject) {
|
||||
return new InstructionsBuilder(jsonObject);
|
||||
}
|
||||
|
||||
//CONFIGURATION BUILDER
|
||||
public InstructionsBuilder lifecycle(String lifecycle) {
|
||||
if (!instructionsJson.has(EntaxyProducerService.DIRECTIVES.LIFECYCLE))
|
||||
instructionsJson.add(EntaxyProducerService.DIRECTIVES.LIFECYCLE, new JsonArray());
|
||||
JsonArray lc = instructionsJson.get(EntaxyProducerService.DIRECTIVES.LIFECYCLE).getAsJsonArray();
|
||||
lc.add(lifecycle);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// UTILS
|
||||
|
||||
public static boolean isPattern(String type) {
|
||||
return type.contains(EntaxyProducer.PATTERN_CHAR_ANY);
|
||||
}
|
||||
public CommandBuilder command(String commandName) {
|
||||
if (!instructionsJson.has(commandName))
|
||||
instructionsJson.add(commandName, new JsonObject());
|
||||
if ("*".equals(commandName))
|
||||
return new AnyCommandBuilder(instructionsJson, instructionsJson.get(commandName).getAsJsonObject());
|
||||
else
|
||||
return new CommandBuilder(instructionsJson, instructionsJson.get(commandName).getAsJsonObject());
|
||||
|
||||
public static String toRegexPattern(String type) {
|
||||
return type.replaceAll("\\.", "\\\\.")
|
||||
.replaceAll("\\" + EntaxyProducer.PATTERN_CHAR_ANY, "\\.*");
|
||||
}
|
||||
|
||||
public static boolean isMatched(String typePattern, String type) {
|
||||
return Pattern.matches(toRegexPattern(typePattern), type);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public AnyCommandBuilder any() {
|
||||
return (AnyCommandBuilder) this.command("*");
|
||||
}
|
||||
|
||||
public JsonObject getInstructions() {
|
||||
return this.instructionsJson;
|
||||
}
|
||||
|
||||
public String getInstructionsString() {
|
||||
return getInstructions().toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static class CommandBuilder extends InstructionsBuilder {
|
||||
|
||||
protected JsonObject commandJson;
|
||||
|
||||
protected CommandBuilder(JsonObject instructionsObject, JsonObject commandObject) {
|
||||
super(instructionsObject);
|
||||
commandJson = commandObject;
|
||||
}
|
||||
|
||||
public CommandBuilder set(String name, Object value) {
|
||||
this.commandJson.remove(name);
|
||||
this.commandJson.add(name, sharedGson.toJsonTree(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class AnyCommandBuilder extends CommandBuilder {
|
||||
|
||||
protected AnyCommandBuilder(JsonObject instructionsObject, JsonObject commandObject) {
|
||||
super(instructionsObject, commandObject);
|
||||
}
|
||||
|
||||
public AnyCommandBuilder skip(String commandExprssion) {
|
||||
if (!this.commandJson.has(EntaxyProducerService.DIRECTIVES.SKIP))
|
||||
this.commandJson.add(EntaxyProducerService.DIRECTIVES.SKIP, new JsonArray());
|
||||
this.commandJson.get(EntaxyProducerService.DIRECTIVES.SKIP).getAsJsonArray().add(commandExprssion);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static InstructionsBuilder instructions() {
|
||||
return instructions(new JsonObject());
|
||||
}
|
||||
|
||||
public static InstructionsBuilder instructions(JsonObject jsonObject) {
|
||||
return new InstructionsBuilder(jsonObject);
|
||||
}
|
||||
|
||||
// CONFIGURATION BUILDER
|
||||
|
||||
|
||||
// UTILS
|
||||
|
||||
public static boolean isPattern(String type) {
|
||||
return type.contains(EntaxyProducer.PATTERN_CHAR_ANY);
|
||||
}
|
||||
|
||||
public static String toRegexPattern(String type) {
|
||||
return type.replaceAll("\\.", "\\\\.")
|
||||
.replaceAll("\\" + EntaxyProducer.PATTERN_CHAR_ANY, "\\.*");
|
||||
}
|
||||
|
||||
public static boolean isMatched(String typePattern, String type) {
|
||||
return Pattern.matches(toRegexPattern(typePattern), type);
|
||||
}
|
||||
|
||||
public static Map<String, Object> getFactoryEssence(EntaxyFactory factory) {
|
||||
return getFactoryEssence(factory, "");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Map<String, Object> getFactoryEssence(EntaxyFactory factory, String prefix) {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
JsonObject config = JSONUtils.getJsonRootObject(factory.getJsonConfiguration());
|
||||
|
||||
if (config == null)
|
||||
return result;
|
||||
|
||||
if (!config.has(EntaxyFactory.CONFIGURATION.FACTORY_SECTION_NAME))
|
||||
return result;
|
||||
|
||||
Object obj = JSONUtils.element2object(config.get(EntaxyFactory.CONFIGURATION.FACTORY_SECTION_NAME));
|
||||
|
||||
if (!(obj instanceof Map))
|
||||
return result;
|
||||
|
||||
result.putAll((Map<String, Object>) ((Map<String, Object>) obj).entrySet().stream()
|
||||
.collect(() -> new LinkedHashMap<String, Object>(),
|
||||
(m, e) -> m.put(String.format("%s%s", prefix, e.getKey()), e.getValue()), HashMap::putAll));
|
||||
|
||||
result.putAll(factory.getTypeInfo().entrySet().stream()
|
||||
.collect(() -> new LinkedHashMap<String, Object>(),
|
||||
(m, e) -> m.put(String.format("%stypeinfo.%s", prefix, e.getKey()), e.getValue()),
|
||||
HashMap::putAll)
|
||||
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static boolean factoryMatches(String filter, EntaxyFactory factory) {
|
||||
try {
|
||||
return factoryMatchesUnsafe(filter, factory);
|
||||
} catch (InvalidSyntaxException ignore) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean factoryMatchesUnsafe(String filter, EntaxyFactory factory) throws InvalidSyntaxException {
|
||||
return factoryMatches(FrameworkUtil.createFilter(filter), factory);
|
||||
}
|
||||
|
||||
public static boolean factoryMatches(Filter filter, EntaxyFactory factory) {
|
||||
return filter.matches(getFactoryEssence(factory));
|
||||
}
|
||||
|
||||
public static List<EntaxyFactory> filterFactories(String filter, List<EntaxyFactory> factories) {
|
||||
try {
|
||||
return filterFactoriesUnsafe(filter, factories);
|
||||
} catch (InvalidSyntaxException ignore) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
public static List<EntaxyFactory> filterFactoriesUnsafe(String filter, List<EntaxyFactory> factories)
|
||||
throws InvalidSyntaxException {
|
||||
Filter osgiFilter = FrameworkUtil.createFilter(filter);
|
||||
|
||||
return filterFactories(osgiFilter, factories);
|
||||
|
||||
}
|
||||
|
||||
public static List<EntaxyFactory> filterFactories(Filter filter, List<EntaxyFactory> factories) {
|
||||
return factories.stream().filter(f -> factoryMatches(filter, f)).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
* ~~~~~~licensing~~~~~~
|
||||
* test-producers
|
||||
* ==========
|
||||
* Copyright (C) 2020 - 2023 EmDev LLC
|
||||
* Copyright (C) 2020 - 2024 EmDev LLC
|
||||
* ==========
|
||||
* You may not use this file except in accordance with the License Terms of the Copyright
|
||||
* Holder located at: https://entaxy.ru/eula . All copyrights, all intellectual property
|
||||
|
@ -2,7 +2,7 @@
|
||||
* ~~~~~~licensing~~~~~~
|
||||
* test-producers
|
||||
* ==========
|
||||
* Copyright (C) 2020 - 2023 EmDev LLC
|
||||
* Copyright (C) 2020 - 2024 EmDev LLC
|
||||
* ==========
|
||||
* You may not use this file except in accordance with the License Terms of the Copyright
|
||||
* Holder located at: https://entaxy.ru/eula . All copyrights, all intellectual property
|
||||
|
@ -2,7 +2,7 @@
|
||||
* ~~~~~~licensing~~~~~~
|
||||
* test-producers
|
||||
* ==========
|
||||
* Copyright (C) 2020 - 2023 EmDev LLC
|
||||
* Copyright (C) 2020 - 2024 EmDev LLC
|
||||
* ==========
|
||||
* You may not use this file except in accordance with the License Terms of the Copyright
|
||||
* Holder located at: https://entaxy.ru/eula . All copyrights, all intellectual property
|
||||
|
@ -2,7 +2,7 @@
|
||||
* ~~~~~~licensing~~~~~~
|
||||
* test-producers
|
||||
* ==========
|
||||
* Copyright (C) 2020 - 2023 EmDev LLC
|
||||
* Copyright (C) 2020 - 2024 EmDev LLC
|
||||
* ==========
|
||||
* You may not use this file except in accordance with the License Terms of the Copyright
|
||||
* Holder located at: https://entaxy.ru/eula . All copyrights, all intellectual property
|
||||
|
@ -2,7 +2,7 @@
|
||||
* ~~~~~~licensing~~~~~~
|
||||
* test-producers
|
||||
* ==========
|
||||
* Copyright (C) 2020 - 2023 EmDev LLC
|
||||
* Copyright (C) 2020 - 2024 EmDev LLC
|
||||
* ==========
|
||||
* You may not use this file except in accordance with the License Terms of the Copyright
|
||||
* Holder located at: https://entaxy.ru/eula . All copyrights, all intellectual property
|
||||
|
Reference in New Issue
Block a user