diff --git a/.github/workflows/maven.yaml b/.github/workflows/maven.yaml index fb14674..3a326c5 100644 --- a/.github/workflows/maven.yaml +++ b/.github/workflows/maven.yaml @@ -20,4 +20,4 @@ jobs: distribution: 'temurin' cache: maven - name: Build with Maven - run: mvn clean install -Pqulice --batch-mode --update-snapshots + run: mvn clean install --batch-mode --update-snapshots diff --git a/pom.xml b/pom.xml index 05db501..0c67179 100644 --- a/pom.xml +++ b/pom.xml @@ -23,10 +23,20 @@ 21 + + com.jcabi + jcabi-http + 2.0.0 + + + org.glassfish + jakarta.json + 1.1.6 + com.github.ArtemGet entrys - 0.3.0 + 0.4.0 com.github.ArtemGet diff --git a/src/main/java/io/github/artemget/tagrelease/Entrypoint.java b/src/main/java/io/github/artemget/tagrelease/Entrypoint.java index b3ac843..955f510 100644 --- a/src/main/java/io/github/artemget/tagrelease/Entrypoint.java +++ b/src/main/java/io/github/artemget/tagrelease/Entrypoint.java @@ -24,28 +24,33 @@ package io.github.artemget.tagrelease; +import io.github.artemget.entrys.Entry; import io.github.artemget.entrys.EntryException; -import io.github.artemget.entrys.system.EEnv; +import io.github.artemget.entrys.file.EVal; +import io.github.artemget.entrys.operation.ESplit; import io.github.artemget.tagrelease.bot.Bot; import io.github.artemget.tagrelease.bot.BotReg; -import io.github.artemget.tagrelease.command.CmdListStand; +import io.github.artemget.tagrelease.command.CmdBuildTags; +import io.github.artemget.tagrelease.command.CmdEcho; +import io.github.artemget.tagrelease.command.CmdEchoReply; +import io.github.artemget.tagrelease.command.CmdListServices; +import io.github.artemget.tagrelease.command.CmdListServicesAll; import io.github.artemget.tagrelease.command.CmdListStands; -import io.github.artemget.tagrelease.domain.StandsGitlab; -import io.github.artemget.tagrelease.entry.ESplit; +import io.github.artemget.tagrelease.domain.Services; +import io.github.artemget.tagrelease.domain.ServicesAll; +import io.github.artemget.tagrelease.domain.Stands; +import io.github.artemget.tagrelease.domain.StandsGl; import io.github.artemget.tagrelease.match.MatchAdmin; -import io.github.artemget.tagrelease.match.MatchChats; -import io.github.artemget.teleroute.match.MatchAll; +import io.github.artemget.tagrelease.match.MatchReply; import io.github.artemget.teleroute.match.MatchRegex; import io.github.artemget.teleroute.route.RouteDfs; -import io.github.artemget.teleroute.route.RouteEnd; import io.github.artemget.teleroute.route.RouteFork; import org.telegram.telegrambots.meta.exceptions.TelegramApiException; /** * Entrypoint. Application starts here. - * - * @since 0.1.0 * @checkstyle HideUtilityClassConstructorCheck (20 lines) + * @since 0.1.0 */ @SuppressWarnings( { @@ -57,32 +62,55 @@ ) public class Entrypoint { public static void main(final String[] args) throws EntryException, TelegramApiException { + final Entry host = new EVal("provider.host"); + final Entry release = new EVal("provider.release"); + final Entry token = new EVal("provider.token"); + final Entry project = new EVal("provider.project"); + final Services all = new ServicesAll(host, project, token); + final Stands stands = new StandsGl(host, release, token); new BotReg( new Bot( - new EEnv("BOT_NAME"), - new EEnv("BOT_TOKEN"), + new EVal("bot.name"), + new EVal("bot.token"), new RouteFork<>( - new MatchAll<>( - new MatchChats(new ESplit(new EEnv("BOT_CHATS"))), - new MatchAdmin(new ESplit(new EEnv("BOT_ADMINS"))) - ), + new MatchAdmin(new ESplit(new EVal("bot.admins"))), new RouteDfs<>( new RouteFork<>( - new MatchRegex<>("Покажи сервисы"), - new CmdListStands(new StandsGitlab()) + new MatchRegex<>("[Э]эхо"), + new RouteFork<>( + new MatchReply(), + new CmdEchoReply(), + new CmdEcho() + ) ), new RouteFork<>( - new MatchRegex<>("Покажи сервис"), - new CmdListStand(new StandsGitlab()) + new MatchRegex<>("[Пп]окажи сервисы"), + new CmdListServicesAll(all) ), new RouteFork<>( - new MatchRegex<>("Собери тэги по стенду"), - new RouteEnd<>(), - new RouteFork<>( - new MatchRegex<>("Собери тэг"), - new RouteEnd<>() - ) + new MatchRegex<>("[Пп]окажи сервисы \\{([^{}]*)\\}$"), + new CmdListServices(stands) + ), + new RouteFork<>( + new MatchRegex<>("[Пп]окажи стенды"), + new CmdListStands(stands) + ), + new RouteFork<>( + new MatchRegex<>("[Сс]обери сервисы \\{([^{}]*)\\}\\s+префикс\\s+\\{([^{}]*)\\}$"), + new CmdBuildTags(host, project, token) ) +// new RouteFork<>( +// new MatchRegex<>("Покажи стенд"), +// new CmdListStand(new StandsGitlab()) +// ), +// new RouteFork<>( +// new MatchRegex<>("Собери тэги по стенду"), +// new RouteEnd<>(), +// new RouteFork<>( +// new MatchRegex<>("Собери тэг"), +// new RouteEnd<>() +// ) +// ) ) ) ) diff --git a/src/main/java/io/github/artemget/tagrelease/command/CmdBuildTags.java b/src/main/java/io/github/artemget/tagrelease/command/CmdBuildTags.java new file mode 100644 index 0000000..f8d566a --- /dev/null +++ b/src/main/java/io/github/artemget/tagrelease/command/CmdBuildTags.java @@ -0,0 +1,127 @@ +/* + * MIT License + * + * Copyright (c) 2024-2025. Artem Getmanskii + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package io.github.artemget.tagrelease.command; + +import io.github.artemget.entrys.Entry; +import io.github.artemget.entrys.EntryException; +import io.github.artemget.entrys.operation.ESplit; +import io.github.artemget.entrys.operation.EUnwrap; +import io.github.artemget.tagrelease.domain.Service; +import io.github.artemget.tagrelease.domain.Services; +import io.github.artemget.tagrelease.domain.ServicesAll; +import io.github.artemget.tagrelease.domain.Tag; +import io.github.artemget.tagrelease.domain.Tags; +import io.github.artemget.tagrelease.domain.TagsGl; +import io.github.artemget.tagrelease.exception.DomainException; +import io.github.artemget.teleroute.command.Cmd; +import io.github.artemget.teleroute.command.CmdException; +import io.github.artemget.teleroute.send.Send; +import io.github.artemget.teleroute.telegrambots.send.SendMessageWrap; +import java.util.ArrayList; +import java.util.List; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.telegram.telegrambots.meta.api.methods.send.SendMessage; +import org.telegram.telegrambots.meta.api.objects.Update; +import org.telegram.telegrambots.meta.bots.AbsSender; + +public class CmdBuildTags implements Cmd { + private final Logger log = LoggerFactory.getLogger(CmdBuildTags.class); + private final Services services; + private final Tags tags; + + public CmdBuildTags( + final Entry host, + final Entry project, + final Entry token + ) { + this(new ServicesAll(host, project, token), new TagsGl(host, token)); + } + + public CmdBuildTags(final Services services, final Tags tags) { + this.services = services; + this.tags = tags; + } + + @Override + public Send execute(Update update) throws CmdException { + final List names; + final String branch; + final String prefix; + final String[] values = StringUtils.substringsBetween(update.getMessage().getText(), "{", "}"); + try { + names = new ESplit(new EUnwrap(values[0]), ",").value(); + } catch (final EntryException exception) { + throw new CmdException("Failed to parse service names for tag build", exception); + } + try { + prefix = new EUnwrap(values[1]).value(); + } catch (final EntryException exception) { + throw new CmdException("Failed to parse prefix name for tag build", exception); + } + try { + if (values.length >= 3) { + branch = new EUnwrap(values[2]).value(); + } else { + branch = "develop"; + } + } catch (final EntryException exception) { + throw new CmdException("Failed to parse branch name for tag build", exception); + } + final List succeed = new ArrayList<>(); + final List failed = new ArrayList<>(); + for (final String name : names) { + Service service; + try { + service = this.services.service(name); + } catch (final DomainException exception) { + log.error("Failed to fetch service:'{}' for tag build", name, exception); + failed.add(name); + continue; + } + final Tag tag; + try { + tag = this.tags.buildNew(service.id(), branch, prefix); + } catch (final DomainException exception) { + log.error("Failed to build tag for service:'{}'", name, exception); + failed.add(name); + continue; + } + succeed.add(tag); + } + final SendMessage message = new SendMessage( + update.getMessage().getChatId().toString(), + String.format( + "Собраны сервисы:\n%s\nОшибка сборки сервисов:\n%s", + new Tag.Printed(succeed).asString(), + String.join("\n", failed) + ) + ); + message.setReplyToMessageId(update.getMessage().getMessageId()); + message.enableMarkdownV2(true); + return new SendMessageWrap<>(message); + } +} diff --git a/src/main/java/io/github/artemget/tagrelease/command/CmdEcho.java b/src/main/java/io/github/artemget/tagrelease/command/CmdEcho.java new file mode 100644 index 0000000..db50e29 --- /dev/null +++ b/src/main/java/io/github/artemget/tagrelease/command/CmdEcho.java @@ -0,0 +1,44 @@ +/* + * MIT License + * + * Copyright (c) 2024-2025. Artem Getmanskii + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package io.github.artemget.tagrelease.command; + +import io.github.artemget.teleroute.command.Cmd; +import io.github.artemget.teleroute.command.CmdException; +import io.github.artemget.teleroute.send.Send; +import io.github.artemget.teleroute.telegrambots.send.SendMessageWrap; +import org.telegram.telegrambots.meta.api.methods.send.SendMessage; +import org.telegram.telegrambots.meta.api.objects.Update; +import org.telegram.telegrambots.meta.bots.AbsSender; +public class CmdEcho implements Cmd { + @Override + public Send execute(Update update) throws CmdException { + return new SendMessageWrap<>( + new SendMessage( + update.getMessage().getChatId().toString(), + update.getMessage().getFrom().getId().toString() + ) + ); + } +} diff --git a/src/main/java/io/github/artemget/tagrelease/command/CmdEchoReply.java b/src/main/java/io/github/artemget/tagrelease/command/CmdEchoReply.java new file mode 100644 index 0000000..cf62b59 --- /dev/null +++ b/src/main/java/io/github/artemget/tagrelease/command/CmdEchoReply.java @@ -0,0 +1,44 @@ +/* + * MIT License + * + * Copyright (c) 2024-2025. Artem Getmanskii + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package io.github.artemget.tagrelease.command; + +import io.github.artemget.teleroute.command.Cmd; +import io.github.artemget.teleroute.command.CmdException; +import io.github.artemget.teleroute.send.Send; +import io.github.artemget.teleroute.telegrambots.send.SendMessageWrap; +import org.telegram.telegrambots.meta.api.methods.send.SendMessage; +import org.telegram.telegrambots.meta.api.objects.Update; +import org.telegram.telegrambots.meta.bots.AbsSender; +public class CmdEchoReply implements Cmd { + @Override + public Send execute(Update update) throws CmdException { + return new SendMessageWrap<>( + new SendMessage( + update.getMessage().getChatId().toString(), + update.getMessage().getReplyToMessage().getFrom().getId().toString() + ) + ); + } +} \ No newline at end of file diff --git a/src/main/java/io/github/artemget/tagrelease/command/CmdListStand.java b/src/main/java/io/github/artemget/tagrelease/command/CmdListServices.java similarity index 62% rename from src/main/java/io/github/artemget/tagrelease/command/CmdListStand.java rename to src/main/java/io/github/artemget/tagrelease/command/CmdListServices.java index 8b96ffd..9bb7b70 100644 --- a/src/main/java/io/github/artemget/tagrelease/command/CmdListStand.java +++ b/src/main/java/io/github/artemget/tagrelease/command/CmdListServices.java @@ -24,25 +24,26 @@ package io.github.artemget.tagrelease.command; -import io.github.artemget.tagrelease.domain.Stand; +import io.github.artemget.entrys.EntryException; +import io.github.artemget.entrys.operation.EUnwrap; +import io.github.artemget.tagrelease.domain.Service; import io.github.artemget.tagrelease.domain.Stands; +import io.github.artemget.tagrelease.exception.DomainException; import io.github.artemget.teleroute.command.Cmd; import io.github.artemget.teleroute.command.CmdException; import io.github.artemget.teleroute.send.Send; import io.github.artemget.teleroute.telegrambots.send.SendMessageWrap; -import org.cactoos.text.Replaced; -import org.cactoos.text.TextOf; -import org.cactoos.text.Trimmed; +import java.util.stream.Collectors; import org.telegram.telegrambots.meta.api.methods.send.SendMessage; import org.telegram.telegrambots.meta.api.objects.Update; import org.telegram.telegrambots.meta.bots.AbsSender; /** - * Lists stand and it's services. + * Lists services by stand. * * @since 0.1.0 */ -public final class CmdListStand implements Cmd { +public final class CmdListServices implements Cmd { /** * Available stands. */ @@ -52,7 +53,7 @@ public final class CmdListStand implements Cmd { * Main ctor. * @param stands Available stands. */ - public CmdListStand(final Stands stands) { + public CmdListServices(final Stands stands) { this.stands = stands; } @@ -60,25 +61,36 @@ public CmdListStand(final Stands stands) { @SuppressWarnings("PMD.AvoidCatchingGenericException") @Override public Send execute(final Update update) throws CmdException { + final String request = update.getMessage().getText(); + final String name; + try { + name = new EUnwrap(update.getMessage().getText()).value(); + } catch (final EntryException exception) { + throw new CmdException( + String.format("Failed to get value from cmd:'%s'", request), + exception + ); + } final SendMessage message; try { message = new SendMessage( update.getMessage().getChatId().toString(), - new Stand.Printed( - this.stands.stand( - new Trimmed( - new Replaced( - new TextOf(update.getMessage().getText()), - "Покажи сервис", - "" - ) - ).asString() - ) - ).toString() + String.format( + "```\n %s\n```", + this.stands.stand(name).services().services() + .stream().map(Service::name).collect(Collectors.joining("\n")) + ) ); - } catch (final Exception exception) { + } catch (final DomainException exception) { throw new CmdException( - String.format("Failed to eject value from cmd %s", update.getMessage().getText()), + String.format( + "Failed get services from stand:'%s' from cmd:'%s'. From user:'%s', userId:'%s' in chat:'%s'", + name, + request, + update.getMessage().getFrom().getUserName(), + update.getMessage().getFrom().getId(), + update.getMessage().getChatId() + ), exception ); } diff --git a/src/main/java/io/github/artemget/tagrelease/command/CmdListServicesAll.java b/src/main/java/io/github/artemget/tagrelease/command/CmdListServicesAll.java new file mode 100644 index 0000000..dd67504 --- /dev/null +++ b/src/main/java/io/github/artemget/tagrelease/command/CmdListServicesAll.java @@ -0,0 +1,90 @@ +/* + * MIT License + * + * Copyright (c) 2024-2025. Artem Getmanskii + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package io.github.artemget.tagrelease.command; + +import io.github.artemget.entrys.Entry; +import io.github.artemget.tagrelease.domain.Service; +import io.github.artemget.tagrelease.domain.Services; +import io.github.artemget.tagrelease.domain.ServicesAll; +import io.github.artemget.tagrelease.exception.DomainException; +import io.github.artemget.teleroute.command.Cmd; +import io.github.artemget.teleroute.command.CmdException; +import io.github.artemget.teleroute.send.Send; +import io.github.artemget.teleroute.telegrambots.send.SendMessageWrap; +import java.util.List; +import java.util.stream.Collectors; +import org.telegram.telegrambots.meta.api.methods.send.SendMessage; +import org.telegram.telegrambots.meta.api.objects.Update; +import org.telegram.telegrambots.meta.bots.AbsSender; + +/** + * Bot command. + * List all repositories in gitlab and send reply to user message. + * + * @since 0.0.1 + */ +public final class CmdListServicesAll implements Cmd { + private final Services services; + + public CmdListServicesAll( + final Entry host, + final Entry project, + final Entry token + ) { + this(new ServicesAll(host, project, token)); + } + + public CmdListServicesAll(final Services services) { + this.services = services; + } + + @Override + public Send execute(final Update update) throws CmdException { + List srvs; + try { + srvs = this.services.services(); + } catch (final DomainException exception) { + throw new CmdException( + String.format( + "Error list all services. From user:'%s', userId:'%s' in chat:'%s'", + update.getMessage().getFrom().getUserName(), + update.getMessage().getFrom().getId(), + update.getMessage().getChatId() + ), + exception + ); + } + final SendMessage message = new SendMessage( + update.getMessage().getChatId().toString(), + String.format( + "```\n%s```", + srvs.stream().map(Service::name).collect(Collectors.joining("\n")) + ) + ); + message.setReplyToMessageId(update.getMessage().getMessageId()); + message.enableMarkdownV2(true); + return new SendMessageWrap<>(message); + } +} diff --git a/src/main/java/io/github/artemget/tagrelease/command/CmdListStands.java b/src/main/java/io/github/artemget/tagrelease/command/CmdListStands.java index c877735..bef6501 100644 --- a/src/main/java/io/github/artemget/tagrelease/command/CmdListStands.java +++ b/src/main/java/io/github/artemget/tagrelease/command/CmdListStands.java @@ -24,17 +24,21 @@ package io.github.artemget.tagrelease.command; +import io.github.artemget.tagrelease.domain.Stand; import io.github.artemget.tagrelease.domain.Stands; +import io.github.artemget.tagrelease.exception.DomainException; import io.github.artemget.teleroute.command.Cmd; +import io.github.artemget.teleroute.command.CmdException; import io.github.artemget.teleroute.send.Send; import io.github.artemget.teleroute.telegrambots.send.SendMessageWrap; +import java.util.List; +import java.util.stream.Collectors; import org.telegram.telegrambots.meta.api.methods.send.SendMessage; import org.telegram.telegrambots.meta.api.objects.Update; import org.telegram.telegrambots.meta.bots.AbsSender; /** * Lists available stands and it's services. - * * @since 0.1.0 */ public final class CmdListStands implements Cmd { @@ -52,10 +56,27 @@ public CmdListStands(final Stands stands) { } @Override - public Send execute(final Update update) { + public Send execute(final Update update) throws CmdException { + final List stands; + try { + stands = this.stands.stands(); + } catch (final DomainException exception) { + throw new CmdException( + String.format( + "Error list all stands. From user:'%s', userId:'%s' in chat:'%s'", + update.getMessage().getFrom().getUserName(), + update.getMessage().getFrom().getId(), + update.getMessage().getChatId() + ), + exception + ); + } final SendMessage message = new SendMessage( update.getMessage().getChatId().toString(), - new Stands.Printed(this.stands).toString() + String.format( + "```\n%s```", + stands.stream().map(Stand::name).collect(Collectors.joining("\n")) + ) ); message.setReplyToMessageId(update.getMessage().getMessageId()); message.enableMarkdownV2(true); diff --git a/src/main/java/io/github/artemget/tagrelease/domain/Service.java b/src/main/java/io/github/artemget/tagrelease/domain/Service.java index b1cf289..addc845 100644 --- a/src/main/java/io/github/artemget/tagrelease/domain/Service.java +++ b/src/main/java/io/github/artemget/tagrelease/domain/Service.java @@ -24,7 +24,7 @@ package io.github.artemget.tagrelease.domain; -import io.github.artemget.tagrelease.exception.TagException; +import io.github.artemget.tagrelease.exception.DomainException; import org.cactoos.Scalar; /** @@ -33,6 +33,7 @@ * @since 0.1.0 */ public interface Service { + String id(); /** * Returns application name. * @@ -45,13 +46,5 @@ public interface Service { * * @return Tag */ - String tag(); - - /** - * Builds new tag. - * - * @param rule To build tag - * @return Service with a new tag - */ - Service tagged(Scalar rule) throws TagException; + String tag() throws DomainException; } diff --git a/src/main/java/io/github/artemget/tagrelease/domain/ServiceEa.java b/src/main/java/io/github/artemget/tagrelease/domain/ServiceEa.java new file mode 100644 index 0000000..ca2e1be --- /dev/null +++ b/src/main/java/io/github/artemget/tagrelease/domain/ServiceEa.java @@ -0,0 +1,117 @@ +/* + * MIT License + * + * Copyright (c) 2024-2025. Artem Getmanskii + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package io.github.artemget.tagrelease.domain; + +import com.amihaiemil.eoyaml.Yaml; +import com.jcabi.http.Request; +import com.jcabi.http.response.RestResponse; +import io.github.artemget.entrys.Entry; +import io.github.artemget.entrys.EntryException; +import io.github.artemget.entrys.fake.EFake; +import io.github.artemget.entrys.json.EJsonStr; +import io.github.artemget.tagrelease.exception.DomainException; +import java.io.IOException; +import java.io.StringReader; +import javax.json.Json; + +/** + * Application's source code in gitlab. + * @since 0.1.0 + */ +@SuppressWarnings("PMD.AvoidFieldNameMatchingMethodName") +public final class ServiceEa implements Service { + /** + * Repository ID. + */ + private final String id; + + /** + * Repository name. + */ + private final String name; + + /** + * Last tag. + */ + private final Entry tag; + + public ServiceEa( + final Entry id, + final Entry name, + final Entry tag + ) throws EntryException, IOException { + this( + id.value(), + name.value(), + new EFake<>( + Yaml.createYamlInput( + new EJsonStr( + Json.createReader( + new StringReader( + tag.value().fetch().as(RestResponse.class).body() + ) + ).readObject() + , "content" + ).value() + ).readYamlMapping() + .yamlMapping("image") + .string("tag") + ) + ); + } + + /** + * Main ctor. + * @param name Of application + * @param tag Of application + */ + public ServiceEa(final String id, final String name, final Entry tag) { + this.id = id; + this.name = name; + this.tag = tag; + } + + @Override + public String id() { + return this.id; + } + + @Override + public String name() { + return this.name; + } + + @Override + public String tag() throws DomainException { + try { + return this.tag.value(); + } catch (final EntryException exception) { + throw new DomainException( + String.format("Failed ro fetch tag for service:'%s'", this.name()), + exception + ); + } + } +} diff --git a/src/main/java/io/github/artemget/tagrelease/domain/Services.java b/src/main/java/io/github/artemget/tagrelease/domain/Services.java index 183a171..bee47df 100644 --- a/src/main/java/io/github/artemget/tagrelease/domain/Services.java +++ b/src/main/java/io/github/artemget/tagrelease/domain/Services.java @@ -24,35 +24,31 @@ package io.github.artemget.tagrelease.domain; +import io.github.artemget.tagrelease.exception.DomainException; import java.util.List; -import java.util.stream.Collectors; import org.cactoos.Text; /** * Applications. - * * @since 0.1.0 */ public interface Services { /** * Returns all services from stand. - * * @return Services */ - List services(); + List services() throws DomainException; /** * Returns service from stand by it's name. - * * @param name Of service * @return Service */ - Service service(String name); + Service service(String name) throws DomainException; /** * Printed Services. * Format: ```java %s:%s```\n - * * @since 0.1.0 */ final class Printed implements Text { @@ -63,7 +59,6 @@ final class Printed implements Text { /** * Main ctor. - * * @param services Services */ public Printed(final Services services) { @@ -71,17 +66,12 @@ public Printed(final Services services) { } @Override - public String asString() { - return this.services.services().stream() - .map( - service -> - String.format( - "```java %s:%s```\n ", - service.name(), - service.tag() - ) - ) - .collect(Collectors.joining()); + public String asString() throws DomainException { + final StringBuilder string = new StringBuilder(); + for (final Service service : this.services.services()) { + string.append(String.format("```java %s:%s```\n", service.name(), service.tag())); + } + return string.toString(); } } } diff --git a/src/main/java/io/github/artemget/tagrelease/domain/ServicesAll.java b/src/main/java/io/github/artemget/tagrelease/domain/ServicesAll.java new file mode 100644 index 0000000..11646f8 --- /dev/null +++ b/src/main/java/io/github/artemget/tagrelease/domain/ServicesAll.java @@ -0,0 +1,123 @@ +/* + * MIT License + * + * Copyright (c) 2024-2025. Artem Getmanskii + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package io.github.artemget.tagrelease.domain; + +import com.jcabi.http.Request; +import com.jcabi.http.request.JdkRequest; +import io.github.artemget.entrys.Entry; +import io.github.artemget.entrys.EntryException; +import io.github.artemget.entrys.json.EJsonStr; +import io.github.artemget.tagrelease.entry.EFetchArr; +import io.github.artemget.tagrelease.entry.EFunc; +import io.github.artemget.tagrelease.exception.DomainException; +import java.util.ArrayList; +import java.util.List; +import javax.json.JsonArray; +import javax.json.JsonObject; +import javax.json.JsonValue; + +/** + * Fetch repositories(services) from gitlab project. + * @since 0.0.1 + */ +public final class ServicesAll implements Services { + private final EFunc named; + private final Entry all; + + public ServicesAll( + final Entry host, + final Entry project, + final Entry token + ) { + this( + (service) -> new EFetchArr( + new JdkRequest( + String.format("%s/api/v4/groups/%s/projects?search=%s", host.value(), project.value(), service) + ).method(Request.GET) + .header("Accept", "application/json") + .header("PRIVATE-TOKEN", token.value()) + ).value(), + () -> new EFetchArr( + new JdkRequest(String.format("%s/api/v4/groups/%s/projects", host.value(), project.value())) + .method(Request.GET) + .header("Accept", "application/json") + .header("PRIVATE-TOKEN", token.value()) + ).value() + ); + } + + public ServicesAll(final EFunc named, final Entry all) { + this.named = named; + this.all = all; + } + + @Override + public List services() throws DomainException { + final JsonArray response; + try { + response = this.all.value(); + } catch (final EntryException exception) { + throw new DomainException("Failed to fetch all services from gitlab", exception); + } + final List services = new ArrayList<>(); + for (final JsonValue service : response) { + services.add(this.parsed(service)); + } + return services; + } + + @Override + public Service service(String name) throws DomainException { + final JsonArray response; + try { + response = this.named.apply(name); + } catch (final EntryException exception) { + throw new DomainException( + String.format("Failed to fetch '%s' service from gitlab", name), + exception + ); + } + for (final JsonValue value : response) { + final Service service = this.parsed(value); + if (name.equals(service.name())) { + return service; + } + } + throw new DomainException(String.format("No service with name: %s", name)); + } + + private Service parsed(final JsonValue service) throws DomainException { + try { + JsonObject json = service.asJsonObject(); + return new ServiceEa( + String.valueOf(json.getInt("id")), + new EJsonStr(json, "name").value(), + () -> "" + ); + } catch (final EntryException | ClassCastException exception) { + throw new DomainException("Failed to parse service from gitlab", exception); + } + } +} diff --git a/src/main/java/io/github/artemget/tagrelease/domain/ServicesGl.java b/src/main/java/io/github/artemget/tagrelease/domain/ServicesGl.java new file mode 100644 index 0000000..5f60a51 --- /dev/null +++ b/src/main/java/io/github/artemget/tagrelease/domain/ServicesGl.java @@ -0,0 +1,166 @@ +/* + * MIT License + * + * Copyright (c) 2024-2025. Artem Getmanskii + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package io.github.artemget.tagrelease.domain; + +import com.amihaiemil.eoyaml.Yaml; +import com.amihaiemil.eoyaml.exceptions.YamlIndentationException; +import com.jcabi.http.Request; +import com.jcabi.http.request.JdkRequest; +import io.github.artemget.entrys.Entry; +import io.github.artemget.entrys.EntryException; +import io.github.artemget.entrys.json.EJsonStr; +import io.github.artemget.tagrelease.entry.EFetchArr; +import io.github.artemget.tagrelease.entry.EFetchObj; +import io.github.artemget.tagrelease.entry.EFunc; +import io.github.artemget.tagrelease.exception.DomainException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import javax.json.JsonArray; +import javax.json.JsonObject; +import javax.json.JsonValue; +import org.apache.commons.codec.binary.Base64; + +/** + * Applications from gitlab. + * @since 0.1.0 + */ +public final class ServicesGl implements Services { + /** + * List repository tree. Each directory with name not starting with _ prefix - is a service. + * GET /projects/:id/repository/tree + */ + private final Entry services; + + /** + * Get file from repository. Tag name placed under image:tag: in this file. + * GET /projects/:id/repository/files/:file_path + */ + private final EFunc tag; + + /** + * Ctor configures https req to gitlab services. + * @param url Of gitlab + * @param release Where to search services + * @param branch Project which services belong to + * @param token Api token + */ + public ServicesGl( + final Entry url, + final Entry release, + final Entry branch, + final Entry token + ) { + this(() -> new EFetchArr( + new JdkRequest( + String.format( + "%s/api/v4/projects/%s/repository/tree?ref=%s&per_page=100", + url.value(), release.value(), branch.value() + ) + ).method(Request.GET) + .header("Accept", "application/json") + .header("PRIVATE-TOKEN", token.value()) + ).value(), + (service) -> new EFetchObj( + new JdkRequest( + String.format( + "%s/api/v4/projects/%s/repository/files/%s?ref=%s", + url.value(), release.value(), service.concat("%2Fvalues.yaml"), branch.value() + ) + ).method(Request.GET) + .header("Accept", "application/json") + .header("PRIVATE-TOKEN", token.value()) + ).value() + ); + } + + /** + * Main ctor. + * @param services At gitlab + */ + public ServicesGl(final Entry services, final EFunc tag) { + this.services = services; + this.tag = tag; + } + + @Override + public List services() throws DomainException { + final JsonArray array; + try { + array = this.services.value(); + } catch (final EntryException exception) { + throw new DomainException("Failed to fetch services from stand", exception); + } + final List services = new ArrayList<>(); + for (final JsonValue dir : array) { + final JsonObject json = dir.asJsonObject(); + final String name = json.getString("name"); + final boolean directory = json.getString("type").equals("tree"); + final boolean service = !name.startsWith("_"); + if (directory && service) { + services.add(this.service(name)); + } + } + return services; + } + + @Override + public Service service(final String name) throws DomainException { + return new ServiceEa( + "", + name, + () -> { + final JsonObject json; + try { + json = this.tag.apply(name); + } catch (final EntryException exception) { + throw new EntryException( + String.format("Failed to fetch '%s' service from stand", name), + exception + ); + } + final String content; + try { + content = new String(Base64.decodeBase64(new EJsonStr(json, "content").value())); + } catch (final EntryException exception) { + throw new EntryException( + String.format("Failed to get content for service:'%s' from:'%s'", name, json), + exception + ); + } + try { + return Yaml.createYamlInput(content).readYamlMapping() + .yamlMapping("image") + .string("tag"); + } catch (final IOException | YamlIndentationException exception) { + throw new EntryException( + String.format("Failed to get content for service:'%s' from:'%s'", name, json), + exception + ); + } + } + ); + } +} diff --git a/src/main/java/io/github/artemget/tagrelease/domain/Stand.java b/src/main/java/io/github/artemget/tagrelease/domain/Stand.java index 66b445d..352b0db 100644 --- a/src/main/java/io/github/artemget/tagrelease/domain/Stand.java +++ b/src/main/java/io/github/artemget/tagrelease/domain/Stand.java @@ -24,6 +24,7 @@ package io.github.artemget.tagrelease.domain; +import io.github.artemget.tagrelease.exception.DomainException; import org.cactoos.Text; /** @@ -44,7 +45,7 @@ public interface Stand { * * @return Services */ - Services services(); + Services services() throws DomainException; /** * Printed server. @@ -71,7 +72,7 @@ public Printed(final Stand stand) { } @Override - public String asString() { + public String asString() throws DomainException { return String.format( """ Стенд: %s diff --git a/src/main/java/io/github/artemget/tagrelease/domain/ServiceGitlabEager.java b/src/main/java/io/github/artemget/tagrelease/domain/StandGl.java similarity index 58% rename from src/main/java/io/github/artemget/tagrelease/domain/ServiceGitlabEager.java rename to src/main/java/io/github/artemget/tagrelease/domain/StandGl.java index 91410ab..f2ca38d 100644 --- a/src/main/java/io/github/artemget/tagrelease/domain/ServiceGitlabEager.java +++ b/src/main/java/io/github/artemget/tagrelease/domain/StandGl.java @@ -24,35 +24,22 @@ package io.github.artemget.tagrelease.domain; -import io.github.artemget.tagrelease.exception.TagException; -import org.cactoos.Scalar; +import io.github.artemget.entrys.Entry; +import io.github.artemget.entrys.EntryException; +import io.github.artemget.tagrelease.exception.DomainException; /** - * Application's source code in gitlab. + * Stand for customer. * * @since 0.1.0 */ -@SuppressWarnings("PMD.AvoidFieldNameMatchingMethodName") -public final class ServiceGitlabEager implements Service { - /** - * Application name. - */ +public final class StandGl implements Stand { private final String name; + private final Entry services; - /** - * Application tag. - */ - private final String tag; - - /** - * Main ctor. - * - * @param name Of application - * @param tag Of application - */ - public ServiceGitlabEager(final String name, final String tag) { + public StandGl(final String name, final Entry services) { this.name = name; - this.tag = tag; + this.services = services; } @Override @@ -61,19 +48,12 @@ public String name() { } @Override - public String tag() { - return this.tag; - } - - // @checkstyle IllegalCatchCheck (50 lines) - @SuppressWarnings("PMD.AvoidCatchingGenericException") - @Override - public Service tagged(final Scalar rule) throws TagException { + public Services services() throws DomainException { try { - return new ServiceGitlabEager(this.name, rule.value()); - } catch (final Exception exception) { - throw new TagException( - String.format("Failed to create tag for service: %s", this.name), + return this.services.value(); + } catch (final EntryException exception) { + throw new DomainException( + String.format("Failed to fetch services from stand:'%s'", this.name), exception ); } diff --git a/src/main/java/io/github/artemget/tagrelease/domain/Stands.java b/src/main/java/io/github/artemget/tagrelease/domain/Stands.java index 78120af..628a115 100644 --- a/src/main/java/io/github/artemget/tagrelease/domain/Stands.java +++ b/src/main/java/io/github/artemget/tagrelease/domain/Stands.java @@ -24,6 +24,7 @@ package io.github.artemget.tagrelease.domain; +import io.github.artemget.tagrelease.exception.DomainException; import java.util.List; import java.util.stream.Collectors; import org.cactoos.Text; @@ -39,7 +40,7 @@ public interface Stands { * * @return Servers */ - List stands(); + List stands() throws DomainException; /** * Returns server by it's name. @@ -47,7 +48,7 @@ public interface Stands { * @param name Name * @return Server */ - Stand stand(String name); + Stand stand(String name) throws DomainException; /** * Printed servers. @@ -69,7 +70,7 @@ public Printed(final Stands stands) { } @Override - public String asString() { + public String asString() throws DomainException { return this.stands.stands().stream() .map(st -> new Stand.Printed(st).toString()) .collect(Collectors.joining()); diff --git a/src/main/java/io/github/artemget/tagrelease/domain/StandsGl.java b/src/main/java/io/github/artemget/tagrelease/domain/StandsGl.java new file mode 100644 index 0000000..4f38740 --- /dev/null +++ b/src/main/java/io/github/artemget/tagrelease/domain/StandsGl.java @@ -0,0 +1,133 @@ +/* + * MIT License + * + * Copyright (c) 2024-2025. Artem Getmanskii + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package io.github.artemget.tagrelease.domain; + +import com.jcabi.http.Request; +import com.jcabi.http.request.JdkRequest; +import io.github.artemget.entrys.Entry; +import io.github.artemget.entrys.EntryException; +import io.github.artemget.entrys.json.EJsonStr; +import io.github.artemget.tagrelease.entry.EFetchArr; +import io.github.artemget.tagrelease.entry.EFetchObj; +import io.github.artemget.tagrelease.entry.EFunc; +import io.github.artemget.tagrelease.exception.DomainException; +import java.util.ArrayList; +import java.util.List; +import javax.json.JsonArray; +import javax.json.JsonObject; +import javax.json.JsonValue; + +/** + * Servers. + * @since 0.1.0 + */ +public final class StandsGl implements Stands { + /** + * Gitlab request. List repository branches. + * GET /projects/:id/repository/branches + */ + private final Entry stands; + + /** + * Gitlab request. List repository branch by name. + * GET /projects/:id/repository/branches + */ + private final EFunc stand; + + private final EFunc services; + + public StandsGl( + final Entry url, + final Entry release, + final Entry token + ) { + this( + () -> new EFetchArr( + new JdkRequest( + String.format("%s/api/v4/projects/%s/repository/branches", url.value(), release.value()) + ).method(Request.GET) + .header("Accept", "application/json") + .header("PRIVATE-TOKEN", token.value()) + ).value(), + (name) -> new EFetchObj( + new JdkRequest( + String.format( + "%s/api/v4/projects/%s/repository/branches/%s", + url.value(), release.value(), name + ) + ).method(Request.GET) + .header("Accept", "application/json") + .header("PRIVATE-TOKEN", token.value()) + ).value(), + (branch) -> new ServicesGl(url, release, () -> branch, token) + ); + } + + public StandsGl( + final Entry stands, + final EFunc stand, + final EFunc services + ) { + this.stands = stands; + this.stand = stand; + this.services = services; + } + + @Override + public List stands() throws DomainException { + final JsonArray response; + try { + response = this.stands.value(); + } catch (final EntryException exception) { + throw new DomainException("Failed to fetch all services from gitlab", exception); + } + final List stands = new ArrayList<>(); + for (final JsonValue branch : response) { + final Stand stand = this.enriched(branch.asJsonObject()); + if (!"main".equals(stand.name())) { + stands.add(stand); + } + } + return stands; + } + + @Override + public Stand stand(final String name) throws DomainException { + try { + return this.enriched(this.stand.apply(name)); + } catch (final EntryException exception) { + throw new DomainException("Failed to fetch all services from gitlab", exception); + } + } + + private Stand enriched(final JsonObject service) throws DomainException { + try { + final String branch = new EJsonStr(service, "name").value(); + return new StandGl(branch, () -> this.services.apply(branch)); + } catch (final EntryException exception) { + throw new DomainException("Failed to parse service from gitlab", exception); + } + } +} diff --git a/src/main/java/io/github/artemget/tagrelease/domain/Tag.java b/src/main/java/io/github/artemget/tagrelease/domain/Tag.java new file mode 100644 index 0000000..288098f --- /dev/null +++ b/src/main/java/io/github/artemget/tagrelease/domain/Tag.java @@ -0,0 +1,65 @@ +/* + * MIT License + * + * Copyright (c) 2024-2025. Artem Getmanskii + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package io.github.artemget.tagrelease.domain; + +import java.util.List; +import org.cactoos.Text; +public interface Tag { + String repo(); + + String name(); + + String branch(); + + String fromCommit(); + + String message(); + + String created(); + + final class Printed implements Text { + /** + * Tags. + */ + private final List tags; + + /** + * Main ctor. + * @param tags Built + */ + public Printed(final List tags) { + this.tags = tags; + } + + @Override + public String asString() { + final StringBuilder string = new StringBuilder(); + for (final Tag tag : this.tags) { + string.append(String.format("```%s:%s```\n", tag.repo(), tag.name())); + } + return string.toString(); + } + } +} diff --git a/src/main/java/io/github/artemget/tagrelease/domain/TagEa.java b/src/main/java/io/github/artemget/tagrelease/domain/TagEa.java new file mode 100644 index 0000000..93da690 --- /dev/null +++ b/src/main/java/io/github/artemget/tagrelease/domain/TagEa.java @@ -0,0 +1,80 @@ +/* + * MIT License + * + * Copyright (c) 2024-2025. Artem Getmanskii + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package io.github.artemget.tagrelease.domain; + +public final class TagEa implements Tag { + private final String repo; + private final String name; + private final String branch; + private final String commit; + private final String message; + private final String created; + + public TagEa( + final String repo, + final String name, + final String branch, + final String commit, + final String message, + final String created + ) { + this.repo = repo; + this.name = name; + this.branch = branch; + this.commit = commit; + this.message = message; + this.created = created; + } + + @Override + public String repo() { + return this.repo; + } + + @Override + public String name() { + return this.name; + } + + @Override + public String branch() { + return this.branch; + } + + @Override + public String fromCommit() { + return this.commit; + } + + @Override + public String message() { + return this.message; + } + + @Override + public String created() { + return this.created; + } +} diff --git a/src/main/java/io/github/artemget/tagrelease/domain/StandGitlab.java b/src/main/java/io/github/artemget/tagrelease/domain/Tags.java similarity index 79% rename from src/main/java/io/github/artemget/tagrelease/domain/StandGitlab.java rename to src/main/java/io/github/artemget/tagrelease/domain/Tags.java index f8135c4..54d4a06 100644 --- a/src/main/java/io/github/artemget/tagrelease/domain/StandGitlab.java +++ b/src/main/java/io/github/artemget/tagrelease/domain/Tags.java @@ -24,19 +24,9 @@ package io.github.artemget.tagrelease.domain; -/** - * Server at gitlab. - * - * @since 0.1.0 - */ -public final class StandGitlab implements Stand { - @Override - public String name() { - throw new UnsupportedOperationException(); - } +import io.github.artemget.tagrelease.exception.DomainException; +public interface Tags { + Tag buildNew(final String serviceId, final String branch, final String prefix) throws DomainException; - @Override - public Services services() { - throw new UnsupportedOperationException(); - } + Tag current(final String serviceId, final String branch, final String prefix) throws DomainException; } diff --git a/src/main/java/io/github/artemget/tagrelease/domain/TagsGl.java b/src/main/java/io/github/artemget/tagrelease/domain/TagsGl.java new file mode 100644 index 0000000..26e5cf0 --- /dev/null +++ b/src/main/java/io/github/artemget/tagrelease/domain/TagsGl.java @@ -0,0 +1,237 @@ +/* + * MIT License + * + * Copyright (c) 2024-2025. Artem Getmanskii + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package io.github.artemget.tagrelease.domain; + +import com.jcabi.http.Request; +import com.jcabi.http.request.JdkRequest; +import io.github.artemget.entrys.Entry; +import io.github.artemget.entrys.EntryException; +import io.github.artemget.tagrelease.entry.EFetchArr; +import io.github.artemget.tagrelease.entry.EFetchObj; +import io.github.artemget.tagrelease.entry.EFunc; +import io.github.artemget.tagrelease.exception.DomainException; +import javax.json.JsonArray; +import javax.json.JsonObject; +import javax.json.JsonValue; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class TagsGl implements Tags { + private final Logger log = LoggerFactory.getLogger(TagsGl.class); + private final EFunc tag; + private final EFunc create; + private final EFunc message; + + public TagsGl( + final Entry url, + final Entry token + ) { + this( + (tag) -> + new EFetchArr( + new JdkRequest( + String.format( + "%s/api/v4/projects/%s/repository/tags?search=%%5E%s", + url.value(), + tag.repo(), + tag.name().replace(".*", "") + ) + ).method(Request.GET) + .header("Accept", "application/json") + .header("PRIVATE-TOKEN", token.value()) + ).value(), + (tag) -> + new EFetchObj( + new JdkRequest( + String.format( + "%s/api/v4/projects/%s/repository/tags?ref=%s&tag_name=%s", + url.value(), + tag.repo(), + tag.branch(), + tag.name() + ) + ).method(Request.POST) + .header("Accept", "application/json") + .header("PRIVATE-TOKEN", token.value()) + ).value(), + (tag) -> { + final StringBuilder message = new StringBuilder(); + int page = 1; + boolean isFound = false; + while (page <= 5 && !isFound) { + for (final JsonValue value : TagsGl.mrs(tag, url.value(), token.value(), String.valueOf(page))) { + final JsonObject mr = value.asJsonObject(); + final boolean isSquash = !mr.isNull("squash_commit_sha") + && tag.fromCommit().equals(mr.getString("squash_commit_sha")); + final boolean isMerge = !mr.isNull("merge_commit_sha") + && tag.fromCommit().equals(mr.getString("merge_commit_sha")); + final boolean isMr = !mr.isNull("sha") + && tag.fromCommit().equals(mr.getString("sha")); + if (isMerge && isSquash && isMr) { + isFound = true; + break; + } + message.append(mr.getString("title")).append("\n\n"); + page++; + } + } + return message.toString(); + } + ); + } + + public TagsGl( + final EFunc tag, + final EFunc create, + EFunc message + ) { + this.tag = tag; + this.create = create; + this.message = message; + } + + @Override + public Tag buildNew(final String serviceId, final String branch, final String prefix) throws + DomainException { + final Tag current = this.current(serviceId, branch, prefix); + final JsonObject created; + try { + created = this.create.apply( + new TagEa( + current.repo(), + TagsGl.next(current.name(), prefix), + current.branch(), + current.fromCommit(), + current.message(), + current.created() + ) + ); + } catch (final EntryException exception) { + throw new DomainException( + String.format( + "Failed to create tag with prefix:'%s' for service:'%s' from branch:'%s'. Latest tag:%s", + prefix, serviceId, branch, current.name() + ), + exception + ); + } + final JsonObject commit = created.getJsonObject("commit"); + String message; + try { + message = this.message.apply(current); + } catch (final EntryException exception) { + message = ""; + log.error( + "Failed to fetch merge requests for service:'{}', branch:'{}'. Between tags '{}' - '{}'", + serviceId, + branch, + current.name(), + created.getString("name"), + exception + ); + } + return new TagEa( + serviceId, + created.getString("name"), + branch, + commit.getString("id"), + message, + commit.getString("created_at") + ); + } + + @Override + public Tag current(final String serviceId, final String branch, final String prefix) throws + DomainException { + final JsonArray response; + try { + response = this.tag.apply(new TagEa(serviceId, prefix, branch, "", "", "")); + } catch (final EntryException exception) { + throw new DomainException( + String.format( + "Failed to fetch tag with prefix:'%s' for service:'%s' from branch:'%s'", + prefix, serviceId, branch + ), + exception + ); + } + //TODO: add branch check + final JsonObject current = response.getJsonObject(0); + final JsonObject commit = current.getJsonObject("commit"); + return new TagEa( + serviceId, + current.getString("name"), + branch, + commit.getString("id"), + commit.getString("message"), + commit.getString("created_at") + ); + } + + public static String next(final String current, final String prefix) { + final String trimmed = prefix.replace("*", ""); + final StringBuilder next = new StringBuilder(); + final int length = current.toCharArray().length; + for (int index = 0; index < length; index++) { + if (trimmed.length() == index) { + final StringBuilder digit = new StringBuilder(); + while (index < length && Character.isDigit(current.charAt(index))) { + digit.append(current.charAt(index)); + index++; + } + next.append(Integer.parseInt(digit.toString()) + 1); + if (index < length) { + next.append(current.charAt(index)); + } + continue; + } + if (trimmed.length() < index && Character.isDigit(current.charAt(index))) { + next.append('0'); + } else { + next.append(current.charAt(index)); + } + } + return next.toString(); + } + + private static JsonArray mrs(final Tag tag, String url, String token, String page) throws + EntryException { + return new EFetchArr( + new JdkRequest( + String.format( + "%s/api/v4/projects/%s/merge_requests?state=merged&scope=all&created_after=%s&target_branch=%s&per_page=%s&page=%s", + url, + tag.repo(), + tag.created(), + tag.branch(), + "20", + page + ) + ).method(Request.POST) + .header("Accept", "application/json") + .header("PRIVATE-TOKEN", token) + ).value(); + } +} diff --git a/src/main/java/io/github/artemget/tagrelease/entry/EFetchArr.java b/src/main/java/io/github/artemget/tagrelease/entry/EFetchArr.java new file mode 100644 index 0000000..d8c4d17 --- /dev/null +++ b/src/main/java/io/github/artemget/tagrelease/entry/EFetchArr.java @@ -0,0 +1,52 @@ +/* + * MIT License + * + * Copyright (c) 2024-2025. Artem Getmanskii + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package io.github.artemget.tagrelease.entry; + +import com.jcabi.http.Request; +import io.github.artemget.entrys.Entry; +import io.github.artemget.entrys.EntryException; +import javax.json.JsonArray; +import javax.json.JsonStructure; + +public class EFetchArr implements Entry { + private final Entry origin; + + public EFetchArr(final Request request) { + this(new EFetchJson(request)); + } + + public EFetchArr(final Entry origin) { + this.origin = origin; + } + + @Override + public JsonArray value() throws EntryException { + try { + return this.origin.value().asJsonArray(); + } catch (final ClassCastException exception) { + throw new EntryException("Failed to map json structure to JsonArray", exception); + } + } +} diff --git a/src/main/java/io/github/artemget/tagrelease/entry/EFetchJson.java b/src/main/java/io/github/artemget/tagrelease/entry/EFetchJson.java new file mode 100644 index 0000000..0a9948c --- /dev/null +++ b/src/main/java/io/github/artemget/tagrelease/entry/EFetchJson.java @@ -0,0 +1,53 @@ +/* + * MIT License + * + * Copyright (c) 2024-2025. Artem Getmanskii + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package io.github.artemget.tagrelease.entry; + +import com.jcabi.http.Request; +import io.github.artemget.entrys.Entry; +import io.github.artemget.entrys.EntryException; +import java.io.IOException; +import java.io.StringReader; +import javax.json.Json; +import javax.json.JsonStructure; + +public class EFetchJson implements Entry { + private final Request request; + + public EFetchJson(final Request request) { + this.request = request; + } + + @Override + public JsonStructure value() throws EntryException { + try { + return Json.createReader(new StringReader(request.fetch().body())).read(); + } catch (final IOException exception) { + throw new EntryException( + String.format("Failed to fetch json array from resource:%s", this.request.uri()), + exception + ); + } + } +} diff --git a/src/main/java/io/github/artemget/tagrelease/entry/EFetchObj.java b/src/main/java/io/github/artemget/tagrelease/entry/EFetchObj.java new file mode 100644 index 0000000..6effbf5 --- /dev/null +++ b/src/main/java/io/github/artemget/tagrelease/entry/EFetchObj.java @@ -0,0 +1,52 @@ +/* + * MIT License + * + * Copyright (c) 2024-2025. Artem Getmanskii + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package io.github.artemget.tagrelease.entry; + +import com.jcabi.http.Request; +import io.github.artemget.entrys.Entry; +import io.github.artemget.entrys.EntryException; +import javax.json.JsonObject; +import javax.json.JsonStructure; + +public class EFetchObj implements Entry { + private final Entry origin; + + public EFetchObj(final Request request) { + this(new EFetchJson(request)); + } + + public EFetchObj(final Entry origin) { + this.origin = origin; + } + + @Override + public JsonObject value() throws EntryException { + try { + return this.origin.value().asJsonObject(); + } catch (final ClassCastException exception) { + throw new EntryException("Failed to map json structure to JsonObject", exception); + } + } +} diff --git a/src/main/java/io/github/artemget/tagrelease/domain/ServicesGitlab.java b/src/main/java/io/github/artemget/tagrelease/entry/EFunc.java similarity index 74% rename from src/main/java/io/github/artemget/tagrelease/domain/ServicesGitlab.java rename to src/main/java/io/github/artemget/tagrelease/entry/EFunc.java index 7ab47fb..7117bf1 100644 --- a/src/main/java/io/github/artemget/tagrelease/domain/ServicesGitlab.java +++ b/src/main/java/io/github/artemget/tagrelease/entry/EFunc.java @@ -22,23 +22,12 @@ * SOFTWARE. */ -package io.github.artemget.tagrelease.domain; +package io.github.artemget.tagrelease.entry; -import java.util.List; - -/** - * Applications from gitlab. - * - * @since 0.1.0 - */ -public final class ServicesGitlab implements Services { - @Override - public List services() { - throw new UnsupportedOperationException(); - } +import io.github.artemget.entrys.EntryException; +import org.cactoos.Func; +public interface EFunc extends Func { @Override - public Service service(final String name) { - throw new UnsupportedOperationException(); - } + Y apply(X x) throws EntryException; } diff --git a/src/main/java/io/github/artemget/tagrelease/entry/ESplit.java b/src/main/java/io/github/artemget/tagrelease/entry/ESplit.java deleted file mode 100644 index 2b343fb..0000000 --- a/src/main/java/io/github/artemget/tagrelease/entry/ESplit.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2024-2025. Artem Getmanskii - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package io.github.artemget.tagrelease.entry; - -import io.github.artemget.entrys.Entry; -import io.github.artemget.entrys.EntryException; -import java.util.Set; -import java.util.regex.PatternSyntaxException; - -/** - * Split entry. - * - * @since 0.1.0 - */ -public final class ESplit implements Entry> { - /** - * Origin string entry. - */ - private final Entry origin; - - /** - * Split delimiter. - */ - private final String delimiter; - - /** - * Creates split entry with default ; delimiter. - * - * @param origin Entry - */ - public ESplit(final Entry origin) { - this(origin, ";"); - } - - /** - * Main ctor. - * - * @param origin Entry - * @param delimiter For splitting - */ - public ESplit(final Entry origin, final String delimiter) { - this.origin = origin; - this.delimiter = delimiter; - } - - @Override - public Set value() throws EntryException { - final String value = this.origin.value(); - try { - return Set.of(value.split(this.delimiter)); - } catch (final PatternSyntaxException exception) { - throw new EntryException( - String.format( - "Wrong pattern delimiter: %s for entry value: %s", - this.delimiter, - value - ), - exception - ); - } - } -} diff --git a/src/main/java/io/github/artemget/tagrelease/exception/TagException.java b/src/main/java/io/github/artemget/tagrelease/exception/DomainException.java similarity index 84% rename from src/main/java/io/github/artemget/tagrelease/exception/TagException.java rename to src/main/java/io/github/artemget/tagrelease/exception/DomainException.java index 56d0123..0847524 100644 --- a/src/main/java/io/github/artemget/tagrelease/exception/TagException.java +++ b/src/main/java/io/github/artemget/tagrelease/exception/DomainException.java @@ -27,23 +27,23 @@ import java.io.Serial; /** - * Throws at build/get tag. + * Throws at domain logic error. * * @since 0.1.0 */ -public class TagException extends Exception { +public class DomainException extends Exception { @Serial private static final long serialVersionUID = 4172661814037122452L; - public TagException(final String message) { + public DomainException(final String message) { super(message); } - public TagException(final String message, final Throwable cause) { + public DomainException(final String message, final Throwable cause) { super(message, cause); } - public TagException(final Throwable cause) { + public DomainException(final Throwable cause) { super(cause); } } diff --git a/src/main/java/io/github/artemget/tagrelease/match/MatchAdmin.java b/src/main/java/io/github/artemget/tagrelease/match/MatchAdmin.java index 2eae29d..d05f98d 100644 --- a/src/main/java/io/github/artemget/tagrelease/match/MatchAdmin.java +++ b/src/main/java/io/github/artemget/tagrelease/match/MatchAdmin.java @@ -28,7 +28,7 @@ import io.github.artemget.entrys.EntryException; import io.github.artemget.entrys.EntryExceptionUnchecked; import io.github.artemget.teleroute.update.Wrap; -import java.util.Set; +import java.util.List; import java.util.function.Predicate; import org.telegram.telegrambots.meta.api.objects.Update; @@ -41,14 +41,14 @@ public final class MatchAdmin implements Predicate> { /** * Admin ids. */ - private final Entry> admins; + private final Entry> admins; /** * Main ctor. * * @param admins Of bot. */ - public MatchAdmin(final Entry> admins) { + public MatchAdmin(final Entry> admins) { this.admins = admins; } diff --git a/src/main/java/io/github/artemget/tagrelease/match/MatchChats.java b/src/main/java/io/github/artemget/tagrelease/match/MatchChats.java index 4684eb1..d167122 100644 --- a/src/main/java/io/github/artemget/tagrelease/match/MatchChats.java +++ b/src/main/java/io/github/artemget/tagrelease/match/MatchChats.java @@ -28,7 +28,7 @@ import io.github.artemget.entrys.EntryException; import io.github.artemget.entrys.EntryExceptionUnchecked; import io.github.artemget.teleroute.update.Wrap; -import java.util.Set; +import java.util.List; import java.util.function.Predicate; import org.telegram.telegrambots.meta.api.objects.Update; @@ -41,14 +41,14 @@ public final class MatchChats implements Predicate> { /** * Chat ids. */ - private final Entry> chats; + private final Entry> chats; /** * Main ctor. * * @param chats Where bot is used. */ - public MatchChats(final Entry> chats) { + public MatchChats(final Entry> chats) { this.chats = chats; } diff --git a/src/main/java/io/github/artemget/tagrelease/domain/StandsGitlab.java b/src/main/java/io/github/artemget/tagrelease/match/MatchReply.java similarity index 76% rename from src/main/java/io/github/artemget/tagrelease/domain/StandsGitlab.java rename to src/main/java/io/github/artemget/tagrelease/match/MatchReply.java index e5fd2ec..13948c1 100644 --- a/src/main/java/io/github/artemget/tagrelease/domain/StandsGitlab.java +++ b/src/main/java/io/github/artemget/tagrelease/match/MatchReply.java @@ -22,23 +22,14 @@ * SOFTWARE. */ -package io.github.artemget.tagrelease.domain; - -import java.util.List; - -/** - * Servers. - * - * @since 0.1.0 - */ -public final class StandsGitlab implements Stands { - @Override - public List stands() { - throw new UnsupportedOperationException(); - } +package io.github.artemget.tagrelease.match; +import io.github.artemget.teleroute.update.Wrap; +import java.util.function.Predicate; +import org.telegram.telegrambots.meta.api.objects.Update; +public class MatchReply implements Predicate> { @Override - public Stand stand(final String name) { - throw new UnsupportedOperationException(); + public boolean test(Wrap wrap) { + return wrap.src().getMessage().getReplyToMessage() != null; } } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml new file mode 100644 index 0000000..5d3ba01 --- /dev/null +++ b/src/main/resources/application.yaml @@ -0,0 +1,25 @@ +#Telegram bot settings +bot: + #Bot name. Provided by botfather. + name: ${BOT_NAME} + #Bot token. Provided by botfather. + #Example: 1234567890:AAxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + token: ${BOT_TOKEN} + #Users that could use bot. Array of userid in format id_1;id_2;id3. + #Example: -123;-234;-345 + admins: ${BOT_ADMINS} +#Source code management settings +provider: + #SCM auth token to make requests. Must have permission to read all project's repos(tags,pull-requests,commits etc), + #create tags and push commits to ci/cd repo. + #Example:glpat-xxxxxxxxxxxxxxxxxxxx + token: ${PROVIDER_TOKEN} + #SCM host, with protocol. Only http:// is supported for now. + #Example:http://gitlab.blabla123.dev.ts/ + host: ${PROVIDER_HOST} + #ID of project in which all repos is placed. Int value. + #Example: 123 + project: ${PROJECT_ID} + #ID of repo where all stands are placed. + #Example: 123 + release: ${RELEASE_ID} diff --git a/src/test/java/io/github/artemget/tagrelease/domain/TagsGlTest.java b/src/test/java/io/github/artemget/tagrelease/domain/TagsGlTest.java new file mode 100644 index 0000000..d6ecad8 --- /dev/null +++ b/src/test/java/io/github/artemget/tagrelease/domain/TagsGlTest.java @@ -0,0 +1,111 @@ +/* + * MIT License + * + * Copyright (c) 2024-2025. Artem Getmanskii + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package io.github.artemget.tagrelease.domain; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class TagsGlTest { + + @Test + void returnsLastIncremented() { + Assertions.assertEquals( + "4.3.1.1", + TagsGl.next("4.3.1.0", "4.3.1.*") + ); + } + + @Test + void returnsLastIncrementedWithStarting() { + Assertions.assertEquals( + "v4.3.1.1", + TagsGl.next("v4.3.1.0", "v4.3.1.*") + ); + } + + @Test + void returnsLastIncrementedWithVersionAndTrailingText() { + Assertions.assertEquals( + "v4.3.1.1_bla_bla", + TagsGl.next("v4.3.1.0_bla_bla", "v4.3.1.*") + ); + } + + @Test + void returnsLastIncrementedOverPlaceNumber() { + Assertions.assertEquals( + "4.3.1.11", + TagsGl.next("4.3.1.10", "4.3.1.*") + ); + } + + @Test + void returnsThirdIncremented() { + Assertions.assertEquals( + "4.3.2.0", + TagsGl.next("4.3.1.0", "4.3.*") + ); + } + + @Test + void returnsThirdIncrementedOverPlaceNumber() { + Assertions.assertEquals( + "4.3.11.0", + TagsGl.next("4.3.10.0", "4.3.*") + ); + } + + @Test + void returnsSecondIncremented() { + Assertions.assertEquals( + "4.4.0.0", + TagsGl.next("4.3.1.0", "4.*") + ); + } + + @Test + void returnsFirstIncremented() { + Assertions.assertEquals( + "5.0.0.0", + TagsGl.next("4.3.1.0", "*") + ); + } + + @Test + void returnsFirstIncrementedOver() { + Assertions.assertEquals( + "10.0.0.0", + TagsGl.next("9.3.1.0", "*") + ); + } + + @Test + void returnsFirstIncrementedOverPlaceNumber() { + Assertions.assertEquals( + "11.0.0.0", + TagsGl.next("10.3.1.0", "*") + ); + } +}