diff --git a/api/src/main/java/run/halo/app/content/comment/CommentSubject.java b/api/src/main/java/run/halo/app/content/comment/CommentSubject.java index cc3a5abcfc..f1706235e6 100644 --- a/api/src/main/java/run/halo/app/content/comment/CommentSubject.java +++ b/api/src/main/java/run/halo/app/content/comment/CommentSubject.java @@ -11,9 +11,16 @@ * @author guqing * @since 2.0.0 */ -public interface CommentSubject extends ExtensionPoint { +public interface CommentSubject extends ExtensionPoint { Mono get(String name); + default Mono getSubjectDisplay(String name) { + return Mono.empty(); + } + boolean supports(Ref ref); + + record SubjectDisplay(String title, String url, String kindName) { + } } diff --git a/api/src/main/java/run/halo/app/core/extension/notification/Notification.java b/api/src/main/java/run/halo/app/core/extension/notification/Notification.java new file mode 100644 index 0000000000..b10f08d56c --- /dev/null +++ b/api/src/main/java/run/halo/app/core/extension/notification/Notification.java @@ -0,0 +1,58 @@ +package run.halo.app.core.extension.notification; + +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.Instant; +import lombok.Data; +import lombok.EqualsAndHashCode; +import run.halo.app.extension.AbstractExtension; +import run.halo.app.extension.GVK; + +/** + *

{@link Notification} is a custom extension that used to store notification information for + * inner use, it's on-site notification.

+ * + *

Supports the following operations:

+ *
    + *
  • Marked as read: {@link NotificationSpec#setUnread(boolean)}
  • + *
  • Get the last read time: {@link NotificationSpec#getLastReadAt()}
  • + *
  • Filter by recipient: {@link NotificationSpec#getRecipient()}
  • + *
+ * + * @author guqing + * @see Reason + * @see ReasonType + * @since 2.10.0 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@GVK(group = "notification.halo.run", version = "v1alpha1", kind = "Notification", plural = + "notifications", singular = "notification") +public class Notification extends AbstractExtension { + + @Schema + private NotificationSpec spec; + + @Data + public static class NotificationSpec { + @Schema(requiredMode = REQUIRED, minLength = 1, description = "The name of user") + private String recipient; + + @Schema(requiredMode = REQUIRED, minLength = 1, description = "The name of reason") + private String reason; + + @Schema(requiredMode = REQUIRED, minLength = 1) + private String title; + + @Schema(requiredMode = REQUIRED) + private String rawContent; + + @Schema(requiredMode = REQUIRED) + private String htmlContent; + + private boolean unread; + + private Instant lastReadAt; + } +} diff --git a/api/src/main/java/run/halo/app/core/extension/notification/NotificationTemplate.java b/api/src/main/java/run/halo/app/core/extension/notification/NotificationTemplate.java new file mode 100644 index 0000000000..a7b0c8fd5a --- /dev/null +++ b/api/src/main/java/run/halo/app/core/extension/notification/NotificationTemplate.java @@ -0,0 +1,59 @@ +package run.halo.app.core.extension.notification; + +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import run.halo.app.extension.AbstractExtension; +import run.halo.app.extension.GVK; + +/** + *

{@link NotificationTemplate} is a custom extension that defines a notification template.

+ *

It describes the notification template's name, description, and the template content.

+ *

{@link Spec#getReasonSelector()} is used to select the template by reasonType and language, + * if multiple templates are matched, the best match will be selected. This is useful when you + * want to override the default template.

+ * + * @author guqing + * @since 2.10.0 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@GVK(group = "notification.halo.run", version = "v1alpha1", kind = "NotificationTemplate", + plural = "notificationtemplates", singular = "notificationtemplate") +public class NotificationTemplate extends AbstractExtension { + + @Schema + private Spec spec; + + @Data + @Schema(name = "NotificationTemplateSpec") + public static class Spec { + @Schema + private ReasonSelector reasonSelector; + + @Schema + private Template template; + } + + @Data + @Schema(name = "TemplateContent") + public static class Template { + @Schema(requiredMode = REQUIRED, minLength = 1) + private String title; + + private String htmlBody; + + private String rawBody; + } + + @Data + public static class ReasonSelector { + @Schema(requiredMode = REQUIRED, minLength = 1) + private String reasonType; + + @Schema(requiredMode = REQUIRED, minLength = 1, defaultValue = "default") + private String language; + } +} diff --git a/api/src/main/java/run/halo/app/core/extension/notification/NotifierDescriptor.java b/api/src/main/java/run/halo/app/core/extension/notification/NotifierDescriptor.java new file mode 100644 index 0000000000..e7adb3fcfd --- /dev/null +++ b/api/src/main/java/run/halo/app/core/extension/notification/NotifierDescriptor.java @@ -0,0 +1,54 @@ +package run.halo.app.core.extension.notification; + +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import run.halo.app.extension.AbstractExtension; +import run.halo.app.extension.GVK; + +/** + *

{@link NotifierDescriptor} is a custom extension that defines a notifier.

+ *

It describes the notifier's name, description, and the extension name of the notifier to + * let the user know what the notifier is and what it can do in the UI and also let the + * {@code NotificationCenter} know how to load the notifier and prepare the notifier's settings.

+ * + * @author guqing + * @since 2.10.0 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@GVK(group = "notification.halo.run", version = "v1alpha1", kind = "NotifierDescriptor", + plural = "notifierDescriptors", singular = "notifierDescriptor") +public class NotifierDescriptor extends AbstractExtension { + + @Schema + private Spec spec; + + @Data + @Schema(name = "NotifierDescriptorSpec") + public static class Spec { + @Schema(requiredMode = REQUIRED, minLength = 1) + private String displayName; + + private String description; + + @Schema(requiredMode = REQUIRED, minLength = 1) + private String notifierExtName; + + private SettingRef senderSettingRef; + + private SettingRef receiverSettingRef; + } + + @Data + @Schema(name = "NotifierSettingRef") + public static class SettingRef { + @Schema(requiredMode = REQUIRED) + private String name; + + @Schema(requiredMode = REQUIRED) + private String group; + } +} diff --git a/api/src/main/java/run/halo/app/core/extension/notification/Reason.java b/api/src/main/java/run/halo/app/core/extension/notification/Reason.java new file mode 100644 index 0000000000..db033859ee --- /dev/null +++ b/api/src/main/java/run/halo/app/core/extension/notification/Reason.java @@ -0,0 +1,70 @@ +package run.halo.app.core.extension.notification; + +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import run.halo.app.extension.AbstractExtension; +import run.halo.app.extension.GVK; +import run.halo.app.notification.ReasonAttributes; + +/** + *

{@link Reason} is a custom extension that defines a reason for a notification, It represents + * an instance of a {@link ReasonType}.

+ *

It can be understood as an event that triggers a notification.

+ * + * @author guqing + * @since 2.10.0 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@GVK(group = "notification.halo.run", version = "v1alpha1", kind = "Reason", plural = + "reasons", singular = "reason") +public class Reason extends AbstractExtension { + + @Schema + private Spec spec; + + @Data + @Schema(name = "ReasonSpec") + public static class Spec { + @Schema(requiredMode = REQUIRED) + private String reasonType; + + @Schema(requiredMode = REQUIRED) + private Subject subject; + + @Schema(requiredMode = REQUIRED) + private String author; + + @Schema(implementation = ReasonAttributes.class, requiredMode = NOT_REQUIRED, + description = "Attributes used to transfer data") + private ReasonAttributes attributes; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Schema(name = "ReasonSubject") + public static class Subject { + @Schema(requiredMode = REQUIRED) + private String apiVersion; + + @Schema(requiredMode = REQUIRED) + private String kind; + + @Schema(requiredMode = REQUIRED) + private String name; + + @Schema(requiredMode = REQUIRED) + private String title; + + private String url; + } +} diff --git a/api/src/main/java/run/halo/app/core/extension/notification/ReasonType.java b/api/src/main/java/run/halo/app/core/extension/notification/ReasonType.java new file mode 100644 index 0000000000..222819f019 --- /dev/null +++ b/api/src/main/java/run/halo/app/core/extension/notification/ReasonType.java @@ -0,0 +1,58 @@ +package run.halo.app.core.extension.notification; + +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; +import lombok.Data; +import lombok.EqualsAndHashCode; +import run.halo.app.extension.AbstractExtension; +import run.halo.app.extension.GVK; + +/** + *

{@link ReasonType} is a custom extension that defines a type of reason.

+ *

One {@link ReasonType} can have multiple {@link Reason}s to notify.

+ * + * @author guqing + * @see NotificationTemplate + * @see Reason + * @since 2.10.0 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@GVK(group = "notification.halo.run", version = "v1alpha1", kind = "ReasonType", + plural = "reasontypes", singular = "reasontype") +public class ReasonType extends AbstractExtension { + public static final String LOCALIZED_RESOURCE_NAME_ANNO = + "notification.halo.run/localized-resource-name"; + + @Schema + private Spec spec; + + @Data + @Schema(name = "ReasonTypeSpec") + public static class Spec { + + @Schema(requiredMode = REQUIRED, minLength = 1) + private String displayName; + + @Schema(requiredMode = REQUIRED, minLength = 1) + private String description; + + private List properties; + } + + @Data + public static class ReasonProperty { + @Schema(requiredMode = REQUIRED, minLength = 1) + private String name; + + @Schema(requiredMode = REQUIRED, minLength = 1) + private String type; + + private String description; + + @Schema(defaultValue = "false") + private boolean optional; + } +} diff --git a/api/src/main/java/run/halo/app/core/extension/notification/Subscription.java b/api/src/main/java/run/halo/app/core/extension/notification/Subscription.java new file mode 100644 index 0000000000..f2e998ab94 --- /dev/null +++ b/api/src/main/java/run/halo/app/core/extension/notification/Subscription.java @@ -0,0 +1,102 @@ +package run.halo.app.core.extension.notification; + +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED; +import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; +import static org.apache.commons.lang3.StringUtils.defaultString; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.UUID; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; +import run.halo.app.extension.AbstractExtension; +import run.halo.app.extension.GVK; + +/** + *

{@link Subscription} is a custom extension that defines a subscriber to be notified when a + * certain {@link Reason} is triggered.

+ *

It holds a {@link Subscriber} to the user to be notified, a {@link InterestReason} to + * subscribe to.

+ * + * @author guqing + * @since 2.10.0 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@GVK(group = "notification.halo.run", version = "v1alpha1", kind = "Subscription", + plural = "subscriptions", singular = "subscription") +public class Subscription extends AbstractExtension { + + @Schema + private Spec spec; + + @Data + @Schema(name = "SubscriptionSpec") + public static class Spec { + @Schema(requiredMode = REQUIRED, description = "The subscriber to be notified") + private Subscriber subscriber; + + @Schema(requiredMode = REQUIRED, description = "The token to unsubscribe") + private String unsubscribeToken; + + @Schema(requiredMode = REQUIRED, description = "The reason to be interested in") + private InterestReason reason; + + @Schema(description = "Perhaps users need to unsubscribe and " + + "interact without receiving notifications again") + private boolean disabled; + } + + @Data + public static class InterestReason { + @Schema(requiredMode = REQUIRED, description = "The name of the reason definition to be " + + "interested in") + private String reasonType; + + @Schema(requiredMode = REQUIRED, description = "The subject name of reason type to be" + + " interested in") + private ReasonSubject subject; + } + + @Data + @Builder + @AllArgsConstructor + @NoArgsConstructor + @Schema(name = "InterestReasonSubject") + public static class ReasonSubject { + + @Schema(requiredMode = NOT_REQUIRED, description = "if name is not specified, it presents " + + "all subjects of the specified reason type and custom resources") + private String name; + + @Schema(requiredMode = REQUIRED, minLength = 1) + private String apiVersion; + + @Schema(requiredMode = REQUIRED, minLength = 1) + private String kind; + + @Override + public String toString() { + return kind + "#" + apiVersion + "/" + defaultString(name); + } + } + + @Data + @ToString + @Schema(name = "SubscriptionSubscriber") + public static class Subscriber { + private String name; + } + + /** + * Generate unsubscribe token for unsubscribe. + * + * @return unsubscribe token + */ + public static String generateUnsubscribeToken() { + return UUID.randomUUID().toString(); + } +} diff --git a/api/src/main/java/run/halo/app/extension/Comparators.java b/api/src/main/java/run/halo/app/extension/Comparators.java index d375a3160b..c03e5e5384 100644 --- a/api/src/main/java/run/halo/app/extension/Comparators.java +++ b/api/src/main/java/run/halo/app/extension/Comparators.java @@ -17,6 +17,12 @@ public static Comparator compareName(boolean asc) { return asc ? comparator : comparator.reversed(); } + public static Comparator defaultComparator() { + Comparator comparator = compareCreationTimestamp(false); + comparator = comparator.thenComparing(compareName(true)); + return comparator; + } + /** * Get a nulls comparator. * diff --git a/api/src/main/java/run/halo/app/extension/MetadataUtil.java b/api/src/main/java/run/halo/app/extension/MetadataUtil.java index 088639662f..9670786a0d 100644 --- a/api/src/main/java/run/halo/app/extension/MetadataUtil.java +++ b/api/src/main/java/run/halo/app/extension/MetadataUtil.java @@ -7,6 +7,8 @@ public enum MetadataUtil { ; + public static final String SYSTEM_FINALIZER = "system-protection"; + /** * Gets extension metadata labels null safe. * diff --git a/application/src/main/java/run/halo/app/infra/AnonymousUserConst.java b/api/src/main/java/run/halo/app/infra/AnonymousUserConst.java similarity index 100% rename from application/src/main/java/run/halo/app/infra/AnonymousUserConst.java rename to api/src/main/java/run/halo/app/infra/AnonymousUserConst.java diff --git a/api/src/main/java/run/halo/app/infra/ExternalLinkProcessor.java b/api/src/main/java/run/halo/app/infra/ExternalLinkProcessor.java new file mode 100644 index 0000000000..ea653c709b --- /dev/null +++ b/api/src/main/java/run/halo/app/infra/ExternalLinkProcessor.java @@ -0,0 +1,20 @@ +package run.halo.app.infra; + +/** + * {@link ExternalLinkProcessor} to process an in-site link to an external link. + * + * @author guqing + * @see ExternalUrlSupplier + * @since 2.9.0 + */ +public interface ExternalLinkProcessor { + + /** + * If the link is in-site link, then process it to an external link with + * {@link ExternalUrlSupplier#getRaw()}, otherwise return the original link. + * + * @param link link to process + * @return processed link or original link + */ + String processLink(String link); +} diff --git a/api/src/main/java/run/halo/app/notification/NotificationCenter.java b/api/src/main/java/run/halo/app/notification/NotificationCenter.java new file mode 100644 index 0000000000..dbd8de71a1 --- /dev/null +++ b/api/src/main/java/run/halo/app/notification/NotificationCenter.java @@ -0,0 +1,46 @@ +package run.halo.app.notification; + +import reactor.core.publisher.Mono; +import run.halo.app.core.extension.notification.Reason; +import run.halo.app.core.extension.notification.Subscription; + +/** + * Notification center to notify and manage notifications. + * + * @author guqing + * @since 2.10.0 + */ +public interface NotificationCenter { + + /** + * Notifies the subscriber with the given reason. + * + * @param reason reason to notify + */ + Mono notify(Reason reason); + + /** + * Subscribes to the given subject with the given reason. + * + * @param subscriber subscriber to subscribe to + * @param reason interest reason to subscribe + * @return a subscription + */ + Mono subscribe(Subscription.Subscriber subscriber, + Subscription.InterestReason reason); + + /** + * Unsubscribes by the given subject. + * + * @param subscriber subscriber to unsubscribe + */ + Mono unsubscribe(Subscription.Subscriber subscriber); + + /** + * Unsubscribes by the given subject and reason. + * + * @param subscriber subscriber to unsubscribe + * @param reason reason to unsubscribe + */ + Mono unsubscribe(Subscription.Subscriber subscriber, Subscription.InterestReason reason); +} diff --git a/api/src/main/java/run/halo/app/notification/NotificationContext.java b/api/src/main/java/run/halo/app/notification/NotificationContext.java new file mode 100644 index 0000000000..5550dfd34e --- /dev/null +++ b/api/src/main/java/run/halo/app/notification/NotificationContext.java @@ -0,0 +1,48 @@ +package run.halo.app.notification; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.Instant; +import lombok.Builder; +import lombok.Data; + +@Data +public class NotificationContext { + + private Message message; + + private ObjectNode receiverConfig; + + private ObjectNode senderConfig; + + @Data + public static class Message { + private MessagePayload payload; + + private Subject subject; + + private String recipient; + + private Instant timestamp; + } + + @Data + @Builder + public static class Subject { + private String apiVersion; + private String kind; + private String name; + private String title; + private String url; + } + + @Data + public static class MessagePayload { + private String title; + + private String rawBody; + + private String htmlBody; + + private ReasonAttributes attributes; + } +} diff --git a/api/src/main/java/run/halo/app/notification/NotificationReasonEmitter.java b/api/src/main/java/run/halo/app/notification/NotificationReasonEmitter.java new file mode 100644 index 0000000000..5c3551f68c --- /dev/null +++ b/api/src/main/java/run/halo/app/notification/NotificationReasonEmitter.java @@ -0,0 +1,22 @@ +package run.halo.app.notification; + +import java.util.function.Consumer; +import reactor.core.publisher.Mono; +import run.halo.app.core.extension.notification.Reason; + +/** + * {@link NotificationReasonEmitter} to emit notification reason. + * + * @author guqing + * @since 2.10.0 + */ +public interface NotificationReasonEmitter { + + /** + * Emit a {@link Reason} with {@link ReasonPayload}. + * + * @param reasonType reason type to emitter must not be blank + * @param reasonData reason data must not be null + */ + Mono emit(String reasonType, Consumer reasonData); +} diff --git a/api/src/main/java/run/halo/app/notification/ReactiveNotifier.java b/api/src/main/java/run/halo/app/notification/ReactiveNotifier.java new file mode 100644 index 0000000000..9093bb3990 --- /dev/null +++ b/api/src/main/java/run/halo/app/notification/ReactiveNotifier.java @@ -0,0 +1,20 @@ +package run.halo.app.notification; + +import org.pf4j.ExtensionPoint; +import reactor.core.publisher.Mono; + +/** + * Notifier to notify user. + * + * @author guqing + * @since 2.10.0 + */ +public interface ReactiveNotifier extends ExtensionPoint { + + /** + * Notify user. + * + * @param context notification context must not be null + */ + Mono notify(NotificationContext context); +} diff --git a/api/src/main/java/run/halo/app/notification/ReasonAttributes.java b/api/src/main/java/run/halo/app/notification/ReasonAttributes.java new file mode 100644 index 0000000000..da42f93f22 --- /dev/null +++ b/api/src/main/java/run/halo/app/notification/ReasonAttributes.java @@ -0,0 +1,13 @@ +package run.halo.app.notification; + +import java.util.HashMap; + +/** + *

{@link ReasonAttributes} is a map that stores the attributes of the reason.

+ * + * @author guqing + * @since 2.10.0 + */ +public class ReasonAttributes extends HashMap { + +} diff --git a/api/src/main/java/run/halo/app/notification/ReasonPayload.java b/api/src/main/java/run/halo/app/notification/ReasonPayload.java new file mode 100644 index 0000000000..fcc711b8d8 --- /dev/null +++ b/api/src/main/java/run/halo/app/notification/ReasonPayload.java @@ -0,0 +1,60 @@ +package run.halo.app.notification; + +import java.util.HashMap; +import java.util.Map; +import lombok.AllArgsConstructor; +import lombok.Data; +import run.halo.app.core.extension.notification.Reason; + +/** + * A value object to hold reason payload. + * + * @author guqing + * @see Reason + * @since 2.10.0 + */ +@Data +@AllArgsConstructor +public class ReasonPayload { + private Reason.Subject subject; + private final UserIdentity author; + private Map attributes; + + public static ReasonPayloadBuilder builder() { + return new ReasonPayloadBuilder(); + } + + public static class ReasonPayloadBuilder { + private Reason.Subject subject; + private UserIdentity author; + private final Map attributes; + + ReasonPayloadBuilder() { + this.attributes = new HashMap<>(); + } + + public ReasonPayloadBuilder subject(Reason.Subject subject) { + this.subject = subject; + return this; + } + + public ReasonPayloadBuilder attribute(String key, Object value) { + this.attributes.put(key, value); + return this; + } + + public ReasonPayloadBuilder attributes(Map attributes) { + this.attributes.putAll(attributes); + return this; + } + + public ReasonPayloadBuilder author(UserIdentity author) { + this.author = author; + return this; + } + + public ReasonPayload build() { + return new ReasonPayload(subject, author, attributes); + } + } +} \ No newline at end of file diff --git a/api/src/main/java/run/halo/app/notification/UserIdentity.java b/api/src/main/java/run/halo/app/notification/UserIdentity.java new file mode 100644 index 0000000000..b9c1efb74e --- /dev/null +++ b/api/src/main/java/run/halo/app/notification/UserIdentity.java @@ -0,0 +1,57 @@ +package run.halo.app.notification; + +import java.util.Optional; +import org.apache.commons.lang3.StringUtils; +import org.springframework.util.Assert; +import run.halo.app.infra.AnonymousUserConst; + +/** + * Identity for user. + * + * @author guqing + * @since 2.10.0 + */ +public record UserIdentity(String name) { + public static final String SEPARATOR = "#"; + + /** + * Create identity with username to identify a user. + * + * @param username username + * @return identity + */ + public static UserIdentity of(String username) { + return new UserIdentity(username); + } + + /** + *

Create identity with email to identify a user, + * the name will be {@code anonymousUser#email}.

+ *

An anonymous user can not be identified by username so we use email to identify it.

+ * + * @param email email + * @return identity + */ + public static UserIdentity anonymousWithEmail(String email) { + Assert.notNull(email, "Email must not be null"); + String name = AnonymousUserConst.PRINCIPAL + SEPARATOR + email; + return of(name); + } + + public boolean isAnonymous() { + return name().startsWith(AnonymousUserConst.PRINCIPAL + SEPARATOR); + } + + /** + * Gets email if the identity is an anonymous user. + * + * @return email if the identity is an anonymous user, otherwise empty + */ + public Optional getEmail() { + if (isAnonymous()) { + return Optional.of(name().substring(name().indexOf(SEPARATOR) + 1)) + .filter(StringUtils::isNotBlank); + } + return Optional.empty(); + } +} diff --git a/application/build.gradle b/application/build.gradle index c64abd253d..97c0e93025 100644 --- a/application/build.gradle +++ b/application/build.gradle @@ -77,7 +77,8 @@ tasks.register('downloadPluginPresets', Download) { 'https://github.com/halo-dev/plugin-comment-widget/releases/download/v1.8.0/plugin-comment-widget-1.8.0.jar', 'https://github.com/halo-dev/plugin-search-widget/releases/download/v1.2.0/plugin-search-widget-1.2.0.jar', 'https://github.com/halo-dev/plugin-sitemap/releases/download/v1.1.1/plugin-sitemap-1.1.1.jar', - 'https://github.com/halo-dev/plugin-feed/releases/download/v1.2.0/plugin-feed-1.2.0.jar' + 'https://github.com/halo-dev/plugin-feed/releases/download/v1.2.0/plugin-feed-1.2.0.jar', + 'https://github.com/halo-dev/plugin-app-store/releases/download/v1.0.0-beta.1/plugin-app-store-1.0.0-beta.1.jar' ]) dest 'src/main/resources/presets/plugins' } diff --git a/application/src/main/java/run/halo/app/content/NotificationReasonConst.java b/application/src/main/java/run/halo/app/content/NotificationReasonConst.java new file mode 100644 index 0000000000..dbc1f3b8e8 --- /dev/null +++ b/application/src/main/java/run/halo/app/content/NotificationReasonConst.java @@ -0,0 +1,14 @@ +package run.halo.app.content; + +/** + * Notification reason constants for content module. + * + * @author guqing + * @since 2.9.0 + */ +public enum NotificationReasonConst { + ; + public static final String NEW_COMMENT_ON_POST = "new-comment-on-post"; + public static final String NEW_COMMENT_ON_PAGE = "new-comment-on-single-page"; + public static final String SOMEONE_REPLIED_TO_YOU = "someone-replied-to-you"; +} diff --git a/application/src/main/java/run/halo/app/content/comment/CommentNotificationReasonPublisher.java b/application/src/main/java/run/halo/app/content/comment/CommentNotificationReasonPublisher.java new file mode 100644 index 0000000000..07f61c0e47 --- /dev/null +++ b/application/src/main/java/run/halo/app/content/comment/CommentNotificationReasonPublisher.java @@ -0,0 +1,343 @@ +package run.halo.app.content.comment; + +import static org.apache.commons.lang3.StringUtils.defaultIfBlank; + +import com.fasterxml.jackson.core.type.TypeReference; +import java.util.Map; +import java.util.Optional; +import lombok.Builder; +import lombok.RequiredArgsConstructor; +import lombok.experimental.UtilityClass; +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.util.Assert; +import run.halo.app.content.NotificationReasonConst; +import run.halo.app.core.extension.User; +import run.halo.app.core.extension.content.Comment; +import run.halo.app.core.extension.content.Post; +import run.halo.app.core.extension.content.Reply; +import run.halo.app.core.extension.content.SinglePage; +import run.halo.app.core.extension.notification.Reason; +import run.halo.app.core.extension.notification.Subscription; +import run.halo.app.event.post.CommentCreatedEvent; +import run.halo.app.event.post.ReplyCreatedEvent; +import run.halo.app.extension.ExtensionClient; +import run.halo.app.extension.GroupVersionKind; +import run.halo.app.extension.Ref; +import run.halo.app.infra.ExternalLinkProcessor; +import run.halo.app.infra.utils.JsonUtils; +import run.halo.app.notification.NotificationReasonEmitter; +import run.halo.app.notification.UserIdentity; +import run.halo.app.plugin.ExtensionComponentsFinder; +import run.halo.app.plugin.extensionpoint.ExtensionGetter; + +/** + * Notification reason publisher for {@link Comment} and {@link Reply}. + * + * @author guqing + * @since 2.9.0 + */ +@Component +@RequiredArgsConstructor +public class CommentNotificationReasonPublisher { + private static final GroupVersionKind POST_GVK = GroupVersionKind.fromExtension(Post.class); + private static final GroupVersionKind PAGE_GVK = + GroupVersionKind.fromExtension(SinglePage.class); + + private final ExtensionClient client; + private final NewCommentOnPostReasonPublisher newCommentOnPostReasonPublisher; + private final NewCommentOnPageReasonPublisher newCommentOnPageReasonPublisher; + private final NewReplyReasonPublisher newReplyReasonPublisher; + + /** + * On new comment. + */ + @Async + @EventListener(CommentCreatedEvent.class) + public void onNewComment(CommentCreatedEvent event) { + Comment comment = event.getComment(); + if (isPostComment(comment)) { + newCommentOnPostReasonPublisher.publishReasonBy(comment); + } else if (isPageComment(comment)) { + newCommentOnPageReasonPublisher.publishReasonBy(comment); + } + } + + /** + * On new reply. + */ + @Async + @EventListener(ReplyCreatedEvent.class) + public void onNewReply(ReplyCreatedEvent event) { + Reply reply = event.getReply(); + var commentName = reply.getSpec().getCommentName(); + client.fetch(Comment.class, commentName) + .ifPresent(comment -> newReplyReasonPublisher.publishReasonBy(reply, comment)); + } + + boolean isPostComment(Comment comment) { + return Ref.groupKindEquals(comment.getSpec().getSubjectRef(), POST_GVK); + } + + boolean isPageComment(Comment comment) { + return Ref.groupKindEquals(comment.getSpec().getSubjectRef(), PAGE_GVK); + } + + @Component + @RequiredArgsConstructor + static class NewCommentOnPostReasonPublisher { + + private final ExtensionClient client; + private final NotificationReasonEmitter notificationReasonEmitter; + private final ExternalLinkProcessor externalLinkProcessor; + + public void publishReasonBy(Comment comment) { + Ref subjectRef = comment.getSpec().getSubjectRef(); + Post post = client.fetch(Post.class, subjectRef.getName()).orElseThrow(); + if (doNotEmitReason(comment, post)) { + return; + } + + String postUrl = + externalLinkProcessor.processLink(post.getStatusOrDefault().getPermalink()); + var reasonSubject = Reason.Subject.builder() + .apiVersion(post.getApiVersion()) + .kind(post.getKind()) + .name(subjectRef.getName()) + .title(post.getSpec().getTitle()) + .url(postUrl) + .build(); + Comment.CommentOwner owner = comment.getSpec().getOwner(); + notificationReasonEmitter.emit(NotificationReasonConst.NEW_COMMENT_ON_POST, + builder -> { + var attributes = CommentOnPostReasonData.builder() + .postName(subjectRef.getName()) + .postTitle(post.getSpec().getTitle()) + .postUrl(postUrl) + .commenter(owner.getDisplayName()) + .content(comment.getSpec().getContent()) + .commentName(comment.getMetadata().getName()) + .build(); + builder.attributes(ReasonDataConverter.toAttributeMap(attributes)) + .author(identityFrom(owner)) + .subject(reasonSubject); + }).block(); + } + + boolean doNotEmitReason(Comment comment, Post post) { + Comment.CommentOwner commentOwner = comment.getSpec().getOwner(); + return isPostOwner(post, commentOwner); + } + + boolean isPostOwner(Post post, Comment.CommentOwner commentOwner) { + String kind = commentOwner.getKind(); + String name = commentOwner.getName(); + var postOwner = post.getSpec().getOwner(); + if (Comment.CommentOwner.KIND_EMAIL.equals(kind)) { + return client.fetch(User.class, postOwner) + .filter(user -> name.equals(user.getSpec().getEmail())) + .isPresent(); + } + return name.equals(postOwner); + } + + @Builder + record CommentOnPostReasonData(String postName, String postTitle, String postUrl, + String commenter, String content, String commentName) { + } + } + + @Component + @RequiredArgsConstructor + static class NewCommentOnPageReasonPublisher { + private final ExtensionClient client; + private final NotificationReasonEmitter notificationReasonEmitter; + private final ExternalLinkProcessor externalLinkProcessor; + + public void publishReasonBy(Comment comment) { + Ref subjectRef = comment.getSpec().getSubjectRef(); + var singlePage = client.fetch(SinglePage.class, subjectRef.getName()).orElseThrow(); + + if (doNotEmitReason(comment, singlePage)) { + return; + } + + var pageUrl = externalLinkProcessor + .processLink(singlePage.getStatusOrDefault().getPermalink()); + + var reasonSubject = Reason.Subject.builder() + .apiVersion(singlePage.getApiVersion()) + .kind(singlePage.getKind()) + .name(subjectRef.getName()) + .title(singlePage.getSpec().getTitle()) + .url(pageUrl) + .build(); + + Comment.CommentOwner owner = comment.getSpec().getOwner(); + notificationReasonEmitter.emit(NotificationReasonConst.NEW_COMMENT_ON_PAGE, + builder -> { + var attributes = CommentOnPageReasonData.builder() + .pageName(subjectRef.getName()) + .pageTitle(singlePage.getSpec().getTitle()) + .pageUrl(pageUrl) + .commenter(defaultIfBlank(owner.getDisplayName(), owner.getName())) + .content(comment.getSpec().getContent()) + .commentName(comment.getMetadata().getName()) + .build(); + builder.attributes(ReasonDataConverter.toAttributeMap(attributes)) + .author(identityFrom(owner)) + .subject(reasonSubject); + }).block(); + } + + public boolean doNotEmitReason(Comment comment, SinglePage page) { + Comment.CommentOwner commentOwner = comment.getSpec().getOwner(); + return isPageOwner(page, commentOwner); + } + + boolean isPageOwner(SinglePage page, Comment.CommentOwner commentOwner) { + String kind = commentOwner.getKind(); + String name = commentOwner.getName(); + var pageOwner = page.getSpec().getOwner(); + if (Comment.CommentOwner.KIND_EMAIL.equals(kind)) { + return client.fetch(User.class, pageOwner) + .filter(user -> name.equals(user.getSpec().getEmail())) + .isPresent(); + } + return name.equals(pageOwner); + } + + @Builder + record CommentOnPageReasonData(String pageName, String pageTitle, String pageUrl, + String commenter, String content, String commentName) { + } + } + + @UtilityClass + static class ReasonDataConverter { + public static Map toAttributeMap(T data) { + Assert.notNull(data, "Reason attributes must not be null"); + return JsonUtils.mapper().convertValue(data, new TypeReference<>() { + }); + } + } + + static UserIdentity identityFrom(Comment.CommentOwner owner) { + if (Comment.CommentOwner.KIND_EMAIL.equals(owner.getKind())) { + return UserIdentity.anonymousWithEmail(owner.getName()); + } + return UserIdentity.of(owner.getName()); + } + + @Component + @RequiredArgsConstructor + static class NewReplyReasonPublisher { + private final ExtensionClient client; + private final NotificationReasonEmitter notificationReasonEmitter; + private final ExtensionComponentsFinder extensionComponentsFinder; + + public void publishReasonBy(Reply reply, Comment comment) { + boolean isQuoteReply = StringUtils.isNotBlank(reply.getSpec().getQuoteReply()); + + Optional quoteReplyOptional = Optional.of(isQuoteReply) + .filter(Boolean::booleanValue) + .flatMap(isQuote -> client.fetch(Reply.class, reply.getSpec().getQuoteReply())); + + if (doNotEmitReason(reply, quoteReplyOptional.orElse(null), comment)) { + return; + } + + var reasonSubject = quoteReplyOptional + .map(quoteReply -> Subscription.ReasonSubject.builder() + .apiVersion(quoteReply.getApiVersion()) + .kind(quoteReply.getKind()) + .name(quoteReply.getMetadata().getName()) + .build() + ) + .orElseGet(() -> Subscription.ReasonSubject.builder() + .apiVersion(comment.getApiVersion()) + .kind(comment.getKind()) + .name(comment.getMetadata().getName()) + .build() + ); + + var reasonSubjectTitle = quoteReplyOptional + .map(quoteReply -> quoteReply.getSpec().getContent()) + .orElse(comment.getSpec().getContent()); + + var quoteReplyContent = quoteReplyOptional + .map(quoteReply -> quoteReply.getSpec().getContent()) + .orElse(null); + var replyOwner = reply.getSpec().getOwner(); + + var reasonAttributesBuilder = NewReplyReasonData.builder() + .commentContent(comment.getSpec().getContent()) + .isQuoteReply(isQuoteReply) + .quoteContent(quoteReplyContent) + .commentName(comment.getMetadata().getName()) + .replier(defaultIfBlank(replyOwner.getDisplayName(), replyOwner.getName())) + .content(reply.getSpec().getContent()) + .replyName(reply.getMetadata().getName()); + + getCommentSubjectDisplay(comment.getSpec().getSubjectRef()) + .ifPresent(subject -> { + reasonAttributesBuilder.commentSubjectTitle(subject.title()); + reasonAttributesBuilder.commentSubjectUrl(subject.url()); + }); + + notificationReasonEmitter.emit(NotificationReasonConst.SOMEONE_REPLIED_TO_YOU, + builder -> { + var data = ReasonDataConverter.toAttributeMap(reasonAttributesBuilder.build()); + builder.attributes(data) + .author(identityFrom(replyOwner)) + .subject(Reason.Subject.builder() + .apiVersion(reasonSubject.getApiVersion()) + .kind(reasonSubject.getKind()) + .name(reasonSubject.getName()) + .title(reasonSubjectTitle) + .build()); + }).block(); + } + + /** + * To be compatible with older versions, it may be empty, so use optional. + * TODO use {@link ExtensionGetter} instead of {@code extensionComponentsFinder} + */ + @SuppressWarnings("unchecked") + Optional getCommentSubjectDisplay(Ref ref) { + return extensionComponentsFinder.getExtensions(CommentSubject.class) + .stream() + .filter(commentSubject -> commentSubject.supports(ref)) + .findFirst() + .flatMap(commentSubject + -> commentSubject.getSubjectDisplay(ref.getName()).blockOptional()); + } + + boolean doNotEmitReason(Reply currentReply, Reply quoteReply, Comment comment) { + boolean isQuoteReply = StringUtils.isNotBlank(currentReply.getSpec().getQuoteReply()); + + if (isQuoteReply && quoteReply == null) { + throw new IllegalArgumentException( + "quoteReply can not be null when currentReply is reply to quote"); + } + + Comment.CommentOwner commentOwner = isQuoteReply ? quoteReply.getSpec().getOwner() + : comment.getSpec().getOwner(); + + var currentReplyOwner = currentReply.getSpec().getOwner(); + // reply to oneself do not emit reason + return currentReplyOwner.getKind().equals(commentOwner.getKind()) + && currentReplyOwner.getName().equals(commentOwner.getName()); + } + + @Builder + record NewReplyReasonData(String commentContent, String commentSubjectTitle, + String commentSubjectUrl, boolean isQuoteReply, + String quoteContent, + String commentName, String replier, String content, + String replyName) { + } + } +} diff --git a/application/src/main/java/run/halo/app/content/comment/PostCommentSubject.java b/application/src/main/java/run/halo/app/content/comment/PostCommentSubject.java index 23af58d158..1e96a53d77 100644 --- a/application/src/main/java/run/halo/app/content/comment/PostCommentSubject.java +++ b/application/src/main/java/run/halo/app/content/comment/PostCommentSubject.java @@ -1,5 +1,6 @@ package run.halo.app.content.comment; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import org.springframework.util.Assert; import reactor.core.publisher.Mono; @@ -7,6 +8,7 @@ import run.halo.app.extension.GroupVersionKind; import run.halo.app.extension.ReactiveExtensionClient; import run.halo.app.extension.Ref; +import run.halo.app.infra.ExternalLinkProcessor; /** * Comment subject for post. @@ -15,19 +17,27 @@ * @since 2.0.0 */ @Component +@RequiredArgsConstructor public class PostCommentSubject implements CommentSubject { private final ReactiveExtensionClient client; - - public PostCommentSubject(ReactiveExtensionClient client) { - this.client = client; - } + private final ExternalLinkProcessor externalLinkProcessor; @Override public Mono get(String name) { return client.fetch(Post.class, name); } + @Override + public Mono getSubjectDisplay(String name) { + return get(name) + .map(post -> { + var url = externalLinkProcessor + .processLink(post.getStatusOrDefault().getPermalink()); + return new SubjectDisplay(post.getSpec().getTitle(), url, "文章"); + }); + } + @Override public boolean supports(Ref ref) { Assert.notNull(ref, "Subject ref must not be null."); diff --git a/application/src/main/java/run/halo/app/content/comment/ReplyNotificationSubscriptionHelper.java b/application/src/main/java/run/halo/app/content/comment/ReplyNotificationSubscriptionHelper.java new file mode 100644 index 0000000000..0a086330e8 --- /dev/null +++ b/application/src/main/java/run/halo/app/content/comment/ReplyNotificationSubscriptionHelper.java @@ -0,0 +1,84 @@ +package run.halo.app.content.comment; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import run.halo.app.content.NotificationReasonConst; +import run.halo.app.core.extension.content.Comment; +import run.halo.app.core.extension.content.Reply; +import run.halo.app.core.extension.notification.Subscription; +import run.halo.app.notification.NotificationCenter; +import run.halo.app.notification.SubscriberEmailResolver; + +/** + * Reply notification subscription helper. + * + * @author guqing + * @since 2.9.0 + */ +@Component +@RequiredArgsConstructor +public class ReplyNotificationSubscriptionHelper { + + private final NotificationCenter notificationCenter; + private final SubscriberEmailResolver subscriberEmailResolver; + + /** + * Subscribe new reply reason for comment. + * + * @param comment comment + */ + public void subscribeNewReplyReasonForComment(Comment comment) { + var reasonSubject = Subscription.ReasonSubject.builder() + .apiVersion(comment.getApiVersion()) + .kind(comment.getKind()) + .name(comment.getMetadata().getName()) + .build(); + subscribeReply(reasonSubject, + Identity.fromCommentOwner(comment.getSpec().getOwner())); + } + + /** + * Subscribe new reply reason for reply. + * + * @param reply reply + */ + public void subscribeNewReplyReasonForReply(Reply reply) { + var reasonSubject = Subscription.ReasonSubject.builder() + .apiVersion(reply.getApiVersion()) + .kind(reply.getKind()) + .name(reply.getMetadata().getName()) + .build(); + var subjectOwner = reply.getSpec().getOwner(); + subscribeReply(reasonSubject, + Identity.fromCommentOwner(subjectOwner)); + } + + void subscribeReply(Subscription.ReasonSubject reasonSubject, + Identity identity) { + var subscriber = createSubscriber(identity); + var interestReason = new Subscription.InterestReason(); + interestReason.setReasonType(NotificationReasonConst.SOMEONE_REPLIED_TO_YOU); + interestReason.setSubject(reasonSubject); + notificationCenter.subscribe(subscriber, interestReason).block(); + } + + private Subscription.Subscriber createSubscriber(Identity author) { + Subscription.Subscriber subscriber; + if (author.isEmail()) { + subscriber = subscriberEmailResolver.ofEmail(author.name()); + } else { + subscriber = new Subscription.Subscriber(); + subscriber.setName(author.name()); + } + return subscriber; + } + + record Identity(String name, boolean isEmail) { + public static Identity fromCommentOwner(Comment.CommentOwner commentOwner) { + if (Comment.CommentOwner.KIND_EMAIL.equals(commentOwner.getKind())) { + return new Identity(commentOwner.getName(), true); + } + return new Identity(commentOwner.getName(), false); + } + } +} diff --git a/application/src/main/java/run/halo/app/content/comment/SinglePageCommentSubject.java b/application/src/main/java/run/halo/app/content/comment/SinglePageCommentSubject.java index 76eaab823f..4f7eeb6f55 100644 --- a/application/src/main/java/run/halo/app/content/comment/SinglePageCommentSubject.java +++ b/application/src/main/java/run/halo/app/content/comment/SinglePageCommentSubject.java @@ -1,5 +1,6 @@ package run.halo.app.content.comment; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import org.springframework.util.Assert; import reactor.core.publisher.Mono; @@ -7,6 +8,7 @@ import run.halo.app.extension.GroupVersionKind; import run.halo.app.extension.ReactiveExtensionClient; import run.halo.app.extension.Ref; +import run.halo.app.infra.ExternalLinkProcessor; /** * Comment subject for {@link SinglePage}. @@ -15,19 +17,28 @@ * @since 2.0.0 */ @Component +@RequiredArgsConstructor public class SinglePageCommentSubject implements CommentSubject { private final ReactiveExtensionClient client; - public SinglePageCommentSubject(ReactiveExtensionClient client) { - this.client = client; - } + private final ExternalLinkProcessor externalLinkProcessor; @Override public Mono get(String name) { return client.fetch(SinglePage.class, name); } + @Override + public Mono getSubjectDisplay(String name) { + return get(name) + .map(page -> { + var url = externalLinkProcessor + .processLink(page.getStatusOrDefault().getPermalink()); + return new SubjectDisplay(page.getSpec().getTitle(), url, "页面"); + }); + } + @Override public boolean supports(Ref ref) { Assert.notNull(ref, "Subject ref must not be null."); diff --git a/application/src/main/java/run/halo/app/core/extension/reconciler/CommentReconciler.java b/application/src/main/java/run/halo/app/core/extension/reconciler/CommentReconciler.java index 10b713042f..800c8ec84f 100644 --- a/application/src/main/java/run/halo/app/core/extension/reconciler/CommentReconciler.java +++ b/application/src/main/java/run/halo/app/core/extension/reconciler/CommentReconciler.java @@ -1,21 +1,25 @@ package run.halo.app.core.extension.reconciler; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; +import static run.halo.app.extension.ExtensionUtil.addFinalizers; import java.time.Instant; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.BooleanUtils; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.lang.Nullable; import org.springframework.stereotype.Component; +import run.halo.app.content.comment.ReplyNotificationSubscriptionHelper; import run.halo.app.content.comment.ReplyService; import run.halo.app.core.extension.Counter; import run.halo.app.core.extension.content.Comment; import run.halo.app.core.extension.content.Constant; import run.halo.app.core.extension.content.Reply; +import run.halo.app.event.post.CommentCreatedEvent; import run.halo.app.extension.ExtensionClient; import run.halo.app.extension.GroupVersionKind; import run.halo.app.extension.MetadataUtil; @@ -34,15 +38,14 @@ * @since 2.0.0 */ @Component +@RequiredArgsConstructor public class CommentReconciler implements Reconciler { public static final String FINALIZER_NAME = "comment-protection"; private final ExtensionClient client; private final SchemeManager schemeManager; + private final ApplicationEventPublisher eventPublisher; - public CommentReconciler(ExtensionClient client, SchemeManager schemeManager) { - this.client = client; - this.schemeManager = schemeManager; - } + private final ReplyNotificationSubscriptionHelper replyNotificationSubscriptionHelper; @Override public Result reconcile(Request request) { @@ -52,7 +55,13 @@ public Result reconcile(Request request) { cleanUpResourcesAndRemoveFinalizer(request.name()); return; } - addFinalizerIfNecessary(comment); + if (addFinalizers(comment.getMetadata(), Set.of(FINALIZER_NAME))) { + client.update(comment); + eventPublisher.publishEvent(new CommentCreatedEvent(this, comment)); + } + + replyNotificationSubscriptionHelper.subscribeNewReplyReasonForComment(comment); + compatibleCreationTime(request.name()); reconcileStatus(request.name()); updateSameSubjectRefCommentCounter(comment.getSpec().getSubjectRef()); @@ -93,23 +102,6 @@ private boolean isDeleted(Comment comment) { return comment.getMetadata().getDeletionTimestamp() != null; } - private void addFinalizerIfNecessary(Comment oldComment) { - Set finalizers = oldComment.getMetadata().getFinalizers(); - if (finalizers != null && finalizers.contains(FINALIZER_NAME)) { - return; - } - client.fetch(Comment.class, oldComment.getMetadata().getName()) - .ifPresent(comment -> { - Set newFinalizers = comment.getMetadata().getFinalizers(); - if (newFinalizers == null) { - newFinalizers = new HashSet<>(); - comment.getMetadata().setFinalizers(newFinalizers); - } - newFinalizers.add(FINALIZER_NAME); - client.update(comment); - }); - } - private void reconcileStatus(String name) { client.fetch(Comment.class, name).ifPresent(comment -> { Comment oldComment = JsonUtils.deepCopy(comment); diff --git a/application/src/main/java/run/halo/app/core/extension/reconciler/PostReconciler.java b/application/src/main/java/run/halo/app/core/extension/reconciler/PostReconciler.java index d1fdec0412..68e8585354 100644 --- a/application/src/main/java/run/halo/app/core/extension/reconciler/PostReconciler.java +++ b/application/src/main/java/run/halo/app/core/extension/reconciler/PostReconciler.java @@ -19,6 +19,7 @@ import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Component; import run.halo.app.content.ContentWrapper; +import run.halo.app.content.NotificationReasonConst; import run.halo.app.content.PostService; import run.halo.app.content.permalinks.PostPermalinkPolicy; import run.halo.app.core.extension.content.Comment; @@ -27,6 +28,7 @@ import run.halo.app.core.extension.content.Post.PostPhase; import run.halo.app.core.extension.content.Post.VisibleEnum; import run.halo.app.core.extension.content.Snapshot; +import run.halo.app.core.extension.notification.Subscription; import run.halo.app.event.post.PostPublishedEvent; import run.halo.app.event.post.PostUnpublishedEvent; import run.halo.app.event.post.PostUpdatedEvent; @@ -42,6 +44,7 @@ import run.halo.app.infra.utils.HaloUtils; import run.halo.app.metrics.CounterService; import run.halo.app.metrics.MeterUtils; +import run.halo.app.notification.NotificationCenter; /** *

Reconciler for {@link Post}.

@@ -65,6 +68,7 @@ public class PostReconciler implements Reconciler { private final CounterService counterService; private final ApplicationEventPublisher eventPublisher; + private final NotificationCenter notificationCenter; @Override public Result reconcile(Request request) { @@ -81,8 +85,11 @@ public Result reconcile(Request request) { events.forEach(eventPublisher::publishEvent); return; } + addFinalizers(post.getMetadata(), Set.of(FINALIZER_NAME)); + subscribeNewCommentNotification(post); + var labels = post.getMetadata().getLabels(); if (labels == null) { labels = new HashMap<>(); @@ -207,6 +214,20 @@ public Controller setupWith(ControllerBuilder builder) { .build(); } + void subscribeNewCommentNotification(Post post) { + var subscriber = new Subscription.Subscriber(); + subscriber.setName(post.getSpec().getOwner()); + + var interestReason = new Subscription.InterestReason(); + interestReason.setReasonType(NotificationReasonConst.NEW_COMMENT_ON_POST); + interestReason.setSubject(Subscription.ReasonSubject.builder() + .apiVersion(post.getApiVersion()) + .kind(post.getKind()) + .name(post.getMetadata().getName()) + .build()); + notificationCenter.subscribe(subscriber, interestReason).block(); + } + private void publishPost(Post post, Set events) { var expectReleaseSnapshot = post.getSpec().getReleaseSnapshot(); if (StringUtils.isBlank(expectReleaseSnapshot)) { diff --git a/application/src/main/java/run/halo/app/core/extension/reconciler/ReplyReconciler.java b/application/src/main/java/run/halo/app/core/extension/reconciler/ReplyReconciler.java index 540e8649f7..8d3a535216 100644 --- a/application/src/main/java/run/halo/app/core/extension/reconciler/ReplyReconciler.java +++ b/application/src/main/java/run/halo/app/core/extension/reconciler/ReplyReconciler.java @@ -1,12 +1,15 @@ package run.halo.app.core.extension.reconciler; -import java.util.HashSet; +import static run.halo.app.extension.ExtensionUtil.addFinalizers; + import java.util.Set; import lombok.AllArgsConstructor; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Component; +import run.halo.app.content.comment.ReplyNotificationSubscriptionHelper; import run.halo.app.core.extension.content.Reply; import run.halo.app.event.post.ReplyChangedEvent; +import run.halo.app.event.post.ReplyCreatedEvent; import run.halo.app.event.post.ReplyDeletedEvent; import run.halo.app.extension.ExtensionClient; import run.halo.app.extension.controller.Controller; @@ -27,6 +30,8 @@ public class ReplyReconciler implements Reconciler { private final ExtensionClient client; private final ApplicationEventPublisher eventPublisher; + private final ReplyNotificationSubscriptionHelper replyNotificationSubscriptionHelper; + @Override public Result reconcile(Request request) { client.fetch(Reply.class, request.name()) @@ -35,9 +40,13 @@ public Result reconcile(Request request) { cleanUpResourcesAndRemoveFinalizer(request.name()); return; } + if (addFinalizers(reply.getMetadata(), Set.of(FINALIZER_NAME))) { + client.update(reply); + eventPublisher.publishEvent(new ReplyCreatedEvent(this, reply)); + } + + replyNotificationSubscriptionHelper.subscribeNewReplyReasonForReply(reply); - addFinalizerIfNecessary(reply); - // on reply created eventPublisher.publishEvent(new ReplyChangedEvent(this, reply)); }); return new Result(false, null); @@ -50,28 +59,11 @@ private void cleanUpResourcesAndRemoveFinalizer(String replyName) { } client.update(reply); - // on reply removed + // on reply removing eventPublisher.publishEvent(new ReplyDeletedEvent(this, reply)); }); } - private void addFinalizerIfNecessary(Reply oldReply) { - Set finalizers = oldReply.getMetadata().getFinalizers(); - if (finalizers != null && finalizers.contains(FINALIZER_NAME)) { - return; - } - client.fetch(Reply.class, oldReply.getMetadata().getName()) - .ifPresent(reply -> { - Set newFinalizers = reply.getMetadata().getFinalizers(); - if (newFinalizers == null) { - newFinalizers = new HashSet<>(); - reply.getMetadata().setFinalizers(newFinalizers); - } - newFinalizers.add(FINALIZER_NAME); - client.update(reply); - }); - } - @Override public Controller setupWith(ControllerBuilder builder) { return builder diff --git a/application/src/main/java/run/halo/app/core/extension/reconciler/SinglePageReconciler.java b/application/src/main/java/run/halo/app/core/extension/reconciler/SinglePageReconciler.java index 6b75a8f423..441d95dae6 100644 --- a/application/src/main/java/run/halo/app/core/extension/reconciler/SinglePageReconciler.java +++ b/application/src/main/java/run/halo/app/core/extension/reconciler/SinglePageReconciler.java @@ -16,13 +16,16 @@ import org.jsoup.Jsoup; import org.springframework.stereotype.Component; import org.springframework.util.Assert; +import run.halo.app.content.NotificationReasonConst; import run.halo.app.content.SinglePageService; import run.halo.app.core.extension.content.Comment; import run.halo.app.core.extension.content.Post; import run.halo.app.core.extension.content.SinglePage; import run.halo.app.core.extension.content.Snapshot; +import run.halo.app.core.extension.notification.Subscription; import run.halo.app.extension.ExtensionClient; import run.halo.app.extension.ExtensionOperator; +import run.halo.app.extension.ExtensionUtil; import run.halo.app.extension.MetadataUtil; import run.halo.app.extension.Ref; import run.halo.app.extension.controller.Controller; @@ -35,6 +38,7 @@ import run.halo.app.infra.utils.JsonUtils; import run.halo.app.metrics.CounterService; import run.halo.app.metrics.MeterUtils; +import run.halo.app.notification.NotificationCenter; /** *

Reconciler for {@link SinglePage}.

@@ -59,16 +63,22 @@ public class SinglePageReconciler implements Reconciler { private final ExternalUrlSupplier externalUrlSupplier; + private final NotificationCenter notificationCenter; + @Override public Result reconcile(Request request) { client.fetch(SinglePage.class, request.name()) .ifPresent(singlePage -> { - SinglePage oldPage = JsonUtils.deepCopy(singlePage); if (ExtensionOperator.isDeleted(singlePage)) { cleanUpResourcesAndRemoveFinalizer(request.name()); return; } - addFinalizerIfNecessary(oldPage); + + if (ExtensionUtil.addFinalizers(singlePage.getMetadata(), Set.of(FINALIZER_NAME))) { + client.update(singlePage); + } + + subscribeNewCommentNotification(singlePage); // reconcile spec first reconcileSpec(request.name()); @@ -86,6 +96,20 @@ public Controller setupWith(ControllerBuilder builder) { .build(); } + void subscribeNewCommentNotification(SinglePage page) { + var subscriber = new Subscription.Subscriber(); + subscriber.setName(page.getSpec().getOwner()); + + var interestReason = new Subscription.InterestReason(); + interestReason.setReasonType(NotificationReasonConst.NEW_COMMENT_ON_PAGE); + interestReason.setSubject(Subscription.ReasonSubject.builder() + .apiVersion(page.getApiVersion()) + .kind(page.getKind()) + .name(page.getMetadata().getName()) + .build()); + notificationCenter.subscribe(subscriber, interestReason).block(); + } + private void reconcileSpec(String name) { client.fetch(SinglePage.class, name).ifPresent(page -> { // un-publish if necessary @@ -212,23 +236,6 @@ private void publishFailed(String name, Throwable error) { }); } - private void addFinalizerIfNecessary(SinglePage oldSinglePage) { - Set finalizers = oldSinglePage.getMetadata().getFinalizers(); - if (finalizers != null && finalizers.contains(FINALIZER_NAME)) { - return; - } - client.fetch(SinglePage.class, oldSinglePage.getMetadata().getName()) - .ifPresent(singlePage -> { - Set newFinalizers = singlePage.getMetadata().getFinalizers(); - if (newFinalizers == null) { - newFinalizers = new HashSet<>(); - singlePage.getMetadata().setFinalizers(newFinalizers); - } - newFinalizers.add(FINALIZER_NAME); - client.update(singlePage); - }); - } - private void cleanUpResources(SinglePage singlePage) { // clean up snapshot Ref ref = Ref.of(singlePage); diff --git a/application/src/main/java/run/halo/app/event/post/CommentCreatedEvent.java b/application/src/main/java/run/halo/app/event/post/CommentCreatedEvent.java new file mode 100644 index 0000000000..8483aa424a --- /dev/null +++ b/application/src/main/java/run/halo/app/event/post/CommentCreatedEvent.java @@ -0,0 +1,22 @@ +package run.halo.app.event.post; + +import lombok.Getter; +import org.springframework.context.ApplicationEvent; +import run.halo.app.core.extension.content.Comment; + +/** + * Comment created event. + * + * @author guqing + * @since 2.9.0 + */ +@Getter +public class CommentCreatedEvent extends ApplicationEvent { + + private final Comment comment; + + public CommentCreatedEvent(Object source, Comment comment) { + super(source); + this.comment = comment; + } +} diff --git a/application/src/main/java/run/halo/app/event/post/ReplyCreatedEvent.java b/application/src/main/java/run/halo/app/event/post/ReplyCreatedEvent.java new file mode 100644 index 0000000000..e0ebab8585 --- /dev/null +++ b/application/src/main/java/run/halo/app/event/post/ReplyCreatedEvent.java @@ -0,0 +1,16 @@ +package run.halo.app.event.post; + +import run.halo.app.core.extension.content.Reply; + +/** + * Reply created event. + * + * @author guqing + * @since 2.9.0 + */ +public class ReplyCreatedEvent extends ReplyEvent { + + public ReplyCreatedEvent(Object source, Reply reply) { + super(source, reply); + } +} diff --git a/application/src/main/java/run/halo/app/infra/DefaultExternalLinkProcessor.java b/application/src/main/java/run/halo/app/infra/DefaultExternalLinkProcessor.java new file mode 100644 index 0000000000..f3b909181d --- /dev/null +++ b/application/src/main/java/run/halo/app/infra/DefaultExternalLinkProcessor.java @@ -0,0 +1,54 @@ +package run.halo.app.infra; + +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.lang.NonNull; +import org.springframework.stereotype.Component; +import run.halo.app.infra.utils.PathUtils; + +/** + * Default implementation of {@link ExternalLinkProcessor}. + * + * @author guqing + * @since 2.9.0 + */ +@Component +@RequiredArgsConstructor +public class DefaultExternalLinkProcessor implements ExternalLinkProcessor { + private final ExternalUrlSupplier externalUrlSupplier; + + @Override + public String processLink(String link) { + var externalLink = externalUrlSupplier.getRaw(); + if (StringUtils.isBlank(link)) { + return link; + } + if (externalLink == null || !linkInSite(externalLink, link)) { + return link; + } + + return append(externalLink.toString(), link); + } + + String append(String externalLink, String link) { + return StringUtils.removeEnd(externalLink, "/") + + StringUtils.prependIfMissing(link, "/"); + } + + boolean linkInSite(@NonNull URL externalUrl, @NonNull String link) { + if (!PathUtils.isAbsoluteUri(link)) { + // relative uri is always in site + return true; + } + try { + URI requestUri = new URI(link); + return StringUtils.equals(externalUrl.getAuthority(), requestUri.getAuthority()); + } catch (URISyntaxException e) { + // ignore this link + } + return false; + } +} diff --git a/application/src/main/java/run/halo/app/infra/SchemeInitializer.java b/application/src/main/java/run/halo/app/infra/SchemeInitializer.java index 252418e5c8..ff2bb8b82f 100644 --- a/application/src/main/java/run/halo/app/infra/SchemeInitializer.java +++ b/application/src/main/java/run/halo/app/infra/SchemeInitializer.java @@ -29,6 +29,12 @@ import run.halo.app.core.extension.content.SinglePage; import run.halo.app.core.extension.content.Snapshot; import run.halo.app.core.extension.content.Tag; +import run.halo.app.core.extension.notification.Notification; +import run.halo.app.core.extension.notification.NotificationTemplate; +import run.halo.app.core.extension.notification.NotifierDescriptor; +import run.halo.app.core.extension.notification.Reason; +import run.halo.app.core.extension.notification.ReasonType; +import run.halo.app.core.extension.notification.Subscription; import run.halo.app.extension.ConfigMap; import run.halo.app.extension.SchemeManager; import run.halo.app.extension.Secret; @@ -95,6 +101,14 @@ public void onApplicationEvent(@NonNull ApplicationStartedEvent event) { // migration.halo.run schemeManager.register(Backup.class); + // notification.halo.run + schemeManager.register(ReasonType.class); + schemeManager.register(Reason.class); + schemeManager.register(NotificationTemplate.class); + schemeManager.register(Subscription.class); + schemeManager.register(NotifierDescriptor.class); + schemeManager.register(Notification.class); + eventPublisher.publishEvent(new SchemeInitializedEvent(this)); } } diff --git a/application/src/main/java/run/halo/app/notification/DefaultNotificationCenter.java b/application/src/main/java/run/halo/app/notification/DefaultNotificationCenter.java new file mode 100644 index 0000000000..d1fd5702ad --- /dev/null +++ b/application/src/main/java/run/halo/app/notification/DefaultNotificationCenter.java @@ -0,0 +1,366 @@ +package run.halo.app.notification; + +import static org.apache.commons.lang3.StringUtils.defaultString; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.function.BiPredicate; +import java.util.function.Function; +import lombok.Builder; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; +import run.halo.app.core.extension.User; +import run.halo.app.core.extension.notification.Notification; +import run.halo.app.core.extension.notification.NotifierDescriptor; +import run.halo.app.core.extension.notification.Reason; +import run.halo.app.core.extension.notification.ReasonType; +import run.halo.app.core.extension.notification.Subscription; +import run.halo.app.extension.GroupVersionKind; +import run.halo.app.extension.Metadata; +import run.halo.app.extension.ReactiveExtensionClient; +import run.halo.app.notification.endpoint.SubscriptionRouter; + +/** + * A default implementation of {@link NotificationCenter}. + * + * @author guqing + * @since 2.10.0 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class DefaultNotificationCenter implements NotificationCenter { + private final ReactiveExtensionClient client; + private final NotificationSender notificationSender; + private final NotifierConfigStore notifierConfigStore; + private final ReasonNotificationTemplateSelector notificationTemplateSelector; + private final UserNotificationPreferenceService userNotificationPreferenceService; + private final NotificationTemplateRender notificationTemplateRender; + private final SubscriptionRouter subscriptionRouter; + + @Override + public Mono notify(Reason reason) { + var reasonSubject = reason.getSpec().getSubject(); + var subscriptionReasonSubject = Subscription.ReasonSubject.builder() + .apiVersion(reasonSubject.getApiVersion()) + .kind(reasonSubject.getKind()) + .name(reasonSubject.getName()) + .build(); + return listObservers(reason.getSpec().getReasonType(), subscriptionReasonSubject) + .doOnNext(subscription -> { + log.debug("Dispatching notification to subscriber [{}] for reason [{}]", + subscription.getSpec().getSubscriber(), reason.getMetadata().getName()); + }) + .publishOn(Schedulers.boundedElastic()) + .flatMap(subscription -> dispatchNotification(reason, subscription)) + .then(); + } + + @Override + public Mono subscribe(Subscription.Subscriber subscriber, + Subscription.InterestReason reason) { + return listSubscription(subscriber) + .filter(subscription -> subscription.getSpec().getReason().equals(reason)) + .next() + .switchIfEmpty(Mono.defer(() -> { + var subscription = new Subscription(); + subscription.setMetadata(new Metadata()); + subscription.getMetadata().setGenerateName("subscription-"); + subscription.setSpec(new Subscription.Spec()); + subscription.getSpec().setUnsubscribeToken(Subscription.generateUnsubscribeToken()); + subscription.getSpec().setSubscriber(subscriber); + subscription.getSpec().setReason(reason); + return client.create(subscription); + })); + } + + @Override + public Mono unsubscribe(Subscription.Subscriber subscriber) { + return listSubscription(subscriber) + .flatMap(client::delete) + .then(); + } + + @Override + public Mono unsubscribe(Subscription.Subscriber subscriber, + Subscription.InterestReason reason) { + return listSubscription(subscriber) + .filter(subscription -> subscription.getSpec().getReason().equals(reason)) + .flatMap(client::delete) + .then(); + } + + Flux listSubscription(Subscription.Subscriber subscriber) { + return client.list(Subscription.class, + subscription -> subscription.getSpec().getSubscriber().equals(subscriber), + null); + } + + Flux getNotifiersBySubscriber(Subscription.Subscriber subscriber, Reason reason) { + var reasonType = reason.getSpec().getReasonType(); + return userNotificationPreferenceService.getByUser(subscriber.getName()) + .map(UserNotificationPreference::getReasonTypeNotifier) + .map(reasonTypeNotification -> reasonTypeNotification.getNotifiers(reasonType)) + .flatMapMany(Flux::fromIterable); + } + + Mono dispatchNotification(Reason reason, Subscription subscription) { + var subscriber = subscription.getSpec().getSubscriber(); + return getNotifiersBySubscriber(subscriber, reason) + .flatMap(notifierName -> client.fetch(NotifierDescriptor.class, notifierName)) + .flatMap(descriptor -> prepareNotificationElement(subscription, reason, descriptor)) + .flatMap(element -> { + var dispatchMono = sendNotification(element); + var innerNofificationMono = createNotification(element); + return Mono.when(dispatchMono, innerNofificationMono); + }) + .then(); + } + + Mono prepareNotificationElement(Subscription subscription, Reason reason, + NotifierDescriptor descriptor) { + return getLocaleFromSubscriber(subscription) + .flatMap(locale -> inferenceTemplate(reason, subscription, locale)) + .map(notificationContent -> NotificationElement.builder() + .descriptor(descriptor) + .reason(reason) + .subscription(subscription) + .reasonType(notificationContent.reasonType()) + .notificationTitle(notificationContent.title()) + .notificationRawBody(defaultString(notificationContent.rawBody())) + .notificationHtmlBody(defaultString(notificationContent.htmlBody())) + .build() + ); + } + + Mono sendNotification(NotificationElement notificationElement) { + var descriptor = notificationElement.descriptor(); + var subscription = notificationElement.subscription(); + final var notifierExtName = descriptor.getSpec().getNotifierExtName(); + return notificationContextFrom(notificationElement) + .flatMap(notificationContext -> notificationSender.sendNotification(notifierExtName, + notificationContext) + .onErrorResume(throwable -> { + log.error( + "Failed to send notification to subscriber [{}] through notifier [{}]", + subscription.getSpec().getSubscriber(), + descriptor.getSpec().getDisplayName(), + throwable); + return Mono.empty(); + }) + ) + .then(); + } + + Mono createNotification(NotificationElement notificationElement) { + var reason = notificationElement.reason(); + var subscription = notificationElement.subscription(); + var subscriber = subscription.getSpec().getSubscriber(); + return client.fetch(User.class, subscriber.getName()) + .flatMap(user -> { + Notification notification = new Notification(); + notification.setMetadata(new Metadata()); + notification.getMetadata().setGenerateName("notification-"); + notification.setSpec(new Notification.NotificationSpec()); + notification.getSpec().setTitle(notificationElement.notificationTitle()); + notification.getSpec().setRawContent(notificationElement.notificationRawBody()); + notification.getSpec().setHtmlContent(notificationElement.notificationHtmlBody); + notification.getSpec().setRecipient(subscriber.getName()); + notification.getSpec().setReason(reason.getMetadata().getName()); + notification.getSpec().setUnread(true); + return client.create(notification); + }); + } + + @Builder + record NotificationElement(ReasonType reasonType, Reason reason, + Subscription subscription, NotifierDescriptor descriptor, + String notificationTitle, + String notificationRawBody, + String notificationHtmlBody) { + } + + Mono notificationContextFrom(NotificationElement element) { + final var descriptorName = element.descriptor().getMetadata().getName(); + final var reason = element.reason(); + final var descriptor = element.descriptor(); + final var subscription = element.subscription(); + + var messagePayload = new NotificationContext.MessagePayload(); + messagePayload.setTitle(element.notificationTitle()); + messagePayload.setRawBody(element.notificationRawBody()); + messagePayload.setHtmlBody(element.notificationHtmlBody()); + messagePayload.setAttributes(reason.getSpec().getAttributes()); + + var message = new NotificationContext.Message(); + message.setRecipient(subscription.getSpec().getSubscriber().getName()); + message.setPayload(messagePayload); + message.setTimestamp(reason.getMetadata().getCreationTimestamp()); + var reasonSubject = reason.getSpec().getSubject(); + var subject = NotificationContext.Subject.builder() + .apiVersion(reasonSubject.getApiVersion()) + .kind(reasonSubject.getKind()) + .title(reasonSubject.getTitle()) + .url(reasonSubject.getUrl()) + .build(); + message.setSubject(subject); + + var notificationContext = new NotificationContext(); + notificationContext.setMessage(message); + + return Mono.just(notificationContext) + .flatMap(context -> { + Mono receiverConfigMono = + Optional.ofNullable(descriptor.getSpec().getReceiverSettingRef()) + .map(ref -> notifierConfigStore.fetchReceiverConfig(descriptorName) + .doOnNext(context::setReceiverConfig) + .then() + ) + .orElse(Mono.empty()); + + Mono senderConfigMono = + Optional.ofNullable(descriptor.getSpec().getSenderSettingRef()) + .map(ref -> notifierConfigStore.fetchSenderConfig(descriptorName) + .doOnNext(context::setSenderConfig) + .then() + ) + .orElse(Mono.empty()); + + return Mono.when(receiverConfigMono, senderConfigMono) + .thenReturn(context); + }); + } + + @Builder + record NotificationContent(String title, String rawBody, String htmlBody, ReasonType reasonType, + Map reasonProperties) { + } + + Mono inferenceTemplate(Reason reason, Subscription subscription, + Locale locale) { + var reasonTypeName = reason.getSpec().getReasonType(); + var subscriber = subscription.getSpec().getSubscriber(); + return getReasonType(reasonTypeName) + .flatMap(reasonType -> notificationTemplateSelector.select(reasonTypeName, locale) + .flatMap(template -> { + final var templateContent = template.getSpec().getTemplate(); + Map model = toReasonAttributes(reason); + var identity = UserIdentity.of(subscriber.getName()); + var subscriberInfo = new HashMap<>(); + if (identity.isAnonymous()) { + subscriberInfo.put("displayName", identity.getEmail().orElse("")); + } else { + subscriberInfo.put("displayName", "@" + identity.name()); + } + subscriberInfo.put("id", subscriber.getName()); + model.put("subscriber", subscriberInfo); + model.put("unsubscribeUrl", getUnsubscribeUrl(subscription)); + + var builder = NotificationContent.builder() + .reasonType(reasonType) + .reasonProperties(model); + + var titleMono = notificationTemplateRender + .render(templateContent.getTitle(), model) + .doOnNext(builder::title); + + var rawBodyMono = notificationTemplateRender + .render(templateContent.getRawBody(), model) + .doOnNext(builder::rawBody); + + var htmlBodyMono = notificationTemplateRender + .render(templateContent.getHtmlBody(), model) + .doOnNext(builder::htmlBody); + return Mono.when(titleMono, rawBodyMono, htmlBodyMono) + .then(Mono.fromSupplier(builder::build)); + }) + ); + } + + String getUnsubscribeUrl(Subscription subscription) { + return subscriptionRouter.getUnsubscribeUrl(subscription); + } + + private Map toReasonAttributes(Reason reason) { + Map model = new HashMap<>(); + var attributes = reason.getSpec().getAttributes(); + if (attributes != null) { + model.putAll(attributes); + } + return model; + } + + Mono getReasonType(String reasonTypeName) { + return client.get(ReasonType.class, reasonTypeName); + } + + Mono getLocaleFromSubscriber(Subscription subscription) { + // TODO get locale from subscriber + return Mono.just(Locale.getDefault()); + } + + Flux listObservers(String reasonTypeName, + Subscription.ReasonSubject reasonSubject) { + var distinctKeyPredicate = subscriptionDistinctKeyPredicate(); + return client.list(Subscription.class, + subscription -> { + var interestReason = subscription.getSpec().getReason(); + var sourceSubject = interestReason.getSubject(); + if (StringUtils.isBlank(sourceSubject.getName())) { + return interestReason.getReasonType().equals(reasonTypeName) + && sourceSubject.getApiVersion().equals(reasonSubject.getApiVersion()) + && sourceSubject.getKind().equals(reasonSubject.getKind()); + } + return interestReason.getReasonType().equals(reasonTypeName) + && interestReason.getSubject().equals(reasonSubject); + }, null) + .distinct(Function.identity(), HashSet::new, + (set, val) -> { + for (Subscription subscription : set) { + if (distinctKeyPredicate.test(subscription, val)) { + return false; + } + } + // no duplicated return true + set.add(val); + return true; + }, + HashSet::clear); + } + + static BiPredicate subscriptionDistinctKeyPredicate() { + return (a, b) -> { + if (!a.getSpec().getSubscriber().equals(b.getSpec().getSubscriber())) { + return false; + } + var reasonA = a.getSpec().getReason(); + var reasonB = b.getSpec().getReason(); + if (!reasonA.getReasonType().equals(reasonB.getReasonType())) { + return false; + } + var ars = reasonA.getSubject(); + var brs = reasonB.getSubject(); + var gvkForA = + GroupVersionKind.fromAPIVersionAndKind(ars.getApiVersion(), ars.getKind()); + var gvkForB = + GroupVersionKind.fromAPIVersionAndKind(brs.getApiVersion(), brs.getKind()); + + if (!gvkForA.groupKind().equals(gvkForB.groupKind())) { + return false; + } + // name is blank present match all + if (StringUtils.isBlank(ars.getName()) || StringUtils.isBlank(brs.getName())) { + return true; + } + return ars.getName().equals(brs.getName()); + }; + } +} diff --git a/application/src/main/java/run/halo/app/notification/DefaultNotificationReasonEmitter.java b/application/src/main/java/run/halo/app/notification/DefaultNotificationReasonEmitter.java new file mode 100644 index 0000000000..2a62cc9fc8 --- /dev/null +++ b/application/src/main/java/run/halo/app/notification/DefaultNotificationReasonEmitter.java @@ -0,0 +1,89 @@ +package run.halo.app.notification; + +import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; + +import java.util.List; +import java.util.function.Consumer; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.util.Assert; +import reactor.core.publisher.Mono; +import run.halo.app.core.extension.notification.Reason; +import run.halo.app.core.extension.notification.ReasonType; +import run.halo.app.extension.Metadata; +import run.halo.app.extension.ReactiveExtensionClient; +import run.halo.app.infra.exception.NotFoundException; + +/** + * A default {@link NotificationReasonEmitter} implementation. + * + * @author guqing + * @since 2.10.0 + */ +@Component +@RequiredArgsConstructor +public class DefaultNotificationReasonEmitter implements NotificationReasonEmitter { + + private final ReactiveExtensionClient client; + + @Override + public Mono emit(String reasonType, + Consumer builder) { + Assert.notNull(reasonType, "Reason type must not be empty."); + var reason = createReason(reasonType, buildReasonPayload(builder)); + return validateReason(reason) + .then(Mono.defer(() -> client.create(reason))) + .then(); + } + + Mono validateReason(Reason reason) { + String reasonTypeName = reason.getSpec().getReasonType(); + return client.fetch(ReasonType.class, reasonTypeName) + .switchIfEmpty(Mono.error(new NotFoundException( + "ReasonType [" + reasonTypeName + "] not found, do you forget to register it?")) + ) + .doOnNext(reasonType -> { + var valueMap = reason.getSpec().getAttributes(); + nullSafeList(reasonType.getSpec().getProperties()) + .forEach(property -> { + if (property.isOptional()) { + return; + } + if (valueMap.get(property.getName()) == null) { + throw new IllegalArgumentException( + "Reason property [" + property.getName() + "] is required."); + } + }); + }) + .then(); + } + + List nullSafeList(List t) { + return defaultIfNull(t, List.of()); + } + + Reason createReason(String reasonType, ReasonPayload reasonData) { + Reason reason = new Reason(); + reason.setMetadata(new Metadata()); + reason.getMetadata().setGenerateName("reason-"); + reason.setSpec(new Reason.Spec()); + if (reasonData.getAuthor() != null) { + reason.getSpec().setAuthor(reasonData.getAuthor().name()); + } + reason.getSpec().setReasonType(reasonType); + reason.getSpec().setSubject(reasonData.getSubject()); + + var reasonAttributes = new ReasonAttributes(); + if (reasonData.getAttributes() != null) { + reasonAttributes.putAll(reasonData.getAttributes()); + } + reason.getSpec().setAttributes(reasonAttributes); + return reason; + } + + ReasonPayload buildReasonPayload(Consumer reasonData) { + var builder = ReasonPayload.builder(); + reasonData.accept(builder); + return builder.build(); + } +} diff --git a/application/src/main/java/run/halo/app/notification/DefaultNotificationSender.java b/application/src/main/java/run/halo/app/notification/DefaultNotificationSender.java new file mode 100644 index 0000000000..dcb61de449 --- /dev/null +++ b/application/src/main/java/run/halo/app/notification/DefaultNotificationSender.java @@ -0,0 +1,150 @@ +package run.halo.app.notification; + +import java.time.Duration; +import java.time.Instant; +import java.util.UUID; +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.SmartLifecycle; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Mono; +import run.halo.app.extension.ReactiveExtensionClient; +import run.halo.app.extension.controller.Controller; +import run.halo.app.extension.controller.ControllerBuilder; +import run.halo.app.extension.controller.DefaultController; +import run.halo.app.extension.controller.DefaultQueue; +import run.halo.app.extension.controller.Reconciler; +import run.halo.app.extension.controller.RequestQueue; +import run.halo.app.plugin.extensionpoint.ExtensionDefinition; +import run.halo.app.plugin.extensionpoint.ExtensionGetter; + +/** + * A default {@link NotificationSender} implementation. + * + * @author guqing + * @since 2.10.0 + */ +@Slf4j +@Component +public class DefaultNotificationSender + implements NotificationSender, Reconciler, + SmartLifecycle { + private final ReactiveExtensionClient client; + private final ExtensionGetter extensionGetter; + + private final RequestQueue requestQueue; + + private final Controller controller; + + private boolean running = false; + + /** + * Constructs a new notification sender with the given {@link ReactiveExtensionClient} and + * {@link ExtensionGetter}. + */ + public DefaultNotificationSender(ReactiveExtensionClient client, + ExtensionGetter extensionGetter) { + this.client = client; + this.extensionGetter = extensionGetter; + requestQueue = new DefaultQueue<>(Instant::now); + controller = this.setupWith(null); + } + + @Override + public Mono sendNotification(String notifierExtensionName, NotificationContext context) { + return selectNotifier(notifierExtensionName) + .doOnNext(notifier -> { + var item = new QueueItem(UUID.randomUUID().toString(), + () -> notifier.notify(context).block(), 0); + requestQueue.addImmediately(item); + }) + .then(); + } + + Mono selectNotifier(String notifierExtensionName) { + return client.fetch(ExtensionDefinition.class, notifierExtensionName) + .flatMap(extDefinition -> extensionGetter.getEnabledExtensionByDefinition( + ReactiveNotifier.class) + .filter(notifier -> notifier.getClass().getName() + .equals(extDefinition.getSpec().getClassName()) + ) + .next() + ); + } + + @Override + public Result reconcile(QueueItem request) { + if (request.getTimes() > 3) { + log.error("Failed to send notification after retrying 3 times, discard it."); + return Result.doNotRetry(); + } + log.debug("Executing send notification task, [{}] remaining to-do tasks", + requestQueue.size()); + request.setTimes(request.getTimes() + 1); + request.getTask().execute(); + return Result.doNotRetry(); + } + + @Override + public Controller setupWith(ControllerBuilder builder) { + return new DefaultController<>( + this.getClass().getName(), + this, + requestQueue, + null, + Duration.ofMillis(100), + Duration.ofSeconds(1000), + 5 + ); + } + + @Override + public void start() { + controller.start(); + running = true; + } + + @Override + public void stop() { + running = false; + controller.dispose(); + } + + @Override + public boolean isRunning() { + return running; + } + + /** + *

Queue item for {@link #requestQueue}.

+ *

It holds a {@link SendNotificationTask} and a {@link #times} field.

+ *

{@link SendNotificationTask} used to send email when consuming.

+ *

{@link #times} will be used to record the number of + * times the task has been executed, if retry three times on failure, it will be discarded.

+ *

It also holds a {@link #id} field, which is used to identify the item. queue item with + * the same id is considered to be the same item to ensure that controller can + * discard the existing item in the queue when item re-queued on failure.

+ */ + @Getter + @AllArgsConstructor + @EqualsAndHashCode(onlyExplicitlyIncluded = true) + public static class QueueItem { + + @EqualsAndHashCode.Include + private final String id; + + private final SendNotificationTask task; + + @Setter + private int times; + } + + @FunctionalInterface + interface SendNotificationTask { + void execute(); + } +} + diff --git a/application/src/main/java/run/halo/app/notification/DefaultNotificationService.java b/application/src/main/java/run/halo/app/notification/DefaultNotificationService.java new file mode 100644 index 0000000000..a5171b90e3 --- /dev/null +++ b/application/src/main/java/run/halo/app/notification/DefaultNotificationService.java @@ -0,0 +1,57 @@ +package run.halo.app.notification; + +import java.time.Instant; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.util.Assert; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import run.halo.app.core.extension.notification.Notification; +import run.halo.app.extension.ListResult; +import run.halo.app.extension.ReactiveExtensionClient; + +/** + * A default implementation of {@link UserNotificationService}. + * + * @author guqing + * @since 2.10.0 + */ +@Component +@RequiredArgsConstructor +public class DefaultNotificationService implements UserNotificationService { + + private final ReactiveExtensionClient client; + + @Override + public Mono> listByUser(String username, UserNotificationQuery query) { + var predicate = query.toPredicate() + .and(notification -> isRecipient(notification, username)); + return client.list(Notification.class, predicate, query.toComparator(), + query.getPage(), query.getSize()); + } + + @Override + public Mono markAsRead(String username, String name) { + return client.fetch(Notification.class, name) + .filter(notification -> isRecipient(notification, username)) + .flatMap(notification -> { + notification.getSpec().setUnread(false); + notification.getSpec().setLastReadAt(Instant.now()); + return client.update(notification); + }); + } + + @Override + public Flux markSpecifiedAsRead(String username, List names) { + return Flux.fromIterable(names) + .flatMap(name -> markAsRead(username, name)) + .map(notification -> notification.getMetadata().getName()); + } + + static boolean isRecipient(Notification notification, String username) { + Assert.notNull(notification, "Notification must not be null"); + Assert.notNull(username, "Username must not be null"); + return username.equals(notification.getSpec().getRecipient()); + } +} diff --git a/application/src/main/java/run/halo/app/notification/DefaultNotificationTemplateRender.java b/application/src/main/java/run/halo/app/notification/DefaultNotificationTemplateRender.java new file mode 100644 index 0000000000..b15ff738e7 --- /dev/null +++ b/application/src/main/java/run/halo/app/notification/DefaultNotificationTemplateRender.java @@ -0,0 +1,66 @@ +package run.halo.app.notification; + +import static org.apache.commons.lang3.StringUtils.defaultString; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.thymeleaf.TemplateEngine; +import org.thymeleaf.context.Context; +import org.thymeleaf.spring6.SpringTemplateEngine; +import org.thymeleaf.templateresolver.StringTemplateResolver; +import reactor.core.publisher.Mono; +import run.halo.app.infra.ExternalUrlSupplier; +import run.halo.app.infra.SystemConfigurableEnvironmentFetcher; +import run.halo.app.infra.SystemSetting; + +/** + *

Default implementation of {@link NotificationTemplateRender}.

+ *

This implementation use {@link TemplateEngine} to render template, and the template engine + * use {@link StringTemplateResolver} to resolve template, so the template + * in {@link #render(String template, Map)} is template content.

+ *

Template syntax: + * usingthymeleaf.html#textual-syntax + *

+ * + * @author guqing + * @since 2.10.0 + */ +@Component +@RequiredArgsConstructor +public class DefaultNotificationTemplateRender implements NotificationTemplateRender { + + private static final TemplateEngine TEMPLATE_ENGINE = createTemplateEngine(); + + private final SystemConfigurableEnvironmentFetcher environmentFetcher; + private final ExternalUrlSupplier externalUrlSupplier; + + @Override + public Mono render(String template, Map model) { + var context = new Context(Locale.getDefault(), model); + var globalAttributeMono = getBasicSetting() + .doOnNext(basic -> { + var site = new HashMap<>(); + site.put("title", basic.getTitle()); + site.put("logo", basic.getLogo()); + site.put("subtitle", basic.getSubtitle()); + site.put("url", externalUrlSupplier.getRaw()); + context.setVariable("site", site); + }); + return Mono.when(globalAttributeMono) + .then(Mono.fromSupplier(() -> + TEMPLATE_ENGINE.process(defaultString(template), context))); + } + + static TemplateEngine createTemplateEngine() { + var template = new SpringTemplateEngine(); + template.setTemplateResolver(new StringTemplateResolver()); + return template; + } + + Mono getBasicSetting() { + return environmentFetcher.fetch(SystemSetting.Basic.GROUP, SystemSetting.Basic.class); + } +} diff --git a/application/src/main/java/run/halo/app/notification/DefaultNotifierConfigStore.java b/application/src/main/java/run/halo/app/notification/DefaultNotifierConfigStore.java new file mode 100644 index 0000000000..467fa9db72 --- /dev/null +++ b/application/src/main/java/run/halo/app/notification/DefaultNotifierConfigStore.java @@ -0,0 +1,93 @@ +package run.halo.app.notification; + +import static run.halo.app.extension.MetadataUtil.SYSTEM_FINALIZER; + +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Mono; +import run.halo.app.extension.Metadata; +import run.halo.app.extension.ReactiveExtensionClient; +import run.halo.app.extension.Secret; +import run.halo.app.infra.utils.JsonUtils; + +/** + * A default implementation of {@link NotifierConfigStore}. + * + * @author guqing + * @since 2.10.0 + */ +@Component +@RequiredArgsConstructor +public class DefaultNotifierConfigStore implements NotifierConfigStore { + public static final String SECRET_NAME = "notifier-setting-secret"; + public static final String RECEIVER_KEY = "receiver"; + public static final String SENDER_KEY = "sender"; + + private final ReactiveExtensionClient client; + + @Override + public Mono fetchReceiverConfig(String notifierDescriptorName) { + return fetchConfig(notifierDescriptorName) + .mapNotNull(setting -> (ObjectNode) setting.get(RECEIVER_KEY)) + .defaultIfEmpty(JsonNodeFactory.instance.objectNode()); + } + + @Override + public Mono fetchSenderConfig(String notifierDescriptorName) { + return fetchConfig(notifierDescriptorName) + .mapNotNull(setting -> (ObjectNode) setting.get(SENDER_KEY)) + .defaultIfEmpty(JsonNodeFactory.instance.objectNode()); + } + + @Override + public Mono saveReceiverConfig(String notifierDescriptorName, ObjectNode config) { + return saveConfig(notifierDescriptorName, RECEIVER_KEY, config); + } + + @Override + public Mono saveSenderConfig(String notifierDescriptorName, ObjectNode config) { + return saveConfig(notifierDescriptorName, SENDER_KEY, config); + } + + Mono saveConfig(String notifierDescriptorName, String key, ObjectNode config) { + return client.fetch(Secret.class, SECRET_NAME) + .switchIfEmpty(Mono.defer(() -> { + Secret secret = new Secret(); + secret.setMetadata(new Metadata()); + secret.getMetadata().setName(SECRET_NAME); + secret.getMetadata().setFinalizers(Set.of(SYSTEM_FINALIZER)); + secret.setStringData(new HashMap<>()); + return client.create(secret); + })) + .flatMap(secret -> { + if (secret.getStringData() == null) { + secret.setStringData(new HashMap<>()); + } + Map map = secret.getStringData(); + ObjectNode wrapperNode = JsonNodeFactory.instance.objectNode(); + wrapperNode.set(key, config); + map.put(resolveKey(notifierDescriptorName), JsonUtils.objectToJson(wrapperNode)); + return client.update(secret); + }) + .then(); + } + + Mono fetchConfig(String notifierDescriptorName) { + return client.fetch(Secret.class, SECRET_NAME) + .mapNotNull(Secret::getStringData) + .mapNotNull(map -> map.get(resolveKey(notifierDescriptorName))) + .filter(StringUtils::isNotBlank) + .map(value -> JsonUtils.jsonToObject(value, ObjectNode.class)) + .defaultIfEmpty(JsonNodeFactory.instance.objectNode()); + } + + String resolveKey(String notifierDescriptorName) { + return notifierDescriptorName + ".json"; + } +} diff --git a/application/src/main/java/run/halo/app/notification/DefaultSubscriberEmailResolver.java b/application/src/main/java/run/halo/app/notification/DefaultSubscriberEmailResolver.java new file mode 100644 index 0000000000..44dc0a22fe --- /dev/null +++ b/application/src/main/java/run/halo/app/notification/DefaultSubscriberEmailResolver.java @@ -0,0 +1,63 @@ +package run.halo.app.notification; + +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Mono; +import run.halo.app.core.extension.User; +import run.halo.app.core.extension.notification.Subscription; +import run.halo.app.extension.ReactiveExtensionClient; + +/** + *

Default implementation of {@link SubscriberEmailResolver}.

+ *

If the subscriber is an anonymous subscriber, the email will be extracted from the + * subscriber name.

+ *

An anonymous subscriber's name is in the format of {@code anonymous#email}.

+ * + * @author guqing + * @since 2.10.0 + */ +@Component +@RequiredArgsConstructor +public class DefaultSubscriberEmailResolver implements SubscriberEmailResolver { + private static final String SEPARATOR = "#"; + + private final ReactiveExtensionClient client; + + @Override + public Mono resolve(Subscription.Subscriber subscriber) { + if (isEmailSubscriber(subscriber)) { + return Mono.fromSupplier(() -> getEmail(subscriber)); + } + return client.fetch(User.class, subscriber.getName()) + .mapNotNull(user -> user.getSpec().getEmail()); + } + + @Override + public Subscription.Subscriber ofEmail(String email) { + if (StringUtils.isBlank(email)) { + throw new IllegalArgumentException("Email must not be blank"); + } + var subscriber = new Subscription.Subscriber(); + subscriber.setName(UserIdentity.anonymousWithEmail(email).name()); + return subscriber; + } + + static boolean isEmailSubscriber(Subscription.Subscriber subscriber) { + return UserIdentity.of(subscriber.getName()).isAnonymous(); + } + + @NonNull + String getEmail(Subscription.Subscriber subscriber) { + if (!isEmailSubscriber(subscriber)) { + throw new IllegalStateException("The subscriber is not an email subscriber"); + } + var subscriberName = subscriber.getName(); + String email = subscriberName.substring(subscriberName.indexOf(SEPARATOR) + 1); + if (StringUtils.isBlank(email)) { + throw new IllegalStateException("The subscriber does not have an email"); + } + return email; + } +} diff --git a/application/src/main/java/run/halo/app/notification/EmailNotifier.java b/application/src/main/java/run/halo/app/notification/EmailNotifier.java new file mode 100644 index 0000000000..50139df413 --- /dev/null +++ b/application/src/main/java/run/halo/app/notification/EmailNotifier.java @@ -0,0 +1,156 @@ +package run.halo.app.notification; + +import com.fasterxml.jackson.databind.JsonNode; +import java.nio.charset.StandardCharsets; +import java.util.Properties; +import java.util.concurrent.atomic.AtomicReference; +import lombok.Data; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.util.Pair; +import org.springframework.mail.javamail.JavaMailSenderImpl; +import org.springframework.mail.javamail.MimeMessageHelper; +import org.springframework.mail.javamail.MimeMessagePreparator; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; +import run.halo.app.core.extension.notification.Subscription; +import run.halo.app.infra.utils.JsonUtils; + +/** + *

A notifier that can send email.

+ * + * @author guqing + * @see ReactiveNotifier + * @see JavaMailSenderImpl + * @since 2.10.0 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class EmailNotifier implements ReactiveNotifier { + + private final SubscriberEmailResolver subscriberEmailResolver; + private final NotificationTemplateRender notificationTemplateRender; + private final AtomicReference> + emailSenderConfigPairRef = new AtomicReference<>(); + + @Override + public Mono notify(NotificationContext context) { + JsonNode senderConfig = context.getSenderConfig(); + var emailSenderConfig = + JsonUtils.DEFAULT_JSON_MAPPER.convertValue(senderConfig, EmailSenderConfig.class); + + JavaMailSenderImpl javaMailSender = getJavaMailSender(emailSenderConfig); + + String recipient = context.getMessage().getRecipient(); + var subscriber = new Subscription.Subscriber(); + subscriber.setName(recipient); + var payload = context.getMessage().getPayload(); + return subscriberEmailResolver.resolve(subscriber) + .flatMap(toEmail -> { + var htmlMono = appendHtmlBodyFooter(payload.getAttributes()) + .doOnNext(footer -> { + if (StringUtils.isNotBlank(payload.getHtmlBody())) { + payload.setHtmlBody(payload.getHtmlBody() + "\n" + footer); + } + }); + var rawMono = appendRawBodyFooter(payload.getAttributes()) + .doOnNext(footer -> { + if (StringUtils.isNotBlank(payload.getRawBody())) { + payload.setRawBody(payload.getRawBody() + "\n" + footer); + } + }); + return Mono.when(htmlMono, rawMono) + .thenReturn(toEmail); + }) + .map(toEmail -> getMimeMessagePreparator(toEmail, emailSenderConfig, payload)) + .publishOn(Schedulers.boundedElastic()) + .doOnNext(javaMailSender::send) + .then(); + } + + @NonNull + private static MimeMessagePreparator getMimeMessagePreparator(String toEmail, + EmailSenderConfig emailSenderConfig, NotificationContext.MessagePayload payload) { + return mimeMessage -> { + MimeMessageHelper helper = + new MimeMessageHelper(mimeMessage, true, StandardCharsets.UTF_8.name()); + helper.setFrom(emailSenderConfig.getUsername(), emailSenderConfig.getDisplayName()); + + helper.setSubject(payload.getTitle()); + helper.setText(payload.getRawBody(), payload.getHtmlBody()); + helper.setTo(toEmail); + }; + } + + @NonNull + private static JavaMailSenderImpl createJavaMailSender(EmailSenderConfig emailSenderConfig) { + JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl(); + javaMailSender.setHost(emailSenderConfig.getHost()); + javaMailSender.setPort(emailSenderConfig.getPort()); + javaMailSender.setUsername(emailSenderConfig.getUsername()); + javaMailSender.setPassword(emailSenderConfig.getPassword()); + + Properties props = javaMailSender.getJavaMailProperties(); + props.put("mail.transport.protocol", "smtp"); + props.put("mail.smtp.auth", "true"); + props.put("mail.smtp.starttls.enable", "true"); + if (log.isDebugEnabled()) { + props.put("mail.debug", "true"); + } + return javaMailSender; + } + + JavaMailSenderImpl getJavaMailSender(EmailSenderConfig emailSenderConfig) { + return emailSenderConfigPairRef.updateAndGet(pair -> { + if (pair != null && pair.getFirst().equals(emailSenderConfig)) { + return pair; + } + return Pair.of(emailSenderConfig, createJavaMailSender(emailSenderConfig)); + }).getSecond(); + } + + Mono appendRawBodyFooter(ReasonAttributes attributes) { + return notificationTemplateRender.render(""" + --- + 如果您不想再收到此类通知,点击链接退订: [(${unsubscribeUrl})] + [(${site.title})] + """, attributes); + } + + Mono appendHtmlBodyFooter(ReasonAttributes attributes) { + return notificationTemplateRender.render(""" + --- + + """, attributes); + } + + @Data + static class EmailSenderConfig { + private String displayName; + private String username; + private String password; + private String host; + private Integer port; + + /** + * Gets email display name. + * + * @return display name if not blank, otherwise username. + */ + public String getDisplayName() { + return StringUtils.defaultIfBlank(displayName, username); + } + } +} diff --git a/application/src/main/java/run/halo/app/notification/LanguageUtils.java b/application/src/main/java/run/halo/app/notification/LanguageUtils.java new file mode 100644 index 0000000000..2a0e281482 --- /dev/null +++ b/application/src/main/java/run/halo/app/notification/LanguageUtils.java @@ -0,0 +1,48 @@ +package run.halo.app.notification; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import lombok.experimental.UtilityClass; +import org.apache.commons.lang3.StringUtils; + +/** + * Language utils to help us to compute the language. + * + * @author guqing + * @since 2.10.0 + */ +@UtilityClass +public class LanguageUtils { + + /** + * Compute all the possible languages we should use: *_gl_ES-gheada, *_gl_ES, _gl... from a + * given locale. + * The first element of the list is "default" if it can not find the language, use default. + * + * @param locale locale + * @return list of possible languages, from less specific to more specific. + */ + public static List computeLangFromLocale(Locale locale) { + final List resourceNames = new ArrayList<>(5); + + if (StringUtils.isBlank(locale.getLanguage())) { + throw new IllegalArgumentException( + "Locale \"" + locale + "\" " + + "cannot be used as it does not specify a language."); + } + + resourceNames.add("default"); + resourceNames.add(locale.getLanguage()); + + if (StringUtils.isNotBlank(locale.getCountry())) { + resourceNames.add(locale.getLanguage() + "_" + locale.getCountry()); + } + + if (StringUtils.isNotBlank(locale.getVariant())) { + resourceNames.add( + locale.getLanguage() + "_" + locale.getCountry() + "-" + locale.getVariant()); + } + return resourceNames; + } +} diff --git a/application/src/main/java/run/halo/app/notification/NotificationSender.java b/application/src/main/java/run/halo/app/notification/NotificationSender.java new file mode 100644 index 0000000000..1946794c7a --- /dev/null +++ b/application/src/main/java/run/halo/app/notification/NotificationSender.java @@ -0,0 +1,21 @@ +package run.halo.app.notification; + +import reactor.core.publisher.Mono; +import run.halo.app.core.extension.notification.Reason; + +/** + *

{@link NotificationSender} used to send notification.

+ *

Send notification is a time-consuming task, so we use a queue to send notification + * asynchronously.

+ *

The caller may not be reactive, and in many cases it is blocking called + * {@link NotificationCenter#notify(Reason)}, so here use the queue to ensure asynchronous + * sending of notification without blocking the calling thread.

+ * + * @author guqing + * @since 2.10.0 + */ +@FunctionalInterface +public interface NotificationSender { + + Mono sendNotification(String notifierExtensionName, NotificationContext context); +} diff --git a/application/src/main/java/run/halo/app/notification/NotificationTemplateRender.java b/application/src/main/java/run/halo/app/notification/NotificationTemplateRender.java new file mode 100644 index 0000000000..6d843ae1e1 --- /dev/null +++ b/application/src/main/java/run/halo/app/notification/NotificationTemplateRender.java @@ -0,0 +1,15 @@ +package run.halo.app.notification; + +import java.util.Map; +import reactor.core.publisher.Mono; + +/** + * {@link NotificationTemplateRender} is used to render the notification template. + * + * @author guqing + * @since 2.10.0 + */ +public interface NotificationTemplateRender { + + Mono render(String template, Map context); +} diff --git a/application/src/main/java/run/halo/app/notification/NotificationTrigger.java b/application/src/main/java/run/halo/app/notification/NotificationTrigger.java new file mode 100644 index 0000000000..34f2de0628 --- /dev/null +++ b/application/src/main/java/run/halo/app/notification/NotificationTrigger.java @@ -0,0 +1,57 @@ +package run.halo.app.notification; + +import java.util.Set; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import run.halo.app.core.extension.notification.Reason; +import run.halo.app.extension.ExtensionClient; +import run.halo.app.extension.ExtensionUtil; +import run.halo.app.extension.controller.Controller; +import run.halo.app.extension.controller.ControllerBuilder; +import run.halo.app.extension.controller.Reconciler; + +/** + *

Notification trigger for {@link Reason}.

+ *

Triggered when a new {@link Reason} is received, and then notify through + * {@link NotificationCenter}.

+ *

It will add a finalizer to the {@link Reason} to avoid duplicate notification, In other + * words, it will only notify once.

+ * + * @author guqing + * @since 2.10.0 + */ +@Component +@RequiredArgsConstructor +public class NotificationTrigger implements Reconciler { + + public static final String TRIGGERED_FINALIZER = "triggered"; + + private final ExtensionClient client; + private final NotificationCenter notificationCenter; + + @Override + public Result reconcile(Request request) { + client.fetch(Reason.class, request.name()).ifPresent(reason -> { + if (ExtensionUtil.isDeleted(reason)) { + return; + } + if (ExtensionUtil.addFinalizers(reason.getMetadata(), Set.of(TRIGGERED_FINALIZER))) { + // notifier + onNewReasonReceived(reason); + client.update(reason); + } + }); + return Result.doNotRetry(); + } + + public void onNewReasonReceived(Reason reason) { + notificationCenter.notify(reason).block(); + } + + @Override + public Controller setupWith(ControllerBuilder builder) { + return builder + .extension(new Reason()) + .build(); + } +} diff --git a/application/src/main/java/run/halo/app/notification/NotifierConfigStore.java b/application/src/main/java/run/halo/app/notification/NotifierConfigStore.java new file mode 100644 index 0000000000..6382567af3 --- /dev/null +++ b/application/src/main/java/run/halo/app/notification/NotifierConfigStore.java @@ -0,0 +1,22 @@ +package run.halo.app.notification; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import reactor.core.publisher.Mono; + +/** + *

{@link NotifierConfigStore} to store notifier config.

+ *

It provides methods to fetch and save config for receiver and sender.

+ * + * @author guqing + * @since 2.10.0 + */ +public interface NotifierConfigStore { + + Mono fetchReceiverConfig(String notifierDescriptorName); + + Mono fetchSenderConfig(String notifierDescriptorName); + + Mono saveReceiverConfig(String notifierDescriptorName, ObjectNode config); + + Mono saveSenderConfig(String notifierDescriptorName, ObjectNode config); +} diff --git a/application/src/main/java/run/halo/app/notification/ReasonNotificationTemplateSelector.java b/application/src/main/java/run/halo/app/notification/ReasonNotificationTemplateSelector.java new file mode 100644 index 0000000000..e233fe4689 --- /dev/null +++ b/application/src/main/java/run/halo/app/notification/ReasonNotificationTemplateSelector.java @@ -0,0 +1,31 @@ +package run.halo.app.notification; + +import java.util.Locale; +import reactor.core.publisher.Mono; +import run.halo.app.core.extension.notification.NotificationTemplate; +import run.halo.app.core.extension.notification.ReasonType; +import run.halo.app.extension.Metadata; + +/** + * Reason notification template selector to select notification template by reason type and locale. + * + * @author guqing + * @see NotificationTemplate + * @see ReasonType + * @since 2.10.0 + */ +public interface ReasonNotificationTemplateSelector { + + /** + * Select notification template by reason type and locale. + *

Locale order is important: as we will let values from more specific to less specific (e.g. + * a value for gl_ES will have more precedence than a value for gl).

+ *

If specific locale found and has multiple templates, we will order them by + * {@link Metadata#getCreationTimestamp()} and return the latest one.

+ * + * @param reasonType reason type + * @param locale locale + * @return notification template if found, or empty + */ + Mono select(String reasonType, Locale locale); +} diff --git a/application/src/main/java/run/halo/app/notification/ReasonNotificationTemplateSelectorImpl.java b/application/src/main/java/run/halo/app/notification/ReasonNotificationTemplateSelectorImpl.java new file mode 100644 index 0000000000..8821f0e866 --- /dev/null +++ b/application/src/main/java/run/halo/app/notification/ReasonNotificationTemplateSelectorImpl.java @@ -0,0 +1,67 @@ +package run.halo.app.notification; + +import static org.apache.commons.lang3.StringUtils.defaultIfBlank; + +import java.util.Collections; +import java.util.Comparator; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.springframework.lang.Nullable; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Mono; +import run.halo.app.core.extension.notification.NotificationTemplate; +import run.halo.app.extension.ReactiveExtensionClient; + +/** + * A default implementation of {@link ReasonNotificationTemplateSelector}. + * + * @author guqing + * @since 2.10.0 + */ +@Component +@RequiredArgsConstructor +public class ReasonNotificationTemplateSelectorImpl implements ReasonNotificationTemplateSelector { + + private final ReactiveExtensionClient client; + + @Override + public Mono select(String reasonType, Locale locale) { + return client.list(NotificationTemplate.class, matchReasonType(reasonType), null) + .collect(Collectors.groupingBy( + getLanguageKey(), + Collectors.maxBy(Comparator.comparing(t -> t.getMetadata().getCreationTimestamp())) + )) + .mapNotNull(map -> lookupTemplateByLocale(locale, map)); + } + + @Nullable + static NotificationTemplate lookupTemplateByLocale(Locale locale, + Map> map) { + return LanguageUtils.computeLangFromLocale(locale).stream() + // reverse order to ensure that the variant is the first element and the default + // is the last element + .sorted(Collections.reverseOrder()) + .map(key -> map.getOrDefault(key, Optional.empty())) + .filter(Optional::isPresent) + .map(Optional::get) + .findFirst() + .orElse(null); + } + + @NonNull + static Predicate matchReasonType(String reasonType) { + return template -> template.getSpec().getReasonSelector().getReasonType() + .equals(reasonType); + } + + static Function getLanguageKey() { + return template -> defaultIfBlank(template.getSpec().getReasonSelector().getLanguage(), + "default"); + } +} diff --git a/application/src/main/java/run/halo/app/notification/SubscriberEmailResolver.java b/application/src/main/java/run/halo/app/notification/SubscriberEmailResolver.java new file mode 100644 index 0000000000..0a02d10c3a --- /dev/null +++ b/application/src/main/java/run/halo/app/notification/SubscriberEmailResolver.java @@ -0,0 +1,24 @@ +package run.halo.app.notification; + +import reactor.core.publisher.Mono; +import run.halo.app.core.extension.notification.Subscription; + +/** + *

{@link SubscriberEmailResolver} used to resolve email from {@link Subscription.Subscriber} + * .

+ * + * @author guqing + * @since 2.10.0 + */ +public interface SubscriberEmailResolver { + + Mono resolve(Subscription.Subscriber subscriber); + + /** + * Creates an email subscriber from email. + * + * @param email email + * @return email subscriber + */ + Subscription.Subscriber ofEmail(String email); +} diff --git a/application/src/main/java/run/halo/app/notification/UserNotificationPreference.java b/application/src/main/java/run/halo/app/notification/UserNotificationPreference.java new file mode 100644 index 0000000000..4762931529 --- /dev/null +++ b/application/src/main/java/run/halo/app/notification/UserNotificationPreference.java @@ -0,0 +1,42 @@ +package run.halo.app.notification; + +import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; + +import java.util.HashMap; +import java.util.Set; +import lombok.Data; +import lombok.Getter; + +/** + * Notification preference of user. + * + * @author guqing + * @since 2.10.0 + */ +@Getter +public class UserNotificationPreference { + private static final String DEFAULT_NOTIFIER = "default-email-notifier"; + + private final ReasonTypeNotifier reasonTypeNotifier = new ReasonTypeNotifier(); + + public static class ReasonTypeNotifier extends HashMap { + + /** + * Gets notifiers by reason type. + * + * @param reasonType reason type + * @return if key of reasonType not exists, return default notifier, otherwise return the + * notifiers + */ + public Set getNotifiers(String reasonType) { + var result = this.get(reasonType); + return result == null ? Set.of(DEFAULT_NOTIFIER) + : defaultIfNull(result.getNotifiers(), Set.of()); + } + } + + @Data + public static class NotifierSetting { + private Set notifiers; + } +} diff --git a/application/src/main/java/run/halo/app/notification/UserNotificationPreferenceService.java b/application/src/main/java/run/halo/app/notification/UserNotificationPreferenceService.java new file mode 100644 index 0000000000..a83eb33aaf --- /dev/null +++ b/application/src/main/java/run/halo/app/notification/UserNotificationPreferenceService.java @@ -0,0 +1,14 @@ +package run.halo.app.notification; + +import reactor.core.publisher.Mono; + +/** + * User notification preference service. + * + * @author guqing + * @since 2.10.0 + */ +public interface UserNotificationPreferenceService { + + Mono getByUser(String username); +} diff --git a/application/src/main/java/run/halo/app/notification/UserNotificationPreferenceServiceImpl.java b/application/src/main/java/run/halo/app/notification/UserNotificationPreferenceServiceImpl.java new file mode 100644 index 0000000000..bcb50dcbc8 --- /dev/null +++ b/application/src/main/java/run/halo/app/notification/UserNotificationPreferenceServiceImpl.java @@ -0,0 +1,45 @@ +package run.halo.app.notification; + +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Mono; +import run.halo.app.extension.ConfigMap; +import run.halo.app.extension.ReactiveExtensionClient; +import run.halo.app.infra.utils.JsonUtils; + +/** + * User notification preference service implementation. + * + * @author guqing + * @since 2.10.0 + */ +@Component +@RequiredArgsConstructor +public class UserNotificationPreferenceServiceImpl implements UserNotificationPreferenceService { + + public static final String NOTIFICATION_PREFERENCE = "notification"; + + private final ReactiveExtensionClient client; + + @Override + public Mono getByUser(String username) { + var configName = buildUserPreferenceConfigMapName(username); + return client.fetch(ConfigMap.class, configName) + .map(config -> { + if (config.getData() == null) { + return new UserNotificationPreference(); + } + String s = config.getData().get(NOTIFICATION_PREFERENCE); + if (StringUtils.isNotBlank(s)) { + return JsonUtils.jsonToObject(s, UserNotificationPreference.class); + } + return new UserNotificationPreference(); + }) + .defaultIfEmpty(new UserNotificationPreference()); + } + + static String buildUserPreferenceConfigMapName(String username) { + return "user-preferences-" + username; + } +} diff --git a/application/src/main/java/run/halo/app/notification/UserNotificationQuery.java b/application/src/main/java/run/halo/app/notification/UserNotificationQuery.java new file mode 100644 index 0000000000..bfaabe711b --- /dev/null +++ b/application/src/main/java/run/halo/app/notification/UserNotificationQuery.java @@ -0,0 +1,115 @@ +package run.halo.app.notification; + +import static java.util.Comparator.comparing; + +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.function.Predicate; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.domain.Sort; +import org.springframework.lang.Nullable; +import org.springframework.web.server.ServerWebExchange; +import run.halo.app.core.extension.endpoint.SortResolver; +import run.halo.app.core.extension.notification.Notification; +import run.halo.app.extension.Comparators; +import run.halo.app.extension.router.IListRequest; + +/** + * Notification query object for authenticated user. + * + * @author guqing + * @since 2.10.0 + */ +public class UserNotificationQuery extends IListRequest.QueryListRequest { + + private final ServerWebExchange exchange; + + public UserNotificationQuery(ServerWebExchange exchange) { + super(exchange.getRequest().getQueryParams()); + this.exchange = exchange; + } + + @Nullable + public String getKeyword() { + return StringUtils.defaultIfBlank(queryParams.getFirst("keyword"), null); + } + + @Nullable + @Schema(description = "true for unread, false for read, null for all") + public Boolean getUnRead() { + var unreadStr = queryParams.getFirst("unRead"); + return StringUtils.isBlank(unreadStr) ? null : Boolean.parseBoolean(unreadStr); + } + + @Nullable + @Schema(description = "Filter by notification reason") + public String getReason() { + return StringUtils.defaultIfBlank(queryParams.getFirst("reason"), null); + } + + @ArraySchema(uniqueItems = true, + arraySchema = @Schema(name = "sort", + description = "Sort property and direction of the list result. Supported fields: " + + "creationTimestamp"), + schema = @Schema(description = "like field,asc or field,desc", + implementation = String.class, + example = "creationTimestamp,desc")) + public Sort getSort() { + return SortResolver.defaultInstance.resolve(exchange); + } + + /** + * Build a predicate from the query object. + * + * @return a predicate + */ + public Predicate toPredicate() { + var unRead = getUnRead(); + var reason = getReason(); + Predicate predicate = notification -> true; + if (unRead != null) { + predicate = predicate.and(notification + -> notification.getSpec().isUnread() == unRead); + } + + if (reason != null) { + predicate = predicate.and(notification + -> reason.equals(notification.getSpec().getReason())); + } + + if (getKeyword() != null) { + predicate = predicate.and(notification + -> notification.getSpec().getTitle().contains(getKeyword()) + || notification.getSpec().getHtmlContent().contains(getKeyword()) + || notification.getSpec().getRawContent().contains(getKeyword())); + } + return predicate; + } + + /** + * Build a comparator from the query object. + * + * @return a comparator + */ + public Comparator toComparator() { + var sort = getSort(); + var creationTimestampOrder = sort.getOrderFor("creationTimestamp"); + List> comparators = new ArrayList<>(); + if (creationTimestampOrder != null) { + Comparator comparator = + comparing(notification -> notification.getMetadata().getCreationTimestamp()); + if (creationTimestampOrder.isDescending()) { + comparator = comparator.reversed(); + } + comparators.add(comparator); + } + + comparators.add(Comparators.defaultComparator()); + return comparators.stream() + .reduce(Comparator::thenComparing) + .orElse(null); + } +} \ No newline at end of file diff --git a/application/src/main/java/run/halo/app/notification/UserNotificationService.java b/application/src/main/java/run/halo/app/notification/UserNotificationService.java new file mode 100644 index 0000000000..34bf5b5fcb --- /dev/null +++ b/application/src/main/java/run/halo/app/notification/UserNotificationService.java @@ -0,0 +1,40 @@ +package run.halo.app.notification; + +import java.util.List; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import run.halo.app.core.extension.notification.Notification; +import run.halo.app.extension.ListResult; + +/** + * Notification service. + * + * @author guqing + * @since 2.10.0 + */ +public interface UserNotificationService { + + /** + * List notifications for the authenticated user. + * + * @param query query object + * @return a page result of notifications + */ + Mono> listByUser(String username, UserNotificationQuery query); + + /** + * Mark the specified notification as read. + * + * @param name notification name + * @return read notification + */ + Mono markAsRead(String username, String name); + + /** + * Mark the specified notifications as read. + * + * @param names the names of notifications + * @return the names of read notification that has been marked as read + */ + Flux markSpecifiedAsRead(String username, List names); +} diff --git a/application/src/main/java/run/halo/app/notification/endpoint/ConsoleNotifierEndpoint.java b/application/src/main/java/run/halo/app/notification/endpoint/ConsoleNotifierEndpoint.java new file mode 100644 index 0000000000..b97f081e17 --- /dev/null +++ b/application/src/main/java/run/halo/app/notification/endpoint/ConsoleNotifierEndpoint.java @@ -0,0 +1,89 @@ +package run.halo.app.notification.endpoint; + +import static org.springdoc.core.fn.builders.apiresponse.Builder.responseBuilder; +import static org.springdoc.core.fn.builders.content.Builder.contentBuilder; +import static org.springdoc.core.fn.builders.parameter.Builder.parameterBuilder; +import static org.springdoc.core.fn.builders.requestbody.Builder.requestBodyBuilder; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import lombok.RequiredArgsConstructor; +import org.springdoc.core.fn.builders.schema.Builder; +import org.springdoc.webflux.core.fn.SpringdocRouteBuilder; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import org.springframework.web.server.ServerWebInputException; +import reactor.core.publisher.Mono; +import run.halo.app.core.extension.endpoint.CustomEndpoint; +import run.halo.app.notification.NotifierConfigStore; + +/** + * Custom notifier endpoint. + * + * @author guqing + * @since 2.10.0 + */ +@Component +@RequiredArgsConstructor +public class ConsoleNotifierEndpoint implements CustomEndpoint { + + private final NotifierConfigStore notifierConfigStore; + + @Override + public RouterFunction endpoint() { + var tag = "api.console.halo.run/v1alpha1/Notifier"; + return SpringdocRouteBuilder.route() + .GET("/notifiers/{name}/sender-config", this::fetchSenderConfig, + builder -> builder.operationId("FetchSenderConfig") + .description("Fetch sender config of notifier") + .tag(tag) + .parameter(parameterBuilder() + .in(ParameterIn.PATH) + .name("name") + .description("Notifier name") + .required(true) + ) + .response(responseBuilder().implementation(ObjectNode.class)) + ) + .POST("/notifiers/{name}/sender-config", this::saveSenderConfig, + builder -> builder.operationId("SaveSenderConfig") + .description("Save sender config of notifier") + .tag(tag) + .parameter(parameterBuilder() + .in(ParameterIn.PATH) + .name("name") + .description("Notifier name") + .required(true) + ) + .requestBody(requestBodyBuilder() + .required(true) + .content(contentBuilder() + .mediaType(MediaType.APPLICATION_JSON_VALUE) + .schema(Builder.schemaBuilder() + .implementation(ObjectNode.class)) + ) + ) + .response(responseBuilder().implementation(Void.class)) + ) + .build(); + } + + private Mono fetchSenderConfig(ServerRequest request) { + var name = request.pathVariable("name"); + return notifierConfigStore.fetchSenderConfig(name) + .flatMap(config -> ServerResponse.ok().bodyValue(config)); + } + + private Mono saveSenderConfig(ServerRequest request) { + var name = request.pathVariable("name"); + return request.bodyToMono(ObjectNode.class) + .switchIfEmpty(Mono.error( + () -> new ServerWebInputException("Request body must not be empty.")) + ) + .flatMap(jsonNode -> notifierConfigStore.saveSenderConfig(name, jsonNode)) + .then(ServerResponse.ok().build()); + } +} diff --git a/application/src/main/java/run/halo/app/notification/endpoint/SubscriptionRouter.java b/application/src/main/java/run/halo/app/notification/endpoint/SubscriptionRouter.java new file mode 100644 index 0000000000..1bc6aa533c --- /dev/null +++ b/application/src/main/java/run/halo/app/notification/endpoint/SubscriptionRouter.java @@ -0,0 +1,91 @@ +package run.halo.app.notification.endpoint; + +import static org.springdoc.core.fn.builders.apiresponse.Builder.responseBuilder; +import static org.springdoc.core.fn.builders.parameter.Builder.parameterBuilder; + +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springdoc.webflux.core.fn.SpringdocRouteBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import org.springframework.web.util.UriComponentsBuilder; +import reactor.core.publisher.Mono; +import run.halo.app.core.extension.notification.Subscription; +import run.halo.app.extension.ReactiveExtensionClient; +import run.halo.app.infra.ExternalUrlSupplier; + +/** + * A router for {@link Subscription}. + * + * @author guqing + * @since 2.10.0 + */ +@Component +@RequiredArgsConstructor +public class SubscriptionRouter { + + public static final String UNSUBSCRIBE_PATTERN = + "/apis/api.notification.halo.run/v1alpha1/subscriptions/{name}/unsubscribe"; + + private final ExternalUrlSupplier externalUrlSupplier; + private final ReactiveExtensionClient client; + + @Bean + RouterFunction notificationSubscriptionRouter() { + return SpringdocRouteBuilder.route() + .GET(UNSUBSCRIBE_PATTERN, this::unsubscribe, builder -> { + builder.operationId("Unsubscribe") + .tag("api.notification.halo.run/v1alpha1/Subscription") + .description("Unsubscribe a subscription") + .parameter(parameterBuilder() + .in(ParameterIn.PATH) + .name("name") + .description("Subscription name") + .required(true) + .in(ParameterIn.QUERY) + .name("token") + .description("Unsubscribe token") + .required(true) + ) + .response(responseBuilder().implementation(String.class)) + .build(); + }) + .build(); + } + + Mono unsubscribe(ServerRequest request) { + var name = request.pathVariable("name"); + var token = request.queryParam("token").orElse(""); + return client.fetch(Subscription.class, name) + .filter(subscription -> { + var unsubscribeToken = subscription.getSpec().getUnsubscribeToken(); + return StringUtils.equals(token, unsubscribeToken); + }) + .flatMap(client::delete) + .then(Mono.defer(() -> ServerResponse.ok() + .contentType(MediaType.TEXT_PLAIN) + .bodyValue("Unsubscribe successfully.")) + ); + } + + /** + * Gets unsubscribe url from the given subscription. + * + * @param subscription subscription must not be null + * @return unsubscribe url + */ + public String getUnsubscribeUrl(Subscription subscription) { + var name = subscription.getMetadata().getName(); + var token = subscription.getSpec().getUnsubscribeToken(); + return UriComponentsBuilder.fromUri(externalUrlSupplier.get()) + .path(UNSUBSCRIBE_PATTERN) + .queryParam("token", token) + .build(name) + .toString(); + } +} diff --git a/application/src/main/java/run/halo/app/notification/endpoint/UserNotificationEndpoint.java b/application/src/main/java/run/halo/app/notification/endpoint/UserNotificationEndpoint.java new file mode 100644 index 0000000000..6980d66e4e --- /dev/null +++ b/application/src/main/java/run/halo/app/notification/endpoint/UserNotificationEndpoint.java @@ -0,0 +1,142 @@ +package run.halo.app.notification.endpoint; + +import static org.springdoc.core.fn.builders.apiresponse.Builder.responseBuilder; +import static org.springdoc.core.fn.builders.content.Builder.contentBuilder; +import static org.springdoc.core.fn.builders.parameter.Builder.parameterBuilder; +import static org.springdoc.core.fn.builders.requestbody.Builder.requestBodyBuilder; + +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import java.util.List; +import java.util.function.Supplier; +import lombok.RequiredArgsConstructor; +import org.springdoc.core.fn.builders.schema.Builder; +import org.springdoc.webflux.core.fn.SpringdocRouteBuilder; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.RequestPredicates; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import reactor.core.publisher.Mono; +import run.halo.app.core.extension.endpoint.CustomEndpoint; +import run.halo.app.core.extension.notification.Notification; +import run.halo.app.extension.GroupVersion; +import run.halo.app.extension.ListResult; +import run.halo.app.extension.router.QueryParamBuildUtil; +import run.halo.app.notification.UserNotificationQuery; +import run.halo.app.notification.UserNotificationService; + +/** + * Custom notification endpoint to managing notification for authenticated user. + * + * @author guqing + * @since 2.10.0 + */ +@Component +@RequiredArgsConstructor +public class UserNotificationEndpoint implements CustomEndpoint { + + private final UserNotificationService notificationService; + + @Override + public RouterFunction endpoint() { + return SpringdocRouteBuilder.route() + .nest(RequestPredicates.path("/userspaces/{username}"), userspaceScopedApis(), + builder -> { + }) + .build(); + } + + Supplier> userspaceScopedApis() { + var tag = "api.notification.halo.run/v1alpha1/Notification"; + return () -> SpringdocRouteBuilder.route() + .GET("/notifications", this::listNotification, + builder -> { + builder.operationId("ListUserNotifications") + .description("List notifications for the authenticated user.") + .tag(tag) + .parameter(parameterBuilder() + .in(ParameterIn.PATH) + .name("username") + .description("Username") + .required(true) + ) + .response(responseBuilder() + .implementation(ListResult.generateGenericClass(Notification.class)) + ); + QueryParamBuildUtil.buildParametersFromType(builder, + UserNotificationQuery.class); + } + ) + .PUT("/notifications/{name}/mark-as-read", this::markNotificationAsRead, + builder -> builder.operationId("MarkNotificationAsRead") + .description("Mark the specified notification as read.") + .tag(tag) + .parameter(parameterBuilder() + .in(ParameterIn.PATH) + .name("username") + .description("Username") + .required(true) + ) + .parameter(parameterBuilder() + .in(ParameterIn.PATH) + .name("name") + .description("Notification name") + .required(true) + ) + .response(responseBuilder().implementation(Notification.class)) + ) + .PUT("/notifications/-/mark-specified-as-read", this::markNotificationsAsRead, + builder -> builder.operationId("MarkNotificationsAsRead") + .description("Mark the specified notifications as read.") + .tag(tag) + .parameter(parameterBuilder() + .in(ParameterIn.PATH) + .name("username") + .description("Username") + .required(true) + ) + .requestBody(requestBodyBuilder() + .required(true) + .content(contentBuilder() + .mediaType(MediaType.APPLICATION_JSON_VALUE) + .schema(Builder.schemaBuilder() + .implementation(MarkSpecifiedRequest.class)) + ) + ) + .response(responseBuilder().implementationArray(String.class)) + ) + .build(); + } + + @Override + public GroupVersion groupVersion() { + return GroupVersion.parseAPIVersion("api.notification.halo.run/v1alpha1"); + } + + record MarkSpecifiedRequest(List names) { + } + + private Mono listNotification(ServerRequest request) { + var query = new UserNotificationQuery(request.exchange()); + var username = request.pathVariable("username"); + return notificationService.listByUser(username, query) + .flatMap(notifications -> ServerResponse.ok().bodyValue(notifications)); + } + + private Mono markNotificationAsRead(ServerRequest request) { + var username = request.pathVariable("username"); + var name = request.pathVariable("name"); + return notificationService.markAsRead(username, name) + .flatMap(notification -> ServerResponse.ok().bodyValue(notification)); + } + + Mono markNotificationsAsRead(ServerRequest request) { + var username = request.pathVariable("username"); + return request.bodyToMono(MarkSpecifiedRequest.class) + .flatMapMany( + requestBody -> notificationService.markSpecifiedAsRead(username, requestBody.names)) + .collectList() + .flatMap(names -> ServerResponse.ok().bodyValue(names)); + } +} diff --git a/application/src/main/java/run/halo/app/notification/endpoint/UserNotifierEndpoint.java b/application/src/main/java/run/halo/app/notification/endpoint/UserNotifierEndpoint.java new file mode 100644 index 0000000000..31a98d1f36 --- /dev/null +++ b/application/src/main/java/run/halo/app/notification/endpoint/UserNotifierEndpoint.java @@ -0,0 +1,95 @@ +package run.halo.app.notification.endpoint; + +import static org.springdoc.core.fn.builders.apiresponse.Builder.responseBuilder; +import static org.springdoc.core.fn.builders.content.Builder.contentBuilder; +import static org.springdoc.core.fn.builders.parameter.Builder.parameterBuilder; +import static org.springdoc.core.fn.builders.requestbody.Builder.requestBodyBuilder; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import lombok.RequiredArgsConstructor; +import org.springdoc.core.fn.builders.schema.Builder; +import org.springdoc.webflux.core.fn.SpringdocRouteBuilder; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.reactive.function.server.ServerResponse; +import org.springframework.web.server.ServerWebInputException; +import reactor.core.publisher.Mono; +import run.halo.app.core.extension.endpoint.CustomEndpoint; +import run.halo.app.extension.GroupVersion; +import run.halo.app.notification.NotifierConfigStore; + +/** + * Notifier endpoint for user center. + * + * @author guqing + * @since 2.10.0 + */ +@Component +@RequiredArgsConstructor +public class UserNotifierEndpoint implements CustomEndpoint { + + private final NotifierConfigStore notifierConfigStore; + + @Override + public RouterFunction endpoint() { + var tag = "api.notification.halo.run/v1alpha1/Notifier"; + return SpringdocRouteBuilder.route() + .GET("/notifiers/{name}/receiver-config", this::fetchReceiverConfig, + builder -> builder.operationId("FetchReceiverConfig") + .description("Fetch receiver config of notifier") + .tag(tag) + .parameter(parameterBuilder() + .in(ParameterIn.PATH) + .name("name") + .description("Notifier name") + .required(true) + ) + .response(responseBuilder().implementation(ObjectNode.class)) + ) + .POST("/notifiers/{name}/receiver-config", this::saveReceiverConfig, + builder -> builder.operationId("SaveReceiverConfig") + .description("Save receiver config of notifier") + .tag(tag) + .parameter(parameterBuilder() + .in(ParameterIn.PATH) + .name("name") + .description("Notifier name") + .required(true) + ) + .requestBody(requestBodyBuilder() + .required(true) + .content(contentBuilder() + .mediaType(MediaType.APPLICATION_JSON_VALUE) + .schema(Builder.schemaBuilder() + .implementation(ObjectNode.class)) + ) + ) + .response(responseBuilder().implementation(Void.class)) + ) + .build(); + } + + private Mono fetchReceiverConfig(ServerRequest request) { + var name = request.pathVariable("name"); + return notifierConfigStore.fetchReceiverConfig(name) + .flatMap(config -> ServerResponse.ok().bodyValue(config)); + } + + private Mono saveReceiverConfig(ServerRequest request) { + var name = request.pathVariable("name"); + return request.bodyToMono(ObjectNode.class) + .switchIfEmpty(Mono.error( + () -> new ServerWebInputException("Request body must not be empty.")) + ) + .flatMap(jsonNode -> notifierConfigStore.saveReceiverConfig(name, jsonNode)) + .then(ServerResponse.ok().build()); + } + + @Override + public GroupVersion groupVersion() { + return GroupVersion.parseAPIVersion("api.notification.halo.run/v1alpha1"); + } +} diff --git a/application/src/main/java/run/halo/app/plugin/SharedApplicationContextHolder.java b/application/src/main/java/run/halo/app/plugin/SharedApplicationContextHolder.java index 96b3a2e4a0..d0b0baec80 100644 --- a/application/src/main/java/run/halo/app/plugin/SharedApplicationContextHolder.java +++ b/application/src/main/java/run/halo/app/plugin/SharedApplicationContextHolder.java @@ -9,7 +9,10 @@ import run.halo.app.extension.ExtensionClient; import run.halo.app.extension.ReactiveExtensionClient; import run.halo.app.infra.BackupRootGetter; +import run.halo.app.infra.ExternalLinkProcessor; import run.halo.app.infra.ExternalUrlSupplier; +import run.halo.app.notification.NotificationCenter; +import run.halo.app.notification.NotificationReasonEmitter; /** *

This {@link SharedApplicationContextHolder} class is used to hold a singleton instance of @@ -73,6 +76,12 @@ SharedApplicationContext createSharedApplicationContext() { rootApplicationContext.getBean(AttachmentService.class)); beanFactory.registerSingleton("backupRootGetter", rootApplicationContext.getBean(BackupRootGetter.class)); + beanFactory.registerSingleton("notificationReasonEmitter", + rootApplicationContext.getBean(NotificationReasonEmitter.class)); + beanFactory.registerSingleton("notificationCenter", + rootApplicationContext.getBean(NotificationCenter.class)); + beanFactory.registerSingleton("externalLinkProcessor", + rootApplicationContext.getBean(ExternalLinkProcessor.class)); // TODO add more shared instance here return sharedApplicationContext; diff --git a/application/src/main/java/run/halo/app/security/authentication/pat/PatAuthenticationManager.java b/application/src/main/java/run/halo/app/security/authentication/pat/PatAuthenticationManager.java index 02f8cee197..13f0db9b61 100644 --- a/application/src/main/java/run/halo/app/security/authentication/pat/PatAuthenticationManager.java +++ b/application/src/main/java/run/halo/app/security/authentication/pat/PatAuthenticationManager.java @@ -6,7 +6,9 @@ import com.nimbusds.jwt.JWTClaimNames; import java.time.Clock; +import java.time.Duration; import java.util.Objects; +import org.springframework.dao.OptimisticLockingFailureException; import org.springframework.security.authentication.DisabledException; import org.springframework.security.authentication.ReactiveAuthenticationManager; import org.springframework.security.core.Authentication; @@ -17,6 +19,7 @@ import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverter; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import reactor.util.retry.Retry; import run.halo.app.extension.ExtensionUtil; import run.halo.app.extension.ReactiveExtensionClient; import run.halo.app.security.PersonalAccessToken; @@ -24,6 +27,11 @@ public class PatAuthenticationManager implements ReactiveAuthenticationManager { + /** + * Minimal duration gap of personal access token update. + */ + private static final Duration MIN_UPDATE_GAP = Duration.ofMinutes(1); + private final ReactiveAuthenticationManager delegate; private final ReactiveExtensionClient client; @@ -78,17 +86,33 @@ private Mono checkAvailability(JwtAuthenticationToken jwtAuthToken) { return client.fetch(PersonalAccessToken.class, patName) .switchIfEmpty( Mono.error(() -> new DisabledException("Personal access token has been deleted."))) - .flatMap(pat -> patChecks(pat, jwtId) - .then(updateLastUsed(pat)) - .then() - ); + .flatMap(pat -> patChecks(pat, jwtId).and(updateLastUsed(patName))); } - private Mono updateLastUsed(PersonalAccessToken pat) { - return Mono.defer(() -> { - pat.getSpec().setLastUsed(clock.instant()); - return client.update(pat); - }); + private Mono updateLastUsed(String patName) { + // we try our best to update the last used timestamp. + + // the now should be outside the retry cycle because we don't want a fresh timestamp at + // every retry. + var now = clock.instant(); + return Mono.defer( + // we have to obtain a fresh PAT and retry the update. + () -> client.fetch(PersonalAccessToken.class, patName) + .filter(pat -> { + var lastUsed = pat.getSpec().getLastUsed(); + if (lastUsed == null) { + return true; + } + var diff = Duration.between(lastUsed, now); + return !diff.minus(MIN_UPDATE_GAP).isNegative(); + }) + .doOnNext(pat -> pat.getSpec().setLastUsed(now)) + .flatMap(client::update) + ) + .retryWhen(Retry.backoff(3, Duration.ofMillis(50)) + .filter(OptimisticLockingFailureException.class::isInstance)) + .onErrorComplete() + .then(); } private Mono patChecks(PersonalAccessToken pat, String tokenId) { diff --git a/application/src/main/java/run/halo/app/security/authorization/Attributes.java b/application/src/main/java/run/halo/app/security/authorization/Attributes.java index 0279d844e9..c4a0758f7e 100644 --- a/application/src/main/java/run/halo/app/security/authorization/Attributes.java +++ b/application/src/main/java/run/halo/app/security/authorization/Attributes.java @@ -67,4 +67,6 @@ public interface Attributes { String getPath(); String getSubName(); + + String getUserSpace(); } diff --git a/application/src/main/java/run/halo/app/security/authorization/AttributesRecord.java b/application/src/main/java/run/halo/app/security/authorization/AttributesRecord.java index 89407be5c7..9ac9869063 100644 --- a/application/src/main/java/run/halo/app/security/authorization/AttributesRecord.java +++ b/application/src/main/java/run/halo/app/security/authorization/AttributesRecord.java @@ -72,4 +72,9 @@ public String getPath() { public String getSubName() { return requestInfo.getSubName(); } + + @Override + public String getUserSpace() { + return requestInfo.getUserspace(); + } } diff --git a/application/src/main/java/run/halo/app/security/authorization/AuthorizingVisitor.java b/application/src/main/java/run/halo/app/security/authorization/AuthorizingVisitor.java index e4df9ddcbc..5ff786abfb 100644 --- a/application/src/main/java/run/halo/app/security/authorization/AuthorizingVisitor.java +++ b/application/src/main/java/run/halo/app/security/authorization/AuthorizingVisitor.java @@ -10,7 +10,7 @@ * @author guqing * @since 2.0.0 */ -class AuthorizingVisitor implements RuleAccumulator { +public class AuthorizingVisitor implements RuleAccumulator { private final RbacRequestEvaluation requestEvaluation = new RbacRequestEvaluation(); private final Attributes requestAttributes; diff --git a/application/src/main/java/run/halo/app/security/authorization/DefaultRuleResolver.java b/application/src/main/java/run/halo/app/security/authorization/DefaultRuleResolver.java index cbd55a39f2..e40f2554bd 100644 --- a/application/src/main/java/run/halo/app/security/authorization/DefaultRuleResolver.java +++ b/application/src/main/java/run/halo/app/security/authorization/DefaultRuleResolver.java @@ -4,6 +4,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import lombok.Data; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; @@ -41,6 +42,18 @@ public Mono visitRules(UserDetails user, RequestInfo request var record = new AttributesRecord(user, requestInfo); var visitor = new AuthorizingVisitor(record); + + // If the request is an userspace scoped request, + // then we should check whether the user is the owner of the userspace. + if (StringUtils.isNotBlank(requestInfo.getUserspace())) { + if (!user.getUsername().equals(requestInfo.getUserspace())) { + return Mono.fromSupplier(() -> { + visitor.visit(null, null, null); + return visitor; + }); + } + } + var stopVisiting = new AtomicBoolean(false); return roleService.listDependenciesFlux(roleNames) .filter(role -> !CollectionUtils.isEmpty(role.getRules())) diff --git a/application/src/main/java/run/halo/app/security/authorization/RequestInfo.java b/application/src/main/java/run/halo/app/security/authorization/RequestInfo.java index a280a88f53..8295f3ab5a 100644 --- a/application/src/main/java/run/halo/app/security/authorization/RequestInfo.java +++ b/application/src/main/java/run/halo/app/security/authorization/RequestInfo.java @@ -18,6 +18,7 @@ public class RequestInfo { boolean isResourceRequest; final String path; String namespace; + String userspace; String verb; String apiPrefix; String apiGroup; @@ -33,10 +34,12 @@ public class RequestInfo { String[] parts; public RequestInfo(boolean isResourceRequest, String path, String verb) { - this(isResourceRequest, path, null, verb, null, null, null, null, null, null, null, null); + this(isResourceRequest, path, null, null, verb, null, null, null, null, null, null, null, + null); } - public RequestInfo(boolean isResourceRequest, String path, String namespace, String verb, + public RequestInfo(boolean isResourceRequest, String path, String namespace, String userspace, + String verb, String apiPrefix, String apiGroup, String apiVersion, String resource, String name, String subresource, String subName, @@ -44,6 +47,7 @@ public RequestInfo(boolean isResourceRequest, String path, String namespace, Str this.isResourceRequest = isResourceRequest; this.path = StringUtils.defaultString(path, ""); this.namespace = StringUtils.defaultString(namespace, ""); + this.userspace = StringUtils.defaultString(userspace, ""); this.verb = StringUtils.defaultString(verb, ""); this.apiPrefix = StringUtils.defaultString(apiPrefix, ""); this.apiGroup = StringUtils.defaultString(apiGroup, ""); diff --git a/application/src/main/java/run/halo/app/security/authorization/RequestInfoFactory.java b/application/src/main/java/run/halo/app/security/authorization/RequestInfoFactory.java index 3dcbbb5baf..1fb898e5d3 100644 --- a/application/src/main/java/run/halo/app/security/authorization/RequestInfoFactory.java +++ b/application/src/main/java/run/halo/app/security/authorization/RequestInfoFactory.java @@ -57,6 +57,8 @@ public RequestInfoFactory(Set apiPrefixes, Set grouplessApiPrefi * /api/{version}/namespaces/{namespace} * /api/{version}/namespaces/{namespace}/{resource} * /api/{version}/namespaces/{namespace}/{resource}/{resourceName} + * /api/{version}/userspaces/{userspace}/{resource} + * /api/{version}/userspaces/{userspace}/{resource}/{resourceName} * /api/{version}/{resource} * /api/{version}/{resource}/{resourceName} * @@ -153,7 +155,18 @@ public RequestInfo newRequestInfo(ServerHttpRequest request) { currentParts = Arrays.copyOfRange(currentParts, 2, currentParts.length); } } + } else if ("userspaces".equals(currentParts[0])) { + if (currentParts.length > 1) { + requestInfo.userspace = currentParts[1]; + + // if there is another step after the userspace name + // move currentParts to include it as a resource in its own right + if (currentParts.length > 2) { + currentParts = Arrays.copyOfRange(currentParts, 2, currentParts.length); + } + } } else { + requestInfo.userspace = ""; requestInfo.namespace = ""; } @@ -186,7 +199,7 @@ public RequestInfo newRequestInfo(ServerHttpRequest request) { // if there's no name on the request and we thought it was a get before, then the actual // verb is a list or a watch - if (requestInfo.name.length() == 0 && "get".equals(requestInfo.verb)) { + if (requestInfo.name.isEmpty() && "get".equals(requestInfo.verb)) { var watch = request.getQueryParams().getFirst("watch"); if (Boolean.parseBoolean(watch)) { requestInfo.verb = "watch"; diff --git a/application/src/main/resources/extensions/extension-definitions.yaml b/application/src/main/resources/extensions/extension-definitions.yaml index 89f58c470e..985b1da3da 100644 --- a/application/src/main/resources/extensions/extension-definitions.yaml +++ b/application/src/main/resources/extensions/extension-definitions.yaml @@ -33,3 +33,14 @@ spec: extensionPointName: additional-webfilter displayName: "DelegatingLogoutPageGeneratingWebFilter" description: "Generates a default log out page." + +--- +apiVersion: plugin.halo.run/v1alpha1 +kind: ExtensionDefinition +metadata: + name: halo-email-notifier +spec: + className: run.halo.app.notification.EmailNotifier + extensionPointName: reactive-notifier + displayName: "EmailNotifier" + description: "Support sending notifications to users via email" \ No newline at end of file diff --git a/application/src/main/resources/extensions/extensionpoint-definitions.yaml b/application/src/main/resources/extensions/extensionpoint-definitions.yaml index be473f68e4..a3d0042fd4 100644 --- a/application/src/main/resources/extensions/extensionpoint-definitions.yaml +++ b/application/src/main/resources/extensions/extensionpoint-definitions.yaml @@ -52,3 +52,14 @@ spec: displayName: Username password authentication manager type: SINGLETON description: "Provides a way to extend the username password authentication." + +--- +apiVersion: plugin.halo.run/v1alpha1 +kind: ExtensionPointDefinition +metadata: + name: reactive-notifier +spec: + className: run.halo.app.notification.ReactiveNotifier + displayName: Notifier + type: MULTI_INSTANCE + description: "Provides a way to extend the notifier to send notifications to users." \ No newline at end of file diff --git a/application/src/main/resources/extensions/notification-templates.yaml b/application/src/main/resources/extensions/notification-templates.yaml new file mode 100644 index 0000000000..7f88488934 --- /dev/null +++ b/application/src/main/resources/extensions/notification-templates.yaml @@ -0,0 +1,101 @@ +apiVersion: notification.halo.run/v1alpha1 +kind: NotificationTemplate +metadata: + name: template-new-comment-on-post +spec: + reasonSelector: + reasonType: new-comment-on-post + language: default + template: + title: "[(${commenter})] 评论了您的文章《[(${postTitle})]》" + rawBody: | + [(${subscriber.displayName})] 你好: + + [(${commenter})] 评论了您的文章 《[(${postTitle})]》,以下是评论的具体内容: + + [(${content})] + htmlBody: | +

+
+

+
+
+

+ 评论了您的文章 + + ,以下是评论的具体内容: +

+

+        
+
+
+ +--- +apiVersion: notification.halo.run/v1alpha1 +kind: NotificationTemplate +metadata: + name: template-new-comment-on-single-page +spec: + reasonSelector: + reasonType: new-comment-on-single-page + language: default + template: + title: "[(${commenter})] 评论了您的页面《[(${pageTitle})]》" + rawBody: | + [(${subscriber.displayName})] 你好: + + [(${commenter})] 评论了您的页面 《[(${pageTitle})]》,以下是评论的具体内容: + + [(${content})] + htmlBody: | +
+
+

+
+
+

+ 评论了您的页面 + + ,以下是评论的具体内容: +

+

+        
+
+
+ +--- +apiVersion: notification.halo.run/v1alpha1 +kind: NotificationTemplate +metadata: + name: template-someone-replied-to-you +spec: + reasonSelector: + reasonType: someone-replied-to-you + language: default + template: + title: "[(${replier})] 在评论中回复了您" + rawBody: | + [(${subscriber.displayName})] 你好: + + [(${replier})] 在评论“[(${isQuoteReply ? quoteContent : commentContent})]”中回复了您,以下是回复的具体内容: + + [(${content})] + htmlBody: | +
+
+

+
+
+

+ 在评论 + + 中回复了您,以下是回复的具体内容: +

+

+        
+
+
diff --git a/application/src/main/resources/extensions/notification.yaml b/application/src/main/resources/extensions/notification.yaml new file mode 100644 index 0000000000..67dcb39ed0 --- /dev/null +++ b/application/src/main/resources/extensions/notification.yaml @@ -0,0 +1,124 @@ +apiVersion: notification.halo.run/v1alpha1 +kind: NotifierDescriptor +metadata: + name: default-email-notifier +spec: + displayName: '邮件通知' + description: '通过邮件将通知发送给用户' + notifierExtName: 'halo-email-notifier' + senderSettingRef: + name: 'notifier-setting-for-email' + group: 'sender' +--- +apiVersion: v1alpha1 +kind: Setting +metadata: + name: notifier-setting-for-email +spec: + forms: + - group: sender + label: 发件设置 + formSchema: + - $formkit: text + label: "用户名" + name: username + validation: required + - $formkit: password + label: "密码" + name: password + validation: required + - $formkit: text + label: "显示名称" + name: displayName + - $formkit: text + label: "SMTP 服务器地址" + name: host + validation: required + - $formkit: text + label: "端口号" + name: port + validation: required +--- +apiVersion: notification.halo.run/v1alpha1 +kind: ReasonType +metadata: + name: new-comment-on-post +spec: + displayName: "我的文章收到新评论" + description: "如果有读者在你的文章下方留下了新的评论,你将会收到一条通知,告诉你有新的评论。 + 这个通知事件可以帮助你及时了解读者对你的文章的反馈,以便你更好地与读者互动,提高文章的质量和受欢迎程度。" + properties: + - name: postName + type: string + description: "The name of the post." + - name: postTitle + type: string + - name: postUrl + type: string + - name: commenter + type: string + description: "The display name of the commenter." + - name: commentName + type: string + description: "The name of the comment." + - name: content + type: string + description: "The content of the comment." +--- +apiVersion: notification.halo.run/v1alpha1 +kind: ReasonType +metadata: + name: new-comment-on-single-page +spec: + displayName: "我的自定义页面收到新评论" + description: "当你创建的自定义页面收到新评论时,你将会收到一条通知,告诉你有新的评论。" + properties: + - name: pageName + type: string + description: "The name of the single page." + - name: pageTitle + type: string + - name: pageUrl + type: string + - name: commenter + type: string + description: "The display name of the commenter." + - name: commentName + type: string + description: "The name of the comment." + - name: content + type: string + description: "The content of the comment." +--- +apiVersion: notification.halo.run/v1alpha1 +kind: ReasonType +metadata: + name: someone-replied-to-you +spec: + displayName: "有人回复了我" + description: "如果有其他用户回复了你的评论,你将会收到一条通知,告诉你有人回复了你。" + properties: + - name: commentName + type: string + description: "The name of the comment." + - name: commentSubjectTitle + type: string + - name: commentSubjectUrl + type: string + - name: quoteContent + type: string + optional: true + description: "The content of quoted reply." + - name: isQuoteReply + type: boolean + - name: commentContent + type: string + - name: replier + type: string + description: "The display name of the replier." + - name: replyName + type: string + description: "The name of the reply." + - name: content + type: string + description: "The content of the reply." diff --git a/application/src/main/resources/extensions/role-template-anonymous.yaml b/application/src/main/resources/extensions/role-template-anonymous.yaml index ff5b78a742..06e23a7a8e 100644 --- a/application/src/main/resources/extensions/role-template-anonymous.yaml +++ b/application/src/main/resources/extensions/role-template-anonymous.yaml @@ -42,4 +42,7 @@ rules: verbs: [ "get", "list" ] - apiGroups: [ "api.plugin.halo.run" ] resources: [ "*" ] + verbs: [ "get", "list" ] + - apiGroups: [ "api.notification.halo.run" ] + resources: [ "subscriptions/unsubscribe" ] verbs: [ "get", "list" ] \ No newline at end of file diff --git a/application/src/main/resources/extensions/role-template-authenticated.yaml b/application/src/main/resources/extensions/role-template-authenticated.yaml index ede5162273..0424b7544c 100644 --- a/application/src/main/resources/extensions/role-template-authenticated.yaml +++ b/application/src/main/resources/extensions/role-template-authenticated.yaml @@ -13,7 +13,8 @@ metadata: "role-template-change-own-password", "role-template-stats", "role-template-annotation-setting", - "role-template-manage-own-pat" + "role-template-manage-own-pat", + "role-template-user-notification" ] rules: - apiGroups: [ "" ] @@ -95,7 +96,6 @@ rules: - apiGroups: [ "" ] resources: [ "annotationsettings" ] verbs: [ "get", "list" ] - --- apiVersion: v1alpha1 kind: "Role" @@ -110,4 +110,22 @@ rules: verbs: [ "*" ] - apiGroups: [ "api.security.halo.run" ] resources: [ "personalaccesstokens/actions" ] - verbs: [ "update" ] \ No newline at end of file + verbs: [ "update" ] +--- +apiVersion: v1alpha1 +kind: Role +metadata: + name: role-template-user-notification + labels: + halo.run/role-template: "true" + halo.run/hidden: "true" +rules: + - apiGroups: [ "api.notification.halo.run" ] + resources: [ "notifications" ] + verbs: [ "get", "list" ] + - apiGroups: [ "api.notification.halo.run" ] + resources: [ "notifications/mark-as-read", "notifications/mark-specified-as-read" ] + verbs: [ "update" ] + - apiGroups: [ "api.notification.halo.run" ] + resources: [ "notifiers/receiver-config" ] + verbs: [ "get", "update" ] diff --git a/application/src/main/resources/extensions/role-template-notification.yaml b/application/src/main/resources/extensions/role-template-notification.yaml new file mode 100644 index 0000000000..0f59b87ae8 --- /dev/null +++ b/application/src/main/resources/extensions/role-template-notification.yaml @@ -0,0 +1,15 @@ +apiVersion: v1alpha1 +kind: "Role" +metadata: + name: role-template-notifier-config + labels: + halo.run/role-template: "true" + annotations: + rbac.authorization.halo.run/module: "Notification Configuration" + rbac.authorization.halo.run/display-name: "Configure Notifier" + rbac.authorization.halo.run/ui-permissions: | + ["system:notifier:configuration"] +rules: + - apiGroups: [ "api.console.halo.run" ] + resources: [ "notifiers/sender-config" ] + verbs: [ "get", "update" ] diff --git a/application/src/test/java/run/halo/app/content/comment/CommentNotificationReasonPublisherTest.java b/application/src/test/java/run/halo/app/content/comment/CommentNotificationReasonPublisherTest.java new file mode 100644 index 0000000000..11848c3508 --- /dev/null +++ b/application/src/test/java/run/halo/app/content/comment/CommentNotificationReasonPublisherTest.java @@ -0,0 +1,485 @@ +package run.halo.app.content.comment; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.assertArg; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Map; +import java.util.Optional; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import reactor.core.publisher.Mono; +import run.halo.app.content.NotificationReasonConst; +import run.halo.app.core.extension.User; +import run.halo.app.core.extension.content.Comment; +import run.halo.app.core.extension.content.Post; +import run.halo.app.core.extension.content.Reply; +import run.halo.app.core.extension.content.SinglePage; +import run.halo.app.core.extension.notification.Reason; +import run.halo.app.event.post.CommentCreatedEvent; +import run.halo.app.event.post.ReplyCreatedEvent; +import run.halo.app.extension.ExtensionClient; +import run.halo.app.extension.GroupVersionKind; +import run.halo.app.extension.Metadata; +import run.halo.app.extension.Ref; +import run.halo.app.infra.ExternalLinkProcessor; +import run.halo.app.notification.NotificationReasonEmitter; +import run.halo.app.notification.ReasonPayload; +import run.halo.app.notification.UserIdentity; +import run.halo.app.plugin.ExtensionComponentsFinder; + +/** + * Tests for {@link CommentNotificationReasonPublisher}. + * + * @author guqing + * @since 2.9.0 + */ +@ExtendWith(MockitoExtension.class) +class CommentNotificationReasonPublisherTest { + + @Mock + private ExtensionClient client; + + @Mock + CommentNotificationReasonPublisher.NewCommentOnPostReasonPublisher + newCommentOnPostReasonPublisher; + + @Mock + CommentNotificationReasonPublisher.NewCommentOnPageReasonPublisher + newCommentOnPageReasonPublisher; + + @Mock + CommentNotificationReasonPublisher.NewReplyReasonPublisher newReplyReasonPublisher; + + @InjectMocks + private CommentNotificationReasonPublisher reasonPublisher; + + + @Test + void onNewCommentTest() { + var comment = mock(Comment.class); + var spyReasonPublisher = spy(reasonPublisher); + + doReturn(true).when(spyReasonPublisher).isPostComment(eq(comment)); + + var event = new CommentCreatedEvent(this, comment); + spyReasonPublisher.onNewComment(event); + + verify(newCommentOnPostReasonPublisher).publishReasonBy(eq(comment)); + + doReturn(false).when(spyReasonPublisher).isPostComment(eq(comment)); + doReturn(true).when(spyReasonPublisher).isPageComment(eq(comment)); + + spyReasonPublisher.onNewComment(event); + verify(newCommentOnPageReasonPublisher).publishReasonBy(eq(comment)); + } + + @Test + void onNewReplyTest() { + var reply = mock(Reply.class); + var spec = mock(Reply.ReplySpec.class); + when(reply.getSpec()).thenReturn(spec); + when(spec.getCommentName()).thenReturn("fake-comment"); + + var spyReasonPublisher = spy(reasonPublisher); + var comment = mock(Comment.class); + + when(client.fetch(eq(Comment.class), eq("fake-comment"))) + .thenReturn(Optional.of(comment)); + + var event = new ReplyCreatedEvent(this, reply); + spyReasonPublisher.onNewReply(event); + + verify(newReplyReasonPublisher).publishReasonBy(eq(reply), eq(comment)); + verify(spec).getCommentName(); + verify(client).fetch(eq(Comment.class), eq("fake-comment")); + } + + @Test + void isPostCommentTest() { + var comment = createComment(); + comment.getSpec() + .setSubjectRef(Ref.of("fake-post", GroupVersionKind.fromExtension(Post.class))); + + assertThat(reasonPublisher.isPostComment(comment)).isTrue(); + + comment.getSpec() + .setSubjectRef(Ref.of("fake-post", GroupVersionKind.fromExtension(SinglePage.class))); + + assertThat(reasonPublisher.isPostComment(comment)).isFalse(); + } + + @Test + void isPageComment() { + var comment = createComment(); + comment.getSpec() + .setSubjectRef(Ref.of("fake-post", GroupVersionKind.fromExtension(Post.class))); + + assertThat(reasonPublisher.isPageComment(comment)).isFalse(); + + comment.getSpec() + .setSubjectRef(Ref.of("fake-post", GroupVersionKind.fromExtension(SinglePage.class))); + + assertThat(reasonPublisher.isPageComment(comment)).isTrue(); + } + + + @Nested + class NewCommentOnPostReasonPublisherTest { + @Mock + ExtensionClient client; + + @Mock + NotificationReasonEmitter emitter; + + @Mock + ExtensionComponentsFinder extensionComponentsFinder; + + @Mock + ExternalLinkProcessor externalLinkProcessor; + + @InjectMocks + CommentNotificationReasonPublisher.NewCommentOnPostReasonPublisher + newCommentOnPostReasonPublisher; + + @Test + void publishReasonByTest() { + final var comment = createComment(); + comment.getSpec().getOwner().setDisplayName("fake-display-name"); + comment.getSpec().setContent("fake-comment-content"); + + var post = mock(Post.class); + final var spec = mock(Post.PostSpec.class); + var metadata = new Metadata(); + metadata.setName("fake-post"); + when(post.getMetadata()).thenReturn(metadata); + when(post.getStatusOrDefault()).thenReturn(new Post.PostStatus()); + when(post.getSpec()).thenReturn(spec); + when(spec.getTitle()).thenReturn("fake-title"); + + when(client.fetch(eq(Post.class), eq(metadata.getName()))) + .thenReturn(Optional.of(post)); + + when(emitter.emit(eq("new-comment-on-post"), any())) + .thenReturn(Mono.empty()); + + newCommentOnPostReasonPublisher.publishReasonBy(comment); + + verify(client).fetch(eq(Post.class), eq(metadata.getName())); + verify(emitter).emit(eq("new-comment-on-post"), assertArg(consumer -> { + var builder = ReasonPayload.builder(); + consumer.accept(builder); + var reasonPayload = builder.build(); + var reasonSubject = Reason.Subject.builder() + .apiVersion(post.getApiVersion()) + .kind(post.getKind()) + .name(post.getMetadata().getName()) + .title(post.getSpec().getTitle()) + .build(); + assertThat(reasonPayload.getSubject()).isEqualTo(reasonSubject); + + assertThat(reasonPayload.getAuthor()) + .isEqualTo( + UserIdentity.anonymousWithEmail(comment.getSpec().getOwner().getName())); + + assertThat(reasonPayload.getAttributes()).containsAllEntriesOf(Map.of( + "postName", post.getMetadata().getName(), + "postTitle", post.getSpec().getTitle(), + "commenter", comment.getSpec().getOwner().getDisplayName(), + "content", comment.getSpec().getContent(), + "commentName", comment.getMetadata().getName() + )); + })); + } + + @Test + void doNotEmitReasonTest() { + final var comment = createComment(); + var commentOwner = new Comment.CommentOwner(); + commentOwner.setKind(User.KIND); + commentOwner.setName("fake-user"); + comment.getSpec().setOwner(commentOwner); + + var post = new Post(); + post.setMetadata(new Metadata()); + post.getMetadata().setName("fake-post"); + post.setSpec(new Post.PostSpec()); + post.getSpec().setOwner("fake-user"); + + // the username is the same as the comment owner + assertThat(newCommentOnPostReasonPublisher.doNotEmitReason(comment, post)).isTrue(); + + // not the same username + commentOwner.setName("other"); + assertThat(newCommentOnPostReasonPublisher.doNotEmitReason(comment, post)).isFalse(); + + // the comment owner is email and the same as the post-owner user email + commentOwner.setKind(Comment.CommentOwner.KIND_EMAIL); + commentOwner.setName("example@example.com"); + var user = new User(); + user.setSpec(new User.UserSpec()); + user.getSpec().setEmail("example@example.com"); + when(client.fetch(eq(User.class), eq("fake-user"))) + .thenReturn(Optional.of(user)); + + assertThat(newCommentOnPostReasonPublisher.doNotEmitReason(comment, post)).isTrue(); + + // the comment owner is email and not the same as the post-owner user email + user.getSpec().setEmail("fake@example.com"); + assertThat(newCommentOnPostReasonPublisher.doNotEmitReason(comment, post)).isFalse(); + } + } + + @Nested + class NewCommentOnPageReasonPublisherTest { + @Mock + ExtensionClient client; + + @Mock + NotificationReasonEmitter emitter; + + @Mock + ExtensionComponentsFinder extensionComponentsFinder; + + @Mock + ExternalLinkProcessor externalLinkProcessor; + + @InjectMocks + CommentNotificationReasonPublisher.NewCommentOnPageReasonPublisher + newCommentOnPageReasonPublisher; + + @Test + void publishReasonByTest() { + final var comment = createComment(); + comment.getSpec().getOwner().setDisplayName("fake-display-name"); + comment.getSpec().setContent("fake-comment-content"); + comment.getSpec().setSubjectRef( + Ref.of("fake-page", GroupVersionKind.fromExtension(SinglePage.class))); + + var page = mock(SinglePage.class); + final var spec = mock(SinglePage.SinglePageSpec.class); + var metadata = new Metadata(); + metadata.setName("fake-page"); + when(page.getMetadata()).thenReturn(metadata); + when(page.getStatusOrDefault()).thenReturn(new SinglePage.SinglePageStatus()); + when(page.getSpec()).thenReturn(spec); + when(spec.getTitle()).thenReturn("fake-title"); + + when(client.fetch(eq(SinglePage.class), eq(metadata.getName()))) + .thenReturn(Optional.of(page)); + + when(emitter.emit(eq("new-comment-on-single-page"), any())) + .thenReturn(Mono.empty()); + + newCommentOnPageReasonPublisher.publishReasonBy(comment); + + verify(client).fetch(eq(SinglePage.class), eq(metadata.getName())); + verify(emitter).emit(eq("new-comment-on-single-page"), assertArg(consumer -> { + var builder = ReasonPayload.builder(); + consumer.accept(builder); + var reasonPayload = builder.build(); + var reasonSubject = Reason.Subject.builder() + .apiVersion(page.getApiVersion()) + .kind(page.getKind()) + .name(page.getMetadata().getName()) + .title(page.getSpec().getTitle()) + .build(); + assertThat(reasonPayload.getSubject()).isEqualTo(reasonSubject); + + assertThat(reasonPayload.getAuthor()) + .isEqualTo( + UserIdentity.anonymousWithEmail(comment.getSpec().getOwner().getName())); + + assertThat(reasonPayload.getAttributes()).containsAllEntriesOf(Map.of( + "pageName", page.getMetadata().getName(), + "pageTitle", page.getSpec().getTitle(), + "commenter", comment.getSpec().getOwner().getDisplayName(), + "content", comment.getSpec().getContent(), + "commentName", comment.getMetadata().getName() + )); + })); + } + + @Test + void doNotEmitReasonTest() { + final var comment = createComment(); + var commentOwner = new Comment.CommentOwner(); + commentOwner.setKind(User.KIND); + commentOwner.setName("fake-user"); + comment.getSpec().setOwner(commentOwner); + + var page = new SinglePage(); + page.setMetadata(new Metadata()); + page.getMetadata().setName("fake-page"); + page.setSpec(new SinglePage.SinglePageSpec()); + page.getSpec().setOwner("fake-user"); + + // the username is the same as the comment owner + assertThat(newCommentOnPageReasonPublisher.doNotEmitReason(comment, page)).isTrue(); + + // not the same username + commentOwner.setName("other"); + assertThat(newCommentOnPageReasonPublisher.doNotEmitReason(comment, page)).isFalse(); + + // the comment owner is email and the same as the page-owner user email + commentOwner.setKind(Comment.CommentOwner.KIND_EMAIL); + commentOwner.setName("example@example.com"); + var user = new User(); + user.setSpec(new User.UserSpec()); + user.getSpec().setEmail("example@example.com"); + when(client.fetch(eq(User.class), eq("fake-user"))) + .thenReturn(Optional.of(user)); + + assertThat(newCommentOnPageReasonPublisher.doNotEmitReason(comment, page)).isTrue(); + + // the comment owner is email and not the same as the post-owner user email + user.getSpec().setEmail("fake@example.com"); + assertThat(newCommentOnPageReasonPublisher.doNotEmitReason(comment, page)).isFalse(); + } + } + + @Nested + class NewReplyReasonPublisherTest { + + @Mock + ExtensionClient client; + + @Mock + NotificationReasonEmitter notificationReasonEmitter; + + @Mock + ExtensionComponentsFinder extensionComponentsFinder; + + @InjectMocks + CommentNotificationReasonPublisher.NewReplyReasonPublisher newReplyReasonPublisher; + + @Test + void publishReasonByTest() { + var reply = createReply("fake-reply"); + + reply.getSpec().setQuoteReply("fake-quote-reply"); + var quoteReply = createReply("fake-quote-reply"); + + when(client.fetch(eq(Reply.class), eq("fake-quote-reply"))) + .thenReturn(Optional.of(quoteReply)); + + var spyNewReplyReasonPublisher = spy(newReplyReasonPublisher); + + var comment = createComment(); + comment.getSpec().setContent("fake-comment-content"); + + doReturn(false).when(spyNewReplyReasonPublisher) + .doNotEmitReason(any(), any(), any()); + when(notificationReasonEmitter.emit(any(), any())) + .thenReturn(Mono.empty()); + + // execute target method + spyNewReplyReasonPublisher.publishReasonBy(reply, comment); + + verify(notificationReasonEmitter) + .emit(eq(NotificationReasonConst.SOMEONE_REPLIED_TO_YOU), assertArg(consumer -> { + var builder = ReasonPayload.builder(); + consumer.accept(builder); + var reasonPayload = builder.build(); + var reasonSubject = Reason.Subject.builder() + .apiVersion(quoteReply.getApiVersion()) + .kind(quoteReply.getKind()) + .name(quoteReply.getMetadata().getName()) + .title(quoteReply.getSpec().getContent()) + .build(); + assertThat(reasonPayload.getSubject()).isEqualTo(reasonSubject); + + assertThat(reasonPayload.getAuthor()) + .isEqualTo( + UserIdentity.of(reply.getSpec().getOwner().getName())); + + assertThat(reasonPayload.getAttributes()).containsAllEntriesOf(Map.of( + "commentContent", comment.getSpec().getContent(), + "isQuoteReply", true, + "quoteContent", quoteReply.getSpec().getContent(), + "commentName", comment.getMetadata().getName(), + "replier", reply.getSpec().getOwner().getDisplayName(), + "content", reply.getSpec().getContent(), + "replyName", reply.getMetadata().getName() + )); + })); + } + + @Test + void doNotEmitReasonTest() { + final var currentReply = createReply("current"); + currentReply.getSpec().setQuoteReply("quote"); + final var quoteReply = createReply("quote"); + final var comment = createComment(); + + assertThat(newReplyReasonPublisher + .doNotEmitReason(currentReply, quoteReply, comment)).isTrue(); + + currentReply.getSpec().getOwner().setName("other"); + assertThat(newReplyReasonPublisher + .doNotEmitReason(currentReply, quoteReply, comment)).isFalse(); + + currentReply.getSpec().setQuoteReply(null); + assertThat(newReplyReasonPublisher + .doNotEmitReason(currentReply, quoteReply, comment)).isFalse(); + + currentReply.getSpec().setOwner(comment.getSpec().getOwner()); + assertThat(newReplyReasonPublisher + .doNotEmitReason(currentReply, quoteReply, comment)).isTrue(); + } + + static Reply createReply(String name) { + var reply = new Reply(); + reply.setMetadata(new Metadata()); + reply.getMetadata().setName(name); + reply.setSpec(new Reply.ReplySpec()); + reply.getSpec().setCommentName("fake-comment"); + var owner = new Comment.CommentOwner(); + owner.setKind(User.KIND); + owner.setName("fake-user"); + owner.setDisplayName("fake-display-name"); + reply.getSpec().setOwner(owner); + reply.getSpec().setContent("fake-reply-content"); + return reply; + } + } + + @Test + void identityFromTest() { + var owner = new Comment.CommentOwner(); + owner.setKind(User.KIND); + owner.setName("fake-user"); + + assertThat(CommentNotificationReasonPublisher.identityFrom(owner)) + .isEqualTo(UserIdentity.of(owner.getName())); + + owner.setKind(Comment.CommentOwner.KIND_EMAIL); + owner.setName("example@example.com"); + assertThat(CommentNotificationReasonPublisher.identityFrom(owner)) + .isEqualTo(UserIdentity.anonymousWithEmail(owner.getName())); + } + + static Comment createComment() { + var comment = new Comment(); + comment.setMetadata(new Metadata()); + comment.getMetadata().setName("fake-comment"); + comment.setSpec(new Comment.CommentSpec()); + var commentOwner = new Comment.CommentOwner(); + commentOwner.setKind(Comment.CommentOwner.KIND_EMAIL); + commentOwner.setName("example@example.com"); + comment.getSpec().setOwner(commentOwner); + comment.getSpec().setSubjectRef( + Ref.of("fake-post", GroupVersionKind.fromExtension(Post.class))); + return comment; + } +} diff --git a/application/src/test/java/run/halo/app/content/comment/ReplyNotificationSubscriptionHelperTest.java b/application/src/test/java/run/halo/app/content/comment/ReplyNotificationSubscriptionHelperTest.java new file mode 100644 index 0000000000..63efcb474b --- /dev/null +++ b/application/src/test/java/run/halo/app/content/comment/ReplyNotificationSubscriptionHelperTest.java @@ -0,0 +1,159 @@ +package run.halo.app.content.comment; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import reactor.core.publisher.Mono; +import run.halo.app.content.NotificationReasonConst; +import run.halo.app.core.extension.User; +import run.halo.app.core.extension.content.Comment; +import run.halo.app.core.extension.content.Post; +import run.halo.app.core.extension.content.Reply; +import run.halo.app.core.extension.notification.Subscription; +import run.halo.app.extension.GroupVersionKind; +import run.halo.app.extension.Metadata; +import run.halo.app.extension.Ref; +import run.halo.app.infra.AnonymousUserConst; +import run.halo.app.notification.NotificationCenter; +import run.halo.app.notification.SubscriberEmailResolver; + +/** + * Tests for {@link ReplyNotificationSubscriptionHelper}. + * + * @author guqing + * @since 2.9.0 + */ +@ExtendWith(MockitoExtension.class) +class ReplyNotificationSubscriptionHelperTest { + + @Mock + NotificationCenter notificationCenter; + + @Mock + SubscriberEmailResolver subscriberEmailResolver; + + @InjectMocks + ReplyNotificationSubscriptionHelper notificationSubscriptionHelper; + + @Test + void subscribeNewReplyReasonForCommentTest() { + var comment = createComment(); + var spyNotificationSubscriptionHelper = spy(notificationSubscriptionHelper); + + doNothing().when(spyNotificationSubscriptionHelper).subscribeReply(any(), any()); + + spyNotificationSubscriptionHelper.subscribeNewReplyReasonForComment(comment); + + var reasonSubject = Subscription.ReasonSubject.builder() + .apiVersion(comment.getApiVersion()) + .kind(comment.getKind()) + .name(comment.getMetadata().getName()) + .build(); + verify(spyNotificationSubscriptionHelper).subscribeReply(eq(reasonSubject), + eq(ReplyNotificationSubscriptionHelper.Identity.fromCommentOwner( + comment.getSpec().getOwner())) + ); + } + + @Test + void subscribeNewReplyReasonForReplyTest() { + var reply = new Reply(); + reply.setMetadata(new Metadata()); + reply.getMetadata().setName("fake-reply"); + reply.setSpec(new Reply.ReplySpec()); + reply.getSpec().setCommentName("fake-comment"); + var owner = new Comment.CommentOwner(); + owner.setKind(User.KIND); + owner.setName("fake-user"); + reply.getSpec().setOwner(owner); + + var spyNotificationSubscriptionHelper = spy(notificationSubscriptionHelper); + + doNothing().when(spyNotificationSubscriptionHelper).subscribeReply(any(), any()); + + spyNotificationSubscriptionHelper.subscribeNewReplyReasonForReply(reply); + + var reasonSubject = Subscription.ReasonSubject.builder() + .apiVersion(reply.getApiVersion()) + .kind(reply.getKind()) + .name(reply.getMetadata().getName()) + .build(); + verify(spyNotificationSubscriptionHelper).subscribeReply(eq(reasonSubject), + eq(ReplyNotificationSubscriptionHelper.Identity.fromCommentOwner( + reply.getSpec().getOwner())) + ); + } + + @Test + void subscribeReplyTest() { + var comment = createComment(); + var reasonSubject = Subscription.ReasonSubject.builder() + .apiVersion(comment.getApiVersion()) + .kind(comment.getKind()) + .name(comment.getMetadata().getName()) + .build(); + var identity = ReplyNotificationSubscriptionHelper.Identity.fromCommentOwner( + comment.getSpec().getOwner()); + + when(notificationCenter.subscribe(any(), any())).thenReturn(Mono.empty()); + + var subscriber = new Subscription.Subscriber(); + subscriber.setName(AnonymousUserConst.PRINCIPAL + "#" + identity.name()); + when(subscriberEmailResolver.ofEmail(eq(identity.name()))) + .thenReturn(subscriber); + + notificationSubscriptionHelper.subscribeReply(reasonSubject, identity); + + var interestReason = new Subscription.InterestReason(); + interestReason.setReasonType(NotificationReasonConst.SOMEONE_REPLIED_TO_YOU); + interestReason.setSubject(reasonSubject); + verify(notificationCenter).subscribe(eq(subscriber), eq(interestReason)); + verify(subscriberEmailResolver).ofEmail(eq(identity.name())); + } + + @Nested + class IdentityTest { + + @Test + void createForCommentOwner() { + var commentOwner = new Comment.CommentOwner(); + commentOwner.setKind(Comment.CommentOwner.KIND_EMAIL); + commentOwner.setName("example@example.com"); + + var sub = ReplyNotificationSubscriptionHelper.Identity.fromCommentOwner(commentOwner); + assertThat(sub.isEmail()).isTrue(); + assertThat(sub.name()).isEqualTo(commentOwner.getName()); + + commentOwner.setKind(User.KIND); + commentOwner.setName("fake-user"); + sub = ReplyNotificationSubscriptionHelper.Identity.fromCommentOwner(commentOwner); + assertThat(sub.isEmail()).isFalse(); + assertThat(sub.name()).isEqualTo(commentOwner.getName()); + } + } + + static Comment createComment() { + var comment = new Comment(); + comment.setMetadata(new Metadata()); + comment.getMetadata().setName("fake-comment"); + comment.setSpec(new Comment.CommentSpec()); + var commentOwner = new Comment.CommentOwner(); + commentOwner.setKind(Comment.CommentOwner.KIND_EMAIL); + commentOwner.setName("example@example.com"); + comment.getSpec().setOwner(commentOwner); + comment.getSpec().setSubjectRef( + Ref.of("fake-post", GroupVersionKind.fromExtension(Post.class))); + return comment; + } +} \ No newline at end of file diff --git a/application/src/test/java/run/halo/app/core/extension/reconciler/CommentReconcilerTest.java b/application/src/test/java/run/halo/app/core/extension/reconciler/CommentReconcilerTest.java index 4aed784479..0d647118d6 100644 --- a/application/src/test/java/run/halo/app/core/extension/reconciler/CommentReconcilerTest.java +++ b/application/src/test/java/run/halo/app/core/extension/reconciler/CommentReconcilerTest.java @@ -13,10 +13,10 @@ import java.util.List; import java.util.Optional; import java.util.Set; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import run.halo.app.core.extension.content.Comment; @@ -42,15 +42,11 @@ class CommentReconcilerTest { @Mock SchemeManager schemeManager; + @InjectMocks private CommentReconciler commentReconciler; private final Instant now = Instant.now(); - @BeforeEach - void setUp() { - commentReconciler = new CommentReconciler(client, schemeManager); - } - @Test void reconcileDelete() { Comment comment = new Comment(); diff --git a/application/src/test/java/run/halo/app/core/extension/reconciler/PostReconcilerTest.java b/application/src/test/java/run/halo/app/core/extension/reconciler/PostReconcilerTest.java index adb9fbc772..18cad76cec 100644 --- a/application/src/test/java/run/halo/app/core/extension/reconciler/PostReconcilerTest.java +++ b/application/src/test/java/run/halo/app/core/extension/reconciler/PostReconcilerTest.java @@ -2,7 +2,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.assertArg; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -12,6 +14,7 @@ import java.util.List; import java.util.Optional; import java.util.Set; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -22,14 +25,17 @@ import org.springframework.context.ApplicationEventPublisher; import reactor.core.publisher.Mono; import run.halo.app.content.ContentWrapper; +import run.halo.app.content.NotificationReasonConst; import run.halo.app.content.PostService; import run.halo.app.content.TestPost; import run.halo.app.content.permalinks.PostPermalinkPolicy; import run.halo.app.core.extension.content.Post; import run.halo.app.core.extension.content.Snapshot; +import run.halo.app.core.extension.notification.Subscription; import run.halo.app.event.post.PostPublishedEvent; import run.halo.app.extension.ExtensionClient; import run.halo.app.extension.controller.Reconciler; +import run.halo.app.notification.NotificationCenter; /** * Tests for {@link PostReconciler}. @@ -52,9 +58,17 @@ class PostReconcilerTest { @Mock private ApplicationEventPublisher eventPublisher; + @Mock + private NotificationCenter notificationCenter; + @InjectMocks private PostReconciler postReconciler; + @BeforeEach + void setUp() { + lenient().when(notificationCenter.subscribe(any(), any())).thenReturn(Mono.empty()); + } + @Test void reconcile() { String name = "post-A"; @@ -188,4 +202,25 @@ void reconcileLastModifyTimeWhenPostIsNotPublished() { assertThat(value.getStatus().getLastModifyTime()).isNull(); } } + + @Test + void subscribeNewCommentNotificationTest() { + Post post = TestPost.postV1(); + + postReconciler.subscribeNewCommentNotification(post); + + verify(notificationCenter).subscribe( + assertArg(subscriber -> assertThat(subscriber.getName()) + .isEqualTo(post.getSpec().getOwner())), + assertArg(argReason -> { + var interestReason = new Subscription.InterestReason(); + interestReason.setReasonType(NotificationReasonConst.NEW_COMMENT_ON_POST); + interestReason.setSubject(Subscription.ReasonSubject.builder() + .apiVersion(post.getApiVersion()) + .kind(post.getKind()) + .name(post.getMetadata().getName()) + .build()); + assertThat(argReason).isEqualTo(interestReason); + })); + } } \ No newline at end of file diff --git a/application/src/test/java/run/halo/app/core/extension/reconciler/SinglePageReconcilerTest.java b/application/src/test/java/run/halo/app/core/extension/reconciler/SinglePageReconcilerTest.java index 68258e017a..6579c5374a 100644 --- a/application/src/test/java/run/halo/app/core/extension/reconciler/SinglePageReconcilerTest.java +++ b/application/src/test/java/run/halo/app/core/extension/reconciler/SinglePageReconcilerTest.java @@ -2,7 +2,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.assertArg; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -13,6 +15,7 @@ import java.util.List; import java.util.Optional; import java.util.Set; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -23,16 +26,19 @@ import org.springframework.context.ApplicationContext; import reactor.core.publisher.Mono; import run.halo.app.content.ContentWrapper; +import run.halo.app.content.NotificationReasonConst; import run.halo.app.content.SinglePageService; import run.halo.app.content.TestPost; import run.halo.app.core.extension.content.Post; import run.halo.app.core.extension.content.SinglePage; import run.halo.app.core.extension.content.Snapshot; +import run.halo.app.core.extension.notification.Subscription; import run.halo.app.extension.ExtensionClient; import run.halo.app.extension.Metadata; import run.halo.app.extension.controller.Reconciler; import run.halo.app.infra.ExternalUrlSupplier; import run.halo.app.metrics.CounterService; +import run.halo.app.notification.NotificationCenter; /** * Tests for {@link SinglePageReconciler}. @@ -57,9 +63,17 @@ class SinglePageReconcilerTest { @Mock private ExternalUrlSupplier externalUrlSupplier; + @Mock + NotificationCenter notificationCenter; + @InjectMocks private SinglePageReconciler singlePageReconciler; + @BeforeEach + void setUp() { + lenient().when(notificationCenter.subscribe(any(), any())).thenReturn(Mono.empty()); + } + @Test void reconcile() { String name = "page-A"; @@ -204,4 +218,26 @@ public static SinglePage pageV1() { return page; } + + + @Test + void subscribeNewCommentNotificationTest() { + var page = pageV1(); + + singlePageReconciler.subscribeNewCommentNotification(page); + + verify(notificationCenter).subscribe( + assertArg(subscriber -> assertThat(subscriber.getName()) + .isEqualTo(page.getSpec().getOwner())), + assertArg(argReason -> { + var interestReason = new Subscription.InterestReason(); + interestReason.setReasonType(NotificationReasonConst.NEW_COMMENT_ON_PAGE); + interestReason.setSubject(Subscription.ReasonSubject.builder() + .apiVersion(page.getApiVersion()) + .kind(page.getKind()) + .name(page.getMetadata().getName()) + .build()); + assertThat(argReason).isEqualTo(interestReason); + })); + } } \ No newline at end of file diff --git a/application/src/test/java/run/halo/app/infra/DefaultExternalLinkProcessorTest.java b/application/src/test/java/run/halo/app/infra/DefaultExternalLinkProcessorTest.java new file mode 100644 index 0000000000..257dbb2eea --- /dev/null +++ b/application/src/test/java/run/halo/app/infra/DefaultExternalLinkProcessorTest.java @@ -0,0 +1,49 @@ +package run.halo.app.infra; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +import java.net.MalformedURLException; +import java.net.URI; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +/** + * Tests for {@link DefaultExternalLinkProcessor}. + * + * @author guqing + * @since 2.9.0 + */ +@ExtendWith(MockitoExtension.class) +class DefaultExternalLinkProcessorTest { + + @Mock + private ExternalUrlSupplier externalUrlSupplier; + + @InjectMocks + DefaultExternalLinkProcessor externalLinkProcessor; + + @Test + void processWhenLinkIsEmpty() { + assertThat(externalLinkProcessor.processLink(null)).isNull(); + assertThat(externalLinkProcessor.processLink("")).isEmpty(); + } + + @Test + void process() throws MalformedURLException { + when(externalUrlSupplier.getRaw()).thenReturn(null); + assertThat(externalLinkProcessor.processLink("/test")).isEqualTo("/test"); + + when(externalUrlSupplier.getRaw()).thenReturn(URI.create("https://halo.run").toURL()); + assertThat(externalLinkProcessor.processLink("/test")).isEqualTo("https://halo.run/test"); + + assertThat(externalLinkProcessor.processLink("https://guqing.xyz/test")) + .isEqualTo("https://guqing.xyz/test"); + + when(externalUrlSupplier.getRaw()).thenReturn(URI.create("https://halo.run/").toURL()); + assertThat(externalLinkProcessor.processLink("/test")).isEqualTo("https://halo.run/test"); + } +} \ No newline at end of file diff --git a/application/src/test/java/run/halo/app/notification/DefaultNotificationCenterTest.java b/application/src/test/java/run/halo/app/notification/DefaultNotificationCenterTest.java new file mode 100644 index 0000000000..ac5a8f940c --- /dev/null +++ b/application/src/test/java/run/halo/app/notification/DefaultNotificationCenterTest.java @@ -0,0 +1,521 @@ +package run.halo.app.notification; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Locale; +import java.util.function.Predicate; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; +import run.halo.app.core.extension.User; +import run.halo.app.core.extension.notification.Notification; +import run.halo.app.core.extension.notification.NotificationTemplate; +import run.halo.app.core.extension.notification.NotifierDescriptor; +import run.halo.app.core.extension.notification.Reason; +import run.halo.app.core.extension.notification.ReasonType; +import run.halo.app.core.extension.notification.Subscription; +import run.halo.app.extension.GroupVersion; +import run.halo.app.extension.Metadata; +import run.halo.app.extension.ReactiveExtensionClient; +import run.halo.app.infra.utils.JsonUtils; + +/** + * Tests for {@link DefaultNotificationCenter}. + * + * @author guqing + * @since 2.9.0 + */ +@ExtendWith(MockitoExtension.class) +class DefaultNotificationCenterTest { + + @Mock + private ReactiveExtensionClient client; + + @Mock + private ReasonNotificationTemplateSelector notificationTemplateSelector; + + @Mock + private UserNotificationPreferenceService userNotificationPreferenceService; + + @Mock + private NotificationTemplateRender notificationTemplateRender; + + @Mock + private NotificationSender notificationSender; + + @InjectMocks + private DefaultNotificationCenter notificationCenter; + + @Test + public void testNotify() { + final Reason reason = new Reason(); + final Reason.Spec spec = new Reason.Spec(); + Reason.Subject subject = new Reason.Subject(); + subject.setApiVersion("content.halo.run/v1alpha1"); + subject.setKind("Comment"); + subject.setName("comment-a"); + spec.setSubject(subject); + spec.setReasonType("new-reply-on-comment"); + spec.setAttributes(null); + reason.setSpec(spec); + reason.setMetadata(new Metadata()); + reason.getMetadata().setName("reason-a"); + + var subscriptionReasonSubject = createNewReplyOnCommentSubject(); + + var spyNotificationCenter = spy(notificationCenter); + var subscriptions = createSubscriptions(); + doReturn(Flux.fromIterable(subscriptions)) + .when(spyNotificationCenter).listObservers(eq("new-reply-on-comment"), + eq(subscriptionReasonSubject)); + doReturn(Mono.empty()).when(spyNotificationCenter) + .dispatchNotification(eq(reason), any()); + + spyNotificationCenter.notify(reason).block(); + + verify(spyNotificationCenter).dispatchNotification(eq(reason), any()); + verify(spyNotificationCenter).listObservers(eq("new-reply-on-comment"), + eq(subscriptionReasonSubject)); + } + + List createSubscriptions() { + Subscription subscription = new Subscription(); + subscription.setMetadata(new Metadata()); + subscription.getMetadata().setName("subscription-a"); + + subscription.setSpec(new Subscription.Spec()); + subscription.getSpec().setSubscriber(new Subscription.Subscriber()); + subscription.getSpec().getSubscriber().setName("anonymousUser#A"); + + var interestReason = new Subscription.InterestReason(); + interestReason.setReasonType("new-reply-on-comment"); + interestReason.setSubject(createNewReplyOnCommentSubject()); + subscription.getSpec().setReason(interestReason); + + return List.of(subscription); + } + + Subscription.ReasonSubject createNewReplyOnCommentSubject() { + var reasonSubject = new Subscription.ReasonSubject(); + reasonSubject.setApiVersion("content.halo.run/v1alpha1"); + reasonSubject.setKind("Comment"); + reasonSubject.setName("comment-a"); + return reasonSubject; + } + + @Test + public void testSubscribe() { + var spyNotificationCenter = spy(notificationCenter); + Subscription subscription = createSubscriptions().get(0); + + var subscriber = subscription.getSpec().getSubscriber(); + + doReturn(Flux.just(subscription)) + .when(spyNotificationCenter).listSubscription(eq(subscriber)); + + var reason = subscription.getSpec().getReason(); + + when(client.create(any(Subscription.class))).thenReturn(Mono.empty()); + + spyNotificationCenter.subscribe(subscriber, reason).block(); + + verify(client, times(0)).create(any(Subscription.class)); + + // not exists subscription will create a new subscription + var newReason = JsonUtils.deepCopy(reason); + newReason.setReasonType("fake-reason-type"); + spyNotificationCenter.subscribe(subscriber, newReason).block(); + verify(client).create(any(Subscription.class)); + } + + + @Test + public void testUnsubscribe() { + Subscription.Subscriber subscriber = new Subscription.Subscriber(); + subscriber.setName("anonymousUser#A"); + var spyNotificationCenter = spy(notificationCenter); + var subscriptions = createSubscriptions(); + doReturn(Flux.fromIterable(subscriptions)) + .when(spyNotificationCenter).listSubscription(eq(subscriber)); + + when(client.delete(any(Subscription.class))).thenReturn(Mono.empty()); + + spyNotificationCenter.unsubscribe(subscriber).block(); + + verify(client).delete(any(Subscription.class)); + } + + + @Test + public void testUnsubscribeWithReason() { + var spyNotificationCenter = spy(notificationCenter); + var subscriptions = createSubscriptions(); + + var subscription = subscriptions.get(0); + + var subscriber = subscription.getSpec().getSubscriber(); + doReturn(Flux.just(subscription)) + .when(spyNotificationCenter).listSubscription(eq(subscriber)); + + var reason = subscription.getSpec().getReason(); + + var newReason = JsonUtils.deepCopy(reason); + newReason.setReasonType("fake-reason-type"); + when(client.delete(any(Subscription.class))).thenReturn(Mono.empty()); + spyNotificationCenter.unsubscribe(subscriber, newReason).block(); + verify(client, times(0)).delete(any(Subscription.class)); + + // exists subscription will be deleted + spyNotificationCenter.unsubscribe(subscriber, reason).block(); + verify(client).delete(any(Subscription.class)); + } + + @Test + public void testGetNotifiersBySubscriber() { + UserNotificationPreference preference = new UserNotificationPreference(); + when(userNotificationPreferenceService.getByUser(any())) + .thenReturn(Mono.just(preference)); + + var reason = new Reason(); + reason.setMetadata(new Metadata()); + reason.getMetadata().setName("reason-a"); + reason.setSpec(new Reason.Spec()); + reason.getSpec().setReasonType("new-reply-on-comment"); + var subscriber = new Subscription.Subscriber(); + subscriber.setName("anonymousUser#A"); + + notificationCenter.getNotifiersBySubscriber(subscriber, reason) + .collectList() + .as(StepVerifier::create) + .consumeNextWith(notifiers -> { + assertThat(notifiers).hasSize(1); + assertThat(notifiers.get(0)).isEqualTo("default-email-notifier"); + }) + .verifyComplete(); + + verify(userNotificationPreferenceService).getByUser(eq(subscriber.getName())); + } + + @Test + public void testDispatchNotification() { + var spyNotificationCenter = spy(notificationCenter); + + doReturn(Flux.just("email-notifier")) + .when(spyNotificationCenter).getNotifiersBySubscriber(any(), any()); + + NotifierDescriptor notifierDescriptor = mock(NotifierDescriptor.class); + when(client.fetch(eq(NotifierDescriptor.class), eq("email-notifier"))) + .thenReturn(Mono.just(notifierDescriptor)); + + var notificationElement = mock(DefaultNotificationCenter.NotificationElement.class); + doReturn(Mono.just(notificationElement)) + .when(spyNotificationCenter).prepareNotificationElement(any(), any(), any()); + + doReturn(Mono.empty()).when(spyNotificationCenter).sendNotification(any()); + doReturn(Mono.empty()).when(spyNotificationCenter).createNotification(any()); + + var reason = new Reason(); + reason.setMetadata(new Metadata()); + reason.getMetadata().setName("reason-a"); + reason.setSpec(new Reason.Spec()); + reason.getSpec().setReasonType("new-reply-on-comment"); + + var subscription = createSubscriptions().get(0); + spyNotificationCenter.dispatchNotification(reason, subscription).block(); + + verify(client).fetch(eq(NotifierDescriptor.class), eq("email-notifier")); + verify(spyNotificationCenter).sendNotification(any()); + verify(spyNotificationCenter).createNotification(any()); + } + + @Test + public void testPrepareNotificationElement() { + var spyNotificationCenter = spy(notificationCenter); + + doReturn(Mono.just(Locale.getDefault())) + .when(spyNotificationCenter).getLocaleFromSubscriber(any()); + + var notificationContent = mock(DefaultNotificationCenter.NotificationContent.class); + doReturn(Mono.just(notificationContent)) + .when(spyNotificationCenter).inferenceTemplate(any(), any(), any()); + + spyNotificationCenter.prepareNotificationElement(any(), any(), any()) + .block(); + + verify(spyNotificationCenter).getLocaleFromSubscriber(any()); + verify(spyNotificationCenter).inferenceTemplate(any(), any(), any()); + } + + @Test + public void testSendNotification() { + var spyNotificationCenter = spy(notificationCenter); + + var context = mock(NotificationContext.class); + doReturn(Mono.just(context)) + .when(spyNotificationCenter).notificationContextFrom(any()); + + when(notificationSender.sendNotification(eq("fake-notifier-ext"), any())) + .thenReturn(Mono.empty()); + + var element = mock(DefaultNotificationCenter.NotificationElement.class); + var mockDescriptor = mock(NotifierDescriptor.class); + when(element.descriptor()).thenReturn(mockDescriptor); + when(element.subscription()).thenReturn(mock(Subscription.class)); + var notifierDescriptorSpec = mock(NotifierDescriptor.Spec.class); + when(mockDescriptor.getSpec()).thenReturn(notifierDescriptorSpec); + when(notifierDescriptorSpec.getNotifierExtName()).thenReturn("fake-notifier-ext"); + + spyNotificationCenter.sendNotification(element).block(); + + verify(spyNotificationCenter).notificationContextFrom(any()); + verify(notificationSender).sendNotification(any(), any()); + } + + @Test + public void testCreateNotification() { + var element = mock(DefaultNotificationCenter.NotificationElement.class); + var subscription = createSubscriptions().get(0); + var user = mock(User.class); + + var subscriberName = subscription.getSpec().getSubscriber().getName(); + when(client.fetch(eq(User.class), eq(subscriberName))).thenReturn(Mono.just(user)); + when(element.subscription()).thenReturn(subscription); + + when(client.create(any(Notification.class))).thenReturn(Mono.empty()); + + var reason = new Reason(); + reason.setMetadata(new Metadata()); + reason.getMetadata().setName("reason-a"); + reason.setSpec(new Reason.Spec()); + reason.getSpec().setReasonType("new-reply-on-comment"); + when(element.reason()).thenReturn(reason); + + notificationCenter.createNotification(element).block(); + + verify(client).fetch(eq(User.class), eq(subscriberName)); + verify(client).create(any(Notification.class)); + } + + @Test + public void testInferenceTemplate() { + final var spyNotificationCenter = spy(notificationCenter); + + final var reasonType = mock(ReasonType.class); + + var reason = new Reason(); + reason.setMetadata(new Metadata()); + reason.getMetadata().setName("reason-a"); + reason.setSpec(new Reason.Spec()); + reason.getSpec().setReasonType("new-reply-on-comment"); + + var reasonTypeName = reason.getSpec().getReasonType(); + + doReturn(Mono.just(reasonType)) + .when(spyNotificationCenter).getReasonType(eq(reasonTypeName)); + doReturn("fake-url") + .when(spyNotificationCenter).getUnsubscribeUrl(any()); + + final var locale = Locale.CHINESE; + + var template = new NotificationTemplate(); + template.setMetadata(new Metadata()); + template.getMetadata().setName("notification-template-a"); + template.setSpec(new NotificationTemplate.Spec()); + template.getSpec().setTemplate(new NotificationTemplate.Template()); + template.getSpec().getTemplate().setRawBody("body"); + template.getSpec().getTemplate().setHtmlBody("html-body"); + template.getSpec().getTemplate().setTitle("title"); + template.getSpec().setReasonSelector(new NotificationTemplate.ReasonSelector()); + template.getSpec().getReasonSelector().setReasonType(reasonTypeName); + template.getSpec().getReasonSelector().setLanguage(locale.getLanguage()); + + when(notificationTemplateRender.render(anyString(), any())) + .thenReturn(Mono.empty()); + when(notificationTemplateSelector.select(eq(reasonTypeName), any())) + .thenReturn(Mono.just(template)); + + var subscription = new Subscription(); + subscription.setSpec(new Subscription.Spec()); + var subscriber = new Subscription.Subscriber(); + subscriber.setName("anonymousUser#A"); + subscription.getSpec().setSubscriber(subscriber); + + spyNotificationCenter.inferenceTemplate(reason, subscription, locale).block(); + + verify(spyNotificationCenter).getReasonType(eq(reasonTypeName)); + verify(notificationTemplateSelector).select(eq(reasonTypeName), any()); + } + + @Test + @SuppressWarnings("unchecked") + void listSubscriptionTest() { + var subscriptions = createSubscriptions(); + + when(client.list(eq(Subscription.class), any(Predicate.class), any())) + .thenAnswer(answer -> { + var predicate = (Predicate) answer.getArgument(1, Predicate.class); + return Flux.fromIterable(subscriptions) + .filter(predicate); + }); + + var subscription = subscriptions.get(0); + var subscriber = subscription.getSpec().getSubscriber(); + notificationCenter.listSubscription(subscriber) + .as(StepVerifier::create) + .expectNext(subscription) + .verifyComplete(); + + verify(client).list(eq(Subscription.class), any(Predicate.class), any()); + + var otherSubscriber = JsonUtils.deepCopy(subscriber); + otherSubscriber.setName("other"); + notificationCenter.listSubscription(otherSubscriber) + .as(StepVerifier::create) + .verifyComplete(); + } + + @Test + @SuppressWarnings("unchecked") + void listObserversTest() { + var subscriptions = createSubscriptions(); + + when(client.list(eq(Subscription.class), any(Predicate.class), any())) + .thenAnswer(answer -> { + var predicate = (Predicate) answer.getArgument(1, Predicate.class); + return Flux.fromIterable(subscriptions) + .filter(predicate); + }); + + var subscription = subscriptions.get(0); + var reasonTypeName = subscription.getSpec().getReason().getReasonType(); + var reasonSubject = subscription.getSpec().getReason().getSubject(); + notificationCenter.listObservers(reasonTypeName, reasonSubject) + .as(StepVerifier::create) + .expectNext(subscription) + .verifyComplete(); + + verify(client).list(eq(Subscription.class), any(Predicate.class), any()); + + notificationCenter.listObservers("other-reason", reasonSubject) + .as(StepVerifier::create) + .verifyComplete(); + } + + @Test + @SuppressWarnings("unchecked") + void listObserverWhenDuplicateSubscribers() { + var sourceSubscriptions = createSubscriptions(); + var subscriptionA = sourceSubscriptions.get(0); + var subscriptionB = JsonUtils.deepCopy(subscriptionA); + + var subscriptionC = JsonUtils.deepCopy(subscriptionA); + subscriptionC.getSpec().getReason().getSubject().setName(null); + + var subscriptions = List.of(subscriptionA, subscriptionB, subscriptionC); + when(client.list(eq(Subscription.class), any(Predicate.class), any())) + .thenAnswer(answer -> { + var predicate = (Predicate) answer.getArgument(1, Predicate.class); + return Flux.fromIterable(subscriptions) + .filter(predicate); + }); + + var subscription = subscriptions.get(0); + var reasonTypeName = subscription.getSpec().getReason().getReasonType(); + var reasonSubject = subscription.getSpec().getReason().getSubject(); + notificationCenter.listObservers(reasonTypeName, reasonSubject) + .as(StepVerifier::create) + .expectNext(subscription) + .verifyComplete(); + + verify(client).list(eq(Subscription.class), any(Predicate.class), any()); + + notificationCenter.listObservers("other-reason", reasonSubject) + .as(StepVerifier::create) + .verifyComplete(); + } + + @Nested + class SubscriptionDistinctKeyPredicateTest { + + @Test + void differentSubjectName() { + var subscriptionA = createSubscriptions().get(0); + var subscriptionB = JsonUtils.deepCopy(subscriptionA); + assertThat(DefaultNotificationCenter.subscriptionDistinctKeyPredicate() + .test(subscriptionA, subscriptionB)).isTrue(); + + subscriptionB.getSpec().getReason().getSubject().setName("other"); + assertThat(DefaultNotificationCenter.subscriptionDistinctKeyPredicate() + .test(subscriptionA, subscriptionB)).isFalse(); + + subscriptionB.getSpec().getReason().getSubject().setName(null); + assertThat(DefaultNotificationCenter.subscriptionDistinctKeyPredicate() + .test(subscriptionA, subscriptionB)).isTrue(); + } + + @Test + void differentSubjectApiVersion() { + var subscriptionA = createSubscriptions().get(0); + var subscriptionB = JsonUtils.deepCopy(subscriptionA); + assertThat(DefaultNotificationCenter.subscriptionDistinctKeyPredicate() + .test(subscriptionA, subscriptionB)).isTrue(); + + var subjectA = subscriptionA.getSpec().getReason().getSubject(); + var gvForA = GroupVersion.parseAPIVersion(subjectA.getApiVersion()); + subscriptionB.getSpec().getReason().getSubject() + .setApiVersion(gvForA.group() + "/otherVersion"); + assertThat(DefaultNotificationCenter.subscriptionDistinctKeyPredicate() + .test(subscriptionA, subscriptionB)).isTrue(); + } + + @Test + void differentReasonType() { + var subscriptionA = createSubscriptions().get(0); + var subscriptionB = JsonUtils.deepCopy(subscriptionA); + assertThat(DefaultNotificationCenter.subscriptionDistinctKeyPredicate() + .test(subscriptionA, subscriptionB)).isTrue(); + + subscriptionB.getSpec().getReason().setReasonType("other"); + assertThat(DefaultNotificationCenter.subscriptionDistinctKeyPredicate() + .test(subscriptionA, subscriptionB)).isFalse(); + } + + @Test + void differentSubscriber() { + var subscriptionA = createSubscriptions().get(0); + var subscriptionB = JsonUtils.deepCopy(subscriptionA); + assertThat(DefaultNotificationCenter.subscriptionDistinctKeyPredicate() + .test(subscriptionA, subscriptionB)).isTrue(); + + subscriptionB.getSpec().getSubscriber().setName("other"); + assertThat(DefaultNotificationCenter.subscriptionDistinctKeyPredicate() + .test(subscriptionA, subscriptionB)).isFalse(); + } + } + + @Test + void getLocaleFromSubscriberTest() { + var subscription = mock(Subscription.class); + + notificationCenter.getLocaleFromSubscriber(subscription) + .as(StepVerifier::create) + .expectNext(Locale.getDefault()) + .verifyComplete(); + } +} diff --git a/application/src/test/java/run/halo/app/notification/DefaultNotificationReasonEmitterTest.java b/application/src/test/java/run/halo/app/notification/DefaultNotificationReasonEmitterTest.java new file mode 100644 index 0000000000..6bad6cb35e --- /dev/null +++ b/application/src/test/java/run/halo/app/notification/DefaultNotificationReasonEmitterTest.java @@ -0,0 +1,200 @@ +package run.halo.app.notification; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import java.util.LinkedHashMap; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.skyscreamer.jsonassert.JSONAssert; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; +import run.halo.app.core.extension.notification.Reason; +import run.halo.app.core.extension.notification.ReasonType; +import run.halo.app.extension.ReactiveExtensionClient; +import run.halo.app.infra.utils.JsonUtils; + +/** + * Tests for {@link DefaultNotificationReasonEmitter}. + * + * @author guqing + * @since 2.9.0 + */ +@ExtendWith(MockitoExtension.class) +class DefaultNotificationReasonEmitterTest { + + @Mock + private ReactiveExtensionClient client; + + @InjectMocks + private DefaultNotificationReasonEmitter emitter; + + @Test + void testEmitWhenReasonTypeNotFound() { + var reasonType = createReasonType(); + when(client.fetch(eq(ReasonType.class), eq(reasonType.getMetadata().getName()))) + .thenReturn(Mono.empty()); + doEmmit(reasonType, reasonAttributes()) + .as(StepVerifier::create) + .verifyErrorMessage("404 NOT_FOUND \"ReasonType [" + reasonType.getMetadata().getName() + + "] not found, do you forget to register it?\""); + } + + @Test + void testEmitWhenMissingAttributeValue() { + var reasonType = createReasonType(); + when(client.fetch(eq(ReasonType.class), eq(reasonType.getMetadata().getName()))) + .thenReturn(Mono.just(reasonType)); + + var map = reasonAttributes(); + map.put("commenter", null); + doEmmit(reasonType, map) + .as(StepVerifier::create) + .verifyErrorMessage("Reason property [commenter] is required."); + } + + @Test + void testEmitWhenMissingOptionalAttribute() { + var reasonType = createReasonType(); + when(client.fetch(eq(ReasonType.class), eq(reasonType.getMetadata().getName()))) + .thenReturn(Mono.just(reasonType)); + + when(client.create(any(Reason.class))).thenReturn(Mono.empty()); + + var map = reasonAttributes(); + map.put("postTitle", null); + doEmmit(reasonType, map) + .as(StepVerifier::create) + .verifyComplete(); + } + + @Test + void testCreateReasonOnEmit() { + var reasonType = createReasonType(); + when(client.fetch(eq(ReasonType.class), eq(reasonType.getMetadata().getName()))) + .thenReturn(Mono.just(reasonType)); + + when(client.create(any(Reason.class))).thenReturn(Mono.empty()); + + var spyEmitter = spy(emitter); + doAnswer(as -> { + var returnedValue = as.callRealMethod(); + JSONAssert.assertEquals(createReasonJson(), + JsonUtils.objectToJson(returnedValue), true); + return returnedValue; + }).when(spyEmitter).createReason(any(), any()); + + spyEmitter.emit(reasonType.getMetadata().getName(), + builder -> builder.attributes(reasonAttributes()) + .subject(Reason.Subject.builder() + .apiVersion("content.halo.run/v1alpha1") + .kind("Post") + .name("5152aea5-c2e8-4717-8bba-2263d46e19d5") + .title("Hello Halo") + .url("/archives/hello-halo") + .build() + ) + ) + .as(StepVerifier::create) + .verifyComplete(); + } + + Map reasonAttributes() { + var map = new LinkedHashMap(); + map.put("postName", "5152aea5-c2e8-4717-8bba-2263d46e19d5"); + map.put("postTitle", "Hello Halo"); + map.put("commenter", "guqing"); + map.put("commentName", "53a76c38-5df2-469d-ae1b-68f5ae21a398"); + map.put("content", "测试评论"); + return map; + } + + private Mono doEmmit(ReasonType reasonType, Map map) { + return emitter.emit(reasonType.getMetadata().getName(), builder -> { + builder.attributes(map) + .subject(Reason.Subject.builder() + .apiVersion("content.halo.run/v1alpha1") + .kind("Post") + .name("5152aea5-c2e8-4717-8bba-2263d46e19d5") + .title("Hello Halo") + .url("/archives/hello-halo") + .build() + ); + }); + } + + String createReasonJson() { + return """ + { + "spec": { + "reasonType": "new-comment-on-post", + "subject": { + "apiVersion": "content.halo.run/v1alpha1", + "kind": "Post", + "name": "5152aea5-c2e8-4717-8bba-2263d46e19d5", + "title": "Hello Halo", + "url": "/archives/hello-halo" + }, + "attributes": { + "postName": "5152aea5-c2e8-4717-8bba-2263d46e19d5", + "postTitle": "Hello Halo", + "commentName": "53a76c38-5df2-469d-ae1b-68f5ae21a398", + "content": "测试评论", + "commenter": "guqing" + } + }, + "apiVersion": "notification.halo.run/v1alpha1", + "kind": "Reason", + "metadata": { + "generateName": "reason-" + } + } + """; + } + + ReasonType createReasonType() { + return JsonUtils.jsonToObject(""" + { + "apiVersion": "notification.halo.run/v1alpha1", + "kind": "ReasonType", + "metadata": { + "name": "new-comment-on-post" + }, + "spec": { + "description": "当你的文章收到新评论时,触发事件", + "displayName": "文章收到新评论", + "properties": [ + { + "name": "postName", + "type": "string" + }, + { + "name": "postTitle", + "type": "string", + "optional": true + }, + { + "name": "commenter", + "type": "string" + }, + { + "name": "commentName", + "type": "string" + }, + { + "name": "content", + "type": "string" + } + ] + } + } + """, ReasonType.class); + } +} diff --git a/application/src/test/java/run/halo/app/notification/DefaultNotificationSenderTest.java b/application/src/test/java/run/halo/app/notification/DefaultNotificationSenderTest.java new file mode 100644 index 0000000000..3a26b833eb --- /dev/null +++ b/application/src/test/java/run/halo/app/notification/DefaultNotificationSenderTest.java @@ -0,0 +1,32 @@ +package run.halo.app.notification; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +/** + * Tests for {@link DefaultNotificationSender}. + * + * @author guqing + * @since 2.9.0 + */ +class DefaultNotificationSenderTest { + + @Nested + class QueueItemTest { + + @Test + void equalsTest() { + var item1 = + new DefaultNotificationSender.QueueItem("1", + mock(DefaultNotificationSender.SendNotificationTask.class), 0); + var item2 = + new DefaultNotificationSender.QueueItem("1", + mock(DefaultNotificationSender.SendNotificationTask.class), 1); + + assertThat(item1).isEqualTo(item2); + } + } +} \ No newline at end of file diff --git a/application/src/test/java/run/halo/app/notification/DefaultNotificationTemplateRenderTest.java b/application/src/test/java/run/halo/app/notification/DefaultNotificationTemplateRenderTest.java new file mode 100644 index 0000000000..e4d10336f2 --- /dev/null +++ b/application/src/test/java/run/halo/app/notification/DefaultNotificationTemplateRenderTest.java @@ -0,0 +1,104 @@ +package run.halo.app.notification; + + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.net.MalformedURLException; +import java.net.URI; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; +import run.halo.app.infra.ExternalUrlSupplier; +import run.halo.app.infra.SystemConfigurableEnvironmentFetcher; +import run.halo.app.infra.SystemSetting; + +/** + * Tests for {@link DefaultNotificationTemplateRender}. + * + * @author guqing + * @since 2.9.0 + */ +@ExtendWith(MockitoExtension.class) +class DefaultNotificationTemplateRenderTest { + + @Mock + private SystemConfigurableEnvironmentFetcher environmentFetcher; + + @Mock + private ExternalUrlSupplier externalUrlSupplier; + + @InjectMocks + DefaultNotificationTemplateRender templateRender; + + @BeforeEach + void setUp() throws MalformedURLException { + var uri = URI.create("http://localhost:8090"); + lenient().when(externalUrlSupplier.get()).thenReturn(uri); + lenient().when(externalUrlSupplier.getRaw()).thenReturn(uri.toURL()); + } + + @Test + void render() { + final String template = """ + 亲爱的博主 + + [(${replier})] 在评论“[(${isQuoteReply ? quoteContent : commentContent})]”中回复了您, + 以下是回复的具体内容: + + [(${content})] + + [(${site.title})] + [(${site.url})] + 祝好! + 查看原文:[(${commentSubjectUrl})] + """; + final var model = Map.of( + "replier", "guqing", + "isQuoteReply", true, + "quoteContent", "这是引用的内容", + "commentContent", "这是评论的内容", + "commentSubjectUrl", "/archives/1", + "content", "这是回复的内容" + ); + + var basic = new SystemSetting.Basic(); + basic.setTitle("Halo"); + basic.setLogo("https://halo.run/logo"); + basic.setSubtitle("Halo"); + when(environmentFetcher.fetch(eq(SystemSetting.Basic.GROUP), eq(SystemSetting.Basic.class))) + .thenReturn(Mono.just(basic)); + + templateRender.render(template, model) + .as(StepVerifier::create) + .consumeNextWith(render -> { + assertThat(render).isEqualTo(""" + 亲爱的博主 + + guqing 在评论“这是引用的内容”中回复了您, + 以下是回复的具体内容: + + 这是回复的内容 + + Halo + http://localhost:8090 + 祝好! + 查看原文:/archives/1 + """); + }) + .verifyComplete(); + + verify(environmentFetcher).fetch(eq(SystemSetting.Basic.GROUP), + eq(SystemSetting.Basic.class)); + verify(externalUrlSupplier).getRaw(); + } +} diff --git a/application/src/test/java/run/halo/app/notification/DefaultNotifierConfigStoreTest.java b/application/src/test/java/run/halo/app/notification/DefaultNotifierConfigStoreTest.java new file mode 100644 index 0000000000..eded2ecc2b --- /dev/null +++ b/application/src/test/java/run/halo/app/notification/DefaultNotifierConfigStoreTest.java @@ -0,0 +1,185 @@ +package run.halo.app.notification; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.assertArg; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static run.halo.app.notification.DefaultNotifierConfigStore.RECEIVER_KEY; +import static run.halo.app.notification.DefaultNotifierConfigStore.SECRET_NAME; +import static run.halo.app.notification.DefaultNotifierConfigStore.SENDER_KEY; + +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; +import run.halo.app.extension.MetadataUtil; +import run.halo.app.extension.ReactiveExtensionClient; +import run.halo.app.extension.Secret; +import run.halo.app.infra.utils.JsonUtils; + +/** + * Tests for {@link DefaultNotifierConfigStore}. + * + * @author guqing + * @since 2.9.0 + */ +@ExtendWith(MockitoExtension.class) +class DefaultNotifierConfigStoreTest { + + @Mock + private ReactiveExtensionClient client; + + @InjectMocks + DefaultNotifierConfigStore notifierConfigStore; + + @Test + void fetchReceiverConfigTest() { + var objectNode = mock(ObjectNode.class); + var spyNotifierConfigStore = spy(notifierConfigStore); + + doReturn(Mono.just(objectNode)).when(spyNotifierConfigStore) + .fetchConfig(eq("fake-notifier")); + var receiverConfig = mock(ObjectNode.class); + when(objectNode.get(eq(RECEIVER_KEY))).thenReturn(receiverConfig); + + spyNotifierConfigStore.fetchReceiverConfig("fake-notifier") + .as(StepVerifier::create) + .consumeNextWith(actual -> assertThat(actual).isEqualTo(receiverConfig)) + .verifyComplete(); + verify(objectNode).get(eq(RECEIVER_KEY)); + } + + @Test + void fetchSenderConfigTest() { + var objectNode = mock(ObjectNode.class); + var spyNotifierConfigStore = spy(notifierConfigStore); + + doReturn(Mono.just(objectNode)).when(spyNotifierConfigStore) + .fetchConfig(eq("fake-notifier")); + var senderConfig = mock(ObjectNode.class); + when(objectNode.get(eq(DefaultNotifierConfigStore.SENDER_KEY))).thenReturn(senderConfig); + + spyNotifierConfigStore.fetchSenderConfig("fake-notifier") + .as(StepVerifier::create) + .consumeNextWith(actual -> assertThat(actual).isEqualTo(senderConfig)) + .verifyComplete(); + verify(objectNode).get(eq(DefaultNotifierConfigStore.SENDER_KEY)); + } + + @Test + void fetchConfigWhenSecretNotFound() { + var spyNotifierConfigStore = spy(notifierConfigStore); + + var objectNode = JsonNodeFactory.instance.objectNode(); + doReturn(Mono.just(objectNode)).when(spyNotifierConfigStore) + .fetchConfig(eq("fake-notifier")); + + spyNotifierConfigStore.fetchSenderConfig("fake-notifier") + .as(StepVerifier::create) + .consumeNextWith(actual -> assertThat(actual).isNotNull()) + .verifyComplete(); + + spyNotifierConfigStore.fetchReceiverConfig("fake-notifier") + .as(StepVerifier::create) + .consumeNextWith(actual -> assertThat(actual).isNotNull()) + .verifyComplete(); + } + + @Test + void saveReceiverConfigTest() { + var receiverConfig = mock(ObjectNode.class); + var spyNotifierConfigStore = spy(notifierConfigStore); + + doReturn(Mono.empty()).when(spyNotifierConfigStore) + .saveConfig(eq("fake-notifier"), eq(RECEIVER_KEY), eq(receiverConfig)); + + spyNotifierConfigStore.saveReceiverConfig("fake-notifier", receiverConfig) + .as(StepVerifier::create) + .verifyComplete(); + + verify(spyNotifierConfigStore) + .saveConfig(eq("fake-notifier"), eq(RECEIVER_KEY), eq(receiverConfig)); + } + + @Test + void saveSenderConfigTest() { + var senderConfig = mock(ObjectNode.class); + var spyNotifierConfigStore = spy(notifierConfigStore); + + doReturn(Mono.empty()).when(spyNotifierConfigStore) + .saveConfig(eq("fake-notifier"), eq(SENDER_KEY), eq(senderConfig)); + + spyNotifierConfigStore.saveSenderConfig("fake-notifier", senderConfig) + .as(StepVerifier::create) + .verifyComplete(); + + verify(spyNotifierConfigStore) + .saveConfig(eq("fake-notifier"), eq(SENDER_KEY), eq(senderConfig)); + + } + + @Test + void saveConfigTest() { + when(client.fetch(eq(Secret.class), eq(SECRET_NAME))).thenReturn(Mono.empty()); + + when(client.create(any(Secret.class))) + .thenAnswer(answer -> Mono.just(answer.getArgument(0, Secret.class))); + when(client.update(any(Secret.class))) + .thenAnswer(answer -> Mono.just(answer.getArgument(0, Secret.class))); + + var objectNode = JsonNodeFactory.instance.objectNode(); + objectNode.put("k1", "v1"); + notifierConfigStore.saveConfig("fake-notifier", "fake-key", objectNode) + .as(StepVerifier::create) + .verifyComplete(); + + verify(client).fetch(eq(Secret.class), eq(SECRET_NAME)); + verify(client).create(assertArg(arg -> { + assertThat(arg).isInstanceOf(Secret.class); + var secret = (Secret) arg; + assertThat(secret.getMetadata().getName()).isEqualTo(SECRET_NAME); + assertThat(secret.getMetadata().getFinalizers()) + .contains(MetadataUtil.SYSTEM_FINALIZER); + assertThat(secret.getStringData()).isNotNull(); + })); + verify(client).update(assertArg(arg -> { + assertThat(arg).isInstanceOf(Secret.class); + var secret = (Secret) arg; + assertThat(secret.getStringData().get("fake-notifier.json")) + .isEqualTo("{\"fake-key\":{\"k1\":\"v1\"}}"); + })); + } + + @Test + void fetchConfigTest() { + String s = "{\"fake-key\":{\"k1\":\"v1\"}}"; + var objectNode = JsonUtils.jsonToObject(s, ObjectNode.class); + var secret = new Secret(); + secret.setStringData(Map.of("fake-notifier.json", s)); + when(client.fetch(eq(Secret.class), eq(SECRET_NAME))) + .thenReturn(Mono.just(secret)); + notifierConfigStore.fetchConfig("fake-notifier") + .as(StepVerifier::create) + .consumeNextWith(actual -> assertThat(actual).isEqualTo(objectNode)) + .verifyComplete(); + } + + @Test + void resolveKeyTest() { + assertThat(notifierConfigStore.resolveKey("fake-notifier")) + .isEqualTo("fake-notifier.json"); + assertThat(notifierConfigStore.resolveKey("other-notifier")) + .isEqualTo("other-notifier.json"); + } +} \ No newline at end of file diff --git a/application/src/test/java/run/halo/app/notification/DefaultSubscriberEmailResolverTest.java b/application/src/test/java/run/halo/app/notification/DefaultSubscriberEmailResolverTest.java new file mode 100644 index 0000000000..2c3f1989cf --- /dev/null +++ b/application/src/test/java/run/halo/app/notification/DefaultSubscriberEmailResolverTest.java @@ -0,0 +1,61 @@ +package run.halo.app.notification; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; +import run.halo.app.core.extension.User; +import run.halo.app.core.extension.notification.Subscription; +import run.halo.app.extension.Metadata; +import run.halo.app.extension.ReactiveExtensionClient; +import run.halo.app.infra.AnonymousUserConst; + +/** + * Tests for {@link DefaultSubscriberEmailResolver}. + * + * @author guqing + * @since 2.9.0 + */ +@ExtendWith(MockitoExtension.class) +class DefaultSubscriberEmailResolverTest { + + @Mock + private ReactiveExtensionClient client; + + @InjectMocks + DefaultSubscriberEmailResolver subscriberEmailResolver; + + @Test + void testResolve() { + var subscriber = new Subscription.Subscriber(); + subscriber.setName(AnonymousUserConst.PRINCIPAL + "#test@example.com"); + subscriberEmailResolver.resolve(subscriber) + .as(StepVerifier::create) + .expectNext("test@example.com") + .verifyComplete(); + + subscriber.setName(AnonymousUserConst.PRINCIPAL + "#"); + subscriberEmailResolver.resolve(subscriber) + .as(StepVerifier::create) + .verifyErrorMessage("The subscriber does not have an email"); + + var user = new User(); + user.setMetadata(new Metadata()); + user.getMetadata().setName("fake-user"); + user.setSpec(new User.UserSpec()); + user.getSpec().setEmail("test@halo.run"); + when(client.fetch(eq(User.class), eq("fake-user"))).thenReturn(Mono.just(user)); + + subscriber.setName("fake-user"); + subscriberEmailResolver.resolve(subscriber) + .as(StepVerifier::create) + .expectNext("test@halo.run") + .verifyComplete(); + } +} diff --git a/application/src/test/java/run/halo/app/notification/LanguageUtilsTest.java b/application/src/test/java/run/halo/app/notification/LanguageUtilsTest.java new file mode 100644 index 0000000000..1f52e38b8f --- /dev/null +++ b/application/src/test/java/run/halo/app/notification/LanguageUtilsTest.java @@ -0,0 +1,47 @@ +package run.halo.app.notification; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.List; +import java.util.Locale; +import org.junit.jupiter.api.Test; + +/** + * Tests for {@link LanguageUtils}. + * + * @author guqing + * @since 2.9.0 + */ +class LanguageUtilsTest { + + @Test + void computeLangFromLocale() { + List languages = LanguageUtils.computeLangFromLocale(Locale.CHINA); + assertThat(languages).isEqualTo(List.of("default", "zh", "zh_CN")); + + languages = LanguageUtils.computeLangFromLocale(Locale.CHINESE); + assertThat(languages).isEqualTo(List.of("default", "zh")); + + languages = LanguageUtils.computeLangFromLocale(Locale.TAIWAN); + assertThat(languages).isEqualTo(List.of("default", "zh", "zh_TW")); + + languages = LanguageUtils.computeLangFromLocale(Locale.ENGLISH); + assertThat(languages).isEqualTo(List.of("default", "en")); + + languages = LanguageUtils.computeLangFromLocale(Locale.US); + assertThat(languages).isEqualTo(List.of("default", "en", "en_US")); + + languages = + LanguageUtils.computeLangFromLocale(Locale.forLanguageTag("en-US-x-lvariant-POSIX")); + assertThat(languages).isEqualTo(List.of("default", "en", "en_US", "en_US-POSIX")); + } + + @Test + void computeLangFromLocaleWhenLanguageIsEmpty() { + assertThatThrownBy(() -> { + LanguageUtils.computeLangFromLocale(Locale.forLanguageTag("")); + }).isInstanceOf(IllegalArgumentException.class) + .hasMessage("Locale \"\" cannot be used as it does not specify a language."); + } +} diff --git a/application/src/test/java/run/halo/app/notification/NotificationContextTest.java b/application/src/test/java/run/halo/app/notification/NotificationContextTest.java new file mode 100644 index 0000000000..347cd5f878 --- /dev/null +++ b/application/src/test/java/run/halo/app/notification/NotificationContextTest.java @@ -0,0 +1,66 @@ +package run.halo.app.notification; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.time.Instant; +import org.junit.jupiter.api.Test; + +/** + * Tests for {@link NotificationContext}. + * + * @author guqing + * @since 2.9.0 + */ +class NotificationContextTest { + + @Test + void constructTest() { + // Create a test message payload + NotificationContext.MessagePayload payload = new NotificationContext.MessagePayload(); + payload.setTitle("Test Title"); + payload.setRawBody("Test Body"); + payload.setHtmlBody("Html body"); + + // Create a test subject + NotificationContext.Subject subject = NotificationContext.Subject.builder() + .apiVersion("v1") + .kind("test") + .name("test-name") + .title("Test Subject") + .url("https://example.com") + .build(); + + // Create a test message + NotificationContext.Message message = new NotificationContext.Message(); + message.setPayload(payload); + message.setSubject(subject); + message.setRecipient("test-recipient"); + message.setTimestamp(Instant.now()); + + // Create a test receiver config + ObjectMapper mapper = new ObjectMapper(); + ObjectNode receiverConfig = mapper.createObjectNode(); + receiverConfig.put("key", "value"); + + // Create a test sender config + ObjectNode senderConfig = mapper.createObjectNode(); + senderConfig.put("key", "value"); + + // Create a test notification context + NotificationContext notificationContext = new NotificationContext(); + notificationContext.setMessage(message); + notificationContext.setReceiverConfig(receiverConfig); + notificationContext.setSenderConfig(senderConfig); + + // Test getter methods + assertThat(notificationContext.getMessage()).isNotNull(); + assertThat(notificationContext.getMessage().getPayload()).isEqualTo(payload); + assertThat(notificationContext.getMessage().getSubject()).isEqualTo(subject); + assertThat("test-recipient").isEqualTo(notificationContext.getMessage().getRecipient()); + assertThat(notificationContext.getMessage().getTimestamp()).isNotNull(); + assertThat(notificationContext.getReceiverConfig()).isEqualTo(receiverConfig); + assertThat(notificationContext.getSenderConfig()).isEqualTo(senderConfig); + } +} \ No newline at end of file diff --git a/application/src/test/java/run/halo/app/notification/NotificationTriggerTest.java b/application/src/test/java/run/halo/app/notification/NotificationTriggerTest.java new file mode 100644 index 0000000000..e9faaa24e4 --- /dev/null +++ b/application/src/test/java/run/halo/app/notification/NotificationTriggerTest.java @@ -0,0 +1,58 @@ +package run.halo.app.notification; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Optional; +import java.util.Set; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import reactor.core.publisher.Mono; +import run.halo.app.core.extension.notification.Reason; +import run.halo.app.extension.ExtensionClient; +import run.halo.app.extension.Metadata; +import run.halo.app.extension.controller.Reconciler; + +/** + * Test for {@link NotificationTrigger}. + * + * @author guqing + * @since 2.9.0 + */ +@ExtendWith(MockitoExtension.class) +class NotificationTriggerTest { + + @Mock + private ExtensionClient client; + + @Mock + private NotificationCenter notificationCenter; + + @InjectMocks + private NotificationTrigger notificationTrigger; + + @Test + void reconcile() { + var reason = mock(Reason.class); + var metadata = mock(Metadata.class); + when(reason.getMetadata()).thenReturn(metadata); + when(metadata.getDeletionTimestamp()).thenReturn(null); + when(metadata.getFinalizers()).thenReturn(Set.of()); + + when(client.fetch(eq(Reason.class), eq("fake-reason"))) + .thenReturn(Optional.of(reason)); + + when(notificationCenter.notify(eq(reason))).thenReturn(Mono.empty()); + notificationTrigger.reconcile(new Reconciler.Request("fake-reason")); + + verify(notificationCenter).notify(eq(reason)); + verify(metadata).setFinalizers(eq(Set.of(NotificationTrigger.TRIGGERED_FINALIZER))); + verify(client).update(any(Reason.class)); + } +} \ No newline at end of file diff --git a/application/src/test/java/run/halo/app/notification/ReasonNotificationTemplateSelectorImplTest.java b/application/src/test/java/run/halo/app/notification/ReasonNotificationTemplateSelectorImplTest.java new file mode 100644 index 0000000000..00a5995db1 --- /dev/null +++ b/application/src/test/java/run/halo/app/notification/ReasonNotificationTemplateSelectorImplTest.java @@ -0,0 +1,177 @@ +package run.halo.app.notification; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; +import lombok.NonNull; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import reactor.core.publisher.Flux; +import reactor.test.StepVerifier; +import run.halo.app.core.extension.notification.NotificationTemplate; +import run.halo.app.extension.Metadata; +import run.halo.app.extension.ReactiveExtensionClient; +import run.halo.app.infra.utils.JsonUtils; + +/** + * Tests for {@link ReasonNotificationTemplateSelectorImpl}. + * + * @author guqing + * @since 2.9.0 + */ +@ExtendWith(MockitoExtension.class) +class ReasonNotificationTemplateSelectorImplTest { + + @Mock + private ReactiveExtensionClient client; + + @InjectMocks + ReasonNotificationTemplateSelectorImpl templateSelector; + + @Test + void select() { + when(client.list(eq(NotificationTemplate.class), any(), any())) + .thenReturn(Flux.fromIterable(templates())); + // language priority: zh_CN -> zh -> default + // if language is same, then compare creationTimestamp to get the latest one + templateSelector.select("new-comment-on-post", Locale.SIMPLIFIED_CHINESE) + .as(StepVerifier::create) + .consumeNextWith(template -> { + assertThat(template.getMetadata().getName()).isEqualTo("template-2"); + assertThat(template.getSpec().getTemplate().getTitle()).isEqualTo("B"); + }) + .verifyComplete(); + } + + @Test + void lookupTemplateByLocaleTest() { + Map> map = new HashMap<>(); + map.put("zh_CN", Optional.of(createNotificationTemplate("zh_CN-template"))); + map.put("zh", Optional.of(createNotificationTemplate("zh-template"))); + map.put("default", Optional.of(createNotificationTemplate("default-template"))); + + var sc = ReasonNotificationTemplateSelectorImpl + .lookupTemplateByLocale(Locale.SIMPLIFIED_CHINESE, map); + assertThat(sc).isNotNull(); + assertThat(sc.getMetadata().getName()).isEqualTo("zh_CN-template"); + + var c = ReasonNotificationTemplateSelectorImpl + .lookupTemplateByLocale(Locale.CHINESE, map); + assertThat(c).isNotNull(); + assertThat(c.getMetadata().getName()).isEqualTo("zh-template"); + + var e = ReasonNotificationTemplateSelectorImpl + .lookupTemplateByLocale(Locale.ENGLISH, map); + assertThat(e).isNotNull(); + assertThat(e.getMetadata().getName()).isEqualTo("default-template"); + } + + @Test + void matchReasonTypeTest() { + var template = createNotificationTemplate("fake-template"); + assertThat(ReasonNotificationTemplateSelectorImpl.matchReasonType("new-comment-on-post") + .test(template)).isTrue(); + + assertThat(ReasonNotificationTemplateSelectorImpl.matchReasonType("fake-reason-type") + .test(template)).isFalse(); + } + + @Test + void getLanguageKeyTest() { + final var languageKeyFunc = ReasonNotificationTemplateSelectorImpl.getLanguageKey(); + var template = createNotificationTemplate("fake-template"); + assertThat(languageKeyFunc.apply(template)).isEqualTo("zh_CN"); + + template.getSpec().getReasonSelector().setLanguage(""); + template.getSpec().getReasonSelector().setReasonType("new-comment-on-post"); + assertThat(languageKeyFunc.apply(template)).isEqualTo("default"); + } + + @NonNull + private static NotificationTemplate createNotificationTemplate(String name) { + var template = new NotificationTemplate(); + template.setMetadata(new Metadata()); + template.getMetadata().setName(name); + template.setSpec(new NotificationTemplate.Spec()); + template.getSpec().setReasonSelector(new NotificationTemplate.ReasonSelector()); + template.getSpec().getReasonSelector().setLanguage("zh_CN"); + template.getSpec().getReasonSelector().setReasonType("new-comment-on-post"); + return template; + } + + List templates() { + return Stream.of(""" + { + "apiVersion": "notification.halo.run/v1alpha1", + "kind": "NotificationTemplate", + "metadata": { + "name": "template-1", + "creationTimestamp": "2023-01-01T00:00:00Z" + }, + "spec": { + "reasonSelector": { + "language": "zh", + "reasonType": "new-comment-on-post" + }, + "template": { + "body": "", + "title": "A" + } + } + } + """, + """ + { + "apiVersion": "notification.halo.run/v1alpha1", + "kind": "NotificationTemplate", + "metadata": { + "name": "template-2", + "creationTimestamp": "2023-01-01T00:00:03Z" + }, + "spec": { + "reasonSelector": { + "language": "zh_CN", + "reasonType": "new-comment-on-post" + }, + "template": { + "body": "", + "title": "B" + } + } + } + """, + """ + { + "apiVersion": "notification.halo.run/v1alpha1", + "kind": "NotificationTemplate", + "metadata": { + "name": "template-3", + "creationTimestamp": "2023-01-01T00:00:00Z" + }, + "spec": { + "reasonSelector": { + "language": "zh_CN", + "reasonType": "new-comment-on-post" + }, + "template": { + "body": "", + "title": "C" + } + } + } + """) + .map(json -> JsonUtils.jsonToObject(json, NotificationTemplate.class)) + .toList(); + } +} diff --git a/application/src/test/java/run/halo/app/notification/ReasonPayloadTest.java b/application/src/test/java/run/halo/app/notification/ReasonPayloadTest.java new file mode 100644 index 0000000000..16590f773e --- /dev/null +++ b/application/src/test/java/run/halo/app/notification/ReasonPayloadTest.java @@ -0,0 +1,45 @@ +package run.halo.app.notification; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.Test; +import run.halo.app.core.extension.notification.Reason; + +/** + * Tests for {@link ReasonPayload}. + * + * @author guqing + * @since 2.9.0 + */ +class ReasonPayloadTest { + + @Test + public void testReasonPayloadBuilder() { + Reason.Subject subject = Reason.Subject.builder() + .kind("Post") + .apiVersion("content.halo.run/v1alpha1") + .name("fake-post") + .title("Fake post title") + .url("https://halo.run/fake-post") + .build(); + Map attributes = new HashMap<>(); + attributes.put("key1", "value1"); + attributes.put("key2", 2); + attributes.put("key3", "value3"); + + ReasonPayload reasonPayload = ReasonPayload.builder() + .subject(subject) + .attribute("key1", "value1") + .attribute("key2", 2) + .attributes(Map.of("key3", "value3")) + .build(); + + assertNotNull(reasonPayload); + assertThat(reasonPayload).isNotNull(); + assertThat(reasonPayload.getSubject()).isEqualTo(subject); + assertThat(reasonPayload.getAttributes()).isEqualTo(attributes); + } +} \ No newline at end of file diff --git a/application/src/test/java/run/halo/app/notification/UserIdentityTest.java b/application/src/test/java/run/halo/app/notification/UserIdentityTest.java new file mode 100644 index 0000000000..8df7d9054f --- /dev/null +++ b/application/src/test/java/run/halo/app/notification/UserIdentityTest.java @@ -0,0 +1,21 @@ +package run.halo.app.notification; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +/** + * Tests for {@link UserIdentity}. + * + * @author guqing + * @since 2.9.0 + */ +class UserIdentityTest { + + + @Test + void getEmailTest() { + var identity = UserIdentity.anonymousWithEmail("test@example.com"); + assertThat(identity.getEmail().orElse(null)).isEqualTo("test@example.com"); + } +} \ No newline at end of file diff --git a/application/src/test/java/run/halo/app/notification/UserNotificationPreferenceServiceImplTest.java b/application/src/test/java/run/halo/app/notification/UserNotificationPreferenceServiceImplTest.java new file mode 100644 index 0000000000..62be0f3bd4 --- /dev/null +++ b/application/src/test/java/run/halo/app/notification/UserNotificationPreferenceServiceImplTest.java @@ -0,0 +1,139 @@ +package run.halo.app.notification; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Map; +import java.util.Set; +import org.json.JSONException; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.skyscreamer.jsonassert.JSONAssert; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; +import run.halo.app.extension.ConfigMap; +import run.halo.app.extension.ReactiveExtensionClient; +import run.halo.app.infra.utils.JsonUtils; + +/** + * Tests for {@link UserNotificationPreferenceServiceImpl}. + * + * @author guqing + * @since 2.9.0 + */ +@ExtendWith(MockitoExtension.class) +class UserNotificationPreferenceServiceImplTest { + + @Mock + private ReactiveExtensionClient client; + + @InjectMocks + private UserNotificationPreferenceServiceImpl userNotificationPreferenceService; + + @Test + void getByUser() { + var configMap = new ConfigMap(); + configMap.setData(Map.of("notification", + "{\"reasonTypeNotifier\":{\"comment\":{\"notifiers\":[\"test-notifier\"]}}}")); + when(client.fetch(ConfigMap.class, "user-preferences-guqing")) + .thenReturn(Mono.just(configMap)); + userNotificationPreferenceService.getByUser("guqing") + .as(StepVerifier::create) + .consumeNextWith(preference -> { + assertThat(preference.getReasonTypeNotifier()).isNotNull(); + assertThat(preference.getReasonTypeNotifier().get("comment")).isNotNull(); + assertThat(preference.getReasonTypeNotifier().get("comment").getNotifiers()) + .containsExactly("test-notifier"); + }) + .verifyComplete(); + + verify(client).fetch(ConfigMap.class, "user-preferences-guqing"); + } + + @Test + void getByUserWhenNotFound() { + when(client.fetch(ConfigMap.class, "user-preferences-guqing")) + .thenReturn(Mono.empty()); + userNotificationPreferenceService.getByUser("guqing") + .as(StepVerifier::create) + .consumeNextWith(preference -> + assertThat(preference.getReasonTypeNotifier()).isNotNull() + ) + .verifyComplete(); + + verify(client).fetch(ConfigMap.class, "user-preferences-guqing"); + } + + @Test + void getByUserWhenConfigDataNotFound() { + when(client.fetch(ConfigMap.class, "user-preferences-guqing")) + .thenReturn(Mono.just(new ConfigMap())); + userNotificationPreferenceService.getByUser("guqing") + .as(StepVerifier::create) + .consumeNextWith(preference -> + assertThat(preference.getReasonTypeNotifier()).isNotNull() + ) + .verifyComplete(); + + verify(client).fetch(ConfigMap.class, "user-preferences-guqing"); + } + + + @Nested + class UserNotificationPreferenceTest { + + @Test + void getNotifiers() { + var preference = new UserNotificationPreference(); + preference.getReasonTypeNotifier().put("comment", null); + // key doesn't exist + assertThat(preference.getReasonTypeNotifier().getNotifiers("comment")) + .containsExactly("default-email-notifier"); + + // key exists but the value is null + preference.getReasonTypeNotifier() + .put("comment", new UserNotificationPreference.NotifierSetting()); + assertThat(preference.getReasonTypeNotifier().getNotifiers("comment")).isEmpty(); + + // key exists and the value is not null + preference.getReasonTypeNotifier().get("comment").setNotifiers(Set.of("test-notifier")); + assertThat(preference.getReasonTypeNotifier().getNotifiers("comment")) + .containsExactly("test-notifier"); + } + + @Test + void toJson() throws JSONException { + var preference = new UserNotificationPreference(); + preference.getReasonTypeNotifier().put("comment", + new UserNotificationPreference.NotifierSetting()); + preference.getReasonTypeNotifier().get("comment").setNotifiers(Set.of("test-notifier")); + + JSONAssert.assertEquals(""" + { + "reasonTypeNotifier": { + "comment": { + "notifiers": [ + "test-notifier" + ] + } + } + } + """, + JsonUtils.objectToJson(preference), + true); + } + } + + @Test + void buildUserPreferenceConfigMapName() { + var preferenceConfigMapName = UserNotificationPreferenceServiceImpl + .buildUserPreferenceConfigMapName("guqing"); + assertEquals("user-preferences-guqing", preferenceConfigMapName); + } +} \ No newline at end of file diff --git a/application/src/test/java/run/halo/app/notification/UserNotificationPreferenceTest.java b/application/src/test/java/run/halo/app/notification/UserNotificationPreferenceTest.java new file mode 100644 index 0000000000..d8179ff5c0 --- /dev/null +++ b/application/src/test/java/run/halo/app/notification/UserNotificationPreferenceTest.java @@ -0,0 +1,43 @@ +package run.halo.app.notification; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import run.halo.app.infra.utils.JsonUtils; + +/** + * Tests for {@link UserNotificationPreference}. + * + * @author guqing + * @since 2.9.0 + */ +class UserNotificationPreferenceTest { + + @Test + void preferenceCreation() { + String s = """ + { + "reasonTypeNotifier": { + "comment": { + "notifiers": [ + "email-notifier", + "sms-notifier" + ] + }, + "new-post": { + "notifiers": [ + "email-notifier", + "webhook-router-notifier" + ] + } + } + } + """; + var preference = JsonUtils.jsonToObject(s, UserNotificationPreference.class); + assertThat(preference.getReasonTypeNotifier()).isNotNull(); + assertThat(preference.getReasonTypeNotifier().get("comment").getNotifiers()) + .containsExactlyInAnyOrder("email-notifier", "sms-notifier"); + assertThat(preference.getReasonTypeNotifier().get("new-post").getNotifiers()) + .containsExactlyInAnyOrder("email-notifier", "webhook-router-notifier"); + } +} diff --git a/application/src/test/java/run/halo/app/notification/endpoint/SubscriptionRouterTest.java b/application/src/test/java/run/halo/app/notification/endpoint/SubscriptionRouterTest.java new file mode 100644 index 0000000000..4240d09ed2 --- /dev/null +++ b/application/src/test/java/run/halo/app/notification/endpoint/SubscriptionRouterTest.java @@ -0,0 +1,50 @@ +package run.halo.app.notification.endpoint; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +import java.net.URI; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import run.halo.app.core.extension.notification.Subscription; +import run.halo.app.extension.Metadata; +import run.halo.app.extension.ReactiveExtensionClient; +import run.halo.app.infra.ExternalUrlSupplier; + +/** + * Tests for {@link SubscriptionRouter}. + * + * @author guqing + * @since 2.9.0 + */ +@ExtendWith(MockitoExtension.class) +class SubscriptionRouterTest { + + @Mock + private ReactiveExtensionClient client; + + @Mock + private ExternalUrlSupplier externalUrlSupplier; + + @InjectMocks + SubscriptionRouter subscriptionRouter; + + @Test + void getUnsubscribeUrlTest() { + when(externalUrlSupplier.get()).thenReturn(URI.create("https://halo.run")); + var subscription = new Subscription(); + subscription.setMetadata(new Metadata()); + subscription.getMetadata().setName("fake-subscription"); + subscription.setSpec(new Subscription.Spec()); + subscription.getSpec().setUnsubscribeToken("fake-unsubscribe-token"); + + var url = subscriptionRouter.getUnsubscribeUrl(subscription); + assertThat(url).isEqualTo("https://halo.run/apis/api.notification.halo.run/v1alpha1" + + "/subscriptions/fake-subscription/unsubscribe" + + "?token=fake-unsubscribe-token"); + } + +} \ No newline at end of file diff --git a/application/src/test/java/run/halo/app/security/authorization/DefaultRuleResolverTest.java b/application/src/test/java/run/halo/app/security/authorization/DefaultRuleResolverTest.java index e2f298f3f5..7785c91fdd 100644 --- a/application/src/test/java/run/halo/app/security/authorization/DefaultRuleResolverTest.java +++ b/application/src/test/java/run/halo/app/security/authorization/DefaultRuleResolverTest.java @@ -52,6 +52,32 @@ void visitRules() { Set.of("authenticated", "anonymous", "ruleReadPost")); } + @Test + void visitRulesForUserspaceScope() { + when(roleService.listDependenciesFlux(Set.of("authenticated", "anonymous", "ruleReadPost"))) + .thenReturn(Flux.just(mockRole())); + var fakeUser = new User("admin", "123456", createAuthorityList("ruleReadPost")); + var cases = List.of( + new RequestResolveCase("/api/v1/categories", "POST", true), + new RequestResolveCase("/api/v1/categories", "DELETE", true), + new RequestResolveCase("/api/v1/userspaces/bar/categories", "DELETE", false), + new RequestResolveCase("/api/v1/userspaces/admin/categories", "DELETE", true), + new RequestResolveCase("/api/v1/posts", "GET", true), + + new RequestResolveCase("/api/v1/userspaces/foo/posts", "GET", false), + new RequestResolveCase("/api/v1/userspaces/admin/posts", "GET", true) + ); + cases.forEach(requestResolveCase -> { + var httpMethod = HttpMethod.valueOf(requestResolveCase.method); + var request = method(httpMethod, requestResolveCase.url).build(); + var requestInfo = RequestInfoFactory.INSTANCE.newRequestInfo(request); + StepVerifier.create(ruleResolver.visitRules(fakeUser, requestInfo)) + .assertNext( + visitor -> assertEquals(requestResolveCase.expected, visitor.isAllowed())) + .verifyComplete(); + }); + } + Role mockRole() { var role = new Role(); var rules = List.of( diff --git a/console/.eslintrc.cjs b/console/.eslintrc.cjs index 55dba4e273..821e927b8c 100644 --- a/console/.eslintrc.cjs +++ b/console/.eslintrc.cjs @@ -16,6 +16,7 @@ module.exports = { rules: { "vue/multi-word-component-names": 0, "@typescript-eslint/ban-ts-comment": 0, + "vue/no-v-html": 0, }, overrides: [ { diff --git a/console/packages/api-client/src/.openapi-generator/FILES b/console/packages/api-client/src/.openapi-generator/FILES index 3330ada84e..edf8d55b42 100644 --- a/console/packages/api-client/src/.openapi-generator/FILES +++ b/console/packages/api-client/src/.openapi-generator/FILES @@ -5,6 +5,7 @@ api/api-console-halo-run-v1alpha1-attachment-api.ts api/api-console-halo-run-v1alpha1-auth-provider-api.ts api/api-console-halo-run-v1alpha1-comment-api.ts api/api-console-halo-run-v1alpha1-indices-api.ts +api/api-console-halo-run-v1alpha1-notifier-api.ts api/api-console-halo-run-v1alpha1-plugin-api.ts api/api-console-halo-run-v1alpha1-post-api.ts api/api-console-halo-run-v1alpha1-reply-api.ts @@ -24,6 +25,9 @@ api/api-halo-run-v1alpha1-post-api.ts api/api-halo-run-v1alpha1-stats-api.ts api/api-halo-run-v1alpha1-tracker-api.ts api/api-halo-run-v1alpha1-user-api.ts +api/api-notification-halo-run-v1alpha1-notification-api.ts +api/api-notification-halo-run-v1alpha1-notifier-api.ts +api/api-notification-halo-run-v1alpha1-subscription-api.ts api/api-plugin-halo-run-v1alpha1-plugin-api.ts api/api-security-halo-run-v1alpha1-personal-access-token-api.ts api/auth-halo-run-v1alpha1-auth-provider-api.ts @@ -38,6 +42,12 @@ api/content-halo-run-v1alpha1-tag-api.ts api/login-api.ts api/metrics-halo-run-v1alpha1-counter-api.ts api/migration-halo-run-v1alpha1-backup-api.ts +api/notification-halo-run-v1alpha1-notification-api.ts +api/notification-halo-run-v1alpha1-notification-template-api.ts +api/notification-halo-run-v1alpha1-notifier-descriptor-api.ts +api/notification-halo-run-v1alpha1-reason-api.ts +api/notification-halo-run-v1alpha1-reason-type-api.ts +api/notification-halo-run-v1alpha1-subscription-api.ts api/plugin-halo-run-v1alpha1-extension-definition-api.ts api/plugin-halo-run-v1alpha1-extension-point-definition-api.ts api/plugin-halo-run-v1alpha1-plugin-api.ts @@ -130,6 +140,8 @@ models/group-status.ts models/group.ts models/index.ts models/install-from-uri-request.ts +models/interest-reason-subject.ts +models/interest-reason.ts models/license.ts models/listed-auth-provider.ts models/listed-comment-list.ts @@ -146,6 +158,7 @@ models/listed-single-page-vo.ts models/listed-single-page.ts models/listed-user.ts models/login-history.ts +models/mark-specified-request.ts models/menu-item-list.ts models/menu-item-spec.ts models/menu-item-status.ts @@ -157,6 +170,16 @@ models/menu-vo.ts models/menu.ts models/metadata.ts models/navigation-post-vo.ts +models/notification-list.ts +models/notification-spec.ts +models/notification-template-list.ts +models/notification-template-spec.ts +models/notification-template.ts +models/notification.ts +models/notifier-descriptor-list.ts +models/notifier-descriptor-spec.ts +models/notifier-descriptor.ts +models/notifier-setting-ref.ts models/owner-info.ts models/pat-spec.ts models/personal-access-token-list.ts @@ -182,6 +205,16 @@ models/post-status.ts models/post-vo.ts models/post.ts models/public-key-response.ts +models/reason-list.ts +models/reason-property.ts +models/reason-selector.ts +models/reason-spec-attributes.ts +models/reason-spec.ts +models/reason-subject.ts +models/reason-type-list.ts +models/reason-type-spec.ts +models/reason-type.ts +models/reason.ts models/ref.ts models/reply-list.ts models/reply-request.ts @@ -221,6 +254,10 @@ models/snapshot.ts models/stats-vo.ts models/stats.ts models/subject.ts +models/subscription-list.ts +models/subscription-spec.ts +models/subscription-subscriber.ts +models/subscription.ts models/system-initialization-request.ts models/tag-list.ts models/tag-spec.ts @@ -228,6 +265,7 @@ models/tag-status.ts models/tag-vo-list.ts models/tag-vo.ts models/tag.ts +models/template-content.ts models/template-descriptor.ts models/theme-list.ts models/theme-spec.ts diff --git a/console/packages/api-client/src/api.ts b/console/packages/api-client/src/api.ts index da76420222..9aa2bb9947 100644 --- a/console/packages/api-client/src/api.ts +++ b/console/packages/api-client/src/api.ts @@ -16,6 +16,7 @@ export * from "./api/api-console-halo-run-v1alpha1-attachment-api"; export * from "./api/api-console-halo-run-v1alpha1-auth-provider-api"; export * from "./api/api-console-halo-run-v1alpha1-comment-api"; export * from "./api/api-console-halo-run-v1alpha1-indices-api"; +export * from "./api/api-console-halo-run-v1alpha1-notifier-api"; export * from "./api/api-console-halo-run-v1alpha1-plugin-api"; export * from "./api/api-console-halo-run-v1alpha1-post-api"; export * from "./api/api-console-halo-run-v1alpha1-reply-api"; @@ -35,6 +36,9 @@ export * from "./api/api-halo-run-v1alpha1-post-api"; export * from "./api/api-halo-run-v1alpha1-stats-api"; export * from "./api/api-halo-run-v1alpha1-tracker-api"; export * from "./api/api-halo-run-v1alpha1-user-api"; +export * from "./api/api-notification-halo-run-v1alpha1-notification-api"; +export * from "./api/api-notification-halo-run-v1alpha1-notifier-api"; +export * from "./api/api-notification-halo-run-v1alpha1-subscription-api"; export * from "./api/api-plugin-halo-run-v1alpha1-plugin-api"; export * from "./api/api-security-halo-run-v1alpha1-personal-access-token-api"; export * from "./api/auth-halo-run-v1alpha1-auth-provider-api"; @@ -49,6 +53,12 @@ export * from "./api/content-halo-run-v1alpha1-tag-api"; export * from "./api/login-api"; export * from "./api/metrics-halo-run-v1alpha1-counter-api"; export * from "./api/migration-halo-run-v1alpha1-backup-api"; +export * from "./api/notification-halo-run-v1alpha1-notification-api"; +export * from "./api/notification-halo-run-v1alpha1-notification-template-api"; +export * from "./api/notification-halo-run-v1alpha1-notifier-descriptor-api"; +export * from "./api/notification-halo-run-v1alpha1-reason-api"; +export * from "./api/notification-halo-run-v1alpha1-reason-type-api"; +export * from "./api/notification-halo-run-v1alpha1-subscription-api"; export * from "./api/plugin-halo-run-v1alpha1-extension-definition-api"; export * from "./api/plugin-halo-run-v1alpha1-extension-point-definition-api"; export * from "./api/plugin-halo-run-v1alpha1-plugin-api"; diff --git a/console/packages/api-client/src/api/api-console-halo-run-v1alpha1-notification-api.ts b/console/packages/api-client/src/api/api-console-halo-run-v1alpha1-notification-api.ts new file mode 100644 index 0000000000..473445fa14 --- /dev/null +++ b/console/packages/api-client/src/api/api-console-halo-run-v1alpha1-notification-api.ts @@ -0,0 +1,662 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import type { Configuration } from "../configuration"; +import type { AxiosPromise, AxiosInstance, AxiosRequestConfig } from "axios"; +import globalAxios from "axios"; +// Some imports not used depending on template conditions +// @ts-ignore +import { + DUMMY_BASE_URL, + assertParamExists, + setApiKeyToObject, + setBasicAuthToObject, + setBearerAuthToObject, + setOAuthToObject, + setSearchParams, + serializeDataIfNeeded, + toPathString, + createRequestFunction, +} from "../common"; +// @ts-ignore +import { + BASE_PATH, + COLLECTION_FORMATS, + RequestArgs, + BaseAPI, + RequiredError, +} from "../base"; +// @ts-ignore +import { MarkSpecifiedRequest } from "../models"; +// @ts-ignore +import { Notification } from "../models"; +// @ts-ignore +import { NotificationList } from "../models"; +/** + * ApiConsoleHaloRunV1alpha1NotificationApi - axios parameter creator + * @export + */ +export const ApiConsoleHaloRunV1alpha1NotificationApiAxiosParamCreator = + function (configuration?: Configuration) { + return { + /** + * List notifications for the authenticated user. + * @param {string} username Username + * @param {Array} [fieldSelector] Field selector for filtering. + * @param {string} [keyword] + * @param {Array} [labelSelector] Label selector for filtering. + * @param {number} [page] The page number. Zero indicates no page. + * @param {string} [reason] Filter by notification reason + * @param {number} [size] Size of one page. Zero indicates no limit. + * @param {Array} [sort] Sort property and direction of the list result. Supported fields: creationTimestamp + * @param {boolean} [unRead] true for unread, false for read, null for all + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listUserNotifications: async ( + username: string, + fieldSelector?: Array, + keyword?: string, + labelSelector?: Array, + page?: number, + reason?: string, + size?: number, + sort?: Array, + unRead?: boolean, + options: AxiosRequestConfig = {} + ): Promise => { + // verify required parameter 'username' is not null or undefined + assertParamExists("listUserNotifications", "username", username); + const localVarPath = + `/apis/api.console.halo.run/v1alpha1/userspaces/{username}/notifications`.replace( + `{${"username"}}`, + encodeURIComponent(String(username)) + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + if (fieldSelector) { + localVarQueryParameter["fieldSelector"] = fieldSelector; + } + + if (keyword !== undefined) { + localVarQueryParameter["keyword"] = keyword; + } + + if (labelSelector) { + localVarQueryParameter["labelSelector"] = labelSelector; + } + + if (page !== undefined) { + localVarQueryParameter["page"] = page; + } + + if (reason !== undefined) { + localVarQueryParameter["reason"] = reason; + } + + if (size !== undefined) { + localVarQueryParameter["size"] = size; + } + + if (sort) { + localVarQueryParameter["sort"] = Array.from(sort); + } + + if (unRead !== undefined) { + localVarQueryParameter["unRead"] = unRead; + } + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Mark the specified notification as read. + * @param {string} username Username + * @param {string} name Notification name + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + markNotificationAsRead: async ( + username: string, + name: string, + options: AxiosRequestConfig = {} + ): Promise => { + // verify required parameter 'username' is not null or undefined + assertParamExists("markNotificationAsRead", "username", username); + // verify required parameter 'name' is not null or undefined + assertParamExists("markNotificationAsRead", "name", name); + const localVarPath = + `/apis/api.console.halo.run/v1alpha1/userspaces/{username}/notifications/{name}/mark-as-read` + .replace(`{${"username"}}`, encodeURIComponent(String(username))) + .replace(`{${"name"}}`, encodeURIComponent(String(name))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "PUT", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Mark the specified notifications as read. + * @param {string} username Username + * @param {MarkSpecifiedRequest} markSpecifiedRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + markNotificationsAsRead: async ( + username: string, + markSpecifiedRequest: MarkSpecifiedRequest, + options: AxiosRequestConfig = {} + ): Promise => { + // verify required parameter 'username' is not null or undefined + assertParamExists("markNotificationsAsRead", "username", username); + // verify required parameter 'markSpecifiedRequest' is not null or undefined + assertParamExists( + "markNotificationsAsRead", + "markSpecifiedRequest", + markSpecifiedRequest + ); + const localVarPath = + `/apis/api.console.halo.run/v1alpha1/userspaces/{username}/notifications/-/mark-specified-as-read`.replace( + `{${"username"}}`, + encodeURIComponent(String(username)) + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "PUT", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + localVarHeaderParameter["Content-Type"] = "application/json"; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + localVarRequestOptions.data = serializeDataIfNeeded( + markSpecifiedRequest, + localVarRequestOptions, + configuration + ); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + }; + }; + +/** + * ApiConsoleHaloRunV1alpha1NotificationApi - functional programming interface + * @export + */ +export const ApiConsoleHaloRunV1alpha1NotificationApiFp = function ( + configuration?: Configuration +) { + const localVarAxiosParamCreator = + ApiConsoleHaloRunV1alpha1NotificationApiAxiosParamCreator(configuration); + return { + /** + * List notifications for the authenticated user. + * @param {string} username Username + * @param {Array} [fieldSelector] Field selector for filtering. + * @param {string} [keyword] + * @param {Array} [labelSelector] Label selector for filtering. + * @param {number} [page] The page number. Zero indicates no page. + * @param {string} [reason] Filter by notification reason + * @param {number} [size] Size of one page. Zero indicates no limit. + * @param {Array} [sort] Sort property and direction of the list result. Supported fields: creationTimestamp + * @param {boolean} [unRead] true for unread, false for read, null for all + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async listUserNotifications( + username: string, + fieldSelector?: Array, + keyword?: string, + labelSelector?: Array, + page?: number, + reason?: string, + size?: number, + sort?: Array, + unRead?: boolean, + options?: AxiosRequestConfig + ): Promise< + ( + axios?: AxiosInstance, + basePath?: string + ) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.listUserNotifications( + username, + fieldSelector, + keyword, + labelSelector, + page, + reason, + size, + sort, + unRead, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * Mark the specified notification as read. + * @param {string} username Username + * @param {string} name Notification name + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async markNotificationAsRead( + username: string, + name: string, + options?: AxiosRequestConfig + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.markNotificationAsRead( + username, + name, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * Mark the specified notifications as read. + * @param {string} username Username + * @param {MarkSpecifiedRequest} markSpecifiedRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async markNotificationsAsRead( + username: string, + markSpecifiedRequest: MarkSpecifiedRequest, + options?: AxiosRequestConfig + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise> + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.markNotificationsAsRead( + username, + markSpecifiedRequest, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + }; +}; + +/** + * ApiConsoleHaloRunV1alpha1NotificationApi - factory interface + * @export + */ +export const ApiConsoleHaloRunV1alpha1NotificationApiFactory = function ( + configuration?: Configuration, + basePath?: string, + axios?: AxiosInstance +) { + const localVarFp = ApiConsoleHaloRunV1alpha1NotificationApiFp(configuration); + return { + /** + * List notifications for the authenticated user. + * @param {ApiConsoleHaloRunV1alpha1NotificationApiListUserNotificationsRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listUserNotifications( + requestParameters: ApiConsoleHaloRunV1alpha1NotificationApiListUserNotificationsRequest, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .listUserNotifications( + requestParameters.username, + requestParameters.fieldSelector, + requestParameters.keyword, + requestParameters.labelSelector, + requestParameters.page, + requestParameters.reason, + requestParameters.size, + requestParameters.sort, + requestParameters.unRead, + options + ) + .then((request) => request(axios, basePath)); + }, + /** + * Mark the specified notification as read. + * @param {ApiConsoleHaloRunV1alpha1NotificationApiMarkNotificationAsReadRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + markNotificationAsRead( + requestParameters: ApiConsoleHaloRunV1alpha1NotificationApiMarkNotificationAsReadRequest, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .markNotificationAsRead( + requestParameters.username, + requestParameters.name, + options + ) + .then((request) => request(axios, basePath)); + }, + /** + * Mark the specified notifications as read. + * @param {ApiConsoleHaloRunV1alpha1NotificationApiMarkNotificationsAsReadRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + markNotificationsAsRead( + requestParameters: ApiConsoleHaloRunV1alpha1NotificationApiMarkNotificationsAsReadRequest, + options?: AxiosRequestConfig + ): AxiosPromise> { + return localVarFp + .markNotificationsAsRead( + requestParameters.username, + requestParameters.markSpecifiedRequest, + options + ) + .then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * Request parameters for listUserNotifications operation in ApiConsoleHaloRunV1alpha1NotificationApi. + * @export + * @interface ApiConsoleHaloRunV1alpha1NotificationApiListUserNotificationsRequest + */ +export interface ApiConsoleHaloRunV1alpha1NotificationApiListUserNotificationsRequest { + /** + * Username + * @type {string} + * @memberof ApiConsoleHaloRunV1alpha1NotificationApiListUserNotifications + */ + readonly username: string; + + /** + * Field selector for filtering. + * @type {Array} + * @memberof ApiConsoleHaloRunV1alpha1NotificationApiListUserNotifications + */ + readonly fieldSelector?: Array; + + /** + * + * @type {string} + * @memberof ApiConsoleHaloRunV1alpha1NotificationApiListUserNotifications + */ + readonly keyword?: string; + + /** + * Label selector for filtering. + * @type {Array} + * @memberof ApiConsoleHaloRunV1alpha1NotificationApiListUserNotifications + */ + readonly labelSelector?: Array; + + /** + * The page number. Zero indicates no page. + * @type {number} + * @memberof ApiConsoleHaloRunV1alpha1NotificationApiListUserNotifications + */ + readonly page?: number; + + /** + * Filter by notification reason + * @type {string} + * @memberof ApiConsoleHaloRunV1alpha1NotificationApiListUserNotifications + */ + readonly reason?: string; + + /** + * Size of one page. Zero indicates no limit. + * @type {number} + * @memberof ApiConsoleHaloRunV1alpha1NotificationApiListUserNotifications + */ + readonly size?: number; + + /** + * Sort property and direction of the list result. Supported fields: creationTimestamp + * @type {Array} + * @memberof ApiConsoleHaloRunV1alpha1NotificationApiListUserNotifications + */ + readonly sort?: Array; + + /** + * true for unread, false for read, null for all + * @type {boolean} + * @memberof ApiConsoleHaloRunV1alpha1NotificationApiListUserNotifications + */ + readonly unRead?: boolean; +} + +/** + * Request parameters for markNotificationAsRead operation in ApiConsoleHaloRunV1alpha1NotificationApi. + * @export + * @interface ApiConsoleHaloRunV1alpha1NotificationApiMarkNotificationAsReadRequest + */ +export interface ApiConsoleHaloRunV1alpha1NotificationApiMarkNotificationAsReadRequest { + /** + * Username + * @type {string} + * @memberof ApiConsoleHaloRunV1alpha1NotificationApiMarkNotificationAsRead + */ + readonly username: string; + + /** + * Notification name + * @type {string} + * @memberof ApiConsoleHaloRunV1alpha1NotificationApiMarkNotificationAsRead + */ + readonly name: string; +} + +/** + * Request parameters for markNotificationsAsRead operation in ApiConsoleHaloRunV1alpha1NotificationApi. + * @export + * @interface ApiConsoleHaloRunV1alpha1NotificationApiMarkNotificationsAsReadRequest + */ +export interface ApiConsoleHaloRunV1alpha1NotificationApiMarkNotificationsAsReadRequest { + /** + * Username + * @type {string} + * @memberof ApiConsoleHaloRunV1alpha1NotificationApiMarkNotificationsAsRead + */ + readonly username: string; + + /** + * + * @type {MarkSpecifiedRequest} + * @memberof ApiConsoleHaloRunV1alpha1NotificationApiMarkNotificationsAsRead + */ + readonly markSpecifiedRequest: MarkSpecifiedRequest; +} + +/** + * ApiConsoleHaloRunV1alpha1NotificationApi - object-oriented interface + * @export + * @class ApiConsoleHaloRunV1alpha1NotificationApi + * @extends {BaseAPI} + */ +export class ApiConsoleHaloRunV1alpha1NotificationApi extends BaseAPI { + /** + * List notifications for the authenticated user. + * @param {ApiConsoleHaloRunV1alpha1NotificationApiListUserNotificationsRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ApiConsoleHaloRunV1alpha1NotificationApi + */ + public listUserNotifications( + requestParameters: ApiConsoleHaloRunV1alpha1NotificationApiListUserNotificationsRequest, + options?: AxiosRequestConfig + ) { + return ApiConsoleHaloRunV1alpha1NotificationApiFp(this.configuration) + .listUserNotifications( + requestParameters.username, + requestParameters.fieldSelector, + requestParameters.keyword, + requestParameters.labelSelector, + requestParameters.page, + requestParameters.reason, + requestParameters.size, + requestParameters.sort, + requestParameters.unRead, + options + ) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Mark the specified notification as read. + * @param {ApiConsoleHaloRunV1alpha1NotificationApiMarkNotificationAsReadRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ApiConsoleHaloRunV1alpha1NotificationApi + */ + public markNotificationAsRead( + requestParameters: ApiConsoleHaloRunV1alpha1NotificationApiMarkNotificationAsReadRequest, + options?: AxiosRequestConfig + ) { + return ApiConsoleHaloRunV1alpha1NotificationApiFp(this.configuration) + .markNotificationAsRead( + requestParameters.username, + requestParameters.name, + options + ) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Mark the specified notifications as read. + * @param {ApiConsoleHaloRunV1alpha1NotificationApiMarkNotificationsAsReadRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ApiConsoleHaloRunV1alpha1NotificationApi + */ + public markNotificationsAsRead( + requestParameters: ApiConsoleHaloRunV1alpha1NotificationApiMarkNotificationsAsReadRequest, + options?: AxiosRequestConfig + ) { + return ApiConsoleHaloRunV1alpha1NotificationApiFp(this.configuration) + .markNotificationsAsRead( + requestParameters.username, + requestParameters.markSpecifiedRequest, + options + ) + .then((request) => request(this.axios, this.basePath)); + } +} diff --git a/console/packages/api-client/src/api/api-console-halo-run-v1alpha1-notifier-api.ts b/console/packages/api-client/src/api/api-console-halo-run-v1alpha1-notifier-api.ts new file mode 100644 index 0000000000..d2f3cb57f2 --- /dev/null +++ b/console/packages/api-client/src/api/api-console-halo-run-v1alpha1-notifier-api.ts @@ -0,0 +1,346 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import type { Configuration } from "../configuration"; +import type { AxiosPromise, AxiosInstance, AxiosRequestConfig } from "axios"; +import globalAxios from "axios"; +// Some imports not used depending on template conditions +// @ts-ignore +import { + DUMMY_BASE_URL, + assertParamExists, + setApiKeyToObject, + setBasicAuthToObject, + setBearerAuthToObject, + setOAuthToObject, + setSearchParams, + serializeDataIfNeeded, + toPathString, + createRequestFunction, +} from "../common"; +// @ts-ignore +import { + BASE_PATH, + COLLECTION_FORMATS, + RequestArgs, + BaseAPI, + RequiredError, +} from "../base"; +/** + * ApiConsoleHaloRunV1alpha1NotifierApi - axios parameter creator + * @export + */ +export const ApiConsoleHaloRunV1alpha1NotifierApiAxiosParamCreator = function ( + configuration?: Configuration +) { + return { + /** + * Fetch sender config of notifier + * @param {string} name Notifier name + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + fetchSenderConfig: async ( + name: string, + options: AxiosRequestConfig = {} + ): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists("fetchSenderConfig", "name", name); + const localVarPath = + `/apis/api.console.halo.run/v1alpha1/notifiers/{name}/sender-config`.replace( + `{${"name"}}`, + encodeURIComponent(String(name)) + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Save sender config of notifier + * @param {string} name Notifier name + * @param {object} body + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + saveSenderConfig: async ( + name: string, + body: object, + options: AxiosRequestConfig = {} + ): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists("saveSenderConfig", "name", name); + // verify required parameter 'body' is not null or undefined + assertParamExists("saveSenderConfig", "body", body); + const localVarPath = + `/apis/api.console.halo.run/v1alpha1/notifiers/{name}/sender-config`.replace( + `{${"name"}}`, + encodeURIComponent(String(name)) + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "POST", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + localVarHeaderParameter["Content-Type"] = "application/json"; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + localVarRequestOptions.data = serializeDataIfNeeded( + body, + localVarRequestOptions, + configuration + ); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + }; +}; + +/** + * ApiConsoleHaloRunV1alpha1NotifierApi - functional programming interface + * @export + */ +export const ApiConsoleHaloRunV1alpha1NotifierApiFp = function ( + configuration?: Configuration +) { + const localVarAxiosParamCreator = + ApiConsoleHaloRunV1alpha1NotifierApiAxiosParamCreator(configuration); + return { + /** + * Fetch sender config of notifier + * @param {string} name Notifier name + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async fetchSenderConfig( + name: string, + options?: AxiosRequestConfig + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.fetchSenderConfig(name, options); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * Save sender config of notifier + * @param {string} name Notifier name + * @param {object} body + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async saveSenderConfig( + name: string, + body: object, + options?: AxiosRequestConfig + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.saveSenderConfig(name, body, options); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + }; +}; + +/** + * ApiConsoleHaloRunV1alpha1NotifierApi - factory interface + * @export + */ +export const ApiConsoleHaloRunV1alpha1NotifierApiFactory = function ( + configuration?: Configuration, + basePath?: string, + axios?: AxiosInstance +) { + const localVarFp = ApiConsoleHaloRunV1alpha1NotifierApiFp(configuration); + return { + /** + * Fetch sender config of notifier + * @param {ApiConsoleHaloRunV1alpha1NotifierApiFetchSenderConfigRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + fetchSenderConfig( + requestParameters: ApiConsoleHaloRunV1alpha1NotifierApiFetchSenderConfigRequest, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .fetchSenderConfig(requestParameters.name, options) + .then((request) => request(axios, basePath)); + }, + /** + * Save sender config of notifier + * @param {ApiConsoleHaloRunV1alpha1NotifierApiSaveSenderConfigRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + saveSenderConfig( + requestParameters: ApiConsoleHaloRunV1alpha1NotifierApiSaveSenderConfigRequest, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .saveSenderConfig( + requestParameters.name, + requestParameters.body, + options + ) + .then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * Request parameters for fetchSenderConfig operation in ApiConsoleHaloRunV1alpha1NotifierApi. + * @export + * @interface ApiConsoleHaloRunV1alpha1NotifierApiFetchSenderConfigRequest + */ +export interface ApiConsoleHaloRunV1alpha1NotifierApiFetchSenderConfigRequest { + /** + * Notifier name + * @type {string} + * @memberof ApiConsoleHaloRunV1alpha1NotifierApiFetchSenderConfig + */ + readonly name: string; +} + +/** + * Request parameters for saveSenderConfig operation in ApiConsoleHaloRunV1alpha1NotifierApi. + * @export + * @interface ApiConsoleHaloRunV1alpha1NotifierApiSaveSenderConfigRequest + */ +export interface ApiConsoleHaloRunV1alpha1NotifierApiSaveSenderConfigRequest { + /** + * Notifier name + * @type {string} + * @memberof ApiConsoleHaloRunV1alpha1NotifierApiSaveSenderConfig + */ + readonly name: string; + + /** + * + * @type {object} + * @memberof ApiConsoleHaloRunV1alpha1NotifierApiSaveSenderConfig + */ + readonly body: object; +} + +/** + * ApiConsoleHaloRunV1alpha1NotifierApi - object-oriented interface + * @export + * @class ApiConsoleHaloRunV1alpha1NotifierApi + * @extends {BaseAPI} + */ +export class ApiConsoleHaloRunV1alpha1NotifierApi extends BaseAPI { + /** + * Fetch sender config of notifier + * @param {ApiConsoleHaloRunV1alpha1NotifierApiFetchSenderConfigRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ApiConsoleHaloRunV1alpha1NotifierApi + */ + public fetchSenderConfig( + requestParameters: ApiConsoleHaloRunV1alpha1NotifierApiFetchSenderConfigRequest, + options?: AxiosRequestConfig + ) { + return ApiConsoleHaloRunV1alpha1NotifierApiFp(this.configuration) + .fetchSenderConfig(requestParameters.name, options) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Save sender config of notifier + * @param {ApiConsoleHaloRunV1alpha1NotifierApiSaveSenderConfigRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ApiConsoleHaloRunV1alpha1NotifierApi + */ + public saveSenderConfig( + requestParameters: ApiConsoleHaloRunV1alpha1NotifierApiSaveSenderConfigRequest, + options?: AxiosRequestConfig + ) { + return ApiConsoleHaloRunV1alpha1NotifierApiFp(this.configuration) + .saveSenderConfig(requestParameters.name, requestParameters.body, options) + .then((request) => request(this.axios, this.basePath)); + } +} diff --git a/console/packages/api-client/src/api/api-notification-halo-run-v1alpha1-notification-api.ts b/console/packages/api-client/src/api/api-notification-halo-run-v1alpha1-notification-api.ts new file mode 100644 index 0000000000..e6be0e109c --- /dev/null +++ b/console/packages/api-client/src/api/api-notification-halo-run-v1alpha1-notification-api.ts @@ -0,0 +1,665 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import type { Configuration } from "../configuration"; +import type { AxiosPromise, AxiosInstance, AxiosRequestConfig } from "axios"; +import globalAxios from "axios"; +// Some imports not used depending on template conditions +// @ts-ignore +import { + DUMMY_BASE_URL, + assertParamExists, + setApiKeyToObject, + setBasicAuthToObject, + setBearerAuthToObject, + setOAuthToObject, + setSearchParams, + serializeDataIfNeeded, + toPathString, + createRequestFunction, +} from "../common"; +// @ts-ignore +import { + BASE_PATH, + COLLECTION_FORMATS, + RequestArgs, + BaseAPI, + RequiredError, +} from "../base"; +// @ts-ignore +import { MarkSpecifiedRequest } from "../models"; +// @ts-ignore +import { Notification } from "../models"; +// @ts-ignore +import { NotificationList } from "../models"; +/** + * ApiNotificationHaloRunV1alpha1NotificationApi - axios parameter creator + * @export + */ +export const ApiNotificationHaloRunV1alpha1NotificationApiAxiosParamCreator = + function (configuration?: Configuration) { + return { + /** + * List notifications for the authenticated user. + * @param {string} username Username + * @param {Array} [fieldSelector] Field selector for filtering. + * @param {string} [keyword] + * @param {Array} [labelSelector] Label selector for filtering. + * @param {number} [page] The page number. Zero indicates no page. + * @param {string} [reason] Filter by notification reason + * @param {number} [size] Size of one page. Zero indicates no limit. + * @param {Array} [sort] Sort property and direction of the list result. Supported fields: creationTimestamp + * @param {boolean} [unRead] true for unread, false for read, null for all + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listUserNotifications: async ( + username: string, + fieldSelector?: Array, + keyword?: string, + labelSelector?: Array, + page?: number, + reason?: string, + size?: number, + sort?: Array, + unRead?: boolean, + options: AxiosRequestConfig = {} + ): Promise => { + // verify required parameter 'username' is not null or undefined + assertParamExists("listUserNotifications", "username", username); + const localVarPath = + `/apis/api.notification.halo.run/v1alpha1/userspaces/{username}/notifications`.replace( + `{${"username"}}`, + encodeURIComponent(String(username)) + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + if (fieldSelector) { + localVarQueryParameter["fieldSelector"] = fieldSelector; + } + + if (keyword !== undefined) { + localVarQueryParameter["keyword"] = keyword; + } + + if (labelSelector) { + localVarQueryParameter["labelSelector"] = labelSelector; + } + + if (page !== undefined) { + localVarQueryParameter["page"] = page; + } + + if (reason !== undefined) { + localVarQueryParameter["reason"] = reason; + } + + if (size !== undefined) { + localVarQueryParameter["size"] = size; + } + + if (sort) { + localVarQueryParameter["sort"] = Array.from(sort); + } + + if (unRead !== undefined) { + localVarQueryParameter["unRead"] = unRead; + } + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Mark the specified notification as read. + * @param {string} username Username + * @param {string} name Notification name + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + markNotificationAsRead: async ( + username: string, + name: string, + options: AxiosRequestConfig = {} + ): Promise => { + // verify required parameter 'username' is not null or undefined + assertParamExists("markNotificationAsRead", "username", username); + // verify required parameter 'name' is not null or undefined + assertParamExists("markNotificationAsRead", "name", name); + const localVarPath = + `/apis/api.notification.halo.run/v1alpha1/userspaces/{username}/notifications/{name}/mark-as-read` + .replace(`{${"username"}}`, encodeURIComponent(String(username))) + .replace(`{${"name"}}`, encodeURIComponent(String(name))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "PUT", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Mark the specified notifications as read. + * @param {string} username Username + * @param {MarkSpecifiedRequest} markSpecifiedRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + markNotificationsAsRead: async ( + username: string, + markSpecifiedRequest: MarkSpecifiedRequest, + options: AxiosRequestConfig = {} + ): Promise => { + // verify required parameter 'username' is not null or undefined + assertParamExists("markNotificationsAsRead", "username", username); + // verify required parameter 'markSpecifiedRequest' is not null or undefined + assertParamExists( + "markNotificationsAsRead", + "markSpecifiedRequest", + markSpecifiedRequest + ); + const localVarPath = + `/apis/api.notification.halo.run/v1alpha1/userspaces/{username}/notifications/-/mark-specified-as-read`.replace( + `{${"username"}}`, + encodeURIComponent(String(username)) + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "PUT", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + localVarHeaderParameter["Content-Type"] = "application/json"; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + localVarRequestOptions.data = serializeDataIfNeeded( + markSpecifiedRequest, + localVarRequestOptions, + configuration + ); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + }; + }; + +/** + * ApiNotificationHaloRunV1alpha1NotificationApi - functional programming interface + * @export + */ +export const ApiNotificationHaloRunV1alpha1NotificationApiFp = function ( + configuration?: Configuration +) { + const localVarAxiosParamCreator = + ApiNotificationHaloRunV1alpha1NotificationApiAxiosParamCreator( + configuration + ); + return { + /** + * List notifications for the authenticated user. + * @param {string} username Username + * @param {Array} [fieldSelector] Field selector for filtering. + * @param {string} [keyword] + * @param {Array} [labelSelector] Label selector for filtering. + * @param {number} [page] The page number. Zero indicates no page. + * @param {string} [reason] Filter by notification reason + * @param {number} [size] Size of one page. Zero indicates no limit. + * @param {Array} [sort] Sort property and direction of the list result. Supported fields: creationTimestamp + * @param {boolean} [unRead] true for unread, false for read, null for all + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async listUserNotifications( + username: string, + fieldSelector?: Array, + keyword?: string, + labelSelector?: Array, + page?: number, + reason?: string, + size?: number, + sort?: Array, + unRead?: boolean, + options?: AxiosRequestConfig + ): Promise< + ( + axios?: AxiosInstance, + basePath?: string + ) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.listUserNotifications( + username, + fieldSelector, + keyword, + labelSelector, + page, + reason, + size, + sort, + unRead, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * Mark the specified notification as read. + * @param {string} username Username + * @param {string} name Notification name + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async markNotificationAsRead( + username: string, + name: string, + options?: AxiosRequestConfig + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.markNotificationAsRead( + username, + name, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * Mark the specified notifications as read. + * @param {string} username Username + * @param {MarkSpecifiedRequest} markSpecifiedRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async markNotificationsAsRead( + username: string, + markSpecifiedRequest: MarkSpecifiedRequest, + options?: AxiosRequestConfig + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise> + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.markNotificationsAsRead( + username, + markSpecifiedRequest, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + }; +}; + +/** + * ApiNotificationHaloRunV1alpha1NotificationApi - factory interface + * @export + */ +export const ApiNotificationHaloRunV1alpha1NotificationApiFactory = function ( + configuration?: Configuration, + basePath?: string, + axios?: AxiosInstance +) { + const localVarFp = + ApiNotificationHaloRunV1alpha1NotificationApiFp(configuration); + return { + /** + * List notifications for the authenticated user. + * @param {ApiNotificationHaloRunV1alpha1NotificationApiListUserNotificationsRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listUserNotifications( + requestParameters: ApiNotificationHaloRunV1alpha1NotificationApiListUserNotificationsRequest, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .listUserNotifications( + requestParameters.username, + requestParameters.fieldSelector, + requestParameters.keyword, + requestParameters.labelSelector, + requestParameters.page, + requestParameters.reason, + requestParameters.size, + requestParameters.sort, + requestParameters.unRead, + options + ) + .then((request) => request(axios, basePath)); + }, + /** + * Mark the specified notification as read. + * @param {ApiNotificationHaloRunV1alpha1NotificationApiMarkNotificationAsReadRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + markNotificationAsRead( + requestParameters: ApiNotificationHaloRunV1alpha1NotificationApiMarkNotificationAsReadRequest, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .markNotificationAsRead( + requestParameters.username, + requestParameters.name, + options + ) + .then((request) => request(axios, basePath)); + }, + /** + * Mark the specified notifications as read. + * @param {ApiNotificationHaloRunV1alpha1NotificationApiMarkNotificationsAsReadRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + markNotificationsAsRead( + requestParameters: ApiNotificationHaloRunV1alpha1NotificationApiMarkNotificationsAsReadRequest, + options?: AxiosRequestConfig + ): AxiosPromise> { + return localVarFp + .markNotificationsAsRead( + requestParameters.username, + requestParameters.markSpecifiedRequest, + options + ) + .then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * Request parameters for listUserNotifications operation in ApiNotificationHaloRunV1alpha1NotificationApi. + * @export + * @interface ApiNotificationHaloRunV1alpha1NotificationApiListUserNotificationsRequest + */ +export interface ApiNotificationHaloRunV1alpha1NotificationApiListUserNotificationsRequest { + /** + * Username + * @type {string} + * @memberof ApiNotificationHaloRunV1alpha1NotificationApiListUserNotifications + */ + readonly username: string; + + /** + * Field selector for filtering. + * @type {Array} + * @memberof ApiNotificationHaloRunV1alpha1NotificationApiListUserNotifications + */ + readonly fieldSelector?: Array; + + /** + * + * @type {string} + * @memberof ApiNotificationHaloRunV1alpha1NotificationApiListUserNotifications + */ + readonly keyword?: string; + + /** + * Label selector for filtering. + * @type {Array} + * @memberof ApiNotificationHaloRunV1alpha1NotificationApiListUserNotifications + */ + readonly labelSelector?: Array; + + /** + * The page number. Zero indicates no page. + * @type {number} + * @memberof ApiNotificationHaloRunV1alpha1NotificationApiListUserNotifications + */ + readonly page?: number; + + /** + * Filter by notification reason + * @type {string} + * @memberof ApiNotificationHaloRunV1alpha1NotificationApiListUserNotifications + */ + readonly reason?: string; + + /** + * Size of one page. Zero indicates no limit. + * @type {number} + * @memberof ApiNotificationHaloRunV1alpha1NotificationApiListUserNotifications + */ + readonly size?: number; + + /** + * Sort property and direction of the list result. Supported fields: creationTimestamp + * @type {Array} + * @memberof ApiNotificationHaloRunV1alpha1NotificationApiListUserNotifications + */ + readonly sort?: Array; + + /** + * true for unread, false for read, null for all + * @type {boolean} + * @memberof ApiNotificationHaloRunV1alpha1NotificationApiListUserNotifications + */ + readonly unRead?: boolean; +} + +/** + * Request parameters for markNotificationAsRead operation in ApiNotificationHaloRunV1alpha1NotificationApi. + * @export + * @interface ApiNotificationHaloRunV1alpha1NotificationApiMarkNotificationAsReadRequest + */ +export interface ApiNotificationHaloRunV1alpha1NotificationApiMarkNotificationAsReadRequest { + /** + * Username + * @type {string} + * @memberof ApiNotificationHaloRunV1alpha1NotificationApiMarkNotificationAsRead + */ + readonly username: string; + + /** + * Notification name + * @type {string} + * @memberof ApiNotificationHaloRunV1alpha1NotificationApiMarkNotificationAsRead + */ + readonly name: string; +} + +/** + * Request parameters for markNotificationsAsRead operation in ApiNotificationHaloRunV1alpha1NotificationApi. + * @export + * @interface ApiNotificationHaloRunV1alpha1NotificationApiMarkNotificationsAsReadRequest + */ +export interface ApiNotificationHaloRunV1alpha1NotificationApiMarkNotificationsAsReadRequest { + /** + * Username + * @type {string} + * @memberof ApiNotificationHaloRunV1alpha1NotificationApiMarkNotificationsAsRead + */ + readonly username: string; + + /** + * + * @type {MarkSpecifiedRequest} + * @memberof ApiNotificationHaloRunV1alpha1NotificationApiMarkNotificationsAsRead + */ + readonly markSpecifiedRequest: MarkSpecifiedRequest; +} + +/** + * ApiNotificationHaloRunV1alpha1NotificationApi - object-oriented interface + * @export + * @class ApiNotificationHaloRunV1alpha1NotificationApi + * @extends {BaseAPI} + */ +export class ApiNotificationHaloRunV1alpha1NotificationApi extends BaseAPI { + /** + * List notifications for the authenticated user. + * @param {ApiNotificationHaloRunV1alpha1NotificationApiListUserNotificationsRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ApiNotificationHaloRunV1alpha1NotificationApi + */ + public listUserNotifications( + requestParameters: ApiNotificationHaloRunV1alpha1NotificationApiListUserNotificationsRequest, + options?: AxiosRequestConfig + ) { + return ApiNotificationHaloRunV1alpha1NotificationApiFp(this.configuration) + .listUserNotifications( + requestParameters.username, + requestParameters.fieldSelector, + requestParameters.keyword, + requestParameters.labelSelector, + requestParameters.page, + requestParameters.reason, + requestParameters.size, + requestParameters.sort, + requestParameters.unRead, + options + ) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Mark the specified notification as read. + * @param {ApiNotificationHaloRunV1alpha1NotificationApiMarkNotificationAsReadRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ApiNotificationHaloRunV1alpha1NotificationApi + */ + public markNotificationAsRead( + requestParameters: ApiNotificationHaloRunV1alpha1NotificationApiMarkNotificationAsReadRequest, + options?: AxiosRequestConfig + ) { + return ApiNotificationHaloRunV1alpha1NotificationApiFp(this.configuration) + .markNotificationAsRead( + requestParameters.username, + requestParameters.name, + options + ) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Mark the specified notifications as read. + * @param {ApiNotificationHaloRunV1alpha1NotificationApiMarkNotificationsAsReadRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ApiNotificationHaloRunV1alpha1NotificationApi + */ + public markNotificationsAsRead( + requestParameters: ApiNotificationHaloRunV1alpha1NotificationApiMarkNotificationsAsReadRequest, + options?: AxiosRequestConfig + ) { + return ApiNotificationHaloRunV1alpha1NotificationApiFp(this.configuration) + .markNotificationsAsRead( + requestParameters.username, + requestParameters.markSpecifiedRequest, + options + ) + .then((request) => request(this.axios, this.basePath)); + } +} diff --git a/console/packages/api-client/src/api/api-notification-halo-run-v1alpha1-notifier-api.ts b/console/packages/api-client/src/api/api-notification-halo-run-v1alpha1-notifier-api.ts new file mode 100644 index 0000000000..d5b63c0d3c --- /dev/null +++ b/console/packages/api-client/src/api/api-notification-halo-run-v1alpha1-notifier-api.ts @@ -0,0 +1,349 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import type { Configuration } from "../configuration"; +import type { AxiosPromise, AxiosInstance, AxiosRequestConfig } from "axios"; +import globalAxios from "axios"; +// Some imports not used depending on template conditions +// @ts-ignore +import { + DUMMY_BASE_URL, + assertParamExists, + setApiKeyToObject, + setBasicAuthToObject, + setBearerAuthToObject, + setOAuthToObject, + setSearchParams, + serializeDataIfNeeded, + toPathString, + createRequestFunction, +} from "../common"; +// @ts-ignore +import { + BASE_PATH, + COLLECTION_FORMATS, + RequestArgs, + BaseAPI, + RequiredError, +} from "../base"; +/** + * ApiNotificationHaloRunV1alpha1NotifierApi - axios parameter creator + * @export + */ +export const ApiNotificationHaloRunV1alpha1NotifierApiAxiosParamCreator = + function (configuration?: Configuration) { + return { + /** + * Fetch receiver config of notifier + * @param {string} name Notifier name + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + fetchReceiverConfig: async ( + name: string, + options: AxiosRequestConfig = {} + ): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists("fetchReceiverConfig", "name", name); + const localVarPath = + `/apis/api.notification.halo.run/v1alpha1/notifiers/{name}/receiver-config`.replace( + `{${"name"}}`, + encodeURIComponent(String(name)) + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Save receiver config of notifier + * @param {string} name Notifier name + * @param {object} body + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + saveReceiverConfig: async ( + name: string, + body: object, + options: AxiosRequestConfig = {} + ): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists("saveReceiverConfig", "name", name); + // verify required parameter 'body' is not null or undefined + assertParamExists("saveReceiverConfig", "body", body); + const localVarPath = + `/apis/api.notification.halo.run/v1alpha1/notifiers/{name}/receiver-config`.replace( + `{${"name"}}`, + encodeURIComponent(String(name)) + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "POST", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + localVarHeaderParameter["Content-Type"] = "application/json"; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + localVarRequestOptions.data = serializeDataIfNeeded( + body, + localVarRequestOptions, + configuration + ); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + }; + }; + +/** + * ApiNotificationHaloRunV1alpha1NotifierApi - functional programming interface + * @export + */ +export const ApiNotificationHaloRunV1alpha1NotifierApiFp = function ( + configuration?: Configuration +) { + const localVarAxiosParamCreator = + ApiNotificationHaloRunV1alpha1NotifierApiAxiosParamCreator(configuration); + return { + /** + * Fetch receiver config of notifier + * @param {string} name Notifier name + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async fetchReceiverConfig( + name: string, + options?: AxiosRequestConfig + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.fetchReceiverConfig(name, options); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * Save receiver config of notifier + * @param {string} name Notifier name + * @param {object} body + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async saveReceiverConfig( + name: string, + body: object, + options?: AxiosRequestConfig + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.saveReceiverConfig(name, body, options); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + }; +}; + +/** + * ApiNotificationHaloRunV1alpha1NotifierApi - factory interface + * @export + */ +export const ApiNotificationHaloRunV1alpha1NotifierApiFactory = function ( + configuration?: Configuration, + basePath?: string, + axios?: AxiosInstance +) { + const localVarFp = ApiNotificationHaloRunV1alpha1NotifierApiFp(configuration); + return { + /** + * Fetch receiver config of notifier + * @param {ApiNotificationHaloRunV1alpha1NotifierApiFetchReceiverConfigRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + fetchReceiverConfig( + requestParameters: ApiNotificationHaloRunV1alpha1NotifierApiFetchReceiverConfigRequest, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .fetchReceiverConfig(requestParameters.name, options) + .then((request) => request(axios, basePath)); + }, + /** + * Save receiver config of notifier + * @param {ApiNotificationHaloRunV1alpha1NotifierApiSaveReceiverConfigRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + saveReceiverConfig( + requestParameters: ApiNotificationHaloRunV1alpha1NotifierApiSaveReceiverConfigRequest, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .saveReceiverConfig( + requestParameters.name, + requestParameters.body, + options + ) + .then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * Request parameters for fetchReceiverConfig operation in ApiNotificationHaloRunV1alpha1NotifierApi. + * @export + * @interface ApiNotificationHaloRunV1alpha1NotifierApiFetchReceiverConfigRequest + */ +export interface ApiNotificationHaloRunV1alpha1NotifierApiFetchReceiverConfigRequest { + /** + * Notifier name + * @type {string} + * @memberof ApiNotificationHaloRunV1alpha1NotifierApiFetchReceiverConfig + */ + readonly name: string; +} + +/** + * Request parameters for saveReceiverConfig operation in ApiNotificationHaloRunV1alpha1NotifierApi. + * @export + * @interface ApiNotificationHaloRunV1alpha1NotifierApiSaveReceiverConfigRequest + */ +export interface ApiNotificationHaloRunV1alpha1NotifierApiSaveReceiverConfigRequest { + /** + * Notifier name + * @type {string} + * @memberof ApiNotificationHaloRunV1alpha1NotifierApiSaveReceiverConfig + */ + readonly name: string; + + /** + * + * @type {object} + * @memberof ApiNotificationHaloRunV1alpha1NotifierApiSaveReceiverConfig + */ + readonly body: object; +} + +/** + * ApiNotificationHaloRunV1alpha1NotifierApi - object-oriented interface + * @export + * @class ApiNotificationHaloRunV1alpha1NotifierApi + * @extends {BaseAPI} + */ +export class ApiNotificationHaloRunV1alpha1NotifierApi extends BaseAPI { + /** + * Fetch receiver config of notifier + * @param {ApiNotificationHaloRunV1alpha1NotifierApiFetchReceiverConfigRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ApiNotificationHaloRunV1alpha1NotifierApi + */ + public fetchReceiverConfig( + requestParameters: ApiNotificationHaloRunV1alpha1NotifierApiFetchReceiverConfigRequest, + options?: AxiosRequestConfig + ) { + return ApiNotificationHaloRunV1alpha1NotifierApiFp(this.configuration) + .fetchReceiverConfig(requestParameters.name, options) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Save receiver config of notifier + * @param {ApiNotificationHaloRunV1alpha1NotifierApiSaveReceiverConfigRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ApiNotificationHaloRunV1alpha1NotifierApi + */ + public saveReceiverConfig( + requestParameters: ApiNotificationHaloRunV1alpha1NotifierApiSaveReceiverConfigRequest, + options?: AxiosRequestConfig + ) { + return ApiNotificationHaloRunV1alpha1NotifierApiFp(this.configuration) + .saveReceiverConfig( + requestParameters.name, + requestParameters.body, + options + ) + .then((request) => request(this.axios, this.basePath)); + } +} diff --git a/console/packages/api-client/src/api/api-notification-halo-run-v1alpha1-subscription-api.ts b/console/packages/api-client/src/api/api-notification-halo-run-v1alpha1-subscription-api.ts new file mode 100644 index 0000000000..8d5c8531f2 --- /dev/null +++ b/console/packages/api-client/src/api/api-notification-halo-run-v1alpha1-subscription-api.ts @@ -0,0 +1,207 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import type { Configuration } from "../configuration"; +import type { AxiosPromise, AxiosInstance, AxiosRequestConfig } from "axios"; +import globalAxios from "axios"; +// Some imports not used depending on template conditions +// @ts-ignore +import { + DUMMY_BASE_URL, + assertParamExists, + setApiKeyToObject, + setBasicAuthToObject, + setBearerAuthToObject, + setOAuthToObject, + setSearchParams, + serializeDataIfNeeded, + toPathString, + createRequestFunction, +} from "../common"; +// @ts-ignore +import { + BASE_PATH, + COLLECTION_FORMATS, + RequestArgs, + BaseAPI, + RequiredError, +} from "../base"; +/** + * ApiNotificationHaloRunV1alpha1SubscriptionApi - axios parameter creator + * @export + */ +export const ApiNotificationHaloRunV1alpha1SubscriptionApiAxiosParamCreator = + function (configuration?: Configuration) { + return { + /** + * Unsubscribe a subscription + * @param {string} token Unsubscribe token + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + unsubscribe: async ( + token: string, + options: AxiosRequestConfig = {} + ): Promise => { + // verify required parameter 'token' is not null or undefined + assertParamExists("unsubscribe", "token", token); + const localVarPath = `/apis/api.notification.halo.run/v1alpha1/subscriptions/{name}/unsubscribe`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + if (token !== undefined) { + localVarQueryParameter["token"] = token; + } + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + }; + }; + +/** + * ApiNotificationHaloRunV1alpha1SubscriptionApi - functional programming interface + * @export + */ +export const ApiNotificationHaloRunV1alpha1SubscriptionApiFp = function ( + configuration?: Configuration +) { + const localVarAxiosParamCreator = + ApiNotificationHaloRunV1alpha1SubscriptionApiAxiosParamCreator( + configuration + ); + return { + /** + * Unsubscribe a subscription + * @param {string} token Unsubscribe token + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async unsubscribe( + token: string, + options?: AxiosRequestConfig + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = await localVarAxiosParamCreator.unsubscribe( + token, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + }; +}; + +/** + * ApiNotificationHaloRunV1alpha1SubscriptionApi - factory interface + * @export + */ +export const ApiNotificationHaloRunV1alpha1SubscriptionApiFactory = function ( + configuration?: Configuration, + basePath?: string, + axios?: AxiosInstance +) { + const localVarFp = + ApiNotificationHaloRunV1alpha1SubscriptionApiFp(configuration); + return { + /** + * Unsubscribe a subscription + * @param {ApiNotificationHaloRunV1alpha1SubscriptionApiUnsubscribeRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + unsubscribe( + requestParameters: ApiNotificationHaloRunV1alpha1SubscriptionApiUnsubscribeRequest, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .unsubscribe(requestParameters.token, options) + .then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * Request parameters for unsubscribe operation in ApiNotificationHaloRunV1alpha1SubscriptionApi. + * @export + * @interface ApiNotificationHaloRunV1alpha1SubscriptionApiUnsubscribeRequest + */ +export interface ApiNotificationHaloRunV1alpha1SubscriptionApiUnsubscribeRequest { + /** + * Unsubscribe token + * @type {string} + * @memberof ApiNotificationHaloRunV1alpha1SubscriptionApiUnsubscribe + */ + readonly token: string; +} + +/** + * ApiNotificationHaloRunV1alpha1SubscriptionApi - object-oriented interface + * @export + * @class ApiNotificationHaloRunV1alpha1SubscriptionApi + * @extends {BaseAPI} + */ +export class ApiNotificationHaloRunV1alpha1SubscriptionApi extends BaseAPI { + /** + * Unsubscribe a subscription + * @param {ApiNotificationHaloRunV1alpha1SubscriptionApiUnsubscribeRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ApiNotificationHaloRunV1alpha1SubscriptionApi + */ + public unsubscribe( + requestParameters: ApiNotificationHaloRunV1alpha1SubscriptionApiUnsubscribeRequest, + options?: AxiosRequestConfig + ) { + return ApiNotificationHaloRunV1alpha1SubscriptionApiFp(this.configuration) + .unsubscribe(requestParameters.token, options) + .then((request) => request(this.axios, this.basePath)); + } +} diff --git a/console/packages/api-client/src/api/notification-halo-run-v1alpha1-notification-api.ts b/console/packages/api-client/src/api/notification-halo-run-v1alpha1-notification-api.ts new file mode 100644 index 0000000000..861aa52db6 --- /dev/null +++ b/console/packages/api-client/src/api/notification-halo-run-v1alpha1-notification-api.ts @@ -0,0 +1,835 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import type { Configuration } from "../configuration"; +import type { AxiosPromise, AxiosInstance, AxiosRequestConfig } from "axios"; +import globalAxios from "axios"; +// Some imports not used depending on template conditions +// @ts-ignore +import { + DUMMY_BASE_URL, + assertParamExists, + setApiKeyToObject, + setBasicAuthToObject, + setBearerAuthToObject, + setOAuthToObject, + setSearchParams, + serializeDataIfNeeded, + toPathString, + createRequestFunction, +} from "../common"; +// @ts-ignore +import { + BASE_PATH, + COLLECTION_FORMATS, + RequestArgs, + BaseAPI, + RequiredError, +} from "../base"; +// @ts-ignore +import { Notification } from "../models"; +// @ts-ignore +import { NotificationList } from "../models"; +/** + * NotificationHaloRunV1alpha1NotificationApi - axios parameter creator + * @export + */ +export const NotificationHaloRunV1alpha1NotificationApiAxiosParamCreator = + function (configuration?: Configuration) { + return { + /** + * Create notification.halo.run/v1alpha1/Notification + * @param {Notification} [notification] Fresh notification + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + createnotificationHaloRunV1alpha1Notification: async ( + notification?: Notification, + options: AxiosRequestConfig = {} + ): Promise => { + const localVarPath = `/apis/notification.halo.run/v1alpha1/notifications`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "POST", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + localVarHeaderParameter["Content-Type"] = "application/json"; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + localVarRequestOptions.data = serializeDataIfNeeded( + notification, + localVarRequestOptions, + configuration + ); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Delete notification.halo.run/v1alpha1/Notification + * @param {string} name Name of notification + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deletenotificationHaloRunV1alpha1Notification: async ( + name: string, + options: AxiosRequestConfig = {} + ): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists( + "deletenotificationHaloRunV1alpha1Notification", + "name", + name + ); + const localVarPath = + `/apis/notification.halo.run/v1alpha1/notifications/{name}`.replace( + `{${"name"}}`, + encodeURIComponent(String(name)) + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "DELETE", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Get notification.halo.run/v1alpha1/Notification + * @param {string} name Name of notification + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getnotificationHaloRunV1alpha1Notification: async ( + name: string, + options: AxiosRequestConfig = {} + ): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists( + "getnotificationHaloRunV1alpha1Notification", + "name", + name + ); + const localVarPath = + `/apis/notification.halo.run/v1alpha1/notifications/{name}`.replace( + `{${"name"}}`, + encodeURIComponent(String(name)) + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * List notification.halo.run/v1alpha1/Notification + * @param {Array} [fieldSelector] Field selector for filtering. + * @param {Array} [labelSelector] Label selector for filtering. + * @param {number} [page] The page number. Zero indicates no page. + * @param {number} [size] Size of one page. Zero indicates no limit. + * @param {Array} [sort] Sort property and direction of the list result. Support sorting based on attribute name path. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listnotificationHaloRunV1alpha1Notification: async ( + fieldSelector?: Array, + labelSelector?: Array, + page?: number, + size?: number, + sort?: Array, + options: AxiosRequestConfig = {} + ): Promise => { + const localVarPath = `/apis/notification.halo.run/v1alpha1/notifications`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + if (fieldSelector) { + localVarQueryParameter["fieldSelector"] = fieldSelector; + } + + if (labelSelector) { + localVarQueryParameter["labelSelector"] = labelSelector; + } + + if (page !== undefined) { + localVarQueryParameter["page"] = page; + } + + if (size !== undefined) { + localVarQueryParameter["size"] = size; + } + + if (sort) { + localVarQueryParameter["sort"] = Array.from(sort); + } + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Update notification.halo.run/v1alpha1/Notification + * @param {string} name Name of notification + * @param {Notification} [notification] Updated notification + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + updatenotificationHaloRunV1alpha1Notification: async ( + name: string, + notification?: Notification, + options: AxiosRequestConfig = {} + ): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists( + "updatenotificationHaloRunV1alpha1Notification", + "name", + name + ); + const localVarPath = + `/apis/notification.halo.run/v1alpha1/notifications/{name}`.replace( + `{${"name"}}`, + encodeURIComponent(String(name)) + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "PUT", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + localVarHeaderParameter["Content-Type"] = "application/json"; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + localVarRequestOptions.data = serializeDataIfNeeded( + notification, + localVarRequestOptions, + configuration + ); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + }; + }; + +/** + * NotificationHaloRunV1alpha1NotificationApi - functional programming interface + * @export + */ +export const NotificationHaloRunV1alpha1NotificationApiFp = function ( + configuration?: Configuration +) { + const localVarAxiosParamCreator = + NotificationHaloRunV1alpha1NotificationApiAxiosParamCreator(configuration); + return { + /** + * Create notification.halo.run/v1alpha1/Notification + * @param {Notification} [notification] Fresh notification + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async createnotificationHaloRunV1alpha1Notification( + notification?: Notification, + options?: AxiosRequestConfig + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.createnotificationHaloRunV1alpha1Notification( + notification, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * Delete notification.halo.run/v1alpha1/Notification + * @param {string} name Name of notification + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async deletenotificationHaloRunV1alpha1Notification( + name: string, + options?: AxiosRequestConfig + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.deletenotificationHaloRunV1alpha1Notification( + name, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * Get notification.halo.run/v1alpha1/Notification + * @param {string} name Name of notification + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getnotificationHaloRunV1alpha1Notification( + name: string, + options?: AxiosRequestConfig + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.getnotificationHaloRunV1alpha1Notification( + name, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * List notification.halo.run/v1alpha1/Notification + * @param {Array} [fieldSelector] Field selector for filtering. + * @param {Array} [labelSelector] Label selector for filtering. + * @param {number} [page] The page number. Zero indicates no page. + * @param {number} [size] Size of one page. Zero indicates no limit. + * @param {Array} [sort] Sort property and direction of the list result. Support sorting based on attribute name path. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async listnotificationHaloRunV1alpha1Notification( + fieldSelector?: Array, + labelSelector?: Array, + page?: number, + size?: number, + sort?: Array, + options?: AxiosRequestConfig + ): Promise< + ( + axios?: AxiosInstance, + basePath?: string + ) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.listnotificationHaloRunV1alpha1Notification( + fieldSelector, + labelSelector, + page, + size, + sort, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * Update notification.halo.run/v1alpha1/Notification + * @param {string} name Name of notification + * @param {Notification} [notification] Updated notification + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async updatenotificationHaloRunV1alpha1Notification( + name: string, + notification?: Notification, + options?: AxiosRequestConfig + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.updatenotificationHaloRunV1alpha1Notification( + name, + notification, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + }; +}; + +/** + * NotificationHaloRunV1alpha1NotificationApi - factory interface + * @export + */ +export const NotificationHaloRunV1alpha1NotificationApiFactory = function ( + configuration?: Configuration, + basePath?: string, + axios?: AxiosInstance +) { + const localVarFp = + NotificationHaloRunV1alpha1NotificationApiFp(configuration); + return { + /** + * Create notification.halo.run/v1alpha1/Notification + * @param {NotificationHaloRunV1alpha1NotificationApiCreatenotificationHaloRunV1alpha1NotificationRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + createnotificationHaloRunV1alpha1Notification( + requestParameters: NotificationHaloRunV1alpha1NotificationApiCreatenotificationHaloRunV1alpha1NotificationRequest = {}, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .createnotificationHaloRunV1alpha1Notification( + requestParameters.notification, + options + ) + .then((request) => request(axios, basePath)); + }, + /** + * Delete notification.halo.run/v1alpha1/Notification + * @param {NotificationHaloRunV1alpha1NotificationApiDeletenotificationHaloRunV1alpha1NotificationRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deletenotificationHaloRunV1alpha1Notification( + requestParameters: NotificationHaloRunV1alpha1NotificationApiDeletenotificationHaloRunV1alpha1NotificationRequest, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .deletenotificationHaloRunV1alpha1Notification( + requestParameters.name, + options + ) + .then((request) => request(axios, basePath)); + }, + /** + * Get notification.halo.run/v1alpha1/Notification + * @param {NotificationHaloRunV1alpha1NotificationApiGetnotificationHaloRunV1alpha1NotificationRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getnotificationHaloRunV1alpha1Notification( + requestParameters: NotificationHaloRunV1alpha1NotificationApiGetnotificationHaloRunV1alpha1NotificationRequest, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .getnotificationHaloRunV1alpha1Notification( + requestParameters.name, + options + ) + .then((request) => request(axios, basePath)); + }, + /** + * List notification.halo.run/v1alpha1/Notification + * @param {NotificationHaloRunV1alpha1NotificationApiListnotificationHaloRunV1alpha1NotificationRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listnotificationHaloRunV1alpha1Notification( + requestParameters: NotificationHaloRunV1alpha1NotificationApiListnotificationHaloRunV1alpha1NotificationRequest = {}, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .listnotificationHaloRunV1alpha1Notification( + requestParameters.fieldSelector, + requestParameters.labelSelector, + requestParameters.page, + requestParameters.size, + requestParameters.sort, + options + ) + .then((request) => request(axios, basePath)); + }, + /** + * Update notification.halo.run/v1alpha1/Notification + * @param {NotificationHaloRunV1alpha1NotificationApiUpdatenotificationHaloRunV1alpha1NotificationRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + updatenotificationHaloRunV1alpha1Notification( + requestParameters: NotificationHaloRunV1alpha1NotificationApiUpdatenotificationHaloRunV1alpha1NotificationRequest, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .updatenotificationHaloRunV1alpha1Notification( + requestParameters.name, + requestParameters.notification, + options + ) + .then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * Request parameters for createnotificationHaloRunV1alpha1Notification operation in NotificationHaloRunV1alpha1NotificationApi. + * @export + * @interface NotificationHaloRunV1alpha1NotificationApiCreatenotificationHaloRunV1alpha1NotificationRequest + */ +export interface NotificationHaloRunV1alpha1NotificationApiCreatenotificationHaloRunV1alpha1NotificationRequest { + /** + * Fresh notification + * @type {Notification} + * @memberof NotificationHaloRunV1alpha1NotificationApiCreatenotificationHaloRunV1alpha1Notification + */ + readonly notification?: Notification; +} + +/** + * Request parameters for deletenotificationHaloRunV1alpha1Notification operation in NotificationHaloRunV1alpha1NotificationApi. + * @export + * @interface NotificationHaloRunV1alpha1NotificationApiDeletenotificationHaloRunV1alpha1NotificationRequest + */ +export interface NotificationHaloRunV1alpha1NotificationApiDeletenotificationHaloRunV1alpha1NotificationRequest { + /** + * Name of notification + * @type {string} + * @memberof NotificationHaloRunV1alpha1NotificationApiDeletenotificationHaloRunV1alpha1Notification + */ + readonly name: string; +} + +/** + * Request parameters for getnotificationHaloRunV1alpha1Notification operation in NotificationHaloRunV1alpha1NotificationApi. + * @export + * @interface NotificationHaloRunV1alpha1NotificationApiGetnotificationHaloRunV1alpha1NotificationRequest + */ +export interface NotificationHaloRunV1alpha1NotificationApiGetnotificationHaloRunV1alpha1NotificationRequest { + /** + * Name of notification + * @type {string} + * @memberof NotificationHaloRunV1alpha1NotificationApiGetnotificationHaloRunV1alpha1Notification + */ + readonly name: string; +} + +/** + * Request parameters for listnotificationHaloRunV1alpha1Notification operation in NotificationHaloRunV1alpha1NotificationApi. + * @export + * @interface NotificationHaloRunV1alpha1NotificationApiListnotificationHaloRunV1alpha1NotificationRequest + */ +export interface NotificationHaloRunV1alpha1NotificationApiListnotificationHaloRunV1alpha1NotificationRequest { + /** + * Field selector for filtering. + * @type {Array} + * @memberof NotificationHaloRunV1alpha1NotificationApiListnotificationHaloRunV1alpha1Notification + */ + readonly fieldSelector?: Array; + + /** + * Label selector for filtering. + * @type {Array} + * @memberof NotificationHaloRunV1alpha1NotificationApiListnotificationHaloRunV1alpha1Notification + */ + readonly labelSelector?: Array; + + /** + * The page number. Zero indicates no page. + * @type {number} + * @memberof NotificationHaloRunV1alpha1NotificationApiListnotificationHaloRunV1alpha1Notification + */ + readonly page?: number; + + /** + * Size of one page. Zero indicates no limit. + * @type {number} + * @memberof NotificationHaloRunV1alpha1NotificationApiListnotificationHaloRunV1alpha1Notification + */ + readonly size?: number; + + /** + * Sort property and direction of the list result. Support sorting based on attribute name path. + * @type {Array} + * @memberof NotificationHaloRunV1alpha1NotificationApiListnotificationHaloRunV1alpha1Notification + */ + readonly sort?: Array; +} + +/** + * Request parameters for updatenotificationHaloRunV1alpha1Notification operation in NotificationHaloRunV1alpha1NotificationApi. + * @export + * @interface NotificationHaloRunV1alpha1NotificationApiUpdatenotificationHaloRunV1alpha1NotificationRequest + */ +export interface NotificationHaloRunV1alpha1NotificationApiUpdatenotificationHaloRunV1alpha1NotificationRequest { + /** + * Name of notification + * @type {string} + * @memberof NotificationHaloRunV1alpha1NotificationApiUpdatenotificationHaloRunV1alpha1Notification + */ + readonly name: string; + + /** + * Updated notification + * @type {Notification} + * @memberof NotificationHaloRunV1alpha1NotificationApiUpdatenotificationHaloRunV1alpha1Notification + */ + readonly notification?: Notification; +} + +/** + * NotificationHaloRunV1alpha1NotificationApi - object-oriented interface + * @export + * @class NotificationHaloRunV1alpha1NotificationApi + * @extends {BaseAPI} + */ +export class NotificationHaloRunV1alpha1NotificationApi extends BaseAPI { + /** + * Create notification.halo.run/v1alpha1/Notification + * @param {NotificationHaloRunV1alpha1NotificationApiCreatenotificationHaloRunV1alpha1NotificationRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1NotificationApi + */ + public createnotificationHaloRunV1alpha1Notification( + requestParameters: NotificationHaloRunV1alpha1NotificationApiCreatenotificationHaloRunV1alpha1NotificationRequest = {}, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1NotificationApiFp(this.configuration) + .createnotificationHaloRunV1alpha1Notification( + requestParameters.notification, + options + ) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Delete notification.halo.run/v1alpha1/Notification + * @param {NotificationHaloRunV1alpha1NotificationApiDeletenotificationHaloRunV1alpha1NotificationRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1NotificationApi + */ + public deletenotificationHaloRunV1alpha1Notification( + requestParameters: NotificationHaloRunV1alpha1NotificationApiDeletenotificationHaloRunV1alpha1NotificationRequest, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1NotificationApiFp(this.configuration) + .deletenotificationHaloRunV1alpha1Notification( + requestParameters.name, + options + ) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Get notification.halo.run/v1alpha1/Notification + * @param {NotificationHaloRunV1alpha1NotificationApiGetnotificationHaloRunV1alpha1NotificationRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1NotificationApi + */ + public getnotificationHaloRunV1alpha1Notification( + requestParameters: NotificationHaloRunV1alpha1NotificationApiGetnotificationHaloRunV1alpha1NotificationRequest, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1NotificationApiFp(this.configuration) + .getnotificationHaloRunV1alpha1Notification( + requestParameters.name, + options + ) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * List notification.halo.run/v1alpha1/Notification + * @param {NotificationHaloRunV1alpha1NotificationApiListnotificationHaloRunV1alpha1NotificationRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1NotificationApi + */ + public listnotificationHaloRunV1alpha1Notification( + requestParameters: NotificationHaloRunV1alpha1NotificationApiListnotificationHaloRunV1alpha1NotificationRequest = {}, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1NotificationApiFp(this.configuration) + .listnotificationHaloRunV1alpha1Notification( + requestParameters.fieldSelector, + requestParameters.labelSelector, + requestParameters.page, + requestParameters.size, + requestParameters.sort, + options + ) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Update notification.halo.run/v1alpha1/Notification + * @param {NotificationHaloRunV1alpha1NotificationApiUpdatenotificationHaloRunV1alpha1NotificationRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1NotificationApi + */ + public updatenotificationHaloRunV1alpha1Notification( + requestParameters: NotificationHaloRunV1alpha1NotificationApiUpdatenotificationHaloRunV1alpha1NotificationRequest, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1NotificationApiFp(this.configuration) + .updatenotificationHaloRunV1alpha1Notification( + requestParameters.name, + requestParameters.notification, + options + ) + .then((request) => request(this.axios, this.basePath)); + } +} diff --git a/console/packages/api-client/src/api/notification-halo-run-v1alpha1-notification-template-api.ts b/console/packages/api-client/src/api/notification-halo-run-v1alpha1-notification-template-api.ts new file mode 100644 index 0000000000..e1bb705555 --- /dev/null +++ b/console/packages/api-client/src/api/notification-halo-run-v1alpha1-notification-template-api.ts @@ -0,0 +1,857 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import type { Configuration } from "../configuration"; +import type { AxiosPromise, AxiosInstance, AxiosRequestConfig } from "axios"; +import globalAxios from "axios"; +// Some imports not used depending on template conditions +// @ts-ignore +import { + DUMMY_BASE_URL, + assertParamExists, + setApiKeyToObject, + setBasicAuthToObject, + setBearerAuthToObject, + setOAuthToObject, + setSearchParams, + serializeDataIfNeeded, + toPathString, + createRequestFunction, +} from "../common"; +// @ts-ignore +import { + BASE_PATH, + COLLECTION_FORMATS, + RequestArgs, + BaseAPI, + RequiredError, +} from "../base"; +// @ts-ignore +import { NotificationTemplate } from "../models"; +// @ts-ignore +import { NotificationTemplateList } from "../models"; +/** + * NotificationHaloRunV1alpha1NotificationTemplateApi - axios parameter creator + * @export + */ +export const NotificationHaloRunV1alpha1NotificationTemplateApiAxiosParamCreator = + function (configuration?: Configuration) { + return { + /** + * Create notification.halo.run/v1alpha1/NotificationTemplate + * @param {NotificationTemplate} [notificationTemplate] Fresh notificationtemplate + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + createnotificationHaloRunV1alpha1NotificationTemplate: async ( + notificationTemplate?: NotificationTemplate, + options: AxiosRequestConfig = {} + ): Promise => { + const localVarPath = `/apis/notification.halo.run/v1alpha1/notificationtemplates`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "POST", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + localVarHeaderParameter["Content-Type"] = "application/json"; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + localVarRequestOptions.data = serializeDataIfNeeded( + notificationTemplate, + localVarRequestOptions, + configuration + ); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Delete notification.halo.run/v1alpha1/NotificationTemplate + * @param {string} name Name of notificationtemplate + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deletenotificationHaloRunV1alpha1NotificationTemplate: async ( + name: string, + options: AxiosRequestConfig = {} + ): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists( + "deletenotificationHaloRunV1alpha1NotificationTemplate", + "name", + name + ); + const localVarPath = + `/apis/notification.halo.run/v1alpha1/notificationtemplates/{name}`.replace( + `{${"name"}}`, + encodeURIComponent(String(name)) + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "DELETE", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Get notification.halo.run/v1alpha1/NotificationTemplate + * @param {string} name Name of notificationtemplate + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getnotificationHaloRunV1alpha1NotificationTemplate: async ( + name: string, + options: AxiosRequestConfig = {} + ): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists( + "getnotificationHaloRunV1alpha1NotificationTemplate", + "name", + name + ); + const localVarPath = + `/apis/notification.halo.run/v1alpha1/notificationtemplates/{name}`.replace( + `{${"name"}}`, + encodeURIComponent(String(name)) + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * List notification.halo.run/v1alpha1/NotificationTemplate + * @param {Array} [fieldSelector] Field selector for filtering. + * @param {Array} [labelSelector] Label selector for filtering. + * @param {number} [page] The page number. Zero indicates no page. + * @param {number} [size] Size of one page. Zero indicates no limit. + * @param {Array} [sort] Sort property and direction of the list result. Support sorting based on attribute name path. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listnotificationHaloRunV1alpha1NotificationTemplate: async ( + fieldSelector?: Array, + labelSelector?: Array, + page?: number, + size?: number, + sort?: Array, + options: AxiosRequestConfig = {} + ): Promise => { + const localVarPath = `/apis/notification.halo.run/v1alpha1/notificationtemplates`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + if (fieldSelector) { + localVarQueryParameter["fieldSelector"] = fieldSelector; + } + + if (labelSelector) { + localVarQueryParameter["labelSelector"] = labelSelector; + } + + if (page !== undefined) { + localVarQueryParameter["page"] = page; + } + + if (size !== undefined) { + localVarQueryParameter["size"] = size; + } + + if (sort) { + localVarQueryParameter["sort"] = Array.from(sort); + } + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Update notification.halo.run/v1alpha1/NotificationTemplate + * @param {string} name Name of notificationtemplate + * @param {NotificationTemplate} [notificationTemplate] Updated notificationtemplate + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + updatenotificationHaloRunV1alpha1NotificationTemplate: async ( + name: string, + notificationTemplate?: NotificationTemplate, + options: AxiosRequestConfig = {} + ): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists( + "updatenotificationHaloRunV1alpha1NotificationTemplate", + "name", + name + ); + const localVarPath = + `/apis/notification.halo.run/v1alpha1/notificationtemplates/{name}`.replace( + `{${"name"}}`, + encodeURIComponent(String(name)) + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "PUT", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + localVarHeaderParameter["Content-Type"] = "application/json"; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + localVarRequestOptions.data = serializeDataIfNeeded( + notificationTemplate, + localVarRequestOptions, + configuration + ); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + }; + }; + +/** + * NotificationHaloRunV1alpha1NotificationTemplateApi - functional programming interface + * @export + */ +export const NotificationHaloRunV1alpha1NotificationTemplateApiFp = function ( + configuration?: Configuration +) { + const localVarAxiosParamCreator = + NotificationHaloRunV1alpha1NotificationTemplateApiAxiosParamCreator( + configuration + ); + return { + /** + * Create notification.halo.run/v1alpha1/NotificationTemplate + * @param {NotificationTemplate} [notificationTemplate] Fresh notificationtemplate + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async createnotificationHaloRunV1alpha1NotificationTemplate( + notificationTemplate?: NotificationTemplate, + options?: AxiosRequestConfig + ): Promise< + ( + axios?: AxiosInstance, + basePath?: string + ) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.createnotificationHaloRunV1alpha1NotificationTemplate( + notificationTemplate, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * Delete notification.halo.run/v1alpha1/NotificationTemplate + * @param {string} name Name of notificationtemplate + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async deletenotificationHaloRunV1alpha1NotificationTemplate( + name: string, + options?: AxiosRequestConfig + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.deletenotificationHaloRunV1alpha1NotificationTemplate( + name, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * Get notification.halo.run/v1alpha1/NotificationTemplate + * @param {string} name Name of notificationtemplate + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getnotificationHaloRunV1alpha1NotificationTemplate( + name: string, + options?: AxiosRequestConfig + ): Promise< + ( + axios?: AxiosInstance, + basePath?: string + ) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.getnotificationHaloRunV1alpha1NotificationTemplate( + name, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * List notification.halo.run/v1alpha1/NotificationTemplate + * @param {Array} [fieldSelector] Field selector for filtering. + * @param {Array} [labelSelector] Label selector for filtering. + * @param {number} [page] The page number. Zero indicates no page. + * @param {number} [size] Size of one page. Zero indicates no limit. + * @param {Array} [sort] Sort property and direction of the list result. Support sorting based on attribute name path. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async listnotificationHaloRunV1alpha1NotificationTemplate( + fieldSelector?: Array, + labelSelector?: Array, + page?: number, + size?: number, + sort?: Array, + options?: AxiosRequestConfig + ): Promise< + ( + axios?: AxiosInstance, + basePath?: string + ) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.listnotificationHaloRunV1alpha1NotificationTemplate( + fieldSelector, + labelSelector, + page, + size, + sort, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * Update notification.halo.run/v1alpha1/NotificationTemplate + * @param {string} name Name of notificationtemplate + * @param {NotificationTemplate} [notificationTemplate] Updated notificationtemplate + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async updatenotificationHaloRunV1alpha1NotificationTemplate( + name: string, + notificationTemplate?: NotificationTemplate, + options?: AxiosRequestConfig + ): Promise< + ( + axios?: AxiosInstance, + basePath?: string + ) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.updatenotificationHaloRunV1alpha1NotificationTemplate( + name, + notificationTemplate, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + }; +}; + +/** + * NotificationHaloRunV1alpha1NotificationTemplateApi - factory interface + * @export + */ +export const NotificationHaloRunV1alpha1NotificationTemplateApiFactory = + function ( + configuration?: Configuration, + basePath?: string, + axios?: AxiosInstance + ) { + const localVarFp = + NotificationHaloRunV1alpha1NotificationTemplateApiFp(configuration); + return { + /** + * Create notification.halo.run/v1alpha1/NotificationTemplate + * @param {NotificationHaloRunV1alpha1NotificationTemplateApiCreatenotificationHaloRunV1alpha1NotificationTemplateRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + createnotificationHaloRunV1alpha1NotificationTemplate( + requestParameters: NotificationHaloRunV1alpha1NotificationTemplateApiCreatenotificationHaloRunV1alpha1NotificationTemplateRequest = {}, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .createnotificationHaloRunV1alpha1NotificationTemplate( + requestParameters.notificationTemplate, + options + ) + .then((request) => request(axios, basePath)); + }, + /** + * Delete notification.halo.run/v1alpha1/NotificationTemplate + * @param {NotificationHaloRunV1alpha1NotificationTemplateApiDeletenotificationHaloRunV1alpha1NotificationTemplateRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deletenotificationHaloRunV1alpha1NotificationTemplate( + requestParameters: NotificationHaloRunV1alpha1NotificationTemplateApiDeletenotificationHaloRunV1alpha1NotificationTemplateRequest, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .deletenotificationHaloRunV1alpha1NotificationTemplate( + requestParameters.name, + options + ) + .then((request) => request(axios, basePath)); + }, + /** + * Get notification.halo.run/v1alpha1/NotificationTemplate + * @param {NotificationHaloRunV1alpha1NotificationTemplateApiGetnotificationHaloRunV1alpha1NotificationTemplateRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getnotificationHaloRunV1alpha1NotificationTemplate( + requestParameters: NotificationHaloRunV1alpha1NotificationTemplateApiGetnotificationHaloRunV1alpha1NotificationTemplateRequest, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .getnotificationHaloRunV1alpha1NotificationTemplate( + requestParameters.name, + options + ) + .then((request) => request(axios, basePath)); + }, + /** + * List notification.halo.run/v1alpha1/NotificationTemplate + * @param {NotificationHaloRunV1alpha1NotificationTemplateApiListnotificationHaloRunV1alpha1NotificationTemplateRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listnotificationHaloRunV1alpha1NotificationTemplate( + requestParameters: NotificationHaloRunV1alpha1NotificationTemplateApiListnotificationHaloRunV1alpha1NotificationTemplateRequest = {}, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .listnotificationHaloRunV1alpha1NotificationTemplate( + requestParameters.fieldSelector, + requestParameters.labelSelector, + requestParameters.page, + requestParameters.size, + requestParameters.sort, + options + ) + .then((request) => request(axios, basePath)); + }, + /** + * Update notification.halo.run/v1alpha1/NotificationTemplate + * @param {NotificationHaloRunV1alpha1NotificationTemplateApiUpdatenotificationHaloRunV1alpha1NotificationTemplateRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + updatenotificationHaloRunV1alpha1NotificationTemplate( + requestParameters: NotificationHaloRunV1alpha1NotificationTemplateApiUpdatenotificationHaloRunV1alpha1NotificationTemplateRequest, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .updatenotificationHaloRunV1alpha1NotificationTemplate( + requestParameters.name, + requestParameters.notificationTemplate, + options + ) + .then((request) => request(axios, basePath)); + }, + }; + }; + +/** + * Request parameters for createnotificationHaloRunV1alpha1NotificationTemplate operation in NotificationHaloRunV1alpha1NotificationTemplateApi. + * @export + * @interface NotificationHaloRunV1alpha1NotificationTemplateApiCreatenotificationHaloRunV1alpha1NotificationTemplateRequest + */ +export interface NotificationHaloRunV1alpha1NotificationTemplateApiCreatenotificationHaloRunV1alpha1NotificationTemplateRequest { + /** + * Fresh notificationtemplate + * @type {NotificationTemplate} + * @memberof NotificationHaloRunV1alpha1NotificationTemplateApiCreatenotificationHaloRunV1alpha1NotificationTemplate + */ + readonly notificationTemplate?: NotificationTemplate; +} + +/** + * Request parameters for deletenotificationHaloRunV1alpha1NotificationTemplate operation in NotificationHaloRunV1alpha1NotificationTemplateApi. + * @export + * @interface NotificationHaloRunV1alpha1NotificationTemplateApiDeletenotificationHaloRunV1alpha1NotificationTemplateRequest + */ +export interface NotificationHaloRunV1alpha1NotificationTemplateApiDeletenotificationHaloRunV1alpha1NotificationTemplateRequest { + /** + * Name of notificationtemplate + * @type {string} + * @memberof NotificationHaloRunV1alpha1NotificationTemplateApiDeletenotificationHaloRunV1alpha1NotificationTemplate + */ + readonly name: string; +} + +/** + * Request parameters for getnotificationHaloRunV1alpha1NotificationTemplate operation in NotificationHaloRunV1alpha1NotificationTemplateApi. + * @export + * @interface NotificationHaloRunV1alpha1NotificationTemplateApiGetnotificationHaloRunV1alpha1NotificationTemplateRequest + */ +export interface NotificationHaloRunV1alpha1NotificationTemplateApiGetnotificationHaloRunV1alpha1NotificationTemplateRequest { + /** + * Name of notificationtemplate + * @type {string} + * @memberof NotificationHaloRunV1alpha1NotificationTemplateApiGetnotificationHaloRunV1alpha1NotificationTemplate + */ + readonly name: string; +} + +/** + * Request parameters for listnotificationHaloRunV1alpha1NotificationTemplate operation in NotificationHaloRunV1alpha1NotificationTemplateApi. + * @export + * @interface NotificationHaloRunV1alpha1NotificationTemplateApiListnotificationHaloRunV1alpha1NotificationTemplateRequest + */ +export interface NotificationHaloRunV1alpha1NotificationTemplateApiListnotificationHaloRunV1alpha1NotificationTemplateRequest { + /** + * Field selector for filtering. + * @type {Array} + * @memberof NotificationHaloRunV1alpha1NotificationTemplateApiListnotificationHaloRunV1alpha1NotificationTemplate + */ + readonly fieldSelector?: Array; + + /** + * Label selector for filtering. + * @type {Array} + * @memberof NotificationHaloRunV1alpha1NotificationTemplateApiListnotificationHaloRunV1alpha1NotificationTemplate + */ + readonly labelSelector?: Array; + + /** + * The page number. Zero indicates no page. + * @type {number} + * @memberof NotificationHaloRunV1alpha1NotificationTemplateApiListnotificationHaloRunV1alpha1NotificationTemplate + */ + readonly page?: number; + + /** + * Size of one page. Zero indicates no limit. + * @type {number} + * @memberof NotificationHaloRunV1alpha1NotificationTemplateApiListnotificationHaloRunV1alpha1NotificationTemplate + */ + readonly size?: number; + + /** + * Sort property and direction of the list result. Support sorting based on attribute name path. + * @type {Array} + * @memberof NotificationHaloRunV1alpha1NotificationTemplateApiListnotificationHaloRunV1alpha1NotificationTemplate + */ + readonly sort?: Array; +} + +/** + * Request parameters for updatenotificationHaloRunV1alpha1NotificationTemplate operation in NotificationHaloRunV1alpha1NotificationTemplateApi. + * @export + * @interface NotificationHaloRunV1alpha1NotificationTemplateApiUpdatenotificationHaloRunV1alpha1NotificationTemplateRequest + */ +export interface NotificationHaloRunV1alpha1NotificationTemplateApiUpdatenotificationHaloRunV1alpha1NotificationTemplateRequest { + /** + * Name of notificationtemplate + * @type {string} + * @memberof NotificationHaloRunV1alpha1NotificationTemplateApiUpdatenotificationHaloRunV1alpha1NotificationTemplate + */ + readonly name: string; + + /** + * Updated notificationtemplate + * @type {NotificationTemplate} + * @memberof NotificationHaloRunV1alpha1NotificationTemplateApiUpdatenotificationHaloRunV1alpha1NotificationTemplate + */ + readonly notificationTemplate?: NotificationTemplate; +} + +/** + * NotificationHaloRunV1alpha1NotificationTemplateApi - object-oriented interface + * @export + * @class NotificationHaloRunV1alpha1NotificationTemplateApi + * @extends {BaseAPI} + */ +export class NotificationHaloRunV1alpha1NotificationTemplateApi extends BaseAPI { + /** + * Create notification.halo.run/v1alpha1/NotificationTemplate + * @param {NotificationHaloRunV1alpha1NotificationTemplateApiCreatenotificationHaloRunV1alpha1NotificationTemplateRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1NotificationTemplateApi + */ + public createnotificationHaloRunV1alpha1NotificationTemplate( + requestParameters: NotificationHaloRunV1alpha1NotificationTemplateApiCreatenotificationHaloRunV1alpha1NotificationTemplateRequest = {}, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1NotificationTemplateApiFp( + this.configuration + ) + .createnotificationHaloRunV1alpha1NotificationTemplate( + requestParameters.notificationTemplate, + options + ) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Delete notification.halo.run/v1alpha1/NotificationTemplate + * @param {NotificationHaloRunV1alpha1NotificationTemplateApiDeletenotificationHaloRunV1alpha1NotificationTemplateRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1NotificationTemplateApi + */ + public deletenotificationHaloRunV1alpha1NotificationTemplate( + requestParameters: NotificationHaloRunV1alpha1NotificationTemplateApiDeletenotificationHaloRunV1alpha1NotificationTemplateRequest, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1NotificationTemplateApiFp( + this.configuration + ) + .deletenotificationHaloRunV1alpha1NotificationTemplate( + requestParameters.name, + options + ) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Get notification.halo.run/v1alpha1/NotificationTemplate + * @param {NotificationHaloRunV1alpha1NotificationTemplateApiGetnotificationHaloRunV1alpha1NotificationTemplateRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1NotificationTemplateApi + */ + public getnotificationHaloRunV1alpha1NotificationTemplate( + requestParameters: NotificationHaloRunV1alpha1NotificationTemplateApiGetnotificationHaloRunV1alpha1NotificationTemplateRequest, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1NotificationTemplateApiFp( + this.configuration + ) + .getnotificationHaloRunV1alpha1NotificationTemplate( + requestParameters.name, + options + ) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * List notification.halo.run/v1alpha1/NotificationTemplate + * @param {NotificationHaloRunV1alpha1NotificationTemplateApiListnotificationHaloRunV1alpha1NotificationTemplateRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1NotificationTemplateApi + */ + public listnotificationHaloRunV1alpha1NotificationTemplate( + requestParameters: NotificationHaloRunV1alpha1NotificationTemplateApiListnotificationHaloRunV1alpha1NotificationTemplateRequest = {}, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1NotificationTemplateApiFp( + this.configuration + ) + .listnotificationHaloRunV1alpha1NotificationTemplate( + requestParameters.fieldSelector, + requestParameters.labelSelector, + requestParameters.page, + requestParameters.size, + requestParameters.sort, + options + ) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Update notification.halo.run/v1alpha1/NotificationTemplate + * @param {NotificationHaloRunV1alpha1NotificationTemplateApiUpdatenotificationHaloRunV1alpha1NotificationTemplateRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1NotificationTemplateApi + */ + public updatenotificationHaloRunV1alpha1NotificationTemplate( + requestParameters: NotificationHaloRunV1alpha1NotificationTemplateApiUpdatenotificationHaloRunV1alpha1NotificationTemplateRequest, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1NotificationTemplateApiFp( + this.configuration + ) + .updatenotificationHaloRunV1alpha1NotificationTemplate( + requestParameters.name, + requestParameters.notificationTemplate, + options + ) + .then((request) => request(this.axios, this.basePath)); + } +} diff --git a/console/packages/api-client/src/api/notification-halo-run-v1alpha1-notifier-descriptor-api.ts b/console/packages/api-client/src/api/notification-halo-run-v1alpha1-notifier-descriptor-api.ts new file mode 100644 index 0000000000..247baa8c9c --- /dev/null +++ b/console/packages/api-client/src/api/notification-halo-run-v1alpha1-notifier-descriptor-api.ts @@ -0,0 +1,857 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import type { Configuration } from "../configuration"; +import type { AxiosPromise, AxiosInstance, AxiosRequestConfig } from "axios"; +import globalAxios from "axios"; +// Some imports not used depending on template conditions +// @ts-ignore +import { + DUMMY_BASE_URL, + assertParamExists, + setApiKeyToObject, + setBasicAuthToObject, + setBearerAuthToObject, + setOAuthToObject, + setSearchParams, + serializeDataIfNeeded, + toPathString, + createRequestFunction, +} from "../common"; +// @ts-ignore +import { + BASE_PATH, + COLLECTION_FORMATS, + RequestArgs, + BaseAPI, + RequiredError, +} from "../base"; +// @ts-ignore +import { NotifierDescriptor } from "../models"; +// @ts-ignore +import { NotifierDescriptorList } from "../models"; +/** + * NotificationHaloRunV1alpha1NotifierDescriptorApi - axios parameter creator + * @export + */ +export const NotificationHaloRunV1alpha1NotifierDescriptorApiAxiosParamCreator = + function (configuration?: Configuration) { + return { + /** + * Create notification.halo.run/v1alpha1/NotifierDescriptor + * @param {NotifierDescriptor} [notifierDescriptor] Fresh notifierDescriptor + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + createnotificationHaloRunV1alpha1NotifierDescriptor: async ( + notifierDescriptor?: NotifierDescriptor, + options: AxiosRequestConfig = {} + ): Promise => { + const localVarPath = `/apis/notification.halo.run/v1alpha1/notifierDescriptors`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "POST", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + localVarHeaderParameter["Content-Type"] = "application/json"; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + localVarRequestOptions.data = serializeDataIfNeeded( + notifierDescriptor, + localVarRequestOptions, + configuration + ); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Delete notification.halo.run/v1alpha1/NotifierDescriptor + * @param {string} name Name of notifierDescriptor + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deletenotificationHaloRunV1alpha1NotifierDescriptor: async ( + name: string, + options: AxiosRequestConfig = {} + ): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists( + "deletenotificationHaloRunV1alpha1NotifierDescriptor", + "name", + name + ); + const localVarPath = + `/apis/notification.halo.run/v1alpha1/notifierDescriptors/{name}`.replace( + `{${"name"}}`, + encodeURIComponent(String(name)) + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "DELETE", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Get notification.halo.run/v1alpha1/NotifierDescriptor + * @param {string} name Name of notifierDescriptor + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getnotificationHaloRunV1alpha1NotifierDescriptor: async ( + name: string, + options: AxiosRequestConfig = {} + ): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists( + "getnotificationHaloRunV1alpha1NotifierDescriptor", + "name", + name + ); + const localVarPath = + `/apis/notification.halo.run/v1alpha1/notifierDescriptors/{name}`.replace( + `{${"name"}}`, + encodeURIComponent(String(name)) + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * List notification.halo.run/v1alpha1/NotifierDescriptor + * @param {Array} [fieldSelector] Field selector for filtering. + * @param {Array} [labelSelector] Label selector for filtering. + * @param {number} [page] The page number. Zero indicates no page. + * @param {number} [size] Size of one page. Zero indicates no limit. + * @param {Array} [sort] Sort property and direction of the list result. Support sorting based on attribute name path. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listnotificationHaloRunV1alpha1NotifierDescriptor: async ( + fieldSelector?: Array, + labelSelector?: Array, + page?: number, + size?: number, + sort?: Array, + options: AxiosRequestConfig = {} + ): Promise => { + const localVarPath = `/apis/notification.halo.run/v1alpha1/notifierDescriptors`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + if (fieldSelector) { + localVarQueryParameter["fieldSelector"] = fieldSelector; + } + + if (labelSelector) { + localVarQueryParameter["labelSelector"] = labelSelector; + } + + if (page !== undefined) { + localVarQueryParameter["page"] = page; + } + + if (size !== undefined) { + localVarQueryParameter["size"] = size; + } + + if (sort) { + localVarQueryParameter["sort"] = Array.from(sort); + } + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Update notification.halo.run/v1alpha1/NotifierDescriptor + * @param {string} name Name of notifierDescriptor + * @param {NotifierDescriptor} [notifierDescriptor] Updated notifierDescriptor + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + updatenotificationHaloRunV1alpha1NotifierDescriptor: async ( + name: string, + notifierDescriptor?: NotifierDescriptor, + options: AxiosRequestConfig = {} + ): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists( + "updatenotificationHaloRunV1alpha1NotifierDescriptor", + "name", + name + ); + const localVarPath = + `/apis/notification.halo.run/v1alpha1/notifierDescriptors/{name}`.replace( + `{${"name"}}`, + encodeURIComponent(String(name)) + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "PUT", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + localVarHeaderParameter["Content-Type"] = "application/json"; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + localVarRequestOptions.data = serializeDataIfNeeded( + notifierDescriptor, + localVarRequestOptions, + configuration + ); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + }; + }; + +/** + * NotificationHaloRunV1alpha1NotifierDescriptorApi - functional programming interface + * @export + */ +export const NotificationHaloRunV1alpha1NotifierDescriptorApiFp = function ( + configuration?: Configuration +) { + const localVarAxiosParamCreator = + NotificationHaloRunV1alpha1NotifierDescriptorApiAxiosParamCreator( + configuration + ); + return { + /** + * Create notification.halo.run/v1alpha1/NotifierDescriptor + * @param {NotifierDescriptor} [notifierDescriptor] Fresh notifierDescriptor + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async createnotificationHaloRunV1alpha1NotifierDescriptor( + notifierDescriptor?: NotifierDescriptor, + options?: AxiosRequestConfig + ): Promise< + ( + axios?: AxiosInstance, + basePath?: string + ) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.createnotificationHaloRunV1alpha1NotifierDescriptor( + notifierDescriptor, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * Delete notification.halo.run/v1alpha1/NotifierDescriptor + * @param {string} name Name of notifierDescriptor + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async deletenotificationHaloRunV1alpha1NotifierDescriptor( + name: string, + options?: AxiosRequestConfig + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.deletenotificationHaloRunV1alpha1NotifierDescriptor( + name, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * Get notification.halo.run/v1alpha1/NotifierDescriptor + * @param {string} name Name of notifierDescriptor + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getnotificationHaloRunV1alpha1NotifierDescriptor( + name: string, + options?: AxiosRequestConfig + ): Promise< + ( + axios?: AxiosInstance, + basePath?: string + ) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.getnotificationHaloRunV1alpha1NotifierDescriptor( + name, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * List notification.halo.run/v1alpha1/NotifierDescriptor + * @param {Array} [fieldSelector] Field selector for filtering. + * @param {Array} [labelSelector] Label selector for filtering. + * @param {number} [page] The page number. Zero indicates no page. + * @param {number} [size] Size of one page. Zero indicates no limit. + * @param {Array} [sort] Sort property and direction of the list result. Support sorting based on attribute name path. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async listnotificationHaloRunV1alpha1NotifierDescriptor( + fieldSelector?: Array, + labelSelector?: Array, + page?: number, + size?: number, + sort?: Array, + options?: AxiosRequestConfig + ): Promise< + ( + axios?: AxiosInstance, + basePath?: string + ) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.listnotificationHaloRunV1alpha1NotifierDescriptor( + fieldSelector, + labelSelector, + page, + size, + sort, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * Update notification.halo.run/v1alpha1/NotifierDescriptor + * @param {string} name Name of notifierDescriptor + * @param {NotifierDescriptor} [notifierDescriptor] Updated notifierDescriptor + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async updatenotificationHaloRunV1alpha1NotifierDescriptor( + name: string, + notifierDescriptor?: NotifierDescriptor, + options?: AxiosRequestConfig + ): Promise< + ( + axios?: AxiosInstance, + basePath?: string + ) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.updatenotificationHaloRunV1alpha1NotifierDescriptor( + name, + notifierDescriptor, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + }; +}; + +/** + * NotificationHaloRunV1alpha1NotifierDescriptorApi - factory interface + * @export + */ +export const NotificationHaloRunV1alpha1NotifierDescriptorApiFactory = + function ( + configuration?: Configuration, + basePath?: string, + axios?: AxiosInstance + ) { + const localVarFp = + NotificationHaloRunV1alpha1NotifierDescriptorApiFp(configuration); + return { + /** + * Create notification.halo.run/v1alpha1/NotifierDescriptor + * @param {NotificationHaloRunV1alpha1NotifierDescriptorApiCreatenotificationHaloRunV1alpha1NotifierDescriptorRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + createnotificationHaloRunV1alpha1NotifierDescriptor( + requestParameters: NotificationHaloRunV1alpha1NotifierDescriptorApiCreatenotificationHaloRunV1alpha1NotifierDescriptorRequest = {}, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .createnotificationHaloRunV1alpha1NotifierDescriptor( + requestParameters.notifierDescriptor, + options + ) + .then((request) => request(axios, basePath)); + }, + /** + * Delete notification.halo.run/v1alpha1/NotifierDescriptor + * @param {NotificationHaloRunV1alpha1NotifierDescriptorApiDeletenotificationHaloRunV1alpha1NotifierDescriptorRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deletenotificationHaloRunV1alpha1NotifierDescriptor( + requestParameters: NotificationHaloRunV1alpha1NotifierDescriptorApiDeletenotificationHaloRunV1alpha1NotifierDescriptorRequest, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .deletenotificationHaloRunV1alpha1NotifierDescriptor( + requestParameters.name, + options + ) + .then((request) => request(axios, basePath)); + }, + /** + * Get notification.halo.run/v1alpha1/NotifierDescriptor + * @param {NotificationHaloRunV1alpha1NotifierDescriptorApiGetnotificationHaloRunV1alpha1NotifierDescriptorRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getnotificationHaloRunV1alpha1NotifierDescriptor( + requestParameters: NotificationHaloRunV1alpha1NotifierDescriptorApiGetnotificationHaloRunV1alpha1NotifierDescriptorRequest, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .getnotificationHaloRunV1alpha1NotifierDescriptor( + requestParameters.name, + options + ) + .then((request) => request(axios, basePath)); + }, + /** + * List notification.halo.run/v1alpha1/NotifierDescriptor + * @param {NotificationHaloRunV1alpha1NotifierDescriptorApiListnotificationHaloRunV1alpha1NotifierDescriptorRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listnotificationHaloRunV1alpha1NotifierDescriptor( + requestParameters: NotificationHaloRunV1alpha1NotifierDescriptorApiListnotificationHaloRunV1alpha1NotifierDescriptorRequest = {}, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .listnotificationHaloRunV1alpha1NotifierDescriptor( + requestParameters.fieldSelector, + requestParameters.labelSelector, + requestParameters.page, + requestParameters.size, + requestParameters.sort, + options + ) + .then((request) => request(axios, basePath)); + }, + /** + * Update notification.halo.run/v1alpha1/NotifierDescriptor + * @param {NotificationHaloRunV1alpha1NotifierDescriptorApiUpdatenotificationHaloRunV1alpha1NotifierDescriptorRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + updatenotificationHaloRunV1alpha1NotifierDescriptor( + requestParameters: NotificationHaloRunV1alpha1NotifierDescriptorApiUpdatenotificationHaloRunV1alpha1NotifierDescriptorRequest, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .updatenotificationHaloRunV1alpha1NotifierDescriptor( + requestParameters.name, + requestParameters.notifierDescriptor, + options + ) + .then((request) => request(axios, basePath)); + }, + }; + }; + +/** + * Request parameters for createnotificationHaloRunV1alpha1NotifierDescriptor operation in NotificationHaloRunV1alpha1NotifierDescriptorApi. + * @export + * @interface NotificationHaloRunV1alpha1NotifierDescriptorApiCreatenotificationHaloRunV1alpha1NotifierDescriptorRequest + */ +export interface NotificationHaloRunV1alpha1NotifierDescriptorApiCreatenotificationHaloRunV1alpha1NotifierDescriptorRequest { + /** + * Fresh notifierDescriptor + * @type {NotifierDescriptor} + * @memberof NotificationHaloRunV1alpha1NotifierDescriptorApiCreatenotificationHaloRunV1alpha1NotifierDescriptor + */ + readonly notifierDescriptor?: NotifierDescriptor; +} + +/** + * Request parameters for deletenotificationHaloRunV1alpha1NotifierDescriptor operation in NotificationHaloRunV1alpha1NotifierDescriptorApi. + * @export + * @interface NotificationHaloRunV1alpha1NotifierDescriptorApiDeletenotificationHaloRunV1alpha1NotifierDescriptorRequest + */ +export interface NotificationHaloRunV1alpha1NotifierDescriptorApiDeletenotificationHaloRunV1alpha1NotifierDescriptorRequest { + /** + * Name of notifierDescriptor + * @type {string} + * @memberof NotificationHaloRunV1alpha1NotifierDescriptorApiDeletenotificationHaloRunV1alpha1NotifierDescriptor + */ + readonly name: string; +} + +/** + * Request parameters for getnotificationHaloRunV1alpha1NotifierDescriptor operation in NotificationHaloRunV1alpha1NotifierDescriptorApi. + * @export + * @interface NotificationHaloRunV1alpha1NotifierDescriptorApiGetnotificationHaloRunV1alpha1NotifierDescriptorRequest + */ +export interface NotificationHaloRunV1alpha1NotifierDescriptorApiGetnotificationHaloRunV1alpha1NotifierDescriptorRequest { + /** + * Name of notifierDescriptor + * @type {string} + * @memberof NotificationHaloRunV1alpha1NotifierDescriptorApiGetnotificationHaloRunV1alpha1NotifierDescriptor + */ + readonly name: string; +} + +/** + * Request parameters for listnotificationHaloRunV1alpha1NotifierDescriptor operation in NotificationHaloRunV1alpha1NotifierDescriptorApi. + * @export + * @interface NotificationHaloRunV1alpha1NotifierDescriptorApiListnotificationHaloRunV1alpha1NotifierDescriptorRequest + */ +export interface NotificationHaloRunV1alpha1NotifierDescriptorApiListnotificationHaloRunV1alpha1NotifierDescriptorRequest { + /** + * Field selector for filtering. + * @type {Array} + * @memberof NotificationHaloRunV1alpha1NotifierDescriptorApiListnotificationHaloRunV1alpha1NotifierDescriptor + */ + readonly fieldSelector?: Array; + + /** + * Label selector for filtering. + * @type {Array} + * @memberof NotificationHaloRunV1alpha1NotifierDescriptorApiListnotificationHaloRunV1alpha1NotifierDescriptor + */ + readonly labelSelector?: Array; + + /** + * The page number. Zero indicates no page. + * @type {number} + * @memberof NotificationHaloRunV1alpha1NotifierDescriptorApiListnotificationHaloRunV1alpha1NotifierDescriptor + */ + readonly page?: number; + + /** + * Size of one page. Zero indicates no limit. + * @type {number} + * @memberof NotificationHaloRunV1alpha1NotifierDescriptorApiListnotificationHaloRunV1alpha1NotifierDescriptor + */ + readonly size?: number; + + /** + * Sort property and direction of the list result. Support sorting based on attribute name path. + * @type {Array} + * @memberof NotificationHaloRunV1alpha1NotifierDescriptorApiListnotificationHaloRunV1alpha1NotifierDescriptor + */ + readonly sort?: Array; +} + +/** + * Request parameters for updatenotificationHaloRunV1alpha1NotifierDescriptor operation in NotificationHaloRunV1alpha1NotifierDescriptorApi. + * @export + * @interface NotificationHaloRunV1alpha1NotifierDescriptorApiUpdatenotificationHaloRunV1alpha1NotifierDescriptorRequest + */ +export interface NotificationHaloRunV1alpha1NotifierDescriptorApiUpdatenotificationHaloRunV1alpha1NotifierDescriptorRequest { + /** + * Name of notifierDescriptor + * @type {string} + * @memberof NotificationHaloRunV1alpha1NotifierDescriptorApiUpdatenotificationHaloRunV1alpha1NotifierDescriptor + */ + readonly name: string; + + /** + * Updated notifierDescriptor + * @type {NotifierDescriptor} + * @memberof NotificationHaloRunV1alpha1NotifierDescriptorApiUpdatenotificationHaloRunV1alpha1NotifierDescriptor + */ + readonly notifierDescriptor?: NotifierDescriptor; +} + +/** + * NotificationHaloRunV1alpha1NotifierDescriptorApi - object-oriented interface + * @export + * @class NotificationHaloRunV1alpha1NotifierDescriptorApi + * @extends {BaseAPI} + */ +export class NotificationHaloRunV1alpha1NotifierDescriptorApi extends BaseAPI { + /** + * Create notification.halo.run/v1alpha1/NotifierDescriptor + * @param {NotificationHaloRunV1alpha1NotifierDescriptorApiCreatenotificationHaloRunV1alpha1NotifierDescriptorRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1NotifierDescriptorApi + */ + public createnotificationHaloRunV1alpha1NotifierDescriptor( + requestParameters: NotificationHaloRunV1alpha1NotifierDescriptorApiCreatenotificationHaloRunV1alpha1NotifierDescriptorRequest = {}, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1NotifierDescriptorApiFp( + this.configuration + ) + .createnotificationHaloRunV1alpha1NotifierDescriptor( + requestParameters.notifierDescriptor, + options + ) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Delete notification.halo.run/v1alpha1/NotifierDescriptor + * @param {NotificationHaloRunV1alpha1NotifierDescriptorApiDeletenotificationHaloRunV1alpha1NotifierDescriptorRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1NotifierDescriptorApi + */ + public deletenotificationHaloRunV1alpha1NotifierDescriptor( + requestParameters: NotificationHaloRunV1alpha1NotifierDescriptorApiDeletenotificationHaloRunV1alpha1NotifierDescriptorRequest, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1NotifierDescriptorApiFp( + this.configuration + ) + .deletenotificationHaloRunV1alpha1NotifierDescriptor( + requestParameters.name, + options + ) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Get notification.halo.run/v1alpha1/NotifierDescriptor + * @param {NotificationHaloRunV1alpha1NotifierDescriptorApiGetnotificationHaloRunV1alpha1NotifierDescriptorRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1NotifierDescriptorApi + */ + public getnotificationHaloRunV1alpha1NotifierDescriptor( + requestParameters: NotificationHaloRunV1alpha1NotifierDescriptorApiGetnotificationHaloRunV1alpha1NotifierDescriptorRequest, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1NotifierDescriptorApiFp( + this.configuration + ) + .getnotificationHaloRunV1alpha1NotifierDescriptor( + requestParameters.name, + options + ) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * List notification.halo.run/v1alpha1/NotifierDescriptor + * @param {NotificationHaloRunV1alpha1NotifierDescriptorApiListnotificationHaloRunV1alpha1NotifierDescriptorRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1NotifierDescriptorApi + */ + public listnotificationHaloRunV1alpha1NotifierDescriptor( + requestParameters: NotificationHaloRunV1alpha1NotifierDescriptorApiListnotificationHaloRunV1alpha1NotifierDescriptorRequest = {}, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1NotifierDescriptorApiFp( + this.configuration + ) + .listnotificationHaloRunV1alpha1NotifierDescriptor( + requestParameters.fieldSelector, + requestParameters.labelSelector, + requestParameters.page, + requestParameters.size, + requestParameters.sort, + options + ) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Update notification.halo.run/v1alpha1/NotifierDescriptor + * @param {NotificationHaloRunV1alpha1NotifierDescriptorApiUpdatenotificationHaloRunV1alpha1NotifierDescriptorRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1NotifierDescriptorApi + */ + public updatenotificationHaloRunV1alpha1NotifierDescriptor( + requestParameters: NotificationHaloRunV1alpha1NotifierDescriptorApiUpdatenotificationHaloRunV1alpha1NotifierDescriptorRequest, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1NotifierDescriptorApiFp( + this.configuration + ) + .updatenotificationHaloRunV1alpha1NotifierDescriptor( + requestParameters.name, + requestParameters.notifierDescriptor, + options + ) + .then((request) => request(this.axios, this.basePath)); + } +} diff --git a/console/packages/api-client/src/api/notification-halo-run-v1alpha1-reason-api.ts b/console/packages/api-client/src/api/notification-halo-run-v1alpha1-reason-api.ts new file mode 100644 index 0000000000..67bd4b2025 --- /dev/null +++ b/console/packages/api-client/src/api/notification-halo-run-v1alpha1-reason-api.ts @@ -0,0 +1,819 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import type { Configuration } from "../configuration"; +import type { AxiosPromise, AxiosInstance, AxiosRequestConfig } from "axios"; +import globalAxios from "axios"; +// Some imports not used depending on template conditions +// @ts-ignore +import { + DUMMY_BASE_URL, + assertParamExists, + setApiKeyToObject, + setBasicAuthToObject, + setBearerAuthToObject, + setOAuthToObject, + setSearchParams, + serializeDataIfNeeded, + toPathString, + createRequestFunction, +} from "../common"; +// @ts-ignore +import { + BASE_PATH, + COLLECTION_FORMATS, + RequestArgs, + BaseAPI, + RequiredError, +} from "../base"; +// @ts-ignore +import { Reason } from "../models"; +// @ts-ignore +import { ReasonList } from "../models"; +/** + * NotificationHaloRunV1alpha1ReasonApi - axios parameter creator + * @export + */ +export const NotificationHaloRunV1alpha1ReasonApiAxiosParamCreator = function ( + configuration?: Configuration +) { + return { + /** + * Create notification.halo.run/v1alpha1/Reason + * @param {Reason} [reason] Fresh reason + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + createnotificationHaloRunV1alpha1Reason: async ( + reason?: Reason, + options: AxiosRequestConfig = {} + ): Promise => { + const localVarPath = `/apis/notification.halo.run/v1alpha1/reasons`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "POST", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + localVarHeaderParameter["Content-Type"] = "application/json"; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + localVarRequestOptions.data = serializeDataIfNeeded( + reason, + localVarRequestOptions, + configuration + ); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Delete notification.halo.run/v1alpha1/Reason + * @param {string} name Name of reason + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deletenotificationHaloRunV1alpha1Reason: async ( + name: string, + options: AxiosRequestConfig = {} + ): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists( + "deletenotificationHaloRunV1alpha1Reason", + "name", + name + ); + const localVarPath = + `/apis/notification.halo.run/v1alpha1/reasons/{name}`.replace( + `{${"name"}}`, + encodeURIComponent(String(name)) + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "DELETE", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Get notification.halo.run/v1alpha1/Reason + * @param {string} name Name of reason + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getnotificationHaloRunV1alpha1Reason: async ( + name: string, + options: AxiosRequestConfig = {} + ): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists("getnotificationHaloRunV1alpha1Reason", "name", name); + const localVarPath = + `/apis/notification.halo.run/v1alpha1/reasons/{name}`.replace( + `{${"name"}}`, + encodeURIComponent(String(name)) + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * List notification.halo.run/v1alpha1/Reason + * @param {Array} [fieldSelector] Field selector for filtering. + * @param {Array} [labelSelector] Label selector for filtering. + * @param {number} [page] The page number. Zero indicates no page. + * @param {number} [size] Size of one page. Zero indicates no limit. + * @param {Array} [sort] Sort property and direction of the list result. Support sorting based on attribute name path. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listnotificationHaloRunV1alpha1Reason: async ( + fieldSelector?: Array, + labelSelector?: Array, + page?: number, + size?: number, + sort?: Array, + options: AxiosRequestConfig = {} + ): Promise => { + const localVarPath = `/apis/notification.halo.run/v1alpha1/reasons`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + if (fieldSelector) { + localVarQueryParameter["fieldSelector"] = fieldSelector; + } + + if (labelSelector) { + localVarQueryParameter["labelSelector"] = labelSelector; + } + + if (page !== undefined) { + localVarQueryParameter["page"] = page; + } + + if (size !== undefined) { + localVarQueryParameter["size"] = size; + } + + if (sort) { + localVarQueryParameter["sort"] = Array.from(sort); + } + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Update notification.halo.run/v1alpha1/Reason + * @param {string} name Name of reason + * @param {Reason} [reason] Updated reason + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + updatenotificationHaloRunV1alpha1Reason: async ( + name: string, + reason?: Reason, + options: AxiosRequestConfig = {} + ): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists( + "updatenotificationHaloRunV1alpha1Reason", + "name", + name + ); + const localVarPath = + `/apis/notification.halo.run/v1alpha1/reasons/{name}`.replace( + `{${"name"}}`, + encodeURIComponent(String(name)) + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "PUT", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + localVarHeaderParameter["Content-Type"] = "application/json"; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + localVarRequestOptions.data = serializeDataIfNeeded( + reason, + localVarRequestOptions, + configuration + ); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + }; +}; + +/** + * NotificationHaloRunV1alpha1ReasonApi - functional programming interface + * @export + */ +export const NotificationHaloRunV1alpha1ReasonApiFp = function ( + configuration?: Configuration +) { + const localVarAxiosParamCreator = + NotificationHaloRunV1alpha1ReasonApiAxiosParamCreator(configuration); + return { + /** + * Create notification.halo.run/v1alpha1/Reason + * @param {Reason} [reason] Fresh reason + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async createnotificationHaloRunV1alpha1Reason( + reason?: Reason, + options?: AxiosRequestConfig + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.createnotificationHaloRunV1alpha1Reason( + reason, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * Delete notification.halo.run/v1alpha1/Reason + * @param {string} name Name of reason + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async deletenotificationHaloRunV1alpha1Reason( + name: string, + options?: AxiosRequestConfig + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.deletenotificationHaloRunV1alpha1Reason( + name, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * Get notification.halo.run/v1alpha1/Reason + * @param {string} name Name of reason + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getnotificationHaloRunV1alpha1Reason( + name: string, + options?: AxiosRequestConfig + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.getnotificationHaloRunV1alpha1Reason( + name, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * List notification.halo.run/v1alpha1/Reason + * @param {Array} [fieldSelector] Field selector for filtering. + * @param {Array} [labelSelector] Label selector for filtering. + * @param {number} [page] The page number. Zero indicates no page. + * @param {number} [size] Size of one page. Zero indicates no limit. + * @param {Array} [sort] Sort property and direction of the list result. Support sorting based on attribute name path. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async listnotificationHaloRunV1alpha1Reason( + fieldSelector?: Array, + labelSelector?: Array, + page?: number, + size?: number, + sort?: Array, + options?: AxiosRequestConfig + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.listnotificationHaloRunV1alpha1Reason( + fieldSelector, + labelSelector, + page, + size, + sort, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * Update notification.halo.run/v1alpha1/Reason + * @param {string} name Name of reason + * @param {Reason} [reason] Updated reason + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async updatenotificationHaloRunV1alpha1Reason( + name: string, + reason?: Reason, + options?: AxiosRequestConfig + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.updatenotificationHaloRunV1alpha1Reason( + name, + reason, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + }; +}; + +/** + * NotificationHaloRunV1alpha1ReasonApi - factory interface + * @export + */ +export const NotificationHaloRunV1alpha1ReasonApiFactory = function ( + configuration?: Configuration, + basePath?: string, + axios?: AxiosInstance +) { + const localVarFp = NotificationHaloRunV1alpha1ReasonApiFp(configuration); + return { + /** + * Create notification.halo.run/v1alpha1/Reason + * @param {NotificationHaloRunV1alpha1ReasonApiCreatenotificationHaloRunV1alpha1ReasonRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + createnotificationHaloRunV1alpha1Reason( + requestParameters: NotificationHaloRunV1alpha1ReasonApiCreatenotificationHaloRunV1alpha1ReasonRequest = {}, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .createnotificationHaloRunV1alpha1Reason( + requestParameters.reason, + options + ) + .then((request) => request(axios, basePath)); + }, + /** + * Delete notification.halo.run/v1alpha1/Reason + * @param {NotificationHaloRunV1alpha1ReasonApiDeletenotificationHaloRunV1alpha1ReasonRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deletenotificationHaloRunV1alpha1Reason( + requestParameters: NotificationHaloRunV1alpha1ReasonApiDeletenotificationHaloRunV1alpha1ReasonRequest, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .deletenotificationHaloRunV1alpha1Reason( + requestParameters.name, + options + ) + .then((request) => request(axios, basePath)); + }, + /** + * Get notification.halo.run/v1alpha1/Reason + * @param {NotificationHaloRunV1alpha1ReasonApiGetnotificationHaloRunV1alpha1ReasonRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getnotificationHaloRunV1alpha1Reason( + requestParameters: NotificationHaloRunV1alpha1ReasonApiGetnotificationHaloRunV1alpha1ReasonRequest, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .getnotificationHaloRunV1alpha1Reason(requestParameters.name, options) + .then((request) => request(axios, basePath)); + }, + /** + * List notification.halo.run/v1alpha1/Reason + * @param {NotificationHaloRunV1alpha1ReasonApiListnotificationHaloRunV1alpha1ReasonRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listnotificationHaloRunV1alpha1Reason( + requestParameters: NotificationHaloRunV1alpha1ReasonApiListnotificationHaloRunV1alpha1ReasonRequest = {}, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .listnotificationHaloRunV1alpha1Reason( + requestParameters.fieldSelector, + requestParameters.labelSelector, + requestParameters.page, + requestParameters.size, + requestParameters.sort, + options + ) + .then((request) => request(axios, basePath)); + }, + /** + * Update notification.halo.run/v1alpha1/Reason + * @param {NotificationHaloRunV1alpha1ReasonApiUpdatenotificationHaloRunV1alpha1ReasonRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + updatenotificationHaloRunV1alpha1Reason( + requestParameters: NotificationHaloRunV1alpha1ReasonApiUpdatenotificationHaloRunV1alpha1ReasonRequest, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .updatenotificationHaloRunV1alpha1Reason( + requestParameters.name, + requestParameters.reason, + options + ) + .then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * Request parameters for createnotificationHaloRunV1alpha1Reason operation in NotificationHaloRunV1alpha1ReasonApi. + * @export + * @interface NotificationHaloRunV1alpha1ReasonApiCreatenotificationHaloRunV1alpha1ReasonRequest + */ +export interface NotificationHaloRunV1alpha1ReasonApiCreatenotificationHaloRunV1alpha1ReasonRequest { + /** + * Fresh reason + * @type {Reason} + * @memberof NotificationHaloRunV1alpha1ReasonApiCreatenotificationHaloRunV1alpha1Reason + */ + readonly reason?: Reason; +} + +/** + * Request parameters for deletenotificationHaloRunV1alpha1Reason operation in NotificationHaloRunV1alpha1ReasonApi. + * @export + * @interface NotificationHaloRunV1alpha1ReasonApiDeletenotificationHaloRunV1alpha1ReasonRequest + */ +export interface NotificationHaloRunV1alpha1ReasonApiDeletenotificationHaloRunV1alpha1ReasonRequest { + /** + * Name of reason + * @type {string} + * @memberof NotificationHaloRunV1alpha1ReasonApiDeletenotificationHaloRunV1alpha1Reason + */ + readonly name: string; +} + +/** + * Request parameters for getnotificationHaloRunV1alpha1Reason operation in NotificationHaloRunV1alpha1ReasonApi. + * @export + * @interface NotificationHaloRunV1alpha1ReasonApiGetnotificationHaloRunV1alpha1ReasonRequest + */ +export interface NotificationHaloRunV1alpha1ReasonApiGetnotificationHaloRunV1alpha1ReasonRequest { + /** + * Name of reason + * @type {string} + * @memberof NotificationHaloRunV1alpha1ReasonApiGetnotificationHaloRunV1alpha1Reason + */ + readonly name: string; +} + +/** + * Request parameters for listnotificationHaloRunV1alpha1Reason operation in NotificationHaloRunV1alpha1ReasonApi. + * @export + * @interface NotificationHaloRunV1alpha1ReasonApiListnotificationHaloRunV1alpha1ReasonRequest + */ +export interface NotificationHaloRunV1alpha1ReasonApiListnotificationHaloRunV1alpha1ReasonRequest { + /** + * Field selector for filtering. + * @type {Array} + * @memberof NotificationHaloRunV1alpha1ReasonApiListnotificationHaloRunV1alpha1Reason + */ + readonly fieldSelector?: Array; + + /** + * Label selector for filtering. + * @type {Array} + * @memberof NotificationHaloRunV1alpha1ReasonApiListnotificationHaloRunV1alpha1Reason + */ + readonly labelSelector?: Array; + + /** + * The page number. Zero indicates no page. + * @type {number} + * @memberof NotificationHaloRunV1alpha1ReasonApiListnotificationHaloRunV1alpha1Reason + */ + readonly page?: number; + + /** + * Size of one page. Zero indicates no limit. + * @type {number} + * @memberof NotificationHaloRunV1alpha1ReasonApiListnotificationHaloRunV1alpha1Reason + */ + readonly size?: number; + + /** + * Sort property and direction of the list result. Support sorting based on attribute name path. + * @type {Array} + * @memberof NotificationHaloRunV1alpha1ReasonApiListnotificationHaloRunV1alpha1Reason + */ + readonly sort?: Array; +} + +/** + * Request parameters for updatenotificationHaloRunV1alpha1Reason operation in NotificationHaloRunV1alpha1ReasonApi. + * @export + * @interface NotificationHaloRunV1alpha1ReasonApiUpdatenotificationHaloRunV1alpha1ReasonRequest + */ +export interface NotificationHaloRunV1alpha1ReasonApiUpdatenotificationHaloRunV1alpha1ReasonRequest { + /** + * Name of reason + * @type {string} + * @memberof NotificationHaloRunV1alpha1ReasonApiUpdatenotificationHaloRunV1alpha1Reason + */ + readonly name: string; + + /** + * Updated reason + * @type {Reason} + * @memberof NotificationHaloRunV1alpha1ReasonApiUpdatenotificationHaloRunV1alpha1Reason + */ + readonly reason?: Reason; +} + +/** + * NotificationHaloRunV1alpha1ReasonApi - object-oriented interface + * @export + * @class NotificationHaloRunV1alpha1ReasonApi + * @extends {BaseAPI} + */ +export class NotificationHaloRunV1alpha1ReasonApi extends BaseAPI { + /** + * Create notification.halo.run/v1alpha1/Reason + * @param {NotificationHaloRunV1alpha1ReasonApiCreatenotificationHaloRunV1alpha1ReasonRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1ReasonApi + */ + public createnotificationHaloRunV1alpha1Reason( + requestParameters: NotificationHaloRunV1alpha1ReasonApiCreatenotificationHaloRunV1alpha1ReasonRequest = {}, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1ReasonApiFp(this.configuration) + .createnotificationHaloRunV1alpha1Reason( + requestParameters.reason, + options + ) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Delete notification.halo.run/v1alpha1/Reason + * @param {NotificationHaloRunV1alpha1ReasonApiDeletenotificationHaloRunV1alpha1ReasonRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1ReasonApi + */ + public deletenotificationHaloRunV1alpha1Reason( + requestParameters: NotificationHaloRunV1alpha1ReasonApiDeletenotificationHaloRunV1alpha1ReasonRequest, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1ReasonApiFp(this.configuration) + .deletenotificationHaloRunV1alpha1Reason(requestParameters.name, options) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Get notification.halo.run/v1alpha1/Reason + * @param {NotificationHaloRunV1alpha1ReasonApiGetnotificationHaloRunV1alpha1ReasonRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1ReasonApi + */ + public getnotificationHaloRunV1alpha1Reason( + requestParameters: NotificationHaloRunV1alpha1ReasonApiGetnotificationHaloRunV1alpha1ReasonRequest, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1ReasonApiFp(this.configuration) + .getnotificationHaloRunV1alpha1Reason(requestParameters.name, options) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * List notification.halo.run/v1alpha1/Reason + * @param {NotificationHaloRunV1alpha1ReasonApiListnotificationHaloRunV1alpha1ReasonRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1ReasonApi + */ + public listnotificationHaloRunV1alpha1Reason( + requestParameters: NotificationHaloRunV1alpha1ReasonApiListnotificationHaloRunV1alpha1ReasonRequest = {}, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1ReasonApiFp(this.configuration) + .listnotificationHaloRunV1alpha1Reason( + requestParameters.fieldSelector, + requestParameters.labelSelector, + requestParameters.page, + requestParameters.size, + requestParameters.sort, + options + ) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Update notification.halo.run/v1alpha1/Reason + * @param {NotificationHaloRunV1alpha1ReasonApiUpdatenotificationHaloRunV1alpha1ReasonRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1ReasonApi + */ + public updatenotificationHaloRunV1alpha1Reason( + requestParameters: NotificationHaloRunV1alpha1ReasonApiUpdatenotificationHaloRunV1alpha1ReasonRequest, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1ReasonApiFp(this.configuration) + .updatenotificationHaloRunV1alpha1Reason( + requestParameters.name, + requestParameters.reason, + options + ) + .then((request) => request(this.axios, this.basePath)); + } +} diff --git a/console/packages/api-client/src/api/notification-halo-run-v1alpha1-reason-type-api.ts b/console/packages/api-client/src/api/notification-halo-run-v1alpha1-reason-type-api.ts new file mode 100644 index 0000000000..395fcb8dbf --- /dev/null +++ b/console/packages/api-client/src/api/notification-halo-run-v1alpha1-reason-type-api.ts @@ -0,0 +1,828 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import type { Configuration } from "../configuration"; +import type { AxiosPromise, AxiosInstance, AxiosRequestConfig } from "axios"; +import globalAxios from "axios"; +// Some imports not used depending on template conditions +// @ts-ignore +import { + DUMMY_BASE_URL, + assertParamExists, + setApiKeyToObject, + setBasicAuthToObject, + setBearerAuthToObject, + setOAuthToObject, + setSearchParams, + serializeDataIfNeeded, + toPathString, + createRequestFunction, +} from "../common"; +// @ts-ignore +import { + BASE_PATH, + COLLECTION_FORMATS, + RequestArgs, + BaseAPI, + RequiredError, +} from "../base"; +// @ts-ignore +import { ReasonType } from "../models"; +// @ts-ignore +import { ReasonTypeList } from "../models"; +/** + * NotificationHaloRunV1alpha1ReasonTypeApi - axios parameter creator + * @export + */ +export const NotificationHaloRunV1alpha1ReasonTypeApiAxiosParamCreator = + function (configuration?: Configuration) { + return { + /** + * Create notification.halo.run/v1alpha1/ReasonType + * @param {ReasonType} [reasonType] Fresh reasontype + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + createnotificationHaloRunV1alpha1ReasonType: async ( + reasonType?: ReasonType, + options: AxiosRequestConfig = {} + ): Promise => { + const localVarPath = `/apis/notification.halo.run/v1alpha1/reasontypes`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "POST", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + localVarHeaderParameter["Content-Type"] = "application/json"; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + localVarRequestOptions.data = serializeDataIfNeeded( + reasonType, + localVarRequestOptions, + configuration + ); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Delete notification.halo.run/v1alpha1/ReasonType + * @param {string} name Name of reasontype + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deletenotificationHaloRunV1alpha1ReasonType: async ( + name: string, + options: AxiosRequestConfig = {} + ): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists( + "deletenotificationHaloRunV1alpha1ReasonType", + "name", + name + ); + const localVarPath = + `/apis/notification.halo.run/v1alpha1/reasontypes/{name}`.replace( + `{${"name"}}`, + encodeURIComponent(String(name)) + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "DELETE", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Get notification.halo.run/v1alpha1/ReasonType + * @param {string} name Name of reasontype + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getnotificationHaloRunV1alpha1ReasonType: async ( + name: string, + options: AxiosRequestConfig = {} + ): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists( + "getnotificationHaloRunV1alpha1ReasonType", + "name", + name + ); + const localVarPath = + `/apis/notification.halo.run/v1alpha1/reasontypes/{name}`.replace( + `{${"name"}}`, + encodeURIComponent(String(name)) + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * List notification.halo.run/v1alpha1/ReasonType + * @param {Array} [fieldSelector] Field selector for filtering. + * @param {Array} [labelSelector] Label selector for filtering. + * @param {number} [page] The page number. Zero indicates no page. + * @param {number} [size] Size of one page. Zero indicates no limit. + * @param {Array} [sort] Sort property and direction of the list result. Support sorting based on attribute name path. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listnotificationHaloRunV1alpha1ReasonType: async ( + fieldSelector?: Array, + labelSelector?: Array, + page?: number, + size?: number, + sort?: Array, + options: AxiosRequestConfig = {} + ): Promise => { + const localVarPath = `/apis/notification.halo.run/v1alpha1/reasontypes`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + if (fieldSelector) { + localVarQueryParameter["fieldSelector"] = fieldSelector; + } + + if (labelSelector) { + localVarQueryParameter["labelSelector"] = labelSelector; + } + + if (page !== undefined) { + localVarQueryParameter["page"] = page; + } + + if (size !== undefined) { + localVarQueryParameter["size"] = size; + } + + if (sort) { + localVarQueryParameter["sort"] = Array.from(sort); + } + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Update notification.halo.run/v1alpha1/ReasonType + * @param {string} name Name of reasontype + * @param {ReasonType} [reasonType] Updated reasontype + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + updatenotificationHaloRunV1alpha1ReasonType: async ( + name: string, + reasonType?: ReasonType, + options: AxiosRequestConfig = {} + ): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists( + "updatenotificationHaloRunV1alpha1ReasonType", + "name", + name + ); + const localVarPath = + `/apis/notification.halo.run/v1alpha1/reasontypes/{name}`.replace( + `{${"name"}}`, + encodeURIComponent(String(name)) + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "PUT", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + localVarHeaderParameter["Content-Type"] = "application/json"; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + localVarRequestOptions.data = serializeDataIfNeeded( + reasonType, + localVarRequestOptions, + configuration + ); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + }; + }; + +/** + * NotificationHaloRunV1alpha1ReasonTypeApi - functional programming interface + * @export + */ +export const NotificationHaloRunV1alpha1ReasonTypeApiFp = function ( + configuration?: Configuration +) { + const localVarAxiosParamCreator = + NotificationHaloRunV1alpha1ReasonTypeApiAxiosParamCreator(configuration); + return { + /** + * Create notification.halo.run/v1alpha1/ReasonType + * @param {ReasonType} [reasonType] Fresh reasontype + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async createnotificationHaloRunV1alpha1ReasonType( + reasonType?: ReasonType, + options?: AxiosRequestConfig + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.createnotificationHaloRunV1alpha1ReasonType( + reasonType, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * Delete notification.halo.run/v1alpha1/ReasonType + * @param {string} name Name of reasontype + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async deletenotificationHaloRunV1alpha1ReasonType( + name: string, + options?: AxiosRequestConfig + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.deletenotificationHaloRunV1alpha1ReasonType( + name, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * Get notification.halo.run/v1alpha1/ReasonType + * @param {string} name Name of reasontype + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getnotificationHaloRunV1alpha1ReasonType( + name: string, + options?: AxiosRequestConfig + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.getnotificationHaloRunV1alpha1ReasonType( + name, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * List notification.halo.run/v1alpha1/ReasonType + * @param {Array} [fieldSelector] Field selector for filtering. + * @param {Array} [labelSelector] Label selector for filtering. + * @param {number} [page] The page number. Zero indicates no page. + * @param {number} [size] Size of one page. Zero indicates no limit. + * @param {Array} [sort] Sort property and direction of the list result. Support sorting based on attribute name path. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async listnotificationHaloRunV1alpha1ReasonType( + fieldSelector?: Array, + labelSelector?: Array, + page?: number, + size?: number, + sort?: Array, + options?: AxiosRequestConfig + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.listnotificationHaloRunV1alpha1ReasonType( + fieldSelector, + labelSelector, + page, + size, + sort, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * Update notification.halo.run/v1alpha1/ReasonType + * @param {string} name Name of reasontype + * @param {ReasonType} [reasonType] Updated reasontype + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async updatenotificationHaloRunV1alpha1ReasonType( + name: string, + reasonType?: ReasonType, + options?: AxiosRequestConfig + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.updatenotificationHaloRunV1alpha1ReasonType( + name, + reasonType, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + }; +}; + +/** + * NotificationHaloRunV1alpha1ReasonTypeApi - factory interface + * @export + */ +export const NotificationHaloRunV1alpha1ReasonTypeApiFactory = function ( + configuration?: Configuration, + basePath?: string, + axios?: AxiosInstance +) { + const localVarFp = NotificationHaloRunV1alpha1ReasonTypeApiFp(configuration); + return { + /** + * Create notification.halo.run/v1alpha1/ReasonType + * @param {NotificationHaloRunV1alpha1ReasonTypeApiCreatenotificationHaloRunV1alpha1ReasonTypeRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + createnotificationHaloRunV1alpha1ReasonType( + requestParameters: NotificationHaloRunV1alpha1ReasonTypeApiCreatenotificationHaloRunV1alpha1ReasonTypeRequest = {}, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .createnotificationHaloRunV1alpha1ReasonType( + requestParameters.reasonType, + options + ) + .then((request) => request(axios, basePath)); + }, + /** + * Delete notification.halo.run/v1alpha1/ReasonType + * @param {NotificationHaloRunV1alpha1ReasonTypeApiDeletenotificationHaloRunV1alpha1ReasonTypeRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deletenotificationHaloRunV1alpha1ReasonType( + requestParameters: NotificationHaloRunV1alpha1ReasonTypeApiDeletenotificationHaloRunV1alpha1ReasonTypeRequest, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .deletenotificationHaloRunV1alpha1ReasonType( + requestParameters.name, + options + ) + .then((request) => request(axios, basePath)); + }, + /** + * Get notification.halo.run/v1alpha1/ReasonType + * @param {NotificationHaloRunV1alpha1ReasonTypeApiGetnotificationHaloRunV1alpha1ReasonTypeRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getnotificationHaloRunV1alpha1ReasonType( + requestParameters: NotificationHaloRunV1alpha1ReasonTypeApiGetnotificationHaloRunV1alpha1ReasonTypeRequest, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .getnotificationHaloRunV1alpha1ReasonType( + requestParameters.name, + options + ) + .then((request) => request(axios, basePath)); + }, + /** + * List notification.halo.run/v1alpha1/ReasonType + * @param {NotificationHaloRunV1alpha1ReasonTypeApiListnotificationHaloRunV1alpha1ReasonTypeRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listnotificationHaloRunV1alpha1ReasonType( + requestParameters: NotificationHaloRunV1alpha1ReasonTypeApiListnotificationHaloRunV1alpha1ReasonTypeRequest = {}, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .listnotificationHaloRunV1alpha1ReasonType( + requestParameters.fieldSelector, + requestParameters.labelSelector, + requestParameters.page, + requestParameters.size, + requestParameters.sort, + options + ) + .then((request) => request(axios, basePath)); + }, + /** + * Update notification.halo.run/v1alpha1/ReasonType + * @param {NotificationHaloRunV1alpha1ReasonTypeApiUpdatenotificationHaloRunV1alpha1ReasonTypeRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + updatenotificationHaloRunV1alpha1ReasonType( + requestParameters: NotificationHaloRunV1alpha1ReasonTypeApiUpdatenotificationHaloRunV1alpha1ReasonTypeRequest, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .updatenotificationHaloRunV1alpha1ReasonType( + requestParameters.name, + requestParameters.reasonType, + options + ) + .then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * Request parameters for createnotificationHaloRunV1alpha1ReasonType operation in NotificationHaloRunV1alpha1ReasonTypeApi. + * @export + * @interface NotificationHaloRunV1alpha1ReasonTypeApiCreatenotificationHaloRunV1alpha1ReasonTypeRequest + */ +export interface NotificationHaloRunV1alpha1ReasonTypeApiCreatenotificationHaloRunV1alpha1ReasonTypeRequest { + /** + * Fresh reasontype + * @type {ReasonType} + * @memberof NotificationHaloRunV1alpha1ReasonTypeApiCreatenotificationHaloRunV1alpha1ReasonType + */ + readonly reasonType?: ReasonType; +} + +/** + * Request parameters for deletenotificationHaloRunV1alpha1ReasonType operation in NotificationHaloRunV1alpha1ReasonTypeApi. + * @export + * @interface NotificationHaloRunV1alpha1ReasonTypeApiDeletenotificationHaloRunV1alpha1ReasonTypeRequest + */ +export interface NotificationHaloRunV1alpha1ReasonTypeApiDeletenotificationHaloRunV1alpha1ReasonTypeRequest { + /** + * Name of reasontype + * @type {string} + * @memberof NotificationHaloRunV1alpha1ReasonTypeApiDeletenotificationHaloRunV1alpha1ReasonType + */ + readonly name: string; +} + +/** + * Request parameters for getnotificationHaloRunV1alpha1ReasonType operation in NotificationHaloRunV1alpha1ReasonTypeApi. + * @export + * @interface NotificationHaloRunV1alpha1ReasonTypeApiGetnotificationHaloRunV1alpha1ReasonTypeRequest + */ +export interface NotificationHaloRunV1alpha1ReasonTypeApiGetnotificationHaloRunV1alpha1ReasonTypeRequest { + /** + * Name of reasontype + * @type {string} + * @memberof NotificationHaloRunV1alpha1ReasonTypeApiGetnotificationHaloRunV1alpha1ReasonType + */ + readonly name: string; +} + +/** + * Request parameters for listnotificationHaloRunV1alpha1ReasonType operation in NotificationHaloRunV1alpha1ReasonTypeApi. + * @export + * @interface NotificationHaloRunV1alpha1ReasonTypeApiListnotificationHaloRunV1alpha1ReasonTypeRequest + */ +export interface NotificationHaloRunV1alpha1ReasonTypeApiListnotificationHaloRunV1alpha1ReasonTypeRequest { + /** + * Field selector for filtering. + * @type {Array} + * @memberof NotificationHaloRunV1alpha1ReasonTypeApiListnotificationHaloRunV1alpha1ReasonType + */ + readonly fieldSelector?: Array; + + /** + * Label selector for filtering. + * @type {Array} + * @memberof NotificationHaloRunV1alpha1ReasonTypeApiListnotificationHaloRunV1alpha1ReasonType + */ + readonly labelSelector?: Array; + + /** + * The page number. Zero indicates no page. + * @type {number} + * @memberof NotificationHaloRunV1alpha1ReasonTypeApiListnotificationHaloRunV1alpha1ReasonType + */ + readonly page?: number; + + /** + * Size of one page. Zero indicates no limit. + * @type {number} + * @memberof NotificationHaloRunV1alpha1ReasonTypeApiListnotificationHaloRunV1alpha1ReasonType + */ + readonly size?: number; + + /** + * Sort property and direction of the list result. Support sorting based on attribute name path. + * @type {Array} + * @memberof NotificationHaloRunV1alpha1ReasonTypeApiListnotificationHaloRunV1alpha1ReasonType + */ + readonly sort?: Array; +} + +/** + * Request parameters for updatenotificationHaloRunV1alpha1ReasonType operation in NotificationHaloRunV1alpha1ReasonTypeApi. + * @export + * @interface NotificationHaloRunV1alpha1ReasonTypeApiUpdatenotificationHaloRunV1alpha1ReasonTypeRequest + */ +export interface NotificationHaloRunV1alpha1ReasonTypeApiUpdatenotificationHaloRunV1alpha1ReasonTypeRequest { + /** + * Name of reasontype + * @type {string} + * @memberof NotificationHaloRunV1alpha1ReasonTypeApiUpdatenotificationHaloRunV1alpha1ReasonType + */ + readonly name: string; + + /** + * Updated reasontype + * @type {ReasonType} + * @memberof NotificationHaloRunV1alpha1ReasonTypeApiUpdatenotificationHaloRunV1alpha1ReasonType + */ + readonly reasonType?: ReasonType; +} + +/** + * NotificationHaloRunV1alpha1ReasonTypeApi - object-oriented interface + * @export + * @class NotificationHaloRunV1alpha1ReasonTypeApi + * @extends {BaseAPI} + */ +export class NotificationHaloRunV1alpha1ReasonTypeApi extends BaseAPI { + /** + * Create notification.halo.run/v1alpha1/ReasonType + * @param {NotificationHaloRunV1alpha1ReasonTypeApiCreatenotificationHaloRunV1alpha1ReasonTypeRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1ReasonTypeApi + */ + public createnotificationHaloRunV1alpha1ReasonType( + requestParameters: NotificationHaloRunV1alpha1ReasonTypeApiCreatenotificationHaloRunV1alpha1ReasonTypeRequest = {}, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1ReasonTypeApiFp(this.configuration) + .createnotificationHaloRunV1alpha1ReasonType( + requestParameters.reasonType, + options + ) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Delete notification.halo.run/v1alpha1/ReasonType + * @param {NotificationHaloRunV1alpha1ReasonTypeApiDeletenotificationHaloRunV1alpha1ReasonTypeRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1ReasonTypeApi + */ + public deletenotificationHaloRunV1alpha1ReasonType( + requestParameters: NotificationHaloRunV1alpha1ReasonTypeApiDeletenotificationHaloRunV1alpha1ReasonTypeRequest, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1ReasonTypeApiFp(this.configuration) + .deletenotificationHaloRunV1alpha1ReasonType( + requestParameters.name, + options + ) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Get notification.halo.run/v1alpha1/ReasonType + * @param {NotificationHaloRunV1alpha1ReasonTypeApiGetnotificationHaloRunV1alpha1ReasonTypeRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1ReasonTypeApi + */ + public getnotificationHaloRunV1alpha1ReasonType( + requestParameters: NotificationHaloRunV1alpha1ReasonTypeApiGetnotificationHaloRunV1alpha1ReasonTypeRequest, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1ReasonTypeApiFp(this.configuration) + .getnotificationHaloRunV1alpha1ReasonType(requestParameters.name, options) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * List notification.halo.run/v1alpha1/ReasonType + * @param {NotificationHaloRunV1alpha1ReasonTypeApiListnotificationHaloRunV1alpha1ReasonTypeRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1ReasonTypeApi + */ + public listnotificationHaloRunV1alpha1ReasonType( + requestParameters: NotificationHaloRunV1alpha1ReasonTypeApiListnotificationHaloRunV1alpha1ReasonTypeRequest = {}, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1ReasonTypeApiFp(this.configuration) + .listnotificationHaloRunV1alpha1ReasonType( + requestParameters.fieldSelector, + requestParameters.labelSelector, + requestParameters.page, + requestParameters.size, + requestParameters.sort, + options + ) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Update notification.halo.run/v1alpha1/ReasonType + * @param {NotificationHaloRunV1alpha1ReasonTypeApiUpdatenotificationHaloRunV1alpha1ReasonTypeRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1ReasonTypeApi + */ + public updatenotificationHaloRunV1alpha1ReasonType( + requestParameters: NotificationHaloRunV1alpha1ReasonTypeApiUpdatenotificationHaloRunV1alpha1ReasonTypeRequest, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1ReasonTypeApiFp(this.configuration) + .updatenotificationHaloRunV1alpha1ReasonType( + requestParameters.name, + requestParameters.reasonType, + options + ) + .then((request) => request(this.axios, this.basePath)); + } +} diff --git a/console/packages/api-client/src/api/notification-halo-run-v1alpha1-subscription-api.ts b/console/packages/api-client/src/api/notification-halo-run-v1alpha1-subscription-api.ts new file mode 100644 index 0000000000..35a082988c --- /dev/null +++ b/console/packages/api-client/src/api/notification-halo-run-v1alpha1-subscription-api.ts @@ -0,0 +1,835 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import type { Configuration } from "../configuration"; +import type { AxiosPromise, AxiosInstance, AxiosRequestConfig } from "axios"; +import globalAxios from "axios"; +// Some imports not used depending on template conditions +// @ts-ignore +import { + DUMMY_BASE_URL, + assertParamExists, + setApiKeyToObject, + setBasicAuthToObject, + setBearerAuthToObject, + setOAuthToObject, + setSearchParams, + serializeDataIfNeeded, + toPathString, + createRequestFunction, +} from "../common"; +// @ts-ignore +import { + BASE_PATH, + COLLECTION_FORMATS, + RequestArgs, + BaseAPI, + RequiredError, +} from "../base"; +// @ts-ignore +import { Subscription } from "../models"; +// @ts-ignore +import { SubscriptionList } from "../models"; +/** + * NotificationHaloRunV1alpha1SubscriptionApi - axios parameter creator + * @export + */ +export const NotificationHaloRunV1alpha1SubscriptionApiAxiosParamCreator = + function (configuration?: Configuration) { + return { + /** + * Create notification.halo.run/v1alpha1/Subscription + * @param {Subscription} [subscription] Fresh subscription + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + createnotificationHaloRunV1alpha1Subscription: async ( + subscription?: Subscription, + options: AxiosRequestConfig = {} + ): Promise => { + const localVarPath = `/apis/notification.halo.run/v1alpha1/subscriptions`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "POST", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + localVarHeaderParameter["Content-Type"] = "application/json"; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + localVarRequestOptions.data = serializeDataIfNeeded( + subscription, + localVarRequestOptions, + configuration + ); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Delete notification.halo.run/v1alpha1/Subscription + * @param {string} name Name of subscription + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deletenotificationHaloRunV1alpha1Subscription: async ( + name: string, + options: AxiosRequestConfig = {} + ): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists( + "deletenotificationHaloRunV1alpha1Subscription", + "name", + name + ); + const localVarPath = + `/apis/notification.halo.run/v1alpha1/subscriptions/{name}`.replace( + `{${"name"}}`, + encodeURIComponent(String(name)) + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "DELETE", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Get notification.halo.run/v1alpha1/Subscription + * @param {string} name Name of subscription + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getnotificationHaloRunV1alpha1Subscription: async ( + name: string, + options: AxiosRequestConfig = {} + ): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists( + "getnotificationHaloRunV1alpha1Subscription", + "name", + name + ); + const localVarPath = + `/apis/notification.halo.run/v1alpha1/subscriptions/{name}`.replace( + `{${"name"}}`, + encodeURIComponent(String(name)) + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * List notification.halo.run/v1alpha1/Subscription + * @param {Array} [fieldSelector] Field selector for filtering. + * @param {Array} [labelSelector] Label selector for filtering. + * @param {number} [page] The page number. Zero indicates no page. + * @param {number} [size] Size of one page. Zero indicates no limit. + * @param {Array} [sort] Sort property and direction of the list result. Support sorting based on attribute name path. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listnotificationHaloRunV1alpha1Subscription: async ( + fieldSelector?: Array, + labelSelector?: Array, + page?: number, + size?: number, + sort?: Array, + options: AxiosRequestConfig = {} + ): Promise => { + const localVarPath = `/apis/notification.halo.run/v1alpha1/subscriptions`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "GET", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + if (fieldSelector) { + localVarQueryParameter["fieldSelector"] = fieldSelector; + } + + if (labelSelector) { + localVarQueryParameter["labelSelector"] = labelSelector; + } + + if (page !== undefined) { + localVarQueryParameter["page"] = page; + } + + if (size !== undefined) { + localVarQueryParameter["size"] = size; + } + + if (sort) { + localVarQueryParameter["sort"] = Array.from(sort); + } + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * Update notification.halo.run/v1alpha1/Subscription + * @param {string} name Name of subscription + * @param {Subscription} [subscription] Updated subscription + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + updatenotificationHaloRunV1alpha1Subscription: async ( + name: string, + subscription?: Subscription, + options: AxiosRequestConfig = {} + ): Promise => { + // verify required parameter 'name' is not null or undefined + assertParamExists( + "updatenotificationHaloRunV1alpha1Subscription", + "name", + name + ); + const localVarPath = + `/apis/notification.halo.run/v1alpha1/subscriptions/{name}`.replace( + `{${"name"}}`, + encodeURIComponent(String(name)) + ); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { + method: "PUT", + ...baseOptions, + ...options, + }; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication BasicAuth required + // http basic authentication required + setBasicAuthToObject(localVarRequestOptions, configuration); + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration); + + localVarHeaderParameter["Content-Type"] = "application/json"; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = + baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = { + ...localVarHeaderParameter, + ...headersFromBaseOptions, + ...options.headers, + }; + localVarRequestOptions.data = serializeDataIfNeeded( + subscription, + localVarRequestOptions, + configuration + ); + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + }; + }; + +/** + * NotificationHaloRunV1alpha1SubscriptionApi - functional programming interface + * @export + */ +export const NotificationHaloRunV1alpha1SubscriptionApiFp = function ( + configuration?: Configuration +) { + const localVarAxiosParamCreator = + NotificationHaloRunV1alpha1SubscriptionApiAxiosParamCreator(configuration); + return { + /** + * Create notification.halo.run/v1alpha1/Subscription + * @param {Subscription} [subscription] Fresh subscription + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async createnotificationHaloRunV1alpha1Subscription( + subscription?: Subscription, + options?: AxiosRequestConfig + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.createnotificationHaloRunV1alpha1Subscription( + subscription, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * Delete notification.halo.run/v1alpha1/Subscription + * @param {string} name Name of subscription + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async deletenotificationHaloRunV1alpha1Subscription( + name: string, + options?: AxiosRequestConfig + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.deletenotificationHaloRunV1alpha1Subscription( + name, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * Get notification.halo.run/v1alpha1/Subscription + * @param {string} name Name of subscription + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getnotificationHaloRunV1alpha1Subscription( + name: string, + options?: AxiosRequestConfig + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.getnotificationHaloRunV1alpha1Subscription( + name, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * List notification.halo.run/v1alpha1/Subscription + * @param {Array} [fieldSelector] Field selector for filtering. + * @param {Array} [labelSelector] Label selector for filtering. + * @param {number} [page] The page number. Zero indicates no page. + * @param {number} [size] Size of one page. Zero indicates no limit. + * @param {Array} [sort] Sort property and direction of the list result. Support sorting based on attribute name path. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async listnotificationHaloRunV1alpha1Subscription( + fieldSelector?: Array, + labelSelector?: Array, + page?: number, + size?: number, + sort?: Array, + options?: AxiosRequestConfig + ): Promise< + ( + axios?: AxiosInstance, + basePath?: string + ) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.listnotificationHaloRunV1alpha1Subscription( + fieldSelector, + labelSelector, + page, + size, + sort, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + /** + * Update notification.halo.run/v1alpha1/Subscription + * @param {string} name Name of subscription + * @param {Subscription} [subscription] Updated subscription + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async updatenotificationHaloRunV1alpha1Subscription( + name: string, + subscription?: Subscription, + options?: AxiosRequestConfig + ): Promise< + (axios?: AxiosInstance, basePath?: string) => AxiosPromise + > { + const localVarAxiosArgs = + await localVarAxiosParamCreator.updatenotificationHaloRunV1alpha1Subscription( + name, + subscription, + options + ); + return createRequestFunction( + localVarAxiosArgs, + globalAxios, + BASE_PATH, + configuration + ); + }, + }; +}; + +/** + * NotificationHaloRunV1alpha1SubscriptionApi - factory interface + * @export + */ +export const NotificationHaloRunV1alpha1SubscriptionApiFactory = function ( + configuration?: Configuration, + basePath?: string, + axios?: AxiosInstance +) { + const localVarFp = + NotificationHaloRunV1alpha1SubscriptionApiFp(configuration); + return { + /** + * Create notification.halo.run/v1alpha1/Subscription + * @param {NotificationHaloRunV1alpha1SubscriptionApiCreatenotificationHaloRunV1alpha1SubscriptionRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + createnotificationHaloRunV1alpha1Subscription( + requestParameters: NotificationHaloRunV1alpha1SubscriptionApiCreatenotificationHaloRunV1alpha1SubscriptionRequest = {}, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .createnotificationHaloRunV1alpha1Subscription( + requestParameters.subscription, + options + ) + .then((request) => request(axios, basePath)); + }, + /** + * Delete notification.halo.run/v1alpha1/Subscription + * @param {NotificationHaloRunV1alpha1SubscriptionApiDeletenotificationHaloRunV1alpha1SubscriptionRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deletenotificationHaloRunV1alpha1Subscription( + requestParameters: NotificationHaloRunV1alpha1SubscriptionApiDeletenotificationHaloRunV1alpha1SubscriptionRequest, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .deletenotificationHaloRunV1alpha1Subscription( + requestParameters.name, + options + ) + .then((request) => request(axios, basePath)); + }, + /** + * Get notification.halo.run/v1alpha1/Subscription + * @param {NotificationHaloRunV1alpha1SubscriptionApiGetnotificationHaloRunV1alpha1SubscriptionRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getnotificationHaloRunV1alpha1Subscription( + requestParameters: NotificationHaloRunV1alpha1SubscriptionApiGetnotificationHaloRunV1alpha1SubscriptionRequest, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .getnotificationHaloRunV1alpha1Subscription( + requestParameters.name, + options + ) + .then((request) => request(axios, basePath)); + }, + /** + * List notification.halo.run/v1alpha1/Subscription + * @param {NotificationHaloRunV1alpha1SubscriptionApiListnotificationHaloRunV1alpha1SubscriptionRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + listnotificationHaloRunV1alpha1Subscription( + requestParameters: NotificationHaloRunV1alpha1SubscriptionApiListnotificationHaloRunV1alpha1SubscriptionRequest = {}, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .listnotificationHaloRunV1alpha1Subscription( + requestParameters.fieldSelector, + requestParameters.labelSelector, + requestParameters.page, + requestParameters.size, + requestParameters.sort, + options + ) + .then((request) => request(axios, basePath)); + }, + /** + * Update notification.halo.run/v1alpha1/Subscription + * @param {NotificationHaloRunV1alpha1SubscriptionApiUpdatenotificationHaloRunV1alpha1SubscriptionRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + updatenotificationHaloRunV1alpha1Subscription( + requestParameters: NotificationHaloRunV1alpha1SubscriptionApiUpdatenotificationHaloRunV1alpha1SubscriptionRequest, + options?: AxiosRequestConfig + ): AxiosPromise { + return localVarFp + .updatenotificationHaloRunV1alpha1Subscription( + requestParameters.name, + requestParameters.subscription, + options + ) + .then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * Request parameters for createnotificationHaloRunV1alpha1Subscription operation in NotificationHaloRunV1alpha1SubscriptionApi. + * @export + * @interface NotificationHaloRunV1alpha1SubscriptionApiCreatenotificationHaloRunV1alpha1SubscriptionRequest + */ +export interface NotificationHaloRunV1alpha1SubscriptionApiCreatenotificationHaloRunV1alpha1SubscriptionRequest { + /** + * Fresh subscription + * @type {Subscription} + * @memberof NotificationHaloRunV1alpha1SubscriptionApiCreatenotificationHaloRunV1alpha1Subscription + */ + readonly subscription?: Subscription; +} + +/** + * Request parameters for deletenotificationHaloRunV1alpha1Subscription operation in NotificationHaloRunV1alpha1SubscriptionApi. + * @export + * @interface NotificationHaloRunV1alpha1SubscriptionApiDeletenotificationHaloRunV1alpha1SubscriptionRequest + */ +export interface NotificationHaloRunV1alpha1SubscriptionApiDeletenotificationHaloRunV1alpha1SubscriptionRequest { + /** + * Name of subscription + * @type {string} + * @memberof NotificationHaloRunV1alpha1SubscriptionApiDeletenotificationHaloRunV1alpha1Subscription + */ + readonly name: string; +} + +/** + * Request parameters for getnotificationHaloRunV1alpha1Subscription operation in NotificationHaloRunV1alpha1SubscriptionApi. + * @export + * @interface NotificationHaloRunV1alpha1SubscriptionApiGetnotificationHaloRunV1alpha1SubscriptionRequest + */ +export interface NotificationHaloRunV1alpha1SubscriptionApiGetnotificationHaloRunV1alpha1SubscriptionRequest { + /** + * Name of subscription + * @type {string} + * @memberof NotificationHaloRunV1alpha1SubscriptionApiGetnotificationHaloRunV1alpha1Subscription + */ + readonly name: string; +} + +/** + * Request parameters for listnotificationHaloRunV1alpha1Subscription operation in NotificationHaloRunV1alpha1SubscriptionApi. + * @export + * @interface NotificationHaloRunV1alpha1SubscriptionApiListnotificationHaloRunV1alpha1SubscriptionRequest + */ +export interface NotificationHaloRunV1alpha1SubscriptionApiListnotificationHaloRunV1alpha1SubscriptionRequest { + /** + * Field selector for filtering. + * @type {Array} + * @memberof NotificationHaloRunV1alpha1SubscriptionApiListnotificationHaloRunV1alpha1Subscription + */ + readonly fieldSelector?: Array; + + /** + * Label selector for filtering. + * @type {Array} + * @memberof NotificationHaloRunV1alpha1SubscriptionApiListnotificationHaloRunV1alpha1Subscription + */ + readonly labelSelector?: Array; + + /** + * The page number. Zero indicates no page. + * @type {number} + * @memberof NotificationHaloRunV1alpha1SubscriptionApiListnotificationHaloRunV1alpha1Subscription + */ + readonly page?: number; + + /** + * Size of one page. Zero indicates no limit. + * @type {number} + * @memberof NotificationHaloRunV1alpha1SubscriptionApiListnotificationHaloRunV1alpha1Subscription + */ + readonly size?: number; + + /** + * Sort property and direction of the list result. Support sorting based on attribute name path. + * @type {Array} + * @memberof NotificationHaloRunV1alpha1SubscriptionApiListnotificationHaloRunV1alpha1Subscription + */ + readonly sort?: Array; +} + +/** + * Request parameters for updatenotificationHaloRunV1alpha1Subscription operation in NotificationHaloRunV1alpha1SubscriptionApi. + * @export + * @interface NotificationHaloRunV1alpha1SubscriptionApiUpdatenotificationHaloRunV1alpha1SubscriptionRequest + */ +export interface NotificationHaloRunV1alpha1SubscriptionApiUpdatenotificationHaloRunV1alpha1SubscriptionRequest { + /** + * Name of subscription + * @type {string} + * @memberof NotificationHaloRunV1alpha1SubscriptionApiUpdatenotificationHaloRunV1alpha1Subscription + */ + readonly name: string; + + /** + * Updated subscription + * @type {Subscription} + * @memberof NotificationHaloRunV1alpha1SubscriptionApiUpdatenotificationHaloRunV1alpha1Subscription + */ + readonly subscription?: Subscription; +} + +/** + * NotificationHaloRunV1alpha1SubscriptionApi - object-oriented interface + * @export + * @class NotificationHaloRunV1alpha1SubscriptionApi + * @extends {BaseAPI} + */ +export class NotificationHaloRunV1alpha1SubscriptionApi extends BaseAPI { + /** + * Create notification.halo.run/v1alpha1/Subscription + * @param {NotificationHaloRunV1alpha1SubscriptionApiCreatenotificationHaloRunV1alpha1SubscriptionRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1SubscriptionApi + */ + public createnotificationHaloRunV1alpha1Subscription( + requestParameters: NotificationHaloRunV1alpha1SubscriptionApiCreatenotificationHaloRunV1alpha1SubscriptionRequest = {}, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1SubscriptionApiFp(this.configuration) + .createnotificationHaloRunV1alpha1Subscription( + requestParameters.subscription, + options + ) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Delete notification.halo.run/v1alpha1/Subscription + * @param {NotificationHaloRunV1alpha1SubscriptionApiDeletenotificationHaloRunV1alpha1SubscriptionRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1SubscriptionApi + */ + public deletenotificationHaloRunV1alpha1Subscription( + requestParameters: NotificationHaloRunV1alpha1SubscriptionApiDeletenotificationHaloRunV1alpha1SubscriptionRequest, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1SubscriptionApiFp(this.configuration) + .deletenotificationHaloRunV1alpha1Subscription( + requestParameters.name, + options + ) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Get notification.halo.run/v1alpha1/Subscription + * @param {NotificationHaloRunV1alpha1SubscriptionApiGetnotificationHaloRunV1alpha1SubscriptionRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1SubscriptionApi + */ + public getnotificationHaloRunV1alpha1Subscription( + requestParameters: NotificationHaloRunV1alpha1SubscriptionApiGetnotificationHaloRunV1alpha1SubscriptionRequest, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1SubscriptionApiFp(this.configuration) + .getnotificationHaloRunV1alpha1Subscription( + requestParameters.name, + options + ) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * List notification.halo.run/v1alpha1/Subscription + * @param {NotificationHaloRunV1alpha1SubscriptionApiListnotificationHaloRunV1alpha1SubscriptionRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1SubscriptionApi + */ + public listnotificationHaloRunV1alpha1Subscription( + requestParameters: NotificationHaloRunV1alpha1SubscriptionApiListnotificationHaloRunV1alpha1SubscriptionRequest = {}, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1SubscriptionApiFp(this.configuration) + .listnotificationHaloRunV1alpha1Subscription( + requestParameters.fieldSelector, + requestParameters.labelSelector, + requestParameters.page, + requestParameters.size, + requestParameters.sort, + options + ) + .then((request) => request(this.axios, this.basePath)); + } + + /** + * Update notification.halo.run/v1alpha1/Subscription + * @param {NotificationHaloRunV1alpha1SubscriptionApiUpdatenotificationHaloRunV1alpha1SubscriptionRequest} requestParameters Request parameters. + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationHaloRunV1alpha1SubscriptionApi + */ + public updatenotificationHaloRunV1alpha1Subscription( + requestParameters: NotificationHaloRunV1alpha1SubscriptionApiUpdatenotificationHaloRunV1alpha1SubscriptionRequest, + options?: AxiosRequestConfig + ) { + return NotificationHaloRunV1alpha1SubscriptionApiFp(this.configuration) + .updatenotificationHaloRunV1alpha1Subscription( + requestParameters.name, + requestParameters.subscription, + options + ) + .then((request) => request(this.axios, this.basePath)); + } +} diff --git a/console/packages/api-client/src/models/index.ts b/console/packages/api-client/src/models/index.ts index 88262a54b6..a1f22074de 100644 --- a/console/packages/api-client/src/models/index.ts +++ b/console/packages/api-client/src/models/index.ts @@ -63,6 +63,8 @@ export * from "./group-list"; export * from "./group-spec"; export * from "./group-status"; export * from "./install-from-uri-request"; +export * from "./interest-reason"; +export * from "./interest-reason-subject"; export * from "./license"; export * from "./listed-auth-provider"; export * from "./listed-comment"; @@ -79,6 +81,7 @@ export * from "./listed-single-page-vo"; export * from "./listed-single-page-vo-list"; export * from "./listed-user"; export * from "./login-history"; +export * from "./mark-specified-request"; export * from "./menu"; export * from "./menu-item"; export * from "./menu-item-list"; @@ -90,6 +93,16 @@ export * from "./menu-spec"; export * from "./menu-vo"; export * from "./metadata"; export * from "./navigation-post-vo"; +export * from "./notification"; +export * from "./notification-list"; +export * from "./notification-spec"; +export * from "./notification-template"; +export * from "./notification-template-list"; +export * from "./notification-template-spec"; +export * from "./notifier-descriptor"; +export * from "./notifier-descriptor-list"; +export * from "./notifier-descriptor-spec"; +export * from "./notifier-setting-ref"; export * from "./owner-info"; export * from "./pat-spec"; export * from "./personal-access-token"; @@ -115,6 +128,16 @@ export * from "./post-spec"; export * from "./post-status"; export * from "./post-vo"; export * from "./public-key-response"; +export * from "./reason"; +export * from "./reason-list"; +export * from "./reason-property"; +export * from "./reason-selector"; +export * from "./reason-spec"; +export * from "./reason-spec-attributes"; +export * from "./reason-subject"; +export * from "./reason-type"; +export * from "./reason-type-list"; +export * from "./reason-type-spec"; export * from "./ref"; export * from "./reply"; export * from "./reply-list"; @@ -154,6 +177,10 @@ export * from "./snapshot-list"; export * from "./stats"; export * from "./stats-vo"; export * from "./subject"; +export * from "./subscription"; +export * from "./subscription-list"; +export * from "./subscription-spec"; +export * from "./subscription-subscriber"; export * from "./system-initialization-request"; export * from "./tag"; export * from "./tag-list"; @@ -161,6 +188,7 @@ export * from "./tag-spec"; export * from "./tag-status"; export * from "./tag-vo"; export * from "./tag-vo-list"; +export * from "./template-content"; export * from "./template-descriptor"; export * from "./theme"; export * from "./theme-list"; diff --git a/console/packages/api-client/src/models/interest-reason-subject.ts b/console/packages/api-client/src/models/interest-reason-subject.ts new file mode 100644 index 0000000000..a7a693c3aa --- /dev/null +++ b/console/packages/api-client/src/models/interest-reason-subject.ts @@ -0,0 +1,39 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +/** + * The subject name of reason type to be interested in + * @export + * @interface InterestReasonSubject + */ +export interface InterestReasonSubject { + /** + * + * @type {string} + * @memberof InterestReasonSubject + */ + apiVersion: string; + /** + * + * @type {string} + * @memberof InterestReasonSubject + */ + kind: string; + /** + * if name is not specified, it presents all subjects of the specified reason type and custom resources + * @type {string} + * @memberof InterestReasonSubject + */ + name?: string; +} diff --git a/console/packages/api-client/src/models/interest-reason.ts b/console/packages/api-client/src/models/interest-reason.ts new file mode 100644 index 0000000000..c33a1d9348 --- /dev/null +++ b/console/packages/api-client/src/models/interest-reason.ts @@ -0,0 +1,37 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +// May contain unused imports in some cases +// @ts-ignore +import { InterestReasonSubject } from "./interest-reason-subject"; + +/** + * The reason to be interested in + * @export + * @interface InterestReason + */ +export interface InterestReason { + /** + * The name of the reason definition to be interested in + * @type {string} + * @memberof InterestReason + */ + reasonType: string; + /** + * + * @type {InterestReasonSubject} + * @memberof InterestReason + */ + subject: InterestReasonSubject; +} diff --git a/console/packages/api-client/src/models/mark-specified-request.ts b/console/packages/api-client/src/models/mark-specified-request.ts new file mode 100644 index 0000000000..ca4181d28e --- /dev/null +++ b/console/packages/api-client/src/models/mark-specified-request.ts @@ -0,0 +1,27 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +/** + * + * @export + * @interface MarkSpecifiedRequest + */ +export interface MarkSpecifiedRequest { + /** + * + * @type {Array} + * @memberof MarkSpecifiedRequest + */ + names?: Array; +} diff --git a/console/packages/api-client/src/models/notification-list.ts b/console/packages/api-client/src/models/notification-list.ts new file mode 100644 index 0000000000..ab305489d0 --- /dev/null +++ b/console/packages/api-client/src/models/notification-list.ts @@ -0,0 +1,79 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +// May contain unused imports in some cases +// @ts-ignore +import { Notification } from "./notification"; + +/** + * + * @export + * @interface NotificationList + */ +export interface NotificationList { + /** + * Indicates whether current page is the first page. + * @type {boolean} + * @memberof NotificationList + */ + first: boolean; + /** + * Indicates whether current page has previous page. + * @type {boolean} + * @memberof NotificationList + */ + hasNext: boolean; + /** + * Indicates whether current page has previous page. + * @type {boolean} + * @memberof NotificationList + */ + hasPrevious: boolean; + /** + * A chunk of items. + * @type {Array} + * @memberof NotificationList + */ + items: Array; + /** + * Indicates whether current page is the last page. + * @type {boolean} + * @memberof NotificationList + */ + last: boolean; + /** + * Page number, starts from 1. If not set or equal to 0, it means no pagination. + * @type {number} + * @memberof NotificationList + */ + page: number; + /** + * Size of each page. If not set or equal to 0, it means no pagination. + * @type {number} + * @memberof NotificationList + */ + size: number; + /** + * Total elements. + * @type {number} + * @memberof NotificationList + */ + total: number; + /** + * Indicates total pages. + * @type {number} + * @memberof NotificationList + */ + totalPages: number; +} diff --git a/console/packages/api-client/src/models/notification-spec.ts b/console/packages/api-client/src/models/notification-spec.ts new file mode 100644 index 0000000000..0d5cdc8b59 --- /dev/null +++ b/console/packages/api-client/src/models/notification-spec.ts @@ -0,0 +1,63 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +/** + * + * @export + * @interface NotificationSpec + */ +export interface NotificationSpec { + /** + * + * @type {string} + * @memberof NotificationSpec + */ + htmlContent: string; + /** + * + * @type {string} + * @memberof NotificationSpec + */ + lastReadAt?: string; + /** + * + * @type {string} + * @memberof NotificationSpec + */ + rawContent: string; + /** + * The name of reason + * @type {string} + * @memberof NotificationSpec + */ + reason: string; + /** + * The name of user + * @type {string} + * @memberof NotificationSpec + */ + recipient: string; + /** + * + * @type {string} + * @memberof NotificationSpec + */ + title: string; + /** + * + * @type {boolean} + * @memberof NotificationSpec + */ + unread?: boolean; +} diff --git a/console/packages/api-client/src/models/notification-template-list.ts b/console/packages/api-client/src/models/notification-template-list.ts new file mode 100644 index 0000000000..0eda98cfd0 --- /dev/null +++ b/console/packages/api-client/src/models/notification-template-list.ts @@ -0,0 +1,79 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +// May contain unused imports in some cases +// @ts-ignore +import { NotificationTemplate } from "./notification-template"; + +/** + * + * @export + * @interface NotificationTemplateList + */ +export interface NotificationTemplateList { + /** + * Indicates whether current page is the first page. + * @type {boolean} + * @memberof NotificationTemplateList + */ + first: boolean; + /** + * Indicates whether current page has previous page. + * @type {boolean} + * @memberof NotificationTemplateList + */ + hasNext: boolean; + /** + * Indicates whether current page has previous page. + * @type {boolean} + * @memberof NotificationTemplateList + */ + hasPrevious: boolean; + /** + * A chunk of items. + * @type {Array} + * @memberof NotificationTemplateList + */ + items: Array; + /** + * Indicates whether current page is the last page. + * @type {boolean} + * @memberof NotificationTemplateList + */ + last: boolean; + /** + * Page number, starts from 1. If not set or equal to 0, it means no pagination. + * @type {number} + * @memberof NotificationTemplateList + */ + page: number; + /** + * Size of each page. If not set or equal to 0, it means no pagination. + * @type {number} + * @memberof NotificationTemplateList + */ + size: number; + /** + * Total elements. + * @type {number} + * @memberof NotificationTemplateList + */ + total: number; + /** + * Indicates total pages. + * @type {number} + * @memberof NotificationTemplateList + */ + totalPages: number; +} diff --git a/console/packages/api-client/src/models/notification-template-spec.ts b/console/packages/api-client/src/models/notification-template-spec.ts new file mode 100644 index 0000000000..54e9e92c0b --- /dev/null +++ b/console/packages/api-client/src/models/notification-template-spec.ts @@ -0,0 +1,40 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +// May contain unused imports in some cases +// @ts-ignore +import { ReasonSelector } from "./reason-selector"; +// May contain unused imports in some cases +// @ts-ignore +import { TemplateContent } from "./template-content"; + +/** + * + * @export + * @interface NotificationTemplateSpec + */ +export interface NotificationTemplateSpec { + /** + * + * @type {ReasonSelector} + * @memberof NotificationTemplateSpec + */ + reasonSelector?: ReasonSelector; + /** + * + * @type {TemplateContent} + * @memberof NotificationTemplateSpec + */ + template?: TemplateContent; +} diff --git a/console/packages/api-client/src/models/notification-template.ts b/console/packages/api-client/src/models/notification-template.ts new file mode 100644 index 0000000000..8470f874f2 --- /dev/null +++ b/console/packages/api-client/src/models/notification-template.ts @@ -0,0 +1,52 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +// May contain unused imports in some cases +// @ts-ignore +import { Metadata } from "./metadata"; +// May contain unused imports in some cases +// @ts-ignore +import { NotificationTemplateSpec } from "./notification-template-spec"; + +/** + * + * @export + * @interface NotificationTemplate + */ +export interface NotificationTemplate { + /** + * + * @type {string} + * @memberof NotificationTemplate + */ + apiVersion: string; + /** + * + * @type {string} + * @memberof NotificationTemplate + */ + kind: string; + /** + * + * @type {Metadata} + * @memberof NotificationTemplate + */ + metadata: Metadata; + /** + * + * @type {NotificationTemplateSpec} + * @memberof NotificationTemplate + */ + spec?: NotificationTemplateSpec; +} diff --git a/console/packages/api-client/src/models/notification.ts b/console/packages/api-client/src/models/notification.ts new file mode 100644 index 0000000000..7704d94c5f --- /dev/null +++ b/console/packages/api-client/src/models/notification.ts @@ -0,0 +1,52 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +// May contain unused imports in some cases +// @ts-ignore +import { Metadata } from "./metadata"; +// May contain unused imports in some cases +// @ts-ignore +import { NotificationSpec } from "./notification-spec"; + +/** + * + * @export + * @interface Notification + */ +export interface Notification { + /** + * + * @type {string} + * @memberof Notification + */ + apiVersion: string; + /** + * + * @type {string} + * @memberof Notification + */ + kind: string; + /** + * + * @type {Metadata} + * @memberof Notification + */ + metadata: Metadata; + /** + * + * @type {NotificationSpec} + * @memberof Notification + */ + spec?: NotificationSpec; +} diff --git a/console/packages/api-client/src/models/notifier-descriptor-list.ts b/console/packages/api-client/src/models/notifier-descriptor-list.ts new file mode 100644 index 0000000000..9bb0cc8feb --- /dev/null +++ b/console/packages/api-client/src/models/notifier-descriptor-list.ts @@ -0,0 +1,79 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +// May contain unused imports in some cases +// @ts-ignore +import { NotifierDescriptor } from "./notifier-descriptor"; + +/** + * + * @export + * @interface NotifierDescriptorList + */ +export interface NotifierDescriptorList { + /** + * Indicates whether current page is the first page. + * @type {boolean} + * @memberof NotifierDescriptorList + */ + first: boolean; + /** + * Indicates whether current page has previous page. + * @type {boolean} + * @memberof NotifierDescriptorList + */ + hasNext: boolean; + /** + * Indicates whether current page has previous page. + * @type {boolean} + * @memberof NotifierDescriptorList + */ + hasPrevious: boolean; + /** + * A chunk of items. + * @type {Array} + * @memberof NotifierDescriptorList + */ + items: Array; + /** + * Indicates whether current page is the last page. + * @type {boolean} + * @memberof NotifierDescriptorList + */ + last: boolean; + /** + * Page number, starts from 1. If not set or equal to 0, it means no pagination. + * @type {number} + * @memberof NotifierDescriptorList + */ + page: number; + /** + * Size of each page. If not set or equal to 0, it means no pagination. + * @type {number} + * @memberof NotifierDescriptorList + */ + size: number; + /** + * Total elements. + * @type {number} + * @memberof NotifierDescriptorList + */ + total: number; + /** + * Indicates total pages. + * @type {number} + * @memberof NotifierDescriptorList + */ + totalPages: number; +} diff --git a/console/packages/api-client/src/models/notifier-descriptor-spec.ts b/console/packages/api-client/src/models/notifier-descriptor-spec.ts new file mode 100644 index 0000000000..70ce155bc0 --- /dev/null +++ b/console/packages/api-client/src/models/notifier-descriptor-spec.ts @@ -0,0 +1,55 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +// May contain unused imports in some cases +// @ts-ignore +import { NotifierSettingRef } from "./notifier-setting-ref"; + +/** + * + * @export + * @interface NotifierDescriptorSpec + */ +export interface NotifierDescriptorSpec { + /** + * + * @type {string} + * @memberof NotifierDescriptorSpec + */ + description?: string; + /** + * + * @type {string} + * @memberof NotifierDescriptorSpec + */ + displayName: string; + /** + * + * @type {string} + * @memberof NotifierDescriptorSpec + */ + notifierExtName: string; + /** + * + * @type {NotifierSettingRef} + * @memberof NotifierDescriptorSpec + */ + receiverSettingRef?: NotifierSettingRef; + /** + * + * @type {NotifierSettingRef} + * @memberof NotifierDescriptorSpec + */ + senderSettingRef?: NotifierSettingRef; +} diff --git a/console/packages/api-client/src/models/notifier-descriptor.ts b/console/packages/api-client/src/models/notifier-descriptor.ts new file mode 100644 index 0000000000..05647d901d --- /dev/null +++ b/console/packages/api-client/src/models/notifier-descriptor.ts @@ -0,0 +1,52 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +// May contain unused imports in some cases +// @ts-ignore +import { Metadata } from "./metadata"; +// May contain unused imports in some cases +// @ts-ignore +import { NotifierDescriptorSpec } from "./notifier-descriptor-spec"; + +/** + * + * @export + * @interface NotifierDescriptor + */ +export interface NotifierDescriptor { + /** + * + * @type {string} + * @memberof NotifierDescriptor + */ + apiVersion: string; + /** + * + * @type {string} + * @memberof NotifierDescriptor + */ + kind: string; + /** + * + * @type {Metadata} + * @memberof NotifierDescriptor + */ + metadata: Metadata; + /** + * + * @type {NotifierDescriptorSpec} + * @memberof NotifierDescriptor + */ + spec?: NotifierDescriptorSpec; +} diff --git a/console/packages/api-client/src/models/notifier-setting-ref.ts b/console/packages/api-client/src/models/notifier-setting-ref.ts new file mode 100644 index 0000000000..4842a80d12 --- /dev/null +++ b/console/packages/api-client/src/models/notifier-setting-ref.ts @@ -0,0 +1,33 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +/** + * + * @export + * @interface NotifierSettingRef + */ +export interface NotifierSettingRef { + /** + * + * @type {string} + * @memberof NotifierSettingRef + */ + group: string; + /** + * + * @type {string} + * @memberof NotifierSettingRef + */ + name: string; +} diff --git a/console/packages/api-client/src/models/reason-list.ts b/console/packages/api-client/src/models/reason-list.ts new file mode 100644 index 0000000000..966dc1ad96 --- /dev/null +++ b/console/packages/api-client/src/models/reason-list.ts @@ -0,0 +1,79 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +// May contain unused imports in some cases +// @ts-ignore +import { Reason } from "./reason"; + +/** + * + * @export + * @interface ReasonList + */ +export interface ReasonList { + /** + * Indicates whether current page is the first page. + * @type {boolean} + * @memberof ReasonList + */ + first: boolean; + /** + * Indicates whether current page has previous page. + * @type {boolean} + * @memberof ReasonList + */ + hasNext: boolean; + /** + * Indicates whether current page has previous page. + * @type {boolean} + * @memberof ReasonList + */ + hasPrevious: boolean; + /** + * A chunk of items. + * @type {Array} + * @memberof ReasonList + */ + items: Array; + /** + * Indicates whether current page is the last page. + * @type {boolean} + * @memberof ReasonList + */ + last: boolean; + /** + * Page number, starts from 1. If not set or equal to 0, it means no pagination. + * @type {number} + * @memberof ReasonList + */ + page: number; + /** + * Size of each page. If not set or equal to 0, it means no pagination. + * @type {number} + * @memberof ReasonList + */ + size: number; + /** + * Total elements. + * @type {number} + * @memberof ReasonList + */ + total: number; + /** + * Indicates total pages. + * @type {number} + * @memberof ReasonList + */ + totalPages: number; +} diff --git a/console/packages/api-client/src/models/reason-property.ts b/console/packages/api-client/src/models/reason-property.ts new file mode 100644 index 0000000000..b8123c6fc1 --- /dev/null +++ b/console/packages/api-client/src/models/reason-property.ts @@ -0,0 +1,45 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +/** + * + * @export + * @interface ReasonProperty + */ +export interface ReasonProperty { + /** + * + * @type {string} + * @memberof ReasonProperty + */ + description?: string; + /** + * + * @type {string} + * @memberof ReasonProperty + */ + name: string; + /** + * + * @type {boolean} + * @memberof ReasonProperty + */ + optional?: boolean; + /** + * + * @type {string} + * @memberof ReasonProperty + */ + type: string; +} diff --git a/console/packages/api-client/src/models/reason-selector.ts b/console/packages/api-client/src/models/reason-selector.ts new file mode 100644 index 0000000000..9fc079200e --- /dev/null +++ b/console/packages/api-client/src/models/reason-selector.ts @@ -0,0 +1,33 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +/** + * + * @export + * @interface ReasonSelector + */ +export interface ReasonSelector { + /** + * + * @type {string} + * @memberof ReasonSelector + */ + language: string; + /** + * + * @type {string} + * @memberof ReasonSelector + */ + reasonType: string; +} diff --git a/console/packages/api-client/src/models/reason-spec-attributes.ts b/console/packages/api-client/src/models/reason-spec-attributes.ts new file mode 100644 index 0000000000..69e0df65f7 --- /dev/null +++ b/console/packages/api-client/src/models/reason-spec-attributes.ts @@ -0,0 +1,27 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +/** + * Attributes used to transfer data + * @export + * @interface ReasonSpecAttributes + */ +export interface ReasonSpecAttributes { + /** + * + * @type {boolean} + * @memberof ReasonSpecAttributes + */ + empty?: boolean; +} diff --git a/console/packages/api-client/src/models/reason-spec.ts b/console/packages/api-client/src/models/reason-spec.ts new file mode 100644 index 0000000000..136aa8039f --- /dev/null +++ b/console/packages/api-client/src/models/reason-spec.ts @@ -0,0 +1,52 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +// May contain unused imports in some cases +// @ts-ignore +import { ReasonSpecAttributes } from "./reason-spec-attributes"; +// May contain unused imports in some cases +// @ts-ignore +import { ReasonSubject } from "./reason-subject"; + +/** + * + * @export + * @interface ReasonSpec + */ +export interface ReasonSpec { + /** + * + * @type {ReasonSpecAttributes} + * @memberof ReasonSpec + */ + attributes?: ReasonSpecAttributes; + /** + * + * @type {string} + * @memberof ReasonSpec + */ + author: string; + /** + * + * @type {string} + * @memberof ReasonSpec + */ + reasonType: string; + /** + * + * @type {ReasonSubject} + * @memberof ReasonSpec + */ + subject: ReasonSubject; +} diff --git a/console/packages/api-client/src/models/reason-subject.ts b/console/packages/api-client/src/models/reason-subject.ts new file mode 100644 index 0000000000..2955ca7de2 --- /dev/null +++ b/console/packages/api-client/src/models/reason-subject.ts @@ -0,0 +1,51 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +/** + * + * @export + * @interface ReasonSubject + */ +export interface ReasonSubject { + /** + * + * @type {string} + * @memberof ReasonSubject + */ + apiVersion: string; + /** + * + * @type {string} + * @memberof ReasonSubject + */ + kind: string; + /** + * + * @type {string} + * @memberof ReasonSubject + */ + name: string; + /** + * + * @type {string} + * @memberof ReasonSubject + */ + title: string; + /** + * + * @type {string} + * @memberof ReasonSubject + */ + url?: string; +} diff --git a/console/packages/api-client/src/models/reason-type-list.ts b/console/packages/api-client/src/models/reason-type-list.ts new file mode 100644 index 0000000000..c1ed630b2a --- /dev/null +++ b/console/packages/api-client/src/models/reason-type-list.ts @@ -0,0 +1,79 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +// May contain unused imports in some cases +// @ts-ignore +import { ReasonType } from "./reason-type"; + +/** + * + * @export + * @interface ReasonTypeList + */ +export interface ReasonTypeList { + /** + * Indicates whether current page is the first page. + * @type {boolean} + * @memberof ReasonTypeList + */ + first: boolean; + /** + * Indicates whether current page has previous page. + * @type {boolean} + * @memberof ReasonTypeList + */ + hasNext: boolean; + /** + * Indicates whether current page has previous page. + * @type {boolean} + * @memberof ReasonTypeList + */ + hasPrevious: boolean; + /** + * A chunk of items. + * @type {Array} + * @memberof ReasonTypeList + */ + items: Array; + /** + * Indicates whether current page is the last page. + * @type {boolean} + * @memberof ReasonTypeList + */ + last: boolean; + /** + * Page number, starts from 1. If not set or equal to 0, it means no pagination. + * @type {number} + * @memberof ReasonTypeList + */ + page: number; + /** + * Size of each page. If not set or equal to 0, it means no pagination. + * @type {number} + * @memberof ReasonTypeList + */ + size: number; + /** + * Total elements. + * @type {number} + * @memberof ReasonTypeList + */ + total: number; + /** + * Indicates total pages. + * @type {number} + * @memberof ReasonTypeList + */ + totalPages: number; +} diff --git a/console/packages/api-client/src/models/reason-type-spec.ts b/console/packages/api-client/src/models/reason-type-spec.ts new file mode 100644 index 0000000000..c02aace4d2 --- /dev/null +++ b/console/packages/api-client/src/models/reason-type-spec.ts @@ -0,0 +1,43 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +// May contain unused imports in some cases +// @ts-ignore +import { ReasonProperty } from "./reason-property"; + +/** + * + * @export + * @interface ReasonTypeSpec + */ +export interface ReasonTypeSpec { + /** + * + * @type {string} + * @memberof ReasonTypeSpec + */ + description: string; + /** + * + * @type {string} + * @memberof ReasonTypeSpec + */ + displayName: string; + /** + * + * @type {Array} + * @memberof ReasonTypeSpec + */ + properties?: Array; +} diff --git a/console/packages/api-client/src/models/reason-type.ts b/console/packages/api-client/src/models/reason-type.ts new file mode 100644 index 0000000000..c2e7d152f5 --- /dev/null +++ b/console/packages/api-client/src/models/reason-type.ts @@ -0,0 +1,52 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +// May contain unused imports in some cases +// @ts-ignore +import { Metadata } from "./metadata"; +// May contain unused imports in some cases +// @ts-ignore +import { ReasonTypeSpec } from "./reason-type-spec"; + +/** + * + * @export + * @interface ReasonType + */ +export interface ReasonType { + /** + * + * @type {string} + * @memberof ReasonType + */ + apiVersion: string; + /** + * + * @type {string} + * @memberof ReasonType + */ + kind: string; + /** + * + * @type {Metadata} + * @memberof ReasonType + */ + metadata: Metadata; + /** + * + * @type {ReasonTypeSpec} + * @memberof ReasonType + */ + spec?: ReasonTypeSpec; +} diff --git a/console/packages/api-client/src/models/reason.ts b/console/packages/api-client/src/models/reason.ts new file mode 100644 index 0000000000..dd810a5231 --- /dev/null +++ b/console/packages/api-client/src/models/reason.ts @@ -0,0 +1,52 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +// May contain unused imports in some cases +// @ts-ignore +import { Metadata } from "./metadata"; +// May contain unused imports in some cases +// @ts-ignore +import { ReasonSpec } from "./reason-spec"; + +/** + * + * @export + * @interface Reason + */ +export interface Reason { + /** + * + * @type {string} + * @memberof Reason + */ + apiVersion: string; + /** + * + * @type {string} + * @memberof Reason + */ + kind: string; + /** + * + * @type {Metadata} + * @memberof Reason + */ + metadata: Metadata; + /** + * + * @type {ReasonSpec} + * @memberof Reason + */ + spec?: ReasonSpec; +} diff --git a/console/packages/api-client/src/models/subscription-list.ts b/console/packages/api-client/src/models/subscription-list.ts new file mode 100644 index 0000000000..aa3254990f --- /dev/null +++ b/console/packages/api-client/src/models/subscription-list.ts @@ -0,0 +1,79 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +// May contain unused imports in some cases +// @ts-ignore +import { Subscription } from "./subscription"; + +/** + * + * @export + * @interface SubscriptionList + */ +export interface SubscriptionList { + /** + * Indicates whether current page is the first page. + * @type {boolean} + * @memberof SubscriptionList + */ + first: boolean; + /** + * Indicates whether current page has previous page. + * @type {boolean} + * @memberof SubscriptionList + */ + hasNext: boolean; + /** + * Indicates whether current page has previous page. + * @type {boolean} + * @memberof SubscriptionList + */ + hasPrevious: boolean; + /** + * A chunk of items. + * @type {Array} + * @memberof SubscriptionList + */ + items: Array; + /** + * Indicates whether current page is the last page. + * @type {boolean} + * @memberof SubscriptionList + */ + last: boolean; + /** + * Page number, starts from 1. If not set or equal to 0, it means no pagination. + * @type {number} + * @memberof SubscriptionList + */ + page: number; + /** + * Size of each page. If not set or equal to 0, it means no pagination. + * @type {number} + * @memberof SubscriptionList + */ + size: number; + /** + * Total elements. + * @type {number} + * @memberof SubscriptionList + */ + total: number; + /** + * Indicates total pages. + * @type {number} + * @memberof SubscriptionList + */ + totalPages: number; +} diff --git a/console/packages/api-client/src/models/subscription-spec.ts b/console/packages/api-client/src/models/subscription-spec.ts new file mode 100644 index 0000000000..c01667587a --- /dev/null +++ b/console/packages/api-client/src/models/subscription-spec.ts @@ -0,0 +1,52 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +// May contain unused imports in some cases +// @ts-ignore +import { InterestReason } from "./interest-reason"; +// May contain unused imports in some cases +// @ts-ignore +import { SubscriptionSubscriber } from "./subscription-subscriber"; + +/** + * + * @export + * @interface SubscriptionSpec + */ +export interface SubscriptionSpec { + /** + * Perhaps users need to unsubscribe and interact without receiving notifications again + * @type {boolean} + * @memberof SubscriptionSpec + */ + disabled?: boolean; + /** + * + * @type {InterestReason} + * @memberof SubscriptionSpec + */ + reason: InterestReason; + /** + * + * @type {SubscriptionSubscriber} + * @memberof SubscriptionSpec + */ + subscriber: SubscriptionSubscriber; + /** + * The token to unsubscribe + * @type {string} + * @memberof SubscriptionSpec + */ + unsubscribeToken: string; +} diff --git a/console/packages/api-client/src/models/subscription-subscriber.ts b/console/packages/api-client/src/models/subscription-subscriber.ts new file mode 100644 index 0000000000..af19db6929 --- /dev/null +++ b/console/packages/api-client/src/models/subscription-subscriber.ts @@ -0,0 +1,27 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +/** + * The subscriber to be notified + * @export + * @interface SubscriptionSubscriber + */ +export interface SubscriptionSubscriber { + /** + * + * @type {string} + * @memberof SubscriptionSubscriber + */ + name?: string; +} diff --git a/console/packages/api-client/src/models/subscription.ts b/console/packages/api-client/src/models/subscription.ts new file mode 100644 index 0000000000..7069662758 --- /dev/null +++ b/console/packages/api-client/src/models/subscription.ts @@ -0,0 +1,52 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +// May contain unused imports in some cases +// @ts-ignore +import { Metadata } from "./metadata"; +// May contain unused imports in some cases +// @ts-ignore +import { SubscriptionSpec } from "./subscription-spec"; + +/** + * + * @export + * @interface Subscription + */ +export interface Subscription { + /** + * + * @type {string} + * @memberof Subscription + */ + apiVersion: string; + /** + * + * @type {string} + * @memberof Subscription + */ + kind: string; + /** + * + * @type {Metadata} + * @memberof Subscription + */ + metadata: Metadata; + /** + * + * @type {SubscriptionSpec} + * @memberof Subscription + */ + spec?: SubscriptionSpec; +} diff --git a/console/packages/api-client/src/models/template-content.ts b/console/packages/api-client/src/models/template-content.ts new file mode 100644 index 0000000000..6f82baa83a --- /dev/null +++ b/console/packages/api-client/src/models/template-content.ts @@ -0,0 +1,39 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * Halo Next API + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: 2.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +/** + * + * @export + * @interface TemplateContent + */ +export interface TemplateContent { + /** + * + * @type {string} + * @memberof TemplateContent + */ + htmlBody?: string; + /** + * + * @type {string} + * @memberof TemplateContent + */ + rawBody?: string; + /** + * + * @type {string} + * @memberof TemplateContent + */ + title: string; +} diff --git a/console/packages/components/src/icons/icons.ts b/console/packages/components/src/icons/icons.ts index 2dff29dc2a..d7ca48dd70 100644 --- a/console/packages/components/src/icons/icons.ts +++ b/console/packages/components/src/icons/icons.ts @@ -66,6 +66,7 @@ import IconZoomOutLine from "~icons/ri/zoom-out-line"; import IconArrowLeftRightLine from "~icons/ri/arrow-left-right-line"; import IconArrowUpDownLine from "~icons/ri/arrow-up-down-line"; import IconRiUpload2Fill from "~icons/ri/upload-2-fill"; +import IconNotificationBadgeLine from "~icons/ri/notification-badge-line"; export { IconDashboard, @@ -136,4 +137,5 @@ export { IconArrowLeftRightLine, IconArrowUpDownLine, IconRiUpload2Fill, + IconNotificationBadgeLine, }; diff --git a/console/src/layouts/BasicLayout.vue b/console/src/layouts/BasicLayout.vue index 4aa16ffb0e..6d26ba1c99 100644 --- a/console/src/layouts/BasicLayout.vue +++ b/console/src/layouts/BasicLayout.vue @@ -302,6 +302,15 @@ onMounted(() => { > {{ $t("core.sidebar.operations.profile.button") }} + + {{ $t("core.sidebar.operations.notifications.button") }} + {{ $t("core.sidebar.operations.logout.button") }} diff --git a/console/src/locales/en.yaml b/console/src/locales/en.yaml index 4879d9b7bc..8d655d985a 100644 --- a/console/src/locales/en.yaml +++ b/console/src/locales/en.yaml @@ -79,6 +79,8 @@ core: button: Profile visit_homepage: title: Visit homepage + notifications: + button: Notifications dashboard: title: Dashboard actions: @@ -987,6 +989,18 @@ core: website: Website help_page: Help page authentication_url: Login URL + notification: + title: Notifications + tabs: + unread: Unread + read: Read + empty: + titles: + unread: No unread notifications + read: No read notifications + operations: + mark_as_read: + button: Mark as read setting: title: Settings actuator: @@ -1258,6 +1272,7 @@ core: submit: Submit detail: Detail select: Select + view_all: View all radio: "yes": Yes "no": No diff --git a/console/src/locales/zh-CN.yaml b/console/src/locales/zh-CN.yaml index d11b0eefd8..2a3f9895d4 100644 --- a/console/src/locales/zh-CN.yaml +++ b/console/src/locales/zh-CN.yaml @@ -79,6 +79,8 @@ core: button: 个人资料 visit_homepage: title: 访问首页 + notifications: + button: 我的消息 dashboard: title: 仪表板 actions: @@ -987,6 +989,18 @@ core: website: 网站 help_page: 帮助页面 authentication_url: 登录入口 + notification: + title: 消息 + tabs: + unread: 未读 + read: 已读 + empty: + titles: + unread: 当前没有未读的消息 + read: 当前没有已读的消息 + operations: + mark_as_read: + button: 标记为已读 setting: title: 设置 actuator: @@ -1258,6 +1272,7 @@ core: submit: 提交 detail: 详情 select: 选择 + view_all: 查看全部 radio: "yes": 是 "no": 否 diff --git a/console/src/locales/zh-TW.yaml b/console/src/locales/zh-TW.yaml index ccd6695eda..0029781fab 100644 --- a/console/src/locales/zh-TW.yaml +++ b/console/src/locales/zh-TW.yaml @@ -79,6 +79,8 @@ core: button: 個人資料 visit_homepage: title: 訪問首頁 + notifications: + button: 我的訊息 dashboard: title: 儀表板 actions: @@ -987,6 +989,18 @@ core: website: 網站 help_page: 幫助頁面 authentication_url: 登入入口 + notification: + title: 訊息 + tabs: + unread: 未讀 + read: 已讀 + empty: + titles: + unread: 目前沒有未讀的訊息 + read: 目前沒有已讀的訊息 + operations: + mark_as_read: + button: 標記為已讀 setting: title: 設置 actuator: @@ -1258,6 +1272,7 @@ core: submit: 提交 detail: 詳情 select: 選擇 + view_all: 查看全部 radio: "yes": 是 "no": 否 diff --git a/console/src/modules/dashboard/Dashboard.vue b/console/src/modules/dashboard/Dashboard.vue index 00d867c5b6..727b483691 100644 --- a/console/src/modules/dashboard/Dashboard.vue +++ b/console/src/modules/dashboard/Dashboard.vue @@ -187,6 +187,7 @@ const widgetsGroup = [ widgets: [ { x: 0, y: 0, w: 3, h: 3, i: 0, widget: "ViewsStatsWidget" }, { x: 0, y: 0, w: 6, h: 10, i: 1, widget: "QuickLinkWidget" }, + { x: 0, y: 0, w: 6, h: 10, i: 2, widget: "NotificationWidget" }, ], }, ]; @@ -242,8 +243,8 @@ const layout = useStorage("widgets", [ w: 6, h: 12, i: 5, - widget: "RecentPublishedWidget", - permissions: ["system:posts:view"], + widget: "NotificationWidget", + permissions: [], }, ]); diff --git a/console/src/modules/index.ts b/console/src/modules/index.ts index c770ff10c2..c3f5a66a36 100644 --- a/console/src/modules/index.ts +++ b/console/src/modules/index.ts @@ -1,3 +1,4 @@ +// fixme: add supports for auto import import dashboardModule from "./dashboard/module"; import postModule from "./contents/posts/module"; import pageModule from "./contents/pages/module"; diff --git a/console/src/modules/system/settings/SystemSettings.vue b/console/src/modules/system/settings/SystemSettings.vue index 91e3486a02..6c285ff3bb 100644 --- a/console/src/modules/system/settings/SystemSettings.vue +++ b/console/src/modules/system/settings/SystemSettings.vue @@ -18,6 +18,7 @@ import type { Component } from "vue"; import { markRaw } from "vue"; import SettingTab from "./tabs/Setting.vue"; import { useRouteQuery } from "@vueuse/router"; +import NotificationsTab from "./tabs/Notifications.vue"; const { t } = useI18n(); @@ -60,6 +61,13 @@ const { data: setting } = useQuery({ if (!activeTab.value) { activeTab.value = tabs.value[0].id; } + + // TODO: use integrations center to refactor this + tabs.value.push({ + id: "notification", + label: "通知设置", + component: markRaw(NotificationsTab), + }); } }, }); diff --git a/console/src/modules/system/settings/tabs/NotificationSetting.vue b/console/src/modules/system/settings/tabs/NotificationSetting.vue new file mode 100644 index 0000000000..30a0778d8f --- /dev/null +++ b/console/src/modules/system/settings/tabs/NotificationSetting.vue @@ -0,0 +1,102 @@ + + + diff --git a/console/src/modules/system/settings/tabs/Notifications.vue b/console/src/modules/system/settings/tabs/Notifications.vue new file mode 100644 index 0000000000..ebbe7b5392 --- /dev/null +++ b/console/src/modules/system/settings/tabs/Notifications.vue @@ -0,0 +1,74 @@ + + + diff --git a/console/src/modules/system/users/Notifications.vue b/console/src/modules/system/users/Notifications.vue new file mode 100644 index 0000000000..33374915f2 --- /dev/null +++ b/console/src/modules/system/users/Notifications.vue @@ -0,0 +1,129 @@ + + + diff --git a/console/src/modules/system/users/components/NotificationContent.vue b/console/src/modules/system/users/components/NotificationContent.vue new file mode 100644 index 0000000000..562a7cb901 --- /dev/null +++ b/console/src/modules/system/users/components/NotificationContent.vue @@ -0,0 +1,43 @@ + + + diff --git a/console/src/modules/system/users/components/NotificationListItem.vue b/console/src/modules/system/users/components/NotificationListItem.vue new file mode 100644 index 0000000000..ff7a633d3a --- /dev/null +++ b/console/src/modules/system/users/components/NotificationListItem.vue @@ -0,0 +1,91 @@ + + diff --git a/console/src/modules/system/users/module.ts b/console/src/modules/system/users/module.ts index d437af010b..9b0fa9f1b0 100644 --- a/console/src/modules/system/users/module.ts +++ b/console/src/modules/system/users/module.ts @@ -8,10 +8,13 @@ import Login from "./Login.vue"; import { IconUserSettings } from "@halo-dev/components"; import { markRaw } from "vue"; import Binding from "./Binding.vue"; +import Notifications from "./Notifications.vue"; +import NotificationWidget from "./widgets/NotificationWidget.vue"; export default definePlugin({ components: { UserStatsWidget, + NotificationWidget, }, routes: [ { @@ -72,6 +75,20 @@ export default definePlugin({ }, ], }, + { + path: "-/notifications", + component: BasicLayout, + children: [ + { + path: "", + name: "UserNotifications", + component: Notifications, + meta: { + title: "core.notification.title", + }, + }, + ], + }, ], }, ], diff --git a/console/src/modules/system/users/widgets/NotificationWidget.vue b/console/src/modules/system/users/widgets/NotificationWidget.vue new file mode 100644 index 0000000000..a054d012ca --- /dev/null +++ b/console/src/modules/system/users/widgets/NotificationWidget.vue @@ -0,0 +1,104 @@ + + + diff --git a/console/src/utils/api-client.ts b/console/src/utils/api-client.ts index 0a709fe1c9..fbcd484868 100644 --- a/console/src/utils/api-client.ts +++ b/console/src/utils/api-client.ts @@ -11,6 +11,8 @@ import { ApiConsoleHaloRunV1alpha1IndicesApi, ApiConsoleHaloRunV1alpha1AuthProviderApi, ApiConsoleHaloRunV1alpha1SystemApi, + ApiConsoleHaloRunV1alpha1NotifierApi, + ApiNotificationHaloRunV1alpha1NotificationApi, ContentHaloRunV1alpha1CategoryApi, ContentHaloRunV1alpha1CommentApi, ContentHaloRunV1alpha1PostApi, @@ -40,6 +42,7 @@ import { ApiHaloRunV1alpha1UserApi, MigrationHaloRunV1alpha1BackupApi, ApiConsoleMigrationHaloRunV1alpha1MigrationApi, + NotificationHaloRunV1alpha1NotifierDescriptorApi, ApiSecurityHaloRunV1alpha1PersonalAccessTokenApi, SecurityHaloRunV1alpha1PersonalAccessTokenApi, } from "@halo-dev/api-client"; @@ -180,6 +183,11 @@ function setupApiClient(axios: AxiosInstance) { axios ), backup: new MigrationHaloRunV1alpha1BackupApi(undefined, baseURL, axios), + notifierDescriptors: new NotificationHaloRunV1alpha1NotifierDescriptorApi( + undefined, + baseURL, + axios + ), pat: new SecurityHaloRunV1alpha1PersonalAccessTokenApi( undefined, baseURL, @@ -221,6 +229,16 @@ function setupApiClient(axios: AxiosInstance) { axios ), system: new ApiConsoleHaloRunV1alpha1SystemApi(undefined, baseURL, axios), + notifier: new ApiConsoleHaloRunV1alpha1NotifierApi( + undefined, + baseURL, + axios + ), + notification: new ApiNotificationHaloRunV1alpha1NotificationApi( + undefined, + baseURL, + axios + ), pat: new ApiSecurityHaloRunV1alpha1PersonalAccessTokenApi( undefined, baseURL, diff --git a/docs/notification/README.md b/docs/notification/README.md new file mode 100644 index 0000000000..f672332e99 --- /dev/null +++ b/docs/notification/README.md @@ -0,0 +1,346 @@ +## 背景 + +在 Halo 系统中,具有用户协作属性,如当用户发布文章后被访客评论而访客希望在作者回复评论时被提醒以此完成进一步互动,而在没有通知功能的情况下无法满足诸如以下描述的使用场景: + +1. 访客只能在评论后一段时间内访问被评论的文章查看是否被回复。 +2. Halo 的用户注册功能无法让用户验证邮箱地址让恶意注册变的更容易。 + +在这些场景下,为了让用户收到通知或验证消息以及管理和处理这些通知,我们需要设计一个通知功能,以实现根据用户的订阅和偏好推送通知并管理通知。 + +## 已有需求 + +- 访客评论文章后希望收到被回复的通知,而文章作者也希望收到文章被评论的通知。 +- 用户注册功能希望验证注册者填写的邮箱实现一个邮箱只能注册一个账号,防止占用别人邮箱,在一定程度上减少恶意注册问题。 +- 关于应用市场插件,管理员希望在用户下单后能收到新订单通知。 +- 付费订阅插件场景,希望给付费订阅用户推送付费文章的浏览链接。 + +## 目标 + +设计一个通知功能,可以根据以下目标,实现订阅和推送通知: + +- 支持扩展多种通知方式,例如邮件、短信、Slack 等。 +- 支持通知条件并可扩展,例如 Halo 有新文章发布事件如果用户订阅了新文章发布事件但付费订阅插件决定了此文章只有付费用户才可收到通知、按照付费等级不同决定是否发送新文章通知给对应用户等需要通过实现通知条件的扩展点来满足对应需求。 +- 支持定制化选项,例如是否开启通知、通知时段等。 +- 支持通知流程,例如通知的发送、接收、查看、标记等。 +- 通知内容支持多语言。 +- 事件类型可扩展,插件可能需要定义自己的事件以通知到订阅事件的用户,如应用市场插件。 + +## 非目标 + +- Halo 只会实现站内消息和邮件通知,更多通知方式需要插件去扩展。 +- 定时通知、通知频率或摘要通知功能属于非必要功能,可由插件去扩展。 +- 多语言支持,目前只会支持中文和英文两种,更多语言支持不是此阶段的目标。 +- 可定制的通知模板:通知默认模板由事件定义者提供,如需修改可考虑使用特定的 Notifier 去适配事件。 + +## 方案 + +为了实现上述目标,我们设计了以下方案: + +### 通知数据模型 + +#### 通知事件类别和事件 + +首先通过定义事件来声明此通知事件包含的数据和发送此事件时默认使用的模板。 + +`ReasonType` 是一个自定义模型,用于定义事件类别,一个事件类别由多个事件表示。 + +```yaml +apiVersion: notification.halo.run/v1alpha1 +kind: ReasonType +metadata: + name: comment +spec: + displayName: "Comment Received" + description: "The user has received a comment on an post." + properties: + - name: postName + type: string + description: "The name of the post." + optional: false + - name: postTitle + type: string + optional: true + - name: commenter + type: string + description: "The email address of the user who has left the comment." + optional: false + - name: comment + type: string + description: "The content of the comment." + optional: false +``` + +`Reason` 是一个自定义模型,用于定义通知原因,它属于 `ReasonType` 的实例。 + +当有事件触发时,创建 `Reason` 资源来触发通知,如当文章收到一个新评论时: + +```yaml +apiVersion: notification.halo.run/v1alpha1 +kind: Reason +metadata: + name: comment-axgu +spec: + # a name of ReasonType + reasonType: comment + author: 'guqing' + subject: + apiVersion: 'content.halo.run/v1alpha1' + kind: Post + name: 'post-axgu' + title: 'Hello World' + url: 'https://guqing.xyz/archives/1' + attributes: + postName: "post-fadp" + commenter: "guqing" + comment: "Hello! This is your first notification." +``` + +#### Subscription + +`Subscription` 自定义模型,定义了特定事件时与要被通知的订阅者之间的关系, 其中 `subscriber` 表示订阅者用户, `unsubscribeToken` 表示退订时的身份验证 token, `reason` 订阅者感兴趣的事件。 + +用户可以通过 `Subscription` 来订阅自己感兴趣的事件,当事件触发时会收到通知: + +```yaml +apiVersion: notification.halo.run/v1alpha1 +kind: Subscription +metadata: + name: user-a-sub +spec: + subscriber: + name: guqing + unsubscribeToken: xxxxxxxxxxxx + reason: + reasonType: new-comment-on-post + subject: + apiVersion: content.halo.run/v1alpha1 + kind: Post + name: 'post-axgu' +``` + +订阅退订链接 API 规则:`/apis/api.notification.halo.run/v1alpha1/subscriptions/{name}/unsubscribe?token={unsubscribeToken}`。 + +#### 用户通知偏好设置 + +通过在用户偏好设置的 ConfigMap 中存储一个 `notification` key 用于保存事件类型与通知方式的关系设置,当用户订阅了如 'new-comment-on-post' 事件时会获取对应的通知方式来给用户发送通知。 + +```yaml +apiVersion: v1alpha1 +kind: ConfigMap +metadata: + name: user-preferences-guqing +data: + notification: | + { + reasonTypeNotification: { + 'new-comment-on-post': { + enabled: true, + notifiers: [ + email-notifier, + sms-notifier + ] + }, + new-post: { + enabled: true, + notifiers: [ + email-notifier, + webhook-router-notifier + ] + } + }, + } +``` + +#### Notification 站内通知 + +当用户订阅到事件后会创建 `Notification`, 它与通知方式(notifier)无关,`recipient` 为用户名,类似站内通知,如用户 `guqing` 订阅了评论事件那么当监听到评论事件时会创建一条记录可以在个人中心的通知列表看到一条通知消息。 + +```yaml +apiVersion: notification.halo.run/v1alpha1 +kind: Notification +metadata: + name: notification-abc +spec: + # username + recipient: "guqing" + reason: 'comment-axgu' + title: 'notification-title' + rawContent: 'notification-raw-body' + htmlContent: 'notification-html' + unread: true + lastReadAt: '2023-08-04T17:01:45Z' +``` + +个人中心通知自定义 APIs: + +1. 获取个人中心获取用户通知列表的 APIs 规则: + `GET /apis/api.notification.halo.run/v1alpha1/userspaces/{username}/notifications` +2. 将通知标记为已读:`PUT /apis/api.notification.halo.run/v1alpha1/userspaces/{username}/notifications/mark-as-read` +3. +批量将通知标记为已读:`PUT /apis/api.notification.halo.run/v1alpha1/userspaces/{username}/notifications/mark-specified-as-read` + +#### 通知模板 + +`NotificationTemplate` 自定义模型用于定义事件的通知模板,当事件触发时会根据事件的通知模板来渲染通知内容。 +它通过定义 `reasonSelector` 来引用事件类别,当事件触发时会根据用户的语言偏好和触发事件的类别来选择一个最佳的通知模板。 +选择通知模板的规则为: + +1. 根据用户设置的语言,选择从通知模板中定义的 `spec.reasonSelector.language` 的值从更具体到不太具体的顺序(例如,gl_ES 的值将比 gl 的值具有更高的优先级)。 +2. 当通过语言成功匹配到模板时,匹配到的结果可能不止一个,如 `language` 为 `zh_CN` 的模板有三个那么会根据 `NotificationTemplate` 的 `metadata.creationTimestamp` 字段来选择一个最新的模板。 + +这样的规则有助于用户可以个性化定制某些事件的模板内容。 + +模板语法使用 ThymeleafEngine 渲染,纯文本模板使用 `textual` 模板模式,语法参考: [usingthymeleaf.html#textual-syntax](https://www.thymeleaf.org/doc/tutorials/3.1/usingthymeleaf.html#textual-syntax) + +`HTML` 则使用标准表达式语法在标签属性中取值,语法参考:[standard-expression-syntax](https://www.thymeleaf.org/doc/tutorials/3.1/usingthymeleaf.html#standard-expression-syntax) + +在通知中心渲染模板时会在 `ReasonAttributes` 中提供额外属性包括: + +- site.title: 站点标题 +- site.subtitle: 站点副标题 +- site.logo: 站点 LOGO +- site.url: 站点访问地址 +- subscriber.id: 如果是用户则为用户名, 如果是匿名用户则为 `annoymousUser#email` +- subscriber.displayName: 邮箱地址或`@username` +- unsubscribeUrl: 退订链接,用于取消订阅 + +因此,任何模板都可以使用这几个属性,但事件定义者需要注意避免使用这些保留属性。 + +```yaml +apiVersion: notification.halo.run/v1alpha1 +kind: NotificationTemplate +metadata: + name: template-new-comment-on-post +spec: + reasonSelector: + reasonType: new-comment-on-post + language: zh_CN + template: + title: "你的文章 [(${postTitle})] 收到了一条新评论" + body: | + [(${commenter})] 评论了你的文章 [(${postTitle})],内容如下: + [(${comment})] +``` + +#### 通知器声明及扩展 + +`NotifierDescriptor` 自定义模型用于声明通知器,通过它来描述通知器的名称、描述和关联的 `ExtensionDefinition` 名称,让用户可以在用户界面知道通知器是什么以及它可以做什么, +还让 NotificationCenter 知道如何加载通知器和准备通知器需要的设置以发送通知。 + +```yaml +apiVersion: notification.halo.run/v1alpha1 +kind: NotifierDescriptor +metadata: + name: email-notifier +spec: + displayName: '邮件通知器' + description: '支持通过邮件的方式发送通知。' + notifierExtName: '通知对应的扩展名称' + senderSettingRef: + name: 'email-notifier' + group: 'sender' + receiverSettingRef: + name: 'email-notifier' + group: 'receiver' +``` + +通知器声明了 senderSettingRef 和 receiverSettingRef 后,对应用户端可以通过以下 APIs 获取和保存配置: + +管理员获取和保存通知器发送配置的 APIs: + +1. 获取通知器发送方配置:`GET /apis/api.console.halo.run/v1alpha1/notifiers/{name}/sender-config` +2. 保存通知器发送方配置:`POST /apis/api.console.halo.run/v1alpha1/notifiers/{name}/sender-config` + +个人中心用户获取和保存对应通知器接收消息配置的 APIs: + +1. 获取通知器接收消息配置:`GET /apis/api.notification.halo.run/v1alpha1/notifiers/{name}/receiver-config` +2. 获取通知器接收消息配置:`POST /apis/api.notification.halo.run/v1alpha1/notifiers/{name}/receiver-config` + +通知器扩展点用于实现发送通知的方式: + +```java +public interface ReactiveNotifier extends ExtensionPoint { + + /** + * Notify user. + * + * @param context notification context must not be null + */ + Mono notify(NotificationContext context); +} + +@Data +public class NotificationContext { + private Message message; + + private ObjectNode receiverConfig; + + private ObjectNode senderConfig; + + @Data + static class Message { + private MessagePayload payload; + + private Subject subject; + + private String recipient; + + private Instant timestamp; + } + + @Data + public static class Subject { + private String apiVersion; + private String kind; + private String name; + private String title; + private String url; + } + + @Data + static class MessagePayload { + private String title; + + private String rawBody; + + private String htmlBody; + + private ReasonAttributes attributes; + } +} +``` + +通知数据结构交互图 + +![Notification datastructures interaction](./image-knhw.png) + +通知功能 UI 设计 +![notification-ui.png](notification-ui.png) + +### 通知模块功能 + +- 发送通知:当触发通知事件时,系统会根据 subscriber 的偏好设置获取到事件对应的通知方式再根据偏好设置自动发送通知。 +- 接收通知:用户可以选择接收通知的方式,例如邮件、短信、自定义路由通知等。 +- 查看通知:用户可以在 Halo 中查看所有的通知,包括已读和未读的通知。 +- 标记通知:用户可以标记通知为已读或未读状态,以便更好地管理和处理通知。 + +### 通知管理列表条件筛选 + +我们支持以下通知条件筛选策略: + +- 按事件类型:列出特定类型的事件通知,例如新文章,新评论、状态更新等。 +- 按已读状态:根据通知是否已读列出,方便用户查看未读通知。 +- 按关键词:列出通知中包含特定关键词的事件通知,例如包含用户名称、标题等关键词的通知。 +- 按时间:列出在特定时间段内发生的事件通知,例如最近一周、最近一个月等时间段内的通知。 + +### 定制化选项 + +如果后续有足够的使用场景,可以考虑支持以下定制化选项: + +- 通知时间段:用户可以设置通知的时间段,例如只在工作时间内推送通知。 +- 通知频率:用户可以设置通知的频率,例如每天、每周、每月等。 +- 摘要通知:用户可以设置接收每周摘要,总结一周内的通知合并为一条通知并通过如邮件等方式接收。 + +## 结论 + +通过以上方案和实现,我们设计了一个通知功能,可以根据用户的需求和偏好,自动筛选和推送通知。同时,为了支持更多的事件类型、通知方式和通知条件筛选策略,系统具有良好的可扩展性。 diff --git a/docs/notification/image-knhw.png b/docs/notification/image-knhw.png new file mode 100644 index 0000000000..821c5d84a6 Binary files /dev/null and b/docs/notification/image-knhw.png differ diff --git a/docs/notification/notification-ui.png b/docs/notification/notification-ui.png new file mode 100644 index 0000000000..76b01c3aec Binary files /dev/null and b/docs/notification/notification-ui.png differ