Tony CHEMIT pushed to branch develop-9.3.x at ultreiaio / ird-observe Commits: e6da8389 by Tony Chemit at 2024-09-19T14:47:40+02:00 Do it for Report adapters (add some equals on Parameters to be able to test them) - See #2938 - - - - - 250b74cd by Tony Chemit at 2024-09-19T15:03:07+02:00 L'action n'est active que si une source de donnée est active - See #2826 - - - - - 12 changed files: - client/core/src/main/java/fr/ird/observe/client/datasource/api/ObserveDataSourcesManager.java - client/datasource/editor/ps/src/main/java/fr/ird/observe/client/datasource/editor/ps/data/dcp/presets/FloatingObjectPresetsUIBodyContent.java - toolkit/api-report/src/main/java/fr/ird/observe/report/ReportVariableSupport.java - toolkit/api-report/src/main/java/fr/ird/observe/report/json/ReportRepeatVariableAdapter.java - toolkit/api-report/src/main/java/fr/ird/observe/report/json/ReportVariableAdapter.java - toolkit/api-report/src/main/java/fr/ird/observe/report/json/ReportVariableSupportAdapter.java - toolkit/api-report/src/main/java/fr/ird/observe/report/renderers/HighlightIfAbsoluteDeltaIsPositive.java - toolkit/api-report/src/main/java/fr/ird/observe/report/renderers/HighlightIfEquals18nReferentialValue.java - toolkit/api-report/src/main/java/fr/ird/observe/report/renderers/HighlightIfNumericalValueIsPositive.java - + toolkit/api-report/src/test/java/fr/ird/observe/report/json/ReportColumnRenderersParametersAdapterTest.java - + toolkit/api-report/src/test/java/fr/ird/observe/report/json/ReportRepeatVariableAdapterTest.java - + toolkit/api-report/src/test/java/fr/ird/observe/report/json/ReportVariableAdapterTest.java Changes: ===================================== client/core/src/main/java/fr/ird/observe/client/datasource/api/ObserveDataSourcesManager.java ===================================== @@ -38,6 +38,8 @@ import fr.ird.observe.decoration.DecoratorService; import fr.ird.observe.navigation.id.Project; import fr.ird.observe.server.security.InvalidAuthenticationTokenException; import fr.ird.observe.services.ObserveServiceMainFactory; +import io.ultreia.java4all.bean.AbstractJavaBean; +import io.ultreia.java4all.bean.spi.GenerateJavaBeanDefinition; import io.ultreia.java4all.i18n.I18n; import io.ultreia.java4all.util.sql.SqlScript; import org.apache.logging.log4j.LogManager; @@ -60,9 +62,11 @@ import java.util.UUID; * @author Tony Chemit - dev@tchemit.fr * @since 5.0 */ -public class ObserveDataSourcesManager implements Closeable { +@GenerateJavaBeanDefinition +public class ObserveDataSourcesManager extends AbstractJavaBean implements Closeable { private static final Logger log = LogManager.getLogger(ObserveDataSourcesManager.class); + public static final String PROPERTY_MAIN_DATA_SOURCE = "mainDataSource"; private final List<ObserveSwingDataSource> dataSources = new LinkedList<>(); private final ClientConfig config; private final ObserveServiceMainFactory serviceFactory; @@ -149,6 +153,7 @@ public class ObserveDataSourcesManager implements Closeable { public void setMainDataSource(ObserveSwingDataSource dataSource) { this.dataSource = dataSource; + firePropertyChange(PROPERTY_MAIN_DATA_SOURCE, dataSource); } public Optional<ObserveSwingDataSource> getOptionalMainDataSource() { ===================================== client/datasource/editor/ps/src/main/java/fr/ird/observe/client/datasource/editor/ps/data/dcp/presets/FloatingObjectPresetsUIBodyContent.java ===================================== @@ -24,6 +24,7 @@ package fr.ird.observe.client.datasource.editor.ps.data.dcp.presets; import com.google.auto.service.AutoService; import fr.ird.observe.client.ObserveSwingApplicationContext; +import fr.ird.observe.client.datasource.api.ObserveDataSourcesManager; import fr.ird.observe.client.datasource.api.ObserveSwingDataSource; import fr.ird.observe.client.datasource.editor.ps.data.dcp.actions.ShowFloatingObjectPresetsUI; import fr.ird.observe.client.main.ObserveMainUI; @@ -31,6 +32,7 @@ import fr.ird.observe.client.main.body.MainUIBodyContent; import io.ultreia.java4all.util.SingletonSupplier; import org.nuiton.jaxx.runtime.context.JAXXInitialContext; +import javax.swing.JMenuItem; import java.util.function.Supplier; /** @@ -65,6 +67,13 @@ public class FloatingObjectPresetsUIBodyContent extends MainUIBodyContent<Floati @Override public void install(ObserveMainUI mainUI) { super.install(mainUI); - ShowFloatingObjectPresetsUI.init(mainUI, mainUI.getShowFloatingObjectPresets(), new ShowFloatingObjectPresetsUI()); + ObserveSwingApplicationContext applicationContext = (ObserveSwingApplicationContext) ObserveSwingApplicationContext.get(); + ObserveDataSourcesManager dataSourcesManager = applicationContext.getDataSourcesManager(); + JMenuItem editor = mainUI.getShowFloatingObjectPresets(); + dataSourcesManager.addPropertyChangeListener(ObserveDataSourcesManager.PROPERTY_MAIN_DATA_SOURCE, evt -> { + ObserveSwingDataSource newValue = (ObserveSwingDataSource) evt.getNewValue(); + editor.setEnabled(newValue != null); + }); + ShowFloatingObjectPresetsUI.init(mainUI, editor, new ShowFloatingObjectPresetsUI()); } } ===================================== toolkit/api-report/src/main/java/fr/ird/observe/report/ReportVariableSupport.java ===================================== @@ -64,6 +64,10 @@ public abstract class ReportVariableSupport<V, D extends ReportVariableDefinitio return definition().getName(); } + public final String getComment() { + return definition().getComment(); + } + public final Class<V> getType() { return definition().getType(); } ===================================== toolkit/api-report/src/main/java/fr/ird/observe/report/json/ReportRepeatVariableAdapter.java ===================================== @@ -23,15 +23,10 @@ package fr.ird.observe.report.json; */ import com.google.auto.service.AutoService; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonObject; -import com.google.gson.reflect.TypeToken; import fr.ird.observe.report.ReportRepeatVariable; import fr.ird.observe.report.definition.ReportRepeatVariableDefinition; import io.ultreia.java4all.util.json.JsonAdapter; -import java.lang.reflect.Type; - /** * Created on 14/12/2022. * @@ -46,16 +41,4 @@ public class ReportRepeatVariableAdapter<V> extends ReportVariableSupportAdapter return ReportRepeatVariable.class; } - @Override - protected Type definitionType() { - return new TypeToken<ReportRepeatVariableDefinition<V>>() { - }.getType(); - } - - @Override - protected ReportRepeatVariable<V> deserialize(JsonDeserializationContext context, JsonObject jsonObject, ReportRepeatVariableDefinition<V> definition) { - ReportRepeatVariable<V> result = new ReportRepeatVariable<>(definition); - deserializeValues(context, jsonObject, result); - return result; - } } ===================================== toolkit/api-report/src/main/java/fr/ird/observe/report/json/ReportVariableAdapter.java ===================================== @@ -27,13 +27,11 @@ import com.google.auto.service.AutoService; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.google.gson.reflect.TypeToken; +import com.google.gson.JsonSerializationContext; import fr.ird.observe.report.ReportVariable; import fr.ird.observe.report.definition.ReportVariableDefinition; import io.ultreia.java4all.util.json.JsonAdapter; -import java.lang.reflect.Type; - /** * @author Tony Chemit - dev@tchemit.fr */ @@ -46,22 +44,24 @@ public class ReportVariableAdapter<V> extends ReportVariableSupportAdapter<V, Re } @Override - protected Type definitionType() { - return new TypeToken<ReportVariableDefinition<V>>() { - }.getType(); + protected void deserialize(JsonDeserializationContext context, JsonObject jsonObject, ReportVariable<V> variable) { + super.deserialize(context, jsonObject, variable); + deserializeSelectedValue(context, jsonObject, variable); } @Override - protected ReportVariable<V> deserialize(JsonDeserializationContext context, JsonObject jsonObject, ReportVariableDefinition<V> definition) { - ReportVariable<V> result = new ReportVariable<>(definition); - deserializeValues(context, jsonObject, result); - deserializeSelectedValue(context, jsonObject, result); - return result; + public void serialize(ReportVariable<V> src, JsonSerializationContext context, JsonObject jsonObject) { + super.serialize(src, context, jsonObject); + if (src.getSelectedValue() != null) { + jsonObject.add(ReportVariable.PROPERTY_SELECTED_VALUE, context.serialize(src.getSelectedValue())); + } } protected void deserializeSelectedValue(JsonDeserializationContext context, JsonObject jsonObject, ReportVariable<V> variable) { JsonElement json = jsonObject.get(ReportVariable.PROPERTY_SELECTED_VALUE); - V selectedValue = deserializeValue(context, variable.getType(), json); - variable.setSelectedValue(selectedValue); + if (json != null) { + V selectedValue = deserializeValue(context, variable.getType(), json); + variable.setSelectedValue(selectedValue); + } } } ===================================== toolkit/api-report/src/main/java/fr/ird/observe/report/json/ReportVariableSupportAdapter.java ===================================== @@ -28,6 +28,9 @@ import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.google.gson.reflect.TypeToken; import fr.ird.observe.report.ReportVariableSupport; import fr.ird.observe.report.definition.ReportVariableDefinitionSupport; import io.ultreia.java4all.util.json.JsonAdapter; @@ -44,21 +47,38 @@ import java.util.Set; * @author Tony Chemit - dev@tchemit.fr * @since 9.0.22 */ -public abstract class ReportVariableSupportAdapter<V, D extends ReportVariableDefinitionSupport<V>, R extends ReportVariableSupport<V, D>> implements JsonAdapter, JsonDeserializer<R> { +public abstract class ReportVariableSupportAdapter<V, D extends ReportVariableDefinitionSupport<V>, R extends ReportVariableSupport<V, D>> implements JsonAdapter, JsonSerializer<R>, JsonDeserializer<R> { - protected abstract Type definitionType(); + @Override + public JsonElement serialize(R src, Type typeOfSrc, JsonSerializationContext context) { + JsonObject jsonObject = new JsonObject(); + jsonObject.add("V", context.serialize(src.definition().getType())); + jsonObject.add("D", context.serialize(src.definition().getClass())); + serialize(src, context, jsonObject); + return jsonObject; + } - protected abstract R deserialize(JsonDeserializationContext context, JsonObject jsonObject, D definition); + protected void serialize(R src, JsonSerializationContext context, JsonObject jsonObject) { + jsonObject.add(ReportVariableSupport.PROPERTY_DEFINITION, context.serialize(src.definition())); + jsonObject.add(ReportVariableSupport.PROPERTY_VALUES, context.serialize(src.getValues())); + } @Override public final R deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { JsonObject jsonObject = json.getAsJsonObject(); - D definition = deserializeDefinition(context, jsonObject); - return deserialize(context, jsonObject, definition); + Class<V> vType = context.deserialize(jsonObject.get("V"), Class.class); + Class<D> dType = context.deserialize(jsonObject.get("D"), Class.class); + Type definitionType = TypeToken.getParameterized(dType, vType).getType(); + + D definition = context.deserialize(jsonObject.get(ReportVariableSupport.PROPERTY_DEFINITION), definitionType); + @SuppressWarnings("unchecked") R variable = (R) definition.toVariable(); + + deserialize(context, jsonObject, variable); + return variable; } - protected D deserializeDefinition(JsonDeserializationContext context, JsonObject jsonObject) { - return context.deserialize(jsonObject.get(ReportVariableSupport.PROPERTY_DEFINITION), definitionType()); + protected void deserialize(JsonDeserializationContext context, JsonObject jsonObject, R variable) { + deserializeValues(context, jsonObject, variable); } protected void deserializeValues(JsonDeserializationContext context, JsonObject jsonObject, R variable) { ===================================== toolkit/api-report/src/main/java/fr/ird/observe/report/renderers/HighlightIfAbsoluteDeltaIsPositive.java ===================================== @@ -31,6 +31,7 @@ import org.jdesktop.swingx.decorator.ComponentAdapter; import org.jdesktop.swingx.decorator.HighlightPredicate; import java.awt.Component; +import java.util.Objects; import static fr.ird.observe.report.renderers.HighlightIfAbsoluteDeltaIsPositive.Parameters; @@ -116,6 +117,19 @@ public class HighlightIfAbsoluteDeltaIsPositive implements ColumnRendererConsume public double getErrorThreshHold() { return errorThreshHold; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Parameters)) return false; + Parameters that = (Parameters) o; + return getColumn1() == that.getColumn1() && getColumn2() == that.getColumn2() && Double.compare(getWarningThreshHold(), that.getWarningThreshHold()) == 0 && Double.compare(getErrorThreshHold(), that.getErrorThreshHold()) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(getColumn1(), getColumn2(), getWarningThreshHold(), getErrorThreshHold()); + } } static class ParametersHighlightPredicate implements HighlightPredicate { ===================================== toolkit/api-report/src/main/java/fr/ird/observe/report/renderers/HighlightIfEquals18nReferentialValue.java ===================================== @@ -155,5 +155,18 @@ public class HighlightIfEquals18nReferentialValue implements ColumnRendererConsu public Parameters setLabel(String labelError, String labelOk) { return new Parameters(columns, idError, idOk, labelError, labelOk); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Parameters)) return false; + Parameters that = (Parameters) o; + return Objects.equals(getColumns(), that.getColumns()) && Objects.equals(getIdError(), that.getIdError()) && Objects.equals(getIdOk(), that.getIdOk()) && Objects.equals(getLabelError(), that.getLabelError()) && Objects.equals(getLabelOk(), that.getLabelOk()); + } + + @Override + public int hashCode() { + return Objects.hash(getColumns(), getIdError(), getIdOk(), getLabelError(), getLabelOk()); + } } } ===================================== toolkit/api-report/src/main/java/fr/ird/observe/report/renderers/HighlightIfNumericalValueIsPositive.java ===================================== @@ -31,6 +31,7 @@ import org.jdesktop.swingx.decorator.ComponentAdapter; import org.jdesktop.swingx.decorator.HighlightPredicate; import java.awt.Component; +import java.util.Objects; import static fr.ird.observe.report.renderers.HighlightIfNumericalValueIsPositive.Parameters; @@ -110,6 +111,19 @@ public class HighlightIfNumericalValueIsPositive implements ColumnRendererConsum public double getWarningThreshHold() { return warningThreshHold; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Parameters)) return false; + Parameters that = (Parameters) o; + return getColumn() == that.getColumn() && Double.compare(getWarningThreshHold(), that.getWarningThreshHold()) == 0 && Double.compare(getErrorThreshHold(), that.getErrorThreshHold()) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(getColumn(), getWarningThreshHold(), getErrorThreshHold()); + } } static class ParametersHighlightPredicate implements HighlightPredicate { ===================================== toolkit/api-report/src/test/java/fr/ird/observe/report/json/ReportColumnRenderersParametersAdapterTest.java ===================================== @@ -0,0 +1,66 @@ +package fr.ird.observe.report.json; + +/*- + * #%L + * ObServe Toolkit :: API :: Report + * %% + * Copyright (C) 2008 - 2024 IRD, Ultreia.io + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import fr.ird.observe.report.ReportColumnRenderersParameters; +import fr.ird.observe.report.renderers.HighlightIfAbsoluteDeltaIsPositive; +import fr.ird.observe.report.renderers.HighlightIfEquals18nReferentialValue; +import fr.ird.observe.report.renderers.HighlightIfNumericalValueIsPositive; +import io.ultreia.java4all.util.json.JsonAdapterProvider; +import org.junit.Assert; +import org.junit.Test; + +import java.util.List; + +/** + * Created at 19/09/2024. + * + * @author Tony Chemit - dev@tchemit.fr + * @since 9.3.7 + */ +public class ReportColumnRenderersParametersAdapterTest { + + private static void assertEquals(ReportColumnRenderersParameters expected, ReportColumnRenderersParameters actual) { + Assert.assertEquals(expected.getColumnRendererParameters(), actual.getColumnRendererParameters()); + } + + @Test + public void test() { + + GsonBuilder gsonBuilder = new GsonBuilder().serializeNulls().setPrettyPrinting(); + JsonAdapterProvider.offers(gsonBuilder::registerTypeAdapter, gsonBuilder::registerTypeHierarchyAdapter); + Gson gson = gsonBuilder.create(); + + ReportColumnRenderersParameters expected = new ReportColumnRenderersParameters(List.of( + new HighlightIfAbsoluteDeltaIsPositive().parseParameters("0|1|0.5f|0.1f"), + new HighlightIfEquals18nReferentialValue().parseParameters("0,1,2|ok|error"), + new HighlightIfNumericalValueIsPositive().parseParameters("0|1.0f|2.5f") + )); + + String json = gson.toJson(expected); + ReportColumnRenderersParameters actual = gson.fromJson(json, ReportColumnRenderersParameters.class); + assertEquals(expected, actual); + } +} ===================================== toolkit/api-report/src/test/java/fr/ird/observe/report/json/ReportRepeatVariableAdapterTest.java ===================================== @@ -0,0 +1,74 @@ +package fr.ird.observe.report.json; + +/*- + * #%L + * ObServe Toolkit :: API :: Report + * %% + * Copyright (C) 2008 - 2024 IRD, Ultreia.io + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import fr.ird.observe.report.ReportRepeatVariable; +import fr.ird.observe.report.definition.ReportRepeatVariableDefinition; +import io.ultreia.java4all.util.json.JsonAdapterProvider; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Set; + +/** + * Created at 19/09/2024. + * + * @author Tony Chemit - dev@tchemit.fr + * @since 9.3.7 + */ +public class ReportRepeatVariableAdapterTest { + + private static <V> void assertEquals(ReportRepeatVariable<V> expected, ReportRepeatVariable<V> actual) { + Assert.assertEquals(expected.definition(), actual.definition()); + Assert.assertEquals(expected.getName(), actual.getName()); + Assert.assertEquals(expected.getRequest(), actual.getRequest()); + Assert.assertEquals(expected.getComment(), actual.getComment()); + Assert.assertEquals(expected.getType(), actual.getType()); + Assert.assertEquals(expected.getValues(), actual.getValues()); + Assert.assertEquals(expected.isAddNullValue(), actual.isAddNullValue()); + } + + @Test + public void test() { + + GsonBuilder gsonBuilder = new GsonBuilder().serializeNulls().setPrettyPrinting(); + JsonAdapterProvider.offers(gsonBuilder::registerTypeAdapter, gsonBuilder::registerTypeHierarchyAdapter); + Gson gson = gsonBuilder.create(); + + ReportRepeatVariable<String> expected = new ReportRepeatVariable<>(new ReportRepeatVariableDefinition<>( + "name", + String.class, + "Request", + "Comment", + false + )); + expected.setValues(Set.of("a", "b", "c")); + + String json = gson.toJson(expected); + ReportRepeatVariable<String> actual = gson.fromJson(json, TypeToken.getParameterized(ReportRepeatVariable.class, String.class).getType()); + assertEquals(expected, actual); + } +} ===================================== toolkit/api-report/src/test/java/fr/ird/observe/report/json/ReportVariableAdapterTest.java ===================================== @@ -0,0 +1,80 @@ +package fr.ird.observe.report.json; + +/*- + * #%L + * ObServe Toolkit :: API :: Report + * %% + * Copyright (C) 2008 - 2024 IRD, Ultreia.io + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import fr.ird.observe.report.ReportVariable; +import fr.ird.observe.report.definition.ReportVariableDefinition; +import io.ultreia.java4all.util.json.JsonAdapterProvider; +import org.junit.Assert; +import org.junit.Test; + +import java.lang.reflect.Type; +import java.util.Set; + +/** + * Created at 19/09/2024. + * + * @author Tony Chemit - dev@tchemit.fr + * @since 9.3.7 + */ +public class ReportVariableAdapterTest { + + private static <V> void assertEquals(ReportVariable<V> expected, ReportVariable<V> actual) { + Assert.assertEquals(expected.definition(), actual.definition()); + Assert.assertEquals(expected.getName(), actual.getName()); + Assert.assertEquals(expected.getRequest(), actual.getRequest()); + Assert.assertEquals(expected.getComment(), actual.getComment()); + Assert.assertEquals(expected.getType(), actual.getType()); + Assert.assertEquals(expected.getValues(), actual.getValues()); + Assert.assertEquals(expected.getSelectedValue(), actual.getSelectedValue()); + } + + @Test + public void test() { + GsonBuilder gsonBuilder = new GsonBuilder().serializeNulls().setPrettyPrinting(); + JsonAdapterProvider.offers(gsonBuilder::registerTypeAdapter, gsonBuilder::registerTypeHierarchyAdapter); + Gson gson = gsonBuilder.create(); + + ReportVariable<String> expected = new ReportVariable<>(new ReportVariableDefinition<>( + "name", + String.class, + "Request", + "Comment" + )); + expected.setValues(Set.of("a", "b", "c")); + + String json = gson.toJson(expected); + Type type = TypeToken.getParameterized(ReportVariable.class, String.class).getType(); + ReportVariable<String> actual = gson.fromJson(json, type); + Assert.assertEquals(expected, actual); + + expected.setSelectedValue("a"); + json = gson.toJson(expected); + actual = gson.fromJson(json, type); + assertEquals(expected, actual); + + } +} View it on GitLab: https://gitlab.com/ultreiaio/ird-observe/-/compare/9afdaf04c3e33e0a57a795e51... -- View it on GitLab: https://gitlab.com/ultreiaio/ird-observe/-/compare/9afdaf04c3e33e0a57a795e51... You're receiving this email because of your account on gitlab.com.
participants (1)
-
Tony CHEMIT (@tchemit)