diff --git a/.env b/.env
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/.env
@@ -0,0 +1 @@
+
diff --git a/.gitignore b/.gitignore
index 2af7cefb..665c192c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,4 +21,5 @@ build/
nbbuild/
dist/
nbdist/
-.nb-gradle/
\ No newline at end of file
+.nb-gradle/
+*.flattened-pom.xml
diff --git a/muttley-admin-server.pom/.gitignore b/muttley-admin-server.pom/.gitignore
new file mode 100644
index 00000000..549e00a2
--- /dev/null
+++ b/muttley-admin-server.pom/.gitignore
@@ -0,0 +1,33 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
diff --git a/muttley-admin-server.pom/.mvn/wrapper/MavenWrapperDownloader.java b/muttley-admin-server.pom/.mvn/wrapper/MavenWrapperDownloader.java
new file mode 100644
index 00000000..a45eb6ba
--- /dev/null
+++ b/muttley-admin-server.pom/.mvn/wrapper/MavenWrapperDownloader.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2007-present the original author or authors.
+ *
+ * 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
+ *
+ * https://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.
+ */
+
+import java.net.*;
+import java.io.*;
+import java.nio.channels.*;
+import java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+ private static final String WRAPPER_VERSION = "0.5.6";
+ /**
+ * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+ */
+ private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
+
+ /**
+ * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
+ * use instead of the default one.
+ */
+ private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+ ".mvn/wrapper/maven-wrapper.properties";
+
+ /**
+ * Path where the maven-wrapper.jar will be saved to.
+ */
+ private static final String MAVEN_WRAPPER_JAR_PATH =
+ ".mvn/wrapper/maven-wrapper.jar";
+
+ /**
+ * Name of the property which should be used to override the default download url for the wrapper.
+ */
+ private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+ public static void main(String args[]) {
+ System.out.println("- Downloader started");
+ File baseDirectory = new File(args[0]);
+ System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
+
+ // If the maven-wrapper.properties exists, read it and check if it contains a custom
+ // wrapperUrl parameter.
+ File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
+ String url = DEFAULT_DOWNLOAD_URL;
+ if (mavenWrapperPropertyFile.exists()) {
+ FileInputStream mavenWrapperPropertyFileInputStream = null;
+ try {
+ mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
+ Properties mavenWrapperProperties = new Properties();
+ mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
+ url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+ } catch (IOException e) {
+ System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
+ } finally {
+ try {
+ if (mavenWrapperPropertyFileInputStream != null) {
+ mavenWrapperPropertyFileInputStream.close();
+ }
+ } catch (IOException e) {
+ // Ignore ...
+ }
+ }
+ }
+ System.out.println("- Downloading from: " + url);
+
+ File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
+ if (!outputFile.getParentFile().exists()) {
+ if (!outputFile.getParentFile().mkdirs()) {
+ System.out.println(
+ "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
+ }
+ }
+ System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
+ try {
+ downloadFileFromURL(url, outputFile);
+ System.out.println("Done");
+ System.exit(0);
+ } catch (Throwable e) {
+ System.out.println("- Error downloading");
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+ if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
+ String username = System.getenv("MVNW_USERNAME");
+ char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
+ Authenticator.setDefault(new Authenticator() {
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(username, password);
+ }
+ });
+ }
+ URL website = new URL(urlString);
+ ReadableByteChannel rbc;
+ rbc = Channels.newChannel(website.openStream());
+ FileOutputStream fos = new FileOutputStream(destination);
+ fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+ fos.close();
+ rbc.close();
+ }
+
+}
diff --git a/muttley-admin-server.pom/.mvn/wrapper/maven-wrapper.jar b/muttley-admin-server.pom/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 00000000..2cc7d4a5
Binary files /dev/null and b/muttley-admin-server.pom/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/muttley-admin-server.pom/.mvn/wrapper/maven-wrapper.properties b/muttley-admin-server.pom/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 00000000..ffdc10e5
--- /dev/null
+++ b/muttley-admin-server.pom/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,2 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
diff --git a/muttley-admin-server.pom/muttley-admin-server/.gitignore b/muttley-admin-server.pom/muttley-admin-server/.gitignore
new file mode 100644
index 00000000..549e00a2
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/.gitignore
@@ -0,0 +1,33 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
diff --git a/muttley-admin-server.pom/muttley-admin-server/.mvn/wrapper/MavenWrapperDownloader.java b/muttley-admin-server.pom/muttley-admin-server/.mvn/wrapper/MavenWrapperDownloader.java
new file mode 100644
index 00000000..a45eb6ba
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/.mvn/wrapper/MavenWrapperDownloader.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2007-present the original author or authors.
+ *
+ * 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
+ *
+ * https://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.
+ */
+
+import java.net.*;
+import java.io.*;
+import java.nio.channels.*;
+import java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+ private static final String WRAPPER_VERSION = "0.5.6";
+ /**
+ * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+ */
+ private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
+
+ /**
+ * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
+ * use instead of the default one.
+ */
+ private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+ ".mvn/wrapper/maven-wrapper.properties";
+
+ /**
+ * Path where the maven-wrapper.jar will be saved to.
+ */
+ private static final String MAVEN_WRAPPER_JAR_PATH =
+ ".mvn/wrapper/maven-wrapper.jar";
+
+ /**
+ * Name of the property which should be used to override the default download url for the wrapper.
+ */
+ private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+ public static void main(String args[]) {
+ System.out.println("- Downloader started");
+ File baseDirectory = new File(args[0]);
+ System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
+
+ // If the maven-wrapper.properties exists, read it and check if it contains a custom
+ // wrapperUrl parameter.
+ File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
+ String url = DEFAULT_DOWNLOAD_URL;
+ if (mavenWrapperPropertyFile.exists()) {
+ FileInputStream mavenWrapperPropertyFileInputStream = null;
+ try {
+ mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
+ Properties mavenWrapperProperties = new Properties();
+ mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
+ url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+ } catch (IOException e) {
+ System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
+ } finally {
+ try {
+ if (mavenWrapperPropertyFileInputStream != null) {
+ mavenWrapperPropertyFileInputStream.close();
+ }
+ } catch (IOException e) {
+ // Ignore ...
+ }
+ }
+ }
+ System.out.println("- Downloading from: " + url);
+
+ File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
+ if (!outputFile.getParentFile().exists()) {
+ if (!outputFile.getParentFile().mkdirs()) {
+ System.out.println(
+ "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
+ }
+ }
+ System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
+ try {
+ downloadFileFromURL(url, outputFile);
+ System.out.println("Done");
+ System.exit(0);
+ } catch (Throwable e) {
+ System.out.println("- Error downloading");
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+ if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
+ String username = System.getenv("MVNW_USERNAME");
+ char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
+ Authenticator.setDefault(new Authenticator() {
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(username, password);
+ }
+ });
+ }
+ URL website = new URL(urlString);
+ ReadableByteChannel rbc;
+ rbc = Channels.newChannel(website.openStream());
+ FileOutputStream fos = new FileOutputStream(destination);
+ fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+ fos.close();
+ rbc.close();
+ }
+
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/.mvn/wrapper/maven-wrapper.jar b/muttley-admin-server.pom/muttley-admin-server/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 00000000..2cc7d4a5
Binary files /dev/null and b/muttley-admin-server.pom/muttley-admin-server/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/muttley-admin-server.pom/muttley-admin-server/.mvn/wrapper/maven-wrapper.properties b/muttley-admin-server.pom/muttley-admin-server/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 00000000..ffdc10e5
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,2 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
diff --git a/muttley-admin-server.pom/muttley-admin-server/mvnw b/muttley-admin-server.pom/muttley-admin-server/mvnw
new file mode 100755
index 00000000..a16b5431
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/mvnw
@@ -0,0 +1,310 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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
+#
+# https://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.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ export JAVA_HOME="`/usr/libexec/java_home`"
+ else
+ export JAVA_HOME="/Library/Java/Home"
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`which java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=`cd "$wdir/.."; pwd`
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' < "$1")"
+ fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found .mvn/wrapper/maven-wrapper.jar"
+ fi
+else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+ fi
+ if [ -n "$MVNW_REPOURL" ]; then
+ jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ else
+ jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ fi
+ while IFS="=" read key value; do
+ case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+ esac
+ done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Downloading from: $jarUrl"
+ fi
+ wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+ if $cygwin; then
+ wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+ fi
+
+ if command -v wget > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found wget ... using wget"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget "$jarUrl" -O "$wrapperJarPath"
+ else
+ wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found curl ... using curl"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl -o "$wrapperJarPath" "$jarUrl" -f
+ else
+ curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+ fi
+
+ else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Falling back to using Java to download"
+ fi
+ javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaClass=`cygpath --path --windows "$javaClass"`
+ fi
+ if [ -e "$javaClass" ]; then
+ if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Compiling MavenWrapperDownloader.java ..."
+ fi
+ # Compiling the Java class
+ ("$JAVA_HOME/bin/javac" "$javaClass")
+ fi
+ if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ # Running the downloader
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Running MavenWrapperDownloader.java ..."
+ fi
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+ echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/muttley-admin-server.pom/muttley-admin-server/mvnw.cmd b/muttley-admin-server.pom/muttley-admin-server/mvnw.cmd
new file mode 100644
index 00000000..c8d43372
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/mvnw.cmd
@@ -0,0 +1,182 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+
+FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %DOWNLOAD_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
diff --git a/muttley-admin-server.pom/muttley-admin-server/pom.xml b/muttley-admin-server.pom/muttley-admin-server/pom.xml
new file mode 100644
index 00000000..482865bd
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/pom.xml
@@ -0,0 +1,96 @@
+
+
+
+ br.com.muttley
+ muttley-admin-server.pom
+ ${revision}
+
+ 4.0.0
+ jar
+
+ muttley-admin-server
+
+ muttley-admin-server
+ Demo project for Spring Boot
+
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+
+ org.hibernate
+ hibernate-validator
+
+
+
+ br.com.muttley
+ muttley-feign
+
+
+
+ br.com.muttley
+ muttley-jackson
+
+
+
+ br.com.muttley
+ muttley-model
+
+
+
+ br.com.muttley
+ muttley-mongo
+
+
+
+ br.com.muttley
+ muttley-security
+
+
+
+ br.com.muttley
+ muttley-exception
+
+
+
+ br.com.muttley
+ muttley-rest
+
+
+
+ br.com.muttley
+ muttley-domain-service
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-eureka
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-devtools
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/MuttleyAdminServerConfig.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/MuttleyAdminServerConfig.java
new file mode 100644
index 00000000..b7d4d85e
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/MuttleyAdminServerConfig.java
@@ -0,0 +1,36 @@
+package br.com.muttley.admin.server;
+
+import org.springframework.boot.autoconfigure.domain.EntityScan;
+import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
+import org.springframework.cloud.netflix.feign.EnableFeignClients;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author Joel Rodrigues Moreira 22/04/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Configuration
+//Packages onde existem entidades
+@EntityScan(basePackages = {"br.com.muttley.model.admin"})
+@ComponentScan(basePackages = {
+ //Injeções internas do projeto
+ "br.com.muttley.admin.server",
+ //Configuração de serviços
+ "br.com.muttley.domain.service",
+ //Configurações de segurança para o gateway
+ "br.com.muttley.security.zuul.gateway.service",
+ //Configurações do serviço de cache
+ "br.com.muttley.redis.service",
+ //Configurações de exceptions
+ "br.com.muttley.exception.service",
+ //Configurações de serialização
+ "br.com.muttley.jackson.service",
+ "br.com.muttley.feign.service",
+ "br.com.muttley.rest"
+})
+@EnableEurekaClient
+@EnableFeignClients(basePackages = "br.com.muttley.security.feign")
+public class MuttleyAdminServerConfig {
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/config/model/DocumentNameConfig.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/config/model/DocumentNameConfig.java
new file mode 100644
index 00000000..9e5b58de
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/config/model/DocumentNameConfig.java
@@ -0,0 +1,81 @@
+package br.com.muttley.admin.server.config.model;
+
+
+import lombok.Getter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author Joel Rodrigues Moreira on 30/04/20.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Configuration(value = "documentNameConfig")
+@Getter
+public class DocumentNameConfig {
+ private final String nameCollectionAdminOwner;
+ private final String nameCollectionOwner;
+ private final String nameCollectionUser;
+ private final String nameCollectionPassword;
+ private final String nameCollectionAdminUserBase;
+ private final String nameCollectionUserBase;
+ private final String nameCollectionAccessPlan;
+ private final String nameCollectionUserPreferences;
+ private final String nameCollectionUserTokensNotification;
+ private final String nameCollectionAdminPassaport;
+ private final String nameCollectionPassaport;
+ private final String nameCollectionWorkTeam;
+
+ private final String nameCollectionXAPIToken;
+
+ private final String nameCollectionAdminUserDataBinding;
+ private final String nameCollectionUserDataBinding;
+ private final String nameViewCollectionUser;
+ private final String nameViewCollectionPassaport;
+ private final String nameViewCollectionPassaportRolesUser;
+
+ private final String nameViewCollectionWorkTeam;
+
+
+ public DocumentNameConfig(
+ @Value("${br.com.muttley.security.server.owner-document:muttley-admin-owners}") final String nameCollectionAdminOwner,
+ @Value("${br.com.muttley.security.server.owner-document:muttley-owners}") final String nameCollectionOwner,
+ @Value("${br.com.muttley.security.server.user-document:muttley-users}") final String nameCollectionUser,
+ @Value("${br.com.muttley.security.server.user-password-document:muttley-users-password}") final String nameCollectionPassword,
+ @Value("${br.com.muttley.security.server.user-base-document:muttley-admin-users-base}") final String nameCollectionAdminUserBase,
+ @Value("${br.com.muttley.security.server.user-base-document:muttley-users-base}") final String nameCollectionUserBase,
+ @Value("${br.com.muttley.security.server.access-plan-document:muttley-access-plans}") final String nameCollectionAccessPlan,
+ @Value("${br.com.muttley.security.server.user-preference-document:muttley-users-preferences}") final String nameCollectionUserPreferences,
+ @Value("${br.com.muttley.security.server.user-tokens-notification-document:muttley-users-tokens-notification}") final String nameCollectionUserTokensNotification,
+ @Value("${br.com.muttley.security.server.admin-passaport-document:muttley-admin-passaports}") final String nameCollectionAdminPassaport,
+ @Value("${br.com.muttley.security.server.passaport-document:muttley-passaports}") final String nameCollectionPassaport,
+ @Value("${br.com.muttley.security.server.work-team-document:muttley-work-teams}") final String nameCollectionWorkTeam,
+ @Value("${br.com.muttley.security.server.x-api-token-document:muttley-x-api-token}") final String nameCollectionXAPIToken,
+ @Value("${br.com.muttley.security.server.user-data-binding:muttley-admin-users-databinding}") final String nameCollectionAdminUserDataBinding,
+ @Value("${br.com.muttley.security.server.user-data-binding:muttley-users-databinding}") final String nameCollectionUserDataBinding,
+ @Value("${br.com.muttley.security.server.user-document-view:view-muttley-users}") final String nameViewCollectionUser,
+ @Value("${br.com.muttley.security.server.passaport-document-view:view-muttley-passaports}") final String nameViewCollectionPassaport,
+ @Value("${br.com.muttley.security.server.passaport-role-document-view:view-muttley-passaports-roles-user}") final String nameViewCollectionPassaportRolesUser,
+ @Value("${br.com.muttley.security.server.work-team-document-view:view-muttley-work-teams}") final String nameViewCollectionWorkTeam
+ ) {
+ this.nameCollectionAdminOwner = nameCollectionAdminOwner;
+ this.nameCollectionOwner = nameCollectionOwner;
+ this.nameCollectionUser = nameCollectionUser;
+ this.nameCollectionPassword = nameCollectionPassword;
+ this.nameCollectionAdminUserBase = nameCollectionAdminUserBase;
+ this.nameCollectionUserBase = nameCollectionUserBase;
+ this.nameCollectionAccessPlan = nameCollectionAccessPlan;
+ this.nameCollectionUserPreferences = nameCollectionUserPreferences;
+ this.nameCollectionUserTokensNotification = nameCollectionUserTokensNotification;
+ this.nameCollectionAdminPassaport = nameCollectionAdminPassaport;
+ this.nameCollectionPassaport = nameCollectionPassaport;
+ this.nameCollectionWorkTeam = nameCollectionWorkTeam;
+ this.nameCollectionXAPIToken = nameCollectionXAPIToken;
+ this.nameCollectionAdminUserDataBinding = nameCollectionAdminUserDataBinding;
+ this.nameCollectionUserDataBinding = nameCollectionUserDataBinding;
+ this.nameViewCollectionUser = nameViewCollectionUser;
+ this.nameViewCollectionPassaport = nameViewCollectionPassaport;
+ this.nameViewCollectionPassaportRolesUser = nameViewCollectionPassaportRolesUser;
+ this.nameViewCollectionWorkTeam = nameViewCollectionWorkTeam;
+ }
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/config/mongo/MongoConfig.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/config/mongo/MongoConfig.java
new file mode 100644
index 00000000..1dc8a6cd
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/config/mongo/MongoConfig.java
@@ -0,0 +1,29 @@
+package br.com.muttley.admin.server.config.mongo;
+
+
+import br.com.muttley.mongo.service.repository.impl.DocumentMongoRepositoryImpl;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
+
+/**
+ * @author Joel Rodrigues Moreira on 22/04/2021.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ *
+ * Realiza a configuração do mongo db
+ */
+@Configuration
+@EnableMongoRepositories(basePackages = {"br.com.muttley.admin.server.repository"}, repositoryBaseClass = DocumentMongoRepositoryImpl.class)
+public class MongoConfig extends br.com.muttley.mongo.service.MongoConfig {
+
+ public MongoConfig(@Value("${spring.data.mongodb.database}") final String dataBaseName,
+ @Value("${spring.data.mongodb.host}") final String hostDataBase,
+ @Value("${spring.data.mongodb.port}") final String portDataBase,
+ @Value("${spring.data.mongodb.username}") final String userName,
+ @Value("${spring.data.mongodb.password}") final String password) {
+ super(dataBaseName, hostDataBase, portDataBase, userName, password);
+ }
+}
+
+
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/config/postint/AccessPlanConfig.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/config/postint/AccessPlanConfig.java
new file mode 100644
index 00000000..11abd825
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/config/postint/AccessPlanConfig.java
@@ -0,0 +1,69 @@
+package br.com.muttley.admin.server.config.postint;
+
+
+import br.com.muttley.model.security.AccessPlan;
+import br.com.muttley.security.feign.AccessPlanServiceClient;
+import br.com.muttley.security.feign.OwnerServiceClient;
+import br.com.muttley.security.feign.PassaportServiceClient;
+import br.com.muttley.security.feign.UserServiceClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.context.ApplicationListener;
+import org.springframework.stereotype.Component;
+
+import java.util.Collection;
+
+import static java.util.Arrays.asList;
+
+/**
+ * @author Joel Rodrigues Moreira on 22/04/2021
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ * Realiza a criação dos planos basico de utilização do sistema.
+ */
+@Component
+public class AccessPlanConfig implements ApplicationListener {
+ private final AccessPlanServiceClient accessplanService;
+ private final OwnerServiceClient ownerService;
+ private final UserServiceClient userService;
+ private final PassaportServiceClient passaportService;
+
+ @Autowired
+ public AccessPlanConfig(final AccessPlanServiceClient accessplanService, final OwnerServiceClient ownerService, final UserServiceClient userService, final PassaportServiceClient passaportService) {
+ this.accessplanService = accessplanService;
+ this.ownerService = ownerService;
+ this.userService = userService;
+ this.passaportService = passaportService;
+ }
+
+ @Override
+ public void onApplicationEvent(final ApplicationReadyEvent event) {
+ /*new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Thread.sleep(10000);
+ } catch (InterruptedException e) {
+ }
+ accessplanService.count();
+ }
+ }).start();*/
+ if (this.accessplanService.count() == 0) {
+ getDefaultPlanos()
+ .forEach(p -> accessplanService.save(p, ""));
+ }
+ }
+
+ private final Collection getDefaultPlanos() {
+ return asList(
+ new AccessPlan()
+ .setName("Básico")
+ .setTotalUsers(10)
+ .setDescription("Plano de acesso básico"),
+ new AccessPlan()
+ .setName("Premium")
+ .setTotalUsers(10)
+ .setDescription("Plano de acesso premium")
+ );
+ }
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/config/postint/UserConfig.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/config/postint/UserConfig.java
new file mode 100644
index 00000000..ce294086
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/config/postint/UserConfig.java
@@ -0,0 +1,130 @@
+package br.com.muttley.admin.server.config.postint;
+
+import br.com.muttley.admin.server.service.AdminUserBaseService;
+import br.com.muttley.admin.server.service.NoSecurityAdminOwnerService;
+import br.com.muttley.admin.server.service.NoSecurityAdminPassaportService;
+import br.com.muttley.exception.throwables.MuttleyConflictException;
+import br.com.muttley.exception.throwables.MuttleyNotFoundException;
+import br.com.muttley.model.admin.AdminOwner;
+import br.com.muttley.model.admin.AdminPassaport;
+import br.com.muttley.model.admin.AdminUserBase;
+import br.com.muttley.model.security.*;
+import br.com.muttley.model.security.preference.Foto;
+import br.com.muttley.security.feign.UserServiceClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.context.ApplicationListener;
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ObjectUtils;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Set;
+
+/**
+ * @author Joel Rodrigues Moreira 23/04/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Component
+public class UserConfig implements ApplicationListener {
+ private final String defaultUser;
+ private final String userRead;
+ private final String passwdDefaultUser;
+ private final String passwdUserRead;
+ private final String nameOrganization;
+ private final UserServiceClient service;
+ private final AdminUserBaseService adminUserBaseService;
+
+ private final NoSecurityAdminOwnerService ownerService;
+ private final NoSecurityAdminPassaportService passaportService;
+
+ @Autowired
+ public UserConfig(
+ @Value("${muttley.admin-server.default-user}") String defaultUser,
+ @Value("${muttley.admin-server.user-read:#{null}}") String userRead,
+ @Value("${muttley.admin-server.passwd-default-user}") String passwdDefaultUser,
+ @Value("${muttley.admin-server.passwd-user-read:#{null}}") String passwdUserRead,
+ @Value("${muttley.admin-server.name-organization}") String nameOrganization,
+ UserServiceClient service,
+ AdminUserBaseService adminUserBaseService,
+ NoSecurityAdminOwnerService ownerService,
+ NoSecurityAdminPassaportService passaportService) {
+ this.defaultUser = defaultUser;
+ this.userRead = userRead;
+ this.passwdDefaultUser = passwdDefaultUser;
+ this.passwdUserRead = passwdUserRead;
+ this.nameOrganization = nameOrganization;
+ this.service = service;
+ this.adminUserBaseService = adminUserBaseService;
+ this.ownerService = ownerService;
+ this.passaportService = passaportService;
+ }
+
+ @Override
+ public void onApplicationEvent(final ApplicationReadyEvent event) {
+ AdminOwner owner;
+ try {
+ owner = this.ownerService.findByName(this.nameOrganization);
+ } catch (MuttleyNotFoundException var7) {
+ User user = null;
+
+ try {
+ user = this.service.save(new UserPayLoad("Admin", "Usuário para administrar todo o ecossistema", this.defaultUser, (String) null, (String) null, (Foto) null, (Set) null, this.passwdDefaultUser, (String) null, true, (String) null, (String) null, false), "true");
+ } catch (MuttleyConflictException var6) {
+ user = this.service.findByUserName(this.defaultUser);
+ }
+
+ owner = this.ownerService.save((AdminOwner) (new AdminOwner()).setName(this.nameOrganization).setDescription("Administrador unico do sistema").setUserMaster(user));
+ this.adminUserBaseService.save(user, (AdminUserBase) (new AdminUserBase()).setOwner(owner).addUser((new UserBaseItem()).setUser(user).setAddedBy(user).setStatus(true).setDtCreate(new Date())));
+ AdminPassaport var5 = this.passaportService.save((AdminPassaport) (new AdminPassaport()).setName("Grupo principal").setUserMaster(user).setOwner(owner).addRole(Role.ROLE_ROOT).setDescription("Não pode exister outro grupo no odin repository").addMember(user));
+ }
+
+ this.createUserRead(owner);
+ }
+
+ private void createUserRead(Owner owner) {
+ if (!ObjectUtils.isEmpty(this.userRead) || !ObjectUtils.isEmpty(this.passwdUserRead)) {
+ try {
+ SecurityContext ctx = SecurityContextHolder.createEmptyContext();
+ SecurityContextHolder.setContext(ctx);
+ ctx.setAuthentication(new AnonymousAuthenticationToken(owner.getUserMaster().getId(), owner.getUserMaster(), Arrays.asList(new GrantedAuthority() {
+ @Override
+ public String getAuthority() {
+ return Role.ROLE_ROOT.toString();
+ }
+ })));
+
+ //Do what ever you want to do
+
+
+ User userRead = null;
+
+ try {
+ userRead = this.service.findByUserName(this.userRead);
+ } catch (MuttleyNotFoundException var5) {
+ }
+
+ if (userRead == null) {
+ try {
+ userRead = this.service.save(new UserPayLoad("AdminRead", "Usuário para consumir dados do ecossistema", this.userRead, (String) null, (String) null, (Foto) null, (Set) null, this.passwdUserRead, (String) null, true, (String) null, (String) null, false), "true");
+ } catch (MuttleyConflictException var4) {
+ userRead = this.service.findByUserName(this.userRead);
+ }
+
+ this.adminUserBaseService.update(owner.getUserMaster(), (AdminUserBase) ((AdminUserBase) this.adminUserBaseService.findFirst(owner.getUserMaster())).addUser((new UserBaseItem()).setUser(userRead).setAddedBy(userRead).setStatus(true).setDtCreate(new Date())));
+ AdminPassaport var3 = this.passaportService.save((AdminPassaport) (new AdminPassaport()).setName("Grupo principal para leitura").setUserMaster(owner.getUserMaster()).setOwner(owner).addRole(Role.ROLE_ROOT).setDescription("Não pode exister outro grupo no odin repository").addMember(userRead));
+ }
+
+ } finally {
+ SecurityContextHolder.clearContext();
+ }
+ }
+ }
+
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/controller/AccessPlanController.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/controller/AccessPlanController.java
new file mode 100644
index 00000000..6c2ff63c
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/controller/AccessPlanController.java
@@ -0,0 +1,122 @@
+package br.com.muttley.admin.server.controller;
+
+import br.com.muttley.model.security.AccessPlan;
+import br.com.muttley.rest.RestController;
+import br.com.muttley.rest.RestResource;
+import br.com.muttley.security.feign.AccessPlanServiceClient;
+import br.com.muttley.security.infra.resource.PageableResource;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.Map;
+
+import static org.springframework.http.HttpStatus.OK;
+import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8_VALUE;
+import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
+import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE;
+import static org.springframework.web.bind.annotation.RequestMethod.DELETE;
+import static org.springframework.web.bind.annotation.RequestMethod.GET;
+import static org.springframework.web.bind.annotation.RequestMethod.POST;
+import static org.springframework.web.bind.annotation.RequestMethod.PUT;
+
+/**
+ * @author Joel Rodrigues Moreira 20/04/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@org.springframework.web.bind.annotation.RestController
+@RequestMapping(value = "/api/v1/access-plan", produces = {APPLICATION_JSON_UTF8_VALUE, APPLICATION_JSON_VALUE})
+public class AccessPlanController implements RestController, RestResource {
+ private final AccessPlanServiceClient client;
+ private final ApplicationEventPublisher eventPublisher;
+
+ @Autowired
+ public AccessPlanController(final AccessPlanServiceClient client, final ApplicationEventPublisher eventPublisher) {
+ this.client = client;
+ this.eventPublisher = eventPublisher;
+ }
+
+ @Override
+ @RequestMapping(method = POST, consumes = {APPLICATION_JSON_UTF8_VALUE})
+ public ResponseEntity save(@RequestBody final AccessPlan value, final HttpServletResponse response, @RequestParam(required = false, value = "returnEntity", defaultValue = "") final String returnEntity) {
+ final AccessPlan record = client.save(value, returnEntity);
+
+ publishCreateResourceEvent(this.eventPublisher, response, record);
+
+ if (returnEntity != null && returnEntity.equals("true")) {
+ return ResponseEntity.status(HttpStatus.CREATED).body(record);
+ }
+ return ResponseEntity.status(HttpStatus.CREATED).build();
+
+ }
+
+ @Override
+ @RequestMapping(value = "/{id}", method = PUT, consumes = APPLICATION_JSON_UTF8_VALUE)
+ public ResponseEntity update(@PathVariable("id") final String id, @RequestBody final AccessPlan model) {
+ return ResponseEntity.ok(client.update(id, model));
+ }
+
+ @Override
+ @RequestMapping(value = "/{id}", method = DELETE)
+ public ResponseEntity deleteById(@PathVariable("id") final String id) {
+ client.deleteById(id);
+ return ResponseEntity.ok().build();
+ }
+
+ @Override
+ @RequestMapping(value = "/{id}", method = GET, consumes = APPLICATION_JSON_UTF8_VALUE)
+ public ResponseEntity findById(@PathVariable("id") final String id, final HttpServletResponse response) {
+ final AccessPlan value = client.findById(id);
+
+ publishSingleResourceRetrievedEvent(this.eventPublisher, response);
+
+ return ResponseEntity.ok(value);
+ }
+
+ @Override
+ @RequestMapping(value = "/reference/{id}", method = GET, produces = {APPLICATION_JSON_UTF8_VALUE, APPLICATION_JSON_VALUE})
+ @ResponseStatus(OK)
+ public ResponseEntity findReferenceById(@PathVariable("id") String id, HttpServletResponse response) {
+ final AccessPlan value = client.findReferenceById(id);
+
+ publishSingleResourceRetrievedEvent(this.eventPublisher, response);
+
+ return ResponseEntity.ok(value);
+ }
+
+ @Override
+ public ResponseEntity findByIds(final String[] strings, final HttpServletResponse httpServletResponse) {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ @RequestMapping(value = "/first", method = GET, produces = APPLICATION_JSON_UTF8_VALUE)
+ public ResponseEntity first(final HttpServletResponse response) {
+ final AccessPlan value = client.first();
+ publishSingleResourceRetrievedEvent(this.eventPublisher, response);
+ return ResponseEntity.ok(value);
+ }
+
+ @Override
+ @RequestMapping(method = GET, produces = APPLICATION_JSON_UTF8_VALUE)
+ public ResponseEntity list(final HttpServletResponse response, @RequestParam final Map allRequestParams) {
+ final PageableResource pageableResource = client.list(allRequestParams);
+ return ResponseEntity.ok(pageableResource);
+ }
+
+ @Override
+ @RequestMapping(value = "/count", method = GET, produces = TEXT_PLAIN_VALUE)
+ public ResponseEntity count(@RequestParam final Map allRequestParams) {
+ return ResponseEntity.ok(String.valueOf(client.count(allRequestParams)));
+ }
+
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/controller/OwnerController.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/controller/OwnerController.java
new file mode 100644
index 00000000..64aeaa67
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/controller/OwnerController.java
@@ -0,0 +1,146 @@
+package br.com.muttley.admin.server.controller;
+
+import br.com.muttley.admin.server.events.OwnerCreatedEvent;
+import br.com.muttley.model.security.Owner;
+import br.com.muttley.rest.RestController;
+import br.com.muttley.rest.RestResource;
+import br.com.muttley.security.feign.OwnerServiceClient;
+import br.com.muttley.security.feign.PassaportServiceClient;
+import br.com.muttley.security.infra.resource.PageableResource;
+import org.apache.commons.lang.NotImplementedException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseStatus;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.Map;
+
+import static org.springframework.http.HttpStatus.OK;
+import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8_VALUE;
+import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
+import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE;
+import static org.springframework.web.bind.annotation.RequestMethod.DELETE;
+import static org.springframework.web.bind.annotation.RequestMethod.GET;
+import static org.springframework.web.bind.annotation.RequestMethod.POST;
+import static org.springframework.web.bind.annotation.RequestMethod.PUT;
+
+/**
+ * @author Joel Rodrigues Moreira 20/04/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@org.springframework.web.bind.annotation.RestController
+@RequestMapping(value = "/api/v1/owners", produces = {APPLICATION_JSON_UTF8_VALUE, APPLICATION_JSON_VALUE})
+public class OwnerController implements RestController, RestResource {
+ private final OwnerServiceClient client;
+ private final PassaportServiceClient passaportService;
+ private final ApplicationEventPublisher eventPublisher;
+
+
+ @Autowired
+ public OwnerController(final OwnerServiceClient client, final PassaportServiceClient passaportService, final ApplicationEventPublisher eventPublisher) {
+ this.client = client;
+ this.passaportService = passaportService;
+ this.eventPublisher = eventPublisher;
+ }
+
+ @Override
+ @RequestMapping(method = POST, consumes = {APPLICATION_JSON_UTF8_VALUE})
+ public ResponseEntity save(@RequestBody final Owner value, final HttpServletResponse response, @RequestParam(required = false, value = "returnEntity", defaultValue = "") final String returnEntity) {
+
+ final Owner record = client.save(value, "true");
+
+ //disparando evento para informar que foi cria o owner
+ this.eventPublisher.publishEvent(new OwnerCreatedEvent(record));
+ /*//criando o grupo de trabalho para vendedores
+ final WorkTeam vendedores = this.workTeamService.createWorkTeamFor(
+ record.getId(), new WorkTeam()
+ .setName(WORK_TEAM_NAME)
+ .setDescription("Grupo principal do sistema criado especificamente para dar autorização a vendedores mobile")
+ .setUserMaster(record.getUserMaster())
+ .setUserMaster(record.getUserMaster())
+ .setOwner(record)
+ .setRoles(
+ Role.getValues()
+ .stream()
+ .filter(it -> it.getRoleName().contains("MOBILE"))
+ .collect(toSet())
+ )
+ );*/
+
+
+ publishCreateResourceEvent(this.eventPublisher, response, record);
+
+ if (returnEntity != null && returnEntity.equals("true")) {
+ return ResponseEntity.status(HttpStatus.CREATED).body(record);
+ }
+ return ResponseEntity.status(HttpStatus.CREATED).build();
+
+ }
+
+ @Override
+ @RequestMapping(value = "/{id}", method = PUT, consumes = APPLICATION_JSON_UTF8_VALUE)
+ public ResponseEntity update(@PathVariable("id") final String id, @RequestBody final Owner model) {
+ return ResponseEntity.ok(client.update(id, model));
+ }
+
+ @Override
+ @RequestMapping(value = "/{id}", method = DELETE)
+ public ResponseEntity deleteById(@PathVariable("id") final String id) {
+ client.deleteById(id);
+ return ResponseEntity.ok().build();
+ }
+
+ @Override
+ @RequestMapping(value = "/{id}", method = GET, consumes = APPLICATION_JSON_UTF8_VALUE)
+ public ResponseEntity findById(@PathVariable("id") final String id, final HttpServletResponse response) {
+ final Owner value = client.findById(id);
+
+ publishSingleResourceRetrievedEvent(this.eventPublisher, response);
+
+ return ResponseEntity.ok(value);
+ }
+
+ @Override
+ @RequestMapping(value = "/reference/{id}", method = GET, produces = {APPLICATION_JSON_UTF8_VALUE, APPLICATION_JSON_VALUE})
+ @ResponseStatus(OK)
+ public ResponseEntity findReferenceById(@PathVariable("id") String id, HttpServletResponse response) {
+ final Owner value = client.findReferenceById(id);
+
+ publishSingleResourceRetrievedEvent(this.eventPublisher, response);
+
+ return ResponseEntity.ok(value);
+ }
+
+ @Override
+ public ResponseEntity findByIds(final String[] strings, final HttpServletResponse httpServletResponse) {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ @RequestMapping(value = "/first", method = GET, produces = APPLICATION_JSON_UTF8_VALUE)
+ public ResponseEntity first(final HttpServletResponse response) {
+ final Owner value = client.first();
+ publishSingleResourceRetrievedEvent(this.eventPublisher, response);
+ return ResponseEntity.ok(value);
+ }
+
+ @Override
+ @RequestMapping(method = GET, produces = APPLICATION_JSON_UTF8_VALUE)
+ public ResponseEntity list(final HttpServletResponse response, @RequestParam final Map allRequestParams) {
+ final PageableResource pageableResource = client.list(allRequestParams);
+ return ResponseEntity.ok(pageableResource);
+ }
+
+ @Override
+ @RequestMapping(value = "/count", method = GET, produces = TEXT_PLAIN_VALUE)
+ public ResponseEntity count(@RequestParam final Map allRequestParams) {
+ return ResponseEntity.ok(String.valueOf(client.count(allRequestParams)));
+ }
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/events/OwnerCreatedEvent.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/events/OwnerCreatedEvent.java
new file mode 100644
index 00000000..aaedf5d5
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/events/OwnerCreatedEvent.java
@@ -0,0 +1,23 @@
+package br.com.muttley.admin.server.events;
+
+import br.com.muttley.model.security.Owner;
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * @author Joel Rodrigues Moreira 20/04/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public class OwnerCreatedEvent extends ApplicationEvent {
+ private final Owner owner;
+
+ public OwnerCreatedEvent(final Owner owner) {
+ super(owner);
+ this.owner = owner;
+ }
+
+ @Override
+ public Owner getSource() {
+ return this.owner;
+ }
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/listeners/OwnerCreatedEventListeners.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/listeners/OwnerCreatedEventListeners.java
new file mode 100644
index 00000000..efd0b042
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/listeners/OwnerCreatedEventListeners.java
@@ -0,0 +1,63 @@
+package br.com.muttley.admin.server.listeners;
+
+import br.com.muttley.admin.server.events.OwnerCreatedEvent;
+import br.com.muttley.admin.server.service.AdminPassaportService;
+import br.com.muttley.model.admin.AdminPassaport;
+import br.com.muttley.model.security.User;
+import br.com.muttley.model.security.UserBase;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationListener;
+
+import static br.com.muttley.model.security.Role.ROLE_OWNER;
+
+/**
+ * @author Joel Rodrigues Moreira 27/04/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+//@Component
+public class OwnerCreatedEventListeners implements ApplicationListener {
+ final AdminPassaportService passaportService;
+ //final br.com.muttley.security.server.service.AdminUserBaseService
+
+ @Autowired
+ public OwnerCreatedEventListeners(final AdminPassaportService passaportService) {
+ this.passaportService = passaportService;
+ }
+
+ @Override
+ public void onApplicationEvent(final OwnerCreatedEvent event) {
+ final User userMaster = event.getSource().getUserMaster();
+
+ // Adicinando a base de usuário para esse novo owner cadastradao
+ final UserBase userBase = new UserBase();
+ userBase.setOwner(event.getSource())
+ .addUser(userMaster, userMaster);
+
+ //this.userBaseService.save(currentUser, event.getSource(), userBase);
+
+
+ AdminPassaport passaport = (AdminPassaport) new AdminPassaport()
+ .setName("Master")
+ .setDescription("Esse é o grupo principal")
+ .setOwner(event.getSource())
+ .setUserMaster(userMaster)
+ .addMember(userMaster)
+ .addRole(ROLE_OWNER);
+ userMaster.setCurrentOwner(passaport.getOwner());
+
+ passaport = this.passaportService.save(userMaster, passaport);
+
+ /*Já que acabamos de criar um Owner, devemos verificar se o usuário master já tem algumas preferencias básicas
+ * tudo isso para evitar erros
+ */
+ /*final UserPreferences preference = this.userService.loadPreference(userMaster);
+ if (!preference.contains(OWNER_PREFERENCE)) {
+ preference.set(OWNER_PREFERENCE, workTeam.getOwner());
+ //salvando as alterções das preferencias
+ this.userService.save(userMaster, preference);
+ }*/
+
+
+ }
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/repository/AdminOwnerRepository.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/repository/AdminOwnerRepository.java
new file mode 100644
index 00000000..ee5f8fe0
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/repository/AdminOwnerRepository.java
@@ -0,0 +1,15 @@
+package br.com.muttley.admin.server.repository;
+
+import br.com.muttley.model.admin.AdminOwner;
+import br.com.muttley.mongo.service.repository.DocumentMongoRepository;
+import org.springframework.stereotype.Repository;
+
+/**
+ * @author Joel Rodrigues Moreira 20/04/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Repository
+public interface AdminOwnerRepository extends DocumentMongoRepository {
+ public AdminOwner findByName(final String name);
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/repository/AdminPassaportRepository.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/repository/AdminPassaportRepository.java
new file mode 100644
index 00000000..0eb27fd8
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/repository/AdminPassaportRepository.java
@@ -0,0 +1,12 @@
+package br.com.muttley.admin.server.repository;
+
+import br.com.muttley.model.admin.AdminPassaport;
+import br.com.muttley.mongo.service.repository.DocumentMongoRepository;
+
+/**
+ * @author Joel Rodrigues Moreira 20/04/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public interface AdminPassaportRepository extends DocumentMongoRepository {
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/repository/AdminUserDataBindingRepository.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/repository/AdminUserDataBindingRepository.java
new file mode 100644
index 00000000..75c22be4
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/repository/AdminUserDataBindingRepository.java
@@ -0,0 +1,26 @@
+package br.com.muttley.admin.server.repository;
+
+import br.com.muttley.model.admin.AdminUserDataBinding;
+import br.com.muttley.model.security.Owner;
+import br.com.muttley.model.security.User;
+import br.com.muttley.model.security.UserDataBinding;
+import br.com.muttley.mongo.service.repository.DocumentMongoRepository;
+import org.springframework.data.mongodb.repository.Query;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+/**
+ * @author Joel Rodrigues Moreira on 01/03/18.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Repository
+public interface AdminUserDataBindingRepository extends DocumentMongoRepository {
+
+ @Query("{'owner': {'$ref' : ?#{@documentNameConfig.getNameCollectionOwner()}, '$id' : ?#{[0].getId()}}, 'user': {'$ref' : ?#{@documentNameConfig.getNameCollectionUser()}, '$id' : ?#{[1].getId()}}}")
+ List findByUser(final Owner owner, final User user);
+
+ @Query("{'owner': {'$ref' : ?#{@documentNameConfig.getNameCollectionOwner()}, '$id' : ?#{[0].getId()}}, 'user': {'$ref' : ?#{@documentNameConfig.getNameCollectionUser()}, '$id' : ?#{[1].getId()}}, 'key': '?2'}")
+ UserDataBinding findByUserAndKey(final Owner owner, final User user, final String key);
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/security/config/WebSecurityGatewayConfig.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/security/config/WebSecurityGatewayConfig.java
new file mode 100644
index 00000000..63a9a7d6
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/security/config/WebSecurityGatewayConfig.java
@@ -0,0 +1,48 @@
+package br.com.muttley.admin.server.security.config;
+
+import br.com.muttley.security.feign.UserServiceClient;
+import br.com.muttley.security.infra.component.AuthenticationTokenFilterGateway;
+import br.com.muttley.security.infra.component.UnauthorizedHandler;
+import br.com.muttley.security.zuul.gateway.AbstractWebSecurityGateway;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+
+/**
+ * @author Joel Rodrigues Moreira 23/04/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Configuration
+@EnableWebSecurity
+@EnableGlobalMethodSecurity(prePostEnabled = true)
+public class WebSecurityGatewayConfig extends AbstractWebSecurityGateway {
+
+ @Autowired
+ public WebSecurityGatewayConfig(
+ @Value("${muttley.security.jwt.controller.loginEndPoint}") final String loginEndPoint,
+ @Value("${muttley.security.jwt.controller.refreshEndPoint}") final String refreshTokenEndPoin,
+ @Value("${muttley.security.jwt.controller.forgotPassword}") final String forgotPassword,
+ @Value("${muttley.security.jwt.controller.createEndPoint}") final String createEndPoint,
+ @Value("${muttley.security.jwt.controller.resetPassword}") final String resetPassword,
+ final UnauthorizedHandler unauthorizedHandler,
+ final AuthenticationTokenFilterGateway authenticationTokenFilterGateway,
+ final UserServiceClient userServiceClient) {
+ super(loginEndPoint, refreshTokenEndPoin, forgotPassword, createEndPoint, resetPassword, unauthorizedHandler, authenticationTokenFilterGateway, userServiceClient);
+ }
+
+ @Override
+ protected String[] endPointPermitAllToGet() {
+ return new String[]{
+ "/",
+ "/*.html",
+ "/**/*.{png,jpg,jpeg,svg.ico}",
+ "/**/*.{html,css,js,svg,woff,woff2}",
+ //endpoit padrão da aplicação
+ "/login",
+ "/create-user",
+ "/home/**"};
+ }
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/security/controller/AuthenticationRestController.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/security/controller/AuthenticationRestController.java
new file mode 100644
index 00000000..a1471086
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/security/controller/AuthenticationRestController.java
@@ -0,0 +1,28 @@
+package br.com.muttley.admin.server.security.controller;
+
+import br.com.muttley.localcache.services.LocalUserAuthenticationService;
+import br.com.muttley.security.feign.auth.AuthenticationRestServiceClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author Joel Rodrigues Moreira 23/04/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@RestController
+public class AuthenticationRestController extends br.com.muttley.security.infra.controller.AuthenticationRestController {
+
+ @Autowired
+ public AuthenticationRestController(
+ @Value("${muttley.security.jwt.controller.tokenHeader:Authorization}") final String tokenHeader,
+ final AuthenticationManager authenticationManager,
+ final AuthenticationRestServiceClient authenticationRestServiceClient,
+ final ApplicationEventPublisher eventPublisher,
+ final LocalUserAuthenticationService localUserAuthenticationService) {
+ super(tokenHeader, authenticationManager, authenticationRestServiceClient, eventPublisher, localUserAuthenticationService);
+ }
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/security/controller/CreateUserController.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/security/controller/CreateUserController.java
new file mode 100644
index 00000000..f6f291f1
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/security/controller/CreateUserController.java
@@ -0,0 +1,18 @@
+package br.com.muttley.admin.server.security.controller;
+
+import br.com.muttley.security.feign.UserServiceClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationEventPublisher;
+
+/**
+ * @author Joel Rodrigues Moreira 23/04/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public class CreateUserController extends br.com.muttley.security.infra.controller.CreateUserController {
+ @Autowired
+ public CreateUserController(final ApplicationEventPublisher eventPublisher, final UserServiceClient service) {
+ super(eventPublisher, service);
+ }
+}
+
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/security/controller/UserManagerController.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/security/controller/UserManagerController.java
new file mode 100644
index 00000000..7491d8c1
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/security/controller/UserManagerController.java
@@ -0,0 +1,20 @@
+package br.com.muttley.admin.server.security.controller;
+
+import br.com.muttley.security.feign.UserServiceClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author Joel Rodrigues Moreira 23/04/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@RestController
+public class UserManagerController extends br.com.muttley.security.infra.controller.UserManagerController {
+
+ @Autowired
+ public UserManagerController(UserServiceClient service) {
+ super(service);
+ }
+
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/security/listener/UserAfterCacheLoadListener.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/security/listener/UserAfterCacheLoadListener.java
new file mode 100644
index 00000000..d5743929
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/security/listener/UserAfterCacheLoadListener.java
@@ -0,0 +1,38 @@
+package br.com.muttley.admin.server.security.listener;
+
+import br.com.muttley.admin.server.service.AdminOwnerService;
+import br.com.muttley.model.security.User;
+import br.com.muttley.model.security.events.UserAfterCacheLoadEvent;
+import br.com.muttley.model.security.preference.UserPreferences;
+import br.com.muttley.security.feign.UserPreferenceServiceClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationListener;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author Joel Rodrigues Moreira 23/04/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Component
+public class UserAfterCacheLoadListener implements ApplicationListener {
+
+ private final UserPreferenceServiceClient preferenceServiceClient;
+ private final AdminOwnerService adminOwnerService;
+
+ @Autowired
+ public UserAfterCacheLoadListener(final UserPreferenceServiceClient preferenceServiceClient, final AdminOwnerService adminOwnerService) {
+ this.preferenceServiceClient = preferenceServiceClient;
+ this.adminOwnerService = adminOwnerService;
+ }
+
+ @Override
+ public void onApplicationEvent(final UserAfterCacheLoadEvent event) {
+ final User user = event.getUser();
+ //carregando preferencias
+ final UserPreferences preferences = preferenceServiceClient.getUserPreferences();
+ final String idPassaport = (String) preferences.get(UserPreferences.OWNER_PREFERENCE).getValue();
+ user.setPreferences(preferences);
+ user.setCurrentOwner(adminOwnerService.findById1(user, idPassaport));
+ }
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/AdminOwnerService.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/AdminOwnerService.java
new file mode 100644
index 00000000..e4bb28ed
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/AdminOwnerService.java
@@ -0,0 +1,27 @@
+package br.com.muttley.admin.server.service;
+
+import br.com.muttley.domain.service.Service;
+import br.com.muttley.model.admin.AdminOwner;
+import br.com.muttley.model.security.User;
+import org.springframework.security.access.prepost.PreAuthorize;
+
+/**
+ * @author Joel Rodrigues Moreira 20/04/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public interface AdminOwnerService extends Service {
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString()" + " ) " +
+ "): " +
+ " true"
+ )
+ AdminOwner findByName(final User user, final String name);
+
+ AdminOwner findById1(final User user, final String id);
+}
+
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/AdminPassaportService.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/AdminPassaportService.java
new file mode 100644
index 00000000..0fc02c9a
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/AdminPassaportService.java
@@ -0,0 +1,60 @@
+package br.com.muttley.admin.server.service;
+
+import br.com.muttley.domain.service.Service;
+import br.com.muttley.model.admin.AdminOwner;
+import br.com.muttley.model.admin.AdminPassaport;
+import br.com.muttley.model.security.User;
+import org.springframework.security.access.prepost.PreAuthorize;
+
+import java.util.List;
+
+/**
+ * @author Joel Rodrigues Moreira 20/04/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public interface AdminPassaportService extends Service {
+ AdminPassaport findById1(final User user, final String id);
+
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString()" +
+ " ) " +
+ "or " +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', this.getBasicRoles())" +
+ " )" +
+ "): " +
+ " true"
+ )
+ AdminPassaport findByName(final AdminOwner owner, final String nome);
+
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString()" +
+ " ) " +
+ "or " +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', this.getBasicRoles())" +
+ " )" +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true"
+ )
+ List loadAllPassaports(final User user);
+
+ void removeUserFromAllPassaport(AdminOwner owner, User user);
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/AdminService.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/AdminService.java
new file mode 100644
index 00000000..143cb1e5
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/AdminService.java
@@ -0,0 +1,12 @@
+package br.com.muttley.admin.server.service;
+
+import br.com.muttley.domain.service.Service;
+import br.com.muttley.model.Document;
+
+/**
+ * @author Joel Rodrigues Moreira 20/04/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public interface AdminService extends Service {
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/AdminUserBaseService.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/AdminUserBaseService.java
new file mode 100644
index 00000000..4055219b
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/AdminUserBaseService.java
@@ -0,0 +1,49 @@
+package br.com.muttley.admin.server.service;
+
+import br.com.muttley.model.admin.AdminUserBase;
+import br.com.muttley.model.security.OwnerData;
+import br.com.muttley.model.security.User;
+import br.com.muttley.model.security.UserBaseItem;
+import br.com.muttley.model.security.UserData;
+import br.com.muttley.model.security.UserView;
+
+import java.util.Set;
+
+/**
+ * @author Joel Rodrigues Moreira on 26/11/2020.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public interface AdminUserBaseService extends AdminService {
+ AdminUserBase save(final User user, final AdminUserBase userBase);
+
+ AdminUserBase update(final User user, final AdminUserBase userBase);
+
+ AdminUserBase findFirst(final User user);
+
+ AdminUserBase save(final User user, final OwnerData owner, final AdminUserBase userBase);
+
+ boolean userNameIsAvaliableForUserName(final User user, final String userName, final Set userNames);
+
+ boolean userNameIsAvaliable(final User user, final Set userNames);
+
+ UserView findUserByEmailOrUserNameOrNickUser(final User user, final String emailOrUserName);
+
+ void addUserItemIfNotExists(final User user, final User userForAdd);
+
+ void addUserItemIfNotExists(final User user, final UserBaseItem userForAdd);
+
+ void createNewUserAndAdd(final User user, final UserBaseItem item);
+
+ void mergeUserItemIfExists(User user, final UserBaseItem item);
+
+ void removeByUserName(User user, String userName);
+
+ boolean hasBeenIncludedAnyGroup(final User user, UserData userForCheck);
+
+ boolean hasBeenIncludedAnyGroup(UserData userForCheck);
+
+ boolean hasBeenIncludedAnyGroup(final User user, final String userNameForCheck);
+
+ boolean hasBeenIncludedAnyGroup(final String userNameForCheck);
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/AdminUserDataBindingService.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/AdminUserDataBindingService.java
new file mode 100644
index 00000000..7d872612
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/AdminUserDataBindingService.java
@@ -0,0 +1,257 @@
+package br.com.muttley.admin.server.service;
+
+import br.com.muttley.model.admin.AdminUserDataBinding;
+import br.com.muttley.model.security.KeyUserDataBinding;
+import br.com.muttley.model.security.User;
+import br.com.muttley.model.security.UserData;
+import org.springframework.security.access.prepost.PreAuthorize;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author Joel Rodrigues Moreira 12/01/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public interface AdminUserDataBindingService {
+
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString() " +
+ " ) " +
+ "or " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).ROLE_USER_DATA_BINDING_CREATE.toString() " +
+ " ) " +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).ROLE_MOBILE_USER_DATA_BINDING_CREATE.toString() " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true "
+ )
+ AdminUserDataBinding save(final User user, final AdminUserDataBinding dataBinding);
+
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString() " +
+ " ) " +
+ "or " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).ROLE_USER_DATA_BINDING_UPDATE.toString() " +
+ " )" +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).ROLE_MOBILE_USER_DATA_BINDING_UPDATE.toString() " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true "
+ )
+ AdminUserDataBinding update(final User user, final AdminUserDataBinding dataBinding);
+
+ /**
+ * Lista os itens levando em consideração não o usuário da requisição,
+ * mas sim o userName informado
+ *
+ * @param user -> usuário da requisição corrente
+ * @param userName -> nome de usuário desejado
+ */
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString() " +
+ " ) " +
+ "or " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).ROLE_USER_DATA_BINDING_OTHERS_USERS_MERGE.toString() " +
+ " ) " +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).ROLE_MOBILE_USER_DATA_BINDING_OTHERS_USERS_MERGE.toString()" +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true "
+ )
+ List listByUserName(final User user, final String userName);
+
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString() " +
+ " ) " +
+ "or " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).ROLE_USER_DATA_BINDING_READ.toString() " +
+ " ) " +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).ROLE_MOBILE_USER_DATA_BINDING_READ.toString() " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true "
+ )
+ List listBy(final User user);
+
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString() " +
+ " ) " +
+ "or " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).ROLE_USER_DATA_BINDING_OTHERS_USERS_MERGE.toString() " +
+ " ) " +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).ROLE_MOBILE_USER_DATA_BINDING_OTHERS_USERS_MERGE.toString()" +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true "
+ )
+ AdminUserDataBinding saveByUserName(final User user, final String userName, final AdminUserDataBinding value);
+
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString() " +
+ " ) " +
+ "or " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).ROLE_USER_DATA_BINDING_OTHERS_USERS_MERGE.toString() " +
+ " ) " +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).ROLE_MOBILE_USER_DATA_BINDING_OTHERS_USERS_MERGE.toString()" +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true "
+ )
+ AdminUserDataBinding updateByUserName(final User user, final String userName, final AdminUserDataBinding dataBinding);
+
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString() " +
+ " ) " +
+ "or " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).ROLE_USER_DATA_BINDING_OTHERS_USERS_MERGE.toString() " +
+ " ) " +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).ROLE_MOBILE_USER_DATA_BINDING_OTHERS_USERS_MERGE.toString()" +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true "
+ )
+ void merge(final User user, final String userName, final AdminUserDataBinding dataBinding);
+
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString() " +
+ " ) " +
+ "or " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).ROLE_USER_DATA_BINDING_OTHERS_USERS_MERGE.toString() " +
+ " ) " +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).ROLE_MOBILE_USER_DATA_BINDING_OTHERS_USERS_MERGE.toString()" +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true "
+ )
+ void merge(final User user, final String userName, final Set dataBindings);
+
+ AdminUserDataBinding getKey(final User user, final KeyUserDataBinding key);
+
+ AdminUserDataBinding getKey(final User user, final String key);
+
+ AdminUserDataBinding getKeyByUserName(final User user, final String userName, final KeyUserDataBinding key);
+
+ AdminUserDataBinding getKeyByUserName(final User user, final String userName, final String key);
+
+ boolean contains(final User user, final KeyUserDataBinding key);
+
+ boolean contains(final User user, final String key);
+
+ boolean containsByUserNameAndKey(final User user, final String userName, final KeyUserDataBinding key);
+
+ boolean containsByUserNameAndKey(final User user, final String userName, final String key);
+
+ /**
+ * Verifica se uma determina chave e valor já esta reservado para algum usuário
+ */
+ boolean containsByKeyAndValue(final User user, final KeyUserDataBinding key, final String value);
+
+ /**
+ * Verifica se uma determina chave e valor já esta reservado para algum usuário
+ */
+ boolean containsByKeyAndValue(final User user, final String key, final String value);
+
+ /**
+ * Verifica se uma determina chave e valor já esta reservado para algum usuário diferente do username informado
+ */
+ boolean containsByKeyAndValueAndUserNameNotEq(final User user, final String userName, final KeyUserDataBinding key, final String value);
+
+ /**
+ * Verifica se uma determina chave e valor já esta reservado para algum usuário diferente do username informado
+ */
+ boolean containsByKeyAndValueAndUserNameNotEq(final User user, final String userName, final String key, final String value);
+
+ UserData getUserBy(final User user, final KeyUserDataBinding key, final String value);
+
+ UserData getUserBy(final User user, final String key, final String value);
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/NoSecurityAdminOwnerService.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/NoSecurityAdminOwnerService.java
new file mode 100644
index 00000000..ab9b7375
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/NoSecurityAdminOwnerService.java
@@ -0,0 +1,17 @@
+package br.com.muttley.admin.server.service;
+
+import br.com.muttley.model.admin.AdminOwner;
+
+/**
+ * Essa classe deve ser utilizada apenas para configuração automatica do sistema,
+ * A mesma não contem validação de negócio nem mesmo de segurança
+ *
+ * @author Joel Rodrigues Moreira 23/04/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public interface NoSecurityAdminOwnerService {
+ AdminOwner findByName(final String nome);
+
+ AdminOwner save(final AdminOwner owner);
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/NoSecurityAdminPassaportService.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/NoSecurityAdminPassaportService.java
new file mode 100644
index 00000000..4a2b60be
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/NoSecurityAdminPassaportService.java
@@ -0,0 +1,15 @@
+package br.com.muttley.admin.server.service;
+
+import br.com.muttley.model.admin.AdminPassaport;
+
+/**
+ * Essa classe deve ser utilizada apenas para configuração automatica do sistema,
+ * A mesma não contem validação de negócio nem mesmo de segurança
+ *
+ * @author Joel Rodrigues Moreira 23/04/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public interface NoSecurityAdminPassaportService {
+ AdminPassaport save(final AdminPassaport passaport);
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/impl/AbstractNoSecurityService.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/impl/AbstractNoSecurityService.java
new file mode 100644
index 00000000..99864dce
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/impl/AbstractNoSecurityService.java
@@ -0,0 +1,35 @@
+package br.com.muttley.admin.server.service.impl;
+
+import br.com.muttley.domain.service.Validator;
+import br.com.muttley.exception.throwables.MuttleyException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * @author Joel Rodrigues Moreira 23/04/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public class AbstractNoSecurityService {
+ @Autowired
+ protected Validator validator;
+
+ /**
+ * O método verifica se estamos executanto dentro de um contexto de requisição HTTP
+ *
+ * Caso estejamos em uma requisição, devemos lançar uma exception
+ */
+ protected void validateContext() {
+ try {
+ //se esssa linha for executada sem lançar uma exception, é sinal que o serviço está trabalhando sobre um contexto http
+ final HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
+ //lançando a exception necessária
+ throw new MuttleyException("ATENÇÃO, ESSE SERVIÇO NÃO PODE SER USADO EM CONTEXTO DE REQUISIÇÃO!");
+ } catch (IllegalStateException ex) {
+ //se chegou aqui está tudo ok podemos proceguir
+ }
+ }
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/impl/AdminOwnerServiceImpl.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/impl/AdminOwnerServiceImpl.java
new file mode 100644
index 00000000..63503a24
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/impl/AdminOwnerServiceImpl.java
@@ -0,0 +1,41 @@
+package br.com.muttley.admin.server.service.impl;
+
+import br.com.muttley.admin.server.repository.AdminOwnerRepository;
+import br.com.muttley.admin.server.service.AdminOwnerService;
+import br.com.muttley.exception.throwables.MuttleyNotFoundException;
+import br.com.muttley.model.admin.AdminOwner;
+import br.com.muttley.model.security.Owner;
+import br.com.muttley.model.security.User;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author Joel Rodrigues Moreira 20/04/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Service
+public class AdminOwnerServiceImpl extends AdminServiceImpl implements AdminOwnerService {
+ private final AdminOwnerRepository repository;
+
+ @Autowired
+ public AdminOwnerServiceImpl(final AdminOwnerRepository repository, final MongoTemplate mongoTemplate) {
+ super(repository, mongoTemplate, AdminOwner.class);
+ this.repository = repository;
+ }
+
+ @Override
+ public AdminOwner findByName(final User user, final String name) {
+ final AdminOwner owner = this.repository.findByName(name);
+ if (owner == null) {
+ throw new MuttleyNotFoundException(Owner.class, "name", "Registro não encontrado");
+ }
+ return owner;
+ }
+
+ @Override
+ public AdminOwner findById1(final User user, final String id) {
+ return super.findById(user, id);
+ }
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/impl/AdminPassaportServiceImpl.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/impl/AdminPassaportServiceImpl.java
new file mode 100644
index 00000000..af7dcc29
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/impl/AdminPassaportServiceImpl.java
@@ -0,0 +1,114 @@
+package br.com.muttley.admin.server.service.impl;
+
+import br.com.muttley.admin.server.config.model.DocumentNameConfig;
+import br.com.muttley.admin.server.repository.AdminPassaportRepository;
+import br.com.muttley.admin.server.service.AdminPassaportService;
+import br.com.muttley.exception.throwables.MuttleyNoContentException;
+import br.com.muttley.exception.throwables.MuttleyNotFoundException;
+import br.com.muttley.model.admin.AdminOwner;
+import br.com.muttley.model.admin.AdminPassaport;
+import br.com.muttley.model.security.User;
+import com.mongodb.BasicDBObject;
+import com.mongodb.DBRef;
+import org.bson.BsonDocument;
+import org.bson.BsonElement;
+import org.bson.BsonObjectId;
+import org.bson.BsonString;
+import org.bson.types.ObjectId;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.aggregation.AggregationResults;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.data.mongodb.core.query.Update;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+
+import java.util.List;
+
+import static java.util.Arrays.asList;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.match;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.newAggregation;
+import static org.springframework.data.mongodb.core.query.Criteria.where;
+import static org.springframework.data.mongodb.core.query.Query.query;
+
+/**
+ * @author Joel Rodrigues Moreira 20/04/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Service
+public class AdminPassaportServiceImpl extends AdminServiceImpl implements AdminPassaportService {
+ private final DocumentNameConfig documentNameConfig;
+
+ @Autowired
+ public AdminPassaportServiceImpl(AdminPassaportRepository repository, final MongoTemplate mongoTemplate, final DocumentNameConfig documentNameConfig) {
+ super(repository, mongoTemplate, AdminPassaport.class);
+ this.documentNameConfig = documentNameConfig;
+ }
+
+ @Override
+ public AdminPassaport findById1(final User user, final String id) {
+ return super.findById(user, id);
+ }
+
+ @Override
+ public AdminPassaport findByName(final AdminOwner owner, final String name) {
+ final AdminPassaport owt = this.mongoTemplate.findOne(
+ query(
+ where("owner.$id").is(owner.getId()).and("name").is(name)
+ ), AdminPassaport.class
+ );
+ if (owt == null) {
+ throw new MuttleyNotFoundException(AdminPassaport.class, "name", "Registro não encontrado");
+ }
+ return owt;
+ }
+
+ @Override
+ public List loadAllPassaports(final User user) {
+ /*
+ db['odin-work-teams'].aggregate(
+ {
+ "$match":{
+ "owner.$id": ObjectId("5a984453aa9f6634ac2e461c"),
+ "members": {"$in":[{"$ref":"users", "$id":ObjectId("5a984453aa9f6634ac2e461d")}]}
+ }
+ }
+ )
+ */
+ AggregationResults result = this.mongoTemplate.aggregate(
+ newAggregation(
+ match(
+ where("members")
+ .in(new BsonDocument(
+ asList(
+ new BsonElement("$ref", new BsonString("users")),
+ new BsonElement("$id", new BsonObjectId(new ObjectId(user.getId())))
+ )
+ )
+ )
+ )
+ ),
+ this.documentNameConfig.getNameCollectionAdminPassaport(),
+ AdminPassaport.class);
+
+ final List list = result.getMappedResults();
+ if (CollectionUtils.isEmpty(list)) {
+ throw new MuttleyNoContentException(AdminPassaport.class, null, "Nenhum grupo de trabalho encontrado");
+ }
+ return list;
+ }
+
+ @Override
+ public void removeUserFromAllPassaport(final AdminOwner owner, final User user) {
+ this.mongoTemplate.updateMulti(
+ new Query(
+ where("owner.$id").is(owner.getObjectId())
+ ),
+ new Update().pull("members", new BasicDBObject("$in", asList(
+ new DBRef(this.documentNameConfig.getNameCollectionUser(), user.getObjectId())
+ ))),
+ AdminPassaport.class
+ );
+ }
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/impl/AdminServiceImpl.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/impl/AdminServiceImpl.java
new file mode 100644
index 00000000..1f05ea25
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/impl/AdminServiceImpl.java
@@ -0,0 +1,18 @@
+package br.com.muttley.admin.server.service.impl;
+
+import br.com.muttley.admin.server.service.AdminService;
+import br.com.muttley.domain.service.impl.ServiceImpl;
+import br.com.muttley.model.Document;
+import br.com.muttley.mongo.service.repository.DocumentMongoRepository;
+import org.springframework.data.mongodb.core.MongoTemplate;
+
+/**
+ * @author Joel Rodrigues Moreira 20/04/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public abstract class AdminServiceImpl extends ServiceImpl implements AdminService {
+ public AdminServiceImpl(final DocumentMongoRepository repository, final MongoTemplate mongoTemplate, final Class clazz) {
+ super(repository, mongoTemplate, clazz);
+ }
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/impl/AdminUserBaseServiceImpl.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/impl/AdminUserBaseServiceImpl.java
new file mode 100644
index 00000000..b715951e
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/impl/AdminUserBaseServiceImpl.java
@@ -0,0 +1,529 @@
+package br.com.muttley.admin.server.service.impl;
+
+import br.com.muttley.admin.server.config.model.DocumentNameConfig;
+import br.com.muttley.admin.server.service.AdminPassaportService;
+import br.com.muttley.admin.server.service.AdminUserBaseService;
+import br.com.muttley.admin.server.service.AdminUserDataBindingService;
+import br.com.muttley.exception.throwables.MuttleyBadRequestException;
+import br.com.muttley.exception.throwables.MuttleyException;
+import br.com.muttley.model.BasicAggregateResultCount;
+import br.com.muttley.model.MetadataDocument;
+import br.com.muttley.model.TimeZoneDocument;
+import br.com.muttley.model.VersionDocument;
+import br.com.muttley.model.admin.AdminOwner;
+import br.com.muttley.model.admin.AdminUserBase;
+import br.com.muttley.model.security.Owner;
+import br.com.muttley.model.security.OwnerData;
+import br.com.muttley.model.security.User;
+import br.com.muttley.model.security.UserBase;
+import br.com.muttley.model.security.UserBaseItem;
+import br.com.muttley.model.security.UserData;
+import br.com.muttley.model.security.UserView;
+import br.com.muttley.mongo.service.infra.AggregationUtils;
+import br.com.muttley.mongo.service.infra.metadata.EntityMetaData;
+import br.com.muttley.security.feign.UserServiceClient;
+import com.mongodb.BasicDBObject;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.experimental.Accessors;
+import org.bson.types.ObjectId;
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.aggregation.Aggregation;
+import org.springframework.data.mongodb.core.aggregation.AggregationResults;
+import org.springframework.data.mongodb.core.mapping.DBRef;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.data.mongodb.core.query.Update;
+import org.springframework.stereotype.Service;
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+
+import javax.validation.constraints.NotNull;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.TimeZone;
+
+import static br.com.muttley.model.security.Role.ROLE_USER_BASE_CREATE;
+import static br.com.muttley.utils.TimeZoneUtils.getTimezoneFromId;
+import static java.util.Arrays.asList;
+import static java.util.Objects.isNull;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.group;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.lookup;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.match;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.newAggregation;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.project;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.unwind;
+import static org.springframework.data.mongodb.core.query.Criteria.where;
+import static org.springframework.data.mongodb.core.query.Update.Position.FIRST;
+
+/**
+ * @author Joel Rodrigues Moreira on 27/04/2021.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Service
+public class AdminUserBaseServiceImpl extends AdminServiceImpl implements AdminUserBaseService {
+ private static final String[] basicRoles = new String[]{ROLE_USER_BASE_CREATE.getSimpleName()};
+ private final UserServiceClient userService;
+ //private final AdminUserDataBindingService dataBindingService;
+ private final DocumentNameConfig documentNameConfig;
+ private final AdminPassaportService passaportService;
+
+
+ @Autowired
+ public AdminUserBaseServiceImpl(
+ final MongoTemplate template,
+ final UserServiceClient userService,
+ final AdminUserDataBindingService dataBindingService,
+ final DocumentNameConfig documentNameConfig,
+ final AdminPassaportService passaportService) {
+ super(null, template, AdminUserBase.class);
+ this.userService = userService;
+ // this.dataBindingService = dataBindingService;
+ this.documentNameConfig = documentNameConfig;
+ this.passaportService = passaportService;
+ }
+
+ @Override
+ public String[] getBasicRoles() {
+ return basicRoles;
+ }
+
+ @Override
+ public void checkPrecondictionSave(final User user, final AdminUserBase value) {
+ final AggregationResults result = this.mongoTemplate.aggregate(
+ newAggregation(
+ AggregationUtils.createAggregationsCount(EntityMetaData.of(AdminUserBase.class), null,
+ addOwnerQueryParam(value.getOwner(), null)
+ )),
+ this.clazz, ResultCount.class);
+ if ((result.getUniqueMappedResult() != null ? ((ResultCount) result.getUniqueMappedResult()).getCount() : 0) == 1) {
+ throw new MuttleyBadRequestException(UserBase.class, null, "Já existe uma base de usuário cadastrada no sistema");
+ }
+ }
+
+ @Override
+ public AdminUserBase save(final User user, final AdminUserBase value) {
+ //verificando se realmente está criando um novo registro
+ checkIdForSave(value);
+ //garantindo que o metadata ta preenchido
+ //this.createMetaData(user, value);
+ try {
+ this.metadataService.generateNewMetadataFor(user, value);
+ } catch (BeanCreationException ex) {
+ this.generateLocalMetadata(user, value);
+ }
+ //processa regra de negocio antes de qualquer validação
+ this.beforeSave(user, value);
+ //verificando precondições
+ this.checkPrecondictionSave(user, value);
+ //validando dados do objeto
+ this.validator.validate(value);
+ this.mongoTemplate.save(value, this.documentNameConfig.getNameCollectionAdminUserBase());
+ final AdminUserBase otherValue = this.mongoTemplate.findOne(new Query(), AdminUserBase.class, this.documentNameConfig.getNameCollectionAdminUserBase());
+ //realizando regras de enegocio depois do objeto ter sido salvo
+ this.afterSave(user, otherValue);
+ //valor salvo
+ return otherValue;
+ }
+
+ @Override
+ public AdminUserBase update(User user, AdminUserBase value) {
+ //verificando se realmente está alterando um registro
+ this.checkIdForUpdate(value);
+ //verificando se o registro realmente existe
+ if (this.mongoTemplate.findById(value.getId(), AdminUserBase.class, documentNameConfig.getNameCollectionAdminUserBase()) == null) {
+ throw this.createNotFoundExceptionById(user, value.getId());
+ //throw new MuttleyNotFoundException(clazz, "id", "Registro não encontrado");
+ }
+ //gerando metadata de alteração0
+
+
+ final AggregationResults result = mongoTemplate.aggregate(
+ newAggregation(
+ match(
+ where("_id").is(value.getObjectId())
+ ), project().and("$metadata.timeZones").as("timeZones")
+ .and("$metadata.versionDocument").as("versionDocument")
+ .and("$metadata.historic").as("historic")
+ ), documentNameConfig.getNameCollectionAdminUserBase(), MetadataDocument.class);
+
+ final MetadataDocument meta = result.getUniqueMappedResult() != null ? ((MetadataDocument) result.getUniqueMappedResult()) : null;
+
+
+ try {
+ this.metadataService.generateMetaDataUpdateFor(user, meta, value);
+ } catch (Throwable ex) {
+ ex.printStackTrace();
+ }
+ //processa regra de negocio antes de qualquer validação
+ beforeUpdate(user, value);
+ //verificando precondições
+ checkPrecondictionUpdate(user, value);
+ //validando dados
+ this.validator.validate(value);
+ this.mongoTemplate.save(value, documentNameConfig.getNameCollectionAdminUserBase());
+ final AdminUserBase otherValue = value;
+ //realizando regras de enegocio depois do objeto ter sido alterado
+ afterUpdate(user, value);
+ return otherValue;
+ }
+
+ @Override
+ public AdminUserBase findFirst(User user) {
+ final AdminUserBase result = mongoTemplate.findOne(new Query(), AdminUserBase.class);
+ if (isNull(result)) {
+ throw this.createNotFoundExceptionById(user).setField("user");
+ //*throw new MuttleyNotFoundException(clazz, "user", "Nenhum registro encontrado");
+ }
+ return result;
+ }
+
+ /**
+ * Gera o metadatalocal caso estejamos fora do contexto de requisição
+ */
+ private void generateLocalMetadata(final User user, final AdminUserBase value) {
+ final MetadataDocument metadataDocument = new MetadataDocument(user);
+ final TimeZone tz = TimeZone.getDefault();
+ final String timezone = getTimezoneFromId(tz.getID());
+ metadataDocument.setTimeZones(new TimeZoneDocument(timezone, timezone, timezone, timezone));
+ metadataDocument.setVersionDocument(new VersionDocument());
+ value.setMetadata(metadataDocument);
+ }
+
+ //@Override
+ public void checkPrecondictionSave(final User user, final OwnerData owner, final AdminUserBase value) {
+ /*if (this.count(user, owner, null) == 1) {
+ throw new MuttleyBadRequestException(UserBase.class, null, "Já existe uma base de usuário cadastrada no sistema");
+ }*/
+ if (owner != null) {
+ throw new MuttleyException();
+ }
+ final AggregationResults result = this.mongoTemplate.aggregate(
+ newAggregation(
+ AggregationUtils.createAggregationsCount(EntityMetaData.of(AdminUserBase.class), null,
+ addOwnerQueryParam(owner, null)
+ )),
+ this.clazz, ResultCount.class);
+
+ if ((result.getUniqueMappedResult() != null ? ((ResultCount) result.getUniqueMappedResult()).getCount() : 0) == 1) {
+ throw new MuttleyBadRequestException(AdminUserBase.class, null, "Já existe uma base de usuário cadastrada no sistema");
+ }
+ }
+
+ private final Map addOwnerQueryParam(final OwnerData owner, final Map queryParams) {
+ final Map query = new LinkedHashMap<>(1);
+ query.put("owner.$id.$is", owner.getObjectId().toString());
+ if (queryParams != null) {
+ query.putAll(queryParams);
+ }
+ return query;
+ }
+
+
+ @Override
+ public void checkPrecondictionDelete(final User user, final String id) {
+ throw new MuttleyBadRequestException(Owner.class, "id", "Não é possível deletar a base de usuário");
+ }
+
+ @Override
+ public AdminUserBase save(final User user, final OwnerData owner, final AdminUserBase userBase) {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public boolean userNameIsAvaliableForUserName(final User user, final String userName, final Set userNames) {
+ //return this.userService.userNameIsAvaliableForUserName(userName, userNames);
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public boolean userNameIsAvaliable(final User user, final Set userNames) {
+ return this.userService.userNameIsAvaliable(userNames);
+ }
+
+ @Override
+ public UserView findUserByEmailOrUserNameOrNickUser(final User user, final String emailOrUserName) {
+ return new UserView(this.userService.findUserByEmailOrUserNameOrNickUsers(emailOrUserName, emailOrUserName, new HashSet<>(asList(emailOrUserName))));
+ }
+
+ @Override
+ public void addUserItemIfNotExists(final User user, final User userForAdd) {
+ this.addUserItemIfNotExists(user, new UserBaseItem(user, userForAdd, null, new Date(), true, null));
+ }
+
+ @Override
+ public void addUserItemIfNotExists(final User user, final UserBaseItem userForAdd) {
+ if (!this.hasBeenIncludedAnyGroup(user, userForAdd.getUser())) {
+ userForAdd.setAddedBy(user);
+ if (userForAdd.getDtCreate() == null) {
+ userForAdd.setDtCreate(new Date());
+ }
+ if (userForAdd.getAddedBy() == null) {
+ userForAdd.setAddedBy(user);
+ }
+ this.validator.validate(userForAdd);
+ if (this.hasBeenIncludedAnyGroup(user, userForAdd.getUser())) {
+ throw new MuttleyBadRequestException(UserBase.class, "users", "Usuário já está presente na base");
+ }
+ this.mongoTemplate.updateFirst(
+ new Query(
+ where("owner.$id").is(user.getCurrentOwner().getObjectId())
+ ),
+ new Update()
+ .push("users")
+ .atPosition(FIRST)
+ .each(userForAdd),
+ UserBase.class
+ );
+ } else {
+ this.mongoTemplate.updateMulti(
+ new Query(
+ where("owner.$id").is(user.getCurrentOwner().getObjectId())
+ .and("users.user.$id").is(new ObjectId(userForAdd.getUser().getId()))
+ ),
+ new Update().set("users.$.status", userForAdd.isStatus()),
+ UserBase.class
+ );
+ }
+ }
+
+ @Override
+ public void createNewUserAndAdd(final User user, final UserBaseItem item) {
+ final User userForSave = new User(item.getUserInfoForMerge());
+ /*if (!item.dataBindingsIsEmpty()) {
+ item.getDataBindings().forEach(it -> {
+ if (it.getKey().isUnique()) {
+ if (this.dataBindingService.containsByKeyAndValueAndUserNameNotEq(user, userForSave.getUserName(), it.getKey(), it.getValue())) {
+ throw new MuttleyBadRequestException(UserDataBinding.class, "key", "Já existe um usuário que possui ligação com " + it.getKey().getDisplayKey() + " informado(a)");
+ }
+ }
+ });
+ }*/
+
+ final User salvedUser = userService.save(item.getUserInfoForMerge(), "true");
+
+ /*if (!item.dataBindingsIsEmpty()) {
+ this.dataBindingService.merge(user, salvedUser.getUserName(), item.getDataBindings().parallelStream().map(AdminUserDataBinding::new).collect(Collectors.toSet()));
+ }*/
+ this.addUserItemIfNotExists(user, salvedUser);
+ }
+
+ @Override
+ public void mergeUserItemIfExists(final User user, final UserBaseItem item) {
+ /*
+ item.setUser(userService.update(user, new User(item.getUserInfoForMerge())));
+ if (!item.dataBindingsIsEmpty()) {
+ this.dataBindingService.merge(user, item.getUser().getUserName(), item.getDataBindings());
+ }
+ this.addUserItemIfNotExists(user, item);*/
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public void removeByUserName(final User user, final String userName) {
+ final User userLoaded = this.userService.findByUserName(userName);
+ this.mongoTemplate.updateFirst(
+ new Query(
+ where("owner.$id").is(user.getCurrentOwner().getObjectId())
+ ),
+ new Update()
+ .pull("users", new BasicDBObject("user.$id", new ObjectId(userLoaded.getId()))),
+ UserBase.class
+ );
+ this.passaportService.removeUserFromAllPassaport((AdminOwner) user.getCurrentOwner(), userLoaded);
+ }
+
+ @Override
+ public boolean hasBeenIncludedAnyGroup(final User user, final UserData userForCheck) {
+ return this.mongoTemplate.exists(
+ new Query(
+ where("owner.$id").is(user.getCurrentOwner().getObjectId())
+ .and("users.user.$id").is(new ObjectId(userForCheck.getId()))
+ ), UserBase.class
+ );
+ }
+
+ @Override
+ public boolean hasBeenIncludedAnyGroup(final UserData userForCheck) {
+ return this.mongoTemplate.exists(
+ new Query(
+ /*where("owner.$id").is(user.getCurrentOwner().getObjectId())*/
+ where("users.user.$id").is(new ObjectId(userForCheck.getId()))
+ ), UserBase.class
+ );
+ }
+
+ @Override
+ public boolean hasBeenIncludedAnyGroup(final User user, final String userNameForCheck) {
+ /**
+ * db.getCollection("muttley-users-base").aggregate([
+ * {$match:{"owner.$id":ObjectId("5e28b3e3637e580001e465d6")}},
+ * {$unwind:"$users"},
+ * {$project:{user:{$objectToArray:"$users.user"}}},
+ * {$project:{user:{$arrayElemAt:["$user.v",1]}}},
+ * {$lookup:{
+ * from:"muttley-users",
+ * localField:"user",
+ * foreignField:"_id",
+ * as:"user"
+ * }},
+ * {$unwind:"$user"},
+ * {$match:{
+ * $or:[
+ * {"user.userName":"asdfasd234asdfasdf"},
+ * {"user.email":"df7899@gmail.com"},
+ * {"user.nickUsers":{$in:["asdfas"]}},
+ * ]
+ * }},
+ * {$group:{_id:"$user"}},
+ * {$count:"result"}
+ * ])
+ */
+ final AggregationResults results = this.mongoTemplate.aggregate(
+ newAggregation(
+ match(where("owner.$id").is(user.getCurrentOwner().getObjectId())),
+ unwind("$users"),
+ project().and(context -> new BasicDBObject("$objectToArray", "$users.user")).as("user"),
+ project().and(context -> new BasicDBObject("$arrayElemAt", asList("$user.v", 1))).as("user"),
+ lookup(
+ this.documentNameConfig.getNameCollectionUser(),
+ "user",
+ "_id",
+ "user"
+ ),
+ unwind("$user"),
+ match(new Criteria().orOperator(
+ where("user.userName").is(userNameForCheck),
+ where("user.email").is(userNameForCheck),
+ where("user.nickUsers").in(asList(userNameForCheck))
+ )),
+ group("$user"),
+ Aggregation.count().as("result")
+ ),
+ this.documentNameConfig.getNameCollectionUserBase(),
+ BasicAggregateResultCount.class
+ );
+ if (results == null || results.getUniqueMappedResult() == null || results.getUniqueMappedResult().getResult() == 0) {
+ return false;
+ }
+ if (results.getUniqueMappedResult().getResult() > 1) {
+ //não pode retornar mais de um usuário
+ throw new MuttleyException("Erro interno");
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean hasBeenIncludedAnyGroup(final String userNameForCheck) {
+ /**
+ * db.getCollection("muttley-users-base").aggregate([
+ * {$unwind:"$users"},
+ * {$project:{user:{$objectToArray:"$users.user"}}},
+ * {$project:{user:{$arrayElemAt:["$user.v",1]}}},
+ * {$lookup:{
+ * from:"muttley-users",
+ * localField:"user",
+ * foreignField:"_id",
+ * as:"user"
+ * }},
+ * {$unwind:"$user"},
+ * {$match:{
+ * $or:[
+ * {"user.userName":"asdfasd234asdfasdf"},
+ * {"user.email":"df7899@gmail.com"},
+ * {"user.nickUsers":{$in:["asdfas"]}},
+ * ]
+ * }},
+ * {$group:{_id:"$user"}},
+ * {$count:"result"}
+ * ])
+ */
+ final AggregationResults results = this.mongoTemplate.aggregate(
+ newAggregation(
+ unwind("$users"),
+ project().and(context -> new BasicDBObject("$objectToArray", "$users.user")).as("user"),
+ project().and(context -> new BasicDBObject("$arrayElemAt", asList("$user.v", 1))).as("user"),
+ lookup(
+ this.documentNameConfig.getNameCollectionUser(),
+ "user",
+ "_id",
+ "user"
+ ),
+ unwind("$user"),
+ match(new Criteria().orOperator(
+ where("user.userName").is(userNameForCheck),
+ where("user.email").is(userNameForCheck),
+ where("user.nickUsers").in(asList(userNameForCheck))
+ )),
+ group("$user"),
+ Aggregation.count().as("result")
+ ),
+ UserBase.class,
+ BasicAggregateResultCount.class
+ );
+ if (results == null || results.getUniqueMappedResult() == null || results.getUniqueMappedResult().getResult() == 0) {
+ return false;
+ }
+ if (results.getUniqueMappedResult().getResult() > 1) {
+ //não pode retornar mais de um usuário
+ throw new MuttleyException("Erro interno");
+ }
+
+ return true;
+ }
+
+ /* */
+
+ /**
+ * Verifica se o usuário já existe na base
+ *//*
+ private boolean userHasBeenIncluded(final User user, final User userForCheck) {
+ return this.userHasBeenIncluded(user, userForCheck.getId());
+ }
+
+ private boolean userHasBeenIncluded(final User user, final String id) {
+ return this.mongoTemplate.exists(
+ new Query(
+ where("owner.$id").is(user.getCurrentOwner().getObjectId())
+ .and("users.user.$id").is(new ObjectId(id))
+ ), UserBase.class
+ );
+ }*/
+
+ @Getter
+ @Setter
+ @Accessors(chain = true)
+ private static class UserItemForAdd {
+ @DBRef
+ @NotNull(message = "Informe o usuário que está efetuando essa operação")
+ private User addedBy;
+
+ @DBRef
+ @NotNull(message = "Informe o usuário participante da base")
+ private User user;
+
+ @NotNull
+ private Date dtCreate;
+
+ private boolean status;
+ }
+
+ protected final class ResultCount {
+ private Long count;
+
+ public Long getCount() {
+ return count;
+ }
+
+ public ResultCount setCount(final Long count) {
+ this.count = count;
+ return this;
+ }
+ }
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/impl/AdminUserDataBindingServiceImpl.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/impl/AdminUserDataBindingServiceImpl.java
new file mode 100644
index 00000000..770804d1
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/impl/AdminUserDataBindingServiceImpl.java
@@ -0,0 +1,628 @@
+package br.com.muttley.admin.server.service.impl;
+
+import br.com.muttley.admin.server.config.model.DocumentNameConfig;
+import br.com.muttley.admin.server.repository.AdminUserDataBindingRepository;
+import br.com.muttley.admin.server.service.AdminUserDataBindingService;
+import br.com.muttley.domain.service.Validator;
+import br.com.muttley.exception.throwables.MuttleyBadRequestException;
+import br.com.muttley.exception.throwables.MuttleyConflictException;
+import br.com.muttley.headers.services.MetadataService;
+import br.com.muttley.model.BasicAggregateResult;
+import br.com.muttley.model.BasicAggregateResultCount;
+import br.com.muttley.model.admin.AdminUserDataBinding;
+import br.com.muttley.model.security.KeyUserDataBinding;
+import br.com.muttley.model.security.Owner;
+import br.com.muttley.model.security.User;
+import br.com.muttley.model.security.UserData;
+import br.com.muttley.model.security.events.UserResolverEvent;
+import com.mongodb.BasicDBObject;
+import io.jsonwebtoken.lang.Collections;
+import org.bson.types.ObjectId;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
+import org.springframework.data.mongodb.core.aggregation.AggregationResults;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import static java.util.Arrays.asList;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.count;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.lookup;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.match;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.newAggregation;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.project;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.unwind;
+import static org.springframework.data.mongodb.core.query.Criteria.where;
+
+/**
+ * @author Joel Rodrigues Moreira 12/01/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Service
+public class AdminUserDataBindingServiceImpl implements AdminUserDataBindingService {
+ @Autowired
+ protected MetadataService metadataService;
+ private final MongoTemplate mongoTemplate;
+ private final AdminUserDataBindingRepository repository;
+ private final DocumentNameConfig documentNameConfig;
+ private final Validator validator;
+ //private final UserService userService;
+ private final ApplicationEventPublisher eventPublisher;
+ @Value("${muttley.security.check-roles:false}")
+ private boolean checkRoles;
+
+ //private final LocalDatabindingService localDatabindingService;
+
+ @Autowired
+ public AdminUserDataBindingServiceImpl(final MongoTemplate mongoTemplate, final AdminUserDataBindingRepository repository, final DocumentNameConfig documentNameConfig, final Validator validator, final ApplicationEventPublisher eventPublisher /*final LocalDatabindingService localDatabindingService*/) {
+ this.mongoTemplate = mongoTemplate;
+ this.repository = repository;
+ this.documentNameConfig = documentNameConfig;
+ this.validator = validator;
+ this.eventPublisher = eventPublisher;
+ //this.localDatabindingService = localDatabindingService;
+ }
+
+ public boolean isCheckRole() {
+ return checkRoles;
+ }
+
+ @Override
+ public AdminUserDataBinding save(final User user, final AdminUserDataBinding dataBinding) {
+ if (dataBinding.getUser() == null) {
+ dataBinding.setUser(user);
+ }
+ checkBasicInfos(user, dataBinding);
+ checkPrecondictionSave(user, dataBinding);
+ final AdminUserDataBinding salved = repository.save(dataBinding);
+ //this.localDatabindingService.expireUserDataBindings(user);
+ return salved;
+ }
+
+ public void checkPrecondictionSave(final User user, final AdminUserDataBinding dataBinding) {
+ if (!user.equals(dataBinding.getUser())) {
+ throw new MuttleyBadRequestException(AdminUserDataBinding.class, "user", "O usuário informado é diferente do da requisição!");
+ }
+ //verificando se já não existe um registro com as informações
+ this.checkIndex(user, dataBinding);
+ this.validator.validate(dataBinding);
+ }
+
+ @Override
+ public AdminUserDataBinding update(final User user, final AdminUserDataBinding dataBinding) {
+ if (dataBinding.getUser() == null) {
+ dataBinding.setUser(user);
+ }
+ checkBasicInfos(user, dataBinding);
+ this.checkPrecondictionUpdate(user, dataBinding);
+ final AdminUserDataBinding salved = repository.save(dataBinding);
+ //this.localDatabindingService.expireUserDataBindings(user);
+ return salved;
+ }
+
+ public void checkPrecondictionUpdate(final User user, final AdminUserDataBinding dataBinding) {
+ if (!user.equals(dataBinding.getUser())) {
+ throw new MuttleyBadRequestException(AdminUserDataBinding.class, "user", "O usuário informado é diferente do da requisição!");
+ }
+ //verificando se já não existe um registro com as informações
+ this.checkIndex(user, dataBinding);
+ this.validator.validate(dataBinding);
+ }
+
+ @Override
+ public List listByUserName(final User user, final String userName) {
+ if (user.getUserName().equals(userName)) {
+ return this.listBy(user);
+ }
+ /**
+ * db.getCollection("muttley-users-databinding").aggregate([
+ * {$match:{"owner.$id":ObjectId("5e28b3e3637e580001e465d6")}},
+ * {$project:{_class:1, key:1, value:1, metadata:1, historic:1, owner:1, user:{$objectToArray:"$user"}}},
+ * {$project:{_class:1, key:1, value:1, metadata:1, historic:1, owner:1, user:{$arrayElemAt:["$user.v", 1]}}},
+ * {$lookup:{
+ * from:"muttley-users",
+ * localField:"user",
+ * foreignField:"_id",
+ * as: "user"
+ * }},
+ * {$unwind:"$user"},
+ * {$match:{"user.userName":"OwnerSiapi5e28b392637e580001e465d3"}}
+ * ])
+ */
+ final AggregationResults results = this.mongoTemplate.aggregate(
+ newAggregation(
+ match(where("owner.$id").is(user.getCurrentOwner().getObjectId())),
+ project("key", "value", "metadata", "owner").and(context -> new BasicDBObject("$objectToArray", "$user")).as("user"),
+ project("key", "value", "metadata", "owner").and(context -> new BasicDBObject("$arrayElemAt", asList("$user.v", 1))).as("user"),
+ lookup(documentNameConfig.getNameCollectionUser(), "user", "_id", "user"),
+ unwind("$user"),
+ match(where("user.userName").is(userName))
+ ),
+ documentNameConfig.getNameCollectionAdminUserDataBinding(),
+ AdminUserDataBinding.class
+ );
+ if (results == null || Collections.isEmpty(results.getMappedResults())) {
+ return new ArrayList<>();
+ }
+ return results.getMappedResults();
+ }
+
+ @Override
+ public List listBy(final User user) {
+ /**
+ * db.getCollection("muttley-users-databinding").aggregate([
+ * {$match:{"owner.$id":ObjectId("5e28b3e3637e580001e465d6"),"user.$id":ObjectId("5e28b3e3637e580001e465d6")}},
+ * ])
+ */
+ final Owner currentOwner = user.getCurrentOwner();
+ final AggregationResults results = this.mongoTemplate.aggregate(
+ newAggregation(
+ match(where("owner.$id").is(currentOwner != null ? currentOwner.getObjectId() : null).and("user.$id").is(new ObjectId(user.getId())))
+ ),
+ AdminUserDataBinding.class,
+ AdminUserDataBinding.class
+ );
+ if (results == null || Collections.isEmpty(results.getMappedResults())) {
+ new ArrayList<>();
+ }
+ return results.getMappedResults();
+ }
+
+ @Override
+ public AdminUserDataBinding saveByUserName(final User user, final String userName, final AdminUserDataBinding dataBinding) {
+ if (dataBinding.getUser() == null) {
+ dataBinding.setUser(this.findByUserName(userName));
+ }
+ checkBasicInfos(user, dataBinding);
+ checkPrecondictionSaveByUserName(user, userName, dataBinding);
+
+ final AdminUserDataBinding salved = repository.save(dataBinding);
+ //this.localDatabindingService.expireUserDataBindings(user);
+ return salved;
+ }
+
+ public void checkPrecondictionSaveByUserName(final User user, final String userName, final AdminUserDataBinding dataBinding) {
+ if (!userName.equals(dataBinding.getUser().getUserName())) {
+ throw new MuttleyBadRequestException(AdminUserDataBinding.class, "user", "O usuário informado é diferente do da requisição!")
+ .addDetails("useName", userName)
+ .addDetails("dataBinding", dataBinding);
+ }
+ this.checkIndex(user, userName, dataBinding);
+ this.validator.validate(dataBinding);
+ }
+
+ @Override
+ public AdminUserDataBinding updateByUserName(final User user, final String userName, final AdminUserDataBinding dataBinding) {
+ if (dataBinding.getUser() == null) {
+ dataBinding.setUser(this.findByUserName(userName));
+ }
+ if (StringUtils.isEmpty(dataBinding.getId())) {
+ dataBinding.setId(this.loadIdFrom(user, dataBinding));
+ }
+ checkBasicInfos(user, dataBinding);
+ checkPrecondictionUpdateByUserName(user, userName, dataBinding);
+
+ final AdminUserDataBinding salved = repository.save(dataBinding);
+ //this.localDatabindingService.expireUserDataBindings(user);
+ return salved;
+ }
+
+ public void checkPrecondictionUpdateByUserName(final User user, final String userName, final AdminUserDataBinding dataBinding) {
+ if (!userName.equals(dataBinding.getUser().getUserName())) {
+ throw new MuttleyBadRequestException(AdminUserDataBinding.class, "user", "O usuário informado é diferente do da requisição!");
+ }
+ this.validator.validate(dataBinding);
+ //verificando se já não existe um registro com as informações
+ this.checkIndex(user, userName, dataBinding);
+ }
+
+ @Override
+ public void merge(final User user, final String userName, final AdminUserDataBinding dataBinding) {
+ if (dataBinding.getUser() == null) {
+ dataBinding.setUser(this.findByUserName(userName));
+ }
+ if (exists(user, dataBinding)) {
+
+ this.updateByUserName(user, userName, dataBinding);
+ } else {
+
+ this.saveByUserName(user, userName, dataBinding);
+ }
+ }
+
+ @Override
+ public void merge(final User user, final String userName, final Set dataBindings) {
+ User userCurrent = null;
+ for (AdminUserDataBinding dataBinding : dataBindings) {
+ if (dataBinding.getUser() == null) {
+ if (userCurrent == null) {
+ if (user.getUserName().equals(userName)) {
+ userCurrent = user;
+ } else {
+ userCurrent = this.findByUserName(userName);
+ }
+ }
+ dataBinding.setUser(userCurrent);
+ }
+ if (exists(user, dataBinding)) {
+ this.updateByUserName(user, userName, dataBinding);
+ } else {
+ this.saveByUserName(user, userName, dataBinding);
+ }
+ }
+ }
+
+ @Override
+ public AdminUserDataBinding getKey(User user, KeyUserDataBinding key) {
+ return this.getKey(user, key.getKey());
+ }
+
+ @Override
+ public AdminUserDataBinding getKey(final User user, final String key) {
+ /**
+ * db.getCollection("muttley-users-databinding").aggregate([
+ * {$match:{"owner.$id":ObjectId("5e28b3e3637e580001e465d6"),"user.$id":ObjectId("5e28b3e3637e580001e465d6"), "key":"asdfasd"}},
+ * ])
+ */
+ final AggregationResults results = this.mongoTemplate.aggregate(
+ newAggregation(
+ match(
+ where("owner.$id").is(user.getCurrentOwner().getObjectId())
+ .and("user.$id").is(new ObjectId(user.getId()))
+ .and("key").is(key)
+ )
+ ),
+ AdminUserDataBinding.class,
+ AdminUserDataBinding.class
+ );
+ if (results == null || results.getUniqueMappedResult() == null) {
+ return null;
+ }
+ return results.getUniqueMappedResult();
+ }
+
+ @Override
+ public AdminUserDataBinding getKeyByUserName(User user, String userName, KeyUserDataBinding key) {
+ return this.getKeyByUserName(user, userName, key.getKey());
+ }
+
+ @Override
+ public AdminUserDataBinding getKeyByUserName(final User user, final String userName, final String key) {
+ if (user.getUserName().equals(userName)) {
+ return this.getKey(user, key);
+ }
+ /**
+ * db.getCollection("muttley-users-databinding").aggregate([
+ * {$match:{"owner.$id":ObjectId("5e28b3e3637e580001e465d6"), "key":"asdfasd"}},
+ * {$project:{_class:1, key:1, value:1, metadata:1, historic:1, owner:1, user:{$objectToArray:"$user"}}},
+ * {$project:{_class:1, key:1, value:1, metadata:1, historic:1, owner:1, user:{$arrayElemAt:["$user.v", 1]}}},
+ * {$lookup:{
+ * from:"muttley-users",
+ * localField:"user",
+ * foreignField:"_id",
+ * as: "user"
+ * }},
+ * {$unwind:"$user"},
+ * {$match:{"user.userName":"OwnerSiapi5e28b392637e580001e465d3", "key":"asdfasd"}}
+ * ])
+ */
+ final AggregationResults results = this.mongoTemplate.aggregate(
+ newAggregation(
+ match(where("owner.$id").is(user.getCurrentOwner().getObjectId()).and("key").is(key)),
+ project("key", "value", "metadata", "owner").and(context -> new BasicDBObject("$objectToArray", "$user")).as("user"),
+ project("key", "value", "metadata", "owner").and(context -> new BasicDBObject("$arrayElemAt", asList("$user.v", 1))).as("user"),
+ lookup(documentNameConfig.getNameCollectionUser(), "user", "_id", "user"),
+ unwind("$user"),
+ match(where("user.userName").is(userName).and("key").is(key))
+ ),
+ documentNameConfig.getNameCollectionAdminUserDataBinding(),
+ AdminUserDataBinding.class
+ );
+ if (results == null || results.getUniqueMappedResult() == null) {
+ return null;
+ }
+ return results.getUniqueMappedResult();
+ }
+
+ @Override
+ public boolean contains(User user, KeyUserDataBinding key) {
+ return this.contains(user, key.getKey());
+ }
+
+ @Override
+ public boolean contains(final User user, final String key) {
+ /**
+ * db.getCollection("muttley-users-databinding").aggregate([
+ * {$match:{"owner.$id":ObjectId("5e28b3e3637e580001e465d6"),"user.$id":ObjectId("5e28b3e3637e580001e465d6"), "key":"asdfasd"}},
+ * ])
+ */
+ return this.mongoTemplate.exists(
+ new Query(
+ where("owner.$id").is(user.getCurrentOwner().getObjectId())
+ .and("user.$id").is(new ObjectId(user.getId()))
+ .and("key").is(key)
+ ), AdminUserDataBinding.class);
+ }
+
+ @Override
+ public boolean containsByUserNameAndKey(User user, String userName, KeyUserDataBinding key) {
+ return this.containsByUserNameAndKey(user, userName, key);
+ }
+
+ @Override
+ public boolean containsByUserNameAndKey(final User user, final String userName, final String key) {
+ if (user.getUserName().equals(userName)) {
+ return this.contains(user, key);
+ }
+ /**
+ * db.getCollection("muttley-users-databinding").aggregate([
+ * {$match:{"owner.$id":ObjectId("5e28b3e3637e580001e465d6"), "key":"asdfasd"}},
+ * {$project:{_class:1, key:1, value:1, metadata:1, historic:1, owner:1, user:{$objectToArray:"$user"}}},
+ * {$project:{_class:1, key:1, value:1, metadata:1, historic:1, owner:1, user:{$arrayElemAt:["$user.v", 1]}}},
+ * {$lookup:{
+ * from:"muttley-users",
+ * localField:"user",
+ * foreignField:"_id",
+ * as: "user"
+ * }},
+ * {$unwind:"$user"},
+ * {$match:{"user.userName":"OwnerSiapi5e28b392637e580001e465d3", "key":"asdfasd"}}
+ * ])
+ */
+ final AggregationResults results = this.mongoTemplate.aggregate(
+ newAggregation(
+ match(where("owner.$id").is(user.getCurrentOwner().getObjectId()).and("key").is(key)),
+ project("key", "value", "metadata", "owner").and(context -> new BasicDBObject("$objectToArray", "$user")).as("user"),
+ project("key", "value", "metadata", "owner").and(context -> new BasicDBObject("$arrayElemAt", asList("$user.v", 1))).as("user"),
+ lookup(documentNameConfig.getNameCollectionUser(), "user", "_id", "user"),
+ unwind("$user"),
+ match(where("user.userName").is(userName).and("key").is(key)),
+ count().as("result")
+ ),
+ documentNameConfig.getNameCollectionAdminUserDataBinding(),
+ BasicAggregateResultCount.class
+ );
+ if (results == null || results.getUniqueMappedResult() == null) {
+ return false;
+ //throw new MuttleyNoContentException(UserDataBinding.class, "userName", "Nenhum registro encontrado para o usuário desejado");
+ }
+ return results.getUniqueMappedResult().getResult() > 0;
+ }
+
+ @Override
+ public boolean containsByKeyAndValue(User user, KeyUserDataBinding key, String value) {
+ return this.containsByKeyAndValue(user, key.getKey(), value);
+ }
+
+ @Override
+ public boolean containsByKeyAndValue(final User user, final String key, final String value) {
+ /**
+ * db.getCollection("muttley-users-databinding").aggregate([
+ * {$match:{"owner.$id":ObjectId("5e28b3e3637e580001e465d6"), "key":"UserColaborador", value:"5ff65e339208bc0007c0d6ba"}},
+ * {}
+ * ])
+ */
+ final AggregationResults results = this.mongoTemplate.aggregate(
+ newAggregation(
+ match(where("owner.$id").is(user.getCurrentOwner().getObjectId()).and("key").is(key).and("value").is(value)),
+ count().as("result")
+ ),
+ documentNameConfig.getNameCollectionAdminUserDataBinding(),
+ BasicAggregateResultCount.class
+ );
+ if (results == null || results.getUniqueMappedResult() == null) {
+ return false;
+ //throw new MuttleyNoContentException(UserDataBinding.class, "userName", "Erro na consulta");
+ }
+ return results.getUniqueMappedResult().getResult() > 0;
+ }
+
+ @Override
+ public boolean containsByKeyAndValueAndUserNameNotEq(User user, String userName, KeyUserDataBinding key, String value) {
+ return this.containsByKeyAndValueAndUserNameNotEq(user, userName, key.getKey(), value);
+ }
+
+ @Override
+ public boolean containsByKeyAndValueAndUserNameNotEq(final User user, final String userName, final String key, final String value) {
+ /**
+ * db.getCollection("muttley-users-databinding").aggregate([
+ *
+ * {$match:{"owner.$id":ObjectId("5e28b3e3637e580001e465d6"), "key":"UserColaborador", value:"5ff65e339208bc0007c0d6ba"}},
+ * {$project:{_class:1, key:1, value:1, metadata:1, historic:1, owner:1, user:{$objectToArray:"$user"}}},
+ * {$project:{_class:1, key:1, value:1, metadata:1, historic:1, owner:1, user:{$arrayElemAt:["$user.v", 1]}}},
+ * {$lookup:{
+ * from:"muttley-users",
+ * localField:"user",
+ * foreignField:"_id",
+ * as: "user"
+ * }},
+ * {$unwind:"$user"},
+ * {$match:{"user.userName":{$ne:"0756143600010711000523BRUNA.Ab"}}}
+ * ])
+ */
+ final List operations = new LinkedList<>(
+ asList(
+ match(where("owner.$id").is(user.getCurrentOwner().getObjectId()).and("key").is(key).and("value").is(value))
+ )
+ );
+ /*if (!StringUtils.isEmpty(userName)) {
+ operations.addAll(
+ asList(
+ project("key", "value", "metadata", "historic", "owner").and(context -> new BasicDBObject("$objectToArray", "$user")).as("user"),
+ project("key", "value", "metadata", "historic", "owner").and(context -> new BasicDBObject("$arrayElemAt", asList("$user.v", 1))).as("user"),
+ lookup(documentNameConfig.getNameCollectionUser(), "user", "_id", "user"),
+ unwind("$user")
+ //match(where("user.userName").ne(userName).and("key").is(key))
+ )
+ );
+ }*/
+ //operations.add(count().as("result"));
+ final AggregationResults results = this.mongoTemplate.aggregate(
+ newAggregation(
+ operations
+ ),
+ documentNameConfig.getNameCollectionAdminUserDataBinding(),
+ AdminUserDataBinding.class
+ );
+ if (results == null || results.getUniqueMappedResult() == null) {
+ //se não encontrou nada é sinal que o databinding está disponivel
+ return false;
+ } else {
+ return results.getMappedResults()
+ .parallelStream()
+ .filter(it -> !it.getUser().getUserName().equals(userName))
+ .count() > 0;
+ }
+ }
+
+ @Override
+ public UserData getUserBy(final User user, final KeyUserDataBinding key, final String value) {
+ return this.getUserBy(user, key.getKey(), value);
+ }
+
+ @Override
+ public UserData getUserBy(final User user, final String key, final String value) {
+ /**
+ * db.getCollection("muttley-users-databinding").aggregate([
+ * {$match:{"owner.$id": ObjectId("5e28b3e3637e580001e465d6"), "key":"UserColaborador", "value":"5e28bcf86f985c00017e7a28"}},
+ * {$project:{user:1}}
+ * ])
+ */
+ final AggregationResults results = this.mongoTemplate.aggregate(
+ newAggregation(
+ match(where("owner.$id").is(user.getCurrentOwner().getObjectId()).and("key").is(key).and("value").is(value))
+ ),
+ AdminUserDataBinding.class,
+ AdminUserDataBinding.class
+ );
+ if (results == null || results.getUniqueMappedResult() == null) {
+ return null;
+ }
+ return results.getUniqueMappedResult().getUser();
+ }
+
+ private final void checkIndex(final User user, final AdminUserDataBinding dataBinding) {
+ if (StringUtils.isEmpty(dataBinding.getId())) {
+ if (this.exists(user, dataBinding)) {
+ throw new MuttleyConflictException(AdminUserDataBinding.class, "key", "Jás existe um registro com essas informações");
+ }
+ } else {
+ if (this.mongoTemplate.exists(
+ new Query(
+ where("id").ne(dataBinding.getObjectId())
+ .and("owner.$id").is(user.getCurrentOwner().getObjectId())
+ .and("user.$id").is(new ObjectId(dataBinding.getUser().getId()))
+ .and("key").is(dataBinding.getKey())
+ ), AdminUserDataBinding.class)) {
+ throw new MuttleyConflictException(AdminUserDataBinding.class, "key", "Jás existe um registro com essas informações");
+ }
+ }
+
+ if (dataBinding.getKey().isUnique()) {
+ //verificando se outro usário já tem esse databinding
+ if (this.containsByKeyAndValueAndUserNameNotEq(user, user.getUserName(), dataBinding.getKey().getKey(), dataBinding.getValue())) {
+ throw new MuttleyBadRequestException(AdminUserDataBinding.class, "key", "Já existe um usuário que possui ligação com " + dataBinding.getKey().getDisplayKey() + " informado(a)");
+ }
+ }
+ }
+
+ private final void checkIndex(final User user, final String userName, final AdminUserDataBinding dataBinding) {
+ if (user.getUserName().equals(userName)) {
+ this.checkIndex(user, dataBinding);
+ } else {
+
+ //caso o registro não tenha id é sinal que estamos inserindo um novo
+ //com isso se faz necessário verificar se já não existe o mesmo
+ if (!dataBinding.contaisObjectId()) {
+ if (this.exists(user, userName, dataBinding.getKey().getKey())) {
+ throw new MuttleyConflictException(AdminUserDataBinding.class, "key", "Jás existe um registro com essas informações");
+ }
+ }
+
+ if (dataBinding.getKey().isUnique()) {
+ //verificando se outro usário já tem esse databinding
+ if (this.containsByKeyAndValueAndUserNameNotEq(user, userName, dataBinding.getKey().getKey(), dataBinding.getValue())) {
+ throw new MuttleyBadRequestException(AdminUserDataBinding.class, "key", "Já existe um usuário que possui ligação com " + dataBinding.getKey().getDisplayKey() + " informado(a)");
+ }
+ }
+ }
+ }
+
+ private boolean exists(final User user, AdminUserDataBinding dataBinding) {
+ return this.repository.exists("owner.$id", user.getCurrentOwner().getObjectId(), "user.$id", new ObjectId(dataBinding.getUser().getId()), "key", dataBinding.getKey().getKey());
+ }
+
+ private boolean exists(final User user, final String userName, final String key) {
+ /**
+ * db.getCollection("muttley-users-databinding").aggregate([
+ * {$match:{"owner.$id":ObjectId("5e28b3e3637e580001e465d6")}},
+ * {$project:{_class:1, key:1, value:1, metadata:1, historic:1, owner:1, user:{$objectToArray:"$user"}}},
+ * {$project:{_class:1, key:1, value:1, metadata:1, historic:1, owner:1, user:{$arrayElemAt:["$user.v", 1]}}},
+ * {$lookup:{
+ * from:"muttley-users",
+ * localField:"user",
+ * foreignField:"_id",
+ * as: "user"
+ * }},
+ * {$unwind:"$user"},
+ * {$match:{"user.userName":"OwnerSiapi5e28b392637e580001e465d3"}}
+ * ])
+ */
+ final AggregationResults results = this.mongoTemplate.aggregate(
+ newAggregation(
+ match(where("owner.$id").is(user.getCurrentOwner().getObjectId())),
+ project("key", "value").and(context -> new BasicDBObject("$objectToArray", "$user")).as("user"),
+ project("key", "value").and(context -> new BasicDBObject("$arrayElemAt", asList("$user.v", 1))).as("user"),
+ lookup(documentNameConfig.getNameCollectionUser(), "user", "_id", "user"),
+ unwind("$user"),
+ match(where("user.userName").is(userName)),
+ count().as("result")
+ ),
+ documentNameConfig.getNameCollectionAdminUserDataBinding(),
+ BasicAggregateResultCount.class
+ );
+
+ return results != null && results.getUniqueMappedResult() != null && results.getUniqueMappedResult().getResult() > 0;
+ }
+
+ private String loadIdFrom(final User user, final AdminUserDataBinding dataBinding) {
+ final AggregationResults results = this.mongoTemplate.aggregate(
+ newAggregation(
+ match(
+ where("owner.$id").is(user.getCurrentOwner().getObjectId())
+ .and("user.$id").is(new ObjectId(dataBinding.getUser().getId()))
+ .and("key").is(dataBinding.getKey())
+ ),
+ project().and("_id").as("result")
+ ),
+ documentNameConfig.getNameCollectionAdminUserDataBinding(),
+ BasicAggregateResult.class
+ );
+ return results.getUniqueMappedResult().getResult().toString();
+ }
+
+ private void checkBasicInfos(final User user, final AdminUserDataBinding userDataBinding) {
+ userDataBinding.setOwner(user.getCurrentOwner());
+ if (StringUtils.isEmpty(userDataBinding.getId())) {
+ this.metadataService.generateNewMetadataFor(user, userDataBinding);
+ } else {
+ this.metadataService.generateMetaDataUpdateFor(user, this.repository.loadMetadata(userDataBinding), userDataBinding);
+ }
+ }
+
+
+ protected User findByUserName(final String userName) {
+ final UserResolverEvent event = new UserResolverEvent(userName);
+ this.eventPublisher.publishEvent(event);
+ return event.getUserResolver();
+ }
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/impl/MuttleyHeadersMetadataServiceImpl.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/impl/MuttleyHeadersMetadataServiceImpl.java
new file mode 100644
index 00000000..4f78c9d3
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/impl/MuttleyHeadersMetadataServiceImpl.java
@@ -0,0 +1,45 @@
+package br.com.muttley.admin.server.service.impl;
+
+import br.com.muttley.feign.service.service.MuttleyHeadersMetadataService;
+import br.com.muttley.headers.components.MuttleyCurrentVersion;
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static br.com.muttley.headers.model.MuttleyHeader.KEY_ADMIN_SERVER;
+
+/**
+ * @author Joel Rodrigues Moreira 22/04/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Service
+public class MuttleyHeadersMetadataServiceImpl implements MuttleyHeadersMetadataService {
+ private boolean resolved = false;
+ private MuttleyCurrentVersion currentVersion;
+ @Autowired
+ protected ObjectProvider currentVersionProvider;
+
+ @Override
+ public Map getHeadersMetadata() {
+ final Map headers = new HashMap<>(1);
+ try {
+ if (!resolved) {
+ resolved = true;
+ this.currentVersion = currentVersionProvider.getIfAvailable();
+ }
+ if (currentVersion != null) {
+ headers.put(KEY_ADMIN_SERVER, currentVersion.getCurrenteFromServer());
+ } else {
+ headers.put(KEY_ADMIN_SERVER, null);
+ }
+ } catch (final BeanCreationException ex) {
+ headers.put(KEY_ADMIN_SERVER, null);
+ }
+ return headers;
+ }
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/impl/NoSecurityAdminOwnerServiceImpl.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/impl/NoSecurityAdminOwnerServiceImpl.java
new file mode 100644
index 00000000..d9ee7809
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/impl/NoSecurityAdminOwnerServiceImpl.java
@@ -0,0 +1,58 @@
+package br.com.muttley.admin.server.service.impl;
+
+import br.com.muttley.admin.server.repository.AdminOwnerRepository;
+import br.com.muttley.admin.server.service.NoSecurityAdminOwnerService;
+import br.com.muttley.exception.throwables.MuttleyBadRequestException;
+import br.com.muttley.exception.throwables.MuttleyNotFoundException;
+import br.com.muttley.model.admin.AdminOwner;
+import br.com.muttley.model.security.Owner;
+import br.com.muttley.model.security.Passaport;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author Joel Rodrigues Moreira 23/04/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Service
+public class NoSecurityAdminOwnerServiceImpl extends AbstractNoSecurityService implements NoSecurityAdminOwnerService {
+ protected final AdminOwnerRepository repository;
+ private final MongoTemplate template;
+
+ @Autowired
+ public NoSecurityAdminOwnerServiceImpl(final MongoTemplate template, final AdminOwnerRepository repository) {
+ this.template = template;
+ this.repository = repository;
+ }
+
+ @Override
+ public AdminOwner findByName(final String name) {
+ //validando contexto de execução
+ this.validateContext();
+ final AdminOwner owner = this.repository.findByName(name);
+ if (owner == null) {
+ throw new MuttleyNotFoundException(Owner.class, "name", "Registro não encontrado");
+ }
+ return owner;
+ }
+
+ @Override
+ public AdminOwner save(final AdminOwner owner) {
+ //validando contexto de execução
+ this.validateContext();
+ //verificando se realmente está criando um novo registro
+ if (owner.getId() != null) {
+ throw new MuttleyBadRequestException(Passaport.class, "id", "Não é possível criar um registro com um id existente");
+ }
+ //validando dados
+ this.validator.validate(owner);
+ /*//verificando precondições
+ this.checkPrecondictionSave(user, value);
+ this.beforeSave(user, value);*/
+ return this.repository.save(owner);
+ }
+
+
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/impl/NoSecurityAdminPassaportServiceImpl.java b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/impl/NoSecurityAdminPassaportServiceImpl.java
new file mode 100644
index 00000000..cf193727
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/java/br/com/muttley/admin/server/service/impl/NoSecurityAdminPassaportServiceImpl.java
@@ -0,0 +1,40 @@
+package br.com.muttley.admin.server.service.impl;
+
+import br.com.muttley.admin.server.repository.AdminPassaportRepository;
+import br.com.muttley.admin.server.service.NoSecurityAdminPassaportService;
+import br.com.muttley.exception.throwables.MuttleyBadRequestException;
+import br.com.muttley.model.admin.AdminPassaport;
+import br.com.muttley.model.security.Passaport;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author Joel Rodrigues Moreira 23/04/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Service
+public class NoSecurityAdminPassaportServiceImpl extends AbstractNoSecurityService implements NoSecurityAdminPassaportService {
+ private final AdminPassaportRepository repository;
+
+ @Autowired
+ public NoSecurityAdminPassaportServiceImpl(final AdminPassaportRepository repository) {
+ this.repository = repository;
+ }
+
+ @Override
+ public AdminPassaport save(final AdminPassaport passaport) {
+ //validando contexto de execução
+ this.validateContext();
+ //verificando se realmente está criando um novo registro
+ if (passaport.getId() != null) {
+ throw new MuttleyBadRequestException(Passaport.class, "id", "Não é possível criar um registro com um id existente");
+ }
+ //validando dados
+ this.validator.validate(passaport);
+ /*//verificando precondições
+ this.checkPrecondictionSave(user, value);
+ this.beforeSave(user, value);*/
+ return this.repository.save(passaport);
+ }
+}
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/resources/META-INF/spring.factories b/muttley-admin-server.pom/muttley-admin-server/src/main/resources/META-INF/spring.factories
new file mode 100644
index 00000000..568fe29a
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ br.com.muttley.admin.server.MuttleyAdminServerConfig
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/main/resources/application.properties b/muttley-admin-server.pom/muttley-admin-server/src/main/resources/application.properties
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/main/resources/application.properties
@@ -0,0 +1 @@
+
diff --git a/muttley-admin-server.pom/muttley-admin-server/src/test/java/br/com/muttley/admin/server/MuttleyHeadersMetadataServiceImplAdminServerApplicationTests.java b/muttley-admin-server.pom/muttley-admin-server/src/test/java/br/com/muttley/admin/server/MuttleyHeadersMetadataServiceImplAdminServerApplicationTests.java
new file mode 100644
index 00000000..cc22ae5b
--- /dev/null
+++ b/muttley-admin-server.pom/muttley-admin-server/src/test/java/br/com/muttley/admin/server/MuttleyHeadersMetadataServiceImplAdminServerApplicationTests.java
@@ -0,0 +1,14 @@
+package br.com.muttley.admin.server;
+
+import org.junit.Test;
+
+
+/*@RunWith(SpringRunner.class)
+@SpringBootTest*/
+public class MuttleyHeadersMetadataServiceImplAdminServerApplicationTests {
+
+ @Test
+ public void contextLoads() {
+ }
+
+}
diff --git a/muttley-admin-server.pom/mvnw b/muttley-admin-server.pom/mvnw
new file mode 100755
index 00000000..a16b5431
--- /dev/null
+++ b/muttley-admin-server.pom/mvnw
@@ -0,0 +1,310 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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
+#
+# https://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.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ export JAVA_HOME="`/usr/libexec/java_home`"
+ else
+ export JAVA_HOME="/Library/Java/Home"
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`which java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=`cd "$wdir/.."; pwd`
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' < "$1")"
+ fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found .mvn/wrapper/maven-wrapper.jar"
+ fi
+else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+ fi
+ if [ -n "$MVNW_REPOURL" ]; then
+ jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ else
+ jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ fi
+ while IFS="=" read key value; do
+ case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+ esac
+ done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Downloading from: $jarUrl"
+ fi
+ wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+ if $cygwin; then
+ wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+ fi
+
+ if command -v wget > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found wget ... using wget"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget "$jarUrl" -O "$wrapperJarPath"
+ else
+ wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found curl ... using curl"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl -o "$wrapperJarPath" "$jarUrl" -f
+ else
+ curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+ fi
+
+ else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Falling back to using Java to download"
+ fi
+ javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaClass=`cygpath --path --windows "$javaClass"`
+ fi
+ if [ -e "$javaClass" ]; then
+ if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Compiling MavenWrapperDownloader.java ..."
+ fi
+ # Compiling the Java class
+ ("$JAVA_HOME/bin/javac" "$javaClass")
+ fi
+ if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ # Running the downloader
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Running MavenWrapperDownloader.java ..."
+ fi
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+ echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/muttley-admin-server.pom/mvnw.cmd b/muttley-admin-server.pom/mvnw.cmd
new file mode 100644
index 00000000..c8d43372
--- /dev/null
+++ b/muttley-admin-server.pom/mvnw.cmd
@@ -0,0 +1,182 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+
+FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %DOWNLOAD_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
diff --git a/muttley-admin-server.pom/pom.xml b/muttley-admin-server.pom/pom.xml
new file mode 100644
index 00000000..fb192777
--- /dev/null
+++ b/muttley-admin-server.pom/pom.xml
@@ -0,0 +1,20 @@
+
+
+
+ br.com.muttley
+ muttley-cloud
+ ${revision}
+
+ 4.0.0
+ pom
+
+ muttley-admin-server.pom
+
+ muttley-admin-server.pom
+ Demo project for Spring Boot
+
+
+ muttley-admin-server
+
+
diff --git a/muttley-config-server/README.adoc b/muttley-config-server/README.adoc
index a54373d8..a8df427d 100644
--- a/muttley-config-server/README.adoc
+++ b/muttley-config-server/README.adoc
@@ -6,7 +6,7 @@ Adicione a dependência em seu serviço de configuração;
br.com.br
muttley-config-server
- 0.0.2-SNAPSHOT
+ ${revision}
----
diff --git a/muttley-config-server/pom.xml b/muttley-config-server/pom.xml
index 8ce898b8..8a62ac1f 100644
--- a/muttley-config-server/pom.xml
+++ b/muttley-config-server/pom.xml
@@ -1,11 +1,11 @@
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
br.com.muttley
muttley-cloud
- 0.0.2-SNAPSHOT
+ ${revision}
jar
diff --git a/muttley-discovery-server/README.adoc b/muttley-discovery-server/README.adoc
index 32c99c94..7a7292dd 100644
--- a/muttley-discovery-server/README.adoc
+++ b/muttley-discovery-server/README.adoc
@@ -6,7 +6,7 @@ Adicione a dependência em seu serviço de configuração;
br.com.muttley
muttley-discovery-server
- 0.0.2-SNAPSHOT
+ ${revision}
----
diff --git a/muttley-discovery-server/pom.xml b/muttley-discovery-server/pom.xml
index 5b2e8e54..f0430815 100644
--- a/muttley-discovery-server/pom.xml
+++ b/muttley-discovery-server/pom.xml
@@ -1,10 +1,10 @@
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
br.com.muttley
muttley-cloud
- 0.0.2-SNAPSHOT
+ ${revision}
4.0.0
jar
diff --git a/muttley-discovery-server/src/test/java/br/com/muttley/muttleydiscoveryserver/MuttleyDiscoveryServerApplicationTests.java b/muttley-discovery-server/src/test/java/br/com/muttley/muttleydiscoveryserver/MuttleyDiscoveryServerApplicationTests.java
index a2c12fc0..24565aa6 100644
--- a/muttley-discovery-server/src/test/java/br/com/muttley/muttleydiscoveryserver/MuttleyDiscoveryServerApplicationTests.java
+++ b/muttley-discovery-server/src/test/java/br/com/muttley/muttleydiscoveryserver/MuttleyDiscoveryServerApplicationTests.java
@@ -1,16 +1,71 @@
package br.com.muttley.muttleydiscoveryserver;
+import org.apache.commons.lang.StringUtils;
import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.test.context.junit4.SpringRunner;
+import sun.security.rsa.RSAUtil;
-@RunWith(SpringRunner.class)
-@SpringBootTest
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.OffsetDateTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Date;
+import java.util.Random;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
+
+/*@RunWith(SpringRunner.class)
+@SpringBootTest*/
public class MuttleyDiscoveryServerApplicationTests {
+ private static final String TIME24HOURS_PATTERN = "(([+-]|)([01]?[0-9]|2[0-3]):[0-5][0-9])|(([+-]|)([01]?[0-9]|2[0-3])([0-5][0-9]))";
@Test
- public void contextLoads() {
+ public void contextLoads() throws ParseException {
+ final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
+ final DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
+
+ final Date date = df.parse("2019-10-14T11:10:32.399-0300");
+ final OffsetDateTime offsetDateTime = OffsetDateTime.parse("2019-10-14T11:10:32.399-0500", dateFormatter);
+ final ZonedDateTime zonedDateTime = ZonedDateTime.parse("2019-10-14T11:10:32.399-0500", dateFormatter);
+
+ System.out.println("date " + date);
+ System.out.println("offsetDateTime " + offsetDateTime);
+ System.out.println("zonedDateTime " + zonedDateTime);
+ System.out.println("date from offse " + Date.from(offsetDateTime.toInstant()));
+
+ System.out.println(zonedDateTime.getZone());
+ System.out.println(ZonedDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()));
+ System.out.println(ZonedDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()).withZoneSameLocal(ZoneId.of("-0200")));
+ System.out.println();
+ System.out.println();
+ System.out.println();
+
+ System.out.println(Date.from(ZonedDateTime.now(ZoneId.of("-0300")).toInstant()));
+ System.out.println(ZonedDateTime.ofInstant(Date.from(ZonedDateTime.now().toInstant()).toInstant(), ZoneId.systemDefault()));
+ System.out.println(Date.from(ZonedDateTime.ofInstant(Date.from(ZonedDateTime.now().toInstant()).toInstant(), ZoneId.systemDefault()).toInstant()));
+ System.out.println(ZonedDateTime.ofInstant(Date.from(ZonedDateTime.now().toInstant()).toInstant(), ZoneId.systemDefault()).getZone());
+ System.out.println(ZonedDateTime.ofInstant(Date.from(ZonedDateTime.now().toInstant()).toInstant(), ZoneId.systemDefault()).getOffset());
+ //System.out.println("date from local " + localDateTime.toInstant(localDateTime.get()));
+ //System.out.println("other " + OffsetDateTime.from(Date.from(offsetDateTime.toInstant()).toInstant()));
+ //System.out.println("other " + OffsetDateTime.from(Date.from(offsetDateTime.toInstant()).toInstant()).atZoneSimilarLocal(ZoneOffset.of("America/Sao_Paulo")));
+
+ final String[] te = "teste".split("\\.");
+ Stream.of(te).forEach(System.out::println);
+ System.out.println("teste".split("\\.").length);
+//verificando se é já tem lookup a ser gerado
+ Pattern pattern = Pattern.compile("\\.");
+ Matcher matcher = pattern.matcher("elep.han.t.$id");
+ int count = 0;
+ //matcher.
+ while (matcher.find()) {
+ count++;
+ }
+
+ System.out.println("count" + count);
+ System.out.println(StringUtils.countMatches("elephant", "e"));
}
}
diff --git a/muttley-domain-service/pom.xml b/muttley-domain-service/pom.xml
index 46b9351f..55b9336d 100644
--- a/muttley-domain-service/pom.xml
+++ b/muttley-domain-service/pom.xml
@@ -1,10 +1,10 @@
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
br.com.muttley
muttley-cloud
- 0.0.2-SNAPSHOT
+ ${revision}
4.0.0
jar
@@ -15,6 +15,23 @@
Demo project for Spring Boot
+
+ org.projectlombok
+ lombok
+ true
+
+
+
+ br.com.muttley
+ muttley-headers
+ provided
+
+
+
+ br.com.muttley
+ muttley-security
+ provided
+
br.com.muttley
diff --git a/muttley-domain-service/src/main/java/br/com/muttley/domain/service/ModelSyncService.java b/muttley-domain-service/src/main/java/br/com/muttley/domain/service/ModelSyncService.java
new file mode 100644
index 00000000..78ab20af
--- /dev/null
+++ b/muttley-domain-service/src/main/java/br/com/muttley/domain/service/ModelSyncService.java
@@ -0,0 +1,344 @@
+package br.com.muttley.domain.service;
+
+import br.com.muttley.model.ModelSync;
+import br.com.muttley.model.SyncObjectId;
+import br.com.muttley.model.security.User;
+import org.springframework.security.access.prepost.PreAuthorize;
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.Set;
+
+/**
+ * @author Joel Rodrigues Moreira on 05/05/2021.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public interface ModelSyncService extends ModelService {
+
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString()" +
+ " ) " +
+ "or " +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).toPatternRole('create', this.getBasicRoles())," +
+ " T(br.com.muttley.model.security.Role).toPatternRole('update', this.getBasicRoles())" +
+ " )" +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('create', 'MOBILE_' + this.getBasicRoles()), " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('update', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true"
+ )
+ void synchronize(final User user, final Collection records);
+
+ /**
+ * Algumas collections pode ter indice pelo de maneira diferente,
+ * por conta disso devemos validar o index do sync de acordo com o indice implementado
+ */
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString()" +
+ " ) " +
+ "or " +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', this.getBasicRoles())" +
+ " )" +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true"
+ )
+ void checkSyncIndex(final User user, final T value);
+
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString()" +
+ " ) " +
+ "or " +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).toPatternRole('update', this.getBasicRoles())" +
+ " )" +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('update', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true"
+ )
+ T updateBySync(final User user, final T value);
+
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString()" +
+ " ) " +
+ "or " +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', this.getBasicRoles()), " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('simple_use', this.getBasicRoles()) " +
+ " )" +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true"
+ )
+ T findBySync(final User user, final String sync);
+
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString()" +
+ " ) " +
+ "or " +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', this.getBasicRoles()), " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('simple_use', this.getBasicRoles()) " +
+ " )" +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true"
+ )
+ T findReferenceBySync(final User user, final String sync);
+
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString()" +
+ " ) " +
+ "or " +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', this.getBasicRoles()), " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('simple_use', this.getBasicRoles()) " +
+ " )" +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true"
+ )
+ T findByIdOrSync(final User user, final String idSync);
+
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString()" +
+ " ) " +
+ "or " +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', this.getBasicRoles())" +
+ " )" +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true"
+ )
+ Date getLastModify(final User user);
+
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString()" +
+ " ) " +
+ "or " +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).toPatternRole('delete', this.getBasicRoles())" +
+ " )" +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('delete', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true"
+ )
+ void deleteBySync(final User user, final String sync);
+
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString()" +
+ " ) " +
+ "or " +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', this.getBasicRoles()), " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('simple_use', this.getBasicRoles()) " +
+ " )" +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true"
+ )
+ boolean existSync(final User user, final T value);
+
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString()" +
+ " ) " +
+ "or " +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', this.getBasicRoles()), " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('simple_use', this.getBasicRoles())" +
+ " )" +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true"
+ )
+ String getIdOfSync(final User user, final String sync);
+
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString()" +
+ " ) " +
+ "or " +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', this.getBasicRoles()), " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('simple_use', this.getBasicRoles())" +
+ " )" +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true"
+ )
+ String getSyncOfId(final User user, final String id);
+
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString()" +
+ " ) " +
+ "or " +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', this.getBasicRoles()), " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('simple_use', this.getBasicRoles())" +
+ " )" +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true"
+ )
+ Set getIdsOfSyncs(final User user, final Set syncs);
+
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString()" +
+ " ) " +
+ "or " +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', this.getBasicRoles()), " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('simple_use', this.getBasicRoles())" +
+ " )" +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true"
+ )
+ Set getSyncsOfIds(final User user, final Set ids);
+}
diff --git a/muttley-domain-service/src/main/java/br/com/muttley/domain/service/Service.java b/muttley-domain-service/src/main/java/br/com/muttley/domain/service/Service.java
index 0b51d228..f0f022c6 100644
--- a/muttley-domain-service/src/main/java/br/com/muttley/domain/service/Service.java
+++ b/muttley-domain-service/src/main/java/br/com/muttley/domain/service/Service.java
@@ -1,11 +1,13 @@
package br.com.muttley.domain.service;
import br.com.muttley.model.Document;
-import br.com.muttley.model.Historic;
import br.com.muttley.model.security.User;
+import org.springframework.security.access.prepost.PreAuthorize;
+import java.util.Collection;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* @author Joel Rodrigues Moreira on 23/02/18.
@@ -13,6 +15,11 @@
* @project muttley-cloud
*/
public interface Service {
+
+ boolean isCheckRole();
+
+ String[] getBasicRoles();
+
/**
* Este método é sempre chamado antes de persistir algum registro no banco de dados,
* e também antes de se chamar o metodo {@link #beforeSave(User, Document)}.
@@ -45,6 +52,28 @@ public interface Service {
* @param user -> usuário da requisição corrente
* @param value -> registro a ser salvo
*/
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString() " +
+ " ) " +
+ "or " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('create', this.getBasicRoles()) " +
+ " ) " +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('create', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true "
+ )
T save(final User user, final T value);
/**
@@ -53,9 +82,112 @@ public interface Service {
* @param user -> usuário da requisição corrente
* @param value -> registro a ser salvo
*/
-
void afterSave(final User user, final T value);
+ /**
+ * Este método é sempre chamado antes de persistir algum registro no banco de dados,
+ * e também antes de se chamar o metodo {@link #beforeSave(User, Collection)}.
+ * Caso queira realizar algum tipo de validação antes de salvar algo, sobrescreva esse método
+ * com sua regra de negócio jutamente com suas exceptions.
+ *
+ * Por padrao esse metodo faz uma interação item por item chamando o methodo {@link #checkPrecondictionSave(User, Document)}.
+ *
+ * @param user -> usuário da requisição corrente
+ * @param values -> registro a ser salvo
+ */
+ void checkPrecondictionSave(final User user, final Collection values);
+
+ /**
+ * Este método é chamado toda vez antes de se salvar algum registro e depois de se chamar o metodo
+ * {@link #checkPrecondictionSave(User, Collection)}.
+ * Este método não deve ser utilizado para executar válidações mas sim para log's, pequenos ajuste
+ * e ou regras de négocio antes de se salvar um registro
+ *
+ * Por padrao esse metodo faz uma interação item por item chamando o methodo {@link #beforeSave(User, Document)}.
+ *
+ * @param user -> usuário da requisição corrente
+ * @param values -> registro a ser salvo
+ */
+ void beforeSave(final User user, final Collection values);
+
+ /**
+ * Salva um novo registro no banco de dados,
+ * garantindo sempre que ele esteja relacionado a um usuário/owner.
+ *
+ * Antes de ser salvo qualquer registro, primeiramente é executado a regra
+ * de negócio presente no metodo {@link #checkPrecondictionSave(User, Document)}
+ *
+ * @param user -> usuário da requisição corrente
+ * @param values -> registro a ser salvo
+ */
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString() " +
+ " ) " +
+ "or " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('create', this.getBasicRoles()) " +
+ " ) " +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('create', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true "
+ )
+ Collection save(final User user, final Collection values);
+
+ /**
+ * Salva um novo registro no banco de dados,
+ * garantindo sempre que ele esteja relacionado a um usuário/owner.
+ *
+ * Antes de ser salvo qualquer registro, primeiramente é executado a regra
+ * de negócio presente no metodo {@link #checkPrecondictionSave(User, Collection)}
+ *
+ * @param user -> usuário da requisição corrente
+ * @param values -> registro a ser salvo
+ */
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString() " +
+ " ) " +
+ "or " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('create', this.getBasicRoles()) " +
+ " ) " +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('create', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true "
+ )
+ void saveOnly(final User user, final Collection values);
+
+ /**
+ * Este metodo é chamado toda vez em que é salvo um registro no banco de dados
+ *
+ * Por padrao esse metodo faz uma interação item por item chamando o methodo {@link #afterSave(User, Document)}.
+ *
+ * @param user -> usuário da requisição corrente
+ * @param values -> registro a ser salvo
+ */
+ void afterSave(final User user, final Collection values);
+
/**
* Este método é sempre chamado antes de persistir a atualização de algum registro no banco de dados.
* Caso queira realizar algum tipo de validação antes de atualizar algo, sobrescreva esse método
@@ -87,6 +219,28 @@ public interface Service {
* @param user -> usuário da requisição corrente
* @param value -> registro a ser atualizado
*/
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString() " +
+ " ) " +
+ "or " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('update', this.getBasicRoles()) " +
+ " )" +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('update', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true "
+ )
T update(final User user, final T value);
/**
@@ -98,38 +252,192 @@ public interface Service {
void afterUpdate(final User user, final T value);
+ /**
+ * Este método é sempre chamado antes de persistir a atualização de algum registro no banco de dados.
+ * Caso queira realizar algum tipo de validação antes de atualizar algo, sobrescreva esse método
+ * com sua regra de negócio jutamente com suas exceptions.
+ *
+ * @param user -> usuário da requisição corrente
+ * @param values -> registros a ser atualizado
+ */
+ void checkPrecondictionUpdate(final User user, final Collection values);
+
+ /**
+ * Este método é chamado toda vez antes de se atualizar algum registro e depois de se chamar o metodo
+ * {@link #checkPrecondictionUpdate(User, Document)}.
+ * Este método não deve ser utilizado para executar válidações mas sim para log's, pequenos ajuste
+ * e ou regras de négocio antes de se salvar a alteração em algum registro
+ *
+ * @param user -> usuário da requisição corrente
+ * @param values -> registro a ser salvo
+ */
+ void beforeUpdate(final User user, final Collection values);
+
+ /**
+ * Atualiza um novo registro no banco de dados,
+ * garantindo sempre que ele esteja relacionado a um usuário/owner.
+ *
+ * Antes de ser atualizado qualquer registro, primeiramente é executado a regra
+ * de negócio presente no metodo checkPrecondictionUpdate
+ *
+ * @param user -> usuário da requisição corrente
+ * @param values -> registro a ser atualizado
+ */
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString() " +
+ " ) " +
+ "or " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('update', this.getBasicRoles()) " +
+ " )" +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('update', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true "
+ )
+ void update(final User user, final Collection values);
+
+ /**
+ * Este metodo é chamado toda vez em que é atualizado um registro no banco de dados
+ *
+ * @param user -> usuário da requisição corrente
+ * @param value -> registro a ser salvo
+ */
+
+ void afterUpdate(final User user, final Collection value);
+
/**
* Busca um registro pelo id
*
* @param user -> usuário da requisição corrente
* @param id -> id procurado
*/
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString() " +
+ " ) " +
+ "or " +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', this.getBasicRoles()), " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('simple_use', this.getBasicRoles()) " +
+ " ) " +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true"
+ )
T findById(final User user, final String id);
/**
- * Pega o primeiro registro que encontrar
+ * Busca um registro pelo id e não carrega os demais campos
*
* @param user -> usuário da requisição corrente
+ * @param id -> id procurado
*/
- T findFirst(final User user);
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString() " +
+ " ) " +
+ "or " +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', this.getBasicRoles()), " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('simple_use', this.getBasicRoles()) " +
+ " ) " +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true"
+ )
+ T findReferenceById(final User user, final String id);
/**
- * Busca o histórico de um determinado registro
+ * Busca varios registros pelo id
*
* @param user -> usuário da requisição corrente
- * @param id -> id do registro a ser buscado
- * @return Historic
+ * @param ids -> array de id a ser procurado
*/
- Historic loadHistoric(final User user, final String id);
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString() " +
+ " ) " +
+ "or " +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', this.getBasicRoles()), " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('simple_use', this.getBasicRoles()) " +
+ " ) " +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true"
+ )
+ Set findByIds(User user, String[] ids);
/**
- * Busca o histórico de um determinado registro
+ * Pega o primeiro registro que encontrar
*
- * @param user -> usuário da requisição corrente
- * @param value -> instancia do registro a ser buscado
- * @return Historic
+ * @param user -> usuário da requisição corrente
*/
- Historic loadHistoric(final User user, final T value);
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString()" +
+ " ) " +
+ "or " +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', this.getBasicRoles()), " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('simple_use', this.getBasicRoles()) " +
+ " )" +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true"
+ )
+ T findFirst(final User user);
/**
* Qualquer regra de négocio que valide o processo de delete deve ser implementada
@@ -140,6 +448,15 @@ public interface Service {
*/
void checkPrecondictionDelete(final User user, final String id);
+ /**
+ * Qualquer regra de négocio que valide o processo de delete deve ser implementada
+ * nesse método através de sobrescrita
+ *
+ * @param user -> usuário da requisição corrente
+ * @param value -> registro que está sendo deletado
+ */
+ void checkPrecondictionDelete(final User user, final T value);
+
/**
* Este método é chamado toda vez antes de se deltetar algum registro e depois de se chamar o metodo
* {@link #checkPrecondictionDelete(User, String)}.
@@ -157,6 +474,28 @@ public interface Service {
* @param user -> usuário da requisição corrente
* @param id -> id procurado
*/
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString()" +
+ " ) " +
+ "or " +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).toPatternRole('delete', this.getBasicRoles())" +
+ " )" +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('delete', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true"
+ )
void deleteById(final User user, final String id);
@@ -171,7 +510,7 @@ public interface Service {
/**
* Este método é chamado toda vez antes de se deltetar algum registro e depois de se chamar o metodo
- * {@link #checkPrecondictionDelete(User, String)}.
+ * {@link #checkPrecondictionDelete(User, T)}.
* Este método não deve ser utilizado para executar válidações mas sim para log's, pequenos ajuste
* e ou regras de négocio antes de se deletar algum registro
*
@@ -188,6 +527,28 @@ public interface Service {
* @param user -> usuário da requisição corrente
* @param value -> registro a ser deletado
*/
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString()" +
+ " ) " +
+ "or " +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).toPatternRole('delete', this.getBasicRoles())" +
+ " )" +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('delete', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true"
+ )
void delete(final User user, final T value);
/**
@@ -206,7 +567,30 @@ public interface Service {
* @param user -> usuário da requisição corrente
* @param allRequestParams -> Todos os parametros passado na query da requisição
*/
- Long count(final User user, final Map allRequestParams);
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString()" +
+ " ) " +
+ "or " +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', this.getBasicRoles()), " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('simple_use', this.getBasicRoles()) " +
+ " )" +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true"
+ )
+ Long count(final User user, final Map allRequestParams);
/**
* Realiza o processo de listagem com base nos critérios
@@ -215,10 +599,140 @@ public interface Service {
* @param user -> usuário da requisição corrente
* @param allRequestParams -> Todos os parametros passado na query da requisição
*/
- List findAll(final User user, final Map allRequestParams);
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString()" +
+ " ) " +
+ "or " +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', this.getBasicRoles()), " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('simple_use', this.getBasicRoles()) " +
+ " )" +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true"
+ )
+ List findAll(final User user, final Map allRequestParams);
/**
* Verifica se existe algum registro no DB
*/
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString()" +
+ " ) " +
+ "or " +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', this.getBasicRoles()), " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('simple_use', this.getBasicRoles()) " +
+ " )" +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true"
+ )
boolean isEmpty(final User user);
+
+ /**
+ * Recupera valor de uma propriedade especifica de um registro no banco de dados, levando em consideração o id
+ */
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString()" +
+ " ) " +
+ "or " +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', this.getBasicRoles()), " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('simple_use', this.getBasicRoles()) " +
+ " )" +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true"
+ )
+ Object getPropertyValueFromId(final User user, final String id, final String property);
+
+ /**
+ * Recupera valor de propriedades especificas de registro no banco de dados, levando em consideração a condição de filtro
+ */
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString()" +
+ " ) " +
+ "or " +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', this.getBasicRoles()), " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('simple_use', this.getBasicRoles()) " +
+ " )" +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true"
+ )
+ Object getPropertyValueFrom(final User user, final Map condictions, final String property);
+
+ /**
+ * Recupera valor de propriedades especificas de um registro no banco de dados, levando em consideração a condição de filtro
+ */
+ @PreAuthorize(
+ "this.isCheckRole()? " +
+ "(" +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).ROLE_OWNER.toString(), " +
+ " T(br.com.muttley.model.security.Role).ROLE_ROOT.toString()" +
+ " ) " +
+ "or " +
+ " hasAnyRole(" +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', this.getBasicRoles()), " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('simple_use', this.getBasicRoles()) " +
+ " )" +
+ "or (" +
+ " @userAgent.isMobile()? " +
+ " ( " +
+ " hasAnyRole( " +
+ " T(br.com.muttley.model.security.Role).toPatternRole('read', 'MOBILE_' + this.getBasicRoles()) " +
+ " ) " +
+ " ):false " +
+ " )" +
+ "): " +
+ " true"
+ )
+ Object[] getPropertiesValueFrom(final User user, final Map condictions, final String... properties);
}
diff --git a/muttley-domain-service/src/main/java/br/com/muttley/domain/service/Validator.java b/muttley-domain-service/src/main/java/br/com/muttley/domain/service/Validator.java
index bb5b6f4b..fa865a9b 100644
--- a/muttley-domain-service/src/main/java/br/com/muttley/domain/service/Validator.java
+++ b/muttley-domain-service/src/main/java/br/com/muttley/domain/service/Validator.java
@@ -6,6 +6,7 @@
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
+import java.util.Collection;
import java.util.Set;
import static org.springframework.util.CollectionUtils.isEmpty;
@@ -29,4 +30,8 @@ public final void validate(final Object o) {
throw new ConstraintViolationException("test", violations);
}
}
+
+ public final void validateCollection(final Collection> value) {
+ value.stream().forEach(this::validate);
+ }
}
diff --git a/muttley-domain-service/src/main/java/br/com/muttley/domain/service/factory/query/AbstractAggregationFactory.java b/muttley-domain-service/src/main/java/br/com/muttley/domain/service/factory/query/AbstractAggregationFactory.java
new file mode 100644
index 00000000..5af90095
--- /dev/null
+++ b/muttley-domain-service/src/main/java/br/com/muttley/domain/service/factory/query/AbstractAggregationFactory.java
@@ -0,0 +1,16 @@
+package br.com.muttley.domain.service.factory.query;
+
+import br.com.muttley.model.security.User;
+import br.com.muttley.security.infra.service.AuthService;
+import org.springframework.data.mongodb.core.aggregation.Aggregation;
+
+/**
+ * @author Joel Rodrigues Moreira 17/08/2020
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ *
+ * Interface básica para armazenar a implementação básica uma fábrica de query
+ */
+public abstract class AbstractAggregationFactory {
+ public abstract Aggregation factory(final AuthService authService, final User user);
+}
diff --git a/muttley-domain-service/src/main/java/br/com/muttley/domain/service/factory/query/AbstractQueryFactory.java b/muttley-domain-service/src/main/java/br/com/muttley/domain/service/factory/query/AbstractQueryFactory.java
new file mode 100644
index 00000000..3362ed44
--- /dev/null
+++ b/muttley-domain-service/src/main/java/br/com/muttley/domain/service/factory/query/AbstractQueryFactory.java
@@ -0,0 +1,16 @@
+package br.com.muttley.domain.service.factory.query;
+
+import br.com.muttley.model.security.User;
+import br.com.muttley.security.infra.service.AuthService;
+import org.springframework.data.mongodb.core.query.Query;
+
+/**
+ * @author Joel Rodrigues Moreira 17/08/2020
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ *
+ * Interface básica para armazenar a implementação básica uma fábrica de query
+ */
+public abstract class AbstractQueryFactory {
+ public abstract Query factory(final AuthService authService, final User user);
+}
diff --git a/muttley-domain-service/src/main/java/br/com/muttley/domain/service/impl/LocalModelServiceImpl.java b/muttley-domain-service/src/main/java/br/com/muttley/domain/service/impl/LocalModelServiceImpl.java
new file mode 100644
index 00000000..c2905997
--- /dev/null
+++ b/muttley-domain-service/src/main/java/br/com/muttley/domain/service/impl/LocalModelServiceImpl.java
@@ -0,0 +1,21 @@
+package br.com.muttley.domain.service.impl;
+
+import br.com.muttley.localcache.services.impl.AbstractLocalModelServiceImpl;
+import br.com.muttley.model.Model;
+import br.com.muttley.redis.service.RedisService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author Joel Rodrigues Moreira on 02/09/2021.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Service
+public class LocalModelServiceImpl extends AbstractLocalModelServiceImpl {
+
+ @Autowired
+ public LocalModelServiceImpl(final RedisService redisService) {
+ super(redisService);
+ }
+}
diff --git a/muttley-domain-service/src/main/java/br/com/muttley/domain/service/impl/ModelServiceImpl.java b/muttley-domain-service/src/main/java/br/com/muttley/domain/service/impl/ModelServiceImpl.java
index 2b1b0e90..d20abd1c 100644
--- a/muttley-domain-service/src/main/java/br/com/muttley/domain/service/impl/ModelServiceImpl.java
+++ b/muttley-domain-service/src/main/java/br/com/muttley/domain/service/impl/ModelServiceImpl.java
@@ -1,46 +1,82 @@
package br.com.muttley.domain.service.impl;
import br.com.muttley.domain.service.ModelService;
+import br.com.muttley.domain.service.impl.utils.MetadataAndIdModel;
import br.com.muttley.exception.throwables.MuttleyBadRequestException;
import br.com.muttley.exception.throwables.MuttleyNoContentException;
-import br.com.muttley.exception.throwables.MuttleyNotFoundException;
-import br.com.muttley.model.Historic;
+import br.com.muttley.exception.throwables.repository.MuttleyRepositoryOwnerNotInformedException;
+import br.com.muttley.model.BasicAggregateResultCount;
+import br.com.muttley.model.Document;
import br.com.muttley.model.Model;
+import br.com.muttley.model.security.Owner;
import br.com.muttley.model.security.User;
+import br.com.muttley.mongo.service.infra.AggregationUtils;
import br.com.muttley.mongo.service.repository.CustomMongoRepository;
+import com.mongodb.BasicDBObject;
+import org.bson.types.ObjectId;
+import org.springframework.data.mongodb.core.BulkOperations;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
+import org.springframework.data.mongodb.core.aggregation.AggregationResults;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.data.mongodb.core.query.Update;
import org.springframework.util.CollectionUtils;
+import org.springframework.util.ObjectUtils;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import static br.com.muttley.model.security.domain.Domain.PUBLIC;
+import static br.com.muttley.model.security.domain.Domain.RESTRICTED;
+import static java.util.Arrays.asList;
import static java.util.Objects.isNull;
+import static java.util.stream.Collectors.groupingBy;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
+import static org.springframework.data.mongodb.core.BulkOperations.BulkMode.UNORDERED;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.match;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.newAggregation;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.project;
+import static org.springframework.data.mongodb.core.query.Criteria.where;
/**
* @author Joel Rodrigues Moreira on 30/01/18.
* @project muttley-cloud
*/
public abstract class ModelServiceImpl extends ServiceImpl implements ModelService {
- final CustomMongoRepository repository;
+ protected final CustomMongoRepository repository;
- public ModelServiceImpl(final CustomMongoRepository repository, final Class clazz) {
- super(repository, clazz);
+ public ModelServiceImpl(final CustomMongoRepository repository, final MongoTemplate mongoTemplate, final Class clazz) {
+ super(repository, mongoTemplate, clazz);
this.repository = repository;
}
@Override
public T save(final User user, final T value) {
//verificando se realmente está criando um novo registro
- if (value.getId() != null) {
- throw new MuttleyBadRequestException(clazz, "id", "Não é possível criar um registro com um id existente");
- }
+ checkIdForSave(value);
+ //setando o dono do registro
value.setOwner(user);
- //garantindo que o históriconão ficará nulo
- value.setHistoric(this.createHistoric(user));
- //validando dados
- this.validator.validate(value);
+ //garantindo que o metadata ta preenchido
+ this.generateNewMetadataFor(user, value, null);
+ //processa regra de negocio antes de qualquer validação
+ this.beforeSave(user, value);
//verificando precondições
this.checkPrecondictionSave(user, value);
- return repository.save(user.getCurrentOwner(), value);
+ //validando dados do objeto
+ this.validator.validate(value);
+ final T salvedValue = repository.save(user.getCurrentOwner(), value);
+ //realizando regras de enegocio depois do objeto ter sido salvo
+ this.afterSave(user, salvedValue);
+ //valor salvo
+ return salvedValue;
}
@Override
@@ -48,6 +84,25 @@ public void checkPrecondictionSave(final User user, final T value) {
}
+ @Override
+ public Collection save(final User user, final Collection values) {
+ //verificando se realmente está criando um novo registro
+ checkIdForSave(values);
+ //garantindo que o metadata ta preenchido
+ this.generateNewMetadataFor(user, values, null);
+ //processa regra de negocio antes de qualquer validação
+ this.beforeSave(user, values);
+ //verificando precondições
+ this.checkPrecondictionSave(user, values);
+ //validando dados do objeto
+ this.validator.validateCollection(values);
+ final Collection otherValues = repository.save(user.getCurrentOwner(), values);
+ //realizando regras de enegocio depois do objeto ter sido salvo
+ this.afterSave(user, otherValues);
+ //valor salvo
+ return otherValues;
+ }
+
@Override
public T update(final User user, final T value) {
//verificando se realmente está alterando um registro
@@ -55,17 +110,22 @@ public T update(final User user, final T value) {
throw new MuttleyBadRequestException(clazz, "id", "Não é possível alterar um registro sem informar um id válido");
}
//verificando se o registro realmente existe
- if (!this.repository.exists(value.getId())) {
- throw new MuttleyNotFoundException(clazz, "id", "Registro não encontrado");
+ if (!this.repository.exists(value)) {
+ throw this.createNotFoundExceptionById(user, value.getId());
+ //throw new MuttleyNotFoundException(clazz, "id", "Registro não encontrado");
}
value.setOwner(user);
- //gerando histórico de alteração
- value.setHistoric(generateHistoricUpdate(user, repository.loadHistoric(user.getCurrentOwner(), value)));
- //validando dados
- this.validator.validate(value);
+ //gerando metadata de alteração
+ this.metadataService.generateMetaDataUpdateFor(user, this.repository.loadMetaData(user.getCurrentOwner(), value), value);
+ //processa regra de negocio antes de qualquer validação
+ this.beforeUpdate(user, value);
//verificando precondições
checkPrecondictionUpdate(user, value);
- return repository.save(user.getCurrentOwner(), value);
+ //validando dados
+ this.validator.validate(value);
+ final T salvedValue = repository.save(user.getCurrentOwner(), value);
+ afterUpdate(user, salvedValue);
+ return salvedValue;
}
@Override
@@ -73,64 +133,169 @@ public void checkPrecondictionUpdate(final User user, final T value) {
}
+ @Override
+ public void update(final User user, final Collection values) {
+ //verificando se realmente está alterando um registro
+ this.checkIdForUpdate(values);
+ //verificando se o registro realmente existe
+
+ final List metadatasAndIds = this.loadIdsAndMetadatasAndHisotricsFor(user, values);
+ final Map> agroupedValues = values.stream()
+ .collect(groupingBy(it -> {
+ final Optional itemOpt = metadatasAndIds
+ .parallelStream()
+ .filter(itMeta -> Objects.equals(it.getId(), itMeta.getId()))
+ .findFirst();
+ if (itemOpt.isPresent()) {
+ final MetadataAndIdModel metadataAndIdModel = itemOpt.get();
+ this.metadataService.generateMetaDataUpdateFor(user, metadataAndIdModel.getMetadata(), it);
+ return true;
+ }
+ return false;
+ }));
+
+ final List valuesForSave = agroupedValues.get(Boolean.TRUE);
+
+ if (!CollectionUtils.isEmpty(valuesForSave)) {
+ //gerando metadata de alteração
+ //valuesForSave.forEach(it -> generateMetaDataUpdate(user, it));
+ //gerando histórico de alteração
+ //valuesForSave.forEach(it -> it.setHistoric(generateHistoricUpdate(user, repository.loadHistoric(it))));
+ //processa regra de negocio antes de qualquer validação
+ beforeUpdate(user, valuesForSave);
+ //verificando precondições
+ checkPrecondictionUpdate(user, valuesForSave);
+ //validando dados
+ this.validator.validateCollection(valuesForSave);
+ //criando o bulk para atualização em massa
+ final BulkOperations operations = this.mongoTemplate.bulkOps(UNORDERED, clazz);
+ valuesForSave.forEach(it -> {
+ operations.updateOne(
+ new Query(
+ where("owner.$id").is(user.getCurrentOwner().getObjectId())
+ .and("_id").is(new ObjectId(it.getId()))
+ ),
+
+ Update.fromDBObject(new BasicDBObject("$set", it))
+ );
+ });
+ operations.execute();
+ //final Collection otherValue = repository.save(user.getCurrentOwner(), valuesForSave);
+ //realizando regras de enegocio depois do objeto ter sido alterado
+ afterUpdate(user, valuesForSave);
+ }
+ final List valuesNotSaved = agroupedValues.get(Boolean.FALSE);
+ if (!CollectionUtils.isEmpty(valuesNotSaved)) {
+ throw this.createNotFoundExceptionById(user, valuesNotSaved.parallelStream().map(Document::getId).collect(toList()));
+ /*throw new MuttleyNotFoundException(clazz, "id", "Registros não encontrados")
+ .addDetails("ids", valuesNotSaved.parallelStream().map(Document::getId).collect(toList()));*/
+ }
+ }
+
+ protected List loadIdsAndMetadatasAndHisotricsFor(final User user, final Collection values) {
+ /**
+ * db.getCollection("contas-pagar").aggregate([
+ * {$match:{"owner.$id":ObjectId("60cc8953279e841c0974da56"), _id:{$in:[ObjectId("60cca012279e8437442bc81c"), ObjectId("60cca012279e8437442bc81d")]}}},
+ * {$project:{_id:1, metadata:1}}
+ * ])
+ */
+ final AggregationResults ids = this.mongoTemplate.aggregate(
+ newAggregation(
+ match(where("owner.$id").is(user.getCurrentOwner().getObjectId())
+ .and("id").in(
+ values.parallelStream().map(it -> it.getObjectId()).collect(toSet())
+ )),
+ project("id", "metadata")
+ ),
+ clazz, MetadataAndIdModel.class);
+ if (ids == null || CollectionUtils.isEmpty(ids.getMappedResults())) {
+ return Collections.emptyList();
+ }
+ return ids.getMappedResults();
+ }
+
@Override
public T findById(final User user, final String id) {
- if (isNull(id)) {
+ if (!ObjectId.isValid(id)) {
throw new MuttleyBadRequestException(clazz, "id", "informe um id válido");
}
final T result = this.repository.findOne(user.getCurrentOwner(), id);
if (isNull(result)) {
- throw new MuttleyNotFoundException(clazz, "id", id + " este registro não foi encontrado");
+ throw this.createNotFoundExceptionById(user, id);
+ //throw new MuttleyNotFoundException(clazz, "id", id + " este registro não foi encontrado");
}
return result;
}
@Override
- public T findFirst(final User user) {
- final T result = this.repository.findFirst(user.getCurrentOwner());
- if (isNull(result)) {
- throw new MuttleyNotFoundException(clazz, "user", "Nenhum registro encontrado");
+ public T findReferenceById(User user, String id) {
+ if (!ObjectId.isValid(id)) {
+ throw new MuttleyBadRequestException(clazz, "id", "informe um id válido");
}
- return result;
+
+ final AggregationResults results = this.mongoTemplate.aggregate(
+ newAggregation(
+ match(where("owner.$id").is(user.getCurrentOwner().getObjectId()).and("id").is(new ObjectId(id))),
+ project("id", "owner")
+ )
+ , clazz, clazz);
+ if (results == null || results.getUniqueMappedResult() == null) {
+ throw this.createNotFoundExceptionById(user, id);
+ //throw new MuttleyNotFoundException(clazz, "id", id + " este registro não foi encontrado");
+ }
+ return results.getUniqueMappedResult();
}
@Override
- public Historic loadHistoric(final User user, final String id) {
- final Historic historic = repository.loadHistoric(user.getCurrentOwner(), id);
- if (isNull(historic)) {
- throw new MuttleyNotFoundException(clazz, "historic", "Nenhum registro encontrado");
+ public Set findByIds(final User user, final String[] ids) {
+ if (ObjectUtils.isEmpty(ids)) {
+ throw new MuttleyBadRequestException(clazz, "id", "informe pelo menos um id válido");
+ }
+ if (ids.length > 50) {
+ throw new MuttleyBadRequestException(clazz, "ids", "Quantidade máxima excedida")
+ .addDetails("min", 1)
+ .addDetails("max", 50);
+ }
+ final Set records = this.repository.findMulti(user.getCurrentOwner(), ids);
+ if (records == null) {
+ return Collections.emptySet();
}
- return historic;
+ return records;
}
@Override
- public Historic loadHistoric(final User user, final T value) {
- final Historic historic = repository.loadHistoric(user.getCurrentOwner(), value);
- if (isNull(historic)) {
- throw new MuttleyNotFoundException(clazz, "historic", "Nenhum registro encontrado");
+ public T findFirst(final User user) {
+ final T result = this.repository.findFirst(user.getCurrentOwner());
+ if (isNull(result)) {
+ throw this.createNotFoundExceptionById(user).setField("user");
+ //throw new MuttleyNotFoundException(clazz, "user", "Nenhum registro encontrado");
}
- return historic;
+ return result;
}
@Override
public void deleteById(final User user, final String id) {
+ this.beforeDelete(user, id);
checkPrecondictionDelete(user, id);
if (!repository.exists(user.getCurrentOwner(), id)) {
- throw new MuttleyNotFoundException(clazz, "id", id + " este registro não foi encontrado");
+ throw this.createNotFoundExceptionById(user, id);
+ //throw new MuttleyNotFoundException(clazz, "id", id + " este registro não foi encontrado");
}
this.repository.delete(user.getCurrentOwner(), id);
- beforeDelete(user, id);
+ this.afterDelete(user, id);
}
@Override
public void delete(final User user, final T value) {
- checkPrecondictionDelete(user, value.getId());
+ this.beforeDelete(user, value);
+ checkPrecondictionDelete(user, value);
if (!repository.exists(user.getCurrentOwner(), value)) {
- throw new MuttleyNotFoundException(clazz, "id", value.getId() + " este registro não foi encontrado");
+ throw this.createNotFoundExceptionById(user, value.getId());
+ //throw new MuttleyNotFoundException(clazz, "id", value.getId() + " este registro não foi encontrado");
}
this.repository.delete(user.getCurrentOwner(), value);
- beforeDelete(user, value);
+ this.afterDelete(user, value);
}
@Override
@@ -138,6 +303,11 @@ public void checkPrecondictionDelete(final User user, final String id) {
}
+ @Override
+ public void checkPrecondictionDelete(final User user, final T value) {
+
+ }
+
@Override
public void beforeDelete(final User user, final T value) {
@@ -149,17 +319,47 @@ public void beforeDelete(final User user, final String id) {
}
@Override
- public Long count(final User user, final Map allRequestParams) {
- return this.repository.count(user.getCurrentOwner(), allRequestParams);
+ public Long count(final User user, final Map allRequestParams) {
+ validateOwner(user.getCurrentOwner());
+
+
+ final AggregationResults result = mongoTemplate.aggregate(
+ newAggregation(
+ AggregationUtils.createAggregationsCount(
+ this.entityMetaData,
+ this.getFilterAggregationOperationByWorkteam(user),
+ addOwnerQueryParam(user.getCurrentOwner(), allRequestParams),
+ "result"
+ )
+ ),
+ clazz, BasicAggregateResultCount.class);
+ return result.getUniqueMappedResult() != null ? result.getUniqueMappedResult().getResult() : 0l;
}
@Override
- public List findAll(final User user, final Map allRequestParams) {
- final List results = this.repository.findAll(user.getCurrentOwner(), allRequestParams);
- if (CollectionUtils.isEmpty(results)) {
+ public List findAll(final User user, final Map allRequestParams) {
+ //validando se o usuário tem owner informado
+ this.validateOwner(user.getCurrentOwner());
+ //perando o filtro
+
+ final List aggregateions = AggregationUtils.createAggregations(this.entityMetaData, null, addOwnerQueryParam(user.getCurrentOwner(), allRequestParams));
+ //adicionando filtro para agregação
+ aggregateions.addAll(this.getFilterAggregationOperationByWorkteam(user));
+
+ final AggregationResults results = mongoTemplate.aggregate(
+ newAggregation(aggregateions), clazz, clazz
+ );
+
+ if (results == null || CollectionUtils.isEmpty(results.getMappedResults())) {
throw new MuttleyNoContentException(clazz, "user", "não foi encontrado nenhum registro");
}
- return results;
+ return results.getMappedResults();
+ }
+
+ @Override
+ protected AggregationResults createAggregateForLoadProperties(final User user, final Map condictions, final String... properties) {
+ condictions.put("owner.$id", user.getCurrentOwner().getObjectId());
+ return super.createAggregateForLoadProperties(user, condictions, properties);
}
/**
@@ -172,4 +372,88 @@ private final void checkOwner(final User user, final T value) {
throw new MuttleyBadRequestException(clazz, "user", "não é possível fazer a alteração do usuário dono do registro");
}
}
+
+
+ private final void validateOwner(final Owner owner) {
+ if (owner == null) {
+ throw new MuttleyRepositoryOwnerNotInformedException(this.clazz);
+ }
+ }
+
+ private final Map addOwnerQueryParam(final Owner owner, final Map queryParams) {
+ final Map query = new LinkedHashMap<>(1);
+ query.put("owner.$id.$is", owner.getObjectId().toString());
+ if (queryParams != null) {
+ query.putAll(queryParams);
+ }
+ return query;
+ }
+
+ protected List getFilterAggregationOperationByWorkteam(final User user) {
+ //se for o owner do sistema não faz sentido colocar restrição pois ele tem acesso a tudo
+ if (user.isOwner()) {
+ return Collections.emptyList();
+ }
+ return asList(
+ match(
+ new Criteria().orOperator(
+ //pegando todos os registros que forem publicos
+ where("metadata.domain").is(PUBLIC),
+ //pegando todos os registros que o proprio usuário criou
+ where("metadata.historic.createdBy.$id").is(user.getObjectId()),
+ //pegando todos os registros de subordinados
+ where("metadata.historic.createdBy.$id")
+ .in(
+ user.getWorkTeamDomain()
+ .getSubordinates()
+ .parallelStream()
+ .map(it -> it.getUser().getObjectId())
+ .collect(toSet())
+ ),
+ //pegando todos os registros dos colegas presentes no workteam
+ where("metadata.historic.createdBy.$id")
+ .in(
+ user.getWorkTeamDomain()
+ .getColleagues()
+ .parallelStream()
+ .map(it -> it.getUser().getObjectId())
+ .collect(toSet())
+ ).and("metadata.domain").is(RESTRICTED)
+ )
+ )
+ );
+ }
+
+ protected List getFilterCriteriaByWorkteam(final User user) {
+ if (user.isOwner()) {
+ return null;
+ }
+ return asList(
+
+ new Criteria().orOperator(
+ //pegando todos os registros que forem publicos
+ where("metadata.domain").is(PUBLIC),
+ //pegando todos os registros que o proprio usuário criou
+ where("metadata.historic.createdBy.$id").is(user.getObjectId()),
+ //pegando todos os registros de subordinados
+ where("metadata.historic.createdBy.$id")
+ .in(
+ user.getWorkTeamDomain()
+ .getSubordinates()
+ .parallelStream()
+ .map(it -> it.getUser().getObjectId())
+ .collect(toSet())
+ ),
+ //pegando todos os registros dos colegas presentes no workteam
+ where("metadata.historic.createdBy.$id")
+ .in(
+ user.getWorkTeamDomain()
+ .getColleagues()
+ .parallelStream()
+ .map(it -> it.getUser().getObjectId())
+ .collect(toSet())
+ ).and("metadata.domain").is(RESTRICTED)
+ )
+ );
+ }
}
diff --git a/muttley-domain-service/src/main/java/br/com/muttley/domain/service/impl/ModelSyncServiceImpl.java b/muttley-domain-service/src/main/java/br/com/muttley/domain/service/impl/ModelSyncServiceImpl.java
new file mode 100644
index 00000000..0bd1514f
--- /dev/null
+++ b/muttley-domain-service/src/main/java/br/com/muttley/domain/service/impl/ModelSyncServiceImpl.java
@@ -0,0 +1,464 @@
+package br.com.muttley.domain.service.impl;
+
+import br.com.muttley.domain.service.ModelSyncService;
+import br.com.muttley.exception.throwables.MuttleyBadRequestException;
+import br.com.muttley.exception.throwables.MuttleyConflictException;
+import br.com.muttley.exception.throwables.MuttleyNotFoundException;
+import br.com.muttley.localcache.services.LocalModelService;
+import br.com.muttley.model.Historic;
+import br.com.muttley.model.ModelSync;
+import br.com.muttley.model.SyncObjectId;
+import br.com.muttley.model.security.User;
+import br.com.muttley.mongo.service.repository.CustomMongoRepository;
+import com.google.common.collect.Lists;
+import org.bson.types.ObjectId;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.aggregation.AggregationResults;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static java.util.stream.Collectors.joining;
+import static org.springframework.data.domain.Sort.Direction.DESC;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.limit;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.match;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.newAggregation;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.project;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.sort;
+import static org.springframework.data.mongodb.core.query.Criteria.where;
+
+/**
+ * @author Joel Rodrigues Moreira on 05/05/2021.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public abstract class ModelSyncServiceImpl extends ModelServiceImpl implements ModelSyncService {
+
+ protected final MongoTemplate mongoTemplate;
+ protected final CustomMongoRepository repository;
+ protected final Integer MAX_RECORD_SYNC;
+ @Autowired
+ protected LocalModelService localModelService;
+
+
+ public ModelSyncServiceImpl(final CustomMongoRepository repository, final Class clazz, final MongoTemplate mongoTemplate) {
+ this(repository, clazz, mongoTemplate, 100);
+ }
+
+ public ModelSyncServiceImpl(final CustomMongoRepository repository, final Class clazz, final MongoTemplate mongoTemplate, final Integer maxRecordSync) {
+ super(repository, mongoTemplate, clazz);
+ this.mongoTemplate = mongoTemplate;
+ this.repository = repository;
+ this.MAX_RECORD_SYNC = maxRecordSync;
+ }
+
+
+ @Override
+ public T save(final User user, final T value) {
+ //validando o sync
+ this.checkSyncIndex(user, value);
+ this.checkDtSync(user, value);
+ return super.save(user, value);
+ }
+
+ @Override
+ public T update(final User user, final T value) {
+ //devemos garantir que ninguem alterou o sync do registro
+ final T other = findById(user, value.getId());
+ //garantindo o sync do registro
+ value.setSync(other.getSync());
+ this.checkDtSync(user, value);
+ final T updatedValue = super.update(user, value);
+ //removendo do cache temporario caso exista
+ this.localModelService.expire(user, clazz, updatedValue.getId());
+ this.localModelService.expire(user, clazz, updatedValue.getSync());
+ return updatedValue;
+ }
+
+ @Override
+ public void update(User user, Collection values) {
+ //devemos garantir que ninguem alterou o sync do registro
+ final Set syncObjectIds = this.getSyncsOfIds(user, values.parallelStream()
+ .map(T::getId)
+ .collect(Collectors.toSet()));
+ values.parallelStream()
+ .forEach(it -> {
+ it.setSync(
+ syncObjectIds
+ .parallelStream()
+ .filter(itt -> itt.getId().equals(it.getId()))
+ .findFirst()
+ .orElseThrow(MuttleyBadRequestException::new)
+ .getSync()
+ ).setDtSync(new Date());
+ });
+ super.update(user, values);
+ values.forEach(it -> {
+ this.localModelService.expire(user, clazz, it.getId());
+ this.localModelService.expire(user, clazz, it.getSync());
+ });
+ }
+
+ @Override
+ public T findById(User user, String id) {
+ final T result;
+ /*if (this.localModelService.containsInCahce(user, clazz, id)) {
+ result = (T) this.localModelService.loadModel(user, clazz, id);
+ } else {*/
+ result = super.findById(user, id);
+/*
+ this.localModelService.addCache(user, result, id);
+ }
+*/
+ return result;
+ }
+
+ @Override
+ public T findReferenceById(User user, String id) {
+ final T result;
+ if (this.localModelService.containsReferenceInCahce(user, clazz, id)) {
+ result = (T) this.localModelService.loadReference(user, clazz, id);
+ } else {
+ if (!ObjectId.isValid(id)) {
+ throw new MuttleyBadRequestException(clazz, "id", "informe um id válido");
+ }
+
+ final AggregationResults results = this.mongoTemplate.aggregate(
+ newAggregation(
+ match(where("owner.$id").is(user.getCurrentOwner().getObjectId()).and("id").is(new ObjectId(id))),
+ project("id", "owner", "sync")
+ )
+ , clazz, clazz);
+ if (results == null || results.getUniqueMappedResult() == null) {
+ throw this.createNotFoundExceptionById(user, id);
+ //throw new MuttleyNotFoundException(clazz, "id", id + " este registro não foi encontrado");
+ }
+ result = results.getUniqueMappedResult();
+ this.localModelService.addReferenceCache(user, result, id);
+ }
+
+
+ return result;
+ }
+
+
+ @Override
+ public void synchronize(final User user, final Collection records) {
+ //this.mongoTemplate.bulkOps(BulkOperations.BulkMode.UNORDERED, clazz).execute();
+ if (MAX_RECORD_SYNC != null && records.size() > MAX_RECORD_SYNC) {
+ throw new MuttleyBadRequestException(this.clazz, null, "Cada requisiçao pode ter no maximo 100 registros");
+ }
+ //quebrando em pacotes de 50 registros
+ Lists.partition(records instanceof List ? (List) records : new ArrayList<>(records), 50)
+ .stream()
+ //processando para carregar os respectivos ids caso os registros não tenha id
+ .map(subList -> {
+ this.getIdsOfSyncs(user, subList.parallelStream()
+ //removendo os itens que já tem id
+ .filter(it -> StringUtils.isEmpty(it.getId()))
+ //pegando apenas os ids para busca
+ .map(ModelSync::getSync)
+ .collect(Collectors.toSet())
+ ).parallelStream()
+ //com base no syncs carregado, vamos interar a lista e preencher nos objtos que não tem sync
+ .forEach((final SyncObjectId syncId) -> {
+ subList.parallelStream()
+ .filter(it -> syncId.getSync().equals(it.getSync()))
+ .forEach(it -> it.setId(syncId.getId()));
+ });
+ return subList;
+ }).forEach(subList -> {
+ subList.parallelStream()
+ .peek(it -> this.checkDtSync(user, it))
+ //agrupando os dados em registros que possui id ou não
+ .collect(Collectors.groupingBy(ModelSync::contaisObjectId))
+ //interando o agrupamento
+ .entrySet()
+ .forEach((key) -> {
+ if (key.getKey()) {
+ //update em cascata
+ super.update(user, key.getValue());
+ //removendo do cache temporario caso exista
+ key.getValue().forEach(it -> {
+ this.localModelService.expire(user, clazz, it.getId());
+ this.localModelService.expire(user, clazz, it.getSync());
+ });
+ } else {
+ //insere em cascata
+ saveOnly(user, key.getValue());
+ }
+ });
+ ;
+ });/*
+
+
+ Lists.partition(records instanceof List ? (List) records : new ArrayList<>(records), 50);
+
+ patials.stream()
+ .forEach(it -> {
+ this.getIdsOfSyncs(user, it.stream().filter(iit -> StringUtils.isEmpty(iit.getId())).map(iit -> iit.getSync()).collect(Collectors.toSet()))
+ .forEach(iit -> {
+ it.stream().filter(iiit -> iit.getSync().equals(iiit.getSync())).forEach(iiit ->);
+ });
+ });*//*
+ records.stream()
+ .map((final T value) -> {
+ if (StringUtils.isEmpty(value.getId())) {
+ value.setId(this.getIdOfSync(user, value.getSync()));
+ }
+ return value;
+ })
+ .forEach((final T value) -> {
+ if (value.getId() == null) {
+ this.save(user, value);
+ } else {
+ this.update(user, value);
+ }
+ });*/
+ }
+
+ @Override
+ public void checkSyncIndex(final User user, final T value) {
+ if (!StringUtils.isEmpty(value.getSync())) {
+ //devemos garantir se já não existe o determinado sync
+ if (this.existSync(user, value)) {
+ throw new MuttleyConflictException(clazz, "sync", "Já existe um registro com esse sync");
+ }
+ } else {
+ //Se não foi passado nenhum syn vamos gerar algum
+ value.setSync("agrifocus-" + new ObjectId(new Date()).toString());
+ }
+ }
+
+ @Override
+ public T updateBySync(final User user, final T value) {
+ this.validadeSyncParam(value.getSync());
+ //devemos garantir que ninguem irá altera o ObjectId do registro
+ final T other = findBySync(user, value.getSync());
+ //garantindo o id do registro
+ value.setId(other.getId());
+ final T updatedValue = update(user, value);
+ this.localModelService.expire(user, clazz, updatedValue.getId());
+ this.localModelService.expire(user, clazz, updatedValue.getSync());
+ return updatedValue;
+ }
+
+ @Override
+ public T findBySync(final User user, final String sync) {
+ this.validadeSyncParam(sync);
+ final T value;
+ /*if (this.localModelService.containsInCahce(user, clazz, sync)) {
+ value = (T) this.localModelService.loadModel(user, clazz, sync);
+ } else {*/
+
+ value = this.mongoTemplate
+ .findOne(
+ new Query(
+ where("owner.$id").is(user.getCurrentOwner().getObjectId())
+ .and("sync").is(sync)
+ ), clazz);
+ if (value == null) {
+ throw this.createNotFoundExceptionBySync(user, sync);
+ /*throw new MuttleyNotFoundException(clazz, "sync", "Registro não encontrado!")
+ .addDetails("syncInformado", sync);*/
+ }
+ /*this.localModelService.addCache(user, (Model) value, sync);
+ }*/
+ return value;
+ }
+
+ @Override
+ public T findReferenceBySync(User user, String sync) {
+ this.validadeSyncParam(sync);
+ final T result;
+ if (this.localModelService.containsReferenceInCahce(user, clazz, sync)) {
+ result = (T) this.localModelService.loadReference(user, clazz, sync);
+ } else {
+
+ final AggregationResults results = this.mongoTemplate.aggregate(
+ newAggregation(
+ match(where("owner.$id").is(user.getCurrentOwner().getObjectId()).and("sync").is(sync)),
+ project("id", "owner", "sync")
+ )
+ , clazz, clazz);
+ if (results == null || results.getUniqueMappedResult() == null) {
+ throw this.createNotFoundExceptionBySync(user, sync);
+ /*throw new MuttleyNotFoundException(clazz, "sync", "Registro não encontrado!")
+ .addDetails("syncInformado", sync);*/
+ }
+ result = results.getUniqueMappedResult();
+ this.localModelService.addReferenceCache(user, result, sync);
+ }
+
+
+ return result;
+ }
+
+ @Override
+ public T findByIdOrSync(final User user, final String idSync) {
+ this.validadeIdOrSync(idSync);
+ if (ObjectId.isValid(idSync)) {
+ return findById(user, idSync);
+ } else {
+ return findBySync(user, idSync);
+ }
+ }
+
+ @Override
+ public Date getLastModify(final User user) {
+ final AggregationResults results = this.mongoTemplate
+ .aggregate(
+ newAggregation(
+ match(where("owner.$id").is(user.getCurrentOwner().getObjectId())),
+ project().and("$historic.dtChange").as("dtChange"),
+ sort(DESC, "dtChange"),
+ limit(1)
+ ), clazz, Historic.class
+ );
+ return results.getUniqueMappedResult() != null ? ((Historic) results.getUniqueMappedResult()).getDtChange() : null;
+ }
+
+ @Override
+ public void deleteById(final User user, final String id) {
+ super.deleteById(user, id);
+ this.localModelService.expire(user, clazz, id);
+ }
+
+ @Override
+ public void delete(final User user, final T value) {
+ super.delete(user, value);
+ this.localModelService.expire(user, clazz, value.getId());
+ this.localModelService.expire(user, clazz, value.getSync());
+ }
+
+ @Override
+ public void deleteBySync(final User user, final String sync) {
+ this.validadeSyncParam(sync);
+ final T value = findBySync(user, sync);
+ this.delete(user, value);
+ this.localModelService.expire(user, clazz, value.getId());
+ this.localModelService.expire(user, clazz, value.getSync());
+ }
+
+ @Override
+ public boolean existSync(final User user, final T value) {
+ return this.mongoTemplate.exists(
+ new Query(
+ where("owner.$id").is(user.getCurrentOwner().getObjectId()).and("sync").is(value.getSync())
+ ), clazz
+ );
+ }
+
+ @Override
+ public String getIdOfSync(final User user, final String sync) {
+ this.validadeSyncParam(sync);
+ final Map map = new HashMap<>();
+ map.put("sync", sync);
+ return (String) this.getPropertyValueFrom(user, map, "id");
+ /*final AggregationResults results = this.mongoTemplate
+ .aggregate(
+ newAggregation(
+ match(
+ where("owner.$id").is(user.getCurrentOwner().getObjectId())
+ .and("sync").is(sync)
+ ),
+ project("id"),
+ limit(1)
+ ), clazz, clazz
+ );
+ return results.getUniqueMappedResult() != null ? ((T) results.getUniqueMappedResult()).getId() : null;*/
+ }
+
+ @Override
+ public String getSyncOfId(User user, String id) {
+ final Map map = new HashMap<>();
+ map.put("_id", new ObjectId(id));
+ return (String) this.getPropertyValueFrom(user, map, "sync");
+ }
+
+ @Override
+ public Set getIdsOfSyncs(final User user, final Set syncs) {
+ if (CollectionUtils.isEmpty(syncs)) {
+ return Collections.emptySet();
+ }
+
+ final AggregationResults results = this.mongoTemplate.aggregate(
+ newAggregation(
+ match(where("owner.$id").is(user.getCurrentOwner().getObjectId()).and("sync").in(syncs)),
+ project("id", "sync")
+ )
+ , clazz, SyncObjectId.class);
+
+ if (results == null || CollectionUtils.isEmpty(results.getMappedResults())) {
+ return Collections.emptySet();
+ }
+ return new HashSet<>(results.getMappedResults());
+ }
+
+ @Override
+ public Set getSyncsOfIds(User user, Set ids) {
+ if (CollectionUtils.isEmpty(ids)) {
+ return Collections.emptySet();
+ }
+
+ final AggregationResults results = this.mongoTemplate.aggregate(
+ newAggregation(
+ match(where("owner.$id").is(user.getCurrentOwner().getObjectId()).and("_id").in(ids.parallelStream().map(ObjectId::new).collect(Collectors.toList()))),
+ project("id", "sync")
+ )
+ , clazz, SyncObjectId.class);
+
+ if (results == null || CollectionUtils.isEmpty(results.getMappedResults())) {
+ return Collections.emptySet();
+ }
+ return new HashSet<>(results.getMappedResults());
+ }
+
+ protected void checkDtSync(final User user, final T value) {
+ if (value.getDtSync() == null) {
+ value.setDtSync(new Date());
+ }
+ }
+
+ protected void validadeSyncParam(final String sync) {
+ if (StringUtils.isEmpty(sync)) {
+ throw new MuttleyBadRequestException(this.clazz, "sync", "Informe um sync válido");
+ }
+ }
+
+ protected void validadeIdOrSync(final String idSync) {
+ this.validadeSyncParam(idSync);
+ }
+
+ protected MuttleyNotFoundException createNotFoundExceptionBySync(final User user) {
+ return new MuttleyNotFoundException(clazz, "sync", this.getSingularNameAlias(this.clazz) + " não encontrado(a)");
+ }
+
+ protected MuttleyNotFoundException createNotFoundExceptionBySync(final User user, final String sync) {
+ final MuttleyNotFoundException exception = new MuttleyNotFoundException(clazz, "id", this.getSingularNameAlias(this.clazz) + "(" + sync + ") não encontrado(a)");
+ exception.addDetails("sync", sync);
+ return exception;
+ }
+
+ protected MuttleyNotFoundException createNotFoundExceptionBySync(final User user, final Collection syncs) {
+ final String syncsConcat = syncs.parallelStream()
+ .limit(3)
+ .collect(joining(", "));
+
+ final MuttleyNotFoundException exception = new MuttleyNotFoundException(clazz, "id", this.getSingularNameAlias(this.clazz) + "(" + syncsConcat + ") não encontrados(as)");
+ exception.addDetails("syncs", syncs);
+ return exception;
+ }
+}
diff --git a/muttley-domain-service/src/main/java/br/com/muttley/domain/service/impl/ServiceImpl.java b/muttley-domain-service/src/main/java/br/com/muttley/domain/service/impl/ServiceImpl.java
index 99bd2087..b01dd456 100644
--- a/muttley-domain-service/src/main/java/br/com/muttley/domain/service/impl/ServiceImpl.java
+++ b/muttley-domain-service/src/main/java/br/com/muttley/domain/service/impl/ServiceImpl.java
@@ -5,18 +5,40 @@
import br.com.muttley.exception.throwables.MuttleyBadRequestException;
import br.com.muttley.exception.throwables.MuttleyNoContentException;
import br.com.muttley.exception.throwables.MuttleyNotFoundException;
+import br.com.muttley.headers.services.MetadataService;
import br.com.muttley.model.Document;
-import br.com.muttley.model.Historic;
+import br.com.muttley.model.NameAlias;
import br.com.muttley.model.security.User;
+import br.com.muttley.model.security.domain.Domain;
+import br.com.muttley.mongo.service.infra.metadata.EntityMetaData;
import br.com.muttley.mongo.service.repository.DocumentMongoRepository;
+import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.aggregation.AggregationResults;
+import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.util.CollectionUtils;
+import org.springframework.util.ObjectUtils;
-import java.util.Date;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.stream.Stream;
+import static br.com.muttley.model.Document.getPropertyFrom;
import static java.util.Objects.isNull;
+import static java.util.stream.Collectors.groupingBy;
+import static java.util.stream.Collectors.joining;
+import static java.util.stream.Collectors.toList;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.limit;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.match;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.newAggregation;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.project;
+import static org.springframework.data.mongodb.core.query.Criteria.where;
/**
* @author Joel Rodrigues Moreira on 30/01/18.
@@ -25,14 +47,32 @@
public abstract class ServiceImpl implements Service {
protected final DocumentMongoRepository repository;
+ protected final MongoTemplate mongoTemplate;
protected final Class clazz;
+ protected final EntityMetaData entityMetaData;
+ @Value("${muttley.security.check-roles:false}")
+ private boolean checkRoles;
+
+ @Autowired
+ protected MetadataService metadataService;
@Autowired
protected Validator validator;
- public ServiceImpl(final DocumentMongoRepository repository, final Class clazz) {
+ public ServiceImpl(final DocumentMongoRepository repository, final MongoTemplate mongoTemplate, final Class clazz) {
this.repository = repository;
+ this.mongoTemplate = mongoTemplate;
this.clazz = clazz;
+ this.entityMetaData = EntityMetaData.of(this.clazz);
+ }
+
+ @Override
+ public boolean isCheckRole() {
+ return this.checkRoles;
+ }
+
+ public String[] getBasicRoles() {
+ return new String[]{""};
}
@Override
@@ -46,18 +86,19 @@ public void beforeSave(final User user, final T value) {
@Override
public T save(final User user, final T value) {
//verificando se realmente está criando um novo registro
- if (value.getId() != null) {
- throw new MuttleyBadRequestException(clazz, "id", "Não é possível criar um registro com um id existente");
- }
- //garantindo que o históriconão ficará nulo
- value.setHistoric(this.createHistoric(user));
- //validando dados
- this.validator.validate(value);
+ checkIdForSave(value);
+ //garantindo que o metadata ta preenchido
+ this.generateNewMetadataFor(user, value, null);
+ //processa regra de negocio antes de qualquer validação
+ this.beforeSave(user, value);
//verificando precondições
this.checkPrecondictionSave(user, value);
- this.beforeSave(user, value);
+ //validando dados do objeto
+ this.validator.validate(value);
final T otherValue = repository.save(value);
+ //realizando regras de enegocio depois do objeto ter sido salvo
this.afterSave(user, otherValue);
+ //valor salvo
return otherValue;
}
@@ -65,6 +106,45 @@ public T save(final User user, final T value) {
public void afterSave(final User user, final T value) {
}
+ @Override
+ public void checkPrecondictionSave(final User user, final Collection values) {
+ values.forEach(it -> this.checkPrecondictionSave(user, it));
+ }
+
+ @Override
+ public void beforeSave(final User user, final Collection values) {
+ values.forEach(it -> this.beforeSave(user, it));
+ }
+
+ @Override
+ public Collection save(final User user, final Collection values) {
+ //verificando se realmente está criando um novo registro
+ checkIdForSave(values);
+ //garantindo que o metadata ta preenchido
+ this.generateNewMetadataFor(user, values, null);
+ //processa regra de negocio antes de qualquer validação
+ this.beforeSave(user, values);
+ //verificando precondições
+ this.checkPrecondictionSave(user, values);
+ //validando dados do objeto
+ this.validator.validateCollection(values);
+ final Collection otherValues = repository.save(values);
+ //realizando regras de enegocio depois do objeto ter sido salvo
+ this.afterSave(user, otherValues);
+ //valor salvo
+ return otherValues;
+ }
+
+ @Override
+ public void saveOnly(final User user, final Collection values) {
+ this.save(user, values);
+ }
+
+ @Override
+ public void afterSave(final User user, final Collection values) {
+ values.forEach(it -> this.afterSave(user, it));
+ }
+
@Override
public void checkPrecondictionUpdate(final User user, final T value) {
@@ -78,21 +158,22 @@ public void beforeUpdate(final User user, final T value) {
@Override
public T update(final User user, final T value) {
//verificando se realmente está alterando um registro
- if (value.getId() == null) {
- throw new MuttleyBadRequestException(clazz, "id", "Não é possível alterar um registro sem informar um id válido");
- }
+ this.checkIdForUpdate(value);
//verificando se o registro realmente existe
if (!this.repository.exists(value.getId())) {
- throw new MuttleyNotFoundException(clazz, "id", "Registro não encontrado");
+ throw this.createNotFoundExceptionById(user, value.getId());
+ //throw new MuttleyNotFoundException(clazz, "id", "Registro não encontrado");
}
- //gerando histórico de alteração
- value.setHistoric(generateHistoricUpdate(user, repository.loadHistoric(value)));
- //validando dados
- this.validator.validate(value);
+ //gerando metadata de alteração
+ this.metadataService.generateMetaDataUpdateFor(user, repository.loadMetadata(value), value);
+ //processa regra de negocio antes de qualquer validação
+ beforeUpdate(user, value);
//verificando precondições
checkPrecondictionUpdate(user, value);
- beforeUpdate(user, value);
+ //validando dados
+ this.validator.validate(value);
final T otherValue = repository.save(value);
+ //realizando regras de enegocio depois do objeto ter sido alterado
afterUpdate(user, value);
return otherValue;
}
@@ -102,40 +183,112 @@ public void afterUpdate(final User user, final T value) {
}
+ @Override
+ public void checkPrecondictionUpdate(final User user, final Collection values) {
+ values.forEach(it -> this.checkPrecondictionUpdate(user, it));
+ }
+
+ @Override
+ public void beforeUpdate(final User user, final Collection values) {
+ values.forEach(it -> this.beforeUpdate(user, it));
+ }
+
+ @Override
+ public void update(final User user, final Collection values) {
+ //verificando se realmente está alterando um registro
+ this.checkIdForUpdate(values);
+ //verificando se o registro realmente existe
+
+ final Map> agroupedValues = values.stream()
+ .collect(groupingBy(it -> this.repository.exists(it.getId())));
+
+ final List valuesForSave = agroupedValues.get(Boolean.TRUE);
+
+ if (!CollectionUtils.isEmpty(valuesForSave)) {
+ //gerando metadata de alteração
+ valuesForSave.forEach(it -> this.metadataService.generateMetaDataUpdateFor(user, this.repository.loadMetadata(it), it));
+ //processa regra de negocio antes de qualquer validação
+ beforeUpdate(user, valuesForSave);
+ //verificando precondições
+ checkPrecondictionUpdate(user, valuesForSave);
+ //validando dados
+ this.validator.validateCollection(valuesForSave);
+ final Collection otherValue = repository.save(valuesForSave);
+ //realizando regras de enegocio depois do objeto ter sido alterado
+ afterUpdate(user, otherValue);
+ }
+ final List valuesNotSaved = agroupedValues.get(Boolean.FALSE);
+ if (!CollectionUtils.isEmpty(valuesNotSaved)) {
+ throw this.createNotFoundExceptionById(user, valuesNotSaved.parallelStream().map(Document::getId).collect(toList()));
+ /*throw new MuttleyNotFoundException(clazz, "id", "Registros não encontrados")
+ .addDetails("ids", valuesNotSaved.parallelStream().map(Document::getId).collect(toList()));*/
+ }
+ }
+
+ @Override
+ public void afterUpdate(final User user, final Collection values) {
+ values.forEach(it -> this.afterUpdate(user, it));
+ }
+
@Override
public T findById(final User user, final String id) {
- if (isNull(id)) {
+ if (!ObjectId.isValid(id)) {
throw new MuttleyBadRequestException(clazz, "id", "informe um id válido");
}
final T result = this.repository.findOne(id);
- if (isNull(id)) {
- throw new MuttleyNotFoundException(clazz, "id", id + " este registro não foi encontrado");
+ if (isNull(result)) {
+ throw this.createNotFoundExceptionById(user, id);
+ //throw new MuttleyNotFoundException(clazz, "id", id + " este registro não foi encontrado");
}
return result;
}
@Override
- public T findFirst(final User user) {
- final T result = this.repository.findFirst();
- if (isNull(result)) {
- throw new MuttleyNotFoundException(clazz, "user", "Nenhum registro encontrado");
+ public T findReferenceById(User user, String id) {
+ if (!ObjectId.isValid(id)) {
+ throw new MuttleyBadRequestException(clazz, "id", "informe um id válido");
}
- return result;
+
+ final AggregationResults results = this.mongoTemplate.aggregate(
+ newAggregation(
+ match(where("id").is(new ObjectId(id))),
+ project("id")
+ )
+ , clazz, clazz);
+
+ if (results == null || results.getUniqueMappedResult() == null) {
+ throw this.createNotFoundExceptionById(user, id);
+ //throw new MuttleyNotFoundException(clazz, "id", id + " este registro não foi encontrado");
+ }
+ return results.getUniqueMappedResult();
}
@Override
- public Historic loadHistoric(final User user, final String id) {
- final Historic historic = this.repository.loadHistoric(id);
- if (isNull(historic)) {
- throw new MuttleyNotFoundException(clazz, "historic", "Nenhum registro encontrado");
+ public Set findByIds(final User user, final String[] ids) {
+ if (ObjectUtils.isEmpty(ids)) {
+ throw new MuttleyBadRequestException(clazz, "id", "informe pelo menos um id válido");
+ }
+ if (ids.length > 50) {
+ throw new MuttleyBadRequestException(clazz, "ids", "Quantidade máxima excedida")
+ .addDetails("min", 1)
+ .addDetails("max", 50);
+ }
+ final Set records = this.repository.findMulti(ids);
+ if (records == null) {
+ return Collections.emptySet();
}
- return historic;
+ return records;
}
@Override
- public Historic loadHistoric(final User user, final T value) {
- return this.loadHistoric(user, value.getId());
+ public T findFirst(final User user) {
+ final T result = this.repository.findFirst();
+ if (isNull(result)) {
+ throw this.createNotFoundExceptionById(user).setField("user");
+ //*throw new MuttleyNotFoundException(clazz, "user", "Nenhum registro encontrado");
+ }
+ return result;
}
@Override
@@ -143,6 +296,11 @@ public void checkPrecondictionDelete(final User user, final String id) {
}
+ @Override
+ public void checkPrecondictionDelete(User user, T value) {
+
+ }
+
@Override
public void beforeDelete(final User user, final String id) {
@@ -150,11 +308,12 @@ public void beforeDelete(final User user, final String id) {
@Override
public void deleteById(final User user, final String id) {
+ beforeDelete(user, id);
checkPrecondictionDelete(user, id);
if (!repository.exists(id)) {
- throw new MuttleyNotFoundException(clazz, "id", id + " este registro não foi encontrado");
+ throw this.createNotFoundExceptionById(user, id);
+ //throw new MuttleyNotFoundException(clazz, "id", id + " este registro não foi encontrado");
}
- beforeDelete(user, id);
this.repository.delete(id);
afterDelete(user, id);
}
@@ -171,11 +330,12 @@ public void beforeDelete(final User user, final T value) {
@Override
public void delete(final User user, final T value) {
- checkPrecondictionDelete(user, value.getId());
+ beforeDelete(user, value);
+ checkPrecondictionDelete(user, value);
if (!repository.exists(value)) {
- throw new MuttleyNotFoundException(clazz, "id", value.getId() + " este registro não foi encontrado");
+ throw this.createNotFoundExceptionById(user, value.getId());
+ //throw new MuttleyNotFoundException(clazz, "id", value.getId() + " este registro não foi encontrado");
}
- beforeDelete(user, value);
this.repository.delete(value);
afterDelete(user, value);
}
@@ -186,12 +346,12 @@ public void afterDelete(final User user, final T value) {
}
@Override
- public Long count(final User user, final Map allRequestParams) {
+ public Long count(final User user, final Map allRequestParams) {
return this.repository.count(allRequestParams);
}
@Override
- public List findAll(final User user, final Map allRequestParams) {
+ public List findAll(final User user, final Map allRequestParams) {
final List results = this.repository.findAll(allRequestParams);
if (CollectionUtils.isEmpty(results)) {
throw new MuttleyNoContentException(clazz, "user", "não foi encontrado nenhum registro");
@@ -199,23 +359,120 @@ public List findAll(final User user, final Map allRequestPara
return results;
}
- protected Historic createHistoric(final User user) {
- final Date now = new Date();
- return new Historic()
- .setCreatedBy(user)
- .setDtCreate(now)
- .setLastChangeBy(user)
- .setDtChange(now);
+ @Override
+ public boolean isEmpty(final User user) {
+ return this.count(user, null) == 0l;
}
- protected Historic generateHistoricUpdate(final User user, final Historic historic) {
- return historic
- .setLastChangeBy(user)
- .setDtChange(new Date());
+
+ @Override
+ public Object getPropertyValueFromId(final User user, final String id, final String property) {
+ final Map map = new HashMap<>(1);
+ map.put("id", id);
+ return this.getPropertyValueFrom(user, map, property);
}
@Override
- public boolean isEmpty(final User user) {
- return this.count(user, null) == 0l;
+ public Object getPropertyValueFrom(final User user, final Map condictions, final String property) {
+ //Criteria condiction = Criteria.where("owner.$id").is(user.getCurrentOwner().getObjectId());
+ final AggregationResults results = createAggregateForLoadProperties(user, condictions, property);
+ return getPropertyFrom(results.getUniqueMappedResult(), property);
+ }
+
+ @Override
+ public Object[] getPropertiesValueFrom(final User user, final Map condictions, final String... properties) {
+ //Criteria condiction = Criteria.where("owner.$id").is(user.getCurrentOwner().getObjectId());
+ final AggregationResults results = createAggregateForLoadProperties(user, condictions, properties);
+
+ final T result = results.getUniqueMappedResult();
+
+ return Stream.of(properties)
+ .map(it -> getPropertyFrom(result, it))
+ .toArray(Object[]::new);
+ }
+
+ protected AggregationResults createAggregateForLoadProperties(final User user, final Map condictions, final String... properties) {
+ Criteria condiction = new Criteria();
+ final Set keysOfCondictions = condictions.keySet();
+
+ for (final String key : keysOfCondictions) {
+ condiction = condiction.and(key).is(condictions.get(key));
+ }
+
+ return this.mongoTemplate
+ .aggregate(
+ newAggregation(
+ match(condiction),
+ project(properties),
+ limit(1)
+ ), clazz, clazz
+ );
+ }
+
+ protected void checkIdForSave(final Collection values) {
+ values.parallelStream().forEach(it -> this.checkIdForSave(it));
+ }
+
+ protected void checkIdForSave(final T value) {
+ if ("".equals(value.getId())) {
+ value.setId(null);
+ }
+ if (value.getId() != null) {
+ throw new MuttleyBadRequestException(clazz, "id", "Não é possível criar um registro com um id existente");
+ }
+ }
+
+ protected void checkIdForUpdate(final Collection values) {
+ values.parallelStream().forEach(it -> this.checkIdForUpdate(it));
+ }
+
+ protected void checkIdForUpdate(final T value) {
+ if (value.getId() == null) {
+ throw new MuttleyBadRequestException(clazz, "id", "Não é possível alterar um registro sem informar um id válido");
+ }
+ }
+
+ protected MuttleyNotFoundException createNotFoundExceptionById(final User user) {
+ return new MuttleyNotFoundException(clazz, "id", this.getSingularNameAlias(this.clazz) + " não encontrado(a)");
+ }
+
+ protected MuttleyNotFoundException createNotFoundExceptionById(final User user, final String id) {
+ final MuttleyNotFoundException exception = new MuttleyNotFoundException(clazz, "id", this.getSingularNameAlias(this.clazz) + "(" + id + ") não encontrado(a)");
+ exception.addDetails("id", id);
+ return exception;
+ }
+
+ protected MuttleyNotFoundException createNotFoundExceptionById(final User user, final Collection ids) {
+ final String idsConcat = ids.parallelStream()
+ .limit(3)
+ .collect(joining(", "));
+
+ final MuttleyNotFoundException exception = new MuttleyNotFoundException(clazz, "id", this.getSingularNameAlias(this.clazz) + "(" + idsConcat + ") não encontrados(as)");
+ exception.addDetails("ids", ids);
+ return exception;
+ }
+
+ protected String getSingularNameAlias(final Class> clazz) {
+ final NameAlias nameAlias = clazz.getAnnotation(NameAlias.class);
+ if (nameAlias != null) {
+ return nameAlias.singularName();
+ }
+ return clazz.getSimpleName();
+ }
+
+ protected String getPluralNameAlias(final Class> clazz) {
+ final NameAlias nameAlias = clazz.getAnnotation(NameAlias.class);
+ if (nameAlias != null) {
+ return nameAlias.pluralName();
+ }
+ return clazz.getSimpleName();
+ }
+
+ protected void generateNewMetadataFor(final User user, final T value, final Domain domain) {
+ this.metadataService.generateNewMetadataFor(user, value, domain);
+ }
+
+ protected void generateNewMetadataFor(final User user, final Collection value, final Domain domain) {
+ this.metadataService.generateNewMetadataFor(user, value, domain);
}
}
diff --git a/muttley-domain-service/src/main/java/br/com/muttley/domain/service/impl/utils/MetadataAndIdModel.java b/muttley-domain-service/src/main/java/br/com/muttley/domain/service/impl/utils/MetadataAndIdModel.java
new file mode 100644
index 00000000..79ec56ec
--- /dev/null
+++ b/muttley-domain-service/src/main/java/br/com/muttley/domain/service/impl/utils/MetadataAndIdModel.java
@@ -0,0 +1,24 @@
+package br.com.muttley.domain.service.impl.utils;
+
+import br.com.muttley.model.MetadataDocument;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import org.springframework.data.annotation.PersistenceConstructor;
+
+/**
+ * @author Joel Rodrigues Moreira on 18/06/2021.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Getter
+@EqualsAndHashCode(of = "id")
+public class MetadataAndIdModel {
+ private final String id;
+ private final MetadataDocument metadata;
+
+ @PersistenceConstructor
+ public MetadataAndIdModel(final String id, final MetadataDocument metadata) {
+ this.id = id;
+ this.metadata = metadata;
+ }
+}
diff --git a/muttley-domain-service/src/main/java/br/com/muttley/domain/service/listener/AbstractModelSyncResolverEventListener.java b/muttley-domain-service/src/main/java/br/com/muttley/domain/service/listener/AbstractModelSyncResolverEventListener.java
new file mode 100644
index 00000000..15e47264
--- /dev/null
+++ b/muttley-domain-service/src/main/java/br/com/muttley/domain/service/listener/AbstractModelSyncResolverEventListener.java
@@ -0,0 +1,45 @@
+package br.com.muttley.domain.service.listener;
+
+import br.com.muttley.exception.throwables.MuttleyException;
+import br.com.muttley.headers.components.MuttleySerializeType;
+import br.com.muttley.model.ModelSync;
+import br.com.muttley.model.jackson.converter.event.ModelSyncResolverEvent;
+import org.bson.types.ObjectId;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * @author Joel Rodrigues Moreira on 10/05/2021.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public abstract class AbstractModelSyncResolverEventListener> extends AbstractModelResolverEventListener {
+ @Autowired
+ private MuttleySerializeType serializerType;
+
+ public AbstractModelSyncResolverEventListener() {
+ super();
+ }
+
+ @Override
+ public void onApplicationEvent(final M event) {
+ if (serializerType.isInternal()) {
+ if (ObjectId.isValid(event.getSource())) {
+ super.onApplicationEvent(event);
+ } else {
+ event.setValueResolved(this.loadValueBySync(event.getSource()));
+ }
+ } else if (serializerType.isObjectId()) {
+ if (ObjectId.isValid(event.getSource())) {
+ super.onApplicationEvent(event);
+ } else {
+ event.setValueResolved(this.loadValueBySync(event.getSource()));
+ }
+ } else if (serializerType.isSync()) {
+ event.setValueResolved(this.loadValueBySync(event.getSource()));
+ } else {
+ throw new MuttleyException("deu asdfasdfasdf");
+ }
+ }
+
+ protected abstract T loadValueBySync(final String sync);
+}
diff --git a/muttley-exception/pom.xml b/muttley-exception/pom.xml
index 87fb7cef..91b3e752 100644
--- a/muttley-exception/pom.xml
+++ b/muttley-exception/pom.xml
@@ -1,10 +1,10 @@
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
br.com.muttley
muttley-cloud
- 0.0.2-SNAPSHOT
+ ${revision}
4.0.0
jar
@@ -29,9 +29,8 @@
- br.com.muttley
- muttley-feign
- provided
+ org.springframework.cloud
+ spring-cloud-starter-feign
diff --git a/muttley-exception/src/main/java/br/com/muttley/exception/service/CustomResponseEntityExceptionHandler.java b/muttley-exception/src/main/java/br/com/muttley/exception/service/CustomResponseEntityExceptionHandler.java
index 1ee3894f..f78dc15c 100644
--- a/muttley-exception/src/main/java/br/com/muttley/exception/service/CustomResponseEntityExceptionHandler.java
+++ b/muttley-exception/src/main/java/br/com/muttley/exception/service/CustomResponseEntityExceptionHandler.java
@@ -4,6 +4,7 @@
import br.com.muttley.exception.throwables.MuttleyException;
import br.com.muttley.exception.throwables.MuttleyNotFoundException;
import br.com.muttley.exception.throwables.repository.MuttleyRepositoryException;
+import br.com.muttley.exception.throwables.security.MuttleySecurityCredentialException;
import br.com.muttley.exception.throwables.security.MuttleySecurityUnauthorizedException;
import br.com.muttley.exception.throwables.security.MuttleySecurityUserNameOrPasswordInvalidException;
import org.springframework.beans.TypeMismatchException;
@@ -30,6 +31,7 @@
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
+import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE;
@@ -122,36 +124,36 @@ public ResponseEntity handleSerializationException(final SerializationException
}
@ExceptionHandler(value = Exception.class)
- public ResponseEntity handleException(final Exception ex) {
+ public ResponseEntity handleException(final HttpServletRequest request, final Exception ex) {
if (ex instanceof MuttleyException) {
- return handleMuttleyException((MuttleyException) ex);
+ return handleMuttleyException(request, (MuttleyException) ex);
}
return messageBuilder.buildMessage(new MuttleyException("ERROR *-*", ex)).toResponseEntity();
}
@ExceptionHandler(value = RuntimeException.class)
- public ResponseEntity exceptionRuntime(final RuntimeException ex) {
+ public ResponseEntity exceptionRuntime(final HttpServletRequest request, final RuntimeException ex) {
if (ex instanceof MuttleyException) {
- return handleMuttleyException((MuttleyException) ex);
+ return handleMuttleyException(request, (MuttleyException) ex);
}
if (ex.getCause() instanceof MuttleyConflictException) {
- return handleMuttleyException((MuttleyException) ex.getCause());
+ return handleMuttleyException(request, (MuttleyException) ex.getCause());
}
return messageBuilder.buildMessage(new MuttleyException("ERROR *-*", ex)).toResponseEntity();
}
@ExceptionHandler(value = Throwable.class)
- public ResponseEntity exceptionThrowable(final Throwable ex) {
+ public ResponseEntity exceptionThrowable(final HttpServletRequest request, final Throwable ex) {
if (ex instanceof MuttleyException) {
- return handleMuttleyException((MuttleyException) ex);
+ return handleMuttleyException(request, (MuttleyException) ex);
}
return messageBuilder.buildMessage(new MuttleyException("ERROR *-*", ex)).toResponseEntity();
}
@ExceptionHandler(MuttleyException.class)
- public ResponseEntity handleMuttleyException(final MuttleyException ex) {
- return messageBuilder.buildMessage(ex).toResponseEntity();
+ public ResponseEntity handleMuttleyException(HttpServletRequest request, final MuttleyException ex) {
+ return messageBuilder.buildMessage(ex).toResponseEntity(request);
}
@ExceptionHandler(MuttleyRepositoryException.class)
@@ -170,6 +172,7 @@ public ResponseEntity handleUsernameNotFoundException(final UsernameNotFoundExce
public ResponseEntity handleAccessDeniedException(final AccessDeniedException ex) {
final MuttleySecurityUnauthorizedException exx = new MuttleySecurityUnauthorizedException().setStatus(FORBIDDEN);
exx.addSuppressed(ex);
+ exx.setMessage("Você não tem permissão para acessar esse recurso");
return handleMuttleySecurityUnauthorizedException(exx);
}
@@ -178,6 +181,11 @@ public ResponseEntity handleMuttleySecurityUserNameOrPasswordInvalidException(fi
return messageBuilder.buildMessage(ex).toResponseEntity();
}
+ @ExceptionHandler(value = MuttleySecurityCredentialException.class)
+ public ResponseEntity handleMuttleySecurityCredentialException(final MuttleySecurityCredentialException ex) {
+ return this.handleMuttleySecurityUnauthorizedException(ex);
+ }
+
@ExceptionHandler(value = MuttleySecurityUnauthorizedException.class)
public ResponseEntity handleMuttleySecurityUnauthorizedException(final MuttleySecurityUnauthorizedException ex) {
return messageBuilder.buildMessage(ex).toResponseEntity();
diff --git a/muttley-exception/src/main/java/br/com/muttley/exception/service/ErrorMessage.java b/muttley-exception/src/main/java/br/com/muttley/exception/service/ErrorMessage.java
index 8f14bd6f..fddda68c 100644
--- a/muttley-exception/src/main/java/br/com/muttley/exception/service/ErrorMessage.java
+++ b/muttley-exception/src/main/java/br/com/muttley/exception/service/ErrorMessage.java
@@ -11,16 +11,25 @@
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
+import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import static java.util.Arrays.asList;
import static org.springframework.http.HttpHeaders.CONTENT_TYPE;
+import static org.springframework.http.MediaType.ALL_VALUE;
+import static org.springframework.http.MediaType.APPLICATION_JSON;
+import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8;
import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8_VALUE;
import static org.springframework.util.StringUtils.isEmpty;
+import static org.springframework.web.servlet.HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE;
/**
* @author Joel Rodrigues Moreira on 14/01/18.
@@ -40,11 +49,13 @@ public final class ErrorMessage {
protected String message;
protected String objectName;
protected final Map details;
+ protected final Map> headers;
@JsonIgnore
public static final String RESPONSE_HEADER = "error-message";
public ErrorMessage() {
this.details = new HashMap<>();
+ this.headers = new HashMap<>();
}
@JsonCreator
@@ -53,12 +64,14 @@ public ErrorMessage(
@JsonProperty("status") final HttpStatus status,
@JsonProperty("message") final String message,
@JsonProperty("objectName") final String objectName,
- @JsonProperty("details") final Map details) {
+ @JsonProperty("details") final Map details,
+ @JsonProperty("headers") final Map> headers) {
this.field = field;
this.status = status;
this.message = message;
this.objectName = objectName;
this.details = details;
+ this.headers = headers;
}
public String getField() {
@@ -144,8 +157,47 @@ public boolean containsDetails() {
return details != null && !details.isEmpty();
}
+ public Map> getHeaders() {
+ return headers;
+ }
+
+ @JsonIgnore
+ public ErrorMessage addHeaders(final String key, final String value) {
+ if (this.headers.containsKey(key)) {
+ this.headers.get(key).add(value);
+ } else {
+ final List values = new ArrayList<>(1);
+ values.add(value);
+ this.headers.put(key, values);
+ }
+ return this;
+ }
+
+ @JsonIgnore
+ public ErrorMessage addHeaders(final String key, final String... value) {
+ return this.addHeaders(key, asList(value));
+ }
+
+ @JsonIgnore
+ public ErrorMessage addHeaders(final String key, final List value) {
+ if (this.headers.containsKey(key)) {
+ this.headers.get(key).addAll(value);
+ } else {
+ final List values = new ArrayList<>(1);
+ values.addAll(value);
+ this.headers.put(key, values);
+ }
+ return this;
+ }
+
+ @JsonIgnore
+ public ErrorMessage addHeaders(final Map> headers) {
+ this.headers.putAll(headers);
+ return this;
+ }
+
@JsonIgnore
- protected final String toJson() {
+ public final String toJsonPretty() {
try {
return new ObjectMapper()
.writerWithDefaultPrettyPrinter()
@@ -156,15 +208,56 @@ protected final String toJson() {
}
}
+ @JsonIgnore
+ public final String toJson() {
+ try {
+ return new ObjectMapper()
+ .writeValueAsString(this);
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ return null;
+ }
+ }
+
@JsonIgnore
public ResponseEntity toResponseEntity() {
- return toResponseEntity(new HttpHeaders());
+ return toResponseEntity((HttpHeaders) null);
}
@JsonIgnore
public ResponseEntity toResponseEntity(final HttpHeaders headers) {
- headers.add(CONTENT_TYPE, APPLICATION_JSON_UTF8_VALUE);
- headers.add(RESPONSE_HEADER, RESPONSE_HEADER_VALUE);
- return new ResponseEntity(this, headers, this.status);
+ return this.toResponseEntity(true, headers);
}
+
+ @JsonIgnore
+ public ResponseEntity toResponseEntity(boolean serializeResponse, final HttpHeaders headers) {
+ final HttpHeaders currentHeaders = new HttpHeaders();
+ currentHeaders.putAll(this.getHeaders());
+ if (headers != null) {
+ currentHeaders.putAll(headers);
+ }
+
+ currentHeaders.add(RESPONSE_HEADER, RESPONSE_HEADER_VALUE);
+ if (serializeResponse) {
+ currentHeaders.add(CONTENT_TYPE, APPLICATION_JSON_UTF8_VALUE);
+ return new ResponseEntity(this, currentHeaders, this.status);
+ }
+ return ResponseEntity.status(this.status).headers(currentHeaders).build();
+ }
+
+ @JsonIgnore
+ public ResponseEntity toResponseEntity(final HttpServletRequest request) {
+ //verificando se tem algum Media type que possa retornar json
+ final LinkedHashSet headers = (LinkedHashSet) request.getAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
+ if (headers != null && headers.parallelStream()
+ .filter(it -> APPLICATION_JSON.equals(it) || APPLICATION_JSON_UTF8.equals(it) || ALL_VALUE.equals(it))
+ .count() > 0) {
+ //se chegou aqui quer dizer que podemo retornar um json,
+ //vamos remover qualquer coisa que possa dar outra exception relacionada a media type
+ request.removeAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
+ return this.toResponseEntity(true, null);
+ }
+ return this.toResponseEntity(false, null);
+ }
+
}
diff --git a/muttley-exception/src/main/java/br/com/muttley/exception/service/ErrorMessageBuilder.java b/muttley-exception/src/main/java/br/com/muttley/exception/service/ErrorMessageBuilder.java
index 5435d751..e83a662e 100644
--- a/muttley-exception/src/main/java/br/com/muttley/exception/service/ErrorMessageBuilder.java
+++ b/muttley-exception/src/main/java/br/com/muttley/exception/service/ErrorMessageBuilder.java
@@ -1,5 +1,6 @@
package br.com.muttley.exception.service;
+import br.com.muttley.exception.service.event.MuttleyExceptionEvent;
import br.com.muttley.exception.throwables.MuttleyBadRequestException;
import br.com.muttley.exception.throwables.MuttleyException;
import br.com.muttley.exception.throwables.repository.MuttleyRepositoryException;
@@ -9,9 +10,12 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.TypeMismatchException;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.stereotype.Component;
+import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
@@ -23,6 +27,7 @@
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
+import java.util.Collection;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.METHOD_NOT_ALLOWED;
@@ -42,6 +47,8 @@ public class ErrorMessageBuilder {
private Boolean STACK_TRACE;
@Value("${muttley.print.responseException:false}")
private Boolean RESPONSE_EXCEPTION;
+ @Autowired
+ private ApplicationEventPublisher publisher;
public ErrorMessage buildMessage(final MethodArgumentNotValidException ex) {
final ErrorMessage message = new ErrorMessage()
@@ -53,6 +60,7 @@ public ErrorMessage buildMessage(final MethodArgumentNotValidException ex) {
String key = fieldError.getCodes()[0].replace(fieldError.getCodes()[fieldError.getCodes().length - 1] + ".", "");
message.addDetails(key, fieldError.getDefaultMessage());
}
+ this.publishEvents(message);
printException(ex, message);
return message;
}
@@ -79,6 +87,7 @@ public ErrorMessage buildMessage(final ConstraintViolationException ex) {
keyDetail.startsWith(message.objectName) ? keyDetail : message.objectName + "." + keyDetail, violation.getMessage()
);
}
+ this.publishEvents(message);
printException(ex, message);
return message;
}
@@ -88,6 +97,7 @@ public ErrorMessage buildMessage(final BindException ex) {
ex.getBindingResult().getFieldErrors().forEach(e -> {
message.addDetails(e.getField(), e.getDefaultMessage());
});
+ this.publishEvents(message);
printException(ex, message);
return message;
}
@@ -95,23 +105,29 @@ public ErrorMessage buildMessage(final BindException ex) {
public ErrorMessage buildMessage(final TypeMismatchException ex) {
final ErrorMessage message = buildMessage(new MuttleyBadRequestException(ex));
printException(ex, message);
+ this.publishEvents(message);
return message;
}
public ErrorMessage buildMessage(final MissingServletRequestPartException ex) {
final ErrorMessage message = buildMessage(new MuttleyBadRequestException(ex));
printException(ex, message);
+ this.publishEvents(message);
return message;
}
public ErrorMessage buildMessage(final MissingServletRequestParameterException ex) {
- final ErrorMessage message = buildMessage(new MuttleyBadRequestException(ex));
+ final ErrorMessage message = buildMessage(new MuttleyBadRequestException(ex))
+ .setMessage("Informe os parametros necessários")
+ .addDetails("nameParam", ex.getParameterName());
+ this.publishEvents(message);
printException(ex, message);
return message;
}
public ErrorMessage buildMessage(final MethodArgumentTypeMismatchException ex) {
final ErrorMessage message = buildMessage(new MuttleyBadRequestException(ex));
+ this.publishEvents(message);
printException(ex, message);
return message;
}
@@ -120,6 +136,7 @@ public ErrorMessage buildMessage(final HttpRequestMethodNotSupportedException ex
final ErrorMessage message = buildMessage(new MuttleyBadRequestException(ex))
.setStatus(METHOD_NOT_ALLOWED)
.setMessage(METHOD_NOT_ALLOWED.getReasonPhrase());
+ this.publishEvents(message);
printException(ex, message);
return message;
}
@@ -130,6 +147,7 @@ public ErrorMessage buildMessage(final HttpMediaTypeNotSupportedException ex) {
.setMessage(ex.getMessage().replace("'null' ", ""))
.addDetails("ContentType", ex.getContentType() == null ? "uninformed" : ex.getContentType().toString())
.addDetails("SupportedMediaTypes", APPLICATION_JSON_VALUE, APPLICATION_JSON_UTF8_VALUE);
+ this.publishEvents(message);
printException(ex, message);
return message;
}
@@ -143,17 +161,33 @@ public ErrorMessage buildMessage(final HttpMessageNotReadableException ex) {
if (throwable instanceof MuttleyException) {
return buildMessage((MuttleyException) throwable);
} else if (ex.getCause() instanceof com.fasterxml.jackson.core.JsonParseException) {
- JsonLocation location = ((com.fasterxml.jackson.core.JsonParseException) ex.getCause()).getLocation();
+ final JsonLocation location = ((com.fasterxml.jackson.core.JsonParseException) ex.getCause()).getLocation();
message.setMessage("Illegal character in line:" + location.getLineNr() + " column:" + location.getColumnNr());
} else if (ex.getCause() instanceof JsonMappingException) {
- JsonLocation location = ((JsonMappingException) ex.getCause()).getLocation();
- if (location != null) {
- message.setMessage("Illegal character in line:" + location.getLineNr() + " column:" + location.getColumnNr());
+ final JsonLocation location = ((JsonMappingException) ex.getCause()).getLocation();
+ final String messageStr = ex.getMessage();
+
+ if (messageStr.startsWith("JSON parse error: Can not deserialize instance of ")) {
+ final String clazz = messageStr.replace("JSON parse error: Can not deserialize instance of ", "").replace(messageStr.substring(messageStr.indexOf(" out of START")), "");
+ final String[] packageClazz = clazz.split("\\.");
+ final String type = packageClazz[packageClazz.length - 1];
+ final String typeStr = type.equals("ArrayList") || type.equals("HashSet") ? "Array" : type;
+ final String typeSeted = messageStr.contains("START_OBJECT") ? "Object" : messageStr.contains("START_ARRAY") ? "Array" : "Unknown";
+ message.setMessage("Era esperado um " + typeStr + "(a) porem foi passado um " + typeSeted);
+ if (location != null) {
+ message.addDetails("info", "Illegal character in line:" + location.getLineNr() + " column:" + location.getColumnNr());
+ }
+ } else {
+ if (location != null) {
+ message.setMessage("Illegal character in line:" + location.getLineNr() + " column:" + location.getColumnNr());
+ }
}
+
} else if (ex.getMessage().contains("Required request body is missing:")) {
message.setMessage("Insira o corpo na requisição!")
.addDetails("body", "body is empty");
}
+ this.publishEvents(message);
printException(ex, message);
return message;
}
@@ -163,14 +197,18 @@ public ErrorMessage buildMessage(final MuttleyException ex) {
.setStatus(ex.getStatus())
.setMessage(ex.getMessage())
.setObjectName(ex.getObjectName())
- .addDetails(ex.getDetails());
+ .addDetails(ex.getDetails())
+ .addHeaders(ex.getHeaders());
+ this.publishEvents(message, ex.getEvents());
printException(ex, message);
return message;
}
public ErrorMessage buildMessage(final MuttleyRepositoryException ex) {
final ErrorMessage message = new ErrorMessage()
- .setStatus(ex.getStatus());
+ .setStatus(ex.getStatus())
+ .addHeaders(ex.getHeaders());
+ this.publishEvents(message, ex.getEvents());
printException(ex, message);
return message;
}
@@ -180,11 +218,26 @@ public ErrorMessage buildMessage(final MuttleySecurityUnauthorizedException ex)
.setStatus(ex.getStatus())
.setMessage(ex.getMessage())
.setObjectName(ex.getObjectName())
- .addDetails(ex.getDetails());
+ .addDetails(ex.getDetails())
+ .addHeaders(ex.getHeaders());
+ this.publishEvents(message, ex.getEvents());
printException(ex, message);
return message;
}
+ private void publishEvents(final ErrorMessage message) {
+ this.publishEvents(message, null);
+ }
+
+ private void publishEvents(final ErrorMessage message, final Collection events) {
+ if (this.publisher != null) {
+ this.publisher.publishEvent(new MuttleyExceptionEvent(message));
+ if (!CollectionUtils.isEmpty(events)) {
+ events.forEach(it -> this.publisher.publishEvent(it.setSource(message)));
+ }
+ }
+ }
+
/**
* Imprime a pilha de Exceptions de acordo com application.properties
*
@@ -195,7 +248,7 @@ private void printException(final Exception ex, final ErrorMessage message) {
ex.printStackTrace();
}
if (RESPONSE_EXCEPTION == null || RESPONSE_EXCEPTION) {
- logger.info(message.toJson());
+ logger.info(message.toJsonPretty());
}
}
diff --git a/muttley-exception/src/main/java/br/com/muttley/exception/service/ExceptionBuilder.java b/muttley-exception/src/main/java/br/com/muttley/exception/service/ExceptionBuilder.java
index d73ea8c9..b2c145a6 100644
--- a/muttley-exception/src/main/java/br/com/muttley/exception/service/ExceptionBuilder.java
+++ b/muttley-exception/src/main/java/br/com/muttley/exception/service/ExceptionBuilder.java
@@ -57,6 +57,9 @@ public static Exception buildException(final Response response, final ObjectMapp
}
private static ErrorMessage getErrorMessage(final Response response, final ObjectMapper mapper) throws IOException {
+ if (response.body() == null) {
+ return new ErrorMessage().setStatus(HttpStatus.valueOf(response.status()));
+ }
return mapper.readValue(response.body().asInputStream(), ErrorMessage.class);
}
}
diff --git a/muttley-exception/src/main/java/br/com/muttley/exception/service/config/ConfigEndPointsErros.java b/muttley-exception/src/main/java/br/com/muttley/exception/service/config/ConfigEndPointsErros.java
index eb00c8e8..3c2d2d1a 100644
--- a/muttley-exception/src/main/java/br/com/muttley/exception/service/config/ConfigEndPointsErros.java
+++ b/muttley-exception/src/main/java/br/com/muttley/exception/service/config/ConfigEndPointsErros.java
@@ -17,6 +17,8 @@ public class ConfigEndPointsErros implements EmbeddedServletContainerCustomizer
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
+ container.addErrorPages(new ErrorPage(HttpStatus.UNAUTHORIZED, "/401"));
+ container.addErrorPages(new ErrorPage(HttpStatus.FORBIDDEN, "/403"));
container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/404"));
container.addErrorPages(new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/500"));
}
diff --git a/muttley-exception/src/main/java/br/com/muttley/exception/service/config/ErrorsController.java b/muttley-exception/src/main/java/br/com/muttley/exception/service/config/ErrorsController.java
index 817de291..25806f5a 100644
--- a/muttley-exception/src/main/java/br/com/muttley/exception/service/config/ErrorsController.java
+++ b/muttley-exception/src/main/java/br/com/muttley/exception/service/config/ErrorsController.java
@@ -3,6 +3,8 @@
import br.com.muttley.exception.service.ErrorMessageBuilder;
import br.com.muttley.exception.throwables.MuttleyException;
import br.com.muttley.exception.throwables.MuttleyNotFoundException;
+import br.com.muttley.exception.throwables.security.MuttleySecurityCredentialException;
+import br.com.muttley.exception.throwables.security.MuttleySecurityUnauthorizedException;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -17,6 +19,19 @@
@Controller
@RequestMapping
public class ErrorsController {
+
+ @RequestMapping(value = "/401", method = GET)
+ public ResponseEntity noHandlerUnauthorizedException(final ErrorMessageBuilder messageBuilder) {
+ return messageBuilder.buildMessage(new MuttleySecurityUnauthorizedException()).toResponseEntity();
+ }
+
+ @RequestMapping(value = "/403", method = GET)
+ public ResponseEntity noHandlerForbiden(final ErrorMessageBuilder messageBuilder) {
+ return messageBuilder.buildMessage(
+ new MuttleySecurityCredentialException("Você não tem permissão para acessar esse recurso")
+ ).toResponseEntity();
+ }
+
@RequestMapping(value = "/404", method = GET)
public ResponseEntity noHandlerFoundException(final ErrorMessageBuilder messageBuilder) {
return messageBuilder.buildMessage(
diff --git a/muttley-exception/src/main/java/br/com/muttley/exception/service/event/MuttleyExceptionEvent.java b/muttley-exception/src/main/java/br/com/muttley/exception/service/event/MuttleyExceptionEvent.java
new file mode 100644
index 00000000..c72d8e0e
--- /dev/null
+++ b/muttley-exception/src/main/java/br/com/muttley/exception/service/event/MuttleyExceptionEvent.java
@@ -0,0 +1,37 @@
+package br.com.muttley.exception.service.event;
+
+import br.com.muttley.exception.service.ErrorMessage;
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * @author Joel Rodrigues Moreira 15/06/2020
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public class MuttleyExceptionEvent extends ApplicationEvent {
+ private T source;
+
+ public MuttleyExceptionEvent() {
+ super("null");
+ }
+
+ /**
+ * Create a new ApplicationEvent.
+ *
+ * @param source the object on which the event initially occurred (never {@code null})
+ */
+ public MuttleyExceptionEvent(final T source) {
+ super(source);
+ this.source = source;
+ }
+
+ @Override
+ public T getSource() {
+ return source;
+ }
+
+ public MuttleyExceptionEvent setSource(final T source) {
+ this.source = source;
+ return this;
+ }
+}
diff --git a/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyBadRequestException.java b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyBadRequestException.java
index 11acd975..e1df8735 100644
--- a/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyBadRequestException.java
+++ b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyBadRequestException.java
@@ -22,7 +22,7 @@ public MuttleyBadRequestException(final Throwable cause) {
}
public MuttleyBadRequestException(final Class clazz, final String field, final String message) {
- super("Bad Request", BAD_REQUEST, clazz, field, message);
+ super(message, BAD_REQUEST, clazz, field, message);
}
public MuttleyBadRequestException(final ErrorMessage errorMessage) {
diff --git a/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyConflictException.java b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyConflictException.java
index 29d73ef5..b9a58669 100644
--- a/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyConflictException.java
+++ b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyConflictException.java
@@ -12,7 +12,7 @@
public class MuttleyConflictException extends MuttleyException {
public MuttleyConflictException(final Class clazz, final String field, final String message) {
- super("Conflict", HttpStatus.CONFLICT, clazz, field, message);
+ super(message, HttpStatus.CONFLICT, clazz, field, message);
/*this.message = "Conflict";
this.status = HttpStatus.CONFLICT;
this.objectName = clazz.getSimpleName().toLowerCase();
diff --git a/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyDeleteException.java b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyDeleteException.java
new file mode 100644
index 00000000..304d97f7
--- /dev/null
+++ b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyDeleteException.java
@@ -0,0 +1,19 @@
+package br.com.muttley.exception.throwables;
+
+import br.com.muttley.exception.service.ErrorMessage;
+
+/**
+ * @author Joel Rodrigues Moreira on 13/01/2023.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public class MuttleyDeleteException extends MuttleyBadRequestException {
+
+ public MuttleyDeleteException(Class clazz, String field, String message) {
+ super(clazz, field, message);
+ }
+
+ public MuttleyDeleteException(ErrorMessage errorMessage) {
+ super(errorMessage);
+ }
+}
diff --git a/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyEmailEmUsoException.java b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyEmailEmUsoException.java
new file mode 100644
index 00000000..334c4a44
--- /dev/null
+++ b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyEmailEmUsoException.java
@@ -0,0 +1,19 @@
+package br.com.muttley.exception.throwables;
+
+import br.com.muttley.exception.service.ErrorMessage;
+
+/**
+ * @author Carolina Cedro on 10/10/2024.
+ * e-mail: ana.carolina@maxxsoft.com.br
+ * @project muttley-cloud
+ */
+public class MuttleyEmailEmUsoException extends MuttleyBadRequestException {
+
+ public MuttleyEmailEmUsoException(Class clazz, String field, String message) {
+ super(clazz, field, message);
+ }
+
+ public MuttleyEmailEmUsoException(ErrorMessage errorMessage) {
+ super(errorMessage);
+ }
+}
diff --git a/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyException.java b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyException.java
index 9f9df68b..cdb1743e 100644
--- a/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyException.java
+++ b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyException.java
@@ -1,11 +1,16 @@
package br.com.muttley.exception.throwables;
import br.com.muttley.exception.service.ErrorMessage;
+import br.com.muttley.exception.service.event.MuttleyExceptionEvent;
import org.springframework.http.HttpStatus;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.stream.Collectors;
+
+import static java.util.Arrays.asList;
/**
* @author Joel Rodrigues Moreira on 14/01/18.
@@ -18,15 +23,18 @@ public class MuttleyException extends RuntimeException {
protected String message;
protected String objectName;
protected Map details = new HashMap<>();
+ protected Map> headers = new HashMap<>();
protected String field;
+ protected List events = new ArrayList<>();
public MuttleyException() {
this.status = HttpStatus.INTERNAL_SERVER_ERROR;
- this.message = "ERROR *-*";
- this.objectName = "unknow :(";
- this.field = null;
+ this.message = "Ocorreu um erro interno não esperado !";
+ this.objectName = "Erro desconhecido";
+ this.field = "Campo não identificado";
}
+
public MuttleyException(final String message, final HttpStatus status, final Class clazz, final String field, final String info) {
this.message = message;
this.status = status;
@@ -153,6 +161,64 @@ public MuttleyException addDetails(final Map details) {
return this;
}
+ public Map> getHeaders() {
+ return headers;
+ }
+
+ public MuttleyException setHeaders(final Map> headers) {
+ this.headers = headers;
+ return this;
+ }
+
+ public MuttleyException addHeaders(final String key, final String value) {
+ if (this.headers.containsKey(key)) {
+ this.headers.get(key).add(value);
+ } else {
+ final List values = new ArrayList<>(1);
+ values.add(value);
+ this.headers.put(key, values);
+ }
+ return this;
+ }
+
+ public MuttleyException addHeaders(final String key, final String... value) {
+ return this.addHeaders(key, asList(value));
+ }
+
+ public MuttleyException addHeaders(final String key, final List value) {
+ if (this.headers.containsKey(key)) {
+ this.headers.get(key).addAll(value);
+ } else {
+ final List values = new ArrayList<>(1);
+ values.addAll(value);
+ this.headers.put(key, values);
+ }
+ return this;
+ }
+
+ public MuttleyException addHeaders(final Map> headers) {
+ this.headers.putAll(headers);
+ return this;
+ }
+
+ public MuttleyException addEvent(final MuttleyExceptionEvent... events) {
+ if (events != null) {
+ this.getEvents().addAll(
+ asList(events)
+ .parallelStream()
+ .filter(e -> e != null)
+ .collect(
+ Collectors.toSet()
+ )
+ );
+ }
+ return this;
+ }
+
+ public List getEvents() {
+ return this.events;
+ }
+
public boolean containsDetais() {
return !this.details.isEmpty();
}
diff --git a/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyIamATeapotException.java b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyIamATeapotException.java
new file mode 100644
index 00000000..ff74604f
--- /dev/null
+++ b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyIamATeapotException.java
@@ -0,0 +1,31 @@
+package br.com.muttley.exception.throwables;
+
+import br.com.muttley.exception.service.ErrorMessage;
+import org.springframework.http.HttpStatus;
+
+/**
+ * @author Joel Rodrigues Moreira on 14/01/18.
+ * e-mail: joel.databox@gmail.com
+ * @project spring-cloud
+ */
+
+public class MuttleyIamATeapotException extends MuttleyException {
+
+ public MuttleyIamATeapotException(final Throwable cause) {
+ super(cause);
+ setStatus(HttpStatus.I_AM_A_TEAPOT);
+ }
+
+ public MuttleyIamATeapotException(final Class clazz, final String field, final String message) {
+ super(message, HttpStatus.I_AM_A_TEAPOT, clazz, field, message);
+ }
+
+ public MuttleyIamATeapotException(final String message) {
+ super(message, HttpStatus.I_AM_A_TEAPOT);
+ }
+
+ public MuttleyIamATeapotException(final ErrorMessage errorMessage) {
+ super(errorMessage);
+ setStatus(HttpStatus.I_AM_A_TEAPOT);
+ }
+}
diff --git a/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyInvalidObjectIdException.java b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyInvalidObjectIdException.java
new file mode 100644
index 00000000..1cfa35c2
--- /dev/null
+++ b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyInvalidObjectIdException.java
@@ -0,0 +1,18 @@
+package br.com.muttley.exception.throwables;
+
+/**
+ * @author Joel Rodrigues Moreira on 14/01/18.
+ * e-mail: joel.databox@gmail.com
+ * @project spring-cloud
+ */
+
+public class MuttleyInvalidObjectIdException extends MuttleyBadRequestException {
+
+ public MuttleyInvalidObjectIdException() {
+ this(null, null, "invalid id");
+ }
+
+ public MuttleyInvalidObjectIdException(final Class clazz, final String field, final String message) {
+ super(clazz, field, message);
+ }
+}
diff --git a/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyMethodNotAllowedException.java b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyMethodNotAllowedException.java
index 63b8cc2d..7dc1a9e6 100644
--- a/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyMethodNotAllowedException.java
+++ b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyMethodNotAllowedException.java
@@ -17,7 +17,7 @@ public MuttleyMethodNotAllowedException(final String message) {
}
public MuttleyMethodNotAllowedException(final Class clazz, final String field, final String message) {
- super("Method not allowed", HttpStatus.METHOD_NOT_ALLOWED, clazz, field, message);
+ super(message, HttpStatus.METHOD_NOT_ALLOWED, clazz, field, message);
}
public MuttleyMethodNotAllowedException(final ErrorMessage errorMessage) {
diff --git a/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyNoContentException.java b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyNoContentException.java
index c16fd051..4f58a8de 100644
--- a/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyNoContentException.java
+++ b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyNoContentException.java
@@ -12,7 +12,7 @@
public class MuttleyNoContentException extends MuttleyException {
public MuttleyNoContentException(final Class clazz, final String field, final String message) {
- super("No Content", HttpStatus.NO_CONTENT, clazz, field, message);
+ super(message, HttpStatus.NO_CONTENT, clazz, field, message);
}
public MuttleyNoContentException(final ErrorMessage errorMessage) {
diff --git a/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyNotAcceptableException.java b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyNotAcceptableException.java
index f5a8d77e..7cc0beab 100644
--- a/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyNotAcceptableException.java
+++ b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyNotAcceptableException.java
@@ -12,7 +12,7 @@
public class MuttleyNotAcceptableException extends MuttleyException {
public MuttleyNotAcceptableException(final Class clazz, final String field, final String message) {
- super("Not Acceptable", HttpStatus.NOT_ACCEPTABLE, clazz, field, message);
+ super(message, HttpStatus.NOT_ACCEPTABLE, clazz, field, message);
}
public MuttleyNotAcceptableException(final ErrorMessage errorMessage) {
diff --git a/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyNotFoundException.java b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyNotFoundException.java
index 063aa675..06046eaf 100644
--- a/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyNotFoundException.java
+++ b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/MuttleyNotFoundException.java
@@ -16,7 +16,7 @@ public MuttleyNotFoundException(final Throwable cause) {
}
public MuttleyNotFoundException(final Class clazz, final String field, final String message) {
- super("AbstractResource Not Found", HttpStatus.NOT_FOUND, clazz, field, message);
+ super(message, HttpStatus.NOT_FOUND, clazz, field, message);
}
public MuttleyNotFoundException(final ErrorMessage errorMessage) {
diff --git a/muttley-exception/src/main/java/br/com/muttley/exception/throwables/security/MuttleySecondaryEmailException.java b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/security/MuttleySecondaryEmailException.java
new file mode 100644
index 00000000..5e006c0e
--- /dev/null
+++ b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/security/MuttleySecondaryEmailException.java
@@ -0,0 +1,16 @@
+package br.com.muttley.exception.throwables.security;
+
+import org.springframework.http.HttpStatus;
+
+/**
+ * @author Carolina Cedro on 09/10/2024.
+ * e-mail: ana.carolina@maxxsoft.com.br
+ * @project spring-cloud
+ */
+public class MuttleySecondaryEmailException extends MuttleySecurityUnauthorizedException {
+
+ public MuttleySecondaryEmailException(final Class> clazz, final String field, final String message) {
+ super(message, HttpStatus.BAD_REQUEST, clazz, field,
+ "O campo de email secundário não pode estar vazio ou nulo.");
+ }
+}
diff --git a/muttley-exception/src/main/java/br/com/muttley/exception/throwables/security/MuttleySecurityEmailNotFoundtException.java b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/security/MuttleySecurityEmailNotFoundtException.java
new file mode 100644
index 00000000..2230fee9
--- /dev/null
+++ b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/security/MuttleySecurityEmailNotFoundtException.java
@@ -0,0 +1,16 @@
+package br.com.muttley.exception.throwables.security;
+
+import org.springframework.http.HttpStatus;
+
+/**
+ * @author Carolina Cedro on 08/10/2024.
+ * e-mail: ana.carolina@maxxsoft.com.br
+ * @project spring-cloud
+ */
+public class MuttleySecurityEmailNotFoundtException extends MuttleySecurityUnauthorizedException {
+
+ public MuttleySecurityEmailNotFoundtException(final Class clazz, final String field, final String message) {
+ super(message, HttpStatus.BAD_REQUEST, clazz, field, "O email informado não foi encontrado. Verifique e tente novamente.");
+ }
+
+}
diff --git a/muttley-exception/src/main/java/br/com/muttley/exception/throwables/security/MuttleySecurityExpiredTokenException.java b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/security/MuttleySecurityExpiredTokenException.java
new file mode 100644
index 00000000..be9b20e5
--- /dev/null
+++ b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/security/MuttleySecurityExpiredTokenException.java
@@ -0,0 +1,16 @@
+package br.com.muttley.exception.throwables.security;
+
+import org.springframework.http.HttpStatus;
+
+/**
+ * @author Carolina Cedro on 08/10/2024.
+ * e-mail: ana.carolina@maxxsoft.com.br
+ * @project spring-cloud
+ */
+public class MuttleySecurityExpiredTokenException extends MuttleySecurityUnauthorizedException {
+
+ public MuttleySecurityExpiredTokenException(final Class> clazz, final String field, final String message) {
+ super(message, HttpStatus.BAD_REQUEST, clazz, field,
+ "O token de redefinição de senha expirou ou é inválido. ");
+ }
+}
diff --git a/muttley-exception/src/main/java/br/com/muttley/exception/throwables/security/MuttleySecurityUserNotFoundException.java b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/security/MuttleySecurityUserNotFoundException.java
new file mode 100644
index 00000000..c5e2c9bb
--- /dev/null
+++ b/muttley-exception/src/main/java/br/com/muttley/exception/throwables/security/MuttleySecurityUserNotFoundException.java
@@ -0,0 +1,16 @@
+package br.com.muttley.exception.throwables.security;
+
+import org.springframework.http.HttpStatus;
+
+/**
+ * @author Carolina Cedro on 08/10/2024.
+ * e-mail: ana.carolina@maxxsoft.com.br
+ * @project spring-cloud
+ */
+public class MuttleySecurityUserNotFoundException extends MuttleySecurityUnauthorizedException {
+
+ public MuttleySecurityUserNotFoundException(final Class clazz, final String field, final String message) {
+ super(message, HttpStatus.BAD_REQUEST, clazz, field, "user não encontrado !");
+ }
+
+}
diff --git a/muttley-feign/pom.xml b/muttley-feign/pom.xml
index 93746ad1..90895420 100644
--- a/muttley-feign/pom.xml
+++ b/muttley-feign/pom.xml
@@ -1,10 +1,10 @@
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
br.com.muttley
muttley-cloud
- 0.0.2-SNAPSHOT
+ ${revision}
4.0.0
jar
@@ -17,6 +17,12 @@
+
+ br.com.muttley
+ muttley-model
+ provided
+
+
org.springframework.cloud
spring-cloud-starter-feign
diff --git a/muttley-feign/src/main/java/br/com/muttley/feign/service/FeignConfig.java b/muttley-feign/src/main/java/br/com/muttley/feign/service/FeignConfig.java
index d4f1d57d..cf0cdfcf 100644
--- a/muttley-feign/src/main/java/br/com/muttley/feign/service/FeignConfig.java
+++ b/muttley-feign/src/main/java/br/com/muttley/feign/service/FeignConfig.java
@@ -3,6 +3,11 @@
import br.com.muttley.feign.service.converters.BooleanHttpMessageConverter;
import br.com.muttley.feign.service.converters.DateHttpMessageConverter;
import br.com.muttley.feign.service.converters.LongHttpMessageConverter;
+import br.com.muttley.feign.service.converters.OwnerDataHttpMessageConverter;
+import br.com.muttley.feign.service.converters.UserHttpMessageConverter;
+import br.com.muttley.feign.service.interceptors.PropagateHeadersInterceptor;
+import br.com.muttley.feign.service.service.MuttleyDecodersService;
+import br.com.muttley.feign.service.service.MuttleyPropagateHeadersService;
import feign.Feign;
import feign.Logger;
import feign.Retryer;
@@ -10,6 +15,7 @@
import feign.okhttp.OkHttpClient;
import feign.slf4j.Slf4jLogger;
import org.springframework.beans.factory.ObjectFactory;
+import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
@@ -18,6 +24,7 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
+import org.springframework.core.env.PropertySource;
import org.springframework.http.converter.HttpMessageConverter;
import java.util.ArrayList;
@@ -33,9 +40,15 @@
*/
@Configuration
public class FeignConfig extends FeignClientsConfiguration {
- private final String PROPERTY_SOURCE = "applicationConfig: [classpath:/bootstrap.properties]";
+ private final String PROPERTY_SOURCE_PROP = "applicationConfig: [classpath:/bootstrap.properties]";
+ private final String PROPERTY_SOURCE_YAML = "applicationConfig: [classpath:/bootstrap.yaml]";
@Autowired
private ObjectFactory messageConverters;
+ @Autowired
+ private ObjectProvider muttleyPropagateHeadersService;
+
+ @Autowired
+ private ObjectProvider muttleyDecodersService;
@Bean
@@ -43,9 +56,25 @@ public Feign.Builder feignBuilder(
final Retryer retryer,
final @Autowired ConfigurableEnvironment env,
final @Value("${muttley.feign.loggin.level:#{null}}") String logLevel) {
- final Map map = (Map) env.getPropertySources().get(PROPERTY_SOURCE).getSource();
+ PropertySource propertySource = env.getPropertySources().get(PROPERTY_SOURCE_PROP);
+ if (propertySource == null) {
+ propertySource = env.getPropertySources().get(PROPERTY_SOURCE_YAML);
+ }
+ final Map map = (Map) propertySource.getSource();
map.put("feign.okhttp.enabled", "true");
+ final Feign.Builder builder = super.feignBuilder(retryer).client(new OkHttpClient());
+
+ //injetando o serviço de headers a ser propagados
+ final MuttleyPropagateHeadersService service = this.muttleyPropagateHeadersService.getIfAvailable();
+ //foi injetado?
+ if (service != null) {
+ //adicionando o interceptor
+ builder.requestInterceptor(new PropagateHeadersInterceptor(service));
+ }
+ builder.requestInterceptor(template -> template.decodeSlash(false));
+
+
return includeLogger(logLevel, super.feignBuilder(retryer).client(new OkHttpClient()));
}
@@ -55,6 +84,15 @@ public Decoder feignDecoder() {
decoderConverters.add(new LongHttpMessageConverter());
decoderConverters.add(new BooleanHttpMessageConverter());
decoderConverters.add(new DateHttpMessageConverter());
+ decoderConverters.add(new OwnerDataHttpMessageConverter());
+ decoderConverters.add(new UserHttpMessageConverter());
+ //decoderConverters.add(new ListOwnerDataHttpMessageConverter());
+
+ //verificando se alguem implementou algum decoder
+ final MuttleyDecodersService decodersService = this.muttleyDecodersService.getIfAvailable();
+ if (decodersService != null) {
+ decodersService.getDecoders().forEach(it -> decoderConverters.add(it));
+ }
//HttpMessageConverters httpMessageConverters = new HttpMessageConverters(decoderConverters);
@@ -86,4 +124,4 @@ private Feign.Builder includeLogger(final String logLevel, final Feign.Builder b
}
return builder;
}
-}
\ No newline at end of file
+}
diff --git a/muttley-feign/src/main/java/br/com/muttley/feign/service/converters/BooleanHttpMessageConverter.java b/muttley-feign/src/main/java/br/com/muttley/feign/service/converters/BooleanHttpMessageConverter.java
index ae8786af..2358e90f 100644
--- a/muttley-feign/src/main/java/br/com/muttley/feign/service/converters/BooleanHttpMessageConverter.java
+++ b/muttley-feign/src/main/java/br/com/muttley/feign/service/converters/BooleanHttpMessageConverter.java
@@ -21,7 +21,7 @@ public BooleanHttpMessageConverter() {
@Override
protected boolean supports(Class> clazz) {
- return Boolean.class.isAssignableFrom(clazz);
+ return boolean.class.isAssignableFrom(clazz) || Boolean.class.isAssignableFrom(clazz);
}
@Override
@@ -34,4 +34,4 @@ protected Boolean readInternal(Class extends Boolean> clazz, HttpInputMessage
protected void writeInternal(Boolean value, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
this.writeString(value == null ? "null" : value.toString(), outputMessage);
}
-}
\ No newline at end of file
+}
diff --git a/muttley-feign/src/main/java/br/com/muttley/feign/service/converters/ListOwnerDataHttpMessageConverter.java b/muttley-feign/src/main/java/br/com/muttley/feign/service/converters/ListOwnerDataHttpMessageConverter.java
new file mode 100644
index 00000000..766eace3
--- /dev/null
+++ b/muttley-feign/src/main/java/br/com/muttley/feign/service/converters/ListOwnerDataHttpMessageConverter.java
@@ -0,0 +1,46 @@
+package br.com.muttley.feign.service.converters;
+
+import br.com.muttley.model.security.OwnerData;
+import br.com.muttley.model.security.OwnerDataImpl;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.http.HttpInputMessage;
+import org.springframework.http.HttpOutputMessage;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.http.converter.HttpMessageNotWritableException;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author Joel Rodrigues Moreira 12/01/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public class ListOwnerDataHttpMessageConverter extends MuttleyHttpMessageConverter> {
+ private final Class> _type = (Class>) Collections.emptyList().getClass();
+
+ public ListOwnerDataHttpMessageConverter() {
+ super(MediaType.APPLICATION_JSON);
+ }
+
+ @Override
+ protected boolean supports(final Class> clazz) {
+ return this._type.isAssignableFrom(clazz);
+ }
+
+ @Override
+ protected List readInternal(final Class extends List> clazz, final HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
+ final ObjectMapper mapper = new ObjectMapper();
+ return mapper.readValue(inputMessage.getBody(), new TypeReference>() {
+ });
+ }
+
+ @Override
+ protected void writeInternal(final List owners, final HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
+ final ObjectMapper mapper = new ObjectMapper();
+ mapper.writeValue(outputMessage.getBody(), owners);
+ }
+}
diff --git a/muttley-feign/src/main/java/br/com/muttley/feign/service/converters/MuttleyHttpMessageConverter.java b/muttley-feign/src/main/java/br/com/muttley/feign/service/converters/MuttleyHttpMessageConverter.java
index ba935346..845daa9d 100644
--- a/muttley-feign/src/main/java/br/com/muttley/feign/service/converters/MuttleyHttpMessageConverter.java
+++ b/muttley-feign/src/main/java/br/com/muttley/feign/service/converters/MuttleyHttpMessageConverter.java
@@ -3,6 +3,7 @@
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
+import org.springframework.http.converter.HttpMessageConverter;
import java.io.IOException;
import java.io.InputStream;
@@ -15,7 +16,7 @@
* e-mail: joel.databox@gmail.com
* @project muttley-cloud
*/
-public abstract class MuttleyHttpMessageConverter extends AbstractHttpMessageConverter {
+public abstract class MuttleyHttpMessageConverter extends AbstractHttpMessageConverter implements HttpMessageConverter {
public MuttleyHttpMessageConverter() {
}
diff --git a/muttley-feign/src/main/java/br/com/muttley/feign/service/converters/OwnerDataHttpMessageConverter.java b/muttley-feign/src/main/java/br/com/muttley/feign/service/converters/OwnerDataHttpMessageConverter.java
new file mode 100644
index 00000000..a3afabfa
--- /dev/null
+++ b/muttley-feign/src/main/java/br/com/muttley/feign/service/converters/OwnerDataHttpMessageConverter.java
@@ -0,0 +1,43 @@
+package br.com.muttley.feign.service.converters;
+
+import br.com.muttley.model.security.OwnerData;
+import br.com.muttley.model.security.OwnerDataImpl;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.springframework.http.HttpInputMessage;
+import org.springframework.http.HttpOutputMessage;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.http.converter.HttpMessageNotWritableException;
+
+import java.io.IOException;
+
+/**
+ * @author Joel Rodrigues Moreira 12/01/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public class OwnerDataHttpMessageConverter extends MuttleyHttpMessageConverter {
+
+ public OwnerDataHttpMessageConverter() {
+ super(MediaType.APPLICATION_JSON);
+ }
+
+ @Override
+ protected boolean supports(final Class> clazz) {
+ return OwnerData.class.isAssignableFrom(clazz);
+ }
+
+ @Override
+ protected OwnerData readInternal(final Class extends OwnerData> clazz, final HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
+ final ObjectMapper mapper = new ObjectMapper();
+ return mapper.readValue(inputMessage.getBody(), new TypeReference() {
+ });
+ }
+
+ @Override
+ protected void writeInternal(final OwnerData ownerData, final HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
+ final ObjectMapper mapper = new ObjectMapper();
+ mapper.writeValue(outputMessage.getBody(), ownerData);
+ }
+}
diff --git a/muttley-feign/src/main/java/br/com/muttley/feign/service/converters/UserHttpMessageConverter.java b/muttley-feign/src/main/java/br/com/muttley/feign/service/converters/UserHttpMessageConverter.java
new file mode 100644
index 00000000..2ab64e28
--- /dev/null
+++ b/muttley-feign/src/main/java/br/com/muttley/feign/service/converters/UserHttpMessageConverter.java
@@ -0,0 +1,81 @@
+package br.com.muttley.feign.service.converters;
+
+import br.com.muttley.model.security.Authority;
+import br.com.muttley.model.security.User;
+import br.com.muttley.model.security.UserDataBinding;
+import br.com.muttley.model.security.preference.UserPreferences;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.ObjectCodec;
+import com.fasterxml.jackson.core.Version;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import org.springframework.http.HttpInputMessage;
+import org.springframework.http.HttpOutputMessage;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+import org.springframework.http.converter.HttpMessageNotWritableException;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author Joel Rodrigues Moreira 26/04/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public class UserHttpMessageConverter extends MuttleyHttpMessageConverter {
+
+ public UserHttpMessageConverter() {
+ super(MediaType.APPLICATION_JSON);
+ }
+
+ @Override
+ protected boolean supports(final Class> clazz) {
+ return User.class.isAssignableFrom(clazz);
+ }
+
+ @Override
+ protected User readInternal(final Class extends User> clazz, final HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
+ final ObjectMapper mapper = new ObjectMapper()
+ .registerModule(
+ new SimpleModule("ObjectId",
+ new Version(1, 0, 0, null, null, null)
+ ).addDeserializer(User.class, new UserSerializer())
+ );
+ return mapper.readValue(inputMessage.getBody(), User.class);
+ }
+
+ @Override
+ protected void writeInternal(final User user, final HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
+ final ObjectMapper mapper = new ObjectMapper();
+ mapper.writeValue(outputMessage.getBody(), user);
+ }
+
+ private static class UserSerializer extends JsonDeserializer {
+ @Override
+ public User deserialize(final JsonParser parser, final DeserializationContext ctxt) throws IOException, JsonProcessingException {
+ final ObjectCodec codec = parser.getCodec();
+ final JsonNode node = codec.readTree(parser);
+ if (node.isNull()) {
+ return null;
+ }
+ return new User().setId(node.get("id").asText())
+ .setName(node.get("name").asText())
+ .setDescription(node.get("description").asText())
+ .setEmail(node.get("email").asText())
+ .setUserName(node.get("userName").asText())
+ .setNickUsers(node.get("nickUsers").traverse(codec).readValueAs(new TypeReference>() {
+ })).setEnable(node.get("enable").asBoolean())
+ .setAuthorities((Set) node.get("authorities").traverse(codec).readValueAs(new TypeReference>() {
+ })).setPreferences(node.get("preferences").traverse(codec).readValueAs(UserPreferences.class))
+ .setDataBindings(node.get("preferences").traverse(codec).readValueAs(new TypeReference>() {
+ })).setOdinUser(node.get("odinUser").asBoolean());
+ }
+ }
+}
diff --git a/muttley-feign/src/main/java/br/com/muttley/feign/service/interceptors/HeadersMetadataInterceptor.java b/muttley-feign/src/main/java/br/com/muttley/feign/service/interceptors/HeadersMetadataInterceptor.java
new file mode 100644
index 00000000..8ef7012b
--- /dev/null
+++ b/muttley-feign/src/main/java/br/com/muttley/feign/service/interceptors/HeadersMetadataInterceptor.java
@@ -0,0 +1,36 @@
+package br.com.muttley.feign.service.interceptors;
+
+import br.com.muttley.feign.service.service.MuttleyHeadersMetadataService;
+import feign.RequestInterceptor;
+import feign.RequestTemplate;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.util.Map;
+
+import static org.springframework.util.ObjectUtils.isEmpty;
+
+/**
+ * @author Joel Rodrigues Moreira 22/04/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public class HeadersMetadataInterceptor implements RequestInterceptor {
+ private final ObjectProvider headersMetadataService;
+
+ @Autowired
+ public HeadersMetadataInterceptor(final ObjectProvider headersMetadataService) {
+ this.headersMetadataService = headersMetadataService;
+ }
+
+ @Override
+ public void apply(final RequestTemplate template) {
+ final MuttleyHeadersMetadataService service = this.headersMetadataService.getIfAvailable();
+ if (service != null) {
+ final Map headers = service.getHeadersMetadata();
+ if (!isEmpty(headers)) {
+ headers.forEach((final String key, final String value) -> template.header(key, value));
+ }
+ }
+ }
+}
diff --git a/muttley-feign/src/main/java/br/com/muttley/feign/service/interceptors/PropagateHeadersInterceptor.java b/muttley-feign/src/main/java/br/com/muttley/feign/service/interceptors/PropagateHeadersInterceptor.java
new file mode 100644
index 00000000..c53507ad
--- /dev/null
+++ b/muttley-feign/src/main/java/br/com/muttley/feign/service/interceptors/PropagateHeadersInterceptor.java
@@ -0,0 +1,71 @@
+package br.com.muttley.feign.service.interceptors;
+
+import br.com.muttley.feign.service.service.MuttleyPropagateHeadersService;
+import feign.RequestInterceptor;
+import feign.RequestTemplate;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+import static java.util.stream.Stream.of;
+
+/**
+ * @author Joel Rodrigues Moreira on 15/08/19.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public class PropagateHeadersInterceptor implements RequestInterceptor {
+
+ private final MuttleyPropagateHeadersService muttleyPropagateHeadersService;
+
+ public PropagateHeadersInterceptor(final MuttleyPropagateHeadersService muttleyPropagateHeadersService) {
+ this.muttleyPropagateHeadersService = muttleyPropagateHeadersService;
+ }
+
+ @Override
+ public void apply(final RequestTemplate template) {
+ if (this.muttleyPropagateHeadersService != null) {
+ final String[] propagateHeaders = this.muttleyPropagateHeadersService.getPropagateHeaders();
+ if (propagateHeaders != null && propagateHeaders.length > 0) {
+ final Map headers = getHeadersFromCurrentRequest(propagateHeaders);
+ if (headers != null) {
+ headers.forEach((final String key, final String value) -> template.header(key, value));
+ }
+ }
+ }
+ }
+
+
+ private Map getHeadersFromCurrentRequest(final String[] propagateHeaders) {
+ final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
+ if (requestAttributes != null) {
+ //recuperando a requisicao corrent
+ final HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
+ //map para armazenar o retorno
+ final Map headers = new HashMap<>(propagateHeaders.length);
+ //pegando um enum para interar o mesmo
+ final Enumeration headerNames = request.getHeaderNames();
+ //interando
+ while (headerNames.hasMoreElements()) {
+ //header atual
+ final String currentHeader = headerNames.nextElement().toString();
+ of(propagateHeaders)
+ .parallel()
+ .forEach(prop -> {
+ if (prop.equalsIgnoreCase(currentHeader)) {
+ headers.put(prop, request.getHeader(currentHeader));
+ }
+ });
+ }
+ return headers;
+ }
+
+ return null;
+ }
+
+}
diff --git a/muttley-feign/src/main/java/br/com/muttley/feign/service/registrar/MuttleyFeignFormatterRegister.java b/muttley-feign/src/main/java/br/com/muttley/feign/service/registrar/MuttleyFeignFormatterRegister.java
new file mode 100644
index 00000000..d0c9810b
--- /dev/null
+++ b/muttley-feign/src/main/java/br/com/muttley/feign/service/registrar/MuttleyFeignFormatterRegister.java
@@ -0,0 +1,40 @@
+package br.com.muttley.feign.service.registrar;
+
+import org.springframework.cloud.netflix.feign.FeignFormatterRegistrar;
+import org.springframework.format.Formatter;
+import org.springframework.format.FormatterRegistry;
+import org.springframework.stereotype.Component;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * @author Joel Rodrigues Moreira 23/11/2020
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Component
+public class MuttleyFeignFormatterRegister implements FeignFormatterRegistrar {
+ @Override
+ public void registerFormatters(final FormatterRegistry registry) {
+ registry.addFormatter(new DateFormatter());
+ }
+
+ private static class DateFormatter implements Formatter {
+ static final ThreadLocal FORMAT = ThreadLocal.withInitial(
+ () -> new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
+ );
+
+ @Override
+ public Date parse(String text, Locale locale) throws ParseException {
+ return FORMAT.get().parse(text);
+ }
+
+ @Override
+ public String print(Date date, Locale locale) {
+ return FORMAT.get().format(date);
+ }
+ }
+}
diff --git a/muttley-feign/src/main/java/br/com/muttley/feign/service/service/MuttleyDecodersService.java b/muttley-feign/src/main/java/br/com/muttley/feign/service/service/MuttleyDecodersService.java
new file mode 100644
index 00000000..88806734
--- /dev/null
+++ b/muttley-feign/src/main/java/br/com/muttley/feign/service/service/MuttleyDecodersService.java
@@ -0,0 +1,14 @@
+package br.com.muttley.feign.service.service;
+
+import br.com.muttley.feign.service.converters.MuttleyHttpMessageConverter;
+
+import java.util.Set;
+
+/**
+ * @author Joel Rodrigues Moreira 12/01/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public interface MuttleyDecodersService {
+ Set extends MuttleyHttpMessageConverter> getDecoders();
+}
diff --git a/muttley-feign/src/main/java/br/com/muttley/feign/service/service/MuttleyHeadersMetadataService.java b/muttley-feign/src/main/java/br/com/muttley/feign/service/service/MuttleyHeadersMetadataService.java
new file mode 100644
index 00000000..7fd927d2
--- /dev/null
+++ b/muttley-feign/src/main/java/br/com/muttley/feign/service/service/MuttleyHeadersMetadataService.java
@@ -0,0 +1,12 @@
+package br.com.muttley.feign.service.service;
+
+import java.util.Map;
+
+/**
+ * @author Joel Rodrigues Moreira 22/04/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public interface MuttleyHeadersMetadataService {
+ Map getHeadersMetadata();
+}
diff --git a/muttley-feign/src/main/java/br/com/muttley/feign/service/service/MuttleyPropagateHeadersService.java b/muttley-feign/src/main/java/br/com/muttley/feign/service/service/MuttleyPropagateHeadersService.java
new file mode 100644
index 00000000..f46a4d4c
--- /dev/null
+++ b/muttley-feign/src/main/java/br/com/muttley/feign/service/service/MuttleyPropagateHeadersService.java
@@ -0,0 +1,10 @@
+package br.com.muttley.feign.service.service;
+
+/**
+ * @author Joel Rodrigues Moreira on 15/08/19.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public interface MuttleyPropagateHeadersService {
+ public String[] getPropagateHeaders();
+}
diff --git a/muttley-files/.gitignore b/muttley-files/.gitignore
new file mode 100644
index 00000000..549e00a2
--- /dev/null
+++ b/muttley-files/.gitignore
@@ -0,0 +1,33 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
diff --git a/muttley-files/.mvn/wrapper/maven-wrapper.jar b/muttley-files/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 00000000..cb28b0e3
Binary files /dev/null and b/muttley-files/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/muttley-files/.mvn/wrapper/maven-wrapper.properties b/muttley-files/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 00000000..462686e2
--- /dev/null
+++ b/muttley-files/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,2 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.3/apache-maven-3.9.3-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar
diff --git a/muttley-files/mvnw b/muttley-files/mvnw
new file mode 100755
index 00000000..66df2854
--- /dev/null
+++ b/muttley-files/mvnw
@@ -0,0 +1,308 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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
+#
+# https://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.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Apache Maven Wrapper startup batch script, version 3.2.0
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /usr/local/etc/mavenrc ] ; then
+ . /usr/local/etc/mavenrc
+ fi
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "$(uname)" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME
+ else
+ JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=$(java-config --jre-home)
+ fi
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
+ JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="$(which javac)"
+ if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=$(which readlink)
+ if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then
+ if $darwin ; then
+ javaHome="$(dirname "\"$javaExecutable\"")"
+ javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac"
+ else
+ javaExecutable="$(readlink -f "\"$javaExecutable\"")"
+ fi
+ javaHome="$(dirname "\"$javaExecutable\"")"
+ javaHome=$(expr "$javaHome" : '\(.*\)/bin')
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=$(cd "$wdir/.." || exit 1; pwd)
+ fi
+ # end of workaround
+ done
+ printf '%s' "$(cd "$basedir" || exit 1; pwd)"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ # Remove \r in case we run on Windows within Git Bash
+ # and check out the repository with auto CRLF management
+ # enabled. Otherwise, we may read lines that are delimited with
+ # \r\n and produce $'-Xarg\r' rather than -Xarg due to word
+ # splitting rules.
+ tr -s '\r\n' ' ' < "$1"
+ fi
+}
+
+log() {
+ if [ "$MVNW_VERBOSE" = true ]; then
+ printf '%s\n' "$1"
+ fi
+}
+
+BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
+log "$MAVEN_PROJECTBASEDIR"
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
+if [ -r "$wrapperJarPath" ]; then
+ log "Found $wrapperJarPath"
+else
+ log "Couldn't find $wrapperJarPath, downloading it ..."
+
+ if [ -n "$MVNW_REPOURL" ]; then
+ wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+ else
+ wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+ fi
+ while IFS="=" read -r key value; do
+ # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
+ safeValue=$(echo "$value" | tr -d '\r')
+ case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;;
+ esac
+ done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
+ log "Downloading from: $wrapperUrl"
+
+ if $cygwin; then
+ wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
+ fi
+
+ if command -v wget > /dev/null; then
+ log "Found wget ... using wget"
+ [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+ else
+ wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ log "Found curl ... using curl"
+ [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
+ else
+ curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
+ fi
+ else
+ log "Falling back to using Java to download"
+ javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaSource=$(cygpath --path --windows "$javaSource")
+ javaClass=$(cygpath --path --windows "$javaClass")
+ fi
+ if [ -e "$javaSource" ]; then
+ if [ ! -e "$javaClass" ]; then
+ log " - Compiling MavenWrapperDownloader.java ..."
+ ("$JAVA_HOME/bin/javac" "$javaSource")
+ fi
+ if [ -e "$javaClass" ]; then
+ log " - Running MavenWrapperDownloader.java ..."
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+# If specified, validate the SHA-256 sum of the Maven wrapper jar file
+wrapperSha256Sum=""
+while IFS="=" read -r key value; do
+ case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
+ esac
+done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
+if [ -n "$wrapperSha256Sum" ]; then
+ wrapperSha256Result=false
+ if command -v sha256sum > /dev/null; then
+ if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then
+ wrapperSha256Result=true
+ fi
+ elif command -v shasum > /dev/null; then
+ if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
+ wrapperSha256Result=true
+ fi
+ else
+ echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
+ echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
+ exit 1
+ fi
+ if [ $wrapperSha256Result = false ]; then
+ echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
+ echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
+ echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
+ exit 1
+ fi
+fi
+
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+# shellcheck disable=SC2086 # safe args
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ $MAVEN_DEBUG_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/muttley-files/mvnw.cmd b/muttley-files/mvnw.cmd
new file mode 100644
index 00000000..95ba6f54
--- /dev/null
+++ b/muttley-files/mvnw.cmd
@@ -0,0 +1,205 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Apache Maven Wrapper startup batch script, version 3.2.0
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
+if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+
+FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %WRAPPER_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
+SET WRAPPER_SHA_256_SUM=""
+FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
+)
+IF NOT %WRAPPER_SHA_256_SUM%=="" (
+ powershell -Command "&{"^
+ "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
+ "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
+ " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
+ " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
+ " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
+ " exit 1;"^
+ "}"^
+ "}"
+ if ERRORLEVEL 1 goto error
+)
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% ^
+ %JVM_CONFIG_MAVEN_PROPS% ^
+ %MAVEN_OPTS% ^
+ %MAVEN_DEBUG_OPTS% ^
+ -classpath %WRAPPER_JAR% ^
+ "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
+ %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
+if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%"=="on" pause
+
+if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
+
+cmd /C exit /B %ERROR_CODE%
diff --git a/muttley-files/pom.xml b/muttley-files/pom.xml
new file mode 100644
index 00000000..ffb85a1f
--- /dev/null
+++ b/muttley-files/pom.xml
@@ -0,0 +1,30 @@
+
+
+
+ br.com.muttley
+ muttley-cloud
+ ${revision}
+
+ 4.0.0
+ jar
+
+ muttley-files
+
+ muttley-files
+ Demo project for Spring Boot
+
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+
+ br.com.muttley
+ muttley-utils
+
+
+
+
diff --git a/muttley-files/src/main/java/br/com/muttley/files/config/MuttleyFilesConfig.java b/muttley-files/src/main/java/br/com/muttley/files/config/MuttleyFilesConfig.java
new file mode 100644
index 00000000..f05e9cc7
--- /dev/null
+++ b/muttley-files/src/main/java/br/com/muttley/files/config/MuttleyFilesConfig.java
@@ -0,0 +1,32 @@
+package br.com.muttley.files.config;
+
+import br.com.muttley.files.properties.Properties;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.EnableAsync;
+
+import java.nio.file.Paths;
+
+/**
+ * @author Joel Rodrigues Moreira on 28/07/2023.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Configuration
+@EnableAsync
+@ComponentScan(basePackages = "br.com.muttley.files.listeners")
+@EnableConfigurationProperties(Properties.class)
+public class MuttleyFilesConfig implements InitializingBean {
+ @Autowired
+ private Properties properties;
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ LoggerFactory.getLogger(MuttleyFilesConfig.class).info("File caching service has been configured: \n\t" + Paths.get(properties.getFiles()).toAbsolutePath().toString());
+ }
+}
+
diff --git a/muttley-files/src/main/java/br/com/muttley/files/events/DeleteAsyncFilesEvent.java b/muttley-files/src/main/java/br/com/muttley/files/events/DeleteAsyncFilesEvent.java
new file mode 100644
index 00000000..2fdbb52a
--- /dev/null
+++ b/muttley-files/src/main/java/br/com/muttley/files/events/DeleteAsyncFilesEvent.java
@@ -0,0 +1,16 @@
+package br.com.muttley.files.events;
+
+import br.com.muttley.files.model.FileForDelete;
+
+import java.util.Set;
+
+/**
+ * @author Joel Rodrigues Moreira on 03/08/2023.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public class DeleteAsyncFilesEvent extends DeleteFilesEvent {
+ public DeleteAsyncFilesEvent(Set files) {
+ super(files);
+ }
+}
diff --git a/muttley-files/src/main/java/br/com/muttley/files/events/DeleteFilesEvent.java b/muttley-files/src/main/java/br/com/muttley/files/events/DeleteFilesEvent.java
new file mode 100644
index 00000000..6d5dbcd3
--- /dev/null
+++ b/muttley-files/src/main/java/br/com/muttley/files/events/DeleteFilesEvent.java
@@ -0,0 +1,27 @@
+package br.com.muttley.files.events;
+
+import br.com.muttley.files.model.FileForDelete;
+import org.springframework.context.ApplicationEvent;
+
+import java.util.Set;
+
+/**
+ * @author Joel Rodrigues Moreira on 03/08/2023.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+
+abstract class DeleteFilesEvent extends ApplicationEvent {
+
+ final Set files;
+
+ protected DeleteFilesEvent(final Set files) {
+ super(files);
+ this.files = files;
+ }
+
+ @Override
+ public Set getSource() {
+ return this.files;
+ }
+}
diff --git a/muttley-files/src/main/java/br/com/muttley/files/events/DeleteSyncFilesEvent.java b/muttley-files/src/main/java/br/com/muttley/files/events/DeleteSyncFilesEvent.java
new file mode 100644
index 00000000..addfb4e0
--- /dev/null
+++ b/muttley-files/src/main/java/br/com/muttley/files/events/DeleteSyncFilesEvent.java
@@ -0,0 +1,16 @@
+package br.com.muttley.files.events;
+
+import br.com.muttley.files.model.FileForDelete;
+
+import java.util.Set;
+
+/**
+ * @author Joel Rodrigues Moreira on 03/08/2023.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public class DeleteSyncFilesEvent extends DeleteFilesEvent {
+ public DeleteSyncFilesEvent(Set files) {
+ super(files);
+ }
+}
diff --git a/muttley-files/src/main/java/br/com/muttley/files/events/DownloadAsyncFilesEvent.java b/muttley-files/src/main/java/br/com/muttley/files/events/DownloadAsyncFilesEvent.java
new file mode 100644
index 00000000..e4d8f3f3
--- /dev/null
+++ b/muttley-files/src/main/java/br/com/muttley/files/events/DownloadAsyncFilesEvent.java
@@ -0,0 +1,17 @@
+package br.com.muttley.files.events;
+
+import br.com.muttley.files.model.FileForDownload;
+
+import java.util.Set;
+
+/**
+ * @author Joel Rodrigues Moreira on 28/07/2023.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public class DownloadAsyncFilesEvent extends DownloadFilesEvent {
+
+ public DownloadAsyncFilesEvent(Set files) {
+ super(files);
+ }
+}
diff --git a/muttley-files/src/main/java/br/com/muttley/files/events/DownloadFilesEvent.java b/muttley-files/src/main/java/br/com/muttley/files/events/DownloadFilesEvent.java
new file mode 100644
index 00000000..7878f09f
--- /dev/null
+++ b/muttley-files/src/main/java/br/com/muttley/files/events/DownloadFilesEvent.java
@@ -0,0 +1,26 @@
+package br.com.muttley.files.events;
+
+import br.com.muttley.files.model.FileForDownload;
+import org.springframework.context.ApplicationEvent;
+
+import java.util.Set;
+
+/**
+ * @author Joel Rodrigues Moreira on 28/07/2023.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+abstract class DownloadFilesEvent extends ApplicationEvent {
+
+ final Set files;
+
+ protected DownloadFilesEvent(final Set files) {
+ super(files);
+ this.files = files;
+ }
+
+ @Override
+ public Set getSource() {
+ return this.files;
+ }
+}
diff --git a/muttley-files/src/main/java/br/com/muttley/files/events/DownloadSyncFilesEvent.java b/muttley-files/src/main/java/br/com/muttley/files/events/DownloadSyncFilesEvent.java
new file mode 100644
index 00000000..bdbd305a
--- /dev/null
+++ b/muttley-files/src/main/java/br/com/muttley/files/events/DownloadSyncFilesEvent.java
@@ -0,0 +1,16 @@
+package br.com.muttley.files.events;
+
+import br.com.muttley.files.model.FileForDownload;
+
+import java.util.Set;
+
+/**
+ * @author Joel Rodrigues Moreira on 28/07/2023.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public class DownloadSyncFilesEvent extends DownloadFilesEvent {
+ public DownloadSyncFilesEvent(Set files) {
+ super(files);
+ }
+}
diff --git a/muttley-files/src/main/java/br/com/muttley/files/events/MergeAsyncFilesEvent.java b/muttley-files/src/main/java/br/com/muttley/files/events/MergeAsyncFilesEvent.java
new file mode 100644
index 00000000..ef965d28
--- /dev/null
+++ b/muttley-files/src/main/java/br/com/muttley/files/events/MergeAsyncFilesEvent.java
@@ -0,0 +1,14 @@
+package br.com.muttley.files.events;
+
+import br.com.muttley.files.model.FileMerge;
+
+/**
+ * @author Joel Rodrigues Moreira on 03/08/2023.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public class MergeAsyncFilesEvent extends MergeFilesEvent{
+ protected MergeAsyncFilesEvent(FileMerge merge) {
+ super(merge);
+ }
+}
diff --git a/muttley-files/src/main/java/br/com/muttley/files/events/MergeFilesEvent.java b/muttley-files/src/main/java/br/com/muttley/files/events/MergeFilesEvent.java
new file mode 100644
index 00000000..a4ea15b2
--- /dev/null
+++ b/muttley-files/src/main/java/br/com/muttley/files/events/MergeFilesEvent.java
@@ -0,0 +1,25 @@
+package br.com.muttley.files.events;
+
+import br.com.muttley.files.model.FileMerge;
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * @author Joel Rodrigues Moreira on 03/08/2023.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+
+abstract class MergeFilesEvent extends ApplicationEvent {
+
+ final FileMerge merge;
+
+ protected MergeFilesEvent(FileMerge merge) {
+ super(merge);
+ this.merge = merge;
+ }
+
+ @Override
+ public FileMerge getSource() {
+ return this.merge;
+ }
+}
diff --git a/muttley-files/src/main/java/br/com/muttley/files/events/MergeSyncFilesEvent.java b/muttley-files/src/main/java/br/com/muttley/files/events/MergeSyncFilesEvent.java
new file mode 100644
index 00000000..9839dbe4
--- /dev/null
+++ b/muttley-files/src/main/java/br/com/muttley/files/events/MergeSyncFilesEvent.java
@@ -0,0 +1,14 @@
+package br.com.muttley.files.events;
+
+import br.com.muttley.files.model.FileMerge;
+
+/**
+ * @author Joel Rodrigues Moreira on 03/08/2023.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public class MergeSyncFilesEvent extends MergeFilesEvent {
+ protected MergeSyncFilesEvent(FileMerge merge) {
+ super(merge);
+ }
+}
diff --git a/muttley-files/src/main/java/br/com/muttley/files/listeners/DeleteAsyncFilesEventListener.java b/muttley-files/src/main/java/br/com/muttley/files/listeners/DeleteAsyncFilesEventListener.java
new file mode 100644
index 00000000..88b45d8b
--- /dev/null
+++ b/muttley-files/src/main/java/br/com/muttley/files/listeners/DeleteAsyncFilesEventListener.java
@@ -0,0 +1,42 @@
+package br.com.muttley.files.listeners;
+
+import br.com.muttley.files.events.DeleteAsyncFilesEvent;
+import br.com.muttley.files.properties.Properties;
+import br.com.muttley.utils.FilesUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.event.EventListener;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Component;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * @author Joel Rodrigues Moreira on 28/07/2023.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Component
+public class DeleteAsyncFilesEventListener {
+ private final Properties properties;
+ private final Logger logger = LoggerFactory.getLogger(DeleteAsyncFilesEventListener.class);
+
+ @Autowired
+ public DeleteAsyncFilesEventListener(final Properties properties) {
+ this.properties = properties;
+ }
+
+ @EventListener(DeleteAsyncFilesEvent.class)
+ @Async
+ public void onApplicationEvent(DeleteAsyncFilesEvent event) {
+ event.getSource()
+ .parallelStream()
+ .forEach(it -> {
+ final Path path = Paths.get(this.properties.getFiles(), it.getPath().toString());
+ FilesUtils.removeFile(path, it.isDropParentIfEmpty());
+ logger.info("The successfully deleted file: \n\t Local file -> " + path.toAbsolutePath());
+ });
+ }
+}
diff --git a/muttley-files/src/main/java/br/com/muttley/files/listeners/DeleteSyncFilesEventListener.java b/muttley-files/src/main/java/br/com/muttley/files/listeners/DeleteSyncFilesEventListener.java
new file mode 100644
index 00000000..aa08c877
--- /dev/null
+++ b/muttley-files/src/main/java/br/com/muttley/files/listeners/DeleteSyncFilesEventListener.java
@@ -0,0 +1,40 @@
+package br.com.muttley.files.listeners;
+
+import br.com.muttley.files.events.DeleteSyncFilesEvent;
+import br.com.muttley.files.properties.Properties;
+import br.com.muttley.utils.FilesUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.event.EventListener;
+import org.springframework.stereotype.Component;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * @author Joel Rodrigues Moreira on 28/07/2023.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Component
+public class DeleteSyncFilesEventListener {
+ private final Properties properties;
+ private final Logger logger = LoggerFactory.getLogger(DeleteSyncFilesEventListener.class);
+
+ @Autowired
+ public DeleteSyncFilesEventListener(final Properties properties) {
+ this.properties = properties;
+ }
+
+ @EventListener(classes = DeleteSyncFilesEvent.class)
+ public void onApplicationEvent(DeleteSyncFilesEvent event) {
+ event.getSource()
+ .parallelStream()
+ .forEach(it -> {
+ final Path path = Paths.get(this.properties.getFiles(), it.getPath().toString());
+ FilesUtils.removeFile(path, it.isDropParentIfEmpty());
+ logger.info("The successfully deleted file: \n\t Local file -> " + path.toAbsolutePath());
+ });
+ }
+}
diff --git a/muttley-files/src/main/java/br/com/muttley/files/listeners/DownloadAsyncFilesEventListener.java b/muttley-files/src/main/java/br/com/muttley/files/listeners/DownloadAsyncFilesEventListener.java
new file mode 100644
index 00000000..5f7d5dd6
--- /dev/null
+++ b/muttley-files/src/main/java/br/com/muttley/files/listeners/DownloadAsyncFilesEventListener.java
@@ -0,0 +1,42 @@
+package br.com.muttley.files.listeners;
+
+import br.com.muttley.files.events.DownloadAsyncFilesEvent;
+import br.com.muttley.files.properties.Properties;
+import br.com.muttley.utils.FilesUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.event.EventListener;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Component;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * @author Joel Rodrigues Moreira on 28/07/2023.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Component
+public class DownloadAsyncFilesEventListener {
+ private final Properties properties;
+ private final Logger logger = LoggerFactory.getLogger(DownloadAsyncFilesEventListener.class);
+
+ @Autowired
+ public DownloadAsyncFilesEventListener(final Properties properties) {
+ this.properties = properties;
+ }
+
+ @EventListener(classes = DownloadAsyncFilesEvent.class)
+ @Async
+ public void onApplicationEvent(DownloadAsyncFilesEvent event) {
+ event.getSource()
+ .parallelStream()
+ .forEach(it -> {
+ final Path path = Paths.get(this.properties.getFiles(), it.getPath().toString());
+ FilesUtils.downloadFile(it.getUrl(), path, it.isReplaceIfExists());
+ logger.info("The file has been successfully downloaded: \n\t Local file -> " + path.toAbsolutePath() + "\n\t Remote file -> " + it.getUrl());
+ });
+ }
+}
diff --git a/muttley-files/src/main/java/br/com/muttley/files/listeners/DownloadSyncFilesEventListener.java b/muttley-files/src/main/java/br/com/muttley/files/listeners/DownloadSyncFilesEventListener.java
new file mode 100644
index 00000000..43409ad9
--- /dev/null
+++ b/muttley-files/src/main/java/br/com/muttley/files/listeners/DownloadSyncFilesEventListener.java
@@ -0,0 +1,40 @@
+package br.com.muttley.files.listeners;
+
+import br.com.muttley.files.events.DownloadSyncFilesEvent;
+import br.com.muttley.files.properties.Properties;
+import br.com.muttley.utils.FilesUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.event.EventListener;
+import org.springframework.stereotype.Component;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * @author Joel Rodrigues Moreira on 28/07/2023.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Component
+public class DownloadSyncFilesEventListener {
+ private final Properties properties;
+ private final Logger logger = LoggerFactory.getLogger(DownloadSyncFilesEventListener.class);
+
+ @Autowired
+ public DownloadSyncFilesEventListener(final Properties properties) {
+ this.properties = properties;
+ }
+
+ @EventListener(DownloadSyncFilesEvent.class)
+ public void onApplicationEvent(DownloadSyncFilesEvent event) {
+ event.getSource()
+ .parallelStream()
+ .forEach(it -> {
+ final Path path = Paths.get(this.properties.getFiles(), it.getPath().toString());
+ FilesUtils.downloadFile(it.getUrl(), path, it.isReplaceIfExists());
+ logger.info("The file has been successfully downloaded: \n\t Local file -> " + path.toAbsolutePath() + "\n\t Remote file -> " + it.getUrl());
+ });
+ }
+}
diff --git a/muttley-files/src/main/java/br/com/muttley/files/listeners/MergeAsyncFilesEventListener.java b/muttley-files/src/main/java/br/com/muttley/files/listeners/MergeAsyncFilesEventListener.java
new file mode 100644
index 00000000..e5c40571
--- /dev/null
+++ b/muttley-files/src/main/java/br/com/muttley/files/listeners/MergeAsyncFilesEventListener.java
@@ -0,0 +1,64 @@
+package br.com.muttley.files.listeners;
+
+import br.com.muttley.files.events.MergeAsyncFilesEvent;
+import br.com.muttley.files.properties.Properties;
+import br.com.muttley.utils.FilesUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.event.EventListener;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Component;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * @author Joel Rodrigues Moreira on 03/08/2023.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Component
+public class MergeAsyncFilesEventListener {
+ private final Properties properties;
+ private final Logger logger = LoggerFactory.getLogger(MergeAsyncFilesEventListener.class);
+
+ @Autowired
+ public MergeAsyncFilesEventListener(final Properties properties) {
+ this.properties = properties;
+ }
+
+ @EventListener(MergeAsyncFilesEvent.class)
+ @Async
+ public void onApplicationEvent(MergeAsyncFilesEvent event) {
+ //removendo todos os arquivos primeiramente
+ event.getSource()
+ .getFilesForDelete()
+ .parallelStream()
+ .forEach(it -> {
+ try {
+ final Path path = Paths.get(this.properties.getFiles(), it.getPath().toString());
+ FilesUtils.removeFile(path, it.isDropParentIfEmpty());
+ logger.info("The successfully deleted file: \n\t Local file -> " + path.toAbsolutePath());
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ });
+
+ //baixando os arquivo necessários
+ event.getSource()
+ .getFilesForDownload()
+ .parallelStream()
+ .forEach(it -> {
+ try {
+ final Path path = Paths.get(this.properties.getFiles(), it.getPath().toString());
+ FilesUtils.downloadFile(it.getUrl(), path, it.isReplaceIfExists());
+ logger.info("The file has been successfully downloaded: \n\t Local file -> " + path.toAbsolutePath() + "\n\t Remote file -> " + it.getUrl());
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ });
+
+
+ }
+}
diff --git a/muttley-files/src/main/java/br/com/muttley/files/listeners/MergeSyncFilesEventListener.java b/muttley-files/src/main/java/br/com/muttley/files/listeners/MergeSyncFilesEventListener.java
new file mode 100644
index 00000000..c756ac18
--- /dev/null
+++ b/muttley-files/src/main/java/br/com/muttley/files/listeners/MergeSyncFilesEventListener.java
@@ -0,0 +1,54 @@
+package br.com.muttley.files.listeners;
+
+import br.com.muttley.files.events.MergeSyncFilesEvent;
+import br.com.muttley.files.properties.Properties;
+import br.com.muttley.utils.FilesUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.event.EventListener;
+import org.springframework.stereotype.Component;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * @author Joel Rodrigues Moreira on 03/08/2023.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Component
+public class MergeSyncFilesEventListener {
+ private final Properties properties;
+ private final Logger logger = LoggerFactory.getLogger(MergeSyncFilesEventListener.class);
+
+ @Autowired
+ public MergeSyncFilesEventListener(final Properties properties) {
+ this.properties = properties;
+ }
+
+ @EventListener(MergeSyncFilesEvent.class)
+ public void onApplicationEvent(MergeSyncFilesEvent event) {
+ //removendo todos os arquivos primeiramente
+ event.getSource()
+ .getFilesForDelete()
+ .parallelStream()
+ .forEach(it -> {
+ final Path path = Paths.get(this.properties.getFiles(), it.getPath().toString());
+ FilesUtils.removeFile(path, it.isDropParentIfEmpty());
+ logger.info("The successfully deleted file: \n\t Local file -> " + path.toAbsolutePath());
+ });
+
+ //baixando os arquivo necessários
+ event.getSource()
+ .getFilesForDownload()
+ .parallelStream()
+ .forEach(it -> {
+ final Path path = Paths.get(this.properties.getFiles(), it.getPath().toString());
+ FilesUtils.downloadFile(it.getUrl(), path, it.isReplaceIfExists());
+ logger.info("The file has been successfully downloaded: \n\t Local file -> " + path.toAbsolutePath() + "\n\t Remote file -> " + it.getUrl());
+ });
+
+
+ }
+}
diff --git a/muttley-files/src/main/java/br/com/muttley/files/model/FileForDelete.java b/muttley-files/src/main/java/br/com/muttley/files/model/FileForDelete.java
new file mode 100644
index 00000000..1cc7c0c9
--- /dev/null
+++ b/muttley-files/src/main/java/br/com/muttley/files/model/FileForDelete.java
@@ -0,0 +1,30 @@
+package br.com.muttley.files.model;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.experimental.Accessors;
+
+import java.nio.file.Path;
+
+/**
+ * @author Joel Rodrigues Moreira on 03/08/2023.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Getter
+@Setter
+@Accessors(chain = true)
+public class FileForDelete {
+ final Path path;
+ final boolean dropParentIfEmpty;
+
+ public FileForDelete(Path path, boolean dropParentIfEmpty) {
+ this.path = path;
+ this.dropParentIfEmpty = dropParentIfEmpty;
+ }
+
+ public FileForDelete(Path path) {
+ this.path = path;
+ this.dropParentIfEmpty = false;
+ }
+}
diff --git a/muttley-files/src/main/java/br/com/muttley/files/model/FileForDownload.java b/muttley-files/src/main/java/br/com/muttley/files/model/FileForDownload.java
new file mode 100644
index 00000000..c198a894
--- /dev/null
+++ b/muttley-files/src/main/java/br/com/muttley/files/model/FileForDownload.java
@@ -0,0 +1,52 @@
+package br.com.muttley.files.model;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * @author Joel Rodrigues Moreira on 28/07/2023.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Getter
+@EqualsAndHashCode(of = "url")
+public class FileForDownload {
+ final URL url;
+ final Path path;
+ final boolean replaceIfExists;
+
+ public FileForDownload(final URL url, final Path path, final boolean replaceIfExists) {
+ this.url = url;
+ this.path = path;
+ this.replaceIfExists = replaceIfExists;
+ }
+
+ public FileForDownload(final URL url, final Path path) {
+ this(url, path, false);
+ }
+
+ public FileForDownload(final String url, final String path) throws MalformedURLException {
+ this(toURL(url), Paths.get(path), false);
+ }
+
+ private static URL toURL(final String url) {
+ try {
+ return new URL(url);
+ } catch (MalformedURLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public FileForDelete toFileForDelete() {
+ return new FileForDelete(this.path);
+ }
+
+ public FileForDelete toFileForDelete(final boolean removeDirectory) {
+ return new FileForDelete(this.path, removeDirectory);
+ }
+}
diff --git a/muttley-files/src/main/java/br/com/muttley/files/model/FileMerge.java b/muttley-files/src/main/java/br/com/muttley/files/model/FileMerge.java
new file mode 100644
index 00000000..65da0763
--- /dev/null
+++ b/muttley-files/src/main/java/br/com/muttley/files/model/FileMerge.java
@@ -0,0 +1,29 @@
+package br.com.muttley.files.model;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.experimental.Accessors;
+
+import java.util.Set;
+
+/**
+ * @author Joel Rodrigues Moreira on 03/08/2023.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Getter
+@Setter
+@Accessors(chain = true)
+public class FileMerge {
+ final Set filesForDownload;
+ final Set filesForDelete;
+
+ public FileMerge(Set filesForDownload, Set filesForDelete) {
+ this.filesForDownload = filesForDownload;
+ this.filesForDelete = filesForDelete;
+ }
+
+ public FileMerge(Set filesForDownload) {
+ this(filesForDownload, null);
+ }
+}
diff --git a/muttley-files/src/main/java/br/com/muttley/files/properties/Properties.java b/muttley-files/src/main/java/br/com/muttley/files/properties/Properties.java
new file mode 100644
index 00000000..eb20a72e
--- /dev/null
+++ b/muttley-files/src/main/java/br/com/muttley/files/properties/Properties.java
@@ -0,0 +1,24 @@
+package br.com.muttley.files.properties;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.experimental.Accessors;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import static br.com.muttley.files.properties.Properties.PREFIX;
+
+/**
+ * @author Joel Rodrigues Moreira on 28/07/2023.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+
+@ConfigurationProperties(prefix = PREFIX)
+@Getter
+@Setter
+@Accessors(chain = true)
+public class Properties {
+ public static final String PREFIX = "muttley-cloud";
+
+ private String files = "muttley-cloud-files";
+}
diff --git a/muttley-files/src/main/resources/META-INF/spring.factories b/muttley-files/src/main/resources/META-INF/spring.factories
new file mode 100644
index 00000000..84ae76ca
--- /dev/null
+++ b/muttley-files/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ br.com.muttley.files.config.MuttleyFilesConfig
diff --git a/muttley-files/src/main/resources/application.properties b/muttley-files/src/main/resources/application.properties
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/muttley-files/src/main/resources/application.properties
@@ -0,0 +1 @@
+
diff --git a/muttley-headers/.gitignore b/muttley-headers/.gitignore
new file mode 100644
index 00000000..a2a3040a
--- /dev/null
+++ b/muttley-headers/.gitignore
@@ -0,0 +1,31 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**
+!**/src/test/**
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+
+### VS Code ###
+.vscode/
diff --git a/muttley-headers/.mvn/wrapper/MavenWrapperDownloader.java b/muttley-headers/.mvn/wrapper/MavenWrapperDownloader.java
new file mode 100644
index 00000000..a45eb6ba
--- /dev/null
+++ b/muttley-headers/.mvn/wrapper/MavenWrapperDownloader.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2007-present the original author or authors.
+ *
+ * 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
+ *
+ * https://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.
+ */
+
+import java.net.*;
+import java.io.*;
+import java.nio.channels.*;
+import java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+ private static final String WRAPPER_VERSION = "0.5.6";
+ /**
+ * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+ */
+ private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
+
+ /**
+ * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
+ * use instead of the default one.
+ */
+ private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+ ".mvn/wrapper/maven-wrapper.properties";
+
+ /**
+ * Path where the maven-wrapper.jar will be saved to.
+ */
+ private static final String MAVEN_WRAPPER_JAR_PATH =
+ ".mvn/wrapper/maven-wrapper.jar";
+
+ /**
+ * Name of the property which should be used to override the default download url for the wrapper.
+ */
+ private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+ public static void main(String args[]) {
+ System.out.println("- Downloader started");
+ File baseDirectory = new File(args[0]);
+ System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
+
+ // If the maven-wrapper.properties exists, read it and check if it contains a custom
+ // wrapperUrl parameter.
+ File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
+ String url = DEFAULT_DOWNLOAD_URL;
+ if (mavenWrapperPropertyFile.exists()) {
+ FileInputStream mavenWrapperPropertyFileInputStream = null;
+ try {
+ mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
+ Properties mavenWrapperProperties = new Properties();
+ mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
+ url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+ } catch (IOException e) {
+ System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
+ } finally {
+ try {
+ if (mavenWrapperPropertyFileInputStream != null) {
+ mavenWrapperPropertyFileInputStream.close();
+ }
+ } catch (IOException e) {
+ // Ignore ...
+ }
+ }
+ }
+ System.out.println("- Downloading from: " + url);
+
+ File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
+ if (!outputFile.getParentFile().exists()) {
+ if (!outputFile.getParentFile().mkdirs()) {
+ System.out.println(
+ "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
+ }
+ }
+ System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
+ try {
+ downloadFileFromURL(url, outputFile);
+ System.out.println("Done");
+ System.exit(0);
+ } catch (Throwable e) {
+ System.out.println("- Error downloading");
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+ if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
+ String username = System.getenv("MVNW_USERNAME");
+ char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
+ Authenticator.setDefault(new Authenticator() {
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(username, password);
+ }
+ });
+ }
+ URL website = new URL(urlString);
+ ReadableByteChannel rbc;
+ rbc = Channels.newChannel(website.openStream());
+ FileOutputStream fos = new FileOutputStream(destination);
+ fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+ fos.close();
+ rbc.close();
+ }
+
+}
diff --git a/muttley-headers/.mvn/wrapper/maven-wrapper.jar b/muttley-headers/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 00000000..2cc7d4a5
Binary files /dev/null and b/muttley-headers/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/muttley-headers/.mvn/wrapper/maven-wrapper.properties b/muttley-headers/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 00000000..642d572c
--- /dev/null
+++ b/muttley-headers/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,2 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
diff --git a/muttley-headers/mvnw b/muttley-headers/mvnw
new file mode 100755
index 00000000..a16b5431
--- /dev/null
+++ b/muttley-headers/mvnw
@@ -0,0 +1,310 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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
+#
+# https://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.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ export JAVA_HOME="`/usr/libexec/java_home`"
+ else
+ export JAVA_HOME="/Library/Java/Home"
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`which java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=`cd "$wdir/.."; pwd`
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' < "$1")"
+ fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found .mvn/wrapper/maven-wrapper.jar"
+ fi
+else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+ fi
+ if [ -n "$MVNW_REPOURL" ]; then
+ jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ else
+ jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ fi
+ while IFS="=" read key value; do
+ case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+ esac
+ done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Downloading from: $jarUrl"
+ fi
+ wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+ if $cygwin; then
+ wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+ fi
+
+ if command -v wget > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found wget ... using wget"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget "$jarUrl" -O "$wrapperJarPath"
+ else
+ wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found curl ... using curl"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl -o "$wrapperJarPath" "$jarUrl" -f
+ else
+ curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+ fi
+
+ else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Falling back to using Java to download"
+ fi
+ javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaClass=`cygpath --path --windows "$javaClass"`
+ fi
+ if [ -e "$javaClass" ]; then
+ if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Compiling MavenWrapperDownloader.java ..."
+ fi
+ # Compiling the Java class
+ ("$JAVA_HOME/bin/javac" "$javaClass")
+ fi
+ if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ # Running the downloader
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Running MavenWrapperDownloader.java ..."
+ fi
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+ echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/muttley-headers/mvnw.cmd b/muttley-headers/mvnw.cmd
new file mode 100644
index 00000000..c8d43372
--- /dev/null
+++ b/muttley-headers/mvnw.cmd
@@ -0,0 +1,182 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+
+FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %DOWNLOAD_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
diff --git a/muttley-headers/pom.xml b/muttley-headers/pom.xml
new file mode 100644
index 00000000..35e0302a
--- /dev/null
+++ b/muttley-headers/pom.xml
@@ -0,0 +1,39 @@
+
+
+
+ br.com.muttley
+ muttley-cloud
+ ${revision}
+
+ 4.0.0
+ jar
+ br.com.muttley
+
+ muttley-headers
+
+ muttley-headers
+ Demo project for Spring Boot
+
+
+
+
+ br.com.muttley
+ muttley-model
+ provided
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+ provided
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
diff --git a/muttley-headers/src/main/java/br/com/muttley/headers/MuttleyHeaderConfig.java b/muttley-headers/src/main/java/br/com/muttley/headers/MuttleyHeaderConfig.java
new file mode 100644
index 00000000..c5a906d2
--- /dev/null
+++ b/muttley-headers/src/main/java/br/com/muttley/headers/MuttleyHeaderConfig.java
@@ -0,0 +1,75 @@
+package br.com.muttley.headers;
+
+import br.com.muttley.headers.components.MuttleyCurrentTimezone;
+import br.com.muttley.headers.components.MuttleyCurrentVersion;
+import br.com.muttley.headers.components.MuttleyRequestHeader;
+import br.com.muttley.headers.components.MuttleySerializeType;
+import br.com.muttley.headers.components.MuttleyUserAgent;
+import br.com.muttley.headers.components.MuttleyUserAgentName;
+import br.com.muttley.headers.components.impl.MuttleyCurrentTimezoneImpl;
+import br.com.muttley.headers.components.impl.MuttleyCurrentVersionImpl;
+import br.com.muttley.headers.components.impl.MuttleyRequestHeaderImpl;
+import br.com.muttley.headers.components.impl.MuttleySerializeTypeImpl;
+import br.com.muttley.headers.components.impl.MuttleyUserAgentImpl;
+import br.com.muttley.headers.components.impl.MuttleyUserAgentNameImpl;
+import br.com.muttley.headers.services.MetadataService;
+import br.com.muttley.headers.services.impl.MetadataServiceImpl;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.info.BuildProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.context.annotation.Scope;
+
+import javax.servlet.http.HttpServletRequest;
+
+import static org.springframework.context.annotation.ScopedProxyMode.TARGET_CLASS;
+import static org.springframework.web.context.WebApplicationContext.SCOPE_REQUEST;
+
+@Configuration
+public class MuttleyHeaderConfig {
+ @Bean(name = "userAgent")
+ @Scope(value = SCOPE_REQUEST, proxyMode = TARGET_CLASS)
+ public MuttleyUserAgent getUserAgent(@Autowired final ObjectProvider requestProvider) {
+ return new MuttleyUserAgentImpl(requestProvider);
+ }
+
+ @Bean(name = "currentTimezone")
+ @Primary
+ @Scope(value = SCOPE_REQUEST, proxyMode = TARGET_CLASS)
+ public MuttleyCurrentTimezone getCurrentTimezone(@Autowired final ObjectProvider requestProvider) {
+ return new MuttleyCurrentTimezoneImpl(requestProvider);
+ }
+
+ @Bean(name = "serializeType")
+ @Scope(value = SCOPE_REQUEST, proxyMode = TARGET_CLASS)
+ public MuttleySerializeType getSerializeType(@Autowired final HttpServletRequest requestProvider) {
+ return new MuttleySerializeTypeImpl(requestProvider);
+ }
+
+ @Bean(name = "currentVersion")
+ @Scope(value = SCOPE_REQUEST, proxyMode = TARGET_CLASS)
+ @Autowired
+ public MuttleyCurrentVersion getCurrentVersion(final ObjectProvider requestProvider, final BuildProperties buildProperties) {
+ return new MuttleyCurrentVersionImpl(requestProvider, buildProperties);
+ }
+
+ @Bean(name = "userAgentName")
+ @Scope(value = SCOPE_REQUEST, proxyMode = TARGET_CLASS)
+ public MuttleyUserAgentName getUserAgentName(@Autowired final ObjectProvider requestProvider) {
+ return new MuttleyUserAgentNameImpl(requestProvider);
+ }
+
+ @Bean
+ @Primary
+ public MetadataService getMetadataService() {
+ return new MetadataServiceImpl();
+ }
+
+ @Bean("requestHeader")
+ @Scope(value = SCOPE_REQUEST, proxyMode = TARGET_CLASS)
+ public MuttleyRequestHeader getRequestHeader(@Autowired final HttpServletRequest request) {
+ return new MuttleyRequestHeaderImpl(request);
+ }
+}
diff --git a/muttley-headers/src/main/java/br/com/muttley/headers/components/MuttleyCurrentTimezone.java b/muttley-headers/src/main/java/br/com/muttley/headers/components/MuttleyCurrentTimezone.java
new file mode 100644
index 00000000..5f0ccfc3
--- /dev/null
+++ b/muttley-headers/src/main/java/br/com/muttley/headers/components/MuttleyCurrentTimezone.java
@@ -0,0 +1,21 @@
+package br.com.muttley.headers.components;
+
+import br.com.muttley.model.TimeZoneDocument;
+
+/**
+ * @author Joel Rodrigues Moreira on 02/06/2021.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public interface MuttleyCurrentTimezone {
+
+ String getCurrentValue();
+
+ String getCurrentTimezoneFromRequestOrServer();
+
+ boolean containsValidValue();
+
+ TimeZoneDocument getCurrentTimezoneDocument();
+
+ String getCurrenteTimeZoneFromServer();
+}
diff --git a/muttley-headers/src/main/java/br/com/muttley/headers/components/MuttleyCurrentVersion.java b/muttley-headers/src/main/java/br/com/muttley/headers/components/MuttleyCurrentVersion.java
new file mode 100644
index 00000000..632608f0
--- /dev/null
+++ b/muttley-headers/src/main/java/br/com/muttley/headers/components/MuttleyCurrentVersion.java
@@ -0,0 +1,14 @@
+package br.com.muttley.headers.components;
+
+/**
+ * @author Joel Rodrigues Moreira on 02/06/2021.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public interface MuttleyCurrentVersion {
+ String getCurrentValue();
+
+ boolean containsValidValue();
+
+ String getCurrenteFromServer();
+}
diff --git a/muttley-headers/src/main/java/br/com/muttley/headers/components/MuttleyRequestHeader.java b/muttley-headers/src/main/java/br/com/muttley/headers/components/MuttleyRequestHeader.java
new file mode 100644
index 00000000..a212c3e6
--- /dev/null
+++ b/muttley-headers/src/main/java/br/com/muttley/headers/components/MuttleyRequestHeader.java
@@ -0,0 +1,14 @@
+package br.com.muttley.headers.components;
+
+/**
+ * @author Joel Rodrigues Moreira on 02/06/2021.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public interface MuttleyRequestHeader {
+ boolean isRequestFromAdminServer();
+
+ boolean hasKey(String key);
+
+ String getByKey(String key);
+}
diff --git a/muttley-headers/src/main/java/br/com/muttley/headers/components/MuttleySerializeType.java b/muttley-headers/src/main/java/br/com/muttley/headers/components/MuttleySerializeType.java
new file mode 100644
index 00000000..b4c14e05
--- /dev/null
+++ b/muttley-headers/src/main/java/br/com/muttley/headers/components/MuttleySerializeType.java
@@ -0,0 +1,18 @@
+package br.com.muttley.headers.components;
+
+/**
+ * @author Joel Rodrigues Moreira on 02/06/2021.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public interface MuttleySerializeType {
+ boolean isSync();
+
+ boolean isObjectId();
+
+ boolean isObjectIdAndSync();
+
+ boolean isInternal();
+
+ boolean containsValidValue();
+}
diff --git a/muttley-headers/src/main/java/br/com/muttley/headers/components/MuttleyUserAgent.java b/muttley-headers/src/main/java/br/com/muttley/headers/components/MuttleyUserAgent.java
new file mode 100644
index 00000000..17efc31f
--- /dev/null
+++ b/muttley-headers/src/main/java/br/com/muttley/headers/components/MuttleyUserAgent.java
@@ -0,0 +1,15 @@
+package br.com.muttley.headers.components;
+
+/**
+ * @author Joel Rodrigues Moreira on 02/06/2021.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public interface MuttleyUserAgent {
+ boolean isMobile();
+
+ boolean containsValidValue();
+
+ String getCurrentValue();
+
+}
diff --git a/muttley-headers/src/main/java/br/com/muttley/headers/components/MuttleyUserAgentName.java b/muttley-headers/src/main/java/br/com/muttley/headers/components/MuttleyUserAgentName.java
new file mode 100644
index 00000000..420aa70e
--- /dev/null
+++ b/muttley-headers/src/main/java/br/com/muttley/headers/components/MuttleyUserAgentName.java
@@ -0,0 +1,11 @@
+package br.com.muttley.headers.components;
+
+/**
+ * @author Joel Rodrigues Moreira on 02/06/2021.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public interface MuttleyUserAgentName {
+
+ String getCurrentValue();
+}
diff --git a/muttley-headers/src/main/java/br/com/muttley/headers/components/impl/MuttleyCurrentTimezoneImpl.java b/muttley-headers/src/main/java/br/com/muttley/headers/components/impl/MuttleyCurrentTimezoneImpl.java
new file mode 100644
index 00000000..e04fbe27
--- /dev/null
+++ b/muttley-headers/src/main/java/br/com/muttley/headers/components/impl/MuttleyCurrentTimezoneImpl.java
@@ -0,0 +1,90 @@
+package br.com.muttley.headers.components.impl;
+
+import br.com.muttley.headers.components.MuttleyCurrentTimezone;
+import br.com.muttley.headers.model.MuttleyHeader;
+import br.com.muttley.model.TimeZoneDocument;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.annotation.RequestScope;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.TimeZone;
+
+import static br.com.muttley.utils.TimeZoneUtils.getTimezoneFromId;
+import static br.com.muttley.utils.TimeZoneUtils.isValidTimeZone;
+import static org.springframework.util.StringUtils.isEmpty;
+
+/**
+ * @author Joel Rodrigues Moreira on 29/07/19.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Component("currentTimezone")
+@RequestScope
+public class MuttleyCurrentTimezoneImpl extends MuttleyHeader implements MuttleyCurrentTimezone {
+ public static final String CURRENT_TIMEZONE = "Current-Timezone";
+
+ public MuttleyCurrentTimezoneImpl(@Autowired final ObjectProvider requestProvider) {
+ super(CURRENT_TIMEZONE, requestProvider);
+ }
+
+ @Override
+ public String getCurrentValue() {
+ if (this.containsValidValue()) {
+ if (this.isValid()) {
+ String currentTimezone = super.getCurrentValue();
+
+ if (!(currentTimezone.startsWith("+") || currentTimezone.startsWith("-"))) {
+ //se chegou aqui logo é timezone positivo
+ currentTimezone = "+" + currentTimezone;
+ }
+ if (!currentTimezone.contains(":")) {
+ currentTimezone = currentTimezone.substring(0, 3) + ":" + currentTimezone.substring(3, 5);
+ }
+
+ return currentTimezone;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * O metodo irá tentar pegar o timezone atual da requisição
+ * caso não exista por padrão irá retornar o do servidor
+ */
+ @Override
+ public String getCurrentTimezoneFromRequestOrServer() {
+ final String curretTimezone = this.getCurrentValue();
+ if (curretTimezone != null) {
+ return curretTimezone;
+ }
+ return this.getCurrenteTimeZoneFromServer();
+ }
+
+ @Override
+ public boolean containsValidValue() {
+ return !isEmpty(super.getCurrentValue());
+ }
+
+ protected boolean isValid() {
+ return isValidTimeZone(super.getCurrentValue());
+ }
+
+ @Override
+ public TimeZoneDocument getCurrentTimezoneDocument() {
+ final String timeZoneServer = getCurrenteTimeZoneFromServer();
+ return new TimeZoneDocument()
+ .setCurrentTimeZone(getCurrentValue())
+ .setCreateTimeZone(getCurrentValue())
+ .setServerCurrentTimeZone(timeZoneServer)
+ .setServerCreteTimeZone(timeZoneServer);
+ }
+
+
+ @Override
+ public String getCurrenteTimeZoneFromServer() {
+ final TimeZone tz = TimeZone.getDefault();
+ return getTimezoneFromId(tz.getID());
+ }
+}
diff --git a/muttley-headers/src/main/java/br/com/muttley/headers/components/impl/MuttleyCurrentVersionImpl.java b/muttley-headers/src/main/java/br/com/muttley/headers/components/impl/MuttleyCurrentVersionImpl.java
new file mode 100644
index 00000000..e9abafe4
--- /dev/null
+++ b/muttley-headers/src/main/java/br/com/muttley/headers/components/impl/MuttleyCurrentVersionImpl.java
@@ -0,0 +1,50 @@
+package br.com.muttley.headers.components.impl;
+
+import br.com.muttley.headers.components.MuttleyCurrentVersion;
+import br.com.muttley.headers.model.MuttleyHeader;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.info.BuildProperties;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.annotation.RequestScope;
+
+import javax.servlet.http.HttpServletRequest;
+
+import static org.springframework.util.StringUtils.isEmpty;
+
+/**
+ * @author Joel Rodrigues Moreira on 29/07/19.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Component("currentVersion")
+@RequestScope
+public class MuttleyCurrentVersionImpl extends MuttleyHeader implements MuttleyCurrentVersion {
+ public static final String CURRENT_VERION = "Current-Version";
+ private final BuildProperties buildProperties;
+
+ @Autowired
+ public MuttleyCurrentVersionImpl(final ObjectProvider requestProvider, final BuildProperties buildProperties) {
+ super(CURRENT_VERION, requestProvider);
+ this.buildProperties = buildProperties;
+ }
+
+ @Override
+ public String getCurrentValue() {
+ if (this.containsValidValue()) {
+ return super.getCurrentValue();
+ }
+ return null;
+ }
+
+ @Override
+ public boolean containsValidValue() {
+ return !isEmpty(super.getCurrentValue());
+ }
+
+
+ @Override
+ public String getCurrenteFromServer() {
+ return this.buildProperties.getVersion();
+ }
+}
diff --git a/muttley-headers/src/main/java/br/com/muttley/headers/components/impl/MuttleyRequestHeaderImpl.java b/muttley-headers/src/main/java/br/com/muttley/headers/components/impl/MuttleyRequestHeaderImpl.java
new file mode 100644
index 00000000..820e7328
--- /dev/null
+++ b/muttley-headers/src/main/java/br/com/muttley/headers/components/impl/MuttleyRequestHeaderImpl.java
@@ -0,0 +1,41 @@
+package br.com.muttley.headers.components.impl;
+
+import br.com.muttley.headers.components.MuttleyRequestHeader;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.annotation.RequestScope;
+
+import javax.servlet.http.HttpServletRequest;
+
+import static br.com.muttley.headers.model.MuttleyHeader.KEY_ADMIN_SERVER;
+
+/**
+ * @author Joel Rodrigues Moreira 23/04/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Component("requestHeader")
+@RequestScope
+public class MuttleyRequestHeaderImpl implements MuttleyRequestHeader {
+ private final HttpServletRequest request;
+
+ @Autowired
+ public MuttleyRequestHeaderImpl(final HttpServletRequest request) {
+ this.request = request;
+ }
+
+ @Override
+ public boolean isRequestFromAdminServer() {
+ return this.hasKey(KEY_ADMIN_SERVER);
+ }
+
+ @Override
+ public boolean hasKey(final String key) {
+ return this.request.getHeader(key) != null;
+ }
+
+ @Override
+ public String getByKey(final String key) {
+ return this.request.getHeader(key);
+ }
+}
diff --git a/muttley-headers/src/main/java/br/com/muttley/headers/components/impl/MuttleySerializeTypeImpl.java b/muttley-headers/src/main/java/br/com/muttley/headers/components/impl/MuttleySerializeTypeImpl.java
new file mode 100644
index 00000000..76bf6c94
--- /dev/null
+++ b/muttley-headers/src/main/java/br/com/muttley/headers/components/impl/MuttleySerializeTypeImpl.java
@@ -0,0 +1,69 @@
+package br.com.muttley.headers.components.impl;
+
+import br.com.muttley.headers.components.MuttleySerializeType;
+import br.com.muttley.headers.model.MuttleyHeader;
+import br.com.muttley.model.SerializeType;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.annotation.RequestScope;
+
+import javax.servlet.http.HttpServletRequest;
+
+import static br.com.muttley.model.SerializeType.KEY_FROM_HEADER;
+import static br.com.muttley.model.SerializeType.OBJECT_ID_AND_SYNC_TYPE;
+import static br.com.muttley.model.SerializeType.OBJECT_ID_TYPE;
+import static br.com.muttley.model.SerializeType.SYNC_TYPE;
+
+/**
+ * @author Joel Rodrigues Moreira on 29/07/19.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ *
+ * Devemos verificar em cada requisição qual o tipo de serialização a ser utilizada
+ * SerializeType = 'sync' => devemos serializar o código vindo do serviço do cliente
+ * SerializeType = 'ObjectId' => devemos serializar o nosso próprio ObjectId
+ * SerializeType = 'ObjectIdAndSync' => devemos serializar o nosso próprio ObjectId juntamente com o sync
+ * SerializeType = null => devemos serializar o nosso próprio ObjectId
+ *
+ */
+@Component("serializeType")
+@RequestScope
+public class MuttleySerializeTypeImpl extends MuttleyHeader implements MuttleySerializeType {
+ private final SerializeType type;
+
+ /*public MuttleySerializeType(@Autowired final ObjectProvider request) {
+ this(request.getIfAvailable());
+ }*/
+
+ @Autowired
+ public MuttleySerializeTypeImpl(final HttpServletRequest request) {
+ super(KEY_FROM_HEADER, request);
+ this.type = SerializeType.Builder.build(request);
+ }
+
+ @Override
+ public boolean isSync() {
+ return this.type.isSync();
+ }
+
+ @Override
+ public boolean isObjectId() {
+ return this.type.isObjectId();
+ }
+
+ @Override
+ public boolean isObjectIdAndSync() {
+ return this.type.isObjectIdAndSync();
+ }
+
+ @Override
+ public boolean isInternal() {
+ return this.type.isInternal();
+ }
+
+ @Override
+ public boolean containsValidValue() {
+ return getCurrentValue() != null && (getCurrentValue().equals(SYNC_TYPE) || getCurrentValue().equals(OBJECT_ID_TYPE) || getCurrentValue().equals(OBJECT_ID_AND_SYNC_TYPE));
+ }
+
+}
diff --git a/muttley-headers/src/main/java/br/com/muttley/headers/components/impl/MuttleyUserAgentImpl.java b/muttley-headers/src/main/java/br/com/muttley/headers/components/impl/MuttleyUserAgentImpl.java
new file mode 100644
index 00000000..3863f6df
--- /dev/null
+++ b/muttley-headers/src/main/java/br/com/muttley/headers/components/impl/MuttleyUserAgentImpl.java
@@ -0,0 +1,32 @@
+package br.com.muttley.headers.components.impl;
+
+import br.com.muttley.headers.components.MuttleyUserAgent;
+import br.com.muttley.headers.model.MuttleyHeader;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.annotation.RequestScope;
+
+import javax.servlet.http.HttpServletRequest;
+
+import static org.springframework.http.HttpHeaders.USER_AGENT;
+
+/**
+ * @author Joel Rodrigues Moreira on 29/07/19.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Component("userAgente")
+@RequestScope
+public class MuttleyUserAgentImpl extends MuttleyHeader implements MuttleyUserAgent {
+ private static final String MOBILE = "MOBILE";
+
+ public MuttleyUserAgentImpl(@Autowired final ObjectProvider requestProvider) {
+ super(USER_AGENT, requestProvider);
+ }
+
+ @Override
+ public boolean isMobile() {
+ return MOBILE.equalsIgnoreCase(getCurrentValue());
+ }
+}
diff --git a/muttley-headers/src/main/java/br/com/muttley/headers/components/impl/MuttleyUserAgentNameImpl.java b/muttley-headers/src/main/java/br/com/muttley/headers/components/impl/MuttleyUserAgentNameImpl.java
new file mode 100644
index 00000000..3c6ebd16
--- /dev/null
+++ b/muttley-headers/src/main/java/br/com/muttley/headers/components/impl/MuttleyUserAgentNameImpl.java
@@ -0,0 +1,25 @@
+package br.com.muttley.headers.components.impl;
+
+import br.com.muttley.headers.components.MuttleyUserAgentName;
+import br.com.muttley.headers.model.MuttleyHeader;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.annotation.RequestScope;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * @author Joel Rodrigues Moreira on 29/07/19.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Component("userAgentName")
+@RequestScope
+public class MuttleyUserAgentNameImpl extends MuttleyHeader implements MuttleyUserAgentName {
+ private static final String USER_AGENT_NAME = "User-Agent-Name";
+
+ public MuttleyUserAgentNameImpl(@Autowired final ObjectProvider requestProvider) {
+ super(USER_AGENT_NAME, requestProvider);
+ }
+}
diff --git a/muttley-headers/src/main/java/br/com/muttley/headers/model/MuttleyHeader.java b/muttley-headers/src/main/java/br/com/muttley/headers/model/MuttleyHeader.java
new file mode 100644
index 00000000..b8bc2ea2
--- /dev/null
+++ b/muttley-headers/src/main/java/br/com/muttley/headers/model/MuttleyHeader.java
@@ -0,0 +1,22 @@
+package br.com.muttley.headers.model;
+
+import org.springframework.beans.factory.ObjectProvider;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * @author Joel Rodrigues Moreira on 29/07/19.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public class MuttleyHeader extends MuttleyRequestMetaData {
+ public static final String KEY_ADMIN_SERVER = "MuttleyAdminServe";
+
+ public MuttleyHeader(final String key, final ObjectProvider requestProvider) {
+ super(key, requestProvider);
+ }
+
+ public MuttleyHeader(final String key, final HttpServletRequest request) {
+ super(key, request);
+ }
+}
diff --git a/muttley-headers/src/main/java/br/com/muttley/headers/model/MuttleyRequestMetaData.java b/muttley-headers/src/main/java/br/com/muttley/headers/model/MuttleyRequestMetaData.java
new file mode 100644
index 00000000..260cca0c
--- /dev/null
+++ b/muttley-headers/src/main/java/br/com/muttley/headers/model/MuttleyRequestMetaData.java
@@ -0,0 +1,46 @@
+package br.com.muttley.headers.model;
+
+import org.springframework.beans.factory.ObjectProvider;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * @author Joel Rodrigues Moreira on 29/07/19.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public class MuttleyRequestMetaData {
+ protected final String key;
+ private final HttpServletRequest request;
+ protected String currentValue;
+ private boolean resolved = false;
+
+ public MuttleyRequestMetaData(final String key, final HttpServletRequest request) {
+ this.key = key;
+ this.request = request;
+ }
+
+ public MuttleyRequestMetaData(final String key, final ObjectProvider requestProvider) {
+ this(key, requestProvider.getIfAvailable());
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public String getCurrentValue() {
+ if (!this.resolved) {
+ this.resolved = true;
+ if (request != null) {
+ this.currentValue = request.getHeader(this.key);
+ } else {
+ this.currentValue = null;
+ }
+ }
+ return this.currentValue;
+ }
+
+ public boolean containsValidValue() {
+ return this.getCurrentValue() != null;
+ }
+}
diff --git a/muttley-headers/src/main/java/br/com/muttley/headers/services/MetadataService.java b/muttley-headers/src/main/java/br/com/muttley/headers/services/MetadataService.java
new file mode 100644
index 00000000..13c1ace3
--- /dev/null
+++ b/muttley-headers/src/main/java/br/com/muttley/headers/services/MetadataService.java
@@ -0,0 +1,25 @@
+package br.com.muttley.headers.services;
+
+import br.com.muttley.model.Document;
+import br.com.muttley.model.MetadataDocument;
+import br.com.muttley.model.security.User;
+import br.com.muttley.model.security.domain.Domain;
+
+import java.util.Collection;
+
+/**
+ * @author Joel Rodrigues Moreira 12/03/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public interface MetadataService {
+ void generateNewMetadataFor(final User user, final Document value);
+
+ void generateNewMetadataFor(final User user, final Document value, final Domain domain);
+
+ void generateNewMetadataFor(final User user, final Collection extends Document> values);
+
+ void generateNewMetadataFor(final User user, final Collection extends Document> values, final Domain domain);
+
+ void generateMetaDataUpdateFor(final User user, final MetadataDocument currentMetadata, final Document value);
+}
diff --git a/muttley-headers/src/main/java/br/com/muttley/headers/services/impl/MetadataServiceImpl.java b/muttley-headers/src/main/java/br/com/muttley/headers/services/impl/MetadataServiceImpl.java
new file mode 100644
index 00000000..9ec258ea
--- /dev/null
+++ b/muttley-headers/src/main/java/br/com/muttley/headers/services/impl/MetadataServiceImpl.java
@@ -0,0 +1,175 @@
+package br.com.muttley.headers.services.impl;
+
+import br.com.muttley.headers.components.MuttleyCurrentTimezone;
+import br.com.muttley.headers.components.MuttleyCurrentVersion;
+import br.com.muttley.headers.components.MuttleyUserAgentName;
+import br.com.muttley.headers.services.MetadataService;
+import br.com.muttley.model.Document;
+import br.com.muttley.model.Historic;
+import br.com.muttley.model.MetadataDocument;
+import br.com.muttley.model.VersionDocument;
+import br.com.muttley.model.security.User;
+import br.com.muttley.model.security.domain.Domain;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+import java.util.Date;
+
+import static br.com.muttley.model.security.domain.Domain.PRIVATE;
+import static br.com.muttley.model.security.domain.Domain.PUBLIC;
+
+/**
+ * @author Joel Rodrigues Moreira 12/03/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Service
+public class MetadataServiceImpl implements MetadataService {
+ @Autowired
+ protected MuttleyCurrentTimezone currentTimezone;
+ @Autowired
+ protected MuttleyCurrentVersion currentVersion;
+ @Autowired
+ protected MuttleyUserAgentName userAgentName;
+
+ @Override
+ public void generateNewMetadataFor(final User user, final Document value) {
+ this.generateNewMetadataFor(user, value, null);
+ }
+
+ @Override
+ public void generateNewMetadataFor(final User user, final Document value, final Domain domain) {
+ //se não tiver nenhum metadata criado, vamos criar um
+ if (!value.containsMetadata()) {
+ value.setMetadata(new MetadataDocument(user)
+ .setTimeZones(this.currentTimezone.getCurrentTimezoneDocument())
+ .setDomain(generateDoaminByUser(user, domain))
+ .setVersionDocument(
+ new VersionDocument()
+ .setOriginVersionClientCreate(this.currentVersion.getCurrentValue())
+ .setOriginVersionClientLastUpdate(this.currentVersion.getCurrentValue())
+ .setOriginNameClientCreate(this.userAgentName.getCurrentValue())
+ .setOriginNameClientLastUpdate(this.userAgentName.getCurrentValue())
+ .setServerVersionCreate(this.currentVersion.getCurrenteFromServer())
+ .setServerVersionLastUpdate(this.currentVersion.getCurrenteFromServer())
+ ));
+ } else {
+ //se não tiver um domain definido devemos atribuir como private
+ if (!value.getMetadata().containsDomain()) {
+ value.getMetadata().setDomain(generateDoaminByUser(user, domain));
+ }
+ //se não tem um timezone válido, vamos criar um
+ if (!value.getMetadata().containsTimeZones()) {
+ value.getMetadata().setTimeZones(this.currentTimezone.getCurrentTimezoneDocument());
+ } else {
+ //se chegou aqui é sinal que já possui infos de timezones e devemos apenas checar e atualizar caso necessário
+
+ //O timezone atual informado é valido?
+ if (value.getMetadata().getTimeZones().isValidCurrentTimeZone()) {
+ //adicionado a mesma info no createTimezone já que estamos criando um novo registro
+ value.getMetadata().getTimeZones().setCreateTimeZone(value.getMetadata().getTimeZones().getCurrentTimeZone());
+ }
+
+ //adicionando infos de timezone do servidor
+ final String currentServerTimezone = this.currentTimezone.getCurrenteTimeZoneFromServer();
+ value.getMetadata().getTimeZones().setServerCreteTimeZone(currentServerTimezone);
+ value.getMetadata().getTimeZones().setServerCurrentTimeZone(currentServerTimezone);
+ }
+
+ //criando version valido
+ value.getMetadata().setVersionDocument(
+ new VersionDocument()
+ .setOriginVersionClientCreate(this.currentVersion.getCurrentValue())
+ .setOriginVersionClientLastUpdate(this.currentVersion.getCurrentValue())
+ .setOriginNameClientCreate(this.userAgentName.getCurrentValue())
+ .setOriginNameClientLastUpdate(this.userAgentName.getCurrentValue())
+ .setServerVersionCreate(this.currentVersion.getCurrenteFromServer())
+ .setServerVersionLastUpdate(this.currentVersion.getCurrenteFromServer())
+ );
+
+ //criando o historic
+ if (!value.getMetadata().containsHistoric()) {
+ value.getMetadata().setHistoric(new Historic());
+ }
+
+ final Date now = new Date();
+ value.getMetadata()
+ .getHistoric()
+ .setDtCreate(now)
+ .setDtChange(now)
+ .setCreatedBy(user)
+ .setLastChangeBy(user);
+
+ }
+ }
+
+ @Override
+ public void generateNewMetadataFor(final User user, final Collection extends Document> values){
+ this.generateNewMetadataFor(user, values, null);
+ }
+
+ @Override
+ public void generateNewMetadataFor(final User user, final Collection extends Document> values, final Domain domain) {
+ values.forEach(it -> {
+ this.generateNewMetadataFor(user, it, domain);
+ });
+ }
+
+ @Override
+ public void generateMetaDataUpdateFor(final User user, final MetadataDocument currentMetadata, final Document value) {
+ currentMetadata
+ .getTimeZones()
+ .setServerCurrentTimeZone(
+ this.currentTimezone.getCurrenteTimeZoneFromServer()
+ );
+
+
+ //se veio informações no registro, devemos aproveitar
+ if (value.containsMetadata()) {
+ if (value.getMetadata().containsDomain()) {
+ currentMetadata.setDomain(value.getMetadata().getDomain());
+ }
+ if (value.getMetadata().containsTimeZones()) {
+ if (value.getMetadata().getTimeZones().isValidCurrentTimeZone()) {
+ currentMetadata.getTimeZones().setCurrentTimeZone(value.getMetadata().getTimeZones().getCurrentTimeZone());
+ } else {
+ currentMetadata.getTimeZones().setCurrentTimeZone(this.currentTimezone.getCurrentValue());
+ }
+ } else {
+ currentMetadata.getTimeZones().setCurrentTimeZone(this.currentTimezone.getCurrentValue());
+ }
+ } else {
+ currentMetadata.getTimeZones()
+ .setCurrentTimeZone(this.currentTimezone.getCurrentValue())
+ .setServerCurrentTimeZone(this.currentTimezone.getCurrenteTimeZoneFromServer());
+ }
+ //setando versionamento
+ currentMetadata
+ .getVersionDocument()
+ .setServerVersionLastUpdate(this.currentVersion.getCurrenteFromServer())
+ .setOriginNameClientLastUpdate(this.userAgentName.getCurrentValue())
+ .setOriginVersionClientLastUpdate(this.currentVersion.getCurrentValue());
+
+ //setando o historic
+ currentMetadata.getHistoric()
+ .setLastChangeBy(user)
+ .setDtChange(new Date());
+
+ value.setMetadata(currentMetadata);
+ }
+
+ private Domain generateDoaminByUser(final User user, final Domain domain) {
+ //Definimos o tipo de dominio baseado no usuário atual
+ //se o usuário for owner, logo podemos definir o registro como publico,
+ //caso contrario será privado
+
+ try {
+ return domain != null ? domain : user.isOwner() ? PUBLIC : PRIVATE;
+ } catch (RuntimeException exception) {
+ //se deu erro ao tentar verificar se é um owner logo podemos incarar que é um registro privado
+ //o erro ocorre pois o usuário é novo e logo não tem info de owner
+ return PRIVATE;
+ }
+ }
+}
diff --git a/muttley-headers/src/main/resources/META-INF/spring.factories b/muttley-headers/src/main/resources/META-INF/spring.factories
new file mode 100644
index 00000000..ff36fdde
--- /dev/null
+++ b/muttley-headers/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ br.com.muttley.headers.MuttleyHeaderConfig
diff --git a/muttley-hermes-server.pom/.gitignore b/muttley-hermes-server.pom/.gitignore
new file mode 100644
index 00000000..a2a3040a
--- /dev/null
+++ b/muttley-hermes-server.pom/.gitignore
@@ -0,0 +1,31 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**
+!**/src/test/**
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+
+### VS Code ###
+.vscode/
diff --git a/muttley-hermes-server.pom/.mvn/wrapper/MavenWrapperDownloader.java b/muttley-hermes-server.pom/.mvn/wrapper/MavenWrapperDownloader.java
new file mode 100644
index 00000000..7f91a56e
--- /dev/null
+++ b/muttley-hermes-server.pom/.mvn/wrapper/MavenWrapperDownloader.java
@@ -0,0 +1,114 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you 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
+
+ https://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.
+*/
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+ /**
+ * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+ */
+ private static final String DEFAULT_DOWNLOAD_URL =
+ "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar";
+
+ /**
+ * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
+ * use instead of the default one.
+ */
+ private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+ ".mvn/wrapper/maven-wrapper.properties";
+
+ /**
+ * Path where the maven-wrapper.jar will be saved to.
+ */
+ private static final String MAVEN_WRAPPER_JAR_PATH =
+ ".mvn/wrapper/maven-wrapper.jar";
+
+ /**
+ * Name of the property which should be used to override the default download url for the wrapper.
+ */
+ private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+ public static void main(String args[]) {
+ System.out.println("- Downloader started");
+ File baseDirectory = new File(args[0]);
+ System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
+
+ // If the maven-wrapper.properties exists, read it and check if it contains a custom
+ // wrapperUrl parameter.
+ File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
+ String url = DEFAULT_DOWNLOAD_URL;
+ if (mavenWrapperPropertyFile.exists()) {
+ FileInputStream mavenWrapperPropertyFileInputStream = null;
+ try {
+ mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
+ Properties mavenWrapperProperties = new Properties();
+ mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
+ url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+ } catch (IOException e) {
+ System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
+ } finally {
+ try {
+ if (mavenWrapperPropertyFileInputStream != null) {
+ mavenWrapperPropertyFileInputStream.close();
+ }
+ } catch (IOException e) {
+ // Ignore ...
+ }
+ }
+ }
+ System.out.println("- Downloading from: : " + url);
+
+ File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
+ if (!outputFile.getParentFile().exists()) {
+ if (!outputFile.getParentFile().mkdirs()) {
+ System.out.println(
+ "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'");
+ }
+ }
+ System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
+ try {
+ downloadFileFromURL(url, outputFile);
+ System.out.println("Done");
+ System.exit(0);
+ } catch (Throwable e) {
+ System.out.println("- Error downloading");
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+ URL website = new URL(urlString);
+ ReadableByteChannel rbc;
+ rbc = Channels.newChannel(website.openStream());
+ FileOutputStream fos = new FileOutputStream(destination);
+ fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+ fos.close();
+ rbc.close();
+ }
+
+}
diff --git a/muttley-hermes-server.pom/.mvn/wrapper/maven-wrapper.jar b/muttley-hermes-server.pom/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 00000000..01e67997
Binary files /dev/null and b/muttley-hermes-server.pom/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/muttley-hermes-server.pom/.mvn/wrapper/maven-wrapper.properties b/muttley-hermes-server.pom/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 00000000..cd0d451c
--- /dev/null
+++ b/muttley-hermes-server.pom/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip
diff --git a/muttley-hermes-server.pom/muttley-hermes-api/.gitignore b/muttley-hermes-server.pom/muttley-hermes-api/.gitignore
new file mode 100644
index 00000000..82eca336
--- /dev/null
+++ b/muttley-hermes-server.pom/muttley-hermes-api/.gitignore
@@ -0,0 +1,25 @@
+/target/
+!.mvn/wrapper/maven-wrapper.jar
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/build/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
\ No newline at end of file
diff --git a/muttley-hermes-server.pom/muttley-hermes-api/.mvn/wrapper/maven-wrapper.jar b/muttley-hermes-server.pom/muttley-hermes-api/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 00000000..9cc84ea9
Binary files /dev/null and b/muttley-hermes-server.pom/muttley-hermes-api/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/muttley-hermes-server.pom/muttley-hermes-api/.mvn/wrapper/maven-wrapper.properties b/muttley-hermes-server.pom/muttley-hermes-api/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 00000000..b573bb50
--- /dev/null
+++ b/muttley-hermes-server.pom/muttley-hermes-api/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1 @@
+distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.3/apache-maven-3.5.3-bin.zip
diff --git a/muttley-hermes-server.pom/muttley-hermes-api/mvnw b/muttley-hermes-server.pom/muttley-hermes-api/mvnw
new file mode 100755
index 00000000..5bf251c0
--- /dev/null
+++ b/muttley-hermes-server.pom/muttley-hermes-api/mvnw
@@ -0,0 +1,225 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven2 Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ export JAVA_HOME="`/usr/libexec/java_home`"
+ else
+ export JAVA_HOME="/Library/Java/Home"
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Migwn, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+ # TODO classpath?
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`which java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=`cd "$wdir/.."; pwd`
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' < "$1")"
+ fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+echo $MAVEN_PROJECTBASEDIR
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/muttley-hermes-server.pom/muttley-hermes-api/mvnw.cmd b/muttley-hermes-server.pom/muttley-hermes-api/mvnw.cmd
new file mode 100644
index 00000000..019bd74d
--- /dev/null
+++ b/muttley-hermes-server.pom/muttley-hermes-api/mvnw.cmd
@@ -0,0 +1,143 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven2 Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
diff --git a/muttley-hermes-server.pom/muttley-hermes-api/pom.xml b/muttley-hermes-server.pom/muttley-hermes-api/pom.xml
new file mode 100644
index 00000000..e93eb5d9
--- /dev/null
+++ b/muttley-hermes-server.pom/muttley-hermes-api/pom.xml
@@ -0,0 +1,45 @@
+
+
+
+ br.com.muttley
+ muttley-hermes-server.pom
+ ${revision}
+
+ 4.0.0
+ jar
+
+ muttley-hermes-api
+
+ muttley-hermes-api
+ Demo project for Spring Boot
+
+
+
+ br.com.muttley
+ muttley-model
+ provided
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
+ br.com.muttley
+ muttley-feign
+
+
+
+ br.com.muttley
+ muttley-security
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
diff --git a/muttley-hermes-server.pom/muttley-hermes-api/src/main/java/br/com/muttley/hermes/api/NotificationClient.java b/muttley-hermes-server.pom/muttley-hermes-api/src/main/java/br/com/muttley/hermes/api/NotificationClient.java
new file mode 100644
index 00000000..a736d087
--- /dev/null
+++ b/muttley-hermes-server.pom/muttley-hermes-api/src/main/java/br/com/muttley/hermes/api/NotificationClient.java
@@ -0,0 +1,33 @@
+package br.com.muttley.hermes.api;
+
+import br.com.muttley.feign.service.config.FeignTimeoutConfig;
+import br.com.muttley.model.hermes.notification.onesignal.Notification;
+import br.com.muttley.security.infra.security.server.FeignClientConfig;
+import org.springframework.cloud.netflix.feign.FeignClient;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8_VALUE;
+import static org.springframework.web.bind.annotation.RequestMethod.POST;
+
+@FeignClient(value = "${muttley.hermes.server.name}", path = "/api/v1/tokens-notification", configuration = {FeignClientConfig.class, FeignTimeoutConfig.class})
+public interface NotificationClient {
+ @RequestMapping(method = POST, consumes = APPLICATION_JSON_UTF8_VALUE)
+ public void sendNotification(@RequestBody final Notification notification);
+
+ @RequestMapping(value = "/{playerId}", method = POST, consumes = APPLICATION_JSON_UTF8_VALUE)
+ public void sendNotification(@PathVariable("playerId") final String playerId, @RequestBody final Notification notification);
+
+ @RequestMapping(value = "/send-by-user/{userId}", method = POST, consumes = APPLICATION_JSON_UTF8_VALUE)
+ public void sendNotificationByUserId(@PathVariable("userId") final String userId, @RequestBody final Notification notification);
+
+ @RequestMapping(value = "/send-by-mobile-user/{userId}", method = POST, consumes = APPLICATION_JSON_UTF8_VALUE)
+ public void sendNotificationMobileByUser(@PathVariable("userId") final String userId, @RequestBody final Notification notification);
+
+ @RequestMapping(value = "/simple-send-by-user/{userId}", method = POST, consumes = APPLICATION_JSON_UTF8_VALUE)
+ public void sendNotificationByUser(@PathVariable("userId") final String userId, @PathVariable(value = "heading", required = false) final String heading, @PathVariable(value = "subtitle", required = false) final String subtitle, @PathVariable(value = "content", required = false) final String content);
+
+ @RequestMapping(value = "/simple-send-by-mobile-user/{userId}", method = POST, consumes = APPLICATION_JSON_UTF8_VALUE)
+ public void sendNotificationMobileByUserId(@PathVariable("userId") final String userId, @PathVariable(value = "heading", required = false) final String heading, @PathVariable(value = "subtitle", required = false) final String subtitle, @PathVariable(value = "content", required = false) final String content);
+}
diff --git a/muttley-hermes-server.pom/muttley-hermes-api/src/main/java/br/com/muttley/hermes/api/UserTokenNotificationClient.java b/muttley-hermes-server.pom/muttley-hermes-api/src/main/java/br/com/muttley/hermes/api/UserTokenNotificationClient.java
new file mode 100644
index 00000000..6e0c08af
--- /dev/null
+++ b/muttley-hermes-server.pom/muttley-hermes-api/src/main/java/br/com/muttley/hermes/api/UserTokenNotificationClient.java
@@ -0,0 +1,20 @@
+package br.com.muttley.hermes.api;
+
+
+import br.com.muttley.feign.service.config.FeignTimeoutConfig;
+import br.com.muttley.model.hermes.notification.TokenId;
+import br.com.muttley.security.infra.security.server.FeignClientConfig;
+import org.springframework.cloud.netflix.feign.FeignClient;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8_VALUE;
+import static org.springframework.web.bind.annotation.RequestMethod.POST;
+
+@FeignClient(value = "${muttley.hermes.server.name}", path = "/api/v1/tokens-notification", configuration = {FeignClientConfig.class, FeignTimeoutConfig.class})
+public interface UserTokenNotificationClient {
+
+ @RequestMapping(method = POST, consumes = APPLICATION_JSON_UTF8_VALUE)
+ public void save(@RequestBody TokenId tokenId);
+
+}
diff --git a/muttley-hermes-server.pom/muttley-hermes-api/src/main/java/br/com/muttley/hermes/api/config/MuttleyHermesAPIConfig.java b/muttley-hermes-server.pom/muttley-hermes-api/src/main/java/br/com/muttley/hermes/api/config/MuttleyHermesAPIConfig.java
new file mode 100644
index 00000000..79b5e5df
--- /dev/null
+++ b/muttley-hermes-server.pom/muttley-hermes-api/src/main/java/br/com/muttley/hermes/api/config/MuttleyHermesAPIConfig.java
@@ -0,0 +1,11 @@
+package br.com.muttley.hermes.api.config;
+
+import org.springframework.cloud.netflix.feign.EnableFeignClients;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@EnableFeignClients(
+ basePackages = "br.com.muttley.hermes.api"
+)
+public class MuttleyHermesAPIConfig {
+}
diff --git a/muttley-hermes-server.pom/muttley-hermes-api/src/main/resources/META-INF/spring.factories b/muttley-hermes-server.pom/muttley-hermes-api/src/main/resources/META-INF/spring.factories
new file mode 100644
index 00000000..4f15eb23
--- /dev/null
+++ b/muttley-hermes-server.pom/muttley-hermes-api/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ br.com.muttley.hermes.api.config.MuttleyHermesAPIConfig
diff --git a/muttley-hermes-server.pom/muttley-hermes-server/.gitignore b/muttley-hermes-server.pom/muttley-hermes-server/.gitignore
new file mode 100644
index 00000000..2af7cefb
--- /dev/null
+++ b/muttley-hermes-server.pom/muttley-hermes-server/.gitignore
@@ -0,0 +1,24 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+nbproject/private/
+build/
+nbbuild/
+dist/
+nbdist/
+.nb-gradle/
\ No newline at end of file
diff --git a/muttley-hermes-server.pom/muttley-hermes-server/.mvn/wrapper/maven-wrapper.jar b/muttley-hermes-server.pom/muttley-hermes-server/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 00000000..9cc84ea9
Binary files /dev/null and b/muttley-hermes-server.pom/muttley-hermes-server/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/muttley-hermes-server.pom/muttley-hermes-server/.mvn/wrapper/maven-wrapper.properties b/muttley-hermes-server.pom/muttley-hermes-server/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 00000000..9dda3b65
--- /dev/null
+++ b/muttley-hermes-server.pom/muttley-hermes-server/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1 @@
+distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.2/apache-maven-3.5.2-bin.zip
diff --git a/muttley-hermes-server.pom/muttley-hermes-server/mvnw b/muttley-hermes-server.pom/muttley-hermes-server/mvnw
new file mode 100755
index 00000000..5bf251c0
--- /dev/null
+++ b/muttley-hermes-server.pom/muttley-hermes-server/mvnw
@@ -0,0 +1,225 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven2 Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ export JAVA_HOME="`/usr/libexec/java_home`"
+ else
+ export JAVA_HOME="/Library/Java/Home"
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Migwn, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+ # TODO classpath?
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`which java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=`cd "$wdir/.."; pwd`
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' < "$1")"
+ fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+echo $MAVEN_PROJECTBASEDIR
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/muttley-hermes-server.pom/muttley-hermes-server/mvnw.cmd b/muttley-hermes-server.pom/muttley-hermes-server/mvnw.cmd
new file mode 100644
index 00000000..019bd74d
--- /dev/null
+++ b/muttley-hermes-server.pom/muttley-hermes-server/mvnw.cmd
@@ -0,0 +1,143 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven2 Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
diff --git a/muttley-hermes-server.pom/muttley-hermes-server/pom.xml b/muttley-hermes-server.pom/muttley-hermes-server/pom.xml
new file mode 100644
index 00000000..9f33c525
--- /dev/null
+++ b/muttley-hermes-server.pom/muttley-hermes-server/pom.xml
@@ -0,0 +1,93 @@
+
+
+
+ br.com.muttley
+ muttley-hermes-server.pom
+ ${revision}
+
+ 4.0.0
+ jar
+
+ muttley-hermes-server
+
+ muttley-hermes-server
+ Demo project for Spring Boot
+
+
+
+
+ br.com.muttley
+ muttley-notification
+
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+
+ br.com.muttley
+ muttley-domain-service
+
+
+
+ br.com.muttley
+ muttley-feign
+
+
+
+ br.com.muttley
+ muttley-jackson
+
+
+
+ br.com.muttley
+ muttley-model
+
+
+
+ br.com.muttley
+ muttley-mongo
+
+
+
+ br.com.muttley
+ muttley-security
+
+
+
+ br.com.muttley
+ muttley-exception
+
+
+
+ br.com.muttley
+ muttley-rest
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-config
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-eureka
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
diff --git a/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/MuttleyHermesServerApplication.java b/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/MuttleyHermesServerApplication.java
new file mode 100644
index 00000000..ab1df981
--- /dev/null
+++ b/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/MuttleyHermesServerApplication.java
@@ -0,0 +1,26 @@
+package br.com.muttley.hermes.server;
+
+/*@SpringBootApplication
+//Packages onde existem entidades
+@EntityScan(basePackages = {"br.com.muttley.model.notification"})
+//Packages onde existem componentes, serviços e configurações
+@ComponentScan(basePackages = {
+ //Injeções internas do projeto
+ "br.com.muttley.domain.service",
+ //Configuração de serviços
+ //"br.com.muttley.security.server",
+ //Configurações de segurança para o gateway
+ //"br.com.muttley.security.zuul.gateway.service",
+ //Configurações de exceptions
+ "br.com.muttley.exception.service",
+ //Configurações de serialização
+ "br.com.muttley.jackson.service"
+})
+//@EnableEurekaClient*/
+public class MuttleyHermesServerApplication {
+
+ /*public static void main(String[] args) {
+ SpringApplication.run(MuttleyHermesServerApplication.class, args);
+ }*/
+
+}
diff --git a/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/config/MuttleyHermesServerConfig.java b/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/config/MuttleyHermesServerConfig.java
new file mode 100644
index 00000000..4e61d12f
--- /dev/null
+++ b/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/config/MuttleyHermesServerConfig.java
@@ -0,0 +1,14 @@
+package br.com.muttley.hermes.server.config;
+
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@ComponentScan(basePackages = {
+ "br.com.muttley.hermes.server",
+ //"br.com.muttley.hermes.server.config.mongo",
+ //"br.com.muttley.hermes.server.service",
+ "br.com.muttley.notification.onesignal.config"
+})
+public class MuttleyHermesServerConfig {
+}
diff --git a/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/config/model/DocumentNameConfig.java b/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/config/model/DocumentNameConfig.java
new file mode 100644
index 00000000..f0d6ae61
--- /dev/null
+++ b/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/config/model/DocumentNameConfig.java
@@ -0,0 +1,76 @@
+package br.com.muttley.hermes.server.config.model;
+
+
+import lombok.Getter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author Joel Rodrigues Moreira on 30/04/20.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+@Configuration(value = "documentNameConfig")
+@Getter
+public class DocumentNameConfig {
+ private final String nameCollectionAdminOwner;
+ private final String nameCollectionOwner;
+ private final String nameCollectionUser;
+ private final String nameCollectionPassword;
+ private final String nameCollectionAdminUserBase;
+ private final String nameCollectionUserBase;
+ private final String nameCollectionAccessPlan;
+ private final String nameCollectionUserPreferences;
+ private final String nameCollectionUserTokensNotification;
+ private final String nameCollectionAdminPassaport;
+ private final String nameCollectionPassaport;
+ private final String nameCollectionWorkTeam;
+
+ private final String nameCollectionXAPIToken;
+ private final String nameCollectionUserDataBinding;
+ private final String nameViewCollectionUser;
+ private final String nameViewCollectionPassaport;
+ private final String nameViewCollectionPassaportRolesUser;
+
+ private final String nameViewCollectionWorkTeam;
+
+ public DocumentNameConfig(
+ @Value("${br.com.muttley.security.server.owner-document:muttley-admin-owners}") final String nameCollectionAdminOwner,
+ @Value("${br.com.muttley.security.server.owner-document:muttley-owners}") final String nameCollectionOwner,
+ @Value("${br.com.muttley.security.server.user-document:muttley-users}") final String nameCollectionUser,
+ @Value("${br.com.muttley.security.server.user-password-document:muttley-users-password}") final String nameCollectionPassword,
+ @Value("${br.com.muttley.security.server.user-base-document:muttley-admin-users-base}") final String nameCollectionAdminUserBase,
+ @Value("${br.com.muttley.security.server.user-base-document:muttley-users-base}") final String nameCollectionUserBase,
+ @Value("${br.com.muttley.security.server.access-plan-document:muttley-access-plans}") final String nameCollectionAccessPlan,
+ @Value("${br.com.muttley.security.server.user-preference-document:muttley-users-preferences}") final String nameCollectionUserPreferences,
+ @Value("${br.com.muttley.security.server.user-tokens-notification-document:muttley-users-tokens-notification}") final String nameCollectionUserTokensNotification,
+ @Value("${br.com.muttley.security.server.admin-passaport-document:muttley-admin-passaports}") final String nameCollectionAdminPassaport,
+ @Value("${br.com.muttley.security.server.passaport-document:muttley-passaports}") final String nameCollectionPassaport,
+ @Value("${br.com.muttley.security.server.work-team-document:muttley-work-teams}") final String nameCollectionWorkTeam,
+ @Value("${br.com.muttley.security.server.x-api-token-document:muttley-x-api-token}") final String nameCollectionXAPIToken,
+ @Value("${br.com.muttley.security.server.user-data-binding:muttley-users-databinding}") final String nameCollectionUserDataBinding,
+ @Value("${br.com.muttley.security.server.user-document-view:view-muttley-users}") final String nameViewCollectionUser,
+ @Value("${br.com.muttley.security.server.passaport-document-view:view-muttley-passaports}") final String nameViewCollectionPassaport,
+ @Value("${br.com.muttley.security.server.passaport-role-document-view:view-muttley-passaports-roles-user}") final String nameViewCollectionPassaportRolesUser,
+ @Value("${br.com.muttley.security.server.work-team-document-view:view-muttley-work-teams}") final String nameViewCollectionWorkTeam
+ ) {
+ this.nameCollectionAdminOwner = nameCollectionAdminOwner;
+ this.nameCollectionOwner = nameCollectionOwner;
+ this.nameCollectionUser = nameCollectionUser;
+ this.nameCollectionPassword = nameCollectionPassword;
+ this.nameCollectionAdminUserBase = nameCollectionAdminUserBase;
+ this.nameCollectionUserBase = nameCollectionUserBase;
+ this.nameCollectionAccessPlan = nameCollectionAccessPlan;
+ this.nameCollectionUserPreferences = nameCollectionUserPreferences;
+ this.nameCollectionUserTokensNotification = nameCollectionUserTokensNotification;
+ this.nameCollectionAdminPassaport = nameCollectionAdminPassaport;
+ this.nameCollectionPassaport = nameCollectionPassaport;
+ this.nameCollectionWorkTeam = nameCollectionWorkTeam;
+ this.nameCollectionXAPIToken = nameCollectionXAPIToken;
+ this.nameCollectionUserDataBinding = nameCollectionUserDataBinding;
+ this.nameViewCollectionUser = nameViewCollectionUser;
+ this.nameViewCollectionPassaport = nameViewCollectionPassaport;
+ this.nameViewCollectionPassaportRolesUser = nameViewCollectionPassaportRolesUser;
+ this.nameViewCollectionWorkTeam = nameViewCollectionWorkTeam;
+ }
+}
diff --git a/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/config/mongo/MongoConfig.java b/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/config/mongo/MongoConfig.java
new file mode 100644
index 00000000..534c5803
--- /dev/null
+++ b/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/config/mongo/MongoConfig.java
@@ -0,0 +1,40 @@
+package br.com.muttley.hermes.server.config.mongo;
+
+import br.com.muttley.model.security.converters.KeyUserDataBindingToStringConverter;
+import br.com.muttley.model.security.converters.StringToKeyUserDataBindingConverter;
+import br.com.muttley.mongo.service.repository.impl.DocumentMongoRepositoryImpl;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
+
+/**
+ * @author Joel Rodrigues Moreira on 30/04/20.
+ * e-mail: joel.databox@gmail.com
+ * @project agrifocus-cloud
+ *
+ * Realiza a configuração do mongo db
+ */
+@Configuration
+@EnableMongoRepositories(basePackages = {"br.com.muttley.hermes.server.repository"}, repositoryBaseClass = DocumentMongoRepositoryImpl.class)
+public class MongoConfig extends br.com.muttley.mongo.service.MongoConfig {
+ private final ApplicationEventPublisher publisher;
+
+ public MongoConfig(@Value("${spring.data.mongodb.database}") final String dataBaseName,
+ @Value("${spring.data.mongodb.host}") final String hostDataBase,
+ @Value("${spring.data.mongodb.port}") final String portDataBase,
+ @Value("${spring.data.mongodb.username}") final String userName,
+ @Value("${spring.data.mongodb.password}") final String password, ApplicationEventPublisher publisher) {
+ super(dataBaseName, hostDataBase, portDataBase, userName, password);
+ this.publisher = publisher;
+ }
+
+ @Override
+ protected Converter[] getConverters() {
+ return new Converter[]{
+ new StringToKeyUserDataBindingConverter(this.publisher),
+ new KeyUserDataBindingToStringConverter()
+ };
+ }
+}
diff --git a/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/controller/NotificationController.java b/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/controller/NotificationController.java
new file mode 100644
index 00000000..e6b94fff
--- /dev/null
+++ b/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/controller/NotificationController.java
@@ -0,0 +1,55 @@
+package br.com.muttley.hermes.server.controller;
+
+import br.com.muttley.hermes.server.service.NotificationService;
+import br.com.muttley.model.hermes.notification.onesignal.Notification;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8_VALUE;
+import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
+import static org.springframework.web.bind.annotation.RequestMethod.POST;
+
+@RestController
+@RequestMapping(value = "/api/v1/notifications", produces = {APPLICATION_JSON_UTF8_VALUE, APPLICATION_JSON_VALUE}, consumes = {APPLICATION_JSON_UTF8_VALUE, APPLICATION_JSON_VALUE})
+public class NotificationController {
+
+ private final NotificationService notificationService;
+
+ @Autowired
+ public NotificationController(final NotificationService notificationService) {
+ this.notificationService = notificationService;
+ }
+
+ @RequestMapping(method = POST)
+ public void sendNotification(@RequestBody final Notification notification) {
+ this.notificationService.sendNotification(notification);
+ }
+
+ @RequestMapping(value = "/{playerId}", method = POST)
+ public void sendNotification(@PathVariable("playerId") final String playerId, @RequestBody final Notification notification) {
+ this.notificationService.sendNotification(notification.addPlayers(playerId));
+ }
+
+ @RequestMapping(value = "/send-by-user/{userId}", method = POST)
+ public void sendNotificationByUserId(@PathVariable("userId") final String userId, @RequestBody final Notification notification) {
+ this.notificationService.sendNotificationByUserId(userId, notification);
+ }
+
+ @RequestMapping(value = "/send-by-mobile-user/{userId}", method = POST)
+ public void sendNotificationMobileByUser(@PathVariable("userId") final String userId, @RequestBody final Notification notification) {
+ this.notificationService.sendNotificationMobileByUserId(userId, notification);
+ }
+
+ @RequestMapping(value = "/simple-send-by-user/{userId}", method = POST)
+ public void sendNotificationByUser(@PathVariable("userId") final String userId, @PathVariable(value = "heading", required = false) final String heading, @PathVariable(value = "subtitle", required = false) final String subtitle, @PathVariable(value = "content", required = false) final String content) {
+ this.notificationService.sendNotificationByUserId(userId, heading, subtitle, content);
+ }
+
+ @RequestMapping(value = "/simple-send-by-mobile-user/{userId}", method = POST)
+ public void sendNotificationMobileByUserId(@PathVariable("userId") final String userId, @PathVariable(value = "heading", required = false) final String heading, @PathVariable(value = "subtitle", required = false) final String subtitle, @PathVariable(value = "content", required = false) final String content) {
+ this.notificationService.sendNotificationMobileByUserId(userId, heading, subtitle, content);
+ }
+}
diff --git a/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/controller/UserTokenNotificationController.java b/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/controller/UserTokenNotificationController.java
new file mode 100644
index 00000000..82379178
--- /dev/null
+++ b/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/controller/UserTokenNotificationController.java
@@ -0,0 +1,35 @@
+package br.com.muttley.hermes.server.controller;
+
+import br.com.muttley.hermes.server.service.UserTokensNotificationService;
+import br.com.muttley.model.hermes.notification.TokenId;
+import br.com.muttley.rest.RestResource;
+import br.com.muttley.security.infra.service.AuthService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8_VALUE;
+import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
+import static org.springframework.web.bind.annotation.RequestMethod.POST;
+
+
+@RestController
+@RequestMapping(value = "/api/v1/tokens-notification", produces = {APPLICATION_JSON_UTF8_VALUE, APPLICATION_JSON_VALUE}, consumes = {APPLICATION_JSON_UTF8_VALUE, APPLICATION_JSON_VALUE})
+public class UserTokenNotificationController implements RestResource {
+ private final AuthService authService;
+ private final UserTokensNotificationService service;
+
+
+ @Autowired
+ public UserTokenNotificationController(final AuthService authService, final UserTokensNotificationService service) {
+ this.authService = authService;
+ this.service = service;
+ }
+
+ @RequestMapping(method = POST, produces = {APPLICATION_JSON_UTF8_VALUE, APPLICATION_JSON_VALUE}, consumes = {APPLICATION_JSON_UTF8_VALUE, APPLICATION_JSON_VALUE})
+ public void save(@RequestBody TokenId tokenId) {
+ this.service.addTokenNotification(this.authService.getCurrentUser(), tokenId);
+ }
+
+}
diff --git a/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/liteners/NotificationEventListener.java b/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/liteners/NotificationEventListener.java
new file mode 100644
index 00000000..b740053b
--- /dev/null
+++ b/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/liteners/NotificationEventListener.java
@@ -0,0 +1,22 @@
+package br.com.muttley.hermes.server.liteners;
+
+import br.com.muttley.model.hermes.notification.onesignal.events.NotificationEvent;
+import br.com.muttley.notification.onesignal.service.OneSignalNotificationService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationListener;
+import org.springframework.stereotype.Component;
+
+@Component
+public class NotificationEventListener implements ApplicationListener {
+ private final OneSignalNotificationService oneSignalService;
+
+ @Autowired
+ public NotificationEventListener(final OneSignalNotificationService oneSignalService) {
+ this.oneSignalService = oneSignalService;
+ }
+
+ @Override
+ public void onApplicationEvent(final NotificationEvent notificationEvent) {
+ this.oneSignalService.sendNotification(notificationEvent.getSource());
+ }
+}
diff --git a/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/repository/UserTokensNotificationRepository.java b/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/repository/UserTokensNotificationRepository.java
new file mode 100644
index 00000000..6db5f360
--- /dev/null
+++ b/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/repository/UserTokensNotificationRepository.java
@@ -0,0 +1,24 @@
+package br.com.muttley.hermes.server.repository;
+
+import br.com.muttley.model.hermes.notification.UserTokensNotification;
+import br.com.muttley.model.security.User;
+import br.com.muttley.model.security.UserView;
+import br.com.muttley.mongo.service.repository.DocumentMongoRepository;
+import org.springframework.data.mongodb.repository.Query;
+import org.springframework.stereotype.Repository;
+
+/**
+ * @author Joel Rodrigues Moreira on 30/04/20.
+ * e-mail: joel.databox@gmail.com
+ * @project agrifocus-cloud
+ */
+@Repository
+public interface UserTokensNotificationRepository extends DocumentMongoRepository {
+ UserTokensNotification findByUser(final User user);
+
+ @Query("{'user': {'$ref' : ?#{@documentNameConfig.getNameCollectionUser()}, '$id': ?#{[0].getId()}}}")
+ UserTokensNotification findByUser(final UserView user);
+
+ @Query("{'user': {'$ref' : ?#{@documentNameConfig.getNameCollectionUser()}, '$id': ?0}}")
+ UserTokensNotification findByUser(final String userId);
+}
diff --git a/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/service/NotificationService.java b/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/service/NotificationService.java
new file mode 100644
index 00000000..9255ebf4
--- /dev/null
+++ b/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/service/NotificationService.java
@@ -0,0 +1,35 @@
+package br.com.muttley.hermes.server.service;
+
+import br.com.muttley.model.hermes.notification.onesignal.Content;
+import br.com.muttley.model.hermes.notification.onesignal.Notification;
+import br.com.muttley.model.security.UserView;
+
+public interface NotificationService {
+ void sendNotification(final Notification notification);
+
+ void sendNotification(final String playerId, final Notification notification);
+
+ void sendNotification(final UserView user, final Notification notification);
+
+ void sendNotificationByUserId(final String userId, final Notification notification);
+
+ void sendNotificationMobile(final UserView user, final Notification notification);
+
+ void sendNotificationMobileByUserId(final String userId, final Notification notification);
+
+ void sendNotification(final UserView user, final Content headings, final Content subtitles, final Content contents);
+
+ void sendNotificationByUserId(final String userId, final Content headings, final Content subtitles, final Content contents);
+
+ void sendNotificationMobile(final UserView user, final Content headings, final Content subtitles, final Content contents);
+
+ void sendNotificationMobileByUserId(final String userId, final Content headings, final Content subtitles, final Content contents);
+
+ void sendNotification(final UserView user, final String heading, final String subtitle, final String content);
+
+ void sendNotificationByUserId(final String userId, final String heading, final String subtitle, final String content);
+
+ void sendNotificationMobile(final UserView user, final String heading, final String subtitle, final String content);
+
+ void sendNotificationMobileByUserId(final String userId, final String heading, final String subtitle, final String content);
+}
diff --git a/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/service/UserTokensNotificationService.java b/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/service/UserTokensNotificationService.java
new file mode 100644
index 00000000..c94b291b
--- /dev/null
+++ b/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/service/UserTokensNotificationService.java
@@ -0,0 +1,19 @@
+package br.com.muttley.hermes.server.service;
+
+import br.com.muttley.domain.service.Service;
+import br.com.muttley.exception.throwables.MuttleyNotFoundException;
+import br.com.muttley.model.hermes.notification.TokenId;
+import br.com.muttley.model.hermes.notification.UserTokensNotification;
+import br.com.muttley.model.security.User;
+import br.com.muttley.model.security.UserView;
+
+
+public interface UserTokensNotificationService extends Service {
+ UserTokensNotification findByUser(final User user) throws MuttleyNotFoundException;
+
+ UserTokensNotification findByUser(final UserView user) throws MuttleyNotFoundException;
+
+ UserTokensNotification findByUser(final String userId) throws MuttleyNotFoundException;
+
+ void addTokenNotification(final User user, final TokenId tokenId);
+}
diff --git a/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/service/impl/NotificationServiceImpl.java b/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/service/impl/NotificationServiceImpl.java
new file mode 100644
index 00000000..999aa3ce
--- /dev/null
+++ b/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/service/impl/NotificationServiceImpl.java
@@ -0,0 +1,109 @@
+package br.com.muttley.hermes.server.service.impl;
+
+import br.com.muttley.exception.throwables.MuttleyNotFoundException;
+import br.com.muttley.hermes.server.service.NotificationService;
+import br.com.muttley.hermes.server.service.UserTokensNotificationService;
+import br.com.muttley.model.hermes.notification.onesignal.Content;
+import br.com.muttley.model.hermes.notification.onesignal.Notification;
+import br.com.muttley.model.security.UserView;
+import br.com.muttley.notification.onesignal.service.OneSignalNotificationService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import static br.com.muttley.model.hermes.notification.onesignal.MuttleyLanguage.Any;
+
+@Service
+public class NotificationServiceImpl implements NotificationService {
+ private final OneSignalNotificationService oneSignalNotificationServiceClient;
+ private final UserTokensNotificationService userTokensNotificationService;
+ private final Logger logger = LoggerFactory.getLogger(NotificationServiceImpl.class);
+
+
+ @Autowired
+ public NotificationServiceImpl(final OneSignalNotificationService oneSignalNotificationServiceClient, final UserTokensNotificationService userTokensNotificationService) {
+ this.oneSignalNotificationServiceClient = oneSignalNotificationServiceClient;
+ this.userTokensNotificationService = userTokensNotificationService;
+ }
+
+ @Override
+ public void sendNotification(final Notification notification) {
+ try {
+ this.oneSignalNotificationServiceClient.sendNotification(notification);
+ } catch (Throwable ex) {
+ logger.error("Erro ao enviar notificação para o serviço do OneSignal", ex);
+ }
+ }
+
+ @Override
+ public void sendNotification(final String playerId, final Notification notification) {
+ this.sendNotification(notification.addPlayers(playerId));
+ }
+
+ @Override
+ public void sendNotification(final UserView user, final Notification notification) {
+ try {
+ this.sendNotification(notification.addPlayers(this.userTokensNotificationService.findByUser(user)));
+ } catch (final MuttleyNotFoundException ex) {
+ }
+ }
+
+ @Override
+ public void sendNotificationByUserId(final String userId, final Notification notification) {
+ this.sendNotification(new UserView().setId(userId), notification);
+ }
+
+ @Override
+ public void sendNotificationMobile(final UserView user, final Notification notification) {
+ try {
+ this.sendNotification(notification.addPlayers(this.userTokensNotificationService.findByUser(user).getTokensMobile()));
+ } catch (final MuttleyNotFoundException ex) {
+ }
+ }
+
+ @Override
+ public void sendNotificationMobileByUserId(final String userId, final Notification notification) {
+ this.sendNotificationMobile(new UserView().setId(userId), notification);
+ }
+
+ @Override
+ public void sendNotification(final UserView user, final Content headings, final Content subtitles, final Content contents) {
+ this.sendNotification(user, new Notification().addHeadings(headings).addSubtitles(subtitles).addContent(contents));
+ }
+
+ @Override
+ public void sendNotificationByUserId(final String userId, final Content headings, final Content subtitles, final Content contents) {
+ this.sendNotification(new UserView().setId(userId), headings, subtitles, contents);
+ }
+
+ @Override
+ public void sendNotificationMobile(final UserView user, final Content headings, final Content subtitles, final Content contents) {
+ this.sendNotificationMobile(user, new Notification().addHeadings(headings).addSubtitles(subtitles).addContent(contents));
+ }
+
+ @Override
+ public void sendNotificationMobileByUserId(final String userId, final Content headings, final Content subtitles, final Content contents) {
+ this.sendNotificationMobile(new UserView().setId(userId), headings, subtitles, contents);
+ }
+
+ @Override
+ public void sendNotification(final UserView user, final String heading, final String subtitle, final String content) {
+ this.sendNotification(user, new Content(Any, heading), new Content(Any, subtitle), new Content(Any, content));
+ }
+
+ @Override
+ public void sendNotificationByUserId(final String userId, final String heading, final String subtitle, final String content) {
+ this.sendNotification(new UserView().setId(userId), heading, subtitle, content);
+ }
+
+ @Override
+ public void sendNotificationMobile(final UserView user, final String heading, final String subtitle, final String content) {
+ this.sendNotificationMobile(user, new Content(Any, heading), new Content(Any, subtitle), new Content(Any, content));
+ }
+
+ @Override
+ public void sendNotificationMobileByUserId(final String userId, final String heading, final String subtitle, final String content) {
+ this.sendNotificationMobile(new UserView().setId(userId), heading, subtitle, content);
+ }
+}
diff --git a/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/service/impl/UserTokensNotificationServiceImpl.java b/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/service/impl/UserTokensNotificationServiceImpl.java
new file mode 100644
index 00000000..a41fc457
--- /dev/null
+++ b/muttley-hermes-server.pom/muttley-hermes-server/src/main/java/br/com/muttley/hermes/server/service/impl/UserTokensNotificationServiceImpl.java
@@ -0,0 +1,124 @@
+package br.com.muttley.hermes.server.service.impl;
+
+import br.com.muttley.domain.service.impl.ServiceImpl;
+import br.com.muttley.exception.throwables.MuttleyNotFoundException;
+import br.com.muttley.headers.components.MuttleyUserAgent;
+import br.com.muttley.hermes.server.repository.UserTokensNotificationRepository;
+import br.com.muttley.hermes.server.service.UserTokensNotificationService;
+import br.com.muttley.model.hermes.notification.TokenId;
+import br.com.muttley.model.hermes.notification.UserTokensNotification;
+import br.com.muttley.model.security.User;
+import br.com.muttley.model.security.UserView;
+import br.com.muttley.redis.service.RedisService;
+import com.mongodb.BasicDBObject;
+import org.bson.types.ObjectId;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.data.mongodb.core.query.Update;
+import org.springframework.stereotype.Service;
+
+import static org.springframework.data.mongodb.core.query.Criteria.where;
+
+@Service
+public class UserTokensNotificationServiceImpl extends ServiceImpl implements UserTokensNotificationService {
+ private static final String KEY_REDIS = "muttley-notification-cache.";
+ private final UserTokensNotificationRepository repository;
+ private final RedisService redisService;
+ @Autowired
+ private MuttleyUserAgent userAgent;
+ private final long cachetimeout;
+
+ @Autowired
+ public UserTokensNotificationServiceImpl(
+ final UserTokensNotificationRepository repository,
+ final MongoTemplate mongoTemplate,
+ final RedisService redisService,
+ //tempo de validade do token 60 * 60 * 24 * 10 = dias
+ @Value("${muttley.hermes.notification.cachetimeout:864000}") final long cachetimeout) {
+ super(repository, mongoTemplate, UserTokensNotification.class);
+ this.repository = repository;
+ this.redisService = redisService;
+ this.cachetimeout = cachetimeout;
+ }
+
+ @Override
+ public UserTokensNotification findByUser(final User user) throws MuttleyNotFoundException {
+ return this.findByUser(user.getId());
+ }
+
+ @Override
+ public UserTokensNotification findByUser(final UserView user) throws MuttleyNotFoundException {
+ return this.findByUser(user.getId());
+ }
+
+ @Override
+ public UserTokensNotification findByUser(final String userId) throws MuttleyNotFoundException {
+ //verificando se já tem no cache
+ if (this.redisService.hasKey(this.generateTokenRedis(userId))) {
+ return (UserTokensNotification) this.redisService.get(this.generateTokenRedis(userId));
+ }
+ final UserTokensNotification token = this.repository.findByUser(userId);
+ if (token == null) {
+ throw new MuttleyNotFoundException(UserTokensNotification.class, "user", "Nenhum registro encontrado");
+ }
+ //se chegou até aqui é sinal que o token ainda não está no cache
+ //adicionando o token no cache
+ this.redisService.set(this.generateTokenRedis(userId), token, cachetimeout);
+ return token;
+ }
+
+ @Override
+ public void addTokenNotification(final User user, final TokenId tokenId) {
+ tokenId.setMobile(this.userAgent.isMobile());
+ //se ainda não existir uma coleção para o usuário, devemos criar uma
+ if (!this.repository.exists("user.$id", new ObjectId(user.getId()))) {
+ //criando coleção do usuário com o primeiro token
+ this.save(user, new UserTokensNotification().setUser(user).add(tokenId));
+ //se o token é da mobilidade, devemos garantira que outros usuário não terá o mesmo token
+ } else if (tokenId.isMobile()) {
+ //garantindo que outros usuários não terão esse token
+ this.removeTokenIdFromAnotherUsers(user, tokenId);
+ //salvando o token
+ this.saveTokenId(user, tokenId);
+ //se não for um token da mobilidade podemos salvar o token para diversos usuários
+ } else {
+ //salvando o token
+ this.saveTokenId(user, tokenId);
+ }
+ this.redisService.delete(this.generateTokenRedis(user.getId()));
+ }
+
+ private void saveTokenId(final User user, final TokenId tokenId) {
+ this.mongoTemplate.updateFirst(
+ new Query(where("user.$id").is(new ObjectId(user.getId()))),
+ new Update()
+ .addToSet("tokens", tokenId),
+ UserTokensNotification.class
+ );
+ }
+
+ /**
+ * Remove o token de outros usuários
+ * Isso se faz necessário pois o token está relacionado ao aparelho e por conta disso
+ * o usuário pode seder o aparelho para outros usuários.
+ */
+ private void removeTokenIdFromAnotherUsers(final User user, final TokenId tokenId) {
+ this.mongoTemplate.updateFirst(
+ new Query(
+ where("user.$id").ne(new ObjectId(user.getId()))
+ .and("tokens").elemMatch(where("token").is(tokenId.getToken()).and("origin").is(tokenId.getOrigin()).and("mobile").is(true))
+ ),
+ new Update()
+ .pull("tokens",
+ new BasicDBObject("origin", tokenId.getOrigin()).append("token", tokenId.getToken())
+ ),
+ UserTokensNotification.class
+ );
+ }
+
+ private String generateTokenRedis(final String userId) {
+ return KEY_REDIS + userId;
+ }
+}
diff --git a/muttley-hermes-server.pom/muttley-hermes-server/src/main/resources/META-INF/spring.factories b/muttley-hermes-server.pom/muttley-hermes-server/src/main/resources/META-INF/spring.factories
new file mode 100644
index 00000000..90340950
--- /dev/null
+++ b/muttley-hermes-server.pom/muttley-hermes-server/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ br.com.muttley.hermes.server.config.MuttleyHermesServerConfig
diff --git a/muttley-hermes-server.pom/mvnw b/muttley-hermes-server.pom/mvnw
new file mode 100755
index 00000000..8b9da3b8
--- /dev/null
+++ b/muttley-hermes-server.pom/mvnw
@@ -0,0 +1,286 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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
+#
+# https://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.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven2 Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ export JAVA_HOME="`/usr/libexec/java_home`"
+ else
+ export JAVA_HOME="/Library/Java/Home"
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+ # TODO classpath?
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`which java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=`cd "$wdir/.."; pwd`
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' < "$1")"
+ fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found .mvn/wrapper/maven-wrapper.jar"
+ fi
+else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+ fi
+ jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
+ while IFS="=" read key value; do
+ case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+ esac
+ done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Downloading from: $jarUrl"
+ fi
+ wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+
+ if command -v wget > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found wget ... using wget"
+ fi
+ wget "$jarUrl" -O "$wrapperJarPath"
+ elif command -v curl > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found curl ... using curl"
+ fi
+ curl -o "$wrapperJarPath" "$jarUrl"
+ else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Falling back to using Java to download"
+ fi
+ javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ if [ -e "$javaClass" ]; then
+ if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Compiling MavenWrapperDownloader.java ..."
+ fi
+ # Compiling the Java class
+ ("$JAVA_HOME/bin/javac" "$javaClass")
+ fi
+ if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ # Running the downloader
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Running MavenWrapperDownloader.java ..."
+ fi
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+ echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/muttley-hermes-server.pom/mvnw.cmd b/muttley-hermes-server.pom/mvnw.cmd
new file mode 100644
index 00000000..fef5a8f7
--- /dev/null
+++ b/muttley-hermes-server.pom/mvnw.cmd
@@ -0,0 +1,161 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven2 Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
+FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO (
+ IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ echo Found %WRAPPER_JAR%
+) else (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %DOWNLOAD_URL%
+ powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"
+ echo Finished downloading %WRAPPER_JAR%
+)
+@REM End of extension
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
diff --git a/muttley-hermes-server.pom/pom.xml b/muttley-hermes-server.pom/pom.xml
new file mode 100644
index 00000000..93726f62
--- /dev/null
+++ b/muttley-hermes-server.pom/pom.xml
@@ -0,0 +1,21 @@
+
+
+
+ br.com.muttley
+ muttley-cloud
+ ${revision}
+
+ 4.0.0
+ pom
+
+ muttley-hermes-server.pom
+
+ muttley-hermes-server.pom
+ Demo project for Spring Boot
+
+
+ muttley-hermes-server
+ muttley-hermes-api
+
+
diff --git a/muttley-jackson/pom.xml b/muttley-jackson/pom.xml
index 4e431a97..ded35212 100644
--- a/muttley-jackson/pom.xml
+++ b/muttley-jackson/pom.xml
@@ -1,10 +1,10 @@
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
br.com.muttley
muttley-cloud
- 0.0.2-SNAPSHOT
+ ${revision}
4.0.0
jar
@@ -21,6 +21,12 @@
provided
+
+ br.com.muttley
+ muttley-exception
+ provided
+
+
org.springframework.boot
spring-boot-starter-data-mongodb
diff --git a/muttley-jackson/src/main/java/br/com/muttley/jackson/service/JacksonConfig.java b/muttley-jackson/src/main/java/br/com/muttley/jackson/service/JacksonConfig.java
index 784e2d52..80589071 100644
--- a/muttley-jackson/src/main/java/br/com/muttley/jackson/service/JacksonConfig.java
+++ b/muttley-jackson/src/main/java/br/com/muttley/jackson/service/JacksonConfig.java
@@ -2,8 +2,13 @@
import br.com.muttley.jackson.service.infra.MuttleyJacksonDeserialize;
import br.com.muttley.jackson.service.infra.MuttleyJacksonSerialize;
+import br.com.muttley.jackson.service.infra.deserializer.BigDecimalDeserializer;
+import br.com.muttley.jackson.service.infra.deserializer.LocalDateDeserializer;
import br.com.muttley.jackson.service.infra.deserializer.ObjectIdDeserializer;
+import br.com.muttley.jackson.service.infra.deserializer.ZonedDateTimeDeserializer;
+import br.com.muttley.jackson.service.infra.serializer.LocalDateSerializer;
import br.com.muttley.jackson.service.infra.serializer.ObjectIdSerializer;
+import br.com.muttley.jackson.service.infra.serializer.ZonedDateTimeSerializer;
import br.com.muttley.model.jackson.DefaultDateFormatConfig;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
@@ -14,6 +19,10 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.ZonedDateTime;
+
/**
* @author Joel Rodrigues Moreira on 13/03/18.
@@ -36,8 +45,21 @@ public Jackson2ObjectMapperBuilderCustomizer addCustomBigDecimalDeserialization(
return new Jackson2ObjectMapperBuilderCustomizer() {
@Override
public void customize(Jackson2ObjectMapperBuilder mapperBuilder) {
+
+ mapperBuilder.deserializerByType(BigDecimal.class, new BigDecimalDeserializer());
+
mapperBuilder.deserializerByType(ObjectId.class, new ObjectIdDeserializer());
mapperBuilder.serializerByType(ObjectId.class, new ObjectIdSerializer());
+
+ mapperBuilder.deserializerByType(ZonedDateTime.class, new ZonedDateTimeDeserializer(datePattern));
+ mapperBuilder.serializerByType(ZonedDateTime.class, new ZonedDateTimeSerializer(datePattern));
+
+ mapperBuilder.deserializerByType(LocalDate.class, new LocalDateDeserializer());
+ mapperBuilder.serializerByType(LocalDate.class, new LocalDateSerializer());
+
+ //mapperBuilder.deserializers(new OwnerDataDeserializer());
+ //mapperBuilder.deserializerByType((Class>) (Class) List.class, new OwnerDataDeserializer());
+
//se não existe um formatador de data padrão, devemos adicionar o nosso
if (dateFormat == null) {
mapperBuilder.dateFormat(new DefaultDateFormatConfig(datePattern));
diff --git a/muttley-jackson/src/main/java/br/com/muttley/jackson/service/infra/deserializer/BigDecimalDeserializer.java b/muttley-jackson/src/main/java/br/com/muttley/jackson/service/infra/deserializer/BigDecimalDeserializer.java
new file mode 100644
index 00000000..9998d998
--- /dev/null
+++ b/muttley-jackson/src/main/java/br/com/muttley/jackson/service/infra/deserializer/BigDecimalDeserializer.java
@@ -0,0 +1,29 @@
+package br.com.muttley.jackson.service.infra.deserializer;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.ObjectCodec;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonNode;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+
+import static br.com.muttley.utils.BigDecimalUtils.setDefaultScale;
+
+/**
+ * @author Joel Rodrigues Moreira on 12/03/18.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public class BigDecimalDeserializer extends JsonDeserializer {
+ @Override
+ public BigDecimal deserialize(final JsonParser parser, final DeserializationContext context) throws IOException, JsonProcessingException {
+ final ObjectCodec oc = parser.getCodec();
+ final JsonNode node = oc.readTree(parser);
+
+ return setDefaultScale(new BigDecimal(node.asText()));
+
+ }
+}
diff --git a/muttley-jackson/src/main/java/br/com/muttley/jackson/service/infra/deserializer/LocalDateDeserializer.java b/muttley-jackson/src/main/java/br/com/muttley/jackson/service/infra/deserializer/LocalDateDeserializer.java
new file mode 100644
index 00000000..ccfe1786
--- /dev/null
+++ b/muttley-jackson/src/main/java/br/com/muttley/jackson/service/infra/deserializer/LocalDateDeserializer.java
@@ -0,0 +1,53 @@
+package br.com.muttley.jackson.service.infra.deserializer;
+
+import br.com.muttley.exception.throwables.MuttleyBadRequestException;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.ObjectCodec;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonNode;
+
+import java.io.IOException;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeParseException;
+import java.util.regex.Pattern;
+
+import static br.com.muttley.utils.DateUtils.DEFAULT_ISO_LOCAL_DATE;
+import static br.com.muttley.utils.DateUtils.DEFAULT_ISO_ZONED_DATE_TIME;
+import static java.util.Arrays.asList;
+
+/**
+ * @author Joel Rodrigues Moreira on 17/06/2021.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public class LocalDateDeserializer extends JsonDeserializer {
+ private static final Pattern DATE_PATTERN = Pattern.compile("^\\d{4}-\\d{2}-\\d{2}$");
+
+ @Override
+ public LocalDate deserialize(final JsonParser parser, final DeserializationContext context) throws IOException, JsonProcessingException {
+ final ObjectCodec oc = parser.getCodec();
+ final JsonNode node = oc.readTree(parser);
+ if (node.isNull()) {
+ return null;
+ }
+ final String value = node.asText();
+ try {
+ if (this.isValideDate(value)) {
+ return LocalDate.parse(value, DEFAULT_ISO_LOCAL_DATE);
+ }
+ return LocalDate.parse(value, DEFAULT_ISO_ZONED_DATE_TIME);
+
+ } catch (DateTimeParseException e) {
+ throw new MuttleyBadRequestException(null, parser.getCurrentName(), "Informe uma data válida ")
+ .addDetails("informado", value)
+ .addDetails("exemplos", asList(LocalDateTime.now().format(DEFAULT_ISO_ZONED_DATE_TIME), LocalDate.now().format(DEFAULT_ISO_LOCAL_DATE)));
+ }
+ }
+
+ private boolean isValideDate(final String value) {
+ return DATE_PATTERN.matcher(value).matches();
+ }
+}
diff --git a/muttley-jackson/src/main/java/br/com/muttley/jackson/service/infra/deserializer/OwnerDataDeserializer.java b/muttley-jackson/src/main/java/br/com/muttley/jackson/service/infra/deserializer/OwnerDataDeserializer.java
new file mode 100644
index 00000000..1d21fd9c
--- /dev/null
+++ b/muttley-jackson/src/main/java/br/com/muttley/jackson/service/infra/deserializer/OwnerDataDeserializer.java
@@ -0,0 +1,39 @@
+package br.com.muttley.jackson.service.infra.deserializer;
+
+/*import br.com.muttley.model.security.OwnerData;
+import br.com.muttley.model.security.OwnerDataImpl;*/
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.ObjectCodec;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonNode;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Joel Rodrigues Moreira 12/01/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public class OwnerDataDeserializer extends JsonDeserializer*OwnerData*/ Object> {
+ @Override
+ public List*OwnerData*/ Object> deserialize(final JsonParser parser, final DeserializationContext context) throws IOException, JsonProcessingException {
+ final ObjectCodec oc = parser.getCodec();
+ final JsonNode node = oc.readTree(parser);
+ final List*OwnerData*/ Object> owners = new ArrayList<>();
+ node.forEach(it -> {
+ try {
+ owners.add(it.traverse(parser.getCodec()).readValueAs(new TypeReference*OwnerData*/ Object>() {
+ }));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ return owners;
+ }
+}
diff --git a/muttley-jackson/src/main/java/br/com/muttley/jackson/service/infra/deserializer/ZonedDateTimeDeserializer.java b/muttley-jackson/src/main/java/br/com/muttley/jackson/service/infra/deserializer/ZonedDateTimeDeserializer.java
new file mode 100644
index 00000000..32c96cf5
--- /dev/null
+++ b/muttley-jackson/src/main/java/br/com/muttley/jackson/service/infra/deserializer/ZonedDateTimeDeserializer.java
@@ -0,0 +1,42 @@
+package br.com.muttley.jackson.service.infra.deserializer;
+
+import br.com.muttley.utils.DateUtils;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonNode;
+import org.springframework.beans.factory.annotation.Value;
+
+import java.io.IOException;
+import java.sql.SQLOutput;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.Date;
+
+/**
+ * @author Joel Rodrigues Moreira on 12/03/18.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public class ZonedDateTimeDeserializer extends JsonDeserializer {
+
+ private final String pattern;
+
+ public ZonedDateTimeDeserializer(@Value("${br.com.muttley.jackson.date-pattern:yyyy-MM-dd'T'HH:mm:ss.SSSZ}") final String pattern) {
+ this.pattern = pattern;
+ }
+
+ @Override
+ public ZonedDateTime deserialize(final JsonParser parser, final DeserializationContext context) throws IOException, JsonProcessingException {
+ final JsonNode node = parser.getCodec().readTree(parser);
+ try {
+ return ZonedDateTime.parse(node.asText(), DateTimeFormatter.ofPattern(pattern));
+ } catch (DateTimeParseException exception) {
+ System.out.println("COBRA O DERCI PRA ARRUMAR SAPORRAKKKKK");
+ System.out.println("A DATA FORNECIDA ESTA COM PADRÕES FORA DOS CONFORMES");
+ return DateUtils.toZonedDateTime(new Date(Long.valueOf(node.asText())));
+ }
+ }
+}
diff --git a/muttley-jackson/src/main/java/br/com/muttley/jackson/service/infra/serializer/LocalDateSerializer.java b/muttley-jackson/src/main/java/br/com/muttley/jackson/service/infra/serializer/LocalDateSerializer.java
new file mode 100644
index 00000000..7f34d8d9
--- /dev/null
+++ b/muttley-jackson/src/main/java/br/com/muttley/jackson/service/infra/serializer/LocalDateSerializer.java
@@ -0,0 +1,27 @@
+package br.com.muttley.jackson.service.infra.serializer;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+import java.io.IOException;
+import java.time.LocalDate;
+
+import static br.com.muttley.utils.DateUtils.DEFAULT_ISO_LOCAL_DATE;
+
+/**
+ * @author Joel Rodrigues Moreira on 17/06/2021.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public class LocalDateSerializer extends JsonSerializer {
+ @Override
+ public void serialize(final LocalDate date, final JsonGenerator gen, final SerializerProvider serializers) throws IOException, JsonProcessingException {
+ if (date == null) {
+ gen.writeNull();
+ }
+
+ gen.writeString(date.format(DEFAULT_ISO_LOCAL_DATE));
+ }
+}
diff --git a/muttley-jackson/src/main/java/br/com/muttley/jackson/service/infra/serializer/ZonedDateTimeSerializer.java b/muttley-jackson/src/main/java/br/com/muttley/jackson/service/infra/serializer/ZonedDateTimeSerializer.java
new file mode 100644
index 00000000..4a336177
--- /dev/null
+++ b/muttley-jackson/src/main/java/br/com/muttley/jackson/service/infra/serializer/ZonedDateTimeSerializer.java
@@ -0,0 +1,30 @@
+package br.com.muttley.jackson.service.infra.serializer;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import org.springframework.beans.factory.annotation.Value;
+
+import java.io.IOException;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * @author Joel Rodrigues Moreira on 12/03/18.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public class ZonedDateTimeSerializer extends JsonSerializer {
+
+ private final String pattern;
+
+ public ZonedDateTimeSerializer(@Value("${br.com.muttley.jackson.date-pattern:yyyy-MM-dd'T'HH:mm:ss.SSSZ}") final String pattern) {
+ this.pattern = pattern;
+ }
+
+ @Override
+ public void serialize(ZonedDateTime dateTime, JsonGenerator jsonGenerator, SerializerProvider serializers) throws IOException, JsonProcessingException {
+ jsonGenerator.writeString(dateTime.format(DateTimeFormatter.ofPattern(pattern)));
+ }
+}
diff --git a/muttley-local-cache/.gitignore b/muttley-local-cache/.gitignore
new file mode 100644
index 00000000..a2a3040a
--- /dev/null
+++ b/muttley-local-cache/.gitignore
@@ -0,0 +1,31 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**
+!**/src/test/**
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+
+### VS Code ###
+.vscode/
diff --git a/muttley-local-cache/.mvn/wrapper/MavenWrapperDownloader.java b/muttley-local-cache/.mvn/wrapper/MavenWrapperDownloader.java
new file mode 100644
index 00000000..a45eb6ba
--- /dev/null
+++ b/muttley-local-cache/.mvn/wrapper/MavenWrapperDownloader.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2007-present the original author or authors.
+ *
+ * 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
+ *
+ * https://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.
+ */
+
+import java.net.*;
+import java.io.*;
+import java.nio.channels.*;
+import java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+ private static final String WRAPPER_VERSION = "0.5.6";
+ /**
+ * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+ */
+ private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
+
+ /**
+ * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
+ * use instead of the default one.
+ */
+ private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+ ".mvn/wrapper/maven-wrapper.properties";
+
+ /**
+ * Path where the maven-wrapper.jar will be saved to.
+ */
+ private static final String MAVEN_WRAPPER_JAR_PATH =
+ ".mvn/wrapper/maven-wrapper.jar";
+
+ /**
+ * Name of the property which should be used to override the default download url for the wrapper.
+ */
+ private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+ public static void main(String args[]) {
+ System.out.println("- Downloader started");
+ File baseDirectory = new File(args[0]);
+ System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
+
+ // If the maven-wrapper.properties exists, read it and check if it contains a custom
+ // wrapperUrl parameter.
+ File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
+ String url = DEFAULT_DOWNLOAD_URL;
+ if (mavenWrapperPropertyFile.exists()) {
+ FileInputStream mavenWrapperPropertyFileInputStream = null;
+ try {
+ mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
+ Properties mavenWrapperProperties = new Properties();
+ mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
+ url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+ } catch (IOException e) {
+ System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
+ } finally {
+ try {
+ if (mavenWrapperPropertyFileInputStream != null) {
+ mavenWrapperPropertyFileInputStream.close();
+ }
+ } catch (IOException e) {
+ // Ignore ...
+ }
+ }
+ }
+ System.out.println("- Downloading from: " + url);
+
+ File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
+ if (!outputFile.getParentFile().exists()) {
+ if (!outputFile.getParentFile().mkdirs()) {
+ System.out.println(
+ "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
+ }
+ }
+ System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
+ try {
+ downloadFileFromURL(url, outputFile);
+ System.out.println("Done");
+ System.exit(0);
+ } catch (Throwable e) {
+ System.out.println("- Error downloading");
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+ if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
+ String username = System.getenv("MVNW_USERNAME");
+ char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
+ Authenticator.setDefault(new Authenticator() {
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(username, password);
+ }
+ });
+ }
+ URL website = new URL(urlString);
+ ReadableByteChannel rbc;
+ rbc = Channels.newChannel(website.openStream());
+ FileOutputStream fos = new FileOutputStream(destination);
+ fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+ fos.close();
+ rbc.close();
+ }
+
+}
diff --git a/muttley-local-cache/.mvn/wrapper/maven-wrapper.jar b/muttley-local-cache/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 00000000..2cc7d4a5
Binary files /dev/null and b/muttley-local-cache/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/muttley-local-cache/.mvn/wrapper/maven-wrapper.properties b/muttley-local-cache/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 00000000..642d572c
--- /dev/null
+++ b/muttley-local-cache/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,2 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
diff --git a/muttley-local-cache/mvnw b/muttley-local-cache/mvnw
new file mode 100755
index 00000000..a16b5431
--- /dev/null
+++ b/muttley-local-cache/mvnw
@@ -0,0 +1,310 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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
+#
+# https://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.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ export JAVA_HOME="`/usr/libexec/java_home`"
+ else
+ export JAVA_HOME="/Library/Java/Home"
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="`which javac`"
+ if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=`which readlink`
+ if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+ if $darwin ; then
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+ else
+ javaExecutable="`readlink -f \"$javaExecutable\"`"
+ fi
+ javaHome="`dirname \"$javaExecutable\"`"
+ javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="`which java`"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=`cd "$wdir/.."; pwd`
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' < "$1")"
+ fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found .mvn/wrapper/maven-wrapper.jar"
+ fi
+else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+ fi
+ if [ -n "$MVNW_REPOURL" ]; then
+ jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ else
+ jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ fi
+ while IFS="=" read key value; do
+ case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+ esac
+ done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Downloading from: $jarUrl"
+ fi
+ wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+ if $cygwin; then
+ wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+ fi
+
+ if command -v wget > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found wget ... using wget"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget "$jarUrl" -O "$wrapperJarPath"
+ else
+ wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found curl ... using curl"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl -o "$wrapperJarPath" "$jarUrl" -f
+ else
+ curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+ fi
+
+ else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Falling back to using Java to download"
+ fi
+ javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaClass=`cygpath --path --windows "$javaClass"`
+ fi
+ if [ -e "$javaClass" ]; then
+ if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Compiling MavenWrapperDownloader.java ..."
+ fi
+ # Compiling the Java class
+ ("$JAVA_HOME/bin/javac" "$javaClass")
+ fi
+ if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ # Running the downloader
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Running MavenWrapperDownloader.java ..."
+ fi
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+ echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/muttley-local-cache/mvnw.cmd b/muttley-local-cache/mvnw.cmd
new file mode 100644
index 00000000..c8d43372
--- /dev/null
+++ b/muttley-local-cache/mvnw.cmd
@@ -0,0 +1,182 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+
+FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %DOWNLOAD_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
diff --git a/muttley-local-cache/pom.xml b/muttley-local-cache/pom.xml
new file mode 100644
index 00000000..bf5ca4d0
--- /dev/null
+++ b/muttley-local-cache/pom.xml
@@ -0,0 +1,33 @@
+
+
+
+ br.com.muttley
+ muttley-cloud
+ ${revision}
+
+ 4.0.0
+ jar
+ br.com.muttley
+
+ muttley-local-cache
+
+ muttley-local-cache
+ Demo project for Spring Boot
+
+
+
+
+ br.com.muttley
+ muttley-model
+ provided
+
+
+
+ br.com.muttley
+ muttley-redis
+
+
+
+
+
diff --git a/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/LocalDatabindingService.java b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/LocalDatabindingService.java
new file mode 100644
index 00000000..e414c479
--- /dev/null
+++ b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/LocalDatabindingService.java
@@ -0,0 +1,23 @@
+package br.com.muttley.localcache.services;
+
+import br.com.muttley.model.security.JwtToken;
+import br.com.muttley.model.security.User;
+import br.com.muttley.model.security.UserDataBinding;
+import br.com.muttley.model.security.XAPIToken;
+
+import java.util.List;
+
+/**
+ * @author Joel Rodrigues Moreira 24/03/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public interface LocalDatabindingService {
+ public static final String BASIC_KEY = "USER-DATABINDING:";
+
+ List getUserDataBindings(final JwtToken jwtUser, final User user);
+
+ List getUserDataBindings(final XAPIToken token, final User user);
+
+ void expireUserDataBindings(final User user);
+}
diff --git a/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/LocalModelService.java b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/LocalModelService.java
new file mode 100644
index 00000000..1c755340
--- /dev/null
+++ b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/LocalModelService.java
@@ -0,0 +1,34 @@
+package br.com.muttley.localcache.services;
+
+import br.com.muttley.model.Model;
+import br.com.muttley.model.security.User;
+
+/**
+ * @author Joel Rodrigues Moreira on 02/09/2021.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public interface LocalModelService {
+
+ boolean containsInCahce(final User user, final Class clazz, final String key);
+
+ boolean containsReferenceInCahce(final User user, final Class clazz, final String key);
+
+ LocalModelService addCache(final User user, T value, final String key);
+
+ LocalModelService addCache(final User user, T value, final String key, final long timeout);
+
+ LocalModelService addReferenceCache(final User user, T value, final String key);
+
+ LocalModelService addReferenceCache(final User user, T value, final String key, final long timeout);
+
+ T loadModel(final User user, final Class clazz, final String key);
+
+ T loadReference(final User user, final Class clazz, final String key);
+
+ LocalModelService expire(final User user, final Class clazz, final String key);
+
+ LocalModelService refreshExpire(final User user, Class clazz, final String key);
+
+ LocalModelService refreshExpireReference(final User user, Class clazz, final String key);
+}
diff --git a/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/LocalOwnerService.java b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/LocalOwnerService.java
new file mode 100644
index 00000000..fa9e820c
--- /dev/null
+++ b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/LocalOwnerService.java
@@ -0,0 +1,23 @@
+package br.com.muttley.localcache.services;
+
+import br.com.muttley.model.security.OwnerData;
+import br.com.muttley.model.security.User;
+
+/**
+ * @author Joel Rodrigues Moreira 25/03/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ *
+ * Seviço responsável recuperar owners e fazer cache em tempo de requisição de usuário autenticados
+ */
+public interface LocalOwnerService {
+ public static final String BASIC_KEY = "OWNER:";
+
+ OwnerData loadOwnerAny();
+
+ OwnerData loadOwnerAny(final User user);
+
+ OwnerData loadOwnerById(final String id);
+
+ OwnerData loadOwnerById(final User user, final String id);
+}
diff --git a/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/LocalRSAKeyPairService.java b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/LocalRSAKeyPairService.java
new file mode 100644
index 00000000..781d21b5
--- /dev/null
+++ b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/LocalRSAKeyPairService.java
@@ -0,0 +1,10 @@
+package br.com.muttley.localcache.services;
+
+public interface LocalRSAKeyPairService {
+ public static final String BASIC_KEY_PRIVATE = "RSA:PRIVATE";
+ public static final String BASIC_KEY_PUBLIC = "RSA:PUBLIC";
+
+ String encryptMessage(final String message);
+
+ String decryptMessage(final String encryptedMessage);
+}
diff --git a/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/LocalRolesService.java b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/LocalRolesService.java
new file mode 100644
index 00000000..c8979e24
--- /dev/null
+++ b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/LocalRolesService.java
@@ -0,0 +1,24 @@
+package br.com.muttley.localcache.services;
+
+import br.com.muttley.model.security.JwtToken;
+import br.com.muttley.model.security.Role;
+import br.com.muttley.model.security.User;
+import br.com.muttley.model.security.XAPIToken;
+
+import java.util.Set;
+
+/**
+ * @author Joel Rodrigues Moreira 25/03/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public interface LocalRolesService {
+ public static final String BASIC_KEY = "ROLES:";
+
+ Set loadCurrentRoles(final JwtToken token, final User user);
+
+ Set loadCurrentRoles(final XAPIToken token, final User user);
+
+ void expireRoles(final User user);
+
+}
diff --git a/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/LocalUserAuthenticationService.java b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/LocalUserAuthenticationService.java
new file mode 100644
index 00000000..86e21d18
--- /dev/null
+++ b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/LocalUserAuthenticationService.java
@@ -0,0 +1,28 @@
+package br.com.muttley.localcache.services;
+
+import br.com.muttley.model.security.JwtToken;
+import br.com.muttley.model.security.JwtUser;
+
+/**
+ * @author Joel Rodrigues Moreira 24/03/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ * Seviço responsável por válidar e recuperar usuário atravez de tokens de authenticação
+ */
+public interface LocalUserAuthenticationService {
+
+ JwtUser getJwtUserFrom(final String apiToken);
+
+ JwtUser getJwtUserFrom(final JwtToken token);
+
+ LocalUserAuthenticationService remove(final JwtToken token);
+
+ /**
+ * Faz a atualização de um determinado token preservando as informações já persistidas
+ *
+ * @param currentToken -> token que será atualizado
+ * @param newToken -> novo token que substituira o antigo
+ * @return true -> se de fato ocorreu essa atualização
+ */
+ void refreshToken(final JwtToken currentToken, final JwtToken newToken);
+}
diff --git a/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/LocalUserPreferenceService.java b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/LocalUserPreferenceService.java
new file mode 100644
index 00000000..ae03375c
--- /dev/null
+++ b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/LocalUserPreferenceService.java
@@ -0,0 +1,21 @@
+package br.com.muttley.localcache.services;
+
+import br.com.muttley.model.security.JwtToken;
+import br.com.muttley.model.security.User;
+import br.com.muttley.model.security.XAPIToken;
+import br.com.muttley.model.security.preference.UserPreferences;
+
+/**
+ * @author Joel Rodrigues Moreira 24/03/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public interface LocalUserPreferenceService {
+ public static final String BASIC_KEY = "USER-PREFENCES:";
+
+ UserPreferences getUserPreferences(final JwtToken jwtUser, final User user);
+
+ UserPreferences getUserPreferences(final XAPIToken token, final User user);
+
+ void expireUserPreferences(final User user);
+}
diff --git a/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/LocalWorkTeamService.java b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/LocalWorkTeamService.java
new file mode 100644
index 00000000..c2597198
--- /dev/null
+++ b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/LocalWorkTeamService.java
@@ -0,0 +1,32 @@
+package br.com.muttley.localcache.services;
+
+import br.com.muttley.model.security.JwtToken;
+import br.com.muttley.model.security.Owner;
+import br.com.muttley.model.security.User;
+import br.com.muttley.model.security.XAPIToken;
+import br.com.muttley.model.workteam.WorkTeamDomain;
+
+/**
+ * @author Joel Rodrigues Moreira on 21/03/2022.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public interface LocalWorkTeamService {
+ public static final String BASIC_KEY = "WORKTEAM:";
+
+ public WorkTeamDomain getWorkTeamDomain(final JwtToken token, final User user);
+
+ public WorkTeamDomain getWorkTeamDomain(final XAPIToken token, final User user);
+
+ public LocalWorkTeamService expire(final User user);
+
+ public LocalWorkTeamService expireByOwner(final User user);
+
+ public static String getBasicKey(final Owner owner, final User user) {
+ return getBasicKeyExpressionOwner(owner) + user.getId();
+ }
+
+ public static String getBasicKeyExpressionOwner(final Owner owner) {
+ return BASIC_KEY + owner.getId() + ":";
+ }
+}
diff --git a/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/LocalXAPITokenService.java b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/LocalXAPITokenService.java
new file mode 100644
index 00000000..106f0203
--- /dev/null
+++ b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/LocalXAPITokenService.java
@@ -0,0 +1,19 @@
+package br.com.muttley.localcache.services;
+
+import br.com.muttley.model.security.JwtToken;
+import br.com.muttley.model.security.XAPIToken;
+
+public interface LocalXAPITokenService {
+ static enum Type {
+ XAPIToken,
+ JWTToken
+ }
+
+ public static final String BASIC_KEY = "API-TOKEN:";
+
+ XAPIToken loadAPIToken(final String token);
+
+ JwtToken loadJwtTokenFrom(final String xAPIToken);
+
+ LocalXAPITokenService expireAPIToken(final String token);
+}
diff --git a/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/impl/AbstractLocalDatabindingServiceImpl.java b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/impl/AbstractLocalDatabindingServiceImpl.java
new file mode 100644
index 00000000..f0091e9f
--- /dev/null
+++ b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/impl/AbstractLocalDatabindingServiceImpl.java
@@ -0,0 +1,68 @@
+package br.com.muttley.localcache.services.impl;
+
+import br.com.muttley.localcache.services.LocalDatabindingService;
+import br.com.muttley.model.security.JwtToken;
+import br.com.muttley.model.security.User;
+import br.com.muttley.model.security.UserDataBinding;
+import br.com.muttley.model.security.XAPIToken;
+import br.com.muttley.redis.service.RedisService;
+import org.springframework.beans.factory.annotation.Autowired;
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author Joel Rodrigues Moreira 25/03/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public abstract class AbstractLocalDatabindingServiceImpl implements LocalDatabindingService {
+
+ protected final RedisService redisService;
+
+ @Autowired
+ public AbstractLocalDatabindingServiceImpl(final RedisService redisService) {
+ this.redisService = redisService;
+ }
+
+ @Override
+ public List getUserDataBindings(final JwtToken jwtUser, final User user) {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public List getUserDataBindings(XAPIToken token, User user) {
+ throw new NotImplementedException();
+ }
+
+ protected void saveDatabindingsInCache(final JwtToken token, final User user, final List dataBindings) {
+ this.saveDatabindingsInCache(token.getDtExpiration(), user, dataBindings);
+ }
+
+ protected void saveDatabindingsInCache(final XAPIToken token, final User user, final List dataBindings) {
+ this.saveDatabindingsInCache(token.generateDtExpiration(), user, dataBindings);
+ }
+
+ private void saveDatabindingsInCache(final Date dtExpiration, final User user, final List dataBindings) {
+ this.redisService.set(this.getBasicKey(user), dataBindings != null ? new ArrayList<>(dataBindings) : dataBindings, dtExpiration);
+ }
+
+ protected List getDatabinDataBindingsInCache(final JwtToken token, final User user) {
+ return (List) this.redisService.get(this.getBasicKey(user));
+ }
+
+ protected List getDatabinDataBindingsInCache(final XAPIToken token, final User user) {
+ return (List) this.redisService.get(this.getBasicKey(user));
+ }
+
+ @Override
+ public void expireUserDataBindings(final User user) {
+ this.redisService.delete(this.getBasicKey(user));
+ }
+
+ protected String getBasicKey(final User user) {
+ return BASIC_KEY + user.getId();
+ }
+}
diff --git a/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/impl/AbstractLocalModelServiceImpl.java b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/impl/AbstractLocalModelServiceImpl.java
new file mode 100644
index 00000000..b6b3508d
--- /dev/null
+++ b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/impl/AbstractLocalModelServiceImpl.java
@@ -0,0 +1,112 @@
+package br.com.muttley.localcache.services.impl;
+
+import br.com.muttley.localcache.services.LocalModelService;
+import br.com.muttley.model.Model;
+import br.com.muttley.model.security.User;
+import br.com.muttley.model.util.RedisUtils;
+import br.com.muttley.redis.service.RedisService;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ * @author Joel Rodrigues Moreira on 02/09/2021.
+ * e-mail: joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public abstract class AbstractLocalModelServiceImpl implements LocalModelService {
+ private final RedisService redisService;
+ private final long TIMEOUT = 25L;
+
+ @Autowired
+ public AbstractLocalModelServiceImpl(final RedisService redisService) {
+ this.redisService = redisService;
+ }
+
+ @Override
+ public boolean containsInCahce(final User user, final Class clazz, final String key) {
+ return this.redisService.hasKey(this.getBasicKey(user, clazz, key));
+ }
+
+ @Override
+ public boolean containsReferenceInCahce(User user, Class clazz, String key) {
+ return this.redisService.hasKey(this.getBasicKeyReference(user, clazz, key));
+ }
+
+ @Override
+ public LocalModelService addCache(final User user, final T value, final String key) {
+ return this.addCache(user, value, key, TIMEOUT);
+ }
+
+ @Override
+ public LocalModelService addCache(final User user, final T value, final String key, long timeout) {
+ this.redisService.set(this.getBasicKey(user, (Class) value.getClass(), key), value, timeout);
+ return this;
+ }
+
+ @Override
+ public LocalModelService addReferenceCache(User user, T value, String key) {
+ return this.addReferenceCache(user, value, key, TIMEOUT);
+ }
+
+ @Override
+ public LocalModelService addReferenceCache(User user, T value, String key, long timeout) {
+ this.redisService.set(this.getBasicKeyReference(user, (Class) value.getClass(), key), value, timeout);
+ return this;
+ }
+
+ @Override
+ public T loadModel(final User user, final Class clazz, String key) {
+ final String basickey = this.getBasicKey(user, clazz, key);
+ final T result;
+ if (this.containsInCahce(basickey)) {
+ result = (T) this.redisService.get(basickey);
+ this.refreshExpire(user, clazz, key);
+ } else {
+ result = null;
+ }
+ return result;
+ }
+
+ @Override
+ public T loadReference(User user, Class clazz, String key) {
+ final String basickey = this.getBasicKeyReference(user, clazz, key);
+ final T result;
+ if (this.containsInCahce(basickey)) {
+ result = (T) this.redisService.get(basickey);
+ this.refreshExpire(user, clazz, key);
+ } else {
+ result = null;
+ }
+ return result;
+ }
+
+ @Override
+ public LocalModelService expire(final User user, final Class clazz, final String key) {
+ this.redisService.delete(this.getBasicKey(user, clazz, key));
+ this.redisService.delete(this.getBasicKeyReference(user, clazz, key));
+ return this;
+ }
+
+ @Override
+ public LocalModelService refreshExpire(User user, Class clazz, String key) {
+ this.redisService.setExpire(this.getBasicKey(user, clazz, key), TIMEOUT);
+ return this;
+ }
+
+ @Override
+ public LocalModelService refreshExpireReference(User user, Class clazz, String key) {
+ this.redisService.setExpire(this.getBasicKeyReference(user, clazz, key), TIMEOUT);
+ return this;
+ }
+
+ protected boolean containsInCahce(final String key) {
+ return this.redisService.hasKey(key);
+ }
+
+ protected String getBasicKey(final User user, final Class clazz, final String key) {
+ return RedisUtils.createKeyByOwner(user, clazz, key);
+ }
+
+ protected String getBasicKeyReference(final User user, final Class clazz, final String key) {
+ return RedisUtils.createKeyByOwner(user, clazz, "REFERENCE:" + key);
+ }
+}
diff --git a/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/impl/AbstractLocalOwnerServiceImpl.java b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/impl/AbstractLocalOwnerServiceImpl.java
new file mode 100644
index 00000000..86902e28
--- /dev/null
+++ b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/impl/AbstractLocalOwnerServiceImpl.java
@@ -0,0 +1,96 @@
+package br.com.muttley.localcache.services.impl;
+
+import br.com.muttley.localcache.services.LocalOwnerService;
+import br.com.muttley.model.security.OwnerData;
+import br.com.muttley.model.security.OwnerDataImpl;
+import br.com.muttley.model.security.User;
+import br.com.muttley.model.security.UserDataImpl;
+import br.com.muttley.redis.service.RedisService;
+import org.springframework.beans.factory.annotation.Autowired;
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author Joel Rodrigues Moreira 25/03/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public abstract class AbstractLocalOwnerServiceImpl implements LocalOwnerService {
+ protected final RedisService redisService;
+ protected static final long timeout = 60 * 60 * 24;//timeout de 24hr
+
+ @Autowired
+ public AbstractLocalOwnerServiceImpl(final RedisService redisService) {
+ this.redisService = redisService;
+ }
+
+ @Override
+ public OwnerData loadOwnerAny() {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public OwnerData loadOwnerAny(final User user) {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public OwnerData loadOwnerById(final String id) {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public OwnerData loadOwnerById(final User user, final String id) {
+ throw new NotImplementedException();
+ }
+
+ protected void saveOwnerInCache(final OwnerData owner) {
+ final Map ownerMap = new HashMap<>();
+ ownerMap.put("id", owner.getId());
+ ownerMap.put("name", owner.getName());
+ ownerMap.put("description", owner.getDescription());
+
+ if (owner.getUserMaster() != null) {
+ final Map userMasterMap = new HashMap<>();
+ userMasterMap.put("id", owner.getUserMaster().getId());
+ userMasterMap.put("name", owner.getUserMaster().getName());
+ userMasterMap.put("description", owner.getUserMaster().getDescription());
+ userMasterMap.put("userName", owner.getUserMaster().getUserName());
+ userMasterMap.put("nickUsers", owner.getUserMaster().getNickUsers());
+ userMasterMap.put("email", owner.getUserMaster().getEmail());
+ ownerMap.put("userMaster", userMasterMap);
+ }
+ this.redisService.set(this.getBasicKey(owner), ownerMap, timeout);
+ }
+
+ protected OwnerData loadOwerInCache(final String id) {
+ final Map owerMap = (Map) this.redisService.get(this.getBasicKey(id));
+ final OwnerDataImpl owner = new OwnerDataImpl();
+ owner.setId(String.valueOf(owerMap.get("id")));
+ owner.setDescription(String.valueOf(owerMap.get("description")));
+ owner.setName(String.valueOf(owerMap.get("name")));
+ if (owerMap.containsKey("userMaster")) {
+ final UserDataImpl userData = new UserDataImpl();
+ final Map userMasterMap = (Map) owerMap.get("userMaster");
+ userData.setId(String.valueOf(userMasterMap.get("id")));
+ userData.setName(String.valueOf(userMasterMap.get("name")));
+ userData.setDescription(String.valueOf(userMasterMap.get("description")));
+ userData.setUserName(String.valueOf(userMasterMap.get("userName")));
+ userData.setNickUsers((Set) userMasterMap.get("nickUsers"));
+ userData.setEmail(String.valueOf(userMasterMap.get("email")));
+ owner.setUserMaster(userData);
+ }
+ return owner;
+ }
+
+ protected String getBasicKey(final OwnerData ownerData) {
+ return getBasicKey(ownerData.getId());
+ }
+
+ protected String getBasicKey(final String id) {
+ return LocalOwnerService.BASIC_KEY + id;
+ }
+}
diff --git a/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/impl/AbstractLocalRSAKeyPairService.java b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/impl/AbstractLocalRSAKeyPairService.java
new file mode 100644
index 00000000..9f52372a
--- /dev/null
+++ b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/impl/AbstractLocalRSAKeyPairService.java
@@ -0,0 +1,65 @@
+package br.com.muttley.localcache.services.impl;
+
+import br.com.muttley.localcache.services.LocalRSAKeyPairService;
+import br.com.muttley.model.security.rsa.RSAUtil;
+import br.com.muttley.redis.service.RedisService;
+
+import java.security.PrivateKey;
+import java.security.PublicKey;
+
+import static br.com.muttley.model.security.rsa.RSAUtil.decrypt;
+import static br.com.muttley.model.security.rsa.RSAUtil.encrypt;
+import static br.com.muttley.model.security.rsa.RSAUtil.readPrivateKeyFromString;
+import static br.com.muttley.model.security.rsa.RSAUtil.readPublicKeyFromString;
+
+public abstract class AbstractLocalRSAKeyPairService implements LocalRSAKeyPairService {
+ protected PrivateKey privateKey;
+ protected PublicKey publicKey;
+ protected final RedisService service;
+
+ protected AbstractLocalRSAKeyPairService(RedisService service) {
+ this.service = service;
+ }
+
+ protected String getBasicKeyPublic() {
+ return BASIC_KEY_PUBLIC;
+ }
+
+ protected String getBasicKeyPrivate() {
+ return BASIC_KEY_PRIVATE;
+ }
+
+ @Override
+ public String encryptMessage(String message) {
+ return encrypt(getPrivateKey(), message);
+ }
+
+ @Override
+ public String decryptMessage(String encryptedMessage) {
+ return decrypt(getPublicKey(), encryptedMessage);
+ }
+
+ protected PrivateKey getPrivateKey() {
+ if (privateKey == null) {
+ privateKey = readPrivateKeyFromString((String) this.service.get(this.getBasicKeyPrivate()));
+ }
+ return privateKey;
+ }
+
+ protected void setPrivateKey(final PrivateKey key) {
+ this.privateKey = key;
+ this.service.set(this.getBasicKeyPrivate(), RSAUtil.toString(key));
+ }
+
+ protected PublicKey getPublicKey() {
+ if (publicKey == null) {
+ publicKey = readPublicKeyFromString((String) this.service.get(this.getBasicKeyPublic()));
+ }
+ return publicKey;
+ }
+
+ protected void setPublicKey(final PublicKey key) {
+ this.publicKey = key;
+ this.service.set(this.getBasicKeyPublic(), RSAUtil.toString(key));
+ }
+}
diff --git a/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/impl/AbstractLocalRolesServiceImpl.java b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/impl/AbstractLocalRolesServiceImpl.java
new file mode 100644
index 00000000..13b7001a
--- /dev/null
+++ b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/impl/AbstractLocalRolesServiceImpl.java
@@ -0,0 +1,71 @@
+package br.com.muttley.localcache.services.impl;
+
+import br.com.muttley.localcache.services.LocalRolesService;
+import br.com.muttley.model.security.JwtToken;
+import br.com.muttley.model.security.Role;
+import br.com.muttley.model.security.User;
+import br.com.muttley.model.security.XAPIToken;
+import br.com.muttley.redis.service.RedisService;
+import org.springframework.beans.factory.annotation.Autowired;
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+
+import java.util.Date;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * @author Joel Rodrigues Moreira 25/03/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public abstract class AbstractLocalRolesServiceImpl implements LocalRolesService {
+ protected final RedisService redisService;
+
+ @Autowired
+ public AbstractLocalRolesServiceImpl(final RedisService redisService) {
+ this.redisService = redisService;
+ }
+
+ @Override
+ public Set loadCurrentRoles(final JwtToken token, final User user) {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public Set loadCurrentRoles(final XAPIToken token, final User user) {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public void expireRoles(final User user) {
+ this.redisService.delete(this.getBasicKey(user));
+ }
+
+ protected void saveRolesInCache(final JwtToken token, final User user, final Set roles) {
+ this.saveRolesInCache(token.getDtExpiration(), user, roles);
+ }
+
+ protected void saveRolesInCache(final XAPIToken token, final User user, final Set roles) {
+ this.saveRolesInCache(token.generateDtExpiration(), user, roles);
+ }
+
+ private void saveRolesInCache(final Date dtExpiration, final User user, final Set roles) {
+ this.redisService.set(
+ this.getBasicKey(user),
+ roles.parallelStream()
+ .map(Role::getRoleName)
+ .collect(Collectors.toSet()),
+ dtExpiration);
+ }
+
+ protected Set loadRolesInCache(final User user) {
+ return ((Set) this.redisService.get(this.getBasicKey(user)))
+ .parallelStream()
+ .map(it -> Role.valueOf(it))
+ .collect(Collectors.toSet());
+ }
+
+ protected String getBasicKey(final User user) {
+ return LocalRolesService.BASIC_KEY + user.getId();
+ }
+}
diff --git a/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/impl/AbstractLocalUserPrefenceServiceImpl.java b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/impl/AbstractLocalUserPrefenceServiceImpl.java
new file mode 100644
index 00000000..325c8ed1
--- /dev/null
+++ b/muttley-local-cache/src/main/java/br/com/muttley/localcache/services/impl/AbstractLocalUserPrefenceServiceImpl.java
@@ -0,0 +1,110 @@
+package br.com.muttley.localcache.services.impl;
+
+import br.com.muttley.localcache.services.LocalUserPreferenceService;
+import br.com.muttley.model.security.JwtToken;
+import br.com.muttley.model.security.User;
+import br.com.muttley.model.security.XAPIToken;
+import br.com.muttley.model.security.preference.Preference;
+import br.com.muttley.model.security.preference.UserPreferences;
+import br.com.muttley.redis.service.RedisService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationEventPublisher;
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * @author Joel Rodrigues Moreira 24/03/2021
+ * joel.databox@gmail.com
+ * @project muttley-cloud
+ */
+public abstract class AbstractLocalUserPrefenceServiceImpl implements LocalUserPreferenceService {
+ protected final RedisService redisService;
+ protected final ApplicationEventPublisher publisher;
+
+ @Autowired
+ public AbstractLocalUserPrefenceServiceImpl(final RedisService redisService, ApplicationEventPublisher publisher) {
+ this.redisService = redisService;
+ this.publisher = publisher;
+ }
+
+ @Override
+ public UserPreferences getUserPreferences(final JwtToken jwtUser, final User user) {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public UserPreferences getUserPreferences(XAPIToken token, User user) {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public void expireUserPreferences(User user) {
+ //deletando item do cache
+ this.redisService.delete(this.getBasicKey(user));
+ }
+
+ protected String getBasicKey(final User user) {
+ return BASIC_KEY + user.getId();
+ }
+
+ protected void savePreferenceInCache(final JwtToken token, final User user, final UserPreferences userPreferences) {
+ savePreferenceInCache(token.getDtExpiration(), user, userPreferences);
+ }
+
+ protected void savePreferenceInCache(final XAPIToken token, final User user, final UserPreferences userPreferences) {
+ savePreferenceInCache(token.generateDtExpiration(), user, userPreferences);
+ }
+
+ protected void savePreferenceInCache(final Date dtExpiration, final User user, final UserPreferences userPreferences) {
+ final Map userPreferencesMap = new HashMap<>();
+ userPreferencesMap.put("id", userPreferences.getId());
+ if (!userPreferences.isEmpty()) {
+ userPreferencesMap.put("preferences",
+ userPreferences.getPreferences()
+ .parallelStream()
+ .map(pre -> {
+ final Map preferencesItemMap = new HashMap<>();
+ preferencesItemMap.put("key", pre.getKey());
+ preferencesItemMap.put("value", pre.getValue());
+ preferencesItemMap.put("resolved", pre.getResolved());
+ return preferencesItemMap;
+ }).collect(Collectors.toList())
+ );
+ }
+ this.redisService.set(this.getBasicKey(user), userPreferencesMap, dtExpiration);
+ }
+
+ protected UserPreferences getPreferenceInCache(final JwtToken token, final User user) {
+ return this.getPreferenceInCache(user);
+ }
+
+ protected UserPreferences getPreferenceInCache(final XAPIToken token, final User user) {
+ return this.getPreferenceInCache(user);
+ }
+
+ private UserPreferences getPreferenceInCache(final User user) {
+ final Map userPreferencesMap = (Map) this.redisService.get(this.getBasicKey(user));
+ final UserPreferences preferences = new UserPreferences();
+ preferences.setId(String.valueOf(userPreferencesMap.get("id")));
+ if (userPreferencesMap.containsKey("preferences")) {
+ preferences.setPreferences(
+ ((List