Skip to content

Commit

Permalink
[FEATURE]: add validation of properties
Browse files Browse the repository at this point in the history
  • Loading branch information
Drednote committed Oct 5, 2024
1 parent 071751b commit 1c28b9b
Show file tree
Hide file tree
Showing 10 changed files with 157 additions and 36 deletions.
34 changes: 20 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ public class MainController {
return "You sent message with types %s".formatted(request.getMessageTypes());
}

@TelegramMessage("My name is {name}")
@TelegramMessage("My name is {name:.*}")
public String onPattern(@TelegramPatternVariable("name") String name) {
return "Hello " + name;
}
Expand Down Expand Up @@ -267,7 +267,7 @@ public class MainController {
return "You sent message with types %s".formatted(request.getMessageTypes());
}

@TelegramMessage("My name is {name}")
@TelegramMessage("My name is {name:.*}")
public String onPattern(@TelegramPatternVariable("name") String name) {
return "Hello " + name;
}
Expand Down Expand Up @@ -329,6 +329,7 @@ define and customize the behavior of your scenarios.
Here example of a configuring scenario, for additional info you can see javadocs.

```java

@Configuration
@RequiredArgsConstructor
public class ScenarioConfig extends ScenarioConfigurerAdapter<Enum<?>> {
Expand All @@ -346,7 +347,7 @@ public class ScenarioConfig extends ScenarioConfigurerAdapter<Enum<?>> {
.source(State.INITIAL).target(State.TEST)
.telegramRequest(command("/test"))
.action(context -> "Test")

.and().withRollback()
.source(ASSISTANT_CHOICE).target(GET_SETTINGS)
.telegramRequest(callbackQuery(SettingsKeyboardButton.GET_CURRENT))
Expand Down Expand Up @@ -395,15 +396,13 @@ and after they are processed.
the main [Update Handling](#update-handling)
- `PostUpdateFilter` - **spring beans** that implement this interface will be called **after**
the main [Update Handling](#update-handling)
- `ConclusivePostUpdateFilter` - **spring beans** that implement this interface will be called *
*after**
the response is sent to telegram. [see](#response-processing)

- Also, for convenience, two interfaces are created. First one - `PriorityPreUpdateFilter` is
implemented
from `PreUpdateFilter` and take precedence over `PreUpdateFilter` and is executed earlier whatever
returns
**getPreOrder()**/**getPostOrder()**.
Second one - `ConclusivePostUpdateFilter` is super to `PreUpdateFilter`, and is executed later
whatever returns
**getPreOrder()**/**getPostOrder()**.
- Also, for convenience, one interface are created. First one - `PriorityPreUpdateFilter` is
implemented from `PreUpdateFilter` and take precedence over `PreUpdateFilter` and is executed
earlier whatever returns **getPreOrder()**.

- To add a filter, you need to create a **spring bean** that will implement the `PreUpdateFilter`
or `PostUpdateFilter` interface.
Expand Down Expand Up @@ -469,6 +468,12 @@ handle this, there is a component called **Response Processing**, which follows
simple words will do `objectMapper.writeValueAsString(response)`
- For more information on wrapping rules, see the `ResponseSetter` and `GenericTelegramResponse`
classes

> The exception is three types of response - `Collection<?>`, `Stream<?>`, `Flux<?>`. For handling
> these types of response are created three additional implementations
> of `TelegramResponse` - `CompositeTelegramResponse`, `FluxTelegramResponse`
> and `StreamTelegramResponse`
- You can create any implementation of `TelegramResponse` for sending response
- Any custom code can be written in `TelegramResponse`, but I strongly recommend using this
interface only for sending a response to **Telegram**
Expand Down Expand Up @@ -501,7 +506,7 @@ To provide maximum flexibility when calling custom code through the reflection m
parameters for any method are calculated dynamically according to the following rules:

> If you need to add support for your custom argument, you need to create **spring bean** and
> implement `HandlerMethodArgumentResolver` interface
> implement `io.github.drednote.telegram.core.resolver.HandlerMethodArgumentResolver` interface
#### Java types

Expand Down Expand Up @@ -555,9 +560,10 @@ created for each update processing.
different databases (postgres, mongo, etc.), using the implementations of `DataSourceAdapter`
interface
- If you want to add support for a database that currently is not supported, you should to
create entity and create repository extending `PermissionRepository` or `ScenarioRepository`
create entity and create repository extending `PermissionRepository`, `ScenarioRepository`
or `ScenarioIdRepository`

> **Currently supported `JpaRepository` and `MongoRepository`**
> **Currently supported `JpaRepository`**
> Note: To enable auto scan for jpa entities, you should manually pick main interfaces for entities
> and use `@EntityScan` annotation. To create spring data repository, you need to just implement one
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;

@Configuration
Expand All @@ -26,12 +27,14 @@ public class TelegramProperties {
* <p>
* <b>Required</b>
*/
@NonNull
private String name;
/**
* The token of a bot.
* <p>
* <b>Required</b>
*/
@NonNull
private String token;
/**
* The default locale with which bot will send responses to user chats. A two-letter ISO 639-1
Expand All @@ -46,17 +49,21 @@ public class TelegramProperties {
/**
* Session properties
*/
@NonNull
private SessionProperties session = new SessionProperties();
/**
* Properties of update handlers
*/
@NonNull
private UpdateHandlerProperties updateHandler = new UpdateHandlerProperties();
/**
* Filters properties
*/
@NonNull
private FilterProperties filters = new FilterProperties();
/**
* Menu properties
*/
@NonNull
private MenuProperties menu = new MenuProperties();
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.NonNull;

/**
* Properties for filters
Expand All @@ -28,18 +29,21 @@ public class FilterProperties {
*
* @see AccessPermissionFilter
*/
@NonNull
private PermissionProperties permission = new PermissionProperties();
/**
* How often each user can perform requests to bot. 0 = no rules
*
* @see UserRateLimitRequestFilter
*/
@NonNull
private long userRateLimit = 0L;
/**
* The {@link ChronoUnit} which will be applied to {@link #userRateLimit}
*
* @see UserRateLimitRequestFilter
*/
@NonNull
private ChronoUnit userRateLimitUnit = ChronoUnit.SECONDS;
/**
* How long cache with rate limit bucket will not expire. This parameter needed just for delete
Expand All @@ -50,19 +54,22 @@ public class FilterProperties {
* {@code #userRateLimit}
* @see UserRateLimitRequestFilter
*/
@NonNull
private long userRateLimitCacheExpire = 1L;
/**
* The {@link ChronoUnit} which will be applied to {@link #userRateLimitCacheExpire}
*
* @see UserRateLimitRequestFilter
*/
@NonNull
private ChronoUnit userRateLimitCacheExpireUnit = ChronoUnit.HOURS;
/**
* If at the end of update handling and post filtering, the response is null, set
* {@link NotHandledTelegramResponse} as response
*
* @see NotHandledUpdateFilter
*/
@NonNull
private boolean setDefaultAnswer = true;

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.NonNull;

@Configuration
@ConfigurationProperties("drednote.telegram.update-handler")
Expand All @@ -27,20 +28,23 @@ public class UpdateHandlerProperties {
* @see TelegramRequest
* @see ControllerUpdateHandler
*/
@NonNull
private boolean controllerEnabled = true;
/**
* Enabled scenario update handling
*
* @see ScenarioConfigurerAdapter
* @see ScenarioUpdateHandler
*/
@NonNull
private boolean scenarioEnabled = true;
/**
* If exception is occurred and no handler has processed it, set
* {@link InternalErrorTelegramResponse} as response
*
* @see DefaultExceptionHandler
*/
@NonNull
private boolean setDefaultErrorAnswer = true;
/**
* By default, java pojo objects will be serialized with Jackson to json in
Expand All @@ -49,11 +53,13 @@ public class UpdateHandlerProperties {
*
* @see GenericTelegramResponse
*/
@NonNull
private boolean serializeJavaObjectWithJackson = true;
/**
* If scenario is enabled and {@link SessionProperties#getMaxThreadsPerUser} is set value other
* than 1, throws an error with a warning about using scenario safe only when
* getMaxThreadsPerUser is set to 1.
*/
@NonNull
private boolean enabledWarningForScenario = true;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.github.drednote.telegram.menu;

import io.github.drednote.telegram.menu.DefaultBotMenu.CommandKey;
import io.github.drednote.telegram.utils.Assert;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
Expand All @@ -11,6 +12,7 @@
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.telegram.telegrambots.meta.api.objects.commands.scope.BotCommandScope;
import org.telegram.telegrambots.meta.api.objects.commands.scope.BotCommandScopeAllChatAdministrators;
Expand All @@ -37,6 +39,7 @@ public class MenuProperties {
/**
* Send policy
*/
@NonNull
private SendPolicy sendPolicy = SendPolicy.ON_STARTUP;

@Getter
Expand All @@ -47,18 +50,21 @@ public static class CommandCls {
/**
* Text for the button. Example: Registration
*/
@NonNull
private String text;

/**
* Command for the button. Example: /register
*/
@NonNull
private String command;
/**
* Field describing the scope of users for which the commands are relevant. Defaults to
* {@link ScopeCommand#DEFAULT}.
*
* @see ScopeCommand
*/
@NonNull
private Set<ScopeCommand> scopes = Set.of(ScopeCommand.DEFAULT);
/**
* A two-letter ISO 639-1 language code. If empty, commands will be applied to all users
Expand All @@ -72,6 +78,7 @@ public static class CommandCls {
* Unique identifier of the target user to who apply commands. Only applicable if
* {@link #scopes} equals to {@link ScopeCommand#CHAT_MEMBER}
*/
@NonNull
private Set<Long> userIds = Set.of();
/**
* Unique identifier for the target chat or username of the target supergroup (in the format
Expand All @@ -80,10 +87,12 @@ public static class CommandCls {
* Only applicable if {@link #scopes} equals to {@link ScopeCommand#CHAT_MEMBER},
* {@link ScopeCommand#CHAT_ADMINISTRATORS} or {@link ScopeCommand#CHAT}
*/
@NonNull
private Set<Long> chatIds = Set.of();

public void setCommand(@Nullable String command) {
if (command != null && !command.isBlank()) {
Assert.notNull(command, "command");
if (!command.isBlank()) {
this.command = (command.startsWith("/") ? "" : "/") + command;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,42 @@
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;
import reactor.core.publisher.Flux;

/**
* Represents a response that processes a Flux of Telegram updates.
* <p>
* Note: It is recommended to avoid using this class directly. Instead, if you need to execute
* {@code Flux} of {@code TelegramResponse} instances, you can return a {@code Flux} of
* {@code TelegramResponse} directly from your handler method.
*
* @author Ivan Galushko
*/
public class FluxTelegramResponse extends AbstractTelegramResponse {

private final Flux<Object> response;
@Nullable
private TelegramApiException exception = null;

/**
* Constructs a {@code FluxTelegramResponse} with the specified response object.
*
* @param response the response object; must be an instance of {@code Flux}
* @throws IllegalArgumentException if {@code response} is null or if {@code response} is not an
* instance of {@code Flux}
*/
public FluxTelegramResponse(Object response) {
Assert.required(response, "response");
if (!(response instanceof Flux)) {
throw new IllegalArgumentException("This class work only with a Flux");
throw new IllegalArgumentException("This class works only with a Flux");
}
this.response = (Flux<Object>) response;
}

/**
* Processes the specified update request using the Flux of updates.
*
* @param request the update request to process; must not be null
* @throws TelegramApiException if a processing error occurs
*/
@Override
public void process(UpdateRequest request) throws TelegramApiException {
try {
Expand All @@ -46,12 +68,21 @@ public void process(UpdateRequest request) throws TelegramApiException {
}
}

/**
* Exception thrown during Flux processing.
*/
private static class FluxException extends RuntimeException {

TelegramApiException exception;

/**
* Constructs a {@code FluxException} with the specified Telegram API exception.
*
* @param exception the Telegram API exception that caused the Flux to fail; must not be
* null
*/
public FluxException(TelegramApiException exception) {
this.exception = exception;
}
}
}
}
Loading

0 comments on commit 1c28b9b

Please sign in to comment.