mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
feature: webhooks (#8674)
* api,server,ui: weebhoks feature Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * fix Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * fix Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * changes Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * registry of message busses * test bus Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * refactor Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * test Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * fix and refactor Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * changes for webhook dispatch history Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * changes, initial ui Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * improvements Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * changes for account webhook cleanup Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * fix remaining event bus usage Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * changes for testing webhook dispatch Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * wip Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * fix test Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * make element Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * missing Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * buid fix * fix lint Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * changes for project delete check Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * fix Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * add collapse in create Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * ui fix and refactor for eventditributor publish Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * update org.json and add json validation Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * schema fixes Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * wordings Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * ui: improve progress button Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * ui improvements Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * remove unrelated change Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * search and count Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * add payloadurl in info Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * positive progress Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * fix hmac key Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * create webhook form fixes Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * refactor, address feedback Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * indentation Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * fix filters Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * remove test eventbus Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * default scope be Local Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * add lifecycle smoke test Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * add test for webhook deliveries Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * refactor Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * fix lint Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * refactor - losgs and others Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * unit tests Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * fix lint Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * build fix Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * smoke test fix, log refactor Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * get bean from all components Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * ui: missing label Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * address review comments Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * add some more tests Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * lint Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * rename setting Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * upgrade: move 4.19.0->4.20.0 to 4.19.1->4.20.0 * fix test delivery layout Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * fix webhook secret display Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * add http to payloadurl when no scheme Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * allow removing secretkey for webhook Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * fix update sslverification Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * disallow same payload url for same account Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * fix delivery with url w/o scheme Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * api: listApis should return params based on caller Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * wip changes Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * Update engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql * remove unique constraint for now Constraint is present in Java code validations Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * fixes Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * ui: add option to delete multiple deliveries Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * add filter for deliveries, delete api start/endtime support Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * do not throw error when no deliveries removed Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * ui: fix deliveries table column sorting, time filter cancel Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> * remove isDebugEnabled wrapping * merge fix Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> --------- Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com> Co-authored-by: Daan Hoogland <daan@onecht.net> Co-authored-by: Wei Zhou <weizhou@apache.org>
This commit is contained in:
parent
2542582c1e
commit
be552fdce9
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@ -86,7 +86,8 @@ jobs:
|
|||||||
smoke/test_migration
|
smoke/test_migration
|
||||||
smoke/test_multipleips_per_nic
|
smoke/test_multipleips_per_nic
|
||||||
smoke/test_nested_virtualization
|
smoke/test_nested_virtualization
|
||||||
smoke/test_set_sourcenat",
|
smoke/test_set_sourcenat
|
||||||
|
smoke/test_webhook_lifecycle",
|
||||||
"smoke/test_network
|
"smoke/test_network
|
||||||
smoke/test_network_acl
|
smoke/test_network_acl
|
||||||
smoke/test_network_ipv6
|
smoke/test_network_ipv6
|
||||||
|
|||||||
@ -175,6 +175,8 @@ public class ApiConstants {
|
|||||||
public static final String END_IPV6 = "endipv6";
|
public static final String END_IPV6 = "endipv6";
|
||||||
public static final String END_PORT = "endport";
|
public static final String END_PORT = "endport";
|
||||||
public static final String ENTRY_TIME = "entrytime";
|
public static final String ENTRY_TIME = "entrytime";
|
||||||
|
public static final String EVENT_ID = "eventid";
|
||||||
|
public static final String EVENT_TYPE = "eventtype";
|
||||||
public static final String EXPIRES = "expires";
|
public static final String EXPIRES = "expires";
|
||||||
public static final String EXTRA_CONFIG = "extraconfig";
|
public static final String EXTRA_CONFIG = "extraconfig";
|
||||||
public static final String EXTRA_DHCP_OPTION = "extradhcpoption";
|
public static final String EXTRA_DHCP_OPTION = "extradhcpoption";
|
||||||
@ -209,6 +211,7 @@ public class ApiConstants {
|
|||||||
public static final String HA_PROVIDER = "haprovider";
|
public static final String HA_PROVIDER = "haprovider";
|
||||||
public static final String HA_STATE = "hastate";
|
public static final String HA_STATE = "hastate";
|
||||||
public static final String HEALTH = "health";
|
public static final String HEALTH = "health";
|
||||||
|
public static final String HEADERS = "headers";
|
||||||
public static final String HIDE_IP_ADDRESS_USAGE = "hideipaddressusage";
|
public static final String HIDE_IP_ADDRESS_USAGE = "hideipaddressusage";
|
||||||
public static final String HOST_ID = "hostid";
|
public static final String HOST_ID = "hostid";
|
||||||
public static final String HOST_IDS = "hostids";
|
public static final String HOST_IDS = "hostids";
|
||||||
@ -281,6 +284,7 @@ public class ApiConstants {
|
|||||||
public static final String JOB_STATUS = "jobstatus";
|
public static final String JOB_STATUS = "jobstatus";
|
||||||
public static final String KEEPALIVE_ENABLED = "keepaliveenabled";
|
public static final String KEEPALIVE_ENABLED = "keepaliveenabled";
|
||||||
public static final String KERNEL_VERSION = "kernelversion";
|
public static final String KERNEL_VERSION = "kernelversion";
|
||||||
|
public static final String KEY = "key";
|
||||||
public static final String LABEL = "label";
|
public static final String LABEL = "label";
|
||||||
public static final String LASTNAME = "lastname";
|
public static final String LASTNAME = "lastname";
|
||||||
public static final String LAST_BOOT = "lastboottime";
|
public static final String LAST_BOOT = "lastboottime";
|
||||||
@ -355,6 +359,7 @@ public class ApiConstants {
|
|||||||
public static final String SSHKEY_ENABLED = "sshkeyenabled";
|
public static final String SSHKEY_ENABLED = "sshkeyenabled";
|
||||||
public static final String PATH = "path";
|
public static final String PATH = "path";
|
||||||
public static final String PAYLOAD = "payload";
|
public static final String PAYLOAD = "payload";
|
||||||
|
public static final String PAYLOAD_URL = "payloadurl";
|
||||||
public static final String POD_ID = "podid";
|
public static final String POD_ID = "podid";
|
||||||
public static final String POD_NAME = "podname";
|
public static final String POD_NAME = "podname";
|
||||||
public static final String POD_IDS = "podids";
|
public static final String POD_IDS = "podids";
|
||||||
@ -400,11 +405,9 @@ public class ApiConstants {
|
|||||||
public static final String QUERY_FILTER = "queryfilter";
|
public static final String QUERY_FILTER = "queryfilter";
|
||||||
public static final String SCHEDULE = "schedule";
|
public static final String SCHEDULE = "schedule";
|
||||||
public static final String SCOPE = "scope";
|
public static final String SCOPE = "scope";
|
||||||
public static final String SECRET_KEY = "usersecretkey";
|
|
||||||
public static final String SECONDARY_IP = "secondaryip";
|
|
||||||
public static final String SINCE = "since";
|
|
||||||
public static final String KEY = "key";
|
|
||||||
public static final String SEARCH_BASE = "searchbase";
|
public static final String SEARCH_BASE = "searchbase";
|
||||||
|
public static final String SECONDARY_IP = "secondaryip";
|
||||||
|
public static final String SECRET_KEY = "secretkey";
|
||||||
public static final String SECURITY_GROUP_IDS = "securitygroupids";
|
public static final String SECURITY_GROUP_IDS = "securitygroupids";
|
||||||
public static final String SECURITY_GROUP_NAMES = "securitygroupnames";
|
public static final String SECURITY_GROUP_NAMES = "securitygroupnames";
|
||||||
public static final String SECURITY_GROUP_NAME = "securitygroupname";
|
public static final String SECURITY_GROUP_NAME = "securitygroupname";
|
||||||
@ -422,6 +425,7 @@ public class ApiConstants {
|
|||||||
public static final String SHOW_UNIQUE = "showunique";
|
public static final String SHOW_UNIQUE = "showunique";
|
||||||
public static final String SIGNATURE = "signature";
|
public static final String SIGNATURE = "signature";
|
||||||
public static final String SIGNATURE_VERSION = "signatureversion";
|
public static final String SIGNATURE_VERSION = "signatureversion";
|
||||||
|
public static final String SINCE = "since";
|
||||||
public static final String SIZE = "size";
|
public static final String SIZE = "size";
|
||||||
public static final String SNAPSHOT = "snapshot";
|
public static final String SNAPSHOT = "snapshot";
|
||||||
public static final String SNAPSHOT_ID = "snapshotid";
|
public static final String SNAPSHOT_ID = "snapshotid";
|
||||||
@ -429,8 +433,7 @@ public class ApiConstants {
|
|||||||
public static final String SNAPSHOT_TYPE = "snapshottype";
|
public static final String SNAPSHOT_TYPE = "snapshottype";
|
||||||
public static final String SNAPSHOT_QUIESCEVM = "quiescevm";
|
public static final String SNAPSHOT_QUIESCEVM = "quiescevm";
|
||||||
public static final String SOURCE_ZONE_ID = "sourcezoneid";
|
public static final String SOURCE_ZONE_ID = "sourcezoneid";
|
||||||
public static final String SUITABLE_FOR_VM = "suitableforvirtualmachine";
|
public static final String SSL_VERIFICATION = "sslverification";
|
||||||
public static final String SUPPORTS_STORAGE_SNAPSHOT = "supportsstoragesnapshot";
|
|
||||||
public static final String START_DATE = "startdate";
|
public static final String START_DATE = "startdate";
|
||||||
public static final String START_ID = "startid";
|
public static final String START_ID = "startid";
|
||||||
public static final String START_IP = "startip";
|
public static final String START_IP = "startip";
|
||||||
@ -449,6 +452,9 @@ public class ApiConstants {
|
|||||||
public static final String SYSTEM_VM_TYPE = "systemvmtype";
|
public static final String SYSTEM_VM_TYPE = "systemvmtype";
|
||||||
public static final String TAGS = "tags";
|
public static final String TAGS = "tags";
|
||||||
public static final String STORAGE_TAGS = "storagetags";
|
public static final String STORAGE_TAGS = "storagetags";
|
||||||
|
public static final String SUCCESS = "success";
|
||||||
|
public static final String SUITABLE_FOR_VM = "suitableforvirtualmachine";
|
||||||
|
public static final String SUPPORTS_STORAGE_SNAPSHOT = "supportsstoragesnapshot";
|
||||||
public static final String TARGET_IQN = "targetiqn";
|
public static final String TARGET_IQN = "targetiqn";
|
||||||
public static final String TEMPLATE_FILTER = "templatefilter";
|
public static final String TEMPLATE_FILTER = "templatefilter";
|
||||||
public static final String TEMPLATE_ID = "templateid";
|
public static final String TEMPLATE_ID = "templateid";
|
||||||
@ -482,6 +488,7 @@ public class ApiConstants {
|
|||||||
public static final String USERNAME = "username";
|
public static final String USERNAME = "username";
|
||||||
public static final String USER_CONFIGURABLE = "userconfigurable";
|
public static final String USER_CONFIGURABLE = "userconfigurable";
|
||||||
public static final String USER_SECURITY_GROUP_LIST = "usersecuritygrouplist";
|
public static final String USER_SECURITY_GROUP_LIST = "usersecuritygrouplist";
|
||||||
|
public static final String USER_SECRET_KEY = "usersecretkey";
|
||||||
public static final String USE_VIRTUAL_NETWORK = "usevirtualnetwork";
|
public static final String USE_VIRTUAL_NETWORK = "usevirtualnetwork";
|
||||||
public static final String UPDATE_IN_SEQUENCE = "updateinsequence";
|
public static final String UPDATE_IN_SEQUENCE = "updateinsequence";
|
||||||
public static final String VALUE = "value";
|
public static final String VALUE = "value";
|
||||||
@ -561,6 +568,7 @@ public class ApiConstants {
|
|||||||
public static final String ALLOCATION_STATE = "allocationstate";
|
public static final String ALLOCATION_STATE = "allocationstate";
|
||||||
public static final String MANAGED_STATE = "managedstate";
|
public static final String MANAGED_STATE = "managedstate";
|
||||||
public static final String MANAGEMENT_SERVER_ID = "managementserverid";
|
public static final String MANAGEMENT_SERVER_ID = "managementserverid";
|
||||||
|
public static final String MANAGEMENT_SERVER_NAME = "managementservername";
|
||||||
public static final String STORAGE = "storage";
|
public static final String STORAGE = "storage";
|
||||||
public static final String STORAGE_ID = "storageid";
|
public static final String STORAGE_ID = "storageid";
|
||||||
public static final String PING_STORAGE_SERVER_IP = "pingstorageserverip";
|
public static final String PING_STORAGE_SERVER_IP = "pingstorageserverip";
|
||||||
@ -1121,6 +1129,9 @@ public class ApiConstants {
|
|||||||
|
|
||||||
public static final String PARAMETER_DESCRIPTION_IS_TAG_A_RULE = "Whether the informed tag is a JS interpretable rule or not.";
|
public static final String PARAMETER_DESCRIPTION_IS_TAG_A_RULE = "Whether the informed tag is a JS interpretable rule or not.";
|
||||||
|
|
||||||
|
public static final String WEBHOOK_ID = "webhookid";
|
||||||
|
public static final String WEBHOOK_NAME = "webhookname";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This enum specifies IO Drivers, each option controls specific policies on I/O.
|
* This enum specifies IO Drivers, each option controls specific policies on I/O.
|
||||||
* Qemu guests support "threads" and "native" options Since 0.8.8 ; "io_uring" is supported Since 6.3.0 (QEMU 5.0).
|
* Qemu guests support "threads" and "native" options Since 0.8.8 ; "io_uring" is supported Since 6.3.0 (QEMU 5.0).
|
||||||
|
|||||||
@ -66,7 +66,7 @@ public class UpdateUserCmd extends BaseCmd {
|
|||||||
@Parameter(name = ApiConstants.CURRENT_PASSWORD, type = CommandType.STRING, description = "Current password that was being used by the user. You must inform the current password when updating the password.", acceptedOnAdminPort = false)
|
@Parameter(name = ApiConstants.CURRENT_PASSWORD, type = CommandType.STRING, description = "Current password that was being used by the user. You must inform the current password when updating the password.", acceptedOnAdminPort = false)
|
||||||
private String currentPassword;
|
private String currentPassword;
|
||||||
|
|
||||||
@Parameter(name = ApiConstants.SECRET_KEY, type = CommandType.STRING, description = "The secret key for the user. Must be specified with userApiKey")
|
@Parameter(name = ApiConstants.USER_SECRET_KEY, type = CommandType.STRING, description = "The secret key for the user. Must be specified with userApiKey")
|
||||||
private String secretKey;
|
private String secretKey;
|
||||||
|
|
||||||
@Parameter(name = ApiConstants.TIMEZONE,
|
@Parameter(name = ApiConstants.TIMEZONE,
|
||||||
|
|||||||
@ -98,7 +98,7 @@ public class BucketResponse extends BaseResponseWithTagInformation implements Co
|
|||||||
@Param(description = "Bucket Access Key")
|
@Param(description = "Bucket Access Key")
|
||||||
private String accessKey;
|
private String accessKey;
|
||||||
|
|
||||||
@SerializedName(ApiConstants.SECRET_KEY)
|
@SerializedName(ApiConstants.USER_SECRET_KEY)
|
||||||
@Param(description = "Bucket Secret Key")
|
@Param(description = "Bucket Secret Key")
|
||||||
private String secretKey;
|
private String secretKey;
|
||||||
|
|
||||||
|
|||||||
@ -438,6 +438,11 @@
|
|||||||
<artifactId>cloud-mom-kafka</artifactId>
|
<artifactId>cloud-mom-kafka</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.cloudstack</groupId>
|
||||||
|
<artifactId>cloud-mom-webhook</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.cloudstack</groupId>
|
<groupId>org.apache.cloudstack</groupId>
|
||||||
<artifactId>cloud-framework-agent-lb</artifactId>
|
<artifactId>cloud-framework-agent-lb</artifactId>
|
||||||
|
|||||||
@ -288,10 +288,10 @@
|
|||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="hypervisorGurusRegistry"
|
<bean id="hypervisorGurusRegistry"
|
||||||
class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
|
class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
|
||||||
<property name="excludeKey" value="hypervisor.gurus.exclude" />
|
<property name="excludeKey" value="hypervisor.gurus.exclude" />
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="vpcProvidersRegistry"
|
<bean id="vpcProvidersRegistry"
|
||||||
class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
|
class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
|
||||||
<property name="excludeKey" value="vpc.providers.exclude" />
|
<property name="excludeKey" value="vpc.providers.exclude" />
|
||||||
@ -358,4 +358,9 @@
|
|||||||
</list>
|
</list>
|
||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<bean id="eventBusRegistry"
|
||||||
|
class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
|
||||||
|
<property name="excludeKey" value="event.buses.exclude" />
|
||||||
|
</bean>
|
||||||
</beans>
|
</beans>
|
||||||
|
|||||||
@ -0,0 +1,21 @@
|
|||||||
|
#
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
name=event
|
||||||
|
parent=core
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
<!--
|
||||||
|
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||||
|
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
|
||||||
|
>
|
||||||
|
<bean class="org.apache.cloudstack.spring.lifecycle.registry.RegistryLifecycle">
|
||||||
|
<property name="registry" ref="eventBusRegistry" />
|
||||||
|
<property name="typeClass" value="org.apache.cloudstack.framework.events.EventBus" />
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
</beans>
|
||||||
@ -25,15 +25,14 @@ import java.util.Map;
|
|||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import org.apache.commons.collections.MapUtils;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
|
||||||
|
|
||||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||||
import org.apache.cloudstack.framework.events.Event;
|
import org.apache.cloudstack.framework.events.Event;
|
||||||
import org.apache.cloudstack.framework.events.EventBus;
|
import org.apache.cloudstack.framework.events.EventBus;
|
||||||
import org.apache.cloudstack.framework.events.EventBusException;
|
import org.apache.cloudstack.framework.events.EventDistributor;
|
||||||
|
import org.apache.commons.collections.MapUtils;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
|
|
||||||
import com.cloud.dc.DataCenterVO;
|
import com.cloud.dc.DataCenterVO;
|
||||||
import com.cloud.dc.dao.DataCenterDao;
|
import com.cloud.dc.dao.DataCenterDao;
|
||||||
@ -50,6 +49,7 @@ public class UsageEventUtils {
|
|||||||
protected static Logger LOGGER = LogManager.getLogger(UsageEventUtils.class);
|
protected static Logger LOGGER = LogManager.getLogger(UsageEventUtils.class);
|
||||||
protected static EventBus s_eventBus = null;
|
protected static EventBus s_eventBus = null;
|
||||||
protected static ConfigurationDao s_configDao;
|
protected static ConfigurationDao s_configDao;
|
||||||
|
private static EventDistributor eventDistributor;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
UsageEventDao usageEventDao;
|
UsageEventDao usageEventDao;
|
||||||
@ -207,9 +207,9 @@ public class UsageEventUtils {
|
|||||||
if( !configValue)
|
if( !configValue)
|
||||||
return;
|
return;
|
||||||
try {
|
try {
|
||||||
s_eventBus = ComponentContext.getComponent(EventBus.class);
|
eventDistributor = ComponentContext.getComponent(EventDistributor.class);
|
||||||
} catch (NoSuchBeanDefinitionException nbe) {
|
} catch (NoSuchBeanDefinitionException nbe) {
|
||||||
return; // no provider is configured to provide events bus, so just return
|
return; // no provider is configured to provide events distributor, so just return
|
||||||
}
|
}
|
||||||
|
|
||||||
Account account = s_accountDao.findById(accountId);
|
Account account = s_accountDao.findById(accountId);
|
||||||
@ -238,11 +238,7 @@ public class UsageEventUtils {
|
|||||||
|
|
||||||
event.setDescription(eventDescription);
|
event.setDescription(eventDescription);
|
||||||
|
|
||||||
try {
|
eventDistributor.publish(event);
|
||||||
s_eventBus.publish(event);
|
|
||||||
} catch (EventBusException e) {
|
|
||||||
LOGGER.warn("Failed to publish usage event on the event bus.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static final String Name = "management-server";
|
static final String Name = "management-server";
|
||||||
|
|||||||
@ -25,11 +25,9 @@ import java.util.Map;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||||
import org.apache.cloudstack.framework.events.EventBus;
|
import org.apache.cloudstack.framework.events.EventDistributor;
|
||||||
import org.apache.cloudstack.framework.events.EventBusException;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import com.cloud.event.EventCategory;
|
import com.cloud.event.EventCategory;
|
||||||
import com.cloud.network.Network.Event;
|
import com.cloud.network.Network.Event;
|
||||||
@ -43,7 +41,7 @@ public class NetworkStateListener implements StateListener<State, Event, Network
|
|||||||
@Inject
|
@Inject
|
||||||
private ConfigurationDao _configDao;
|
private ConfigurationDao _configDao;
|
||||||
|
|
||||||
private static EventBus s_eventBus = null;
|
private EventDistributor eventDistributor;
|
||||||
|
|
||||||
protected Logger logger = LogManager.getLogger(getClass());
|
protected Logger logger = LogManager.getLogger(getClass());
|
||||||
|
|
||||||
@ -51,6 +49,10 @@ public class NetworkStateListener implements StateListener<State, Event, Network
|
|||||||
_configDao = configDao;
|
_configDao = configDao;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setEventDistributor(EventDistributor eventDistributor) {
|
||||||
|
this.eventDistributor = eventDistributor;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean preStateTransitionEvent(State oldState, Event event, State newState, Network vo, boolean status, Object opaque) {
|
public boolean preStateTransitionEvent(State oldState, Event event, State newState, Network vo, boolean status, Object opaque) {
|
||||||
pubishOnEventBus(event.name(), "preStateTransitionEvent", vo, oldState, newState);
|
pubishOnEventBus(event.name(), "preStateTransitionEvent", vo, oldState, newState);
|
||||||
@ -66,23 +68,20 @@ public class NetworkStateListener implements StateListener<State, Event, Network
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pubishOnEventBus(String event, String status, Network vo, State oldState, State newState) {
|
private void pubishOnEventBus(String event, String status, Network vo, State oldState, State newState) {
|
||||||
|
|
||||||
String configKey = "publish.resource.state.events";
|
String configKey = "publish.resource.state.events";
|
||||||
String value = _configDao.getValue(configKey);
|
String value = _configDao.getValue(configKey);
|
||||||
boolean configValue = Boolean.parseBoolean(value);
|
boolean configValue = Boolean.parseBoolean(value);
|
||||||
if(!configValue)
|
if(!configValue)
|
||||||
return;
|
return;
|
||||||
try {
|
if (eventDistributor == null) {
|
||||||
s_eventBus = ComponentContext.getComponent(EventBus.class);
|
setEventDistributor(ComponentContext.getComponent(EventDistributor.class));
|
||||||
} catch (NoSuchBeanDefinitionException nbe) {
|
|
||||||
return; // no provider is configured to provide events bus, so just return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String resourceName = getEntityFromClassName(Network.class.getName());
|
String resourceName = getEntityFromClassName(Network.class.getName());
|
||||||
org.apache.cloudstack.framework.events.Event eventMsg =
|
org.apache.cloudstack.framework.events.Event eventMsg =
|
||||||
new org.apache.cloudstack.framework.events.Event("management-server", EventCategory.RESOURCE_STATE_CHANGE_EVENT.getName(), event, resourceName, vo.getUuid());
|
new org.apache.cloudstack.framework.events.Event("management-server", EventCategory.RESOURCE_STATE_CHANGE_EVENT.getName(), event, resourceName, vo.getUuid());
|
||||||
Map<String, String> eventDescription = new HashMap<String, String>();
|
Map<String, String> eventDescription = new HashMap<>();
|
||||||
eventDescription.put("resource", resourceName);
|
eventDescription.put("resource", resourceName);
|
||||||
eventDescription.put("id", vo.getUuid());
|
eventDescription.put("id", vo.getUuid());
|
||||||
eventDescription.put("old-state", oldState.name());
|
eventDescription.put("old-state", oldState.name());
|
||||||
@ -92,11 +91,8 @@ public class NetworkStateListener implements StateListener<State, Event, Network
|
|||||||
eventDescription.put("eventDateTime", eventDate);
|
eventDescription.put("eventDateTime", eventDate);
|
||||||
|
|
||||||
eventMsg.setDescription(eventDescription);
|
eventMsg.setDescription(eventDescription);
|
||||||
try {
|
|
||||||
s_eventBus.publish(eventMsg);
|
eventDistributor.publish(eventMsg);
|
||||||
} catch (EventBusException e) {
|
|
||||||
logger.warn("Failed to publish state change event on the event bus.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getEntityFromClassName(String entityClassName) {
|
private String getEntityFromClassName(String entityClassName) {
|
||||||
|
|||||||
@ -0,0 +1,40 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
package com.cloud.network;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||||
|
import org.apache.cloudstack.framework.events.EventDistributor;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
|
public class NetworkStateListenerTest {
|
||||||
|
@InjectMocks
|
||||||
|
NetworkStateListener networkStateListener = new NetworkStateListener(Mockito.mock(ConfigurationDao.class));
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetEventDistributor() {
|
||||||
|
EventDistributor eventDistributor = null;
|
||||||
|
networkStateListener.setEventDistributor(eventDistributor);
|
||||||
|
Assert.assertNull(ReflectionTestUtils.getField(networkStateListener, "eventDistributor"));
|
||||||
|
eventDistributor = Mockito.mock(EventDistributor.class);
|
||||||
|
networkStateListener.setEventDistributor(eventDistributor);
|
||||||
|
Assert.assertEquals(eventDistributor, ReflectionTestUtils.getField(networkStateListener, "eventDistributor"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -70,7 +70,6 @@ CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.network_offerings','nsx_mode', 'varc
|
|||||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc_offerings','for_nsx', 'int(1) unsigned DEFAULT "0" COMMENT "is nsx enabled for the resource"');
|
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc_offerings','for_nsx', 'int(1) unsigned DEFAULT "0" COMMENT "is nsx enabled for the resource"');
|
||||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc_offerings','nsx_mode', 'varchar(32) COMMENT "mode in which the network would route traffic"');
|
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc_offerings','nsx_mode', 'varchar(32) COMMENT "mode in which the network would route traffic"');
|
||||||
|
|
||||||
|
|
||||||
-- Create table to persist quota email template configurations
|
-- Create table to persist quota email template configurations
|
||||||
CREATE TABLE IF NOT EXISTS `cloud_usage`.`quota_email_configuration`(
|
CREATE TABLE IF NOT EXISTS `cloud_usage`.`quota_email_configuration`(
|
||||||
`account_id` int(11) NOT NULL,
|
`account_id` int(11) NOT NULL,
|
||||||
@ -82,3 +81,44 @@ CREATE TABLE IF NOT EXISTS `cloud_usage`.`quota_email_configuration`(
|
|||||||
|
|
||||||
-- Add `is_implicit` column to `host_tags` table
|
-- Add `is_implicit` column to `host_tags` table
|
||||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.host_tags', 'is_implicit', 'int(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT "If host tag is implicit or explicit" ');
|
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.host_tags', 'is_implicit', 'int(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT "If host tag is implicit or explicit" ');
|
||||||
|
|
||||||
|
-- Webhooks feature
|
||||||
|
DROP TABLE IF EXISTS `cloud`.`webhook`;
|
||||||
|
CREATE TABLE `cloud`.`webhook` (
|
||||||
|
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id of the webhook',
|
||||||
|
`uuid` varchar(255) COMMENT 'uuid of the webhook',
|
||||||
|
`name` varchar(255) NOT NULL COMMENT 'name of the webhook',
|
||||||
|
`description` varchar(4096) COMMENT 'description for the webhook',
|
||||||
|
`state` char(32) NOT NULL COMMENT 'state of the webhook - Enabled or Disabled',
|
||||||
|
`domain_id` bigint unsigned NOT NULL COMMENT 'id of the owner domain of the webhook',
|
||||||
|
`account_id` bigint unsigned NOT NULL COMMENT 'id of the owner account of the webhook',
|
||||||
|
`payload_url` varchar(255) COMMENT 'payload URL for the webhook',
|
||||||
|
`secret_key` varchar(255) COMMENT 'secret key for the webhook',
|
||||||
|
`ssl_verification` boolean COMMENT 'for https payload url, if true then strict ssl verification',
|
||||||
|
`scope` char(32) NOT NULL COMMENT 'scope for the webhook - Local, Domain, Global',
|
||||||
|
`created` datetime COMMENT 'date the webhook was created',
|
||||||
|
`removed` datetime COMMENT 'date removed if not null',
|
||||||
|
PRIMARY KEY(`id`),
|
||||||
|
INDEX `i_webhook__account_id`(`account_id`),
|
||||||
|
CONSTRAINT `fk_webhook__account_id` FOREIGN KEY (`account_id`) REFERENCES `account`(`id`) ON DELETE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `cloud`.`webhook_delivery`;
|
||||||
|
CREATE TABLE `cloud`.`webhook_delivery` (
|
||||||
|
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'id of the webhook delivery',
|
||||||
|
`uuid` varchar(255) COMMENT 'uuid of the webhook',
|
||||||
|
`event_id` bigint unsigned NOT NULL COMMENT 'id of the event',
|
||||||
|
`webhook_id` bigint unsigned NOT NULL COMMENT 'id of the webhook',
|
||||||
|
`mshost_msid` bigint unsigned NOT NULL COMMENT 'msid of the management server',
|
||||||
|
`headers` TEXT COMMENT 'headers for the webhook delivery',
|
||||||
|
`payload` TEXT COMMENT 'payload for the webhook delivery',
|
||||||
|
`success` boolean COMMENT 'webhook delivery succeeded or not',
|
||||||
|
`response` TEXT COMMENT 'response of the webhook delivery',
|
||||||
|
`start_time` datetime COMMENT 'start timestamp of the webhook delivery',
|
||||||
|
`end_time` datetime COMMENT 'end timestamp of the webhook delivery',
|
||||||
|
PRIMARY KEY(`id`),
|
||||||
|
INDEX `i_webhook__event_id`(`event_id`),
|
||||||
|
INDEX `i_webhook__webhook_id`(`webhook_id`),
|
||||||
|
CONSTRAINT `fk_webhook__event_id` FOREIGN KEY (`event_id`) REFERENCES `event`(`id`) ON DELETE CASCADE,
|
||||||
|
CONSTRAINT `fk_webhook__webhook_id` FOREIGN KEY (`webhook_id`) REFERENCES `webhook`(`id`) ON DELETE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||||
|
|||||||
@ -0,0 +1,48 @@
|
|||||||
|
-- Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
-- or more contributor license agreements. See the NOTICE file
|
||||||
|
-- distributed with this work for additional information
|
||||||
|
-- regarding copyright ownership. The ASF licenses this file
|
||||||
|
-- to you under the Apache License, Version 2.0 (the
|
||||||
|
-- "License"); you may not use this file except in compliance
|
||||||
|
-- with the License. You may obtain a copy of the License at
|
||||||
|
--
|
||||||
|
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
--
|
||||||
|
-- Unless required by applicable law or agreed to in writing,
|
||||||
|
-- software distributed under the License is distributed on an
|
||||||
|
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
-- KIND, either express or implied. See the License for the
|
||||||
|
-- specific language governing permissions and limitations
|
||||||
|
-- under the License.
|
||||||
|
|
||||||
|
-- VIEW `cloud`.`webhook_delivery_view`;
|
||||||
|
|
||||||
|
DROP VIEW IF EXISTS `cloud`.`webhook_delivery_view`;
|
||||||
|
CREATE VIEW `cloud`.`webhook_delivery_view` AS
|
||||||
|
SELECT
|
||||||
|
webhook_delivery.id,
|
||||||
|
webhook_delivery.uuid,
|
||||||
|
webhook_delivery.headers,
|
||||||
|
webhook_delivery.payload,
|
||||||
|
webhook_delivery.success,
|
||||||
|
webhook_delivery.response,
|
||||||
|
webhook_delivery.start_time,
|
||||||
|
webhook_delivery.end_time,
|
||||||
|
event.id event_id,
|
||||||
|
event.uuid event_uuid,
|
||||||
|
event.type event_type,
|
||||||
|
webhook.id webhook_id,
|
||||||
|
webhook.uuid webhook_uuid,
|
||||||
|
webhook.name webhook_name,
|
||||||
|
mshost.id mshost_id,
|
||||||
|
mshost.uuid mshost_uuid,
|
||||||
|
mshost.msid mshost_msid,
|
||||||
|
mshost.name mshost_name
|
||||||
|
FROM
|
||||||
|
`cloud`.`webhook_delivery`
|
||||||
|
INNER JOIN
|
||||||
|
`cloud`.`event` ON webhook_delivery.event_id = event.id
|
||||||
|
INNER JOIN
|
||||||
|
`cloud`.`webhook` ON webhook_delivery.webhook_id = webhook.id
|
||||||
|
LEFT JOIN
|
||||||
|
`cloud`.`mshost` ON mshost.msid = webhook_delivery.mshost_msid;
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
-- Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
-- or more contributor license agreements. See the NOTICE file
|
||||||
|
-- distributed with this work for additional information
|
||||||
|
-- regarding copyright ownership. The ASF licenses this file
|
||||||
|
-- to you under the Apache License, Version 2.0 (the
|
||||||
|
-- "License"); you may not use this file except in compliance
|
||||||
|
-- with the License. You may obtain a copy of the License at
|
||||||
|
--
|
||||||
|
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
--
|
||||||
|
-- Unless required by applicable law or agreed to in writing,
|
||||||
|
-- software distributed under the License is distributed on an
|
||||||
|
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
-- KIND, either express or implied. See the License for the
|
||||||
|
-- specific language governing permissions and limitations
|
||||||
|
-- under the License.
|
||||||
|
|
||||||
|
-- VIEW `cloud`.`webhook_view`;
|
||||||
|
|
||||||
|
DROP VIEW IF EXISTS `cloud`.`webhook_view`;
|
||||||
|
CREATE VIEW `cloud`.`webhook_view` AS
|
||||||
|
SELECT
|
||||||
|
webhook.id,
|
||||||
|
webhook.uuid,
|
||||||
|
webhook.name,
|
||||||
|
webhook.description,
|
||||||
|
webhook.state,
|
||||||
|
webhook.payload_url,
|
||||||
|
webhook.secret_key,
|
||||||
|
webhook.ssl_verification,
|
||||||
|
webhook.scope,
|
||||||
|
webhook.created,
|
||||||
|
webhook.removed,
|
||||||
|
account.id account_id,
|
||||||
|
account.uuid account_uuid,
|
||||||
|
account.account_name account_name,
|
||||||
|
account.type account_type,
|
||||||
|
domain.id domain_id,
|
||||||
|
domain.uuid domain_uuid,
|
||||||
|
domain.name domain_name,
|
||||||
|
domain.path domain_path,
|
||||||
|
projects.id project_id,
|
||||||
|
projects.uuid project_uuid,
|
||||||
|
projects.name project_name
|
||||||
|
FROM
|
||||||
|
`cloud`.`webhook`
|
||||||
|
INNER JOIN
|
||||||
|
`cloud`.`account` ON webhook.account_id = account.id
|
||||||
|
INNER JOIN
|
||||||
|
`cloud`.`domain` ON webhook.domain_id = domain.id
|
||||||
|
LEFT JOIN
|
||||||
|
`cloud`.`projects` ON projects.project_account_id = webhook.account_id;
|
||||||
@ -20,22 +20,49 @@
|
|||||||
package org.apache.cloudstack.framework.events;
|
package org.apache.cloudstack.framework.events;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.annotations.Expose;
|
||||||
|
|
||||||
public class Event {
|
public class Event {
|
||||||
|
|
||||||
|
@Expose(serialize = false, deserialize = false)
|
||||||
|
Long eventId;
|
||||||
|
@Expose(serialize = false, deserialize = false)
|
||||||
|
String eventUuid;
|
||||||
String eventCategory;
|
String eventCategory;
|
||||||
String eventType;
|
String eventType;
|
||||||
String eventSource;
|
String eventSource;
|
||||||
String resourceType;
|
String resourceType;
|
||||||
String resourceUUID;
|
String resourceUUID;
|
||||||
String description;
|
String description;
|
||||||
|
@Expose(serialize = false, deserialize = false)
|
||||||
|
Long resourceAccountId;
|
||||||
|
@Expose(serialize = false, deserialize = false)
|
||||||
|
String resourceAccountUuid;
|
||||||
|
@Expose(serialize = false, deserialize = false)
|
||||||
|
Long resourceDomainId;
|
||||||
|
|
||||||
public Event(String eventSource, String eventCategory, String eventType, String resourceType, String resourceUUID) {
|
public Event(String eventSource, String eventCategory, String eventType, String resourceType, String resourceUUID) {
|
||||||
this.eventCategory = eventCategory;
|
setEventCategory(eventCategory);
|
||||||
this.eventType = eventType;
|
setEventType(eventType);
|
||||||
this.eventSource = eventSource;
|
setEventSource(eventSource);
|
||||||
this.resourceType = resourceType;
|
setResourceType(resourceType);
|
||||||
this.resourceUUID = resourceUUID;
|
setResourceUUID(resourceUUID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getEventId() {
|
||||||
|
return eventId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEventId(Long eventId) {
|
||||||
|
this.eventId = eventId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEventUuid() {
|
||||||
|
return eventUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEventUuid(String eventUuid) {
|
||||||
|
this.eventUuid = eventUuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getEventCategory() {
|
public String getEventCategory() {
|
||||||
@ -68,7 +95,7 @@ public class Event {
|
|||||||
|
|
||||||
public void setDescription(Object message) {
|
public void setDescription(Object message) {
|
||||||
Gson gson = new Gson();
|
Gson gson = new Gson();
|
||||||
this.description = gson.toJson(message).toString();
|
this.description = gson.toJson(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDescription(String description) {
|
public void setDescription(String description) {
|
||||||
@ -90,4 +117,28 @@ public class Event {
|
|||||||
public String getResourceUUID() {
|
public String getResourceUUID() {
|
||||||
return resourceUUID;
|
return resourceUUID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getResourceAccountId() {
|
||||||
|
return resourceAccountId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResourceAccountId(Long resourceAccountId) {
|
||||||
|
this.resourceAccountId = resourceAccountId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getResourceAccountUuid() {
|
||||||
|
return resourceAccountUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResourceAccountUuid(String resourceAccountUuid) {
|
||||||
|
this.resourceAccountUuid = resourceAccountUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getResourceDomainId() {
|
||||||
|
return resourceDomainId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResourceDomainId(Long resourceDomainId) {
|
||||||
|
this.resourceDomainId = resourceDomainId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,8 @@ import java.util.UUID;
|
|||||||
*/
|
*/
|
||||||
public interface EventBus {
|
public interface EventBus {
|
||||||
|
|
||||||
|
String getName();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* publish an event on to the event bus
|
* publish an event on to the event bus
|
||||||
*
|
*
|
||||||
|
|||||||
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cloudstack.framework.events;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.cloud.utils.component.Manager;
|
||||||
|
|
||||||
|
public interface EventDistributor extends Manager {
|
||||||
|
/**
|
||||||
|
* Publish an event on to all the available event buses
|
||||||
|
*
|
||||||
|
* @param event event that needs to be published on the event bus
|
||||||
|
* @return Map of bus names and EventBusException for buses that failed with
|
||||||
|
* exception
|
||||||
|
*/
|
||||||
|
Map<String, EventBusException> publish(Event event);
|
||||||
|
}
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cloudstack.framework.events;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import com.cloud.utils.component.ManagerBase;
|
||||||
|
|
||||||
|
public class EventDistributorImpl extends ManagerBase implements EventDistributor {
|
||||||
|
|
||||||
|
List<EventBus> eventBuses;
|
||||||
|
|
||||||
|
public void setEventBuses(List<EventBus> eventBuses) {
|
||||||
|
this.eventBuses = eventBuses;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init() {
|
||||||
|
logger.trace("Found {} event buses : {}", () -> eventBuses.size(),
|
||||||
|
() -> StringUtils.join(eventBuses.stream().map(x->x.getClass().getName()).toArray()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, EventBusException> publish(Event event) {
|
||||||
|
Map<String, EventBusException> exceptions = new HashMap<>();
|
||||||
|
if (event == null) {
|
||||||
|
return exceptions;
|
||||||
|
}
|
||||||
|
logger.trace("Publishing event [category: {}, type: {}]: {} to {} event buses",
|
||||||
|
event.getEventCategory(), event.getEventType(),
|
||||||
|
event.getDescription(), eventBuses.size());
|
||||||
|
for (EventBus bus : eventBuses) {
|
||||||
|
try {
|
||||||
|
bus.publish(event);
|
||||||
|
} catch (EventBusException e) {
|
||||||
|
logger.warn("Failed to publish event [category: {}, type: {}] on bus {}",
|
||||||
|
event.getEventCategory(), event.getEventType(), bus.getName());
|
||||||
|
logger.trace(event.getDescription());
|
||||||
|
exceptions.put(bus.getName(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return exceptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.framework.events;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.commons.collections.MapUtils;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class EventDistributorImplTest {
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
EventDistributorImpl eventDistributor = new EventDistributorImpl();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetEventBuses() {
|
||||||
|
Assert.assertNull(ReflectionTestUtils.getField(eventDistributor, "eventBuses"));
|
||||||
|
EventBus eventBus = Mockito.mock(EventBus.class);
|
||||||
|
EventBus eventBus1 = Mockito.mock(EventBus.class);
|
||||||
|
eventDistributor.setEventBuses(List.of(eventBus, eventBus1));
|
||||||
|
Assert.assertNotNull(ReflectionTestUtils.getField(eventDistributor, "eventBuses"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPublishNullEvent() {
|
||||||
|
Map<String, EventBusException> exceptionMap = eventDistributor.publish(null);
|
||||||
|
Assert.assertTrue(MapUtils.isEmpty(exceptionMap));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPublishOneReturnsException() throws EventBusException {
|
||||||
|
String busName = "Test";
|
||||||
|
EventBus eventBus = Mockito.mock(EventBus.class);
|
||||||
|
Mockito.doReturn(busName).when(eventBus).getName();
|
||||||
|
Mockito.doThrow(EventBusException.class).when(eventBus).publish(Mockito.any(Event.class));
|
||||||
|
EventBus eventBus1 = Mockito.mock(EventBus.class);
|
||||||
|
Mockito.doNothing().when(eventBus1).publish(Mockito.any(Event.class));
|
||||||
|
eventDistributor.eventBuses = List.of(eventBus, eventBus1);
|
||||||
|
Map<String, EventBusException> exceptionMap = eventDistributor.publish(Mockito.mock(Event.class));
|
||||||
|
Assert.assertTrue(MapUtils.isNotEmpty(exceptionMap));
|
||||||
|
Assert.assertEquals(1, exceptionMap.size());
|
||||||
|
Assert.assertTrue(exceptionMap.containsKey(busName));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -16,13 +16,14 @@
|
|||||||
// under the License.
|
// under the License.
|
||||||
package org.apache.cloudstack.api.response;
|
package org.apache.cloudstack.api.response;
|
||||||
|
|
||||||
import com.cloud.serializer.Param;
|
import java.util.HashSet;
|
||||||
import com.google.gson.annotations.SerializedName;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.cloudstack.api.ApiConstants;
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
import org.apache.cloudstack.api.BaseResponse;
|
import org.apache.cloudstack.api.BaseResponse;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import com.cloud.serializer.Param;
|
||||||
import java.util.Set;
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class ApiDiscoveryResponse extends BaseResponse {
|
public class ApiDiscoveryResponse extends BaseResponse {
|
||||||
@ -64,6 +65,18 @@ public class ApiDiscoveryResponse extends BaseResponse {
|
|||||||
isAsync = false;
|
isAsync = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ApiDiscoveryResponse(ApiDiscoveryResponse another) {
|
||||||
|
this.name = another.getName();
|
||||||
|
this.description = another.getDescription();
|
||||||
|
this.since = another.getSince();
|
||||||
|
this.isAsync = another.getAsync();
|
||||||
|
this.related = another.getRelated();
|
||||||
|
this.params = new HashSet<>(another.getParams());
|
||||||
|
this.apiResponse = new HashSet<>(another.getApiResponse());
|
||||||
|
this.type = another.getType();
|
||||||
|
this.setObjectName(another.getObjectName());
|
||||||
|
}
|
||||||
|
|
||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
@ -123,4 +136,8 @@ public class ApiDiscoveryResponse extends BaseResponse {
|
|||||||
public Set<ApiResponseResponse> getApiResponse() {
|
public Set<ApiResponseResponse> getApiResponse() {
|
||||||
return apiResponse;
|
return apiResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,12 +16,14 @@
|
|||||||
// under the License.
|
// under the License.
|
||||||
package org.apache.cloudstack.api.response;
|
package org.apache.cloudstack.api.response;
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
import org.apache.cloudstack.api.ApiConstants;
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
import org.apache.cloudstack.api.BaseResponse;
|
import org.apache.cloudstack.api.BaseResponse;
|
||||||
|
|
||||||
import com.cloud.serializer.Param;
|
import com.cloud.serializer.Param;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
public class ApiParameterResponse extends BaseResponse {
|
public class ApiParameterResponse extends BaseResponse {
|
||||||
@SerializedName(ApiConstants.NAME)
|
@SerializedName(ApiConstants.NAME)
|
||||||
@ -52,6 +54,8 @@ public class ApiParameterResponse extends BaseResponse {
|
|||||||
@Param(description = "comma separated related apis to get the parameter")
|
@Param(description = "comma separated related apis to get the parameter")
|
||||||
private String related;
|
private String related;
|
||||||
|
|
||||||
|
private transient List<RoleType> authorizedRoleTypes = null;
|
||||||
|
|
||||||
public ApiParameterResponse() {
|
public ApiParameterResponse() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,4 +91,11 @@ public class ApiParameterResponse extends BaseResponse {
|
|||||||
this.related = related;
|
this.related = related;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAuthorizedRoleTypes(List<RoleType> authorizedRoleTypes) {
|
||||||
|
this.authorizedRoleTypes = authorizedRoleTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<RoleType> getAuthorizedRoleTypes() {
|
||||||
|
return authorizedRoleTypes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,8 +18,10 @@ package org.apache.cloudstack.discovery;
|
|||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -28,21 +30,22 @@ import java.util.Set;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import org.apache.cloudstack.acl.APIChecker;
|
import org.apache.cloudstack.acl.APIChecker;
|
||||||
|
import org.apache.cloudstack.acl.Role;
|
||||||
|
import org.apache.cloudstack.acl.RoleService;
|
||||||
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
import org.apache.cloudstack.api.APICommand;
|
import org.apache.cloudstack.api.APICommand;
|
||||||
import org.apache.cloudstack.api.BaseAsyncCmd;
|
import org.apache.cloudstack.api.BaseAsyncCmd;
|
||||||
import org.apache.cloudstack.api.BaseAsyncCreateCmd;
|
import org.apache.cloudstack.api.BaseAsyncCreateCmd;
|
||||||
import org.apache.cloudstack.api.BaseCmd;
|
import org.apache.cloudstack.api.BaseCmd;
|
||||||
import org.apache.cloudstack.api.BaseResponse;
|
import org.apache.cloudstack.api.BaseResponse;
|
||||||
import org.apache.cloudstack.api.Parameter;
|
import org.apache.cloudstack.api.Parameter;
|
||||||
import org.apache.cloudstack.acl.Role;
|
|
||||||
import org.apache.cloudstack.acl.RoleService;
|
|
||||||
import org.apache.cloudstack.acl.RoleType;
|
|
||||||
import org.apache.cloudstack.api.command.user.discovery.ListApisCmd;
|
import org.apache.cloudstack.api.command.user.discovery.ListApisCmd;
|
||||||
import org.apache.cloudstack.api.response.ApiDiscoveryResponse;
|
import org.apache.cloudstack.api.response.ApiDiscoveryResponse;
|
||||||
import org.apache.cloudstack.api.response.ApiParameterResponse;
|
import org.apache.cloudstack.api.response.ApiParameterResponse;
|
||||||
import org.apache.cloudstack.api.response.ApiResponseResponse;
|
import org.apache.cloudstack.api.response.ApiResponseResponse;
|
||||||
import org.apache.cloudstack.api.response.ListResponse;
|
import org.apache.cloudstack.api.response.ListResponse;
|
||||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.reflections.ReflectionUtils;
|
import org.reflections.ReflectionUtils;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
@ -215,6 +218,9 @@ public class ApiDiscoveryServiceImpl extends ComponentLifecycleBase implements A
|
|||||||
paramResponse.setSince(parameterAnnotation.since());
|
paramResponse.setSince(parameterAnnotation.since());
|
||||||
}
|
}
|
||||||
paramResponse.setRelated(parameterAnnotation.entityType()[0].getName());
|
paramResponse.setRelated(parameterAnnotation.entityType()[0].getName());
|
||||||
|
if (parameterAnnotation.authorized() != null) {
|
||||||
|
paramResponse.setAuthorizedRoleTypes(Arrays.asList(parameterAnnotation.authorized()));
|
||||||
|
}
|
||||||
response.addParam(paramResponse);
|
response.addParam(paramResponse);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -247,6 +253,7 @@ public class ApiDiscoveryServiceImpl extends ComponentLifecycleBase implements A
|
|||||||
|
|
||||||
if (user == null)
|
if (user == null)
|
||||||
return null;
|
return null;
|
||||||
|
Account account = accountService.getAccount(user.getAccountId());
|
||||||
|
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
if (!s_apiNameDiscoveryResponseMap.containsKey(name))
|
if (!s_apiNameDiscoveryResponseMap.containsKey(name))
|
||||||
@ -260,10 +267,9 @@ public class ApiDiscoveryServiceImpl extends ComponentLifecycleBase implements A
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
responseList.add(s_apiNameDiscoveryResponseMap.get(name));
|
responseList.add(getApiDiscoveryResponseWithAccessibleParams(name, account));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Account account = accountService.getAccount(user.getAccountId());
|
|
||||||
if (account == null) {
|
if (account == null) {
|
||||||
throw new PermissionDeniedException(String.format("The account with id [%s] for user [%s] is null.", user.getAccountId(), user));
|
throw new PermissionDeniedException(String.format("The account with id [%s] for user [%s] is null.", user.getAccountId(), user));
|
||||||
}
|
}
|
||||||
@ -284,13 +290,33 @@ public class ApiDiscoveryServiceImpl extends ComponentLifecycleBase implements A
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (String apiName: apisAllowed) {
|
for (String apiName: apisAllowed) {
|
||||||
responseList.add(s_apiNameDiscoveryResponseMap.get(apiName));
|
responseList.add(getApiDiscoveryResponseWithAccessibleParams(apiName, account));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
response.setResponses(responseList);
|
response.setResponses(responseList);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ApiDiscoveryResponse getApiDiscoveryResponseWithAccessibleParams(String name, Account account) {
|
||||||
|
if (Account.Type.ADMIN.equals(account.getType())) {
|
||||||
|
return s_apiNameDiscoveryResponseMap.get(name);
|
||||||
|
}
|
||||||
|
ApiDiscoveryResponse apiDiscoveryResponse =
|
||||||
|
new ApiDiscoveryResponse(s_apiNameDiscoveryResponseMap.get(name));
|
||||||
|
Iterator<ApiParameterResponse> iterator = apiDiscoveryResponse.getParams().iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
ApiParameterResponse parameterResponse = iterator.next();
|
||||||
|
List<RoleType> authorizedRoleTypes = parameterResponse.getAuthorizedRoleTypes();
|
||||||
|
RoleType accountRoleType = RoleType.getByAccountType(account.getType());
|
||||||
|
if (CollectionUtils.isNotEmpty(parameterResponse.getAuthorizedRoleTypes()) &&
|
||||||
|
accountRoleType != null &&
|
||||||
|
!authorizedRoleTypes.contains(accountRoleType)) {
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return apiDiscoveryResponse;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Class<?>> getCommands() {
|
public List<Class<?>> getCommands() {
|
||||||
List<Class<?>> cmdList = new ArrayList<Class<?>>();
|
List<Class<?>> cmdList = new ArrayList<Class<?>>();
|
||||||
|
|||||||
@ -60,6 +60,8 @@ public class InMemoryEventBus extends ManagerBase implements EventBus {
|
|||||||
if (subscriber == null || topic == null) {
|
if (subscriber == null || topic == null) {
|
||||||
throw new EventBusException("Invalid EventSubscriber/EventTopic object passed.");
|
throw new EventBusException("Invalid EventSubscriber/EventTopic object passed.");
|
||||||
}
|
}
|
||||||
|
logger.debug("subscribing '{}' to events of type '{}' from '{}'", subscriber.toString(), topic.getEventType(), topic.getEventSource());
|
||||||
|
|
||||||
UUID subscriberId = UUID.randomUUID();
|
UUID subscriberId = UUID.randomUUID();
|
||||||
|
|
||||||
subscribers.put(subscriberId, new Pair<EventTopic, EventSubscriber>(topic, subscriber));
|
subscribers.put(subscriberId, new Pair<EventTopic, EventSubscriber>(topic, subscriber));
|
||||||
@ -68,6 +70,7 @@ public class InMemoryEventBus extends ManagerBase implements EventBus {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unsubscribe(UUID subscriberId, EventSubscriber subscriber) throws EventBusException {
|
public void unsubscribe(UUID subscriberId, EventSubscriber subscriber) throws EventBusException {
|
||||||
|
logger.debug("unsubscribing '{}'", subscriberId);
|
||||||
if (subscriberId == null) {
|
if (subscriberId == null) {
|
||||||
throw new EventBusException("Cannot unregister a null subscriberId.");
|
throw new EventBusException("Cannot unregister a null subscriberId.");
|
||||||
}
|
}
|
||||||
@ -85,7 +88,9 @@ public class InMemoryEventBus extends ManagerBase implements EventBus {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void publish(Event event) throws EventBusException {
|
public void publish(Event event) throws EventBusException {
|
||||||
|
logger.trace("publish '{}'", event.getDescription());
|
||||||
if (subscribers == null || subscribers.isEmpty()) {
|
if (subscribers == null || subscribers.isEmpty()) {
|
||||||
|
logger.trace("no subscribers, no publish");
|
||||||
return; // no subscriber to publish to, so just return
|
return; // no subscriber to publish to, so just return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -87,19 +87,23 @@ public class KafkaEventBus extends ManagerBase implements EventBus {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UUID subscribe(EventTopic topic, EventSubscriber subscriber) throws EventBusException {
|
public UUID subscribe(EventTopic topic, EventSubscriber subscriber) throws EventBusException {
|
||||||
|
logger.debug("subscribing '{}' to events of type '{}' from '{}'", subscriber.toString(), topic.getEventType(), topic.getEventSource());
|
||||||
|
|
||||||
/* NOOP */
|
/* NOOP */
|
||||||
return UUID.randomUUID();
|
return UUID.randomUUID();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unsubscribe(UUID subscriberId, EventSubscriber subscriber) throws EventBusException {
|
public void unsubscribe(UUID subscriberId, EventSubscriber subscriber) throws EventBusException {
|
||||||
|
logger.debug("unsubscribing '{}'", subscriberId);
|
||||||
/* NOOP */
|
/* NOOP */
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void publish(Event event) throws EventBusException {
|
public void publish(Event event) throws EventBusException {
|
||||||
ProducerRecord<String, String> record = new ProducerRecord<String,String>(_topic, event.getResourceUUID(), event.getDescription());
|
logger.trace("publish '{}'", event.getDescription());
|
||||||
_producer.send(record);
|
ProducerRecord<String, String> newRecord = new ProducerRecord<>(_topic, event.getResourceUUID(), event.getDescription());
|
||||||
|
_producer.send(newRecord);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -185,11 +185,12 @@ public class RabbitMQEventBus extends ManagerBase implements EventBus {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public UUID subscribe(EventTopic topic, EventSubscriber subscriber) throws EventBusException {
|
public UUID subscribe(EventTopic topic, EventSubscriber subscriber) throws EventBusException {
|
||||||
|
|
||||||
if (subscriber == null || topic == null) {
|
if (subscriber == null || topic == null) {
|
||||||
throw new EventBusException("Invalid EventSubscriber/EventTopic object passed.");
|
throw new EventBusException("Invalid EventSubscriber/EventTopic object passed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.debug("subscribing '{}' to events of type '{}' from '{}'", subscriber.toString(), topic.getEventType(), topic.getEventSource());
|
||||||
|
|
||||||
// create a UUID, that will be used for managing subscriptions and also used as queue name
|
// create a UUID, that will be used for managing subscriptions and also used as queue name
|
||||||
// for on the queue used for the subscriber on the AMQP broker
|
// for on the queue used for the subscriber on the AMQP broker
|
||||||
UUID queueId = UUID.randomUUID();
|
UUID queueId = UUID.randomUUID();
|
||||||
@ -250,6 +251,7 @@ public class RabbitMQEventBus extends ManagerBase implements EventBus {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unsubscribe(UUID subscriberId, EventSubscriber subscriber) throws EventBusException {
|
public void unsubscribe(UUID subscriberId, EventSubscriber subscriber) throws EventBusException {
|
||||||
|
logger.debug("unsubscribing '{}'", subscriberId);
|
||||||
try {
|
try {
|
||||||
String classname = subscriber.getClass().getName();
|
String classname = subscriber.getClass().getName();
|
||||||
String queueName = UUID.nameUUIDFromBytes(classname.getBytes()).toString();
|
String queueName = UUID.nameUUIDFromBytes(classname.getBytes()).toString();
|
||||||
@ -265,6 +267,7 @@ public class RabbitMQEventBus extends ManagerBase implements EventBus {
|
|||||||
// publish event on to the exchange created on AMQP server
|
// publish event on to the exchange created on AMQP server
|
||||||
@Override
|
@Override
|
||||||
public void publish(Event event) throws EventBusException {
|
public void publish(Event event) throws EventBusException {
|
||||||
|
logger.trace("publish '{}'", event.getDescription());
|
||||||
|
|
||||||
String routingKey = createRoutingKey(event);
|
String routingKey = createRoutingKey(event);
|
||||||
String eventDescription = event.getDescription();
|
String eventDescription = event.getDescription();
|
||||||
|
|||||||
46
plugins/event-bus/webhook/pom.xml
Normal file
46
plugins/event-bus/webhook/pom.xml
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<!--
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
-->
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>cloud-mom-webhook</artifactId>
|
||||||
|
<name>Apache CloudStack Plugin - Webhook Event Bus</name>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.apache.cloudstack</groupId>
|
||||||
|
<artifactId>cloudstack-plugins</artifactId>
|
||||||
|
<version>4.20.0.0-SNAPSHOT</version>
|
||||||
|
<relativePath>../../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.cloudstack</groupId>
|
||||||
|
<artifactId>cloud-framework-events</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.cloudstack</groupId>
|
||||||
|
<artifactId>cloud-engine-api</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.json</groupId>
|
||||||
|
<artifactId>json</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.acl.ControlledEntity;
|
||||||
|
import org.apache.cloudstack.api.Identity;
|
||||||
|
import org.apache.cloudstack.api.InternalIdentity;
|
||||||
|
|
||||||
|
public interface Webhook extends ControlledEntity, Identity, InternalIdentity {
|
||||||
|
public static final long ID_DUMMY = 0L;
|
||||||
|
public static final String NAME_DUMMY = "Test";
|
||||||
|
enum State {
|
||||||
|
Enabled, Disabled;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Scope {
|
||||||
|
Local, Domain, Global;
|
||||||
|
};
|
||||||
|
|
||||||
|
long getId();
|
||||||
|
String getName();
|
||||||
|
String getDescription();
|
||||||
|
State getState();
|
||||||
|
long getDomainId();
|
||||||
|
long getAccountId();
|
||||||
|
String getPayloadUrl();
|
||||||
|
String getSecretKey();
|
||||||
|
boolean isSslVerification();
|
||||||
|
Scope getScope();
|
||||||
|
Date getCreated();
|
||||||
|
}
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.response.ListResponse;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.command.user.CreateWebhookCmd;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.command.user.DeleteWebhookCmd;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.command.user.DeleteWebhookDeliveryCmd;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.command.user.ExecuteWebhookDeliveryCmd;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.command.user.ListWebhookDeliveriesCmd;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.command.user.ListWebhooksCmd;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.command.user.UpdateWebhookCmd;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.response.WebhookDeliveryResponse;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.response.WebhookResponse;
|
||||||
|
|
||||||
|
import com.cloud.utils.component.PluggableService;
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
|
||||||
|
public interface WebhookApiService extends PluggableService {
|
||||||
|
|
||||||
|
ListResponse<WebhookResponse> listWebhooks(ListWebhooksCmd cmd);
|
||||||
|
WebhookResponse createWebhook(CreateWebhookCmd cmd) throws CloudRuntimeException;
|
||||||
|
boolean deleteWebhook(DeleteWebhookCmd cmd) throws CloudRuntimeException;
|
||||||
|
WebhookResponse updateWebhook(UpdateWebhookCmd cmd) throws CloudRuntimeException;
|
||||||
|
WebhookResponse createWebhookResponse(long webhookId);
|
||||||
|
ListResponse<WebhookDeliveryResponse> listWebhookDeliveries(ListWebhookDeliveriesCmd cmd);
|
||||||
|
int deleteWebhookDelivery(DeleteWebhookDeliveryCmd cmd) throws CloudRuntimeException;
|
||||||
|
WebhookDeliveryResponse executeWebhookDelivery(ExecuteWebhookDeliveryCmd cmd) throws CloudRuntimeException;
|
||||||
|
}
|
||||||
@ -0,0 +1,574 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.acl.SecurityChecker;
|
||||||
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
|
import org.apache.cloudstack.api.response.ListResponse;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.command.user.CreateWebhookCmd;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.command.user.DeleteWebhookCmd;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.command.user.DeleteWebhookDeliveryCmd;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.command.user.ExecuteWebhookDeliveryCmd;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.command.user.ListWebhookDeliveriesCmd;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.command.user.ListWebhooksCmd;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.command.user.UpdateWebhookCmd;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.response.WebhookDeliveryResponse;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.response.WebhookResponse;
|
||||||
|
import org.apache.cloudstack.mom.webhook.dao.WebhookDao;
|
||||||
|
import org.apache.cloudstack.mom.webhook.dao.WebhookDeliveryDao;
|
||||||
|
import org.apache.cloudstack.mom.webhook.dao.WebhookDeliveryJoinDao;
|
||||||
|
import org.apache.cloudstack.mom.webhook.dao.WebhookJoinDao;
|
||||||
|
import org.apache.cloudstack.mom.webhook.vo.WebhookDeliveryJoinVO;
|
||||||
|
import org.apache.cloudstack.mom.webhook.vo.WebhookDeliveryVO;
|
||||||
|
import org.apache.cloudstack.mom.webhook.vo.WebhookJoinVO;
|
||||||
|
import org.apache.cloudstack.mom.webhook.vo.WebhookVO;
|
||||||
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import com.cloud.api.ApiResponseHelper;
|
||||||
|
import com.cloud.cluster.ManagementServerHostVO;
|
||||||
|
import com.cloud.cluster.dao.ManagementServerHostDao;
|
||||||
|
import com.cloud.domain.Domain;
|
||||||
|
import com.cloud.domain.dao.DomainDao;
|
||||||
|
import com.cloud.exception.InvalidParameterValueException;
|
||||||
|
import com.cloud.exception.PermissionDeniedException;
|
||||||
|
import com.cloud.projects.Project;
|
||||||
|
import com.cloud.user.Account;
|
||||||
|
import com.cloud.user.AccountManager;
|
||||||
|
import com.cloud.utils.Pair;
|
||||||
|
import com.cloud.utils.Ternary;
|
||||||
|
import com.cloud.utils.UriUtils;
|
||||||
|
import com.cloud.utils.component.ManagerBase;
|
||||||
|
import com.cloud.utils.db.Filter;
|
||||||
|
import com.cloud.utils.db.SearchBuilder;
|
||||||
|
import com.cloud.utils.db.SearchCriteria;
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
import com.cloud.utils.rest.HttpConstants;
|
||||||
|
|
||||||
|
public class WebhookApiServiceImpl extends ManagerBase implements WebhookApiService {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
AccountManager accountManager;
|
||||||
|
@Inject
|
||||||
|
DomainDao domainDao;
|
||||||
|
@Inject
|
||||||
|
WebhookDao webhookDao;
|
||||||
|
@Inject
|
||||||
|
WebhookJoinDao webhookJoinDao;
|
||||||
|
@Inject
|
||||||
|
WebhookDeliveryDao webhookDeliveryDao;
|
||||||
|
@Inject
|
||||||
|
WebhookDeliveryJoinDao webhookDeliveryJoinDao;
|
||||||
|
@Inject
|
||||||
|
ManagementServerHostDao managementServerHostDao;
|
||||||
|
@Inject
|
||||||
|
WebhookService webhookService;
|
||||||
|
|
||||||
|
protected WebhookResponse createWebhookResponse(WebhookJoinVO webhookVO) {
|
||||||
|
WebhookResponse response = new WebhookResponse();
|
||||||
|
response.setObjectName("webhook");
|
||||||
|
response.setId(webhookVO.getUuid());
|
||||||
|
response.setName(webhookVO.getName());
|
||||||
|
response.setDescription(webhookVO.getDescription());
|
||||||
|
ApiResponseHelper.populateOwner(response, webhookVO);
|
||||||
|
response.setState(webhookVO.getState().toString());
|
||||||
|
response.setPayloadUrl(webhookVO.getPayloadUrl());
|
||||||
|
response.setSecretKey(webhookVO.getSecretKey());
|
||||||
|
response.setSslVerification(webhookVO.isSslVerification());
|
||||||
|
response.setScope(webhookVO.getScope().toString());
|
||||||
|
response.setCreated(webhookVO.getCreated());
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<Long> getIdsOfAccessibleWebhooks(Account caller) {
|
||||||
|
if (Account.Type.ADMIN.equals(caller.getType())) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
String domainPath = null;
|
||||||
|
if (Account.Type.DOMAIN_ADMIN.equals(caller.getType())) {
|
||||||
|
Domain domain = domainDao.findById(caller.getDomainId());
|
||||||
|
domainPath = domain.getPath();
|
||||||
|
}
|
||||||
|
List<WebhookJoinVO> webhooks = webhookJoinDao.listByAccountOrDomain(caller.getId(), domainPath);
|
||||||
|
return webhooks.stream().map(WebhookJoinVO::getId).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ManagementServerHostVO basicWebhookDeliveryApiCheck(Account caller, final Long id, final Long webhookId,
|
||||||
|
final Long managementServerId, final Date startDate, final Date endDate) {
|
||||||
|
if (id != null) {
|
||||||
|
WebhookDeliveryVO webhookDeliveryVO = webhookDeliveryDao.findById(id);
|
||||||
|
if (webhookDeliveryVO == null) {
|
||||||
|
throw new InvalidParameterValueException("Invalid ID specified");
|
||||||
|
}
|
||||||
|
WebhookVO webhookVO = webhookDao.findById(webhookDeliveryVO.getWebhookId());
|
||||||
|
if (webhookVO != null) {
|
||||||
|
accountManager.checkAccess(caller, SecurityChecker.AccessType.OperateEntry, false, webhookVO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (webhookId != null) {
|
||||||
|
WebhookVO webhookVO = webhookDao.findById(webhookId);
|
||||||
|
if (webhookVO == null) {
|
||||||
|
throw new InvalidParameterValueException("Invalid Webhook specified");
|
||||||
|
}
|
||||||
|
accountManager.checkAccess(caller, SecurityChecker.AccessType.OperateEntry, false, webhookVO);
|
||||||
|
}
|
||||||
|
if (endDate != null && startDate != null && endDate.before(startDate)) {
|
||||||
|
throw new InvalidParameterValueException(String.format("Invalid %s specified", ApiConstants.END_DATE));
|
||||||
|
}
|
||||||
|
ManagementServerHostVO managementServerHostVO = null;
|
||||||
|
if (managementServerId != null) {
|
||||||
|
if (!Account.Type.ADMIN.equals(caller.getType())) {
|
||||||
|
throw new PermissionDeniedException("Invalid parameter specified");
|
||||||
|
}
|
||||||
|
managementServerHostVO = managementServerHostDao.findById(managementServerId);
|
||||||
|
if (managementServerHostVO == null) {
|
||||||
|
throw new InvalidParameterValueException("Invalid management server specified");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return managementServerHostVO;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected WebhookDeliveryResponse createWebhookDeliveryResponse(WebhookDeliveryJoinVO webhookDeliveryVO) {
|
||||||
|
WebhookDeliveryResponse response = new WebhookDeliveryResponse();
|
||||||
|
response.setObjectName(WebhookDelivery.class.getSimpleName().toLowerCase());
|
||||||
|
response.setId(webhookDeliveryVO.getUuid());
|
||||||
|
response.setEventId(webhookDeliveryVO.getEventUuid());
|
||||||
|
response.setEventType(webhookDeliveryVO.getEventType());
|
||||||
|
response.setWebhookId(webhookDeliveryVO.getWebhookUuId());
|
||||||
|
response.setWebhookName(webhookDeliveryVO.getWebhookName());
|
||||||
|
response.setManagementServerId(webhookDeliveryVO.getManagementServerUuId());
|
||||||
|
response.setManagementServerName(webhookDeliveryVO.getManagementServerName());
|
||||||
|
response.setHeaders(webhookDeliveryVO.getHeaders());
|
||||||
|
response.setPayload(webhookDeliveryVO.getPayload());
|
||||||
|
response.setSuccess(webhookDeliveryVO.isSuccess());
|
||||||
|
response.setResponse(webhookDeliveryVO.getResponse());
|
||||||
|
response.setStartTime(webhookDeliveryVO.getStartTime());
|
||||||
|
response.setEndTime(webhookDeliveryVO.getEndTime());
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected WebhookDeliveryResponse createTestWebhookDeliveryResponse(WebhookDelivery webhookDelivery,
|
||||||
|
Webhook webhook) {
|
||||||
|
WebhookDeliveryResponse response = new WebhookDeliveryResponse();
|
||||||
|
response.setObjectName(WebhookDelivery.class.getSimpleName().toLowerCase());
|
||||||
|
response.setId(webhookDelivery.getUuid());
|
||||||
|
response.setEventType(WebhookDelivery.TEST_EVENT_TYPE);
|
||||||
|
if (webhook != null) {
|
||||||
|
response.setWebhookId(webhook.getUuid());
|
||||||
|
response.setWebhookName(webhook.getName());
|
||||||
|
}
|
||||||
|
ManagementServerHostVO msHost =
|
||||||
|
managementServerHostDao.findByMsid(webhookDelivery.getManagementServerId());
|
||||||
|
if (msHost != null) {
|
||||||
|
response.setManagementServerId(msHost.getUuid());
|
||||||
|
response.setManagementServerName(msHost.getName());
|
||||||
|
}
|
||||||
|
response.setHeaders(webhookDelivery.getHeaders());
|
||||||
|
response.setPayload(webhookDelivery.getPayload());
|
||||||
|
response.setSuccess(webhookDelivery.isSuccess());
|
||||||
|
response.setResponse(webhookDelivery.getResponse());
|
||||||
|
response.setStartTime(webhookDelivery.getStartTime());
|
||||||
|
response.setEndTime(webhookDelivery.getEndTime());
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param cmd
|
||||||
|
* @return Account
|
||||||
|
*/
|
||||||
|
protected Account getOwner(final CreateWebhookCmd cmd) {
|
||||||
|
final Account caller = CallContext.current().getCallingAccount();
|
||||||
|
return accountManager.finalizeOwner(caller, cmd.getAccountName(), cmd.getDomainId(), cmd.getProjectId());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getNormalizedPayloadUrl(String payloadUrl) {
|
||||||
|
if (StringUtils.isBlank(payloadUrl) || payloadUrl.startsWith("http://") || payloadUrl.startsWith("https://")) {
|
||||||
|
return payloadUrl;
|
||||||
|
}
|
||||||
|
return String.format("http://%s", payloadUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void validateWebhookOwnerPayloadUrl(Account owner, String payloadUrl, Webhook currentWebhook) {
|
||||||
|
WebhookVO webhookVO = webhookDao.findByAccountAndPayloadUrl(owner.getId(), payloadUrl);
|
||||||
|
if (webhookVO == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (currentWebhook != null && webhookVO.getId() == currentWebhook.getId()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String error = String.format("Payload URL: %s is already in use by another webhook", payloadUrl);
|
||||||
|
logger.error(String.format("%s: %s for Account [%s]", error, webhookVO, owner));
|
||||||
|
throw new InvalidParameterValueException(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListResponse<WebhookResponse> listWebhooks(ListWebhooksCmd cmd) {
|
||||||
|
final CallContext ctx = CallContext.current();
|
||||||
|
final Account caller = ctx.getCallingAccount();
|
||||||
|
final Long clusterId = cmd.getId();
|
||||||
|
final String stateStr = cmd.getState();
|
||||||
|
final String name = cmd.getName();
|
||||||
|
final String keyword = cmd.getKeyword();
|
||||||
|
final String scopeStr = cmd.getScope();
|
||||||
|
List<WebhookResponse> responsesList = new ArrayList<>();
|
||||||
|
List<Long> permittedAccounts = new ArrayList<>();
|
||||||
|
Ternary<Long, Boolean, Project.ListProjectResourcesCriteria> domainIdRecursiveListProject =
|
||||||
|
new Ternary<>(cmd.getDomainId(), cmd.isRecursive(), null);
|
||||||
|
accountManager.buildACLSearchParameters(caller, clusterId, cmd.getAccountName(), cmd.getProjectId(),
|
||||||
|
permittedAccounts, domainIdRecursiveListProject, cmd.listAll(), false);
|
||||||
|
Long domainId = domainIdRecursiveListProject.first();
|
||||||
|
Boolean isRecursive = domainIdRecursiveListProject.second();
|
||||||
|
Project.ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
|
||||||
|
|
||||||
|
|
||||||
|
Filter searchFilter = new Filter(WebhookJoinVO.class, "id", true, cmd.getStartIndex(),
|
||||||
|
cmd.getPageSizeVal());
|
||||||
|
SearchBuilder<WebhookJoinVO> sb = webhookJoinDao.createSearchBuilder();
|
||||||
|
accountManager.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts,
|
||||||
|
listProjectResourcesCriteria);
|
||||||
|
sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ);
|
||||||
|
sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ);
|
||||||
|
sb.and("keyword", sb.entity().getName(), SearchCriteria.Op.LIKE);
|
||||||
|
sb.and("state", sb.entity().getState(), SearchCriteria.Op.EQ);
|
||||||
|
sb.and("scope", sb.entity().getScope(), SearchCriteria.Op.EQ);
|
||||||
|
SearchCriteria<WebhookJoinVO> sc = sb.create();
|
||||||
|
accountManager.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts,
|
||||||
|
listProjectResourcesCriteria);
|
||||||
|
Webhook.Scope scope = null;
|
||||||
|
if (StringUtils.isNotEmpty(scopeStr)) {
|
||||||
|
try {
|
||||||
|
scope = Webhook.Scope.valueOf(scopeStr);
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
throw new InvalidParameterValueException("Invalid scope specified");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((Webhook.Scope.Global.equals(scope) && !Account.Type.ADMIN.equals(caller.getType())) ||
|
||||||
|
(Webhook.Scope.Domain.equals(scope) &&
|
||||||
|
!List.of(Account.Type.ADMIN, Account.Type.DOMAIN_ADMIN).contains(caller.getType()))) {
|
||||||
|
throw new InvalidParameterValueException(String.format("Scope %s can not be specified", scope));
|
||||||
|
}
|
||||||
|
Webhook.State state = null;
|
||||||
|
if (StringUtils.isNotEmpty(stateStr)) {
|
||||||
|
try {
|
||||||
|
state = Webhook.State.valueOf(stateStr);
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
throw new InvalidParameterValueException("Invalid state specified");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (scope != null) {
|
||||||
|
sc.setParameters("scope", scope.name());
|
||||||
|
}
|
||||||
|
if (state != null) {
|
||||||
|
sc.setParameters("state", state.name());
|
||||||
|
}
|
||||||
|
if(keyword != null){
|
||||||
|
sc.setParameters("keyword", "%" + keyword + "%");
|
||||||
|
}
|
||||||
|
if (clusterId != null) {
|
||||||
|
sc.setParameters("id", clusterId);
|
||||||
|
}
|
||||||
|
if (name != null) {
|
||||||
|
sc.setParameters("name", name);
|
||||||
|
}
|
||||||
|
Pair<List<WebhookJoinVO>, Integer> webhooksAndCount = webhookJoinDao.searchAndCount(sc, searchFilter);
|
||||||
|
for (WebhookJoinVO webhook : webhooksAndCount.first()) {
|
||||||
|
WebhookResponse response = createWebhookResponse(webhook);
|
||||||
|
responsesList.add(response);
|
||||||
|
}
|
||||||
|
ListResponse<WebhookResponse> response = new ListResponse<>();
|
||||||
|
response.setResponses(responsesList, webhooksAndCount.second());
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WebhookResponse createWebhook(CreateWebhookCmd cmd) throws CloudRuntimeException {
|
||||||
|
final Account owner = getOwner(cmd);
|
||||||
|
final String name = cmd.getName();
|
||||||
|
final String description = cmd.getDescription();
|
||||||
|
final String payloadUrl = getNormalizedPayloadUrl(cmd.getPayloadUrl());
|
||||||
|
final String secretKey = cmd.getSecretKey();
|
||||||
|
final boolean sslVerification = cmd.isSslVerification();
|
||||||
|
final String scopeStr = cmd.getScope();
|
||||||
|
final String stateStr = cmd.getState();
|
||||||
|
Webhook.Scope scope = Webhook.Scope.Local;
|
||||||
|
if (StringUtils.isNotEmpty(scopeStr)) {
|
||||||
|
try {
|
||||||
|
scope = Webhook.Scope.valueOf(scopeStr);
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
throw new InvalidParameterValueException("Invalid scope specified");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((Webhook.Scope.Global.equals(scope) && !Account.Type.ADMIN.equals(owner.getType())) ||
|
||||||
|
(Webhook.Scope.Domain.equals(scope) &&
|
||||||
|
!List.of(Account.Type.ADMIN, Account.Type.DOMAIN_ADMIN).contains(owner.getType()))) {
|
||||||
|
throw new InvalidParameterValueException(
|
||||||
|
String.format("Scope %s can not be specified for owner %s", scope, owner.getName()));
|
||||||
|
}
|
||||||
|
Webhook.State state = Webhook.State.Enabled;
|
||||||
|
if (StringUtils.isNotEmpty(stateStr)) {
|
||||||
|
try {
|
||||||
|
state = Webhook.State.valueOf(stateStr);
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
throw new InvalidParameterValueException("Invalid state specified");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UriUtils.validateUrl(payloadUrl);
|
||||||
|
validateWebhookOwnerPayloadUrl(owner, payloadUrl, null);
|
||||||
|
URI uri = URI.create(payloadUrl);
|
||||||
|
if (sslVerification && !HttpConstants.HTTPS.equalsIgnoreCase(uri.getScheme())) {
|
||||||
|
throw new InvalidParameterValueException(
|
||||||
|
String.format("SSL verification can be specified only for HTTPS URLs, %s", payloadUrl));
|
||||||
|
}
|
||||||
|
long domainId = owner.getDomainId();
|
||||||
|
Long cmdDomainId = cmd.getDomainId();
|
||||||
|
if (cmdDomainId != null &&
|
||||||
|
List.of(Account.Type.ADMIN, Account.Type.DOMAIN_ADMIN).contains(owner.getType()) &&
|
||||||
|
Webhook.Scope.Domain.equals(scope)) {
|
||||||
|
domainId = cmdDomainId;
|
||||||
|
}
|
||||||
|
WebhookVO webhook = new WebhookVO(name, description, state, domainId, owner.getId(), payloadUrl, secretKey,
|
||||||
|
sslVerification, scope);
|
||||||
|
webhook = webhookDao.persist(webhook);
|
||||||
|
return createWebhookResponse(webhook.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean deleteWebhook(DeleteWebhookCmd cmd) throws CloudRuntimeException {
|
||||||
|
final Account caller = CallContext.current().getCallingAccount();
|
||||||
|
final long id = cmd.getId();
|
||||||
|
Webhook webhook = webhookDao.findById(id);
|
||||||
|
if (webhook == null) {
|
||||||
|
throw new InvalidParameterValueException("Unable to find the webhook with the specified ID");
|
||||||
|
}
|
||||||
|
accountManager.checkAccess(caller, SecurityChecker.AccessType.OperateEntry, false, webhook);
|
||||||
|
return webhookDao.remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WebhookResponse updateWebhook(UpdateWebhookCmd cmd) throws CloudRuntimeException {
|
||||||
|
final Account caller = CallContext.current().getCallingAccount();
|
||||||
|
final long id = cmd.getId();
|
||||||
|
final String name = cmd.getName();
|
||||||
|
final String description = cmd.getDescription();
|
||||||
|
final String payloadUrl = getNormalizedPayloadUrl(cmd.getPayloadUrl());
|
||||||
|
String secretKey = cmd.getSecretKey();
|
||||||
|
final Boolean sslVerification = cmd.isSslVerification();
|
||||||
|
final String scopeStr = cmd.getScope();
|
||||||
|
final String stateStr = cmd.getState();
|
||||||
|
WebhookVO webhook = webhookDao.findById(id);
|
||||||
|
if (webhook == null) {
|
||||||
|
throw new InvalidParameterValueException("Unable to find the webhook with the specified ID");
|
||||||
|
}
|
||||||
|
accountManager.checkAccess(caller, SecurityChecker.AccessType.OperateEntry, false, webhook);
|
||||||
|
boolean updateNeeded = false;
|
||||||
|
if (StringUtils.isNotBlank(name)) {
|
||||||
|
webhook.setName(name);
|
||||||
|
updateNeeded = true;
|
||||||
|
}
|
||||||
|
if (description != null) {
|
||||||
|
webhook.setDescription(description);
|
||||||
|
updateNeeded = true;
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotEmpty(stateStr)) {
|
||||||
|
try {
|
||||||
|
Webhook.State state = Webhook.State.valueOf(stateStr);
|
||||||
|
webhook.setState(state);
|
||||||
|
updateNeeded = true;
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
throw new InvalidParameterValueException("Invalid state specified");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Account owner = accountManager.getAccount(webhook.getAccountId());
|
||||||
|
if (StringUtils.isNotEmpty(scopeStr)) {
|
||||||
|
try {
|
||||||
|
Webhook.Scope scope = Webhook.Scope.valueOf(scopeStr);
|
||||||
|
if ((Webhook.Scope.Global.equals(scope) && !Account.Type.ADMIN.equals(owner.getType())) ||
|
||||||
|
(Webhook.Scope.Domain.equals(scope) &&
|
||||||
|
!List.of(Account.Type.ADMIN, Account.Type.DOMAIN_ADMIN).contains(owner.getType()))) {
|
||||||
|
throw new InvalidParameterValueException(
|
||||||
|
String.format("Scope %s can not be specified for owner %s", scope, owner.getName()));
|
||||||
|
}
|
||||||
|
webhook.setScope(scope);
|
||||||
|
updateNeeded = true;
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
throw new InvalidParameterValueException("Invalid scope specified");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
URI uri = URI.create(webhook.getPayloadUrl());
|
||||||
|
if (StringUtils.isNotEmpty(payloadUrl)) {
|
||||||
|
UriUtils.validateUrl(payloadUrl);
|
||||||
|
validateWebhookOwnerPayloadUrl(owner, payloadUrl, webhook);
|
||||||
|
uri = URI.create(payloadUrl);
|
||||||
|
webhook.setPayloadUrl(payloadUrl);
|
||||||
|
updateNeeded = true;
|
||||||
|
}
|
||||||
|
if (sslVerification != null) {
|
||||||
|
if (Boolean.TRUE.equals(sslVerification) && !HttpConstants.HTTPS.equalsIgnoreCase(uri.getScheme())) {
|
||||||
|
throw new InvalidParameterValueException(
|
||||||
|
String.format("SSL verification can be specified only for HTTPS URLs, %s", payloadUrl));
|
||||||
|
}
|
||||||
|
webhook.setSslVerification(sslVerification);
|
||||||
|
updateNeeded = true;
|
||||||
|
}
|
||||||
|
if (secretKey != null) {
|
||||||
|
if (StringUtils.isBlank(secretKey)) {
|
||||||
|
secretKey = null;
|
||||||
|
}
|
||||||
|
webhook.setSecretKey(secretKey);
|
||||||
|
updateNeeded = true;
|
||||||
|
}
|
||||||
|
if (updateNeeded && !webhookDao.update(id, webhook)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return createWebhookResponse(webhook.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WebhookResponse createWebhookResponse(long webhookId) {
|
||||||
|
WebhookJoinVO webhookVO = webhookJoinDao.findById(webhookId);
|
||||||
|
return createWebhookResponse(webhookVO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListResponse<WebhookDeliveryResponse> listWebhookDeliveries(ListWebhookDeliveriesCmd cmd) {
|
||||||
|
final CallContext ctx = CallContext.current();
|
||||||
|
final Account caller = ctx.getCallingAccount();
|
||||||
|
final Long id = cmd.getId();
|
||||||
|
final Long webhookId = cmd.getWebhookId();
|
||||||
|
final Long managementServerId = cmd.getManagementServerId();
|
||||||
|
final String keyword = cmd.getKeyword();
|
||||||
|
final Date startDate = cmd.getStartDate();
|
||||||
|
final Date endDate = cmd.getEndDate();
|
||||||
|
final String eventType = cmd.getEventType();
|
||||||
|
List<WebhookDeliveryResponse> responsesList = new ArrayList<>();
|
||||||
|
ManagementServerHostVO host = basicWebhookDeliveryApiCheck(caller, id, webhookId, managementServerId,
|
||||||
|
startDate, endDate);
|
||||||
|
|
||||||
|
Filter searchFilter = new Filter(WebhookDeliveryJoinVO.class, "id", false, cmd.getStartIndex(),
|
||||||
|
cmd.getPageSizeVal());
|
||||||
|
List<Long> webhookIds = new ArrayList<>();
|
||||||
|
if (webhookId != null) {
|
||||||
|
webhookIds.add(webhookId);
|
||||||
|
} else {
|
||||||
|
webhookIds.addAll(getIdsOfAccessibleWebhooks(caller));
|
||||||
|
}
|
||||||
|
Pair<List<WebhookDeliveryJoinVO>, Integer> deliveriesAndCount =
|
||||||
|
webhookDeliveryJoinDao.searchAndCountByListApiParameters(id, webhookIds,
|
||||||
|
(host != null ? host.getMsid() : null), keyword, startDate, endDate, eventType, searchFilter);
|
||||||
|
for (WebhookDeliveryJoinVO delivery : deliveriesAndCount.first()) {
|
||||||
|
WebhookDeliveryResponse response = createWebhookDeliveryResponse(delivery);
|
||||||
|
responsesList.add(response);
|
||||||
|
}
|
||||||
|
ListResponse<WebhookDeliveryResponse> response = new ListResponse<>();
|
||||||
|
response.setResponses(responsesList, deliveriesAndCount.second());
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int deleteWebhookDelivery(DeleteWebhookDeliveryCmd cmd) throws CloudRuntimeException {
|
||||||
|
final CallContext ctx = CallContext.current();
|
||||||
|
final Account caller = ctx.getCallingAccount();
|
||||||
|
final Long id = cmd.getId();
|
||||||
|
final Long webhookId = cmd.getWebhookId();
|
||||||
|
final Long managementServerId = cmd.getManagementServerId();
|
||||||
|
final Date startDate = cmd.getStartDate();
|
||||||
|
final Date endDate = cmd.getEndDate();
|
||||||
|
ManagementServerHostVO host = basicWebhookDeliveryApiCheck(caller, id, webhookId, managementServerId,
|
||||||
|
startDate, endDate);
|
||||||
|
int removed = webhookDeliveryDao.deleteByDeleteApiParams(id, webhookId,
|
||||||
|
(host != null ? host.getMsid() : null), startDate, endDate);
|
||||||
|
logger.info("{} webhook deliveries removed", removed);
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WebhookDeliveryResponse executeWebhookDelivery(ExecuteWebhookDeliveryCmd cmd) throws CloudRuntimeException {
|
||||||
|
final CallContext ctx = CallContext.current();
|
||||||
|
final Account caller = ctx.getCallingAccount();
|
||||||
|
final Long deliveryId = cmd.getId();
|
||||||
|
final Long webhookId = cmd.getWebhookId();
|
||||||
|
final String payloadUrl = getNormalizedPayloadUrl(cmd.getPayloadUrl());
|
||||||
|
final String secretKey = cmd.getSecretKey();
|
||||||
|
final Boolean sslVerification = cmd.isSslVerification();
|
||||||
|
final String payload = cmd.getPayload();
|
||||||
|
final Account owner = accountManager.finalizeOwner(caller, null, null, null);
|
||||||
|
|
||||||
|
if (ObjectUtils.allNull(deliveryId, webhookId) && StringUtils.isBlank(payloadUrl)) {
|
||||||
|
throw new InvalidParameterValueException(String.format("One of the %s, %s or %s must be specified",
|
||||||
|
ApiConstants.ID, ApiConstants.WEBHOOK_ID, ApiConstants.PAYLOAD_URL));
|
||||||
|
}
|
||||||
|
WebhookDeliveryVO existingDelivery = null;
|
||||||
|
WebhookVO webhook = null;
|
||||||
|
if (deliveryId != null) {
|
||||||
|
existingDelivery = webhookDeliveryDao.findById(deliveryId);
|
||||||
|
if (existingDelivery == null) {
|
||||||
|
throw new InvalidParameterValueException("Invalid webhook delivery specified");
|
||||||
|
}
|
||||||
|
webhook = webhookDao.findById(existingDelivery.getWebhookId());
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(payloadUrl)) {
|
||||||
|
UriUtils.validateUrl(payloadUrl);
|
||||||
|
}
|
||||||
|
if (webhookId != null) {
|
||||||
|
webhook = webhookDao.findById(webhookId);
|
||||||
|
if (webhook == null) {
|
||||||
|
throw new InvalidParameterValueException("Invalid webhook specified");
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(payloadUrl)) {
|
||||||
|
webhook.setPayloadUrl(payloadUrl);
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(secretKey)) {
|
||||||
|
webhook.setSecretKey(secretKey);
|
||||||
|
}
|
||||||
|
if (sslVerification != null) {
|
||||||
|
webhook.setSslVerification(Boolean.TRUE.equals(sslVerification));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ObjectUtils.allNull(deliveryId, webhookId)) {
|
||||||
|
webhook = new WebhookVO(owner.getDomainId(), owner.getId(), payloadUrl, secretKey,
|
||||||
|
Boolean.TRUE.equals(sslVerification));
|
||||||
|
}
|
||||||
|
WebhookDelivery webhookDelivery = webhookService.executeWebhookDelivery(existingDelivery, webhook, payload);
|
||||||
|
if (webhookDelivery.getId() != WebhookDelivery.ID_DUMMY) {
|
||||||
|
return createWebhookDeliveryResponse(webhookDeliveryJoinDao.findById(webhookDelivery.getId()));
|
||||||
|
}
|
||||||
|
return createTestWebhookDeliveryResponse(webhookDelivery, webhook);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Class<?>> getCommands() {
|
||||||
|
List<Class<?>> cmdList = new ArrayList<>();
|
||||||
|
cmdList.add(CreateWebhookCmd.class);
|
||||||
|
cmdList.add(ListWebhooksCmd.class);
|
||||||
|
cmdList.add(UpdateWebhookCmd.class);
|
||||||
|
cmdList.add(DeleteWebhookCmd.class);
|
||||||
|
cmdList.add(ListWebhookDeliveriesCmd.class);
|
||||||
|
cmdList.add(DeleteWebhookDeliveryCmd.class);
|
||||||
|
cmdList.add(ExecuteWebhookDeliveryCmd.class);
|
||||||
|
return cmdList;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.Identity;
|
||||||
|
import org.apache.cloudstack.api.InternalIdentity;
|
||||||
|
|
||||||
|
public interface WebhookDelivery extends Identity, InternalIdentity {
|
||||||
|
public static final long ID_DUMMY = 0L;
|
||||||
|
public static final String TEST_EVENT_TYPE = "TEST.WEBHOOK";
|
||||||
|
|
||||||
|
long getId();
|
||||||
|
long getEventId();
|
||||||
|
long getWebhookId();
|
||||||
|
long getManagementServerId();
|
||||||
|
String getHeaders();
|
||||||
|
String getPayload();
|
||||||
|
boolean isSuccess();
|
||||||
|
String getResponse();
|
||||||
|
Date getStartTime();
|
||||||
|
Date getEndTime();
|
||||||
|
}
|
||||||
@ -0,0 +1,287 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.KeyManagementException;
|
||||||
|
import java.security.KeyStoreException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.crypto.Mac;
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
|
||||||
|
import org.apache.cloudstack.framework.async.AsyncRpcContext;
|
||||||
|
import org.apache.cloudstack.framework.events.Event;
|
||||||
|
import org.apache.cloudstack.storage.command.CommandResult;
|
||||||
|
import org.apache.commons.codec.DecoderException;
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
|
import org.apache.commons.httpclient.HttpStatus;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.http.Header;
|
||||||
|
import org.apache.http.HttpEntity;
|
||||||
|
import org.apache.http.HttpHeaders;
|
||||||
|
import org.apache.http.client.config.RequestConfig;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.conn.ssl.NoopHostnameVerifier;
|
||||||
|
import org.apache.http.conn.ssl.TrustAllStrategy;
|
||||||
|
import org.apache.http.entity.ContentType;
|
||||||
|
import org.apache.http.entity.StringEntity;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClients;
|
||||||
|
import org.apache.http.message.BasicHeader;
|
||||||
|
import org.apache.http.ssl.SSLContextBuilder;
|
||||||
|
import org.apache.http.util.EntityUtils;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
public class WebhookDeliveryThread implements Runnable {
|
||||||
|
protected static Logger LOGGER = LogManager.getLogger(WebhookDeliveryThread.class);
|
||||||
|
|
||||||
|
private static final String HEADER_X_CS_EVENT_ID = "X-CS-Event-ID";
|
||||||
|
private static final String HEADER_X_CS_EVENT = "X-CS-Event";
|
||||||
|
private static final String HEADER_X_CS_SIGNATURE = "X-CS-Signature";
|
||||||
|
private static final String PREFIX_HEADER_USER_AGENT = "CS-Hookshot/";
|
||||||
|
private final Webhook webhook;
|
||||||
|
private final Event event;
|
||||||
|
private CloseableHttpClient httpClient;
|
||||||
|
private String headers;
|
||||||
|
private String payload;
|
||||||
|
private String response;
|
||||||
|
private Date startTime;
|
||||||
|
private int deliveryTries = 3;
|
||||||
|
private int deliveryTimeout = 10;
|
||||||
|
|
||||||
|
AsyncCompletionCallback<WebhookDeliveryResult> callback;
|
||||||
|
|
||||||
|
protected boolean isValidJson(String json) {
|
||||||
|
try {
|
||||||
|
new JSONObject(json);
|
||||||
|
} catch (JSONException ex) {
|
||||||
|
try {
|
||||||
|
new JSONArray(json);
|
||||||
|
} catch (JSONException ex1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setHttpClient() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
|
||||||
|
if (webhook.isSslVerification()) {
|
||||||
|
httpClient = HttpClients.createDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
httpClient = HttpClients
|
||||||
|
.custom()
|
||||||
|
.setSSLContext(new SSLContextBuilder().loadTrustMaterial(null,
|
||||||
|
TrustAllStrategy.INSTANCE).build())
|
||||||
|
.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected HttpPost getBasicHttpPostRequest() throws URISyntaxException {
|
||||||
|
final URI uri = new URI(webhook.getPayloadUrl());
|
||||||
|
HttpPost request = new HttpPost();
|
||||||
|
RequestConfig.Builder requestConfig = RequestConfig.custom();
|
||||||
|
requestConfig.setConnectTimeout(deliveryTimeout * 1000);
|
||||||
|
requestConfig.setConnectionRequestTimeout(deliveryTimeout * 1000);
|
||||||
|
requestConfig.setSocketTimeout(deliveryTimeout * 1000);
|
||||||
|
request.setConfig(requestConfig.build());
|
||||||
|
request.setURI(uri);
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void updateRequestHeaders(HttpPost request) throws DecoderException, NoSuchAlgorithmException,
|
||||||
|
InvalidKeyException {
|
||||||
|
request.addHeader(HEADER_X_CS_EVENT_ID, event.getEventUuid());
|
||||||
|
request.addHeader(HEADER_X_CS_EVENT, event.getEventType());
|
||||||
|
request.setHeader(HttpHeaders.USER_AGENT, String.format("%s%s", PREFIX_HEADER_USER_AGENT,
|
||||||
|
event.getResourceAccountUuid()));
|
||||||
|
if (StringUtils.isNotBlank(webhook.getSecretKey())) {
|
||||||
|
request.addHeader(HEADER_X_CS_SIGNATURE, generateHMACSignature(payload, webhook.getSecretKey()));
|
||||||
|
}
|
||||||
|
List<Header> headers = new ArrayList<>(Arrays.asList(request.getAllHeaders()));
|
||||||
|
HttpEntity entity = request.getEntity();
|
||||||
|
if (entity.getContentLength() > 0 && !request.containsHeader(HttpHeaders.CONTENT_LENGTH)) {
|
||||||
|
headers.add(new BasicHeader(HttpHeaders.CONTENT_LENGTH, Long.toString(entity.getContentLength())));
|
||||||
|
}
|
||||||
|
if (entity.getContentType() != null && !request.containsHeader(HttpHeaders.CONTENT_TYPE)) {
|
||||||
|
headers.add(entity.getContentType());
|
||||||
|
}
|
||||||
|
if (entity.getContentEncoding() != null && !request.containsHeader(HttpHeaders.CONTENT_ENCODING)) {
|
||||||
|
headers.add(entity.getContentEncoding());
|
||||||
|
}
|
||||||
|
this.headers = StringUtils.join(headers, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebhookDeliveryThread(Webhook webhook, Event event,
|
||||||
|
AsyncCompletionCallback<WebhookDeliveryResult> callback) {
|
||||||
|
this.webhook = webhook;
|
||||||
|
this.event = event;
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeliveryTries(int deliveryTries) {
|
||||||
|
this.deliveryTries = deliveryTries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeliveryTimeout(int deliveryTimeout) {
|
||||||
|
this.deliveryTimeout = deliveryTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
LOGGER.debug("Delivering event: {} for {}", event.getEventType(), webhook);
|
||||||
|
if (event == null) {
|
||||||
|
LOGGER.warn("Invalid event received for delivering to {}", webhook);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
payload = event.getDescription();
|
||||||
|
LOGGER.trace("Payload: {}", payload);
|
||||||
|
int attempt = 0;
|
||||||
|
boolean success = false;
|
||||||
|
try {
|
||||||
|
setHttpClient();
|
||||||
|
} catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException e) {
|
||||||
|
response = String.format("Failed to initiate delivery due to : %s", e.getMessage());
|
||||||
|
callback.complete(new WebhookDeliveryResult(headers, payload, success, response, new Date()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while (attempt < deliveryTries) {
|
||||||
|
attempt++;
|
||||||
|
if (delivery(attempt)) {
|
||||||
|
success = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callback.complete(new WebhookDeliveryResult(headers, payload, success, response, startTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void updateResponseFromRequest(HttpEntity entity) {
|
||||||
|
try {
|
||||||
|
this.response = EntityUtils.toString(entity, StandardCharsets.UTF_8);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.error("Failed to parse response for event: {} for {}",
|
||||||
|
event.getEventType(), webhook);
|
||||||
|
this.response = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean delivery(int attempt) {
|
||||||
|
startTime = new Date();
|
||||||
|
try {
|
||||||
|
HttpPost request = getBasicHttpPostRequest();
|
||||||
|
StringEntity input = new StringEntity(payload,
|
||||||
|
isValidJson(payload) ? ContentType.APPLICATION_JSON : ContentType.TEXT_PLAIN);
|
||||||
|
request.setEntity(input);
|
||||||
|
updateRequestHeaders(request);
|
||||||
|
LOGGER.trace("Delivering event: {} for {} with timeout: {}, " +
|
||||||
|
"attempt #{}", event.getEventType(), webhook,
|
||||||
|
deliveryTimeout, attempt);
|
||||||
|
final CloseableHttpResponse response = httpClient.execute(request);
|
||||||
|
updateResponseFromRequest(response.getEntity());
|
||||||
|
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
|
||||||
|
LOGGER.trace("Successfully delivered event: {} for {}",
|
||||||
|
event.getEventType(), webhook);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (URISyntaxException | IOException | DecoderException | NoSuchAlgorithmException |
|
||||||
|
InvalidKeyException e) {
|
||||||
|
LOGGER.warn("Failed to deliver {}, in attempt #{} due to: {}",
|
||||||
|
webhook, attempt, e.getMessage());
|
||||||
|
response = String.format("Failed due to : %s", e.getMessage());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String generateHMACSignature(String data, String key)
|
||||||
|
throws InvalidKeyException, NoSuchAlgorithmException, DecoderException {
|
||||||
|
Mac mac = Mac.getInstance("HMACSHA256");
|
||||||
|
SecretKey secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), mac.getAlgorithm());
|
||||||
|
mac.init(secretKey);
|
||||||
|
byte[] dataAsBytes = data.getBytes(StandardCharsets.UTF_8);
|
||||||
|
byte[] encodedText = mac.doFinal(dataAsBytes);
|
||||||
|
return new String(Base64.encodeBase64(encodedText)).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class WebhookDeliveryContext<T> extends AsyncRpcContext<T> {
|
||||||
|
private final Long eventId;
|
||||||
|
private final Long ruleId;
|
||||||
|
|
||||||
|
public WebhookDeliveryContext(AsyncCompletionCallback<T> callback, Long eventId, Long ruleId) {
|
||||||
|
super(callback);
|
||||||
|
this.eventId = eventId;
|
||||||
|
this.ruleId = ruleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getEventId() {
|
||||||
|
return eventId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getRuleId() {
|
||||||
|
return ruleId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class WebhookDeliveryResult extends CommandResult {
|
||||||
|
private final String headers;
|
||||||
|
private final String payload;
|
||||||
|
private final Date starTime;
|
||||||
|
private final Date endTime;
|
||||||
|
|
||||||
|
public WebhookDeliveryResult(String headers, String payload, boolean success, String response, Date starTime) {
|
||||||
|
super();
|
||||||
|
this.headers = headers;
|
||||||
|
this.payload = payload;
|
||||||
|
this.setResult(response);
|
||||||
|
this.setSuccess(success);
|
||||||
|
this.starTime = starTime;
|
||||||
|
this.endTime = new Date();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHeaders() {
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPayload() {
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getStarTime() {
|
||||||
|
return starTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getEndTime() {
|
||||||
|
return endTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.naming.ConfigurationException;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.framework.events.Event;
|
||||||
|
import org.apache.cloudstack.framework.events.EventBus;
|
||||||
|
import org.apache.cloudstack.framework.events.EventBusException;
|
||||||
|
import org.apache.cloudstack.framework.events.EventSubscriber;
|
||||||
|
import org.apache.cloudstack.framework.events.EventTopic;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import com.cloud.utils.component.ManagerBase;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
|
||||||
|
public class WebhookEventBus extends ManagerBase implements EventBus {
|
||||||
|
|
||||||
|
protected static Logger LOGGER = LogManager.getLogger(WebhookEventBus.class);
|
||||||
|
private static Gson gson;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
WebhookService webhookService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
|
||||||
|
_name = name;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setName(String name) {
|
||||||
|
_name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UUID subscribe(EventTopic topic, EventSubscriber subscriber) throws EventBusException {
|
||||||
|
/* NOOP */
|
||||||
|
return UUID.randomUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unsubscribe(UUID subscriberId, EventSubscriber subscriber) throws EventBusException {
|
||||||
|
/* NOOP */
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void publish(Event event) throws EventBusException {
|
||||||
|
webhookService.handleEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return _name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean start() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean stop() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||||
|
import org.apache.cloudstack.framework.config.Configurable;
|
||||||
|
import org.apache.cloudstack.framework.events.Event;
|
||||||
|
import org.apache.cloudstack.framework.events.EventBusException;
|
||||||
|
|
||||||
|
import com.cloud.utils.component.PluggableService;
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
|
||||||
|
public interface WebhookService extends PluggableService, Configurable {
|
||||||
|
|
||||||
|
ConfigKey<Integer> WebhookDeliveryTimeout = new ConfigKey<>("Advanced", Integer.class,
|
||||||
|
"webhook.delivery.timeout", "10",
|
||||||
|
"Wait timeout (in seconds) for a webhook delivery delivery",
|
||||||
|
true, ConfigKey.Scope.Domain);
|
||||||
|
|
||||||
|
ConfigKey<Integer> WebhookDeliveryTries = new ConfigKey<>("Advanced", Integer.class,
|
||||||
|
"webhook.delivery.tries", "3",
|
||||||
|
"Number of tries to be made for a webhook delivery",
|
||||||
|
true, ConfigKey.Scope.Domain);
|
||||||
|
|
||||||
|
ConfigKey<Integer> WebhookDeliveryThreadPoolSize = new ConfigKey<>("Advanced", Integer.class,
|
||||||
|
"webhook.delivery.thread.pool.size", "5",
|
||||||
|
"Size of the thread pool for webhook deliveries",
|
||||||
|
false, ConfigKey.Scope.Global);
|
||||||
|
|
||||||
|
ConfigKey<Integer> WebhookDeliveriesLimit = new ConfigKey<>("Advanced", Integer.class,
|
||||||
|
"webhook.deliveries.limit", "10",
|
||||||
|
"Limit for the number of deliveries to keep in DB per webhook",
|
||||||
|
true, ConfigKey.Scope.Global);
|
||||||
|
|
||||||
|
ConfigKey<Integer> WebhookDeliveriesCleanupInitialDelay = new ConfigKey<>("Advanced", Integer.class,
|
||||||
|
"webhook.deliveries.cleanup.initial.delay", "180",
|
||||||
|
"Initial delay (in seconds) for webhook deliveries cleanup task",
|
||||||
|
false, ConfigKey.Scope.Global);
|
||||||
|
|
||||||
|
ConfigKey<Integer> WebhookDeliveriesCleanupInterval = new ConfigKey<>("Advanced", Integer.class,
|
||||||
|
"webhook.deliveries.cleanup.interval", "3600",
|
||||||
|
"Interval (in seconds) for cleaning up webhook deliveries",
|
||||||
|
false, ConfigKey.Scope.Global);
|
||||||
|
|
||||||
|
void handleEvent(Event event) throws EventBusException;
|
||||||
|
WebhookDelivery executeWebhookDelivery(WebhookDelivery delivery, Webhook webhook, String payload)
|
||||||
|
throws CloudRuntimeException;
|
||||||
|
}
|
||||||
@ -0,0 +1,354 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.naming.ConfigurationException;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.acl.ControlledEntity;
|
||||||
|
import org.apache.cloudstack.framework.async.AsyncCallFuture;
|
||||||
|
import org.apache.cloudstack.framework.async.AsyncCallbackDispatcher;
|
||||||
|
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
|
||||||
|
import org.apache.cloudstack.framework.async.AsyncRpcContext;
|
||||||
|
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||||
|
import org.apache.cloudstack.framework.events.Event;
|
||||||
|
import org.apache.cloudstack.framework.events.EventBusException;
|
||||||
|
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
|
||||||
|
import org.apache.cloudstack.mom.webhook.dao.WebhookDao;
|
||||||
|
import org.apache.cloudstack.mom.webhook.dao.WebhookDeliveryDao;
|
||||||
|
import org.apache.cloudstack.mom.webhook.vo.WebhookDeliveryVO;
|
||||||
|
import org.apache.cloudstack.mom.webhook.vo.WebhookVO;
|
||||||
|
import org.apache.cloudstack.utils.identity.ManagementServerNode;
|
||||||
|
import org.apache.cloudstack.webhook.WebhookHelper;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import com.cloud.api.query.vo.EventJoinVO;
|
||||||
|
import com.cloud.cluster.ManagementServerHostVO;
|
||||||
|
import com.cloud.cluster.dao.ManagementServerHostDao;
|
||||||
|
import com.cloud.domain.dao.DomainDao;
|
||||||
|
import com.cloud.event.EventCategory;
|
||||||
|
import com.cloud.event.dao.EventJoinDao;
|
||||||
|
import com.cloud.server.ManagementService;
|
||||||
|
import com.cloud.user.Account;
|
||||||
|
import com.cloud.user.AccountManager;
|
||||||
|
import com.cloud.utils.Pair;
|
||||||
|
import com.cloud.utils.component.ComponentContext;
|
||||||
|
import com.cloud.utils.component.ManagerBase;
|
||||||
|
import com.cloud.utils.concurrency.NamedThreadFactory;
|
||||||
|
import com.cloud.utils.db.Filter;
|
||||||
|
import com.cloud.utils.db.GlobalLock;
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
|
||||||
|
public class WebhookServiceImpl extends ManagerBase implements WebhookService, WebhookHelper {
|
||||||
|
public static final String WEBHOOK_JOB_POOL_THREAD_PREFIX = "Webhook-Job-Executor";
|
||||||
|
private ExecutorService webhookJobExecutor;
|
||||||
|
private ScheduledExecutorService webhookDeliveriesCleanupExecutor;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
EventJoinDao eventJoinDao;
|
||||||
|
@Inject
|
||||||
|
WebhookDao webhookDao;
|
||||||
|
@Inject
|
||||||
|
protected WebhookDeliveryDao webhookDeliveryDao;
|
||||||
|
@Inject
|
||||||
|
ManagementServerHostDao managementServerHostDao;
|
||||||
|
@Inject
|
||||||
|
DomainDao domainDao;
|
||||||
|
@Inject
|
||||||
|
AccountManager accountManager;
|
||||||
|
|
||||||
|
protected WebhookDeliveryThread getDeliveryJob(Event event, Webhook webhook, Pair<Integer, Integer> configs) {
|
||||||
|
WebhookDeliveryThread.WebhookDeliveryContext<WebhookDeliveryThread.WebhookDeliveryResult> context =
|
||||||
|
new WebhookDeliveryThread.WebhookDeliveryContext<>(null, event.getEventId(), webhook.getId());
|
||||||
|
AsyncCallbackDispatcher<WebhookServiceImpl, WebhookDeliveryThread.WebhookDeliveryResult> caller =
|
||||||
|
AsyncCallbackDispatcher.create(this);
|
||||||
|
caller.setCallback(caller.getTarget().deliveryCompleteCallback(null, null))
|
||||||
|
.setContext(context);
|
||||||
|
WebhookDeliveryThread job = new WebhookDeliveryThread(webhook, event, caller);
|
||||||
|
job = ComponentContext.inject(job);
|
||||||
|
job.setDeliveryTries(configs.first());
|
||||||
|
job.setDeliveryTimeout(configs.second());
|
||||||
|
return job;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<Runnable> getDeliveryJobs(Event event) throws EventBusException {
|
||||||
|
List<Runnable> jobs = new ArrayList<>();
|
||||||
|
if (!EventCategory.ACTION_EVENT.getName().equals(event.getEventCategory())) {
|
||||||
|
return jobs;
|
||||||
|
}
|
||||||
|
if (event.getResourceAccountId() == null) {
|
||||||
|
logger.warn("Skipping delivering event [ID: {}, description: {}] to any webhook as account ID is missing",
|
||||||
|
event.getEventId(), event.getDescription());
|
||||||
|
throw new EventBusException(String.format("Account missing for the event ID: %s", event.getEventUuid()));
|
||||||
|
}
|
||||||
|
List<Long> domainIds = new ArrayList<>();
|
||||||
|
if (event.getResourceDomainId() != null) {
|
||||||
|
domainIds.add(event.getResourceDomainId());
|
||||||
|
domainIds.addAll(domainDao.getDomainParentIds(event.getResourceDomainId()));
|
||||||
|
}
|
||||||
|
List<WebhookVO> webhooks =
|
||||||
|
webhookDao.listByEnabledForDelivery(event.getResourceAccountId(), domainIds);
|
||||||
|
Map<Long, Pair<Integer, Integer>> domainConfigs = new HashMap<>();
|
||||||
|
for (WebhookVO webhook : webhooks) {
|
||||||
|
if (!domainConfigs.containsKey(webhook.getDomainId())) {
|
||||||
|
domainConfigs.put(webhook.getDomainId(),
|
||||||
|
new Pair<>(WebhookDeliveryTries.valueIn(webhook.getDomainId()),
|
||||||
|
WebhookDeliveryTimeout.valueIn(webhook.getDomainId())));
|
||||||
|
}
|
||||||
|
Pair<Integer, Integer> configs = domainConfigs.get(webhook.getDomainId());
|
||||||
|
WebhookDeliveryThread job = getDeliveryJob(event, webhook, configs);
|
||||||
|
jobs.add(job);
|
||||||
|
}
|
||||||
|
return jobs;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Runnable getManualDeliveryJob(WebhookDelivery existingDelivery, Webhook webhook, String payload,
|
||||||
|
AsyncCallFuture<WebhookDeliveryThread.WebhookDeliveryResult> future) {
|
||||||
|
if (StringUtils.isBlank(payload)) {
|
||||||
|
payload = "{ \"CloudStack\": \"works!\" }";
|
||||||
|
}
|
||||||
|
long eventId = Webhook.ID_DUMMY;
|
||||||
|
String eventType = WebhookDelivery.TEST_EVENT_TYPE;
|
||||||
|
String eventUuid = UUID.randomUUID().toString();
|
||||||
|
String description = payload;
|
||||||
|
String resourceAccountUuid = null;
|
||||||
|
if (existingDelivery != null) {
|
||||||
|
EventJoinVO eventJoinVO = eventJoinDao.findById(existingDelivery.getEventId());
|
||||||
|
eventId = eventJoinVO.getId();
|
||||||
|
eventType = eventJoinVO.getType();
|
||||||
|
eventUuid = eventJoinVO.getUuid();
|
||||||
|
description = existingDelivery.getPayload();
|
||||||
|
resourceAccountUuid = eventJoinVO.getAccountUuid();
|
||||||
|
} else {
|
||||||
|
Account account = accountManager.getAccount(webhook.getAccountId());
|
||||||
|
resourceAccountUuid = account.getUuid();
|
||||||
|
}
|
||||||
|
Event event = new Event(ManagementService.Name, EventCategory.ACTION_EVENT.toString(),
|
||||||
|
eventType, null, null);
|
||||||
|
event.setEventId(eventId);
|
||||||
|
event.setEventUuid(eventUuid);
|
||||||
|
event.setDescription(description);
|
||||||
|
event.setResourceAccountUuid(resourceAccountUuid);
|
||||||
|
ManualDeliveryContext<WebhookDeliveryThread.WebhookDeliveryResult> context =
|
||||||
|
new ManualDeliveryContext<>(null, webhook, future);
|
||||||
|
AsyncCallbackDispatcher<WebhookServiceImpl, WebhookDeliveryThread.WebhookDeliveryResult> caller =
|
||||||
|
AsyncCallbackDispatcher.create(this);
|
||||||
|
caller.setCallback(caller.getTarget().manualDeliveryCompleteCallback(null, null))
|
||||||
|
.setContext(context);
|
||||||
|
WebhookDeliveryThread job = new WebhookDeliveryThread(webhook, event, caller);
|
||||||
|
job.setDeliveryTries(WebhookDeliveryTries.valueIn(webhook.getDomainId()));
|
||||||
|
job.setDeliveryTimeout(WebhookDeliveryTimeout.valueIn(webhook.getDomainId()));
|
||||||
|
return job;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Void deliveryCompleteCallback(
|
||||||
|
AsyncCallbackDispatcher<WebhookServiceImpl, WebhookDeliveryThread.WebhookDeliveryResult> callback,
|
||||||
|
WebhookDeliveryThread.WebhookDeliveryContext<Webhook> context) {
|
||||||
|
WebhookDeliveryThread.WebhookDeliveryResult result = callback.getResult();
|
||||||
|
WebhookDeliveryVO deliveryVO = new WebhookDeliveryVO(context.getEventId(), context.getRuleId(),
|
||||||
|
ManagementServerNode.getManagementServerId(), result.getHeaders(), result.getPayload(),
|
||||||
|
result.isSuccess(), result.getResult(), result.getStarTime(), result.getEndTime());
|
||||||
|
webhookDeliveryDao.persist(deliveryVO);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Void manualDeliveryCompleteCallback(
|
||||||
|
AsyncCallbackDispatcher<WebhookServiceImpl, WebhookDeliveryThread.WebhookDeliveryResult> callback,
|
||||||
|
ManualDeliveryContext<WebhookDeliveryThread.WebhookDeliveryResult> context) {
|
||||||
|
WebhookDeliveryThread.WebhookDeliveryResult result = callback.getResult();
|
||||||
|
context.future.complete(result);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected long cleanupOldWebhookDeliveries(long deliveriesLimit) {
|
||||||
|
Filter filter = new Filter(WebhookVO.class, "id", true, 0L, 50L);
|
||||||
|
Pair<List<WebhookVO>, Integer> webhooksAndCount =
|
||||||
|
webhookDao.searchAndCount(webhookDao.createSearchCriteria(), filter);
|
||||||
|
List<WebhookVO> webhooks = webhooksAndCount.first();
|
||||||
|
long count = webhooksAndCount.second();
|
||||||
|
long processed = 0;
|
||||||
|
do {
|
||||||
|
for (WebhookVO webhook : webhooks) {
|
||||||
|
webhookDeliveryDao.removeOlderDeliveries(webhook.getId(), deliveriesLimit);
|
||||||
|
processed++;
|
||||||
|
}
|
||||||
|
if (processed < count) {
|
||||||
|
filter.setOffset(processed);
|
||||||
|
webhooks = webhookDao.listAll(filter);
|
||||||
|
}
|
||||||
|
} while (processed < count);
|
||||||
|
return processed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
|
||||||
|
try {
|
||||||
|
webhookJobExecutor = Executors.newFixedThreadPool(WebhookDeliveryThreadPoolSize.value(),
|
||||||
|
new NamedThreadFactory(WEBHOOK_JOB_POOL_THREAD_PREFIX));
|
||||||
|
webhookDeliveriesCleanupExecutor = Executors.newScheduledThreadPool(1,
|
||||||
|
new NamedThreadFactory("Webhook-Deliveries-Cleanup-Worker"));
|
||||||
|
} catch (final Exception e) {
|
||||||
|
throw new ConfigurationException("Unable to to configure WebhookServiceImpl");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean start() {
|
||||||
|
long webhookDeliveriesCleanupInitialDelay = WebhookDeliveriesCleanupInitialDelay.value();
|
||||||
|
long webhookDeliveriesCleanupInterval = WebhookDeliveriesCleanupInterval.value();
|
||||||
|
logger.debug("Scheduling webhook deliveries cleanup task with initial delay={}s and interval={}s",
|
||||||
|
webhookDeliveriesCleanupInitialDelay, webhookDeliveriesCleanupInterval);
|
||||||
|
webhookDeliveriesCleanupExecutor.scheduleWithFixedDelay(new WebhookDeliveryCleanupWorker(),
|
||||||
|
webhookDeliveriesCleanupInitialDelay, webhookDeliveriesCleanupInterval, TimeUnit.SECONDS);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean stop() {
|
||||||
|
webhookJobExecutor.shutdown();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getConfigComponentName() {
|
||||||
|
return WebhookService.class.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigKey<?>[] getConfigKeys() {
|
||||||
|
return new ConfigKey[]{
|
||||||
|
WebhookDeliveryTimeout,
|
||||||
|
WebhookDeliveryTries,
|
||||||
|
WebhookDeliveryThreadPoolSize,
|
||||||
|
WebhookDeliveriesLimit,
|
||||||
|
WebhookDeliveriesCleanupInitialDelay,
|
||||||
|
WebhookDeliveriesCleanupInterval
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteWebhooksForAccount(long accountId) {
|
||||||
|
webhookDao.deleteByAccount(accountId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<? extends ControlledEntity> listWebhooksByAccount(long accountId) {
|
||||||
|
return webhookDao.listByAccount(accountId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleEvent(Event event) throws EventBusException {
|
||||||
|
List<Runnable> jobs = getDeliveryJobs(event);
|
||||||
|
for(Runnable job : jobs) {
|
||||||
|
webhookJobExecutor.submit(job);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WebhookDelivery executeWebhookDelivery(WebhookDelivery delivery, Webhook webhook, String payload)
|
||||||
|
throws CloudRuntimeException {
|
||||||
|
AsyncCallFuture<WebhookDeliveryThread.WebhookDeliveryResult> future = new AsyncCallFuture<>();
|
||||||
|
Runnable job = getManualDeliveryJob(delivery, webhook, payload, future);
|
||||||
|
webhookJobExecutor.submit(job);
|
||||||
|
WebhookDeliveryThread.WebhookDeliveryResult result = null;
|
||||||
|
WebhookDeliveryVO webhookDeliveryVO;
|
||||||
|
try {
|
||||||
|
result = future.get();
|
||||||
|
if (delivery != null) {
|
||||||
|
webhookDeliveryVO = new WebhookDeliveryVO(delivery.getEventId(), delivery.getWebhookId(),
|
||||||
|
ManagementServerNode.getManagementServerId(), result.getHeaders(), result.getPayload(),
|
||||||
|
result.isSuccess(), result.getResult(), result.getStarTime(), result.getEndTime());
|
||||||
|
webhookDeliveryVO = webhookDeliveryDao.persist(webhookDeliveryVO);
|
||||||
|
} else {
|
||||||
|
webhookDeliveryVO = new WebhookDeliveryVO(ManagementServerNode.getManagementServerId(),
|
||||||
|
result.getHeaders(), result.getPayload(), result.isSuccess(), result.getResult(),
|
||||||
|
result.getStarTime(), result.getEndTime());
|
||||||
|
}
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
|
logger.error(String.format("Failed to execute test webhook delivery due to: %s", e.getMessage()), e);
|
||||||
|
throw new CloudRuntimeException("Failed to execute test webhook delivery");
|
||||||
|
}
|
||||||
|
return webhookDeliveryVO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Class<?>> getCommands() {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
static public class ManualDeliveryContext<T> extends AsyncRpcContext<T> {
|
||||||
|
final Webhook webhook;
|
||||||
|
final AsyncCallFuture<WebhookDeliveryThread.WebhookDeliveryResult> future;
|
||||||
|
|
||||||
|
public ManualDeliveryContext(AsyncCompletionCallback<T> callback, Webhook webhook,
|
||||||
|
AsyncCallFuture<WebhookDeliveryThread.WebhookDeliveryResult> future) {
|
||||||
|
super(callback);
|
||||||
|
this.webhook = webhook;
|
||||||
|
this.future = future;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WebhookDeliveryCleanupWorker extends ManagedContextRunnable {
|
||||||
|
|
||||||
|
protected void runCleanupForLongestRunningManagementServer() {
|
||||||
|
try {
|
||||||
|
ManagementServerHostVO msHost = managementServerHostDao.findOneByLongestRuntime();
|
||||||
|
if (msHost == null || (msHost.getMsid() != ManagementServerNode.getManagementServerId())) {
|
||||||
|
logger.debug("Skipping the webhook delivery cleanup task on this management server");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
long deliveriesLimit = WebhookDeliveriesLimit.value();
|
||||||
|
logger.debug("Clearing old deliveries for webhooks with limit={} using management server {}",
|
||||||
|
deliveriesLimit, msHost.getMsid());
|
||||||
|
long processed = cleanupOldWebhookDeliveries(deliveriesLimit);
|
||||||
|
logger.debug("Cleared old deliveries with limit={} for {} webhooks", deliveriesLimit, processed);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.warn("Cleanup task failed to cleanup old webhook deliveries", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void runInContext() {
|
||||||
|
GlobalLock gcLock = GlobalLock.getInternLock("WebhookDeliveriesCleanup");
|
||||||
|
try {
|
||||||
|
if (gcLock.lock(3)) {
|
||||||
|
try {
|
||||||
|
runCleanupForLongestRunningManagementServer();
|
||||||
|
} finally {
|
||||||
|
gcLock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
gcLock.releaseRef();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,167 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook.api.command.user;
|
||||||
|
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
|
import org.apache.cloudstack.acl.SecurityChecker;
|
||||||
|
import org.apache.cloudstack.api.ACL;
|
||||||
|
import org.apache.cloudstack.api.APICommand;
|
||||||
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
|
import org.apache.cloudstack.api.ApiErrorCode;
|
||||||
|
import org.apache.cloudstack.api.BaseCmd;
|
||||||
|
import org.apache.cloudstack.api.Parameter;
|
||||||
|
import org.apache.cloudstack.api.ResponseObject;
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.api.response.DomainResponse;
|
||||||
|
import org.apache.cloudstack.api.response.ProjectResponse;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.mom.webhook.WebhookApiService;
|
||||||
|
import org.apache.cloudstack.mom.webhook.Webhook;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.response.WebhookResponse;
|
||||||
|
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
|
||||||
|
@APICommand(name = "createWebhook",
|
||||||
|
description = "Creates a Webhook",
|
||||||
|
responseObject = WebhookResponse.class,
|
||||||
|
responseView = ResponseObject.ResponseView.Restricted,
|
||||||
|
entityType = {Webhook.class},
|
||||||
|
requestHasSensitiveInfo = false,
|
||||||
|
responseHasSensitiveInfo = true,
|
||||||
|
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User},
|
||||||
|
since = "4.20.0")
|
||||||
|
public class CreateWebhookCmd extends BaseCmd {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
WebhookApiService webhookApiService;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
//////////////// API parameters /////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.NAME, type = BaseCmd.CommandType.STRING, required = true, description = "Name for the Webhook")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.DESCRIPTION, type = BaseCmd.CommandType.STRING, description = "Description for the Webhook")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.STATE, type = BaseCmd.CommandType.STRING, description = "State of the Webhook")
|
||||||
|
private String state;
|
||||||
|
|
||||||
|
@ACL(accessType = SecurityChecker.AccessType.UseEntry)
|
||||||
|
@Parameter(name = ApiConstants.ACCOUNT, type = BaseCmd.CommandType.STRING, description = "An optional account for the" +
|
||||||
|
" Webhook. Must be used with domainId.")
|
||||||
|
private String accountName;
|
||||||
|
|
||||||
|
@ACL(accessType = SecurityChecker.AccessType.UseEntry)
|
||||||
|
@Parameter(name = ApiConstants.DOMAIN_ID, type = BaseCmd.CommandType.UUID, entityType = DomainResponse.class,
|
||||||
|
description = "an optional domainId for the Webhook. If the account parameter is used, domainId must also be used.")
|
||||||
|
private Long domainId;
|
||||||
|
|
||||||
|
@ACL(accessType = SecurityChecker.AccessType.UseEntry)
|
||||||
|
@Parameter(name = ApiConstants.PROJECT_ID, type = BaseCmd.CommandType.UUID, entityType = ProjectResponse.class,
|
||||||
|
description = "Project for the Webhook")
|
||||||
|
private Long projectId;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.PAYLOAD_URL,
|
||||||
|
type = BaseCmd.CommandType.STRING,
|
||||||
|
required = true,
|
||||||
|
description = "Payload URL of the Webhook")
|
||||||
|
private String payloadUrl;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.SECRET_KEY, type = BaseCmd.CommandType.STRING, description = "Secret key of the Webhook")
|
||||||
|
private String secretKey;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.SSL_VERIFICATION, type = BaseCmd.CommandType.BOOLEAN, description = "If set to true then SSL verification will be done for the Webhook otherwise not")
|
||||||
|
private Boolean sslVerification;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.SCOPE, type = BaseCmd.CommandType.STRING, description = "Scope of the Webhook",
|
||||||
|
authorized = {RoleType.Admin, RoleType.DomainAdmin})
|
||||||
|
private String scope;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////////// Accessors ///////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAccountName() {
|
||||||
|
return accountName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getDomainId() {
|
||||||
|
return domainId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getProjectId() {
|
||||||
|
return projectId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPayloadUrl() {
|
||||||
|
return payloadUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSecretKey() {
|
||||||
|
return secretKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSslVerification() {
|
||||||
|
return Boolean.TRUE.equals(sslVerification);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getScope() {
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getEntityOwnerId() {
|
||||||
|
return CallContext.current().getCallingAccountId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////// API Implementation///////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws ServerApiException {
|
||||||
|
try {
|
||||||
|
WebhookResponse response = webhookApiService.createWebhook(this);
|
||||||
|
if (response == null) {
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create webhook");
|
||||||
|
}
|
||||||
|
response.setResponseName(getCommandName());
|
||||||
|
setResponseObject(response);
|
||||||
|
} catch (CloudRuntimeException ex) {
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,84 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook.api.command.user;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
|
import org.apache.cloudstack.api.APICommand;
|
||||||
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
|
import org.apache.cloudstack.api.ApiErrorCode;
|
||||||
|
import org.apache.cloudstack.api.BaseCmd;
|
||||||
|
import org.apache.cloudstack.api.Parameter;
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.api.response.SuccessResponse;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.mom.webhook.WebhookApiService;
|
||||||
|
import org.apache.cloudstack.mom.webhook.Webhook;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.response.WebhookResponse;
|
||||||
|
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
|
||||||
|
@APICommand(name = "deleteWebhook",
|
||||||
|
description = "Deletes a Webhook",
|
||||||
|
responseObject = SuccessResponse.class,
|
||||||
|
entityType = {Webhook.class},
|
||||||
|
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User},
|
||||||
|
since = "4.20.0")
|
||||||
|
public class DeleteWebhookCmd extends BaseCmd {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
WebhookApiService webhookApiService;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
//////////////// API parameters /////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
@Parameter(name = ApiConstants.ID, type = CommandType.UUID,
|
||||||
|
entityType = WebhookResponse.class,
|
||||||
|
required = true,
|
||||||
|
description = "The ID of the Webhook")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////////// Accessors ///////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getEntityOwnerId() {
|
||||||
|
return CallContext.current().getCallingAccountId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////// API Implementation///////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
@Override
|
||||||
|
public void execute() throws ServerApiException {
|
||||||
|
try {
|
||||||
|
if (!webhookApiService.deleteWebhook(this)) {
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Failed to delete webhook ID: %d", getId()));
|
||||||
|
}
|
||||||
|
SuccessResponse response = new SuccessResponse(getCommandName());
|
||||||
|
setResponseObject(response);
|
||||||
|
} catch (CloudRuntimeException ex) {
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,126 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook.api.command.user;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
|
import org.apache.cloudstack.api.APICommand;
|
||||||
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
|
import org.apache.cloudstack.api.ApiErrorCode;
|
||||||
|
import org.apache.cloudstack.api.BaseCmd;
|
||||||
|
import org.apache.cloudstack.api.Parameter;
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.api.response.ManagementServerResponse;
|
||||||
|
import org.apache.cloudstack.api.response.SuccessResponse;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.mom.webhook.WebhookApiService;
|
||||||
|
import org.apache.cloudstack.mom.webhook.WebhookDelivery;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.response.WebhookDeliveryResponse;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.response.WebhookResponse;
|
||||||
|
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
|
||||||
|
@APICommand(name = "deleteWebhookDelivery",
|
||||||
|
description = "Deletes Webhook delivery",
|
||||||
|
responseObject = SuccessResponse.class,
|
||||||
|
entityType = {WebhookDelivery.class},
|
||||||
|
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User},
|
||||||
|
since = "4.20.0")
|
||||||
|
public class DeleteWebhookDeliveryCmd extends BaseCmd {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
WebhookApiService webhookApiService;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
//////////////// API parameters /////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
@Parameter(name = ApiConstants.ID, type = BaseCmd.CommandType.UUID,
|
||||||
|
entityType = WebhookDeliveryResponse.class,
|
||||||
|
description = "The ID of the Webhook delivery")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.WEBHOOK_ID, type = BaseCmd.CommandType.UUID,
|
||||||
|
entityType = WebhookResponse.class,
|
||||||
|
description = "The ID of the Webhook")
|
||||||
|
private Long webhookId;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.MANAGEMENT_SERVER_ID, type = BaseCmd.CommandType.UUID,
|
||||||
|
entityType = ManagementServerResponse.class,
|
||||||
|
description = "The ID of the management server",
|
||||||
|
authorized = {RoleType.Admin})
|
||||||
|
private Long managementServerId;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.START_DATE,
|
||||||
|
type = CommandType.DATE,
|
||||||
|
description = "The start date range for the Webhook delivery " +
|
||||||
|
"(use format \"yyyy-MM-dd\" or \"yyyy-MM-dd HH:mm:ss\"). " +
|
||||||
|
"All deliveries having start date equal to or after the specified date will be considered.")
|
||||||
|
private Date startDate;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.END_DATE,
|
||||||
|
type = CommandType.DATE,
|
||||||
|
description = "The end date range for the Webhook delivery " +
|
||||||
|
"(use format \"yyyy-MM-dd\" or \"yyyy-MM-dd HH:mm:ss\"). " +
|
||||||
|
"All deliveries having end date equal to or before the specified date will be considered.")
|
||||||
|
private Date endDate;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////////// Accessors ///////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getWebhookId() {
|
||||||
|
return webhookId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getManagementServerId() {
|
||||||
|
return managementServerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getStartDate() {
|
||||||
|
return startDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getEndDate() {
|
||||||
|
return endDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getEntityOwnerId() {
|
||||||
|
return CallContext.current().getCallingAccountId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////// API Implementation///////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
@Override
|
||||||
|
public void execute() throws ServerApiException {
|
||||||
|
try {
|
||||||
|
webhookApiService.deleteWebhookDelivery(this);
|
||||||
|
SuccessResponse response = new SuccessResponse(getCommandName());
|
||||||
|
setResponseObject(response);
|
||||||
|
} catch (CloudRuntimeException ex) {
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,132 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook.api.command.user;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
|
import org.apache.cloudstack.api.APICommand;
|
||||||
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
|
import org.apache.cloudstack.api.ApiErrorCode;
|
||||||
|
import org.apache.cloudstack.api.BaseCmd;
|
||||||
|
import org.apache.cloudstack.api.Parameter;
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.mom.webhook.WebhookApiService;
|
||||||
|
import org.apache.cloudstack.mom.webhook.WebhookDelivery;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.response.WebhookDeliveryResponse;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.response.WebhookResponse;
|
||||||
|
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
|
||||||
|
|
||||||
|
@APICommand(name = "executeWebhookDelivery",
|
||||||
|
description = "Executes a Webhook delivery",
|
||||||
|
responseObject = WebhookDeliveryResponse.class,
|
||||||
|
entityType = {WebhookDelivery.class},
|
||||||
|
requestHasSensitiveInfo = false,
|
||||||
|
responseHasSensitiveInfo = false,
|
||||||
|
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User},
|
||||||
|
since = "4.20.0")
|
||||||
|
public class ExecuteWebhookDeliveryCmd extends BaseCmd {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
WebhookApiService webhookApiService;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
//////////////// API parameters /////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
@Parameter(name = ApiConstants.ID, type = CommandType.UUID,
|
||||||
|
entityType = WebhookDeliveryResponse.class,
|
||||||
|
description = "The ID of the Webhook delivery for redelivery")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.WEBHOOK_ID, type = CommandType.UUID,
|
||||||
|
entityType = WebhookResponse.class,
|
||||||
|
description = "The ID of the Webhook")
|
||||||
|
private Long webhookId;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.PAYLOAD_URL,
|
||||||
|
type = BaseCmd.CommandType.STRING,
|
||||||
|
description = "Payload URL of the Webhook delivery")
|
||||||
|
private String payloadUrl;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.SECRET_KEY, type = BaseCmd.CommandType.STRING, description = "Secret key of the Webhook delivery")
|
||||||
|
private String secretKey;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.SSL_VERIFICATION, type = BaseCmd.CommandType.BOOLEAN, description = "If set to true then SSL verification will be done for the Webhook delivery otherwise not")
|
||||||
|
private Boolean sslVerification;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.PAYLOAD,
|
||||||
|
type = BaseCmd.CommandType.STRING,
|
||||||
|
description = "Payload of the Webhook delivery")
|
||||||
|
private String payload;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////////// Accessors ///////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getWebhookId() {
|
||||||
|
return webhookId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPayloadUrl() {
|
||||||
|
return payloadUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSecretKey() {
|
||||||
|
return secretKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean isSslVerification() {
|
||||||
|
return sslVerification;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPayload() {
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getEntityOwnerId() {
|
||||||
|
return CallContext.current().getCallingAccountId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////// API Implementation///////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute() throws ServerApiException {
|
||||||
|
try {
|
||||||
|
WebhookDeliveryResponse response = webhookApiService.executeWebhookDelivery(this);
|
||||||
|
if (response == null) {
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to test Webhook delivery");
|
||||||
|
}
|
||||||
|
response.setResponseName(getCommandName());
|
||||||
|
setResponseObject(response);
|
||||||
|
} catch (CloudRuntimeException ex) {
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,125 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook.api.command.user;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
|
import org.apache.cloudstack.api.APICommand;
|
||||||
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
|
import org.apache.cloudstack.api.BaseCmd;
|
||||||
|
import org.apache.cloudstack.api.BaseListCmd;
|
||||||
|
import org.apache.cloudstack.api.Parameter;
|
||||||
|
import org.apache.cloudstack.api.ResponseObject;
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.api.response.ListResponse;
|
||||||
|
import org.apache.cloudstack.api.response.ManagementServerResponse;
|
||||||
|
import org.apache.cloudstack.mom.webhook.WebhookApiService;
|
||||||
|
import org.apache.cloudstack.mom.webhook.WebhookDelivery;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.response.WebhookDeliveryResponse;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.response.WebhookResponse;
|
||||||
|
|
||||||
|
@APICommand(name = "listWebhookDeliveries",
|
||||||
|
description = "Lists Webhook deliveries",
|
||||||
|
responseObject = WebhookResponse.class,
|
||||||
|
responseView = ResponseObject.ResponseView.Restricted,
|
||||||
|
entityType = {WebhookDelivery.class},
|
||||||
|
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User},
|
||||||
|
since = "4.20.0")
|
||||||
|
public class ListWebhookDeliveriesCmd extends BaseListCmd {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
WebhookApiService webhookApiService;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
//////////////// API parameters /////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
@Parameter(name = ApiConstants.ID, type = BaseCmd.CommandType.UUID,
|
||||||
|
entityType = WebhookDeliveryResponse.class,
|
||||||
|
description = "The ID of the Webhook delivery")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.WEBHOOK_ID, type = BaseCmd.CommandType.UUID,
|
||||||
|
entityType = WebhookResponse.class,
|
||||||
|
description = "The ID of the Webhook")
|
||||||
|
private Long webhookId;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.MANAGEMENT_SERVER_ID, type = BaseCmd.CommandType.UUID,
|
||||||
|
entityType = ManagementServerResponse.class,
|
||||||
|
description = "The ID of the management server",
|
||||||
|
authorized = {RoleType.Admin})
|
||||||
|
private Long managementServerId;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.START_DATE,
|
||||||
|
type = CommandType.DATE,
|
||||||
|
description = "The start date range for the Webhook delivery " +
|
||||||
|
"(use format \"yyyy-MM-dd\" or \"yyyy-MM-dd HH:mm:ss\"). " +
|
||||||
|
"All deliveries having start date equal to or after the specified date will be listed.")
|
||||||
|
private Date startDate;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.END_DATE,
|
||||||
|
type = CommandType.DATE,
|
||||||
|
description = "The end date range for the Webhook delivery " +
|
||||||
|
"(use format \"yyyy-MM-dd\" or \"yyyy-MM-dd HH:mm:ss\"). " +
|
||||||
|
"All deliveries having end date equal to or before the specified date will be listed.")
|
||||||
|
private Date endDate;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.EVENT_TYPE,
|
||||||
|
type = CommandType.STRING,
|
||||||
|
description = "The event type of the Webhook delivery")
|
||||||
|
private String eventType;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////////// Accessors ///////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getWebhookId() {
|
||||||
|
return webhookId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getManagementServerId() {
|
||||||
|
return managementServerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getStartDate() {
|
||||||
|
return startDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getEndDate() {
|
||||||
|
return endDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEventType() {
|
||||||
|
return eventType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////// API Implementation///////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
@Override
|
||||||
|
public void execute() throws ServerApiException {
|
||||||
|
ListResponse<WebhookDeliveryResponse> response = webhookApiService.listWebhookDeliveries(this);
|
||||||
|
response.setResponseName(getCommandName());
|
||||||
|
setResponseObject(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,95 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook.api.command.user;
|
||||||
|
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
|
import org.apache.cloudstack.api.APICommand;
|
||||||
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
|
import org.apache.cloudstack.api.BaseListProjectAndAccountResourcesCmd;
|
||||||
|
import org.apache.cloudstack.api.Parameter;
|
||||||
|
import org.apache.cloudstack.api.ResponseObject;
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.api.response.ListResponse;
|
||||||
|
import org.apache.cloudstack.mom.webhook.WebhookApiService;
|
||||||
|
import org.apache.cloudstack.mom.webhook.Webhook;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.response.WebhookResponse;
|
||||||
|
|
||||||
|
@APICommand(name = "listWebhooks",
|
||||||
|
description = "Lists Webhooks",
|
||||||
|
responseObject = WebhookResponse.class,
|
||||||
|
responseView = ResponseObject.ResponseView.Restricted,
|
||||||
|
entityType = {Webhook.class},
|
||||||
|
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User},
|
||||||
|
since = "4.20.0")
|
||||||
|
public class ListWebhooksCmd extends BaseListProjectAndAccountResourcesCmd {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
WebhookApiService webhookApiService;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
//////////////// API parameters /////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
@Parameter(name = ApiConstants.ID, type = CommandType.UUID,
|
||||||
|
entityType = WebhookResponse.class,
|
||||||
|
description = "The ID of the Webhook")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "The state of the Webhook")
|
||||||
|
private String state;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "The name of the Webhook")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.SCOPE,
|
||||||
|
type = CommandType.STRING,
|
||||||
|
description = "The scope of the Webhook",
|
||||||
|
authorized = {RoleType.Admin, RoleType.DomainAdmin})
|
||||||
|
private String scope;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////////// Accessors ///////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getScope() {
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////// API Implementation///////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
@Override
|
||||||
|
public void execute() throws ServerApiException {
|
||||||
|
ListResponse<WebhookResponse> response = webhookApiService.listWebhooks(this);
|
||||||
|
response.setResponseName(getCommandName());
|
||||||
|
setResponseObject(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,136 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook.api.command.user;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.acl.RoleType;
|
||||||
|
import org.apache.cloudstack.api.APICommand;
|
||||||
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
|
import org.apache.cloudstack.api.ApiErrorCode;
|
||||||
|
import org.apache.cloudstack.api.BaseCmd;
|
||||||
|
import org.apache.cloudstack.api.Parameter;
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.api.response.SuccessResponse;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.mom.webhook.WebhookApiService;
|
||||||
|
import org.apache.cloudstack.mom.webhook.Webhook;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.response.WebhookResponse;
|
||||||
|
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
|
||||||
|
@APICommand(name = "updateWebhook",
|
||||||
|
description = "Updates a Webhook",
|
||||||
|
responseObject = SuccessResponse.class,
|
||||||
|
entityType = {Webhook.class},
|
||||||
|
authorized = {RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User},
|
||||||
|
since = "4.20.0")
|
||||||
|
public class UpdateWebhookCmd extends BaseCmd {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
WebhookApiService webhookApiService;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
//////////////// API parameters /////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
@Parameter(name = ApiConstants.ID, type = CommandType.UUID,
|
||||||
|
entityType = WebhookResponse.class,
|
||||||
|
required = true,
|
||||||
|
description = "The ID of the Webhook")
|
||||||
|
private Long id;
|
||||||
|
@Parameter(name = ApiConstants.NAME, type = BaseCmd.CommandType.STRING, description = "Name for the Webhook")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.DESCRIPTION, type = BaseCmd.CommandType.STRING, description = "Description for the Webhook")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.STATE, type = BaseCmd.CommandType.STRING, description = "State of the Webhook")
|
||||||
|
private String state;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.PAYLOAD_URL,
|
||||||
|
type = BaseCmd.CommandType.STRING,
|
||||||
|
description = "Payload URL of the Webhook")
|
||||||
|
private String payloadUrl;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.SECRET_KEY, type = BaseCmd.CommandType.STRING, description = "Secret key of the Webhook")
|
||||||
|
private String secretKey;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.SSL_VERIFICATION, type = BaseCmd.CommandType.BOOLEAN, description = "If set to true then SSL verification will be done for the Webhook otherwise not")
|
||||||
|
private Boolean sslVerification;
|
||||||
|
|
||||||
|
@Parameter(name = ApiConstants.SCOPE, type = BaseCmd.CommandType.STRING, description = "Scope of the Webhook",
|
||||||
|
authorized = {RoleType.Admin, RoleType.DomainAdmin})
|
||||||
|
private String scope;
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////////// Accessors ///////////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPayloadUrl() {
|
||||||
|
return payloadUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSecretKey() {
|
||||||
|
return secretKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean isSslVerification() {
|
||||||
|
return sslVerification;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getScope() {
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getEntityOwnerId() {
|
||||||
|
return CallContext.current().getCallingAccountId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
/////////////// API Implementation///////////////////
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
@Override
|
||||||
|
public void execute() throws ServerApiException {
|
||||||
|
try {
|
||||||
|
WebhookResponse response = webhookApiService.updateWebhook(this);
|
||||||
|
if (response == null) {
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update Webhook");
|
||||||
|
}
|
||||||
|
response.setResponseName(getCommandName());
|
||||||
|
setResponseObject(response);
|
||||||
|
} catch (CloudRuntimeException ex) {
|
||||||
|
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,136 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook.api.response;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
|
import org.apache.cloudstack.api.BaseResponse;
|
||||||
|
import org.apache.cloudstack.api.EntityReference;
|
||||||
|
import org.apache.cloudstack.mom.webhook.WebhookDelivery;
|
||||||
|
|
||||||
|
import com.cloud.serializer.Param;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
@EntityReference(value = {WebhookDelivery.class})
|
||||||
|
public class WebhookDeliveryResponse extends BaseResponse {
|
||||||
|
@SerializedName(ApiConstants.ID)
|
||||||
|
@Param(description = "The ID of the Webhook delivery")
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.EVENT_ID)
|
||||||
|
@Param(description = "The ID of the event")
|
||||||
|
private String eventId;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.EVENT_TYPE)
|
||||||
|
@Param(description = "The type of the event")
|
||||||
|
private String eventType;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.WEBHOOK_ID)
|
||||||
|
@Param(description = "The ID of the Webhook")
|
||||||
|
private String webhookId;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.WEBHOOK_NAME)
|
||||||
|
@Param(description = "The name of the Webhook")
|
||||||
|
private String webhookName;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.MANAGEMENT_SERVER_ID)
|
||||||
|
@Param(description = "The ID of the management server which executed delivery")
|
||||||
|
private String managementServerId;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.MANAGEMENT_SERVER_NAME)
|
||||||
|
@Param(description = "The name of the management server which executed delivery")
|
||||||
|
private String managementServerName;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.HEADERS)
|
||||||
|
@Param(description = "The headers of the webhook delivery")
|
||||||
|
private String headers;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.PAYLOAD)
|
||||||
|
@Param(description = "The payload of the webhook delivery")
|
||||||
|
private String payload;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.SUCCESS)
|
||||||
|
@Param(description = "Whether Webhook delivery succeeded or not")
|
||||||
|
private boolean success;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.RESPONSE)
|
||||||
|
@Param(description = "The response of the webhook delivery")
|
||||||
|
private String response;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.START_DATE)
|
||||||
|
@Param(description = "The start time of the Webhook delivery")
|
||||||
|
private Date startTime;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.END_DATE)
|
||||||
|
@Param(description = "The end time of the Webhook delivery")
|
||||||
|
private Date endTime;
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEventId(String eventId) {
|
||||||
|
this.eventId = eventId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEventType(String eventType) {
|
||||||
|
this.eventType = eventType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWebhookId(String webhookId) {
|
||||||
|
this.webhookId = webhookId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWebhookName(String webhookName) {
|
||||||
|
this.webhookName = webhookName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setManagementServerId(String managementServerId) {
|
||||||
|
this.managementServerId = managementServerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setManagementServerName(String managementServerName) {
|
||||||
|
this.managementServerName = managementServerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeaders(String headers) {
|
||||||
|
this.headers = headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPayload(String payload) {
|
||||||
|
this.payload = payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSuccess(boolean success) {
|
||||||
|
this.success = success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setResponse(String response) {
|
||||||
|
this.response = response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStartTime(Date startTime) {
|
||||||
|
this.startTime = startTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEndTime(Date endTime) {
|
||||||
|
this.endTime = endTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,149 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook.api.response;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.ApiConstants;
|
||||||
|
import org.apache.cloudstack.api.BaseResponse;
|
||||||
|
import org.apache.cloudstack.api.EntityReference;
|
||||||
|
import org.apache.cloudstack.api.response.ControlledViewEntityResponse;
|
||||||
|
import org.apache.cloudstack.mom.webhook.Webhook;
|
||||||
|
|
||||||
|
import com.cloud.serializer.Param;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
@EntityReference(value = {Webhook.class})
|
||||||
|
public class WebhookResponse extends BaseResponse implements ControlledViewEntityResponse {
|
||||||
|
@SerializedName(ApiConstants.ID)
|
||||||
|
@Param(description = "The ID of the Webhook")
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.NAME)
|
||||||
|
@Param(description = "The name of the Webhook")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.DESCRIPTION)
|
||||||
|
@Param(description = "The description of the Webhook")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.STATE)
|
||||||
|
@Param(description = "The state of the Webhook")
|
||||||
|
private String state;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.DOMAIN_ID)
|
||||||
|
@Param(description = "The ID of the domain in which the Webhook exists")
|
||||||
|
private String domainId;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.DOMAIN)
|
||||||
|
@Param(description = "The name of the domain in which the Webhook exists")
|
||||||
|
private String domainName;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.ACCOUNT)
|
||||||
|
@Param(description = "The account associated with the Webhook")
|
||||||
|
private String accountName;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.PROJECT_ID)
|
||||||
|
@Param(description = "the project id of the Kubernetes cluster")
|
||||||
|
private String projectId;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.PROJECT)
|
||||||
|
@Param(description = "the project name of the Kubernetes cluster")
|
||||||
|
private String projectName;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.PAYLOAD_URL)
|
||||||
|
@Param(description = "The payload URL end point for the Webhook")
|
||||||
|
private String payloadUrl;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.SECRET_KEY)
|
||||||
|
@Param(description = "The secret key for the Webhook")
|
||||||
|
private String secretKey;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.SSL_VERIFICATION)
|
||||||
|
@Param(description = "Whether SSL verification is enabled for the Webhook")
|
||||||
|
private boolean sslVerification;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.SCOPE)
|
||||||
|
@Param(description = "The scope of the Webhook")
|
||||||
|
private String scope;
|
||||||
|
|
||||||
|
@SerializedName(ApiConstants.CREATED)
|
||||||
|
@Param(description = "The date when this Webhook was created")
|
||||||
|
private Date created;
|
||||||
|
|
||||||
|
public void setId(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setState(String state) {
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDomainId(String domainId) {
|
||||||
|
this.domainId = domainId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setDomainName(String domainName) {
|
||||||
|
this.domainName = domainName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAccountName(String accountName) {
|
||||||
|
this.accountName = accountName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setProjectId(String projectId) {
|
||||||
|
this.projectId = projectId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setProjectName(String projectName) {
|
||||||
|
this.projectName = projectName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPayloadUrl(String payloadUrl) {
|
||||||
|
this.payloadUrl = payloadUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSecretKey(String secretKey) {
|
||||||
|
this.secretKey = secretKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSslVerification(boolean sslVerification) {
|
||||||
|
this.sslVerification = sslVerification;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setScope(String scope) {
|
||||||
|
this.scope = scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreated(Date created) {
|
||||||
|
this.created = created;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook.dao;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.mom.webhook.vo.WebhookVO;
|
||||||
|
|
||||||
|
import com.cloud.utils.db.GenericDao;
|
||||||
|
|
||||||
|
public interface WebhookDao extends GenericDao<WebhookVO, Long> {
|
||||||
|
List<WebhookVO> listByEnabledForDelivery(Long accountId, List<Long> domainIds);
|
||||||
|
void deleteByAccount(long accountId);
|
||||||
|
List<WebhookVO> listByAccount(long accountId);
|
||||||
|
WebhookVO findByAccountAndPayloadUrl(long accountId, String payloadUrl);
|
||||||
|
}
|
||||||
@ -0,0 +1,99 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook.dao;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.naming.ConfigurationException;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.mom.webhook.Webhook;
|
||||||
|
import org.apache.cloudstack.mom.webhook.vo.WebhookVO;
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
|
||||||
|
import com.cloud.utils.db.GenericDaoBase;
|
||||||
|
import com.cloud.utils.db.SearchBuilder;
|
||||||
|
import com.cloud.utils.db.SearchCriteria;
|
||||||
|
|
||||||
|
public class WebhookDaoImpl extends GenericDaoBase<WebhookVO, Long> implements WebhookDao {
|
||||||
|
SearchBuilder<WebhookVO> accountIdSearch;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
|
||||||
|
super.configure(name, params);
|
||||||
|
|
||||||
|
accountIdSearch = createSearchBuilder();
|
||||||
|
accountIdSearch.and("accountId", accountIdSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public List<WebhookVO> listByEnabledForDelivery(Long accountId, List<Long> domainIds) {
|
||||||
|
SearchBuilder<WebhookVO> sb = createSearchBuilder();
|
||||||
|
sb.and("state", sb.entity().getState(), SearchCriteria.Op.EQ);
|
||||||
|
sb.and().op("scopeGlobal", sb.entity().getScope(), SearchCriteria.Op.EQ);
|
||||||
|
if (accountId != null) {
|
||||||
|
sb.or().op("scopeLocal", sb.entity().getScope(), SearchCriteria.Op.EQ);
|
||||||
|
sb.and("accountId", sb.entity().getAccountId(), SearchCriteria.Op.EQ);
|
||||||
|
sb.cp();
|
||||||
|
}
|
||||||
|
if (CollectionUtils.isNotEmpty(domainIds)) {
|
||||||
|
sb.or().op("scopeDomain", sb.entity().getScope(), SearchCriteria.Op.EQ);
|
||||||
|
sb.and("domainId", sb.entity().getDomainId(), SearchCriteria.Op.IN);
|
||||||
|
sb.cp();
|
||||||
|
}
|
||||||
|
sb.cp();
|
||||||
|
SearchCriteria<WebhookVO> sc = sb.create();
|
||||||
|
sc.setParameters("state", Webhook.State.Enabled.name());
|
||||||
|
sc.setParameters("scopeGlobal", Webhook.Scope.Global.name());
|
||||||
|
if (accountId != null) {
|
||||||
|
sc.setParameters("scopeLocal", Webhook.Scope.Local.name());
|
||||||
|
sc.setParameters("accountId", accountId);
|
||||||
|
}
|
||||||
|
if (CollectionUtils.isNotEmpty(domainIds)) {
|
||||||
|
sc.setParameters("scopeDomain", Webhook.Scope.Domain.name());
|
||||||
|
sc.setParameters("domainId", domainIds.toArray());
|
||||||
|
}
|
||||||
|
return listBy(sc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteByAccount(long accountId) {
|
||||||
|
SearchCriteria<WebhookVO> sc = accountIdSearch.create();
|
||||||
|
sc.setParameters("accountId", accountId);
|
||||||
|
remove(sc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<WebhookVO> listByAccount(long accountId) {
|
||||||
|
SearchCriteria<WebhookVO> sc = accountIdSearch.create();
|
||||||
|
sc.setParameters("accountId", accountId);
|
||||||
|
return listBy(sc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WebhookVO findByAccountAndPayloadUrl(long accountId, String payloadUrl) {
|
||||||
|
SearchBuilder<WebhookVO> sb = createSearchBuilder();
|
||||||
|
sb.and("accountId", sb.entity().getAccountId(), SearchCriteria.Op.EQ);
|
||||||
|
sb.and("payloadUrl", sb.entity().getPayloadUrl(), SearchCriteria.Op.EQ);
|
||||||
|
SearchCriteria<WebhookVO> sc = sb.create();
|
||||||
|
sc.setParameters("accountId", accountId);
|
||||||
|
sc.setParameters("payloadUrl", payloadUrl);
|
||||||
|
return findOneBy(sc);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook.dao;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.mom.webhook.vo.WebhookDeliveryVO;
|
||||||
|
|
||||||
|
import com.cloud.utils.db.GenericDao;
|
||||||
|
|
||||||
|
public interface WebhookDeliveryDao extends GenericDao<WebhookDeliveryVO, Long> {
|
||||||
|
int deleteByDeleteApiParams(Long id, Long webhookId, Long managementServerId, Date startDate, Date endDate);
|
||||||
|
void removeOlderDeliveries(long webhookId, long limit);
|
||||||
|
}
|
||||||
@ -0,0 +1,73 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook.dao;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.mom.webhook.vo.WebhookDeliveryVO;
|
||||||
|
|
||||||
|
import com.cloud.utils.db.Filter;
|
||||||
|
import com.cloud.utils.db.GenericDaoBase;
|
||||||
|
import com.cloud.utils.db.SearchBuilder;
|
||||||
|
import com.cloud.utils.db.SearchCriteria;
|
||||||
|
|
||||||
|
public class WebhookDeliveryDaoImpl extends GenericDaoBase<WebhookDeliveryVO, Long> implements WebhookDeliveryDao {
|
||||||
|
@Override
|
||||||
|
public int deleteByDeleteApiParams(Long id, Long webhookId, Long managementServerId, Date startDate,
|
||||||
|
Date endDate) {
|
||||||
|
SearchBuilder<WebhookDeliveryVO> sb = createSearchBuilder();
|
||||||
|
sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ);
|
||||||
|
sb.and("webhookId", sb.entity().getWebhookId(), SearchCriteria.Op.EQ);
|
||||||
|
sb.and("managementServerId", sb.entity().getManagementServerId(), SearchCriteria.Op.EQ);
|
||||||
|
sb.and("startDate", sb.entity().getStartTime(), SearchCriteria.Op.GTEQ);
|
||||||
|
sb.and("endDate", sb.entity().getEndTime(), SearchCriteria.Op.LTEQ);
|
||||||
|
SearchCriteria<WebhookDeliveryVO> sc = sb.create();
|
||||||
|
if (id != null) {
|
||||||
|
sc.setParameters("id", id);
|
||||||
|
}
|
||||||
|
if (webhookId != null) {
|
||||||
|
sc.setParameters("webhookId", webhookId);
|
||||||
|
}
|
||||||
|
if (managementServerId != null) {
|
||||||
|
sc.setParameters("managementServerId", managementServerId);
|
||||||
|
}
|
||||||
|
if (startDate != null) {
|
||||||
|
sc.setParameters("startDate", startDate);
|
||||||
|
}
|
||||||
|
if (endDate != null) {
|
||||||
|
sc.setParameters("endDate", endDate);
|
||||||
|
}
|
||||||
|
return remove(sc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeOlderDeliveries(long webhookId, long limit) {
|
||||||
|
Filter searchFilter = new Filter(WebhookDeliveryVO.class, "id", false, 0L, limit);
|
||||||
|
SearchBuilder<WebhookDeliveryVO> sb = createSearchBuilder();
|
||||||
|
sb.and("webhookId", sb.entity().getWebhookId(), SearchCriteria.Op.EQ);
|
||||||
|
SearchCriteria<WebhookDeliveryVO> sc = sb.create();
|
||||||
|
sc.setParameters("webhookId", webhookId);
|
||||||
|
List<WebhookDeliveryVO> keep = listBy(sc, searchFilter);
|
||||||
|
SearchBuilder<WebhookDeliveryVO> sbDelete = createSearchBuilder();
|
||||||
|
sbDelete.and("id", sbDelete.entity().getId(), SearchCriteria.Op.NOTIN);
|
||||||
|
SearchCriteria<WebhookDeliveryVO> scDelete = sbDelete.create();
|
||||||
|
scDelete.setParameters("id", keep.stream().map(WebhookDeliveryVO::getId).toArray());
|
||||||
|
remove(scDelete);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook.dao;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.mom.webhook.vo.WebhookDeliveryJoinVO;
|
||||||
|
|
||||||
|
import com.cloud.utils.Pair;
|
||||||
|
import com.cloud.utils.db.Filter;
|
||||||
|
import com.cloud.utils.db.GenericDao;
|
||||||
|
|
||||||
|
public interface WebhookDeliveryJoinDao extends GenericDao<WebhookDeliveryJoinVO, Long> {
|
||||||
|
Pair<List<WebhookDeliveryJoinVO>, Integer> searchAndCountByListApiParameters(Long id,
|
||||||
|
List<Long> webhookIds, Long managementServerId, final String keyword, final Date startDate,
|
||||||
|
final Date endDate, final String eventType, Filter searchFilter);
|
||||||
|
}
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook.dao;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.mom.webhook.vo.WebhookDeliveryJoinVO;
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
|
||||||
|
import com.cloud.utils.Pair;
|
||||||
|
import com.cloud.utils.StringUtils;
|
||||||
|
import com.cloud.utils.db.Filter;
|
||||||
|
import com.cloud.utils.db.GenericDaoBase;
|
||||||
|
import com.cloud.utils.db.SearchBuilder;
|
||||||
|
import com.cloud.utils.db.SearchCriteria;
|
||||||
|
|
||||||
|
public class WebhookDeliveryJoinDaoImpl extends GenericDaoBase<WebhookDeliveryJoinVO, Long>
|
||||||
|
implements WebhookDeliveryJoinDao {
|
||||||
|
@Override
|
||||||
|
public Pair<List<WebhookDeliveryJoinVO>, Integer> searchAndCountByListApiParameters(Long id,
|
||||||
|
List<Long> webhookIds, Long managementServerId, String keyword, final Date startDate,
|
||||||
|
final Date endDate, final String eventType, Filter searchFilter) {
|
||||||
|
SearchBuilder<WebhookDeliveryJoinVO> sb = createSearchBuilder();
|
||||||
|
sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ);
|
||||||
|
sb.and("webhookId", sb.entity().getWebhookId(), SearchCriteria.Op.IN);
|
||||||
|
sb.and("managementServerId", sb.entity().getManagementServerMsId(), SearchCriteria.Op.EQ);
|
||||||
|
sb.and("keyword", sb.entity().getPayload(), SearchCriteria.Op.LIKE);
|
||||||
|
sb.and("startDate", sb.entity().getStartTime(), SearchCriteria.Op.GTEQ);
|
||||||
|
sb.and("endDate", sb.entity().getEndTime(), SearchCriteria.Op.LTEQ);
|
||||||
|
sb.and("eventType", sb.entity().getEventType(), SearchCriteria.Op.EQ);
|
||||||
|
SearchCriteria<WebhookDeliveryJoinVO> sc = sb.create();
|
||||||
|
if (id != null) {
|
||||||
|
sc.setParameters("id", id);
|
||||||
|
}
|
||||||
|
if (CollectionUtils.isNotEmpty(webhookIds)) {
|
||||||
|
sc.setParameters("webhookId", webhookIds.toArray());
|
||||||
|
}
|
||||||
|
if (managementServerId != null) {
|
||||||
|
sc.setParameters("managementServerId", managementServerId);
|
||||||
|
}
|
||||||
|
if (keyword != null) {
|
||||||
|
sc.setParameters("keyword", "%" + keyword + "%");
|
||||||
|
}
|
||||||
|
if (startDate != null) {
|
||||||
|
sc.setParameters("startDate", startDate);
|
||||||
|
}
|
||||||
|
if (endDate != null) {
|
||||||
|
sc.setParameters("endDate", endDate);
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotBlank(eventType)) {
|
||||||
|
sc.setParameters("eventType", eventType);
|
||||||
|
}
|
||||||
|
return searchAndCount(sc, searchFilter);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook.dao;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.mom.webhook.vo.WebhookJoinVO;
|
||||||
|
|
||||||
|
import com.cloud.utils.db.GenericDao;
|
||||||
|
|
||||||
|
public interface WebhookJoinDao extends GenericDao<WebhookJoinVO, Long> {
|
||||||
|
List<WebhookJoinVO> listByAccountOrDomain(long accountId, String domainPath);
|
||||||
|
}
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook.dao;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.mom.webhook.vo.WebhookJoinVO;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import com.cloud.utils.db.GenericDaoBase;
|
||||||
|
import com.cloud.utils.db.SearchBuilder;
|
||||||
|
import com.cloud.utils.db.SearchCriteria;
|
||||||
|
|
||||||
|
public class WebhookJoinDaoImpl extends GenericDaoBase<WebhookJoinVO, Long> implements WebhookJoinDao {
|
||||||
|
@Override
|
||||||
|
public List<WebhookJoinVO> listByAccountOrDomain(long accountId, String domainPath) {
|
||||||
|
SearchBuilder<WebhookJoinVO> sb = createSearchBuilder();
|
||||||
|
sb.and().op("accountId", sb.entity().getAccountId(), SearchCriteria.Op.EQ);
|
||||||
|
if (StringUtils.isNotBlank(domainPath)) {
|
||||||
|
sb.or("domainPath", sb.entity().getDomainPath(), SearchCriteria.Op.LIKE);
|
||||||
|
}
|
||||||
|
sb.cp();
|
||||||
|
SearchCriteria<WebhookJoinVO> sc = sb.create();
|
||||||
|
sc.setParameters("accountId", accountId);
|
||||||
|
if (StringUtils.isNotBlank(domainPath)) {
|
||||||
|
sc.setParameters("domainPath", domainPath);
|
||||||
|
}
|
||||||
|
return listBy(sc);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,182 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook.vo;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import javax.persistence.Temporal;
|
||||||
|
import javax.persistence.TemporalType;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.Identity;
|
||||||
|
import org.apache.cloudstack.api.InternalIdentity;
|
||||||
|
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||||
|
|
||||||
|
import com.cloud.api.query.vo.BaseViewVO;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "webhook_delivery_view")
|
||||||
|
public class WebhookDeliveryJoinVO extends BaseViewVO implements InternalIdentity, Identity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Column(name = "id")
|
||||||
|
private long id;
|
||||||
|
|
||||||
|
@Column(name = "uuid")
|
||||||
|
private String uuid;
|
||||||
|
|
||||||
|
@Column(name = "event_id")
|
||||||
|
private long eventId;
|
||||||
|
|
||||||
|
@Column(name = "event_uuid")
|
||||||
|
private String eventUuid;
|
||||||
|
|
||||||
|
@Column(name = "event_type")
|
||||||
|
private String eventType;
|
||||||
|
|
||||||
|
@Column(name = "webhook_id")
|
||||||
|
private long webhookId;
|
||||||
|
|
||||||
|
@Column(name = "webhook_uuid")
|
||||||
|
private String webhookUuId;
|
||||||
|
|
||||||
|
@Column(name = "webhook_name")
|
||||||
|
private String webhookName;
|
||||||
|
|
||||||
|
@Column(name = "mshost_id")
|
||||||
|
private long managementServerId;
|
||||||
|
|
||||||
|
@Column(name = "mshost_uuid")
|
||||||
|
private String managementServerUuId;
|
||||||
|
|
||||||
|
@Column(name = "mshost_msid")
|
||||||
|
private long managementServerMsId;
|
||||||
|
|
||||||
|
@Column(name = "mshost_name")
|
||||||
|
private String managementServerName;
|
||||||
|
|
||||||
|
@Column(name = "headers", length = 65535)
|
||||||
|
private String headers;
|
||||||
|
|
||||||
|
@Column(name = "payload", length = 65535)
|
||||||
|
private String payload;
|
||||||
|
|
||||||
|
@Column(name = "success")
|
||||||
|
private boolean success;
|
||||||
|
|
||||||
|
@Column(name = "response", length = 65535)
|
||||||
|
private String response;
|
||||||
|
|
||||||
|
@Column(name = "start_time")
|
||||||
|
@Temporal(value = TemporalType.TIMESTAMP)
|
||||||
|
private Date startTime;
|
||||||
|
|
||||||
|
@Column(name = "end_time")
|
||||||
|
@Temporal(value = TemporalType.TIMESTAMP)
|
||||||
|
private Date endTime;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getId() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getEventId() {
|
||||||
|
return eventId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEventUuid() {
|
||||||
|
return eventUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEventType() {
|
||||||
|
return eventType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getWebhookId() {
|
||||||
|
return webhookId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWebhookUuId() {
|
||||||
|
return webhookUuId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWebhookName() {
|
||||||
|
return webhookName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getManagementServerId() {
|
||||||
|
return managementServerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getManagementServerUuId() {
|
||||||
|
return managementServerUuId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getManagementServerMsId() {
|
||||||
|
return managementServerMsId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getManagementServerName() {
|
||||||
|
return managementServerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHeaders() {
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPayload() {
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSuccess() {
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getResponse() {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getStartTime() {
|
||||||
|
return startTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getEndTime() {
|
||||||
|
return endTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("WebhookDelivery [%s]", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(
|
||||||
|
this, "id", "uuid", "webhookId", "startTime", "success"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebhookDeliveryJoinVO() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,174 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook.vo;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import javax.persistence.Temporal;
|
||||||
|
import javax.persistence.TemporalType;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.mom.webhook.WebhookDelivery;
|
||||||
|
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "webhook_delivery")
|
||||||
|
public class WebhookDeliveryVO implements WebhookDelivery {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Column(name = "id")
|
||||||
|
private long id;
|
||||||
|
|
||||||
|
@Column(name = "uuid")
|
||||||
|
private String uuid;
|
||||||
|
|
||||||
|
@Column(name = "event_id")
|
||||||
|
private long eventId;
|
||||||
|
|
||||||
|
@Column(name = "webhook_id")
|
||||||
|
private long webhookId;
|
||||||
|
|
||||||
|
@Column(name = "mshost_msid")
|
||||||
|
private long mangementServerId;
|
||||||
|
|
||||||
|
@Column(name = "headers", length = 65535)
|
||||||
|
private String headers;
|
||||||
|
|
||||||
|
@Column(name = "payload", length = 65535)
|
||||||
|
private String payload;
|
||||||
|
|
||||||
|
@Column(name = "success")
|
||||||
|
private boolean success;
|
||||||
|
|
||||||
|
@Column(name = "response", length = 65535)
|
||||||
|
private String response;
|
||||||
|
|
||||||
|
@Column(name = "start_time")
|
||||||
|
@Temporal(value = TemporalType.TIMESTAMP)
|
||||||
|
private Date startTime;
|
||||||
|
|
||||||
|
@Column(name = "end_time")
|
||||||
|
@Temporal(value = TemporalType.TIMESTAMP)
|
||||||
|
private Date endTime;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getEventId() {
|
||||||
|
return eventId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getWebhookId() {
|
||||||
|
return webhookId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getManagementServerId() {
|
||||||
|
return mangementServerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHeaders() {
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPayload() {
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSuccess() {
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getResponse() {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date getStartTime() {
|
||||||
|
return startTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date getEndTime() {
|
||||||
|
return endTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("WebhookDelivery [%s]", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(
|
||||||
|
this, "id", "uuid", "webhookId", "startTime", "success"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebhookDeliveryVO() {
|
||||||
|
this.uuid = UUID.randomUUID().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebhookDeliveryVO(long eventId, long webhookId, long managementServerId, String headers, String payload,
|
||||||
|
boolean success, String response, Date startTime, Date endTime) {
|
||||||
|
this.uuid = UUID.randomUUID().toString();
|
||||||
|
this.eventId = eventId;
|
||||||
|
this.webhookId = webhookId;
|
||||||
|
this.mangementServerId = managementServerId;
|
||||||
|
this.headers = headers;
|
||||||
|
this.payload = payload;
|
||||||
|
this.success = success;
|
||||||
|
this.response = response;
|
||||||
|
this.startTime = startTime;
|
||||||
|
this.endTime = endTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For creating a dummy object for testing delivery
|
||||||
|
*/
|
||||||
|
public WebhookDeliveryVO(long managementServerId, String headers, String payload, boolean success,
|
||||||
|
String response, Date startTime, Date endTime) {
|
||||||
|
this.id = WebhookDelivery.ID_DUMMY;
|
||||||
|
this.uuid = UUID.randomUUID().toString();
|
||||||
|
this.eventId = WebhookDelivery.ID_DUMMY;
|
||||||
|
this.webhookId = WebhookDelivery.ID_DUMMY;
|
||||||
|
this.mangementServerId = managementServerId;
|
||||||
|
this.headers = headers;
|
||||||
|
this.payload = payload;
|
||||||
|
this.success = success;
|
||||||
|
this.response = response;
|
||||||
|
this.startTime = startTime;
|
||||||
|
this.endTime = endTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,234 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook.vo;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.EnumType;
|
||||||
|
import javax.persistence.Enumerated;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.mom.webhook.Webhook;
|
||||||
|
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||||
|
|
||||||
|
import com.cloud.api.query.vo.ControlledViewEntity;
|
||||||
|
import com.cloud.user.Account;
|
||||||
|
import com.cloud.utils.db.Encrypt;
|
||||||
|
import com.cloud.utils.db.GenericDao;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "webhook_view")
|
||||||
|
public class WebhookJoinVO implements ControlledViewEntity {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name = "id", updatable = false, nullable = false)
|
||||||
|
private long id;
|
||||||
|
|
||||||
|
@Column(name = "uuid")
|
||||||
|
private String uuid;
|
||||||
|
|
||||||
|
@Column(name = "name")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Column(name = "description", length = 4096)
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Column(name = "state")
|
||||||
|
@Enumerated(value = EnumType.STRING)
|
||||||
|
private Webhook.State state;
|
||||||
|
|
||||||
|
@Column(name = "payload_url")
|
||||||
|
private String payloadUrl;
|
||||||
|
|
||||||
|
@Column(name = "secret_key")
|
||||||
|
@Encrypt
|
||||||
|
private String secretKey;
|
||||||
|
|
||||||
|
@Column(name = "ssl_verification")
|
||||||
|
private boolean sslVerification;
|
||||||
|
|
||||||
|
@Column(name = "scope")
|
||||||
|
@Enumerated(value = EnumType.STRING)
|
||||||
|
private Webhook.Scope scope;
|
||||||
|
|
||||||
|
@Column(name = GenericDao.CREATED_COLUMN)
|
||||||
|
private Date created;
|
||||||
|
|
||||||
|
@Column(name = GenericDao.REMOVED_COLUMN)
|
||||||
|
private Date removed;
|
||||||
|
|
||||||
|
@Column(name = "account_id")
|
||||||
|
private long accountId;
|
||||||
|
|
||||||
|
@Column(name = "account_uuid")
|
||||||
|
private String accountUuid;
|
||||||
|
|
||||||
|
@Column(name = "account_name")
|
||||||
|
private String accountName;
|
||||||
|
|
||||||
|
@Column(name = "account_type")
|
||||||
|
@Enumerated(value = EnumType.STRING)
|
||||||
|
private Account.Type accountType;
|
||||||
|
|
||||||
|
@Column(name = "domain_id")
|
||||||
|
private long domainId;
|
||||||
|
|
||||||
|
@Column(name = "domain_uuid")
|
||||||
|
private String domainUuid;
|
||||||
|
|
||||||
|
@Column(name = "domain_name")
|
||||||
|
private String domainName;
|
||||||
|
|
||||||
|
@Column(name = "domain_path")
|
||||||
|
private String domainPath;
|
||||||
|
|
||||||
|
@Column(name = "project_id")
|
||||||
|
private long projectId;
|
||||||
|
|
||||||
|
@Column(name = "project_uuid")
|
||||||
|
private String projectUuid;
|
||||||
|
|
||||||
|
@Column(name = "project_name")
|
||||||
|
private String projectName;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Webhook.State getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPayloadUrl() {
|
||||||
|
return payloadUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPayloadUrl(String payloadUrl) {
|
||||||
|
this.payloadUrl = payloadUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSecretKey() {
|
||||||
|
return secretKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Webhook.Scope getScope() {
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSslVerification() {
|
||||||
|
return sslVerification;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getCreated() {
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getRemoved() {
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getDomainId() {
|
||||||
|
return domainId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDomainPath() {
|
||||||
|
return domainPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDomainUuid() {
|
||||||
|
return domainUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDomainName() {
|
||||||
|
return domainName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Account.Type getAccountType() {
|
||||||
|
return accountType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getAccountId() {
|
||||||
|
return accountId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAccountUuid() {
|
||||||
|
return accountUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAccountName() {
|
||||||
|
return accountName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getProjectUuid() {
|
||||||
|
return projectUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getProjectName() {
|
||||||
|
return projectName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getEntityType() {
|
||||||
|
return Webhook.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("Webhook [%s]", ReflectionToStringBuilderUtils.reflectOnlySelectedFields(
|
||||||
|
this, "id", "uuid", "name"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebhookJoinVO() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,232 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook.vo;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.EnumType;
|
||||||
|
import javax.persistence.Enumerated;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.mom.webhook.Webhook;
|
||||||
|
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||||
|
|
||||||
|
import com.cloud.utils.db.Encrypt;
|
||||||
|
import com.cloud.utils.db.GenericDao;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "webhook")
|
||||||
|
public class WebhookVO implements Webhook {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Column(name = "id")
|
||||||
|
private long id;
|
||||||
|
|
||||||
|
@Column(name = "uuid")
|
||||||
|
private String uuid;
|
||||||
|
|
||||||
|
@Column(name = "name")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Column(name = "description", length = 4096)
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Column(name = "state")
|
||||||
|
@Enumerated(value = EnumType.STRING)
|
||||||
|
private State state;
|
||||||
|
|
||||||
|
@Column(name = "domain_id")
|
||||||
|
private long domainId;
|
||||||
|
|
||||||
|
@Column(name = "account_id")
|
||||||
|
private long accountId;
|
||||||
|
|
||||||
|
@Column(name = "payload_url")
|
||||||
|
private String payloadUrl;
|
||||||
|
|
||||||
|
@Column(name = "secret_key")
|
||||||
|
@Encrypt
|
||||||
|
private String secretKey;
|
||||||
|
|
||||||
|
@Column(name = "ssl_verification")
|
||||||
|
private boolean sslVerification;
|
||||||
|
|
||||||
|
@Column(name = "scope")
|
||||||
|
@Enumerated(value = EnumType.STRING)
|
||||||
|
private Scope scope;
|
||||||
|
|
||||||
|
@Column(name = GenericDao.CREATED_COLUMN)
|
||||||
|
private Date created;
|
||||||
|
|
||||||
|
@Column(name = GenericDao.REMOVED_COLUMN)
|
||||||
|
private Date removed;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public State getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setState(State state) {
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getDomainId() {
|
||||||
|
return domainId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDomainId(long domainId) {
|
||||||
|
this.domainId = domainId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getAccountId() {
|
||||||
|
return accountId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAccountId(long accountId) {
|
||||||
|
this.accountId = accountId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPayloadUrl() {
|
||||||
|
return payloadUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPayloadUrl(String payloadUrl) {
|
||||||
|
this.payloadUrl = payloadUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSecretKey() {
|
||||||
|
return secretKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSecretKey(String secretKey) {
|
||||||
|
this.secretKey = secretKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Scope getScope() {
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setScope(Scope scope) {
|
||||||
|
this.scope = scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSslVerification() {
|
||||||
|
return sslVerification;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSslVerification(boolean sslVerification) {
|
||||||
|
this.sslVerification = sslVerification;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date getCreated() {
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getRemoved() {
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getEntityType() {
|
||||||
|
return Webhook.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("Webhook [%s]",ReflectionToStringBuilderUtils.reflectOnlySelectedFields(
|
||||||
|
this, "id", "uuid", "name", "payloadUrl"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebhookVO() {
|
||||||
|
this.uuid = UUID.randomUUID().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public WebhookVO(String name, String description, State state, long domainId, long accountId,
|
||||||
|
String payloadUrl, String secretKey, boolean sslVerification, Scope scope) {
|
||||||
|
this.uuid = UUID.randomUUID().toString();
|
||||||
|
this.name = name;
|
||||||
|
this.description = description;
|
||||||
|
this.state = state;
|
||||||
|
this.domainId = domainId;
|
||||||
|
this.accountId = accountId;
|
||||||
|
this.payloadUrl = payloadUrl;
|
||||||
|
this.secretKey = secretKey;
|
||||||
|
this.sslVerification = sslVerification;
|
||||||
|
this.scope = scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For creating a dummy rule for testing delivery
|
||||||
|
*/
|
||||||
|
public WebhookVO(long domainId, long accountId, String payloadUrl, String secretKey, boolean sslVerification) {
|
||||||
|
this.uuid = UUID.randomUUID().toString();
|
||||||
|
this.id = ID_DUMMY;
|
||||||
|
this.name = NAME_DUMMY;
|
||||||
|
this.description = NAME_DUMMY;
|
||||||
|
this.state = State.Enabled;
|
||||||
|
this.domainId = domainId;
|
||||||
|
this.accountId = accountId;
|
||||||
|
this.payloadUrl = payloadUrl;
|
||||||
|
this.secretKey = secretKey;
|
||||||
|
this.sslVerification = sslVerification;
|
||||||
|
this.scope = Scope.Local;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
name=webhook
|
||||||
|
parent=event
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
<!--
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
-->
|
||||||
|
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:context="http://www.springframework.org/schema/context"
|
||||||
|
xmlns:aop="http://www.springframework.org/schema/aop"
|
||||||
|
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||||
|
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
||||||
|
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
|
||||||
|
http://www.springframework.org/schema/context
|
||||||
|
http://www.springframework.org/schema/context/spring-context-3.0.xsd"
|
||||||
|
>
|
||||||
|
|
||||||
|
<bean id="webhookDao" class="org.apache.cloudstack.mom.webhook.dao.WebhookDaoImpl" />
|
||||||
|
<bean id="webhookJoinDao" class="org.apache.cloudstack.mom.webhook.dao.WebhookJoinDaoImpl" />
|
||||||
|
<bean id="webhookDeliveryDao" class="org.apache.cloudstack.mom.webhook.dao.WebhookDeliveryDaoImpl" />
|
||||||
|
<bean id="webhookDeliveryJoinDao" class="org.apache.cloudstack.mom.webhook.dao.WebhookDeliveryJoinDaoImpl" />
|
||||||
|
<bean id="webhookApiService" class="org.apache.cloudstack.mom.webhook.WebhookApiServiceImpl" />
|
||||||
|
<bean id="webhookService" class="org.apache.cloudstack.mom.webhook.WebhookServiceImpl" />
|
||||||
|
|
||||||
|
<bean id="webhookEventBus" class="org.apache.cloudstack.mom.webhook.WebhookEventBus">
|
||||||
|
<property name="name" value="webhookEventBus" />
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
</beans>
|
||||||
@ -0,0 +1,253 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
package org.apache.cloudstack.mom.webhook;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.acl.SecurityChecker;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.command.user.DeleteWebhookCmd;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.response.WebhookResponse;
|
||||||
|
import org.apache.cloudstack.mom.webhook.dao.WebhookDao;
|
||||||
|
import org.apache.cloudstack.mom.webhook.dao.WebhookJoinDao;
|
||||||
|
import org.apache.cloudstack.mom.webhook.vo.WebhookJoinVO;
|
||||||
|
import org.apache.cloudstack.mom.webhook.vo.WebhookVO;
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockedStatic;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
|
import com.cloud.api.ApiResponseHelper;
|
||||||
|
import com.cloud.domain.DomainVO;
|
||||||
|
import com.cloud.domain.dao.DomainDao;
|
||||||
|
import com.cloud.exception.InvalidParameterValueException;
|
||||||
|
import com.cloud.exception.PermissionDeniedException;
|
||||||
|
import com.cloud.user.Account;
|
||||||
|
import com.cloud.user.AccountManager;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class WebhookApiServiceImplTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
WebhookDao webhookDao;
|
||||||
|
@Mock
|
||||||
|
WebhookJoinDao webhookJoinDao;
|
||||||
|
@Mock
|
||||||
|
AccountManager accountManager;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
DomainDao domainDao;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
WebhookApiServiceImpl webhookApiServiceImpl = new WebhookApiServiceImpl();
|
||||||
|
|
||||||
|
private WebhookJoinVO prepareTestWebhookJoinVO() {
|
||||||
|
String name = "webhook";
|
||||||
|
String description = "webhook-description";
|
||||||
|
Webhook.State state = Webhook.State.Enabled;
|
||||||
|
String payloadUrl = "url";
|
||||||
|
String secretKey = "key";
|
||||||
|
boolean sslVerification = false;
|
||||||
|
Webhook.Scope scope = Webhook.Scope.Local;
|
||||||
|
WebhookJoinVO webhookJoinVO = new WebhookJoinVO();
|
||||||
|
ReflectionTestUtils.setField(webhookJoinVO, "name", name);
|
||||||
|
ReflectionTestUtils.setField(webhookJoinVO, "description", description);
|
||||||
|
ReflectionTestUtils.setField(webhookJoinVO, "state", state);
|
||||||
|
ReflectionTestUtils.setField(webhookJoinVO, "payloadUrl", payloadUrl);
|
||||||
|
ReflectionTestUtils.setField(webhookJoinVO, "secretKey", secretKey);
|
||||||
|
ReflectionTestUtils.setField(webhookJoinVO, "sslVerification", sslVerification);
|
||||||
|
ReflectionTestUtils.setField(webhookJoinVO, "scope", scope);
|
||||||
|
return webhookJoinVO;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateWebhookResponseWithWebhookJoinVO(WebhookResponse response, WebhookJoinVO webhookJoinVO) {
|
||||||
|
Assert.assertEquals(webhookJoinVO.getName(), ReflectionTestUtils.getField(response, "name"));
|
||||||
|
Assert.assertEquals(webhookJoinVO.getDescription(), ReflectionTestUtils.getField(response, "description"));
|
||||||
|
Assert.assertEquals(webhookJoinVO.getState().toString(), ReflectionTestUtils.getField(response, "state"));
|
||||||
|
Assert.assertEquals(webhookJoinVO.getPayloadUrl(), ReflectionTestUtils.getField(response, "payloadUrl"));
|
||||||
|
Assert.assertEquals(webhookJoinVO.getSecretKey(), ReflectionTestUtils.getField(response, "secretKey"));
|
||||||
|
Assert.assertEquals(webhookJoinVO.isSslVerification(), ReflectionTestUtils.getField(response, "sslVerification"));
|
||||||
|
Assert.assertEquals(webhookJoinVO.getScope().toString(), ReflectionTestUtils.getField(response, "scope"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateWebhookResponse() {
|
||||||
|
WebhookJoinVO webhookJoinVO = prepareTestWebhookJoinVO();
|
||||||
|
try (MockedStatic<ApiResponseHelper> mockedApiResponseHelper = Mockito.mockStatic(ApiResponseHelper.class)) {
|
||||||
|
WebhookResponse response = webhookApiServiceImpl.createWebhookResponse(webhookJoinVO);
|
||||||
|
validateWebhookResponseWithWebhookJoinVO(response, webhookJoinVO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateWebhookResponseId() {
|
||||||
|
WebhookJoinVO webhookJoinVO = prepareTestWebhookJoinVO();
|
||||||
|
long id = 1L;
|
||||||
|
Mockito.when(webhookJoinDao.findById(id)).thenReturn(webhookJoinVO);
|
||||||
|
try (MockedStatic<ApiResponseHelper> mockedApiResponseHelper = Mockito.mockStatic(ApiResponseHelper.class)) {
|
||||||
|
WebhookResponse response = webhookApiServiceImpl.createWebhookResponse(id);
|
||||||
|
validateWebhookResponseWithWebhookJoinVO(response, webhookJoinVO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetIdsOfAccessibleWebhooksAdmin() {
|
||||||
|
Account account = Mockito.mock(Account.class);
|
||||||
|
Mockito.when(account.getType()).thenReturn(Account.Type.ADMIN);
|
||||||
|
Assert.assertTrue(CollectionUtils.isEmpty(webhookApiServiceImpl.getIdsOfAccessibleWebhooks(account)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetIdsOfAccessibleWebhooksDomainAdmin() {
|
||||||
|
Long accountId = 1L;
|
||||||
|
Account account = Mockito.mock(Account.class);
|
||||||
|
Mockito.when(account.getType()).thenReturn(Account.Type.DOMAIN_ADMIN);
|
||||||
|
Mockito.when(account.getDomainId()).thenReturn(1L);
|
||||||
|
Mockito.when(account.getId()).thenReturn(accountId);
|
||||||
|
String domainPath = "d1";
|
||||||
|
DomainVO domain = Mockito.mock(DomainVO.class);
|
||||||
|
Mockito.when(domain.getPath()).thenReturn(domainPath);
|
||||||
|
Mockito.when(domainDao.findById(1L)).thenReturn(domain);
|
||||||
|
WebhookJoinVO webhookJoinVO = Mockito.mock(WebhookJoinVO.class);
|
||||||
|
Mockito.when(webhookJoinVO.getId()).thenReturn(1L);
|
||||||
|
Mockito.when(webhookJoinDao.listByAccountOrDomain(accountId, domainPath)).thenReturn(List.of(webhookJoinVO));
|
||||||
|
List<Long> result = webhookApiServiceImpl.getIdsOfAccessibleWebhooks(account);
|
||||||
|
Assert.assertTrue(CollectionUtils.isNotEmpty(result));
|
||||||
|
Assert.assertEquals(1, result.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetIdsOfAccessibleWebhooksNormalUser() {
|
||||||
|
Long accountId = 1L;
|
||||||
|
Account account = Mockito.mock(Account.class);
|
||||||
|
Mockito.when(account.getType()).thenReturn(Account.Type.NORMAL);
|
||||||
|
Mockito.when(account.getId()).thenReturn(accountId);
|
||||||
|
WebhookJoinVO webhookJoinVO = Mockito.mock(WebhookJoinVO.class);
|
||||||
|
Mockito.when(webhookJoinVO.getId()).thenReturn(1L);
|
||||||
|
Mockito.when(webhookJoinDao.listByAccountOrDomain(accountId, null)).thenReturn(List.of(webhookJoinVO));
|
||||||
|
List<Long> result = webhookApiServiceImpl.getIdsOfAccessibleWebhooks(account);
|
||||||
|
Assert.assertTrue(CollectionUtils.isNotEmpty(result));
|
||||||
|
Assert.assertEquals(1, result.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = InvalidParameterValueException.class)
|
||||||
|
public void testDeleteWebhookInvalidWebhook() {
|
||||||
|
try (MockedStatic<CallContext> callContextMocked = Mockito.mockStatic(CallContext.class)) {
|
||||||
|
DeleteWebhookCmd cmd = Mockito.mock(DeleteWebhookCmd.class);
|
||||||
|
Mockito.when(cmd.getId()).thenReturn(1L);
|
||||||
|
CallContext callContextMock = Mockito.mock(CallContext.class);
|
||||||
|
callContextMocked.when(CallContext::current).thenReturn(callContextMock);
|
||||||
|
webhookApiServiceImpl.deleteWebhook(cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = PermissionDeniedException.class)
|
||||||
|
public void testDeleteWebhookNoPermission() {
|
||||||
|
try (MockedStatic<CallContext> callContextMocked = Mockito.mockStatic(CallContext.class)) {
|
||||||
|
DeleteWebhookCmd cmd = Mockito.mock(DeleteWebhookCmd.class);
|
||||||
|
Mockito.when(cmd.getId()).thenReturn(1L);
|
||||||
|
WebhookVO webhookVO = Mockito.mock(WebhookVO.class);
|
||||||
|
Mockito.when(webhookDao.findById(1L)).thenReturn(webhookVO);
|
||||||
|
CallContext callContextMock = Mockito.mock(CallContext.class);
|
||||||
|
Account account = Mockito.mock(Account.class);
|
||||||
|
Mockito.when(callContextMock.getCallingAccount()).thenReturn(account);
|
||||||
|
callContextMocked.when(CallContext::current).thenReturn(callContextMock);
|
||||||
|
Mockito.doThrow(PermissionDeniedException.class).when(accountManager).checkAccess(account,
|
||||||
|
SecurityChecker.AccessType.OperateEntry, false, webhookVO);
|
||||||
|
webhookApiServiceImpl.deleteWebhook(cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeleteWebhook() {
|
||||||
|
try (MockedStatic<CallContext> callContextMocked = Mockito.mockStatic(CallContext.class)) {
|
||||||
|
DeleteWebhookCmd cmd = Mockito.mock(DeleteWebhookCmd.class);
|
||||||
|
Mockito.when(cmd.getId()).thenReturn(1L);
|
||||||
|
WebhookVO webhookVO = Mockito.mock(WebhookVO.class);
|
||||||
|
Mockito.when(webhookDao.findById(1L)).thenReturn(webhookVO);
|
||||||
|
CallContext callContextMock = Mockito.mock(CallContext.class);
|
||||||
|
Account account = Mockito.mock(Account.class);
|
||||||
|
Mockito.when(callContextMock.getCallingAccount()).thenReturn(account);
|
||||||
|
callContextMocked.when(CallContext::current).thenReturn(callContextMock);
|
||||||
|
Mockito.doNothing().when(accountManager).checkAccess(account,
|
||||||
|
SecurityChecker.AccessType.OperateEntry, false, webhookVO);
|
||||||
|
Mockito.doReturn(true).when(webhookDao).remove(Mockito.anyLong());
|
||||||
|
Assert.assertTrue(webhookApiServiceImpl.deleteWebhook(cmd));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateWebhookOwnerPayloadUrlNonExistent() {
|
||||||
|
Mockito.when(webhookDao.findByAccountAndPayloadUrl(Mockito.anyLong(), Mockito.anyString())).thenReturn(null);
|
||||||
|
Account account = Mockito.mock(Account.class);
|
||||||
|
String url = "url";
|
||||||
|
webhookApiServiceImpl.validateWebhookOwnerPayloadUrl(account, url, null);
|
||||||
|
webhookApiServiceImpl.validateWebhookOwnerPayloadUrl(account, url, Mockito.mock(Webhook.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = InvalidParameterValueException.class)
|
||||||
|
public void testValidateWebhookOwnerPayloadUrlCreateExist() {
|
||||||
|
Mockito.when(webhookDao.findByAccountAndPayloadUrl(Mockito.anyLong(), Mockito.anyString()))
|
||||||
|
.thenReturn(Mockito.mock(WebhookVO.class));
|
||||||
|
webhookApiServiceImpl.validateWebhookOwnerPayloadUrl(Mockito.mock(Account.class), "url",
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Webhook mockWebhook(long id) {
|
||||||
|
Webhook webhook = Mockito.mock(Webhook.class);
|
||||||
|
Mockito.when(webhook.getId()).thenReturn(id);
|
||||||
|
return webhook;
|
||||||
|
}
|
||||||
|
|
||||||
|
private WebhookVO mockWebhookVO(long id) {
|
||||||
|
WebhookVO webhook = Mockito.mock(WebhookVO.class);
|
||||||
|
Mockito.when(webhook.getId()).thenReturn(id);
|
||||||
|
return webhook;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateWebhookOwnerPayloadUrlUpdateSameExist() {
|
||||||
|
WebhookVO webhookVO = mockWebhookVO(1L);
|
||||||
|
Mockito.when(webhookDao.findByAccountAndPayloadUrl(Mockito.anyLong(), Mockito.anyString()))
|
||||||
|
.thenReturn(webhookVO);
|
||||||
|
webhookApiServiceImpl.validateWebhookOwnerPayloadUrl(Mockito.mock(Account.class), "url",
|
||||||
|
mockWebhook(1L));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = InvalidParameterValueException.class)
|
||||||
|
public void testValidateWebhookOwnerPayloadUrlUpdateDifferentExist() {
|
||||||
|
WebhookVO webhookVO = mockWebhookVO(2L);
|
||||||
|
Mockito.when(webhookDao.findByAccountAndPayloadUrl(Mockito.anyLong(), Mockito.anyString()))
|
||||||
|
.thenReturn(webhookVO);
|
||||||
|
webhookApiServiceImpl.validateWebhookOwnerPayloadUrl(Mockito.mock(Account.class), "url",
|
||||||
|
mockWebhook(1L));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetNormalizedPayloadUrl() {
|
||||||
|
Assert.assertEquals("http://abc.com", webhookApiServiceImpl.getNormalizedPayloadUrl("abc.com"));
|
||||||
|
Assert.assertEquals("http://abc.com", webhookApiServiceImpl.getNormalizedPayloadUrl("http://abc.com"));
|
||||||
|
Assert.assertEquals("https://abc.com",
|
||||||
|
webhookApiServiceImpl.getNormalizedPayloadUrl("https://abc.com"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook;
|
||||||
|
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.DecoderException;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class WebhookDeliveryThreadTest {
|
||||||
|
@InjectMocks
|
||||||
|
WebhookDeliveryThread webhookDeliveryThread;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsValidJson() {
|
||||||
|
Assert.assertFalse(webhookDeliveryThread.isValidJson("text"));
|
||||||
|
Assert.assertTrue(webhookDeliveryThread.isValidJson("{ \"CloudStack\": \"works!\" }"));
|
||||||
|
Assert.assertTrue(webhookDeliveryThread.isValidJson("[{ \"CloudStack\": \"works!\" }]"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGenerateHMACSignature() {
|
||||||
|
String data = "CloudStack works!";
|
||||||
|
String key = "Pj4pnwSUBZ4wQFXw2zWdVY1k5Ku9bIy70wCNG1DmS8keO7QapCLw2Axtgc2nEPYzfFCfB38ATNLt6caDqU2dSw";
|
||||||
|
String result = "HYLWSII5Ap23WeSaykNsIo6mOhmV3d18s5p2cq2ebCA=";
|
||||||
|
try {
|
||||||
|
String sign = WebhookDeliveryThread.generateHMACSignature(data, key);
|
||||||
|
Assert.assertEquals(result, sign);
|
||||||
|
} catch (InvalidKeyException | NoSuchAlgorithmException | DecoderException e) {
|
||||||
|
Assert.fail(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetDeliveryTries() {
|
||||||
|
int tries = 2;
|
||||||
|
webhookDeliveryThread.setDeliveryTries(tries);
|
||||||
|
Assert.assertEquals(tries, ReflectionTestUtils.getField(webhookDeliveryThread, "deliveryTries"));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,106 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import javax.naming.ConfigurationException;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.framework.events.Event;
|
||||||
|
import org.apache.cloudstack.framework.events.EventBusException;
|
||||||
|
import org.apache.cloudstack.framework.events.EventSubscriber;
|
||||||
|
import org.apache.cloudstack.framework.events.EventTopic;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class WebhookEventBusTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
WebhookService webhookService;
|
||||||
|
@InjectMocks
|
||||||
|
WebhookEventBus eventBus = new WebhookEventBus();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConfigure() {
|
||||||
|
String name = "name";
|
||||||
|
try {
|
||||||
|
Assert.assertTrue(eventBus.configure(name, new HashMap<>()));
|
||||||
|
String result = (String)ReflectionTestUtils.getField(eventBus, "_name");
|
||||||
|
Assert.assertEquals(name, result);
|
||||||
|
} catch (ConfigurationException e) {
|
||||||
|
Assert.fail("Error configuring");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetName() {
|
||||||
|
String name = "name";
|
||||||
|
eventBus.setName(name);
|
||||||
|
String result = (String)ReflectionTestUtils.getField(eventBus, "_name");
|
||||||
|
Assert.assertEquals(name, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetName() {
|
||||||
|
String name = "name";
|
||||||
|
ReflectionTestUtils.setField(eventBus, "_name", name);
|
||||||
|
Assert.assertEquals(name, eventBus.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStart() {
|
||||||
|
Assert.assertTrue(eventBus.start());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStop() {
|
||||||
|
Assert.assertTrue(eventBus.stop());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSubscribe() {
|
||||||
|
try {
|
||||||
|
Assert.assertNotNull(eventBus.subscribe(Mockito.mock(EventTopic.class), Mockito.mock(EventSubscriber.class)));
|
||||||
|
} catch (EventBusException e) {
|
||||||
|
Assert.fail("Error subscribing");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUnsubscribe() {
|
||||||
|
try {
|
||||||
|
eventBus.unsubscribe(Mockito.mock(UUID.class), Mockito.mock(EventSubscriber.class));
|
||||||
|
} catch (EventBusException e) {
|
||||||
|
Assert.fail("Error unsubscribing");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = EventBusException.class)
|
||||||
|
public void testPublishException() throws EventBusException {
|
||||||
|
Mockito.doThrow(EventBusException.class).when(webhookService).handleEvent(Mockito.any(Event.class));
|
||||||
|
eventBus.publish(Mockito.mock(Event.class));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,173 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook.api.command.user;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.mom.webhook.WebhookApiService;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.response.WebhookResponse;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
|
import com.cloud.user.Account;
|
||||||
|
import com.cloud.user.AccountVO;
|
||||||
|
import com.cloud.user.User;
|
||||||
|
import com.cloud.user.UserVO;
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class CreateWebhookCmdTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
WebhookApiService webhookApiService;
|
||||||
|
|
||||||
|
private Object getCommandMethodValue(Object obj, String methodName) {
|
||||||
|
Object result = null;
|
||||||
|
try {
|
||||||
|
Method method = obj.getClass().getMethod(methodName);
|
||||||
|
result = method.invoke(obj);
|
||||||
|
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
|
||||||
|
Assert.fail(String.format("Failed to get method %s value", methodName));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runStringMemberTest(String memberName) {
|
||||||
|
String methodName = "get" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1);
|
||||||
|
CreateWebhookCmd cmd = new CreateWebhookCmd();
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, null);
|
||||||
|
Assert.assertNull(getCommandMethodValue(cmd, methodName));
|
||||||
|
String value = UUID.randomUUID().toString();
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, value);
|
||||||
|
Assert.assertEquals(value, getCommandMethodValue(cmd, methodName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runLongMemberTest(String memberName) {
|
||||||
|
String methodName = "get" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1);
|
||||||
|
CreateWebhookCmd cmd = new CreateWebhookCmd();
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, null);
|
||||||
|
Assert.assertNull(getCommandMethodValue(cmd, methodName));
|
||||||
|
Long value = 100L;
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, value);
|
||||||
|
Assert.assertEquals(value, getCommandMethodValue(cmd, methodName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runBooleanMemberTest(String memberName) {
|
||||||
|
String methodName = "is" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1);
|
||||||
|
CreateWebhookCmd cmd = new CreateWebhookCmd();
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, null);
|
||||||
|
Assert.assertFalse((boolean)getCommandMethodValue(cmd, methodName));
|
||||||
|
Boolean value = true;
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, value);
|
||||||
|
Assert.assertEquals(value, getCommandMethodValue(cmd, methodName));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetName() {
|
||||||
|
runStringMemberTest("name");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetDescription() {
|
||||||
|
runStringMemberTest("description");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetPayloadUrl() {
|
||||||
|
runStringMemberTest("payloadUrl");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetSecretKey() {
|
||||||
|
runStringMemberTest("secretKey");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetScope() {
|
||||||
|
runStringMemberTest("scope");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetState() {
|
||||||
|
runStringMemberTest("state");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAccount() {
|
||||||
|
runStringMemberTest("accountName");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetDomainId() {
|
||||||
|
runLongMemberTest("domainId");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetProjectId() {
|
||||||
|
runLongMemberTest("projectId");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsSslVerification() {
|
||||||
|
runBooleanMemberTest("sslVerification");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetEntityOwnerId() {
|
||||||
|
Account account = new AccountVO("testaccount", 1L, "networkdomain", Account.Type.NORMAL, "uuid");
|
||||||
|
UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString(), User.Source.UNKNOWN);
|
||||||
|
CallContext.register(user, account);
|
||||||
|
CreateWebhookCmd cmd = new CreateWebhookCmd();
|
||||||
|
Assert.assertEquals(account.getId(), cmd.getEntityOwnerId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = ServerApiException.class)
|
||||||
|
public void testExecuteNullResponse() {
|
||||||
|
CreateWebhookCmd cmd = new CreateWebhookCmd();
|
||||||
|
cmd.webhookApiService = webhookApiService;
|
||||||
|
Mockito.when(webhookApiService.createWebhook(cmd)).thenReturn(null);
|
||||||
|
cmd.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = ServerApiException.class)
|
||||||
|
public void testExecuteCRE() {
|
||||||
|
CreateWebhookCmd cmd = new CreateWebhookCmd();
|
||||||
|
cmd.webhookApiService = webhookApiService;
|
||||||
|
Mockito.when(webhookApiService.createWebhook(cmd)).thenThrow(CloudRuntimeException.class);
|
||||||
|
cmd.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExecute() {
|
||||||
|
CreateWebhookCmd cmd = new CreateWebhookCmd();
|
||||||
|
cmd.webhookApiService = webhookApiService;
|
||||||
|
WebhookResponse response = new WebhookResponse();
|
||||||
|
Mockito.when(webhookApiService.createWebhook(cmd)).thenReturn(response);
|
||||||
|
cmd.execute();
|
||||||
|
Assert.assertEquals(cmd.getCommandName(), response.getResponseName());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,106 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook.api.command.user;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.mom.webhook.WebhookApiService;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
|
import com.cloud.user.Account;
|
||||||
|
import com.cloud.user.AccountVO;
|
||||||
|
import com.cloud.user.User;
|
||||||
|
import com.cloud.user.UserVO;
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class DeleteWebhookCmdTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
WebhookApiService webhookApiService;
|
||||||
|
|
||||||
|
private Object getCommandMethodValue(Object obj, String methodName) {
|
||||||
|
Object result = null;
|
||||||
|
try {
|
||||||
|
Method method = obj.getClass().getMethod(methodName);
|
||||||
|
result = method.invoke(obj);
|
||||||
|
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
|
||||||
|
Assert.fail(String.format("Failed to get method %s value", methodName));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runLongMemberTest(String memberName) {
|
||||||
|
String methodName = "get" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1);
|
||||||
|
DeleteWebhookCmd cmd = new DeleteWebhookCmd();
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, null);
|
||||||
|
Assert.assertNull(getCommandMethodValue(cmd, methodName));
|
||||||
|
Long value = 100L;
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, value);
|
||||||
|
Assert.assertEquals(value, getCommandMethodValue(cmd, methodName));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetId() {
|
||||||
|
runLongMemberTest("id");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetEntityOwnerId() {
|
||||||
|
Account account = new AccountVO("testaccount", 1L, "networkdomain", Account.Type.NORMAL, "uuid");
|
||||||
|
UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString(), User.Source.UNKNOWN);
|
||||||
|
CallContext.register(user, account);
|
||||||
|
DeleteWebhookCmd cmd = new DeleteWebhookCmd();
|
||||||
|
Assert.assertEquals(account.getId(), cmd.getEntityOwnerId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = ServerApiException.class)
|
||||||
|
public void testExecuteFalseResponse() {
|
||||||
|
DeleteWebhookCmd cmd = new DeleteWebhookCmd();
|
||||||
|
cmd.webhookApiService = webhookApiService;
|
||||||
|
Mockito.when(webhookApiService.deleteWebhook(cmd)).thenReturn(false);
|
||||||
|
cmd.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = ServerApiException.class)
|
||||||
|
public void testExecuteCRE() {
|
||||||
|
DeleteWebhookCmd cmd = new DeleteWebhookCmd();
|
||||||
|
cmd.webhookApiService = webhookApiService;
|
||||||
|
Mockito.when(webhookApiService.deleteWebhook(cmd)).thenThrow(CloudRuntimeException.class);
|
||||||
|
cmd.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExecute() {
|
||||||
|
DeleteWebhookCmd cmd = new DeleteWebhookCmd();
|
||||||
|
cmd.webhookApiService = webhookApiService;
|
||||||
|
Mockito.when(webhookApiService.deleteWebhook(cmd)).thenReturn(true);
|
||||||
|
cmd.execute();
|
||||||
|
Assert.assertNotNull(cmd.getResponseObject());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,108 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook.api.command.user;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.mom.webhook.WebhookApiService;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
|
import com.cloud.user.Account;
|
||||||
|
import com.cloud.user.AccountVO;
|
||||||
|
import com.cloud.user.User;
|
||||||
|
import com.cloud.user.UserVO;
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class DeleteWebhookDeliveryCmdTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
WebhookApiService webhookApiService;
|
||||||
|
|
||||||
|
private Object getCommandMethodValue(Object obj, String methodName) {
|
||||||
|
Object result = null;
|
||||||
|
try {
|
||||||
|
Method method = obj.getClass().getMethod(methodName);
|
||||||
|
result = method.invoke(obj);
|
||||||
|
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
|
||||||
|
Assert.fail(String.format("Failed to get method %s value", methodName));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runLongMemberTest(String memberName) {
|
||||||
|
String methodName = "get" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1);
|
||||||
|
DeleteWebhookDeliveryCmd cmd = new DeleteWebhookDeliveryCmd();
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, null);
|
||||||
|
Assert.assertNull(getCommandMethodValue(cmd, methodName));
|
||||||
|
Long value = 100L;
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, value);
|
||||||
|
Assert.assertEquals(value, getCommandMethodValue(cmd, methodName));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetId() {
|
||||||
|
runLongMemberTest("id");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetWebhookId() {
|
||||||
|
runLongMemberTest("webhookId");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetManagementServerId() {
|
||||||
|
runLongMemberTest("managementServerId");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetEntityOwnerId() {
|
||||||
|
Account account = new AccountVO("testaccount", 1L, "networkdomain", Account.Type.NORMAL, "uuid");
|
||||||
|
UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString(), User.Source.UNKNOWN);
|
||||||
|
CallContext.register(user, account);
|
||||||
|
DeleteWebhookDeliveryCmd cmd = new DeleteWebhookDeliveryCmd();
|
||||||
|
Assert.assertEquals(account.getId(), cmd.getEntityOwnerId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = ServerApiException.class)
|
||||||
|
public void testExecuteCRE() {
|
||||||
|
DeleteWebhookDeliveryCmd cmd = new DeleteWebhookDeliveryCmd();
|
||||||
|
cmd.webhookApiService = webhookApiService;
|
||||||
|
Mockito.when(webhookApiService.deleteWebhookDelivery(cmd)).thenThrow(CloudRuntimeException.class);
|
||||||
|
cmd.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExecute() {
|
||||||
|
DeleteWebhookDeliveryCmd cmd = new DeleteWebhookDeliveryCmd();
|
||||||
|
cmd.webhookApiService = webhookApiService;
|
||||||
|
Mockito.when(webhookApiService.deleteWebhookDelivery(cmd)).thenReturn(10);
|
||||||
|
cmd.execute();
|
||||||
|
Assert.assertNotNull(cmd.getResponseObject());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,153 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook.api.command.user;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.mom.webhook.WebhookApiService;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.response.WebhookDeliveryResponse;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
|
import com.cloud.user.Account;
|
||||||
|
import com.cloud.user.AccountVO;
|
||||||
|
import com.cloud.user.User;
|
||||||
|
import com.cloud.user.UserVO;
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class ExecuteWebhookDeliveryCmdTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
WebhookApiService webhookApiService;
|
||||||
|
|
||||||
|
private Object getCommandMethodValue(Object obj, String methodName) {
|
||||||
|
Object result = null;
|
||||||
|
try {
|
||||||
|
Method method = obj.getClass().getMethod(methodName);
|
||||||
|
result = method.invoke(obj);
|
||||||
|
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
|
||||||
|
Assert.fail(String.format("Failed to get method %s value", methodName));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runStringMemberTest(String memberName) {
|
||||||
|
String methodName = "get" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1);
|
||||||
|
ExecuteWebhookDeliveryCmd cmd = new ExecuteWebhookDeliveryCmd();
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, null);
|
||||||
|
Assert.assertNull(getCommandMethodValue(cmd, methodName));
|
||||||
|
String value = UUID.randomUUID().toString();
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, value);
|
||||||
|
Assert.assertEquals(value, getCommandMethodValue(cmd, methodName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runLongMemberTest(String memberName) {
|
||||||
|
String methodName = "get" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1);
|
||||||
|
ExecuteWebhookDeliveryCmd cmd = new ExecuteWebhookDeliveryCmd();
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, null);
|
||||||
|
Assert.assertNull(getCommandMethodValue(cmd, methodName));
|
||||||
|
Long value = 100L;
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, value);
|
||||||
|
Assert.assertEquals(value, getCommandMethodValue(cmd, methodName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runBooleanMemberTest(String memberName) {
|
||||||
|
String methodName = "is" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1);
|
||||||
|
ExecuteWebhookDeliveryCmd cmd = new ExecuteWebhookDeliveryCmd();
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, null);
|
||||||
|
Assert.assertNull(getCommandMethodValue(cmd, methodName));
|
||||||
|
Boolean value = true;
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, value);
|
||||||
|
Assert.assertEquals(value, getCommandMethodValue(cmd, methodName));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetId() {
|
||||||
|
runLongMemberTest("id");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetWebhookId() {
|
||||||
|
runLongMemberTest("webhookId");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetPayloadUrl() {
|
||||||
|
runStringMemberTest("payloadUrl");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetSecretKey() {
|
||||||
|
runStringMemberTest("secretKey");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsSslVerification() {
|
||||||
|
runBooleanMemberTest("sslVerification");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetPayload() {
|
||||||
|
runStringMemberTest("payload");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetEntityOwnerId() {
|
||||||
|
Account account = new AccountVO("testaccount", 1L, "networkdomain", Account.Type.NORMAL, "uuid");
|
||||||
|
UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString(), User.Source.UNKNOWN);
|
||||||
|
CallContext.register(user, account);
|
||||||
|
ExecuteWebhookDeliveryCmd cmd = new ExecuteWebhookDeliveryCmd();
|
||||||
|
Assert.assertEquals(account.getId(), cmd.getEntityOwnerId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = ServerApiException.class)
|
||||||
|
public void testExecuteNullResponse() {
|
||||||
|
ExecuteWebhookDeliveryCmd cmd = new ExecuteWebhookDeliveryCmd();
|
||||||
|
cmd.webhookApiService = webhookApiService;
|
||||||
|
Mockito.when(webhookApiService.executeWebhookDelivery(cmd)).thenReturn(null);
|
||||||
|
cmd.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = ServerApiException.class)
|
||||||
|
public void testExecuteCRE() {
|
||||||
|
ExecuteWebhookDeliveryCmd cmd = new ExecuteWebhookDeliveryCmd();
|
||||||
|
cmd.webhookApiService = webhookApiService;
|
||||||
|
Mockito.when(webhookApiService.executeWebhookDelivery(cmd)).thenThrow(CloudRuntimeException.class);
|
||||||
|
cmd.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExecute() {
|
||||||
|
ExecuteWebhookDeliveryCmd cmd = new ExecuteWebhookDeliveryCmd();
|
||||||
|
cmd.webhookApiService = webhookApiService;
|
||||||
|
WebhookDeliveryResponse response = new WebhookDeliveryResponse();
|
||||||
|
Mockito.when(webhookApiService.executeWebhookDelivery(cmd)).thenReturn(response);
|
||||||
|
cmd.execute();
|
||||||
|
Assert.assertEquals(cmd.getCommandName(), response.getResponseName());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,141 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook.api.command.user;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.response.ListResponse;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.mom.webhook.WebhookApiService;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.response.WebhookDeliveryResponse;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
|
import com.cloud.user.Account;
|
||||||
|
import com.cloud.user.AccountVO;
|
||||||
|
import com.cloud.user.User;
|
||||||
|
import com.cloud.user.UserVO;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class ListWebhookDeliveriesCmdTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
WebhookApiService webhookApiService;
|
||||||
|
|
||||||
|
private Object getCommandMethodValue(Object obj, String methodName) {
|
||||||
|
Object result = null;
|
||||||
|
try {
|
||||||
|
Method method = obj.getClass().getMethod(methodName);
|
||||||
|
result = method.invoke(obj);
|
||||||
|
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
|
||||||
|
Assert.fail(String.format("Failed to get method %s value", methodName));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runLongMemberTest(String memberName) {
|
||||||
|
String methodName = "get" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1);
|
||||||
|
ListWebhookDeliveriesCmd cmd = new ListWebhookDeliveriesCmd();
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, null);
|
||||||
|
Assert.assertNull(getCommandMethodValue(cmd, methodName));
|
||||||
|
Long value = 100L;
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, value);
|
||||||
|
Assert.assertEquals(value, getCommandMethodValue(cmd, methodName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runStringMemberTest(String memberName) {
|
||||||
|
String methodName = "get" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1);
|
||||||
|
ListWebhookDeliveriesCmd cmd = new ListWebhookDeliveriesCmd();
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, null);
|
||||||
|
Assert.assertNull(getCommandMethodValue(cmd, methodName));
|
||||||
|
String value = UUID.randomUUID().toString();
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, value);
|
||||||
|
Assert.assertEquals(value, getCommandMethodValue(cmd, methodName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runDateMemberTest(String memberName) {
|
||||||
|
String methodName = "get" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1);
|
||||||
|
ListWebhookDeliveriesCmd cmd = new ListWebhookDeliveriesCmd();
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, null);
|
||||||
|
Assert.assertNull(getCommandMethodValue(cmd, methodName));
|
||||||
|
Date value = new Date();
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, value);
|
||||||
|
Assert.assertEquals(value, getCommandMethodValue(cmd, methodName));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetId() {
|
||||||
|
runLongMemberTest("id");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetWebhookId() {
|
||||||
|
runLongMemberTest("webhookId");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetManagementServerId() {
|
||||||
|
runLongMemberTest("managementServerId");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStartDate() {
|
||||||
|
runDateMemberTest("startDate");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEndDate() {
|
||||||
|
runDateMemberTest("endDate");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEventType() {
|
||||||
|
runStringMemberTest("eventType");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetEntityOwnerId() {
|
||||||
|
Account account = new AccountVO("testaccount", 1L, "networkdomain", Account.Type.NORMAL, "uuid");
|
||||||
|
UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString(), User.Source.UNKNOWN);
|
||||||
|
CallContext.register(user, account);
|
||||||
|
ListWebhookDeliveriesCmd cmd = new ListWebhookDeliveriesCmd();
|
||||||
|
Assert.assertEquals(account.getId(), cmd.getEntityOwnerId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExecute() {
|
||||||
|
ListWebhookDeliveriesCmd cmd = new ListWebhookDeliveriesCmd();
|
||||||
|
cmd.webhookApiService = webhookApiService;
|
||||||
|
List<WebhookDeliveryResponse> responseList = new ArrayList<>();
|
||||||
|
ListResponse<WebhookDeliveryResponse> listResponse = new ListResponse<>();
|
||||||
|
listResponse.setResponses(responseList);
|
||||||
|
Mockito.when(webhookApiService.listWebhookDeliveries(cmd)).thenReturn(listResponse);
|
||||||
|
cmd.execute();
|
||||||
|
Assert.assertNotNull(cmd.getResponseObject());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,105 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook.api.command.user;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.response.ListResponse;
|
||||||
|
import org.apache.cloudstack.mom.webhook.WebhookApiService;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.response.WebhookResponse;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class ListWebhooksCmdTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
WebhookApiService webhookApiService;
|
||||||
|
|
||||||
|
private Object getCommandMethodValue(Object obj, String methodName) {
|
||||||
|
Object result = null;
|
||||||
|
try {
|
||||||
|
Method method = obj.getClass().getMethod(methodName);
|
||||||
|
result = method.invoke(obj);
|
||||||
|
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
|
||||||
|
Assert.fail(String.format("Failed to get method %s value", methodName));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runStringMemberTest(String memberName) {
|
||||||
|
String methodName = "get" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1);
|
||||||
|
ListWebhooksCmd cmd = new ListWebhooksCmd();
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, null);
|
||||||
|
Assert.assertNull(getCommandMethodValue(cmd, methodName));
|
||||||
|
String value = UUID.randomUUID().toString();
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, value);
|
||||||
|
Assert.assertEquals(value, getCommandMethodValue(cmd, methodName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runLongMemberTest(String memberName) {
|
||||||
|
String methodName = "get" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1);
|
||||||
|
ListWebhooksCmd cmd = new ListWebhooksCmd();
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, null);
|
||||||
|
Assert.assertNull(getCommandMethodValue(cmd, methodName));
|
||||||
|
Long value = 100L;
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, value);
|
||||||
|
Assert.assertEquals(value, getCommandMethodValue(cmd, methodName));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetId() {
|
||||||
|
runLongMemberTest("id");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetName() {
|
||||||
|
runStringMemberTest("name");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetState() {
|
||||||
|
runStringMemberTest("state");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetScope() {
|
||||||
|
runStringMemberTest("scope");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExecute() {
|
||||||
|
ListWebhooksCmd cmd = new ListWebhooksCmd();
|
||||||
|
cmd.webhookApiService = webhookApiService;
|
||||||
|
List<WebhookResponse> responseList = new ArrayList<>();
|
||||||
|
ListResponse<WebhookResponse> listResponse = new ListResponse<>();
|
||||||
|
listResponse.setResponses(responseList);
|
||||||
|
Mockito.when(webhookApiService.listWebhooks(cmd)).thenReturn(listResponse);
|
||||||
|
cmd.execute();
|
||||||
|
Assert.assertNotNull(cmd.getResponseObject());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,163 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.mom.webhook.api.command.user;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.api.ServerApiException;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.mom.webhook.WebhookApiService;
|
||||||
|
import org.apache.cloudstack.mom.webhook.api.response.WebhookResponse;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
|
import com.cloud.user.Account;
|
||||||
|
import com.cloud.user.AccountVO;
|
||||||
|
import com.cloud.user.User;
|
||||||
|
import com.cloud.user.UserVO;
|
||||||
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class UpdateWebhookCmdTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
WebhookApiService webhookApiService;
|
||||||
|
|
||||||
|
private Object getCommandMethodValue(Object obj, String methodName) {
|
||||||
|
Object result = null;
|
||||||
|
try {
|
||||||
|
Method method = obj.getClass().getMethod(methodName);
|
||||||
|
result = method.invoke(obj);
|
||||||
|
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
|
||||||
|
Assert.fail(String.format("Failed to get method %s value", methodName));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runStringMemberTest(String memberName) {
|
||||||
|
String methodName = "get" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1);
|
||||||
|
UpdateWebhookCmd cmd = new UpdateWebhookCmd();
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, null);
|
||||||
|
Assert.assertNull(getCommandMethodValue(cmd, methodName));
|
||||||
|
String value = UUID.randomUUID().toString();
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, value);
|
||||||
|
Assert.assertEquals(value, getCommandMethodValue(cmd, methodName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runLongMemberTest(String memberName) {
|
||||||
|
String methodName = "get" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1);
|
||||||
|
UpdateWebhookCmd cmd = new UpdateWebhookCmd();
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, null);
|
||||||
|
Assert.assertNull(getCommandMethodValue(cmd, methodName));
|
||||||
|
Long value = 100L;
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, value);
|
||||||
|
Assert.assertEquals(value, getCommandMethodValue(cmd, methodName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runBooleanMemberTest(String memberName) {
|
||||||
|
String methodName = "is" + memberName.substring(0, 1).toUpperCase() + memberName.substring(1);
|
||||||
|
UpdateWebhookCmd cmd = new UpdateWebhookCmd();
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, null);
|
||||||
|
Assert.assertNull(getCommandMethodValue(cmd, methodName));
|
||||||
|
Boolean value = true;
|
||||||
|
ReflectionTestUtils.setField(cmd, memberName, value);
|
||||||
|
Assert.assertEquals(value, getCommandMethodValue(cmd, methodName));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetId() {
|
||||||
|
runLongMemberTest("id");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetName() {
|
||||||
|
runStringMemberTest("name");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetDescription() {
|
||||||
|
runStringMemberTest("description");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetPayloadUrl() {
|
||||||
|
runStringMemberTest("payloadUrl");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetSecretKey() {
|
||||||
|
runStringMemberTest("secretKey");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetScope() {
|
||||||
|
runStringMemberTest("scope");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetState() {
|
||||||
|
runStringMemberTest("state");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsSslVerification() {
|
||||||
|
runBooleanMemberTest("sslVerification");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetEntityOwnerId() {
|
||||||
|
Account account = new AccountVO("testaccount", 1L, "networkdomain", Account.Type.NORMAL, "uuid");
|
||||||
|
UserVO user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString(), User.Source.UNKNOWN);
|
||||||
|
CallContext.register(user, account);
|
||||||
|
UpdateWebhookCmd cmd = new UpdateWebhookCmd();
|
||||||
|
Assert.assertEquals(account.getId(), cmd.getEntityOwnerId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = ServerApiException.class)
|
||||||
|
public void testExecuteNullResponse() {
|
||||||
|
UpdateWebhookCmd cmd = new UpdateWebhookCmd();
|
||||||
|
cmd.webhookApiService = webhookApiService;
|
||||||
|
Mockito.when(webhookApiService.updateWebhook(cmd)).thenReturn(null);
|
||||||
|
cmd.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = ServerApiException.class)
|
||||||
|
public void testExecuteCRE() {
|
||||||
|
UpdateWebhookCmd cmd = new UpdateWebhookCmd();
|
||||||
|
cmd.webhookApiService = webhookApiService;
|
||||||
|
Mockito.when(webhookApiService.updateWebhook(cmd)).thenThrow(CloudRuntimeException.class);
|
||||||
|
cmd.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExecute() {
|
||||||
|
UpdateWebhookCmd cmd = new UpdateWebhookCmd();
|
||||||
|
cmd.webhookApiService = webhookApiService;
|
||||||
|
WebhookResponse response = new WebhookResponse();
|
||||||
|
Mockito.when(webhookApiService.updateWebhook(cmd)).thenReturn(response);
|
||||||
|
cmd.execute();
|
||||||
|
Assert.assertEquals(cmd.getCommandName(), response.getResponseName());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -18,21 +18,19 @@
|
|||||||
package org.apache.cloudstack.network.contrail.management;
|
package org.apache.cloudstack.network.contrail.management;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import org.apache.cloudstack.context.CallContext;
|
|
||||||
import org.apache.cloudstack.framework.events.EventBus;
|
|
||||||
import org.apache.cloudstack.framework.events.EventBusException;
|
|
||||||
import org.aopalliance.intercept.MethodInterceptor;
|
import org.aopalliance.intercept.MethodInterceptor;
|
||||||
import org.aopalliance.intercept.MethodInvocation;
|
import org.aopalliance.intercept.MethodInvocation;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.framework.events.EventDistributor;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import com.cloud.event.ActionEvent;
|
import com.cloud.event.ActionEvent;
|
||||||
import com.cloud.event.ActionEvents;
|
import com.cloud.event.ActionEvents;
|
||||||
@ -43,15 +41,20 @@ import com.cloud.server.ManagementService;
|
|||||||
import com.cloud.utils.component.ComponentContext;
|
import com.cloud.utils.component.ComponentContext;
|
||||||
import com.cloud.utils.component.ComponentMethodInterceptor;
|
import com.cloud.utils.component.ComponentMethodInterceptor;
|
||||||
|
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class EventUtils {
|
public class EventUtils {
|
||||||
protected static Logger LOGGER = LogManager.getLogger(EventUtils.class);
|
protected static Logger LOGGER = LogManager.getLogger(EventUtils.class);
|
||||||
|
|
||||||
protected static EventBus s_eventBus = null;
|
private static EventDistributor eventDistributor;
|
||||||
|
|
||||||
public EventUtils() {
|
public EventUtils() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void setEventDistributor(EventDistributor eventDistributorImpl) {
|
||||||
|
eventDistributor = eventDistributorImpl;
|
||||||
|
}
|
||||||
|
|
||||||
private static void publishOnMessageBus(String eventCategory, String eventType, String details, Event.State state) {
|
private static void publishOnMessageBus(String eventCategory, String eventType, String details, Event.State state) {
|
||||||
|
|
||||||
if (state != com.cloud.event.Event.State.Completed) {
|
if (state != com.cloud.event.Event.State.Completed) {
|
||||||
@ -59,7 +62,7 @@ public class EventUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
s_eventBus = ComponentContext.getComponent(EventBus.class);
|
setEventDistributor(ComponentContext.getComponent(EventDistributor.class));
|
||||||
} catch (NoSuchBeanDefinitionException nbe) {
|
} catch (NoSuchBeanDefinitionException nbe) {
|
||||||
return; // no provider is configured to provide events bus, so just return
|
return; // no provider is configured to provide events bus, so just return
|
||||||
}
|
}
|
||||||
@ -67,18 +70,12 @@ public class EventUtils {
|
|||||||
org.apache.cloudstack.framework.events.Event event =
|
org.apache.cloudstack.framework.events.Event event =
|
||||||
new org.apache.cloudstack.framework.events.Event(ManagementService.Name, eventCategory, eventType, EventTypes.getEntityForEvent(eventType), null);
|
new org.apache.cloudstack.framework.events.Event(ManagementService.Name, eventCategory, eventType, EventTypes.getEntityForEvent(eventType), null);
|
||||||
|
|
||||||
Map<String, String> eventDescription = new HashMap<String, String>();
|
Map<String, String> eventDescription = new HashMap<>();
|
||||||
eventDescription.put("event", eventType);
|
eventDescription.put("event", eventType);
|
||||||
eventDescription.put("status", state.toString());
|
eventDescription.put("status", state.toString());
|
||||||
eventDescription.put("details", details);
|
eventDescription.put("details", details);
|
||||||
event.setDescription(eventDescription);
|
event.setDescription(eventDescription);
|
||||||
try {
|
eventDistributor.publish(event);
|
||||||
s_eventBus.publish(event);
|
|
||||||
} catch (EventBusException evx) {
|
|
||||||
String errMsg = "Failed to publish contrail event.";
|
|
||||||
LOGGER.warn(errMsg, evx);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class EventInterceptor implements ComponentMethodInterceptor, MethodInterceptor {
|
public static class EventInterceptor implements ComponentMethodInterceptor, MethodInterceptor {
|
||||||
@ -119,7 +116,7 @@ public class EventUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected List<ActionEvent> getActionEvents(Method m) {
|
protected List<ActionEvent> getActionEvents(Method m) {
|
||||||
List<ActionEvent> result = new ArrayList<ActionEvent>();
|
List<ActionEvent> result = new ArrayList<>();
|
||||||
|
|
||||||
ActionEvents events = m.getAnnotation(ActionEvents.class);
|
ActionEvents events = m.getAnnotation(ActionEvents.class);
|
||||||
|
|
||||||
|
|||||||
@ -79,6 +79,7 @@
|
|||||||
<module>event-bus/inmemory</module>
|
<module>event-bus/inmemory</module>
|
||||||
<module>event-bus/kafka</module>
|
<module>event-bus/kafka</module>
|
||||||
<module>event-bus/rabbitmq</module>
|
<module>event-bus/rabbitmq</module>
|
||||||
|
<module>event-bus/webhook</module>
|
||||||
|
|
||||||
<module>ha-planners/skip-heurestics</module>
|
<module>ha-planners/skip-heurestics</module>
|
||||||
|
|
||||||
|
|||||||
@ -95,8 +95,7 @@ import org.apache.cloudstack.config.ApiServiceConfiguration;
|
|||||||
import org.apache.cloudstack.context.CallContext;
|
import org.apache.cloudstack.context.CallContext;
|
||||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||||
import org.apache.cloudstack.framework.config.Configurable;
|
import org.apache.cloudstack.framework.config.Configurable;
|
||||||
import org.apache.cloudstack.framework.events.EventBus;
|
import org.apache.cloudstack.framework.events.EventDistributor;
|
||||||
import org.apache.cloudstack.framework.events.EventBusException;
|
|
||||||
import org.apache.cloudstack.framework.jobs.AsyncJob;
|
import org.apache.cloudstack.framework.jobs.AsyncJob;
|
||||||
import org.apache.cloudstack.framework.jobs.AsyncJobManager;
|
import org.apache.cloudstack.framework.jobs.AsyncJobManager;
|
||||||
import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO;
|
import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO;
|
||||||
@ -132,10 +131,9 @@ import org.apache.http.protocol.ResponseConnControl;
|
|||||||
import org.apache.http.protocol.ResponseContent;
|
import org.apache.http.protocol.ResponseContent;
|
||||||
import org.apache.http.protocol.ResponseDate;
|
import org.apache.http.protocol.ResponseDate;
|
||||||
import org.apache.http.protocol.ResponseServer;
|
import org.apache.http.protocol.ResponseServer;
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import com.cloud.api.dispatch.DispatchChainFactory;
|
import com.cloud.api.dispatch.DispatchChainFactory;
|
||||||
@ -196,26 +194,26 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||||||
*/
|
*/
|
||||||
private static final String CONTROL_CHARACTERS = "[\000-\011\013-\014\016-\037\177]";
|
private static final String CONTROL_CHARACTERS = "[\000-\011\013-\014\016-\037\177]";
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private AccountManager accountMgr;
|
||||||
|
@Inject
|
||||||
|
private APIAuthenticationManager authManager;
|
||||||
@Inject
|
@Inject
|
||||||
private ApiDispatcher dispatcher;
|
private ApiDispatcher dispatcher;
|
||||||
@Inject
|
@Inject
|
||||||
private DispatchChainFactory dispatchChainFactory;
|
private AsyncJobManager asyncMgr;
|
||||||
@Inject
|
@Inject
|
||||||
private AccountManager accountMgr;
|
private DispatchChainFactory dispatchChainFactory;
|
||||||
@Inject
|
@Inject
|
||||||
private DomainManager domainMgr;
|
private DomainManager domainMgr;
|
||||||
@Inject
|
@Inject
|
||||||
private DomainDao domainDao;
|
private DomainDao domainDao;
|
||||||
@Inject
|
@Inject
|
||||||
private UUIDManager uuidMgr;
|
|
||||||
@Inject
|
|
||||||
private AsyncJobManager asyncMgr;
|
|
||||||
@Inject
|
|
||||||
private EntityManager entityMgr;
|
private EntityManager entityMgr;
|
||||||
@Inject
|
@Inject
|
||||||
private APIAuthenticationManager authManager;
|
|
||||||
@Inject
|
|
||||||
private ProjectDao projectDao;
|
private ProjectDao projectDao;
|
||||||
|
@Inject
|
||||||
|
private UUIDManager uuidMgr;
|
||||||
|
|
||||||
private List<PluggableService> pluggableServices;
|
private List<PluggableService> pluggableServices;
|
||||||
|
|
||||||
@ -224,6 +222,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||||||
@Inject
|
@Inject
|
||||||
private ApiAsyncJobDispatcher asyncDispatcher;
|
private ApiAsyncJobDispatcher asyncDispatcher;
|
||||||
|
|
||||||
|
private EventDistributor eventDistributor = null;
|
||||||
private static int s_workerCount = 0;
|
private static int s_workerCount = 0;
|
||||||
private static Map<String, List<Class<?>>> s_apiNameCmdClassMap = new HashMap<String, List<Class<?>>>();
|
private static Map<String, List<Class<?>>> s_apiNameCmdClassMap = new HashMap<String, List<Class<?>>>();
|
||||||
|
|
||||||
@ -311,6 +310,10 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setEventDistributor(EventDistributor eventDistributor) {
|
||||||
|
this.eventDistributor = eventDistributor;
|
||||||
|
}
|
||||||
|
|
||||||
@MessageHandler(topic = AsyncJob.Topics.JOB_EVENT_PUBLISH)
|
@MessageHandler(topic = AsyncJob.Topics.JOB_EVENT_PUBLISH)
|
||||||
public void handleAsyncJobPublishEvent(String subject, String senderAddress, Object args) {
|
public void handleAsyncJobPublishEvent(String subject, String senderAddress, Object args) {
|
||||||
assert (args != null);
|
assert (args != null);
|
||||||
@ -322,12 +325,8 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||||||
|
|
||||||
if (logger.isTraceEnabled())
|
if (logger.isTraceEnabled())
|
||||||
logger.trace("Handle asyjob publish event " + jobEvent);
|
logger.trace("Handle asyjob publish event " + jobEvent);
|
||||||
|
if (eventDistributor == null) {
|
||||||
EventBus eventBus = null;
|
setEventDistributor(ComponentContext.getComponent(EventDistributor.class));
|
||||||
try {
|
|
||||||
eventBus = ComponentContext.getComponent(EventBus.class);
|
|
||||||
} catch (NoSuchBeanDefinitionException nbe) {
|
|
||||||
return; // no provider is configured to provide events bus, so just return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!job.getDispatcher().equalsIgnoreCase("ApiAsyncJobDispatcher")) {
|
if (!job.getDispatcher().equalsIgnoreCase("ApiAsyncJobDispatcher")) {
|
||||||
@ -340,7 +339,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||||||
// Get the event type from the cmdInfo json string
|
// Get the event type from the cmdInfo json string
|
||||||
String info = job.getCmdInfo();
|
String info = job.getCmdInfo();
|
||||||
String cmdEventType = "unknown";
|
String cmdEventType = "unknown";
|
||||||
Map<String, Object> cmdInfoObj = new HashMap<String, Object>();
|
Map<String, Object> cmdInfoObj = new HashMap<>();
|
||||||
if (info != null) {
|
if (info != null) {
|
||||||
Type type = new TypeToken<Map<String, String>>(){}.getType();
|
Type type = new TypeToken<Map<String, String>>(){}.getType();
|
||||||
Map<String, String> cmdInfo = ApiGsonHelper.getBuilder().create().fromJson(info, type);
|
Map<String, String> cmdInfo = ApiGsonHelper.getBuilder().create().fromJson(info, type);
|
||||||
@ -368,7 +367,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||||||
org.apache.cloudstack.framework.events.Event event = new org.apache.cloudstack.framework.events.Event("management-server", EventCategory.ASYNC_JOB_CHANGE_EVENT.getName(),
|
org.apache.cloudstack.framework.events.Event event = new org.apache.cloudstack.framework.events.Event("management-server", EventCategory.ASYNC_JOB_CHANGE_EVENT.getName(),
|
||||||
jobEvent, instanceType, instanceUuid);
|
jobEvent, instanceType, instanceUuid);
|
||||||
|
|
||||||
Map<String, Object> eventDescription = new HashMap<String, Object>();
|
Map<String, Object> eventDescription = new HashMap<>();
|
||||||
eventDescription.put("command", job.getCmd());
|
eventDescription.put("command", job.getCmd());
|
||||||
eventDescription.put("user", userJobOwner.getUuid());
|
eventDescription.put("user", userJobOwner.getUuid());
|
||||||
eventDescription.put("account", jobOwner.getUuid());
|
eventDescription.put("account", jobOwner.getUuid());
|
||||||
@ -389,13 +388,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
|||||||
eventDescription.put("domainname", domain.getName());
|
eventDescription.put("domainname", domain.getName());
|
||||||
}
|
}
|
||||||
event.setDescription(eventDescription);
|
event.setDescription(eventDescription);
|
||||||
|
eventDistributor.publish(event);
|
||||||
try {
|
|
||||||
eventBus.publish(event);
|
|
||||||
} catch (EventBusException evx) {
|
|
||||||
String errMsg = "Failed to publish async job event on the event bus.";
|
|
||||||
logger.warn(errMsg, evx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@ -32,12 +32,11 @@ import org.apache.cloudstack.api.Identity;
|
|||||||
import org.apache.cloudstack.api.InternalIdentity;
|
import org.apache.cloudstack.api.InternalIdentity;
|
||||||
import org.apache.cloudstack.context.CallContext;
|
import org.apache.cloudstack.context.CallContext;
|
||||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||||
import org.apache.cloudstack.framework.events.EventBus;
|
import org.apache.cloudstack.framework.events.EventDistributor;
|
||||||
import org.apache.cloudstack.framework.events.EventBusException;
|
|
||||||
import org.apache.commons.lang3.ObjectUtils;
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
|
|
||||||
import com.cloud.configuration.Config;
|
import com.cloud.configuration.Config;
|
||||||
@ -63,7 +62,7 @@ public class ActionEventUtils {
|
|||||||
private static AccountDao s_accountDao;
|
private static AccountDao s_accountDao;
|
||||||
private static ProjectDao s_projectDao;
|
private static ProjectDao s_projectDao;
|
||||||
protected static UserDao s_userDao;
|
protected static UserDao s_userDao;
|
||||||
protected static EventBus s_eventBus = null;
|
private static EventDistributor eventDistributor;
|
||||||
protected static EntityManager s_entityMgr;
|
protected static EntityManager s_entityMgr;
|
||||||
protected static ConfigurationDao s_configDao;
|
protected static ConfigurationDao s_configDao;
|
||||||
|
|
||||||
@ -101,8 +100,10 @@ public class ActionEventUtils {
|
|||||||
|
|
||||||
public static Long onActionEvent(Long userId, Long accountId, Long domainId, String type, String description, Long resourceId, String resourceType) {
|
public static Long onActionEvent(Long userId, Long accountId, Long domainId, String type, String description, Long resourceId, String resourceType) {
|
||||||
Ternary<Long, String, String> resourceDetails = getResourceDetails(resourceId, resourceType, type);
|
Ternary<Long, String, String> resourceDetails = getResourceDetails(resourceId, resourceType, type);
|
||||||
publishOnEventBus(userId, accountId, EventCategory.ACTION_EVENT.getName(), type, com.cloud.event.Event.State.Completed, description, resourceDetails.second(), resourceDetails.third());
|
Event event = persistActionEvent(userId, accountId, domainId, null, type, Event.State.Completed,
|
||||||
Event event = persistActionEvent(userId, accountId, domainId, null, type, Event.State.Completed, true, description, resourceDetails.first(), resourceDetails.third(), null);
|
true, description, resourceDetails.first(), resourceDetails.third(), null);
|
||||||
|
publishOnEventBus(event, userId, accountId, domainId, EventCategory.ACTION_EVENT.getName(), type,
|
||||||
|
com.cloud.event.Event.State.Completed, description, resourceDetails.second(), resourceDetails.third());
|
||||||
return event.getId();
|
return event.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,8 +112,10 @@ public class ActionEventUtils {
|
|||||||
*/
|
*/
|
||||||
public static Long onScheduledActionEvent(Long userId, Long accountId, String type, String description, Long resourceId, String resourceType, boolean eventDisplayEnabled, long startEventId) {
|
public static Long onScheduledActionEvent(Long userId, Long accountId, String type, String description, Long resourceId, String resourceType, boolean eventDisplayEnabled, long startEventId) {
|
||||||
Ternary<Long, String, String> resourceDetails = getResourceDetails(resourceId, resourceType, type);
|
Ternary<Long, String, String> resourceDetails = getResourceDetails(resourceId, resourceType, type);
|
||||||
publishOnEventBus(userId, accountId, EventCategory.ACTION_EVENT.getName(), type, com.cloud.event.Event.State.Scheduled, description, resourceDetails.second(), resourceDetails.third());
|
Event event = persistActionEvent(userId, accountId, null, null, type, Event.State.Scheduled,
|
||||||
Event event = persistActionEvent(userId, accountId, null, null, type, Event.State.Scheduled, eventDisplayEnabled, description, resourceDetails.first(), resourceDetails.third(), startEventId);
|
eventDisplayEnabled, description, resourceDetails.first(), resourceDetails.third(), startEventId);
|
||||||
|
publishOnEventBus(event, userId, accountId, EventCategory.ACTION_EVENT.getName(), type,
|
||||||
|
com.cloud.event.Event.State.Scheduled, description, resourceDetails.second(), resourceDetails.third());
|
||||||
return event.getId();
|
return event.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,8 +139,10 @@ public class ActionEventUtils {
|
|||||||
*/
|
*/
|
||||||
public static Long onStartedActionEvent(Long userId, Long accountId, String type, String description, Long resourceId, String resourceType, boolean eventDisplayEnabled, long startEventId) {
|
public static Long onStartedActionEvent(Long userId, Long accountId, String type, String description, Long resourceId, String resourceType, boolean eventDisplayEnabled, long startEventId) {
|
||||||
Ternary<Long, String, String> resourceDetails = getResourceDetails(resourceId, resourceType, type);
|
Ternary<Long, String, String> resourceDetails = getResourceDetails(resourceId, resourceType, type);
|
||||||
publishOnEventBus(userId, accountId, EventCategory.ACTION_EVENT.getName(), type, com.cloud.event.Event.State.Started, description, resourceDetails.second(), resourceDetails.third());
|
Event event = persistActionEvent(userId, accountId, null, null, type, Event.State.Started,
|
||||||
Event event = persistActionEvent(userId, accountId, null, null, type, Event.State.Started, eventDisplayEnabled, description, resourceDetails.first(), resourceDetails.third(), startEventId);
|
eventDisplayEnabled, description, resourceDetails.first(), resourceDetails.third(), startEventId);
|
||||||
|
publishOnEventBus(event, userId, accountId, EventCategory.ACTION_EVENT.getName(), type,
|
||||||
|
com.cloud.event.Event.State.Started, description, resourceDetails.second(), resourceDetails.third());
|
||||||
return event.getId();
|
return event.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,16 +153,20 @@ public class ActionEventUtils {
|
|||||||
|
|
||||||
public static Long onCompletedActionEvent(Long userId, Long accountId, String level, String type, boolean eventDisplayEnabled, String description, Long resourceId, String resourceType, long startEventId) {
|
public static Long onCompletedActionEvent(Long userId, Long accountId, String level, String type, boolean eventDisplayEnabled, String description, Long resourceId, String resourceType, long startEventId) {
|
||||||
Ternary<Long, String, String> resourceDetails = getResourceDetails(resourceId, resourceType, type);
|
Ternary<Long, String, String> resourceDetails = getResourceDetails(resourceId, resourceType, type);
|
||||||
publishOnEventBus(userId, accountId, EventCategory.ACTION_EVENT.getName(), type, com.cloud.event.Event.State.Completed, description, resourceDetails.second(), resourceDetails.third());
|
Event event = persistActionEvent(userId, accountId, null, level, type, Event.State.Completed,
|
||||||
Event event = persistActionEvent(userId, accountId, null, level, type, Event.State.Completed, eventDisplayEnabled, description, resourceDetails.first(), resourceDetails.third(), startEventId);
|
eventDisplayEnabled, description, resourceDetails.first(), resourceDetails.third(), startEventId);
|
||||||
|
publishOnEventBus(event, userId, accountId, EventCategory.ACTION_EVENT.getName(), type,
|
||||||
|
com.cloud.event.Event.State.Completed, description, resourceDetails.second(), resourceDetails.third());
|
||||||
return event.getId();
|
return event.getId();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Long onCreatedActionEvent(Long userId, Long accountId, String level, String type, boolean eventDisplayEnabled, String description, Long resourceId, String resourceType) {
|
public static Long onCreatedActionEvent(Long userId, Long accountId, String level, String type, boolean eventDisplayEnabled, String description, Long resourceId, String resourceType) {
|
||||||
Ternary<Long, String, String> resourceDetails = getResourceDetails(resourceId, resourceType, type);
|
Ternary<Long, String, String> resourceDetails = getResourceDetails(resourceId, resourceType, type);
|
||||||
publishOnEventBus(userId, accountId, EventCategory.ACTION_EVENT.getName(), type, com.cloud.event.Event.State.Created, description, resourceDetails.second(), resourceDetails.third());
|
Event event = persistActionEvent(userId, accountId, null, level, type, Event.State.Created,
|
||||||
Event event = persistActionEvent(userId, accountId, null, level, type, Event.State.Created, eventDisplayEnabled, description, resourceDetails.first(), resourceDetails.third(), null);
|
eventDisplayEnabled, description, resourceDetails.first(), resourceDetails.third(), null);
|
||||||
|
publishOnEventBus(event, userId, accountId, EventCategory.ACTION_EVENT.getName(), type,
|
||||||
|
com.cloud.event.Event.State.Created, description, resourceDetails.second(), resourceDetails.third());
|
||||||
return event.getId();
|
return event.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,20 +202,25 @@ public class ActionEventUtils {
|
|||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void publishOnEventBus(long userId, long accountId, String eventCategory, String eventType, Event.State state, String description, String resourceUuid, String resourceType) {
|
private static void publishOnEventBus(Event eventRecord, long userId, long accountId, Long domainId,
|
||||||
|
String eventCategory, String eventType, Event.State state, String description, String resourceUuid,
|
||||||
|
String resourceType) {
|
||||||
String configKey = Config.PublishActionEvent.key();
|
String configKey = Config.PublishActionEvent.key();
|
||||||
String value = s_configDao.getValue(configKey);
|
String value = s_configDao.getValue(configKey);
|
||||||
boolean configValue = Boolean.parseBoolean(value);
|
boolean configValue = Boolean.parseBoolean(value);
|
||||||
if(!configValue)
|
if(!configValue)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
s_eventBus = ComponentContext.getComponent(EventBus.class);
|
eventDistributor = ComponentContext.getComponent(EventDistributor.class);
|
||||||
} catch (NoSuchBeanDefinitionException nbe) {
|
} catch (NoSuchBeanDefinitionException nbe) {
|
||||||
return; // no provider is configured to provide events bus, so just return
|
return; // no provider is configured to provide events bus, so just return
|
||||||
}
|
}
|
||||||
|
|
||||||
org.apache.cloudstack.framework.events.Event event =
|
org.apache.cloudstack.framework.events.Event event =
|
||||||
new org.apache.cloudstack.framework.events.Event(ManagementService.Name, eventCategory, eventType, resourceType, resourceUuid);
|
new org.apache.cloudstack.framework.events.Event(ManagementService.Name, eventCategory, eventType, resourceType, resourceUuid);
|
||||||
|
event.setEventId(eventRecord.getId());
|
||||||
|
event.setEventUuid(eventRecord.getUuid());
|
||||||
|
|
||||||
Map<String, String> eventDescription = new HashMap<String, String>();
|
Map<String, String> eventDescription = new HashMap<String, String>();
|
||||||
Project project = s_projectDao.findByProjectAccountId(accountId);
|
Project project = s_projectDao.findByProjectAccountId(accountId);
|
||||||
@ -219,6 +233,9 @@ public class ActionEventUtils {
|
|||||||
return;
|
return;
|
||||||
if (project != null)
|
if (project != null)
|
||||||
eventDescription.put("project", project.getUuid());
|
eventDescription.put("project", project.getUuid());
|
||||||
|
event.setResourceAccountId(accountId);
|
||||||
|
event.setResourceAccountUuid(account.getUuid());
|
||||||
|
event.setResourceDomainId(domainId == null ? account.getDomainId() : domainId);
|
||||||
eventDescription.put("user", user.getUuid());
|
eventDescription.put("user", user.getUuid());
|
||||||
eventDescription.put("account", account.getUuid());
|
eventDescription.put("account", account.getUuid());
|
||||||
eventDescription.put("event", eventType);
|
eventDescription.put("event", eventType);
|
||||||
@ -234,11 +251,13 @@ public class ActionEventUtils {
|
|||||||
|
|
||||||
event.setDescription(eventDescription);
|
event.setDescription(eventDescription);
|
||||||
|
|
||||||
try {
|
eventDistributor.publish(event);
|
||||||
s_eventBus.publish(event);
|
}
|
||||||
} catch (EventBusException e) {
|
|
||||||
LOGGER.warn("Failed to publish action event on the event bus.");
|
private static void publishOnEventBus(Event event, long userId, long accountId, String eventCategory,
|
||||||
}
|
String eventType, Event.State state, String description, String resourceUuid, String resourceType) {
|
||||||
|
publishOnEventBus(event, userId, accountId, null, eventCategory, eventType, state, description,
|
||||||
|
resourceUuid, resourceType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Ternary<Long, String, String> getResourceDetailsUsingEntityClassAndContext(Class<?> entityClass, ApiCommandResourceType resourceType) {
|
private static Ternary<Long, String, String> getResourceDetailsUsingEntityClassAndContext(Class<?> entityClass, ApiCommandResourceType resourceType) {
|
||||||
|
|||||||
@ -25,15 +25,13 @@ import java.util.Map;
|
|||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||||
|
import org.apache.cloudstack.framework.events.EventDistributor;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
|
||||||
import org.apache.cloudstack.framework.events.EventBus;
|
|
||||||
import org.apache.cloudstack.framework.events.EventBusException;
|
|
||||||
|
|
||||||
import com.cloud.configuration.Config;
|
import com.cloud.configuration.Config;
|
||||||
import com.cloud.dc.DataCenterVO;
|
import com.cloud.dc.DataCenterVO;
|
||||||
import com.cloud.dc.HostPodVO;
|
import com.cloud.dc.HostPodVO;
|
||||||
@ -48,8 +46,8 @@ public class AlertGenerator {
|
|||||||
protected static Logger LOGGER = LogManager.getLogger(AlertGenerator.class);
|
protected static Logger LOGGER = LogManager.getLogger(AlertGenerator.class);
|
||||||
private static DataCenterDao s_dcDao;
|
private static DataCenterDao s_dcDao;
|
||||||
private static HostPodDao s_podDao;
|
private static HostPodDao s_podDao;
|
||||||
protected static EventBus s_eventBus = null;
|
|
||||||
protected static ConfigurationDao s_configDao;
|
protected static ConfigurationDao s_configDao;
|
||||||
|
protected static EventDistributor eventDistributor;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
DataCenterDao dcDao;
|
DataCenterDao dcDao;
|
||||||
@ -76,9 +74,9 @@ public class AlertGenerator {
|
|||||||
if(!configValue)
|
if(!configValue)
|
||||||
return;
|
return;
|
||||||
try {
|
try {
|
||||||
s_eventBus = ComponentContext.getComponent(EventBus.class);
|
eventDistributor = ComponentContext.getComponent(EventDistributor.class);
|
||||||
} catch (NoSuchBeanDefinitionException nbe) {
|
} catch (NoSuchBeanDefinitionException nbe) {
|
||||||
return; // no provider is configured to provide events bus, so just return
|
return; // no provider is configured to provide events distributor, so just return
|
||||||
}
|
}
|
||||||
|
|
||||||
org.apache.cloudstack.framework.events.Event event =
|
org.apache.cloudstack.framework.events.Event event =
|
||||||
@ -107,10 +105,6 @@ public class AlertGenerator {
|
|||||||
|
|
||||||
event.setDescription(eventDescription);
|
event.setDescription(eventDescription);
|
||||||
|
|
||||||
try {
|
eventDistributor.publish(event);
|
||||||
s_eventBus.publish(event);
|
|
||||||
} catch (EventBusException e) {
|
|
||||||
LOGGER.warn("Failed to publish alert on the event bus.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,7 @@ package com.cloud.projects;
|
|||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -35,6 +36,7 @@ import javax.inject.Inject;
|
|||||||
import javax.mail.MessagingException;
|
import javax.mail.MessagingException;
|
||||||
import javax.naming.ConfigurationException;
|
import javax.naming.ConfigurationException;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.acl.ControlledEntity;
|
||||||
import org.apache.cloudstack.acl.ProjectRole;
|
import org.apache.cloudstack.acl.ProjectRole;
|
||||||
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
||||||
import org.apache.cloudstack.acl.dao.ProjectRoleDao;
|
import org.apache.cloudstack.acl.dao.ProjectRoleDao;
|
||||||
@ -48,7 +50,9 @@ import org.apache.cloudstack.managed.context.ManagedContextRunnable;
|
|||||||
import org.apache.cloudstack.utils.mailing.MailAddress;
|
import org.apache.cloudstack.utils.mailing.MailAddress;
|
||||||
import org.apache.cloudstack.utils.mailing.SMTPMailProperties;
|
import org.apache.cloudstack.utils.mailing.SMTPMailProperties;
|
||||||
import org.apache.cloudstack.utils.mailing.SMTPMailSender;
|
import org.apache.cloudstack.utils.mailing.SMTPMailSender;
|
||||||
|
import org.apache.cloudstack.webhook.WebhookHelper;
|
||||||
import org.apache.commons.lang3.BooleanUtils;
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import com.cloud.api.ApiDBUtils;
|
import com.cloud.api.ApiDBUtils;
|
||||||
@ -89,6 +93,7 @@ import com.cloud.user.ResourceLimitService;
|
|||||||
import com.cloud.user.User;
|
import com.cloud.user.User;
|
||||||
import com.cloud.user.dao.AccountDao;
|
import com.cloud.user.dao.AccountDao;
|
||||||
import com.cloud.user.dao.UserDao;
|
import com.cloud.user.dao.UserDao;
|
||||||
|
import com.cloud.utils.component.ComponentContext;
|
||||||
import com.cloud.utils.component.ManagerBase;
|
import com.cloud.utils.component.ManagerBase;
|
||||||
import com.cloud.utils.concurrency.NamedThreadFactory;
|
import com.cloud.utils.concurrency.NamedThreadFactory;
|
||||||
import com.cloud.utils.db.DB;
|
import com.cloud.utils.db.DB;
|
||||||
@ -163,6 +168,17 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C
|
|||||||
private String senderAddress;
|
private String senderAddress;
|
||||||
protected SMTPMailSender mailSender;
|
protected SMTPMailSender mailSender;
|
||||||
|
|
||||||
|
protected List<? extends ControlledEntity> listWebhooksForProject(Project project) {
|
||||||
|
List<? extends ControlledEntity> webhooks = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
WebhookHelper webhookService = ComponentContext.getDelegateComponentOfType(WebhookHelper.class);
|
||||||
|
webhooks = webhookService.listWebhooksByAccount(project.getProjectAccountId());
|
||||||
|
} catch (NoSuchBeanDefinitionException ignored) {
|
||||||
|
logger.debug("No WebhookHelper bean found");
|
||||||
|
}
|
||||||
|
return webhooks;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException {
|
public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException {
|
||||||
|
|
||||||
@ -339,8 +355,9 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C
|
|||||||
List<VolumeVO> volumes = _volumeDao.findDetachedByAccount(project.getProjectAccountId());
|
List<VolumeVO> volumes = _volumeDao.findDetachedByAccount(project.getProjectAccountId());
|
||||||
List<NetworkVO> networks = _networkDao.listByOwner(project.getProjectAccountId());
|
List<NetworkVO> networks = _networkDao.listByOwner(project.getProjectAccountId());
|
||||||
List<? extends Vpc> vpcs = _vpcMgr.getVpcsForAccount(project.getProjectAccountId());
|
List<? extends Vpc> vpcs = _vpcMgr.getVpcsForAccount(project.getProjectAccountId());
|
||||||
|
List<? extends ControlledEntity> webhooks = listWebhooksForProject(project);
|
||||||
|
|
||||||
Optional<String> message = Stream.of(userTemplates, vmSnapshots, vms, volumes, networks, vpcs)
|
Optional<String> message = Stream.of(userTemplates, vmSnapshots, vms, volumes, networks, vpcs, webhooks)
|
||||||
.filter(entity -> !entity.isEmpty())
|
.filter(entity -> !entity.isEmpty())
|
||||||
.map(entity -> entity.size() + " " + entity.get(0).getEntityType().getSimpleName() + " to clean up")
|
.map(entity -> entity.size() + " " + entity.get(0).getEntityType().getSimpleName() + " to clean up")
|
||||||
.findFirst();
|
.findFirst();
|
||||||
|
|||||||
@ -26,11 +26,9 @@ import javax.annotation.PostConstruct;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||||
import org.apache.cloudstack.framework.events.EventBus;
|
import org.apache.cloudstack.framework.events.EventDistributor;
|
||||||
import org.apache.cloudstack.framework.events.EventBusException;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import com.cloud.configuration.Config;
|
import com.cloud.configuration.Config;
|
||||||
@ -47,12 +45,12 @@ import com.cloud.utils.fsm.StateMachine2;
|
|||||||
@Component
|
@Component
|
||||||
public class SnapshotStateListener implements StateListener<State, Event, SnapshotVO> {
|
public class SnapshotStateListener implements StateListener<State, Event, SnapshotVO> {
|
||||||
|
|
||||||
protected static EventBus s_eventBus = null;
|
|
||||||
protected static ConfigurationDao s_configDao;
|
protected static ConfigurationDao s_configDao;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private ConfigurationDao configDao;
|
private ConfigurationDao configDao;
|
||||||
|
|
||||||
|
private EventDistributor eventDistributor = null;
|
||||||
protected Logger logger = LogManager.getLogger(getClass());
|
protected Logger logger = LogManager.getLogger(getClass());
|
||||||
|
|
||||||
public SnapshotStateListener() {
|
public SnapshotStateListener() {
|
||||||
@ -64,6 +62,10 @@ public class SnapshotStateListener implements StateListener<State, Event, Snapsh
|
|||||||
s_configDao = configDao;
|
s_configDao = configDao;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setEventDistributor(EventDistributor eventDistributor) {
|
||||||
|
this.eventDistributor = eventDistributor;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean preStateTransitionEvent(State oldState, Event event, State newState, SnapshotVO vo, boolean status, Object opaque) {
|
public boolean preStateTransitionEvent(State oldState, Event event, State newState, SnapshotVO vo, boolean status, Object opaque) {
|
||||||
pubishOnEventBus(event.name(), "preStateTransitionEvent", vo, oldState, newState);
|
pubishOnEventBus(event.name(), "preStateTransitionEvent", vo, oldState, newState);
|
||||||
@ -84,17 +86,15 @@ public class SnapshotStateListener implements StateListener<State, Event, Snapsh
|
|||||||
if(!configValue) {
|
if(!configValue) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
if (eventDistributor == null) {
|
||||||
s_eventBus = ComponentContext.getComponent(EventBus.class);
|
setEventDistributor(ComponentContext.getComponent(EventDistributor.class));
|
||||||
} catch (NoSuchBeanDefinitionException nbe) {
|
|
||||||
return; // no provider is configured to provide events bus, so just return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String resourceName = getEntityFromClassName(Snapshot.class.getName());
|
String resourceName = getEntityFromClassName(Snapshot.class.getName());
|
||||||
org.apache.cloudstack.framework.events.Event eventMsg =
|
org.apache.cloudstack.framework.events.Event eventMsg =
|
||||||
new org.apache.cloudstack.framework.events.Event(ManagementService.Name, EventCategory.RESOURCE_STATE_CHANGE_EVENT.getName(), event, resourceName,
|
new org.apache.cloudstack.framework.events.Event(ManagementService.Name, EventCategory.RESOURCE_STATE_CHANGE_EVENT.getName(), event, resourceName,
|
||||||
vo.getUuid());
|
vo.getUuid());
|
||||||
Map<String, String> eventDescription = new HashMap<String, String>();
|
Map<String, String> eventDescription = new HashMap<>();
|
||||||
eventDescription.put("resource", resourceName);
|
eventDescription.put("resource", resourceName);
|
||||||
eventDescription.put("id", vo.getUuid());
|
eventDescription.put("id", vo.getUuid());
|
||||||
eventDescription.put("old-state", oldState.name());
|
eventDescription.put("old-state", oldState.name());
|
||||||
@ -104,11 +104,7 @@ public class SnapshotStateListener implements StateListener<State, Event, Snapsh
|
|||||||
eventDescription.put("eventDateTime", eventDate);
|
eventDescription.put("eventDateTime", eventDate);
|
||||||
|
|
||||||
eventMsg.setDescription(eventDescription);
|
eventMsg.setDescription(eventDescription);
|
||||||
try {
|
eventDistributor.publish(eventMsg);
|
||||||
s_eventBus.publish(eventMsg);
|
|
||||||
} catch (EventBusException e) {
|
|
||||||
logger.warn("Failed to publish state change event on the event bus.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getEntityFromClassName(String entityClassName) {
|
private String getEntityFromClassName(String entityClassName) {
|
||||||
|
|||||||
@ -22,35 +22,32 @@ import java.util.Date;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.cloud.event.EventTypes;
|
|
||||||
import com.cloud.event.UsageEventUtils;
|
|
||||||
import com.cloud.utils.fsm.StateMachine2;
|
|
||||||
import com.cloud.vm.VMInstanceVO;
|
|
||||||
import com.cloud.vm.VirtualMachine;
|
|
||||||
import com.cloud.vm.dao.VMInstanceDao;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
|
||||||
|
|
||||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||||
import org.apache.cloudstack.framework.events.EventBus;
|
import org.apache.cloudstack.framework.events.EventDistributor;
|
||||||
import org.apache.cloudstack.framework.events.EventBusException;
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import com.cloud.configuration.Config;
|
import com.cloud.configuration.Config;
|
||||||
import com.cloud.event.EventCategory;
|
import com.cloud.event.EventCategory;
|
||||||
|
import com.cloud.event.EventTypes;
|
||||||
|
import com.cloud.event.UsageEventUtils;
|
||||||
import com.cloud.server.ManagementService;
|
import com.cloud.server.ManagementService;
|
||||||
import com.cloud.storage.Volume;
|
import com.cloud.storage.Volume;
|
||||||
import com.cloud.storage.Volume.Event;
|
import com.cloud.storage.Volume.Event;
|
||||||
import com.cloud.storage.Volume.State;
|
import com.cloud.storage.Volume.State;
|
||||||
import com.cloud.utils.component.ComponentContext;
|
import com.cloud.utils.component.ComponentContext;
|
||||||
import com.cloud.utils.fsm.StateListener;
|
import com.cloud.utils.fsm.StateListener;
|
||||||
|
import com.cloud.utils.fsm.StateMachine2;
|
||||||
|
import com.cloud.vm.VMInstanceVO;
|
||||||
|
import com.cloud.vm.VirtualMachine;
|
||||||
|
import com.cloud.vm.dao.VMInstanceDao;
|
||||||
|
|
||||||
public class VolumeStateListener implements StateListener<State, Event, Volume> {
|
public class VolumeStateListener implements StateListener<State, Event, Volume> {
|
||||||
|
|
||||||
protected static EventBus s_eventBus = null;
|
|
||||||
protected ConfigurationDao _configDao;
|
protected ConfigurationDao _configDao;
|
||||||
protected VMInstanceDao _vmInstanceDao;
|
protected VMInstanceDao _vmInstanceDao;
|
||||||
|
|
||||||
|
private EventDistributor eventDistributor;
|
||||||
protected Logger logger = LogManager.getLogger(getClass());
|
protected Logger logger = LogManager.getLogger(getClass());
|
||||||
|
|
||||||
public VolumeStateListener(ConfigurationDao configDao, VMInstanceDao vmInstanceDao) {
|
public VolumeStateListener(ConfigurationDao configDao, VMInstanceDao vmInstanceDao) {
|
||||||
@ -58,6 +55,10 @@ public class VolumeStateListener implements StateListener<State, Event, Volume>
|
|||||||
this._vmInstanceDao = vmInstanceDao;
|
this._vmInstanceDao = vmInstanceDao;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setEventDistributor(EventDistributor eventDistributor) {
|
||||||
|
this.eventDistributor = eventDistributor;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean preStateTransitionEvent(State oldState, Event event, State newState, Volume vo, boolean status, Object opaque) {
|
public boolean preStateTransitionEvent(State oldState, Event event, State newState, Volume vo, boolean status, Object opaque) {
|
||||||
pubishOnEventBus(event.name(), "preStateTransitionEvent", vo, oldState, newState);
|
pubishOnEventBus(event.name(), "preStateTransitionEvent", vo, oldState, newState);
|
||||||
@ -93,23 +94,21 @@ public class VolumeStateListener implements StateListener<State, Event, Volume>
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pubishOnEventBus(String event, String status, Volume vo, State oldState, State newState) {
|
private void pubishOnEventBus(String event, String status, Volume vo, State oldState, State newState) {
|
||||||
|
|
||||||
String configKey = Config.PublishResourceStateEvent.key();
|
String configKey = Config.PublishResourceStateEvent.key();
|
||||||
String value = _configDao.getValue(configKey);
|
String value = _configDao.getValue(configKey);
|
||||||
boolean configValue = Boolean.parseBoolean(value);
|
boolean configValue = Boolean.parseBoolean(value);
|
||||||
if(!configValue)
|
if(!configValue)
|
||||||
return;
|
return;
|
||||||
try {
|
if (eventDistributor == null) {
|
||||||
s_eventBus = ComponentContext.getComponent(EventBus.class);
|
setEventDistributor(ComponentContext.getComponent(EventDistributor.class));
|
||||||
} catch (NoSuchBeanDefinitionException nbe) {
|
|
||||||
return; // no provider is configured to provide events bus, so just return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String resourceName = getEntityFromClassName(Volume.class.getName());
|
String resourceName = getEntityFromClassName(Volume.class.getName());
|
||||||
org.apache.cloudstack.framework.events.Event eventMsg =
|
org.apache.cloudstack.framework.events.Event eventMsg =
|
||||||
new org.apache.cloudstack.framework.events.Event(ManagementService.Name, EventCategory.RESOURCE_STATE_CHANGE_EVENT.getName(), event, resourceName,
|
new org.apache.cloudstack.framework.events.Event(ManagementService.Name, EventCategory.RESOURCE_STATE_CHANGE_EVENT.getName(), event, resourceName,
|
||||||
vo.getUuid());
|
vo.getUuid());
|
||||||
Map<String, String> eventDescription = new HashMap<String, String>();
|
Map<String, String> eventDescription = new HashMap<String, String>();
|
||||||
eventDescription.put("resource", resourceName);
|
eventDescription.put("resource", resourceName);
|
||||||
eventDescription.put("id", vo.getUuid());
|
eventDescription.put("id", vo.getUuid());
|
||||||
@ -120,11 +119,7 @@ public class VolumeStateListener implements StateListener<State, Event, Volume>
|
|||||||
eventDescription.put("eventDateTime", eventDate);
|
eventDescription.put("eventDateTime", eventDate);
|
||||||
|
|
||||||
eventMsg.setDescription(eventDescription);
|
eventMsg.setDescription(eventDescription);
|
||||||
try {
|
eventDistributor.publish(eventMsg);
|
||||||
s_eventBus.publish(eventMsg);
|
|
||||||
} catch (EventBusException e) {
|
|
||||||
logger.warn("Failed to state change event on the event bus.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getEntityFromClassName(String entityClassName) {
|
private String getEntityFromClassName(String entityClassName) {
|
||||||
|
|||||||
@ -77,11 +77,13 @@ import org.apache.cloudstack.region.gslb.GlobalLoadBalancerRuleDao;
|
|||||||
import org.apache.cloudstack.resourcedetail.UserDetailVO;
|
import org.apache.cloudstack.resourcedetail.UserDetailVO;
|
||||||
import org.apache.cloudstack.resourcedetail.dao.UserDetailsDao;
|
import org.apache.cloudstack.resourcedetail.dao.UserDetailsDao;
|
||||||
import org.apache.cloudstack.utils.baremetal.BaremetalUtils;
|
import org.apache.cloudstack.utils.baremetal.BaremetalUtils;
|
||||||
|
import org.apache.cloudstack.webhook.WebhookHelper;
|
||||||
import org.apache.commons.codec.binary.Base64;
|
import org.apache.commons.codec.binary.Base64;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.commons.lang3.BooleanUtils;
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
|
|
||||||
import com.cloud.api.ApiDBUtils;
|
import com.cloud.api.ApiDBUtils;
|
||||||
import com.cloud.api.auth.SetupUserTwoFactorAuthenticationCmd;
|
import com.cloud.api.auth.SetupUserTwoFactorAuthenticationCmd;
|
||||||
@ -168,6 +170,7 @@ import com.cloud.utils.ConstantTimeComparator;
|
|||||||
import com.cloud.utils.NumbersUtil;
|
import com.cloud.utils.NumbersUtil;
|
||||||
import com.cloud.utils.Pair;
|
import com.cloud.utils.Pair;
|
||||||
import com.cloud.utils.Ternary;
|
import com.cloud.utils.Ternary;
|
||||||
|
import com.cloud.utils.component.ComponentContext;
|
||||||
import com.cloud.utils.component.Manager;
|
import com.cloud.utils.component.Manager;
|
||||||
import com.cloud.utils.component.ManagerBase;
|
import com.cloud.utils.component.ManagerBase;
|
||||||
import com.cloud.utils.component.PluggableService;
|
import com.cloud.utils.component.PluggableService;
|
||||||
@ -426,6 +429,15 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
_querySelectors = querySelectors;
|
_querySelectors = querySelectors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void deleteWebhooksForAccount(long accountId) {
|
||||||
|
try {
|
||||||
|
WebhookHelper webhookService = ComponentContext.getDelegateComponentOfType(WebhookHelper.class);
|
||||||
|
webhookService.deleteWebhooksForAccount(accountId);
|
||||||
|
} catch (NoSuchBeanDefinitionException ignored) {
|
||||||
|
logger.debug("No WebhookHelper bean found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getApiNameList() {
|
public List<String> getApiNameList() {
|
||||||
return apiNameList;
|
return apiNameList;
|
||||||
@ -1105,6 +1117,9 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
// Delete registered UserData
|
// Delete registered UserData
|
||||||
userDataDao.removeByAccountId(accountId);
|
userDataDao.removeByAccountId(accountId);
|
||||||
|
|
||||||
|
// Delete Webhooks
|
||||||
|
deleteWebhooksForAccount(accountId);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
logger.warn("Failed to cleanup account " + account + " due to ", ex);
|
logger.warn("Failed to cleanup account " + account + " due to ", ex);
|
||||||
|
|||||||
@ -24,15 +24,11 @@ import java.util.Map;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import com.cloud.server.ManagementService;
|
|
||||||
import com.cloud.utils.fsm.StateMachine2;
|
|
||||||
import com.cloud.vm.dao.UserVmDao;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
|
||||||
|
|
||||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||||
import org.apache.cloudstack.framework.events.EventBus;
|
import org.apache.cloudstack.framework.events.EventDistributor;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
|
|
||||||
import com.cloud.configuration.Config;
|
import com.cloud.configuration.Config;
|
||||||
import com.cloud.event.EventCategory;
|
import com.cloud.event.EventCategory;
|
||||||
@ -41,12 +37,15 @@ import com.cloud.event.UsageEventUtils;
|
|||||||
import com.cloud.event.dao.UsageEventDao;
|
import com.cloud.event.dao.UsageEventDao;
|
||||||
import com.cloud.network.dao.NetworkDao;
|
import com.cloud.network.dao.NetworkDao;
|
||||||
import com.cloud.network.dao.NetworkVO;
|
import com.cloud.network.dao.NetworkVO;
|
||||||
|
import com.cloud.server.ManagementService;
|
||||||
import com.cloud.service.dao.ServiceOfferingDao;
|
import com.cloud.service.dao.ServiceOfferingDao;
|
||||||
import com.cloud.utils.component.ComponentContext;
|
import com.cloud.utils.component.ComponentContext;
|
||||||
import com.cloud.utils.fsm.StateListener;
|
import com.cloud.utils.fsm.StateListener;
|
||||||
|
import com.cloud.utils.fsm.StateMachine2;
|
||||||
import com.cloud.vm.VirtualMachine.Event;
|
import com.cloud.vm.VirtualMachine.Event;
|
||||||
import com.cloud.vm.VirtualMachine.State;
|
import com.cloud.vm.VirtualMachine.State;
|
||||||
import com.cloud.vm.dao.NicDao;
|
import com.cloud.vm.dao.NicDao;
|
||||||
|
import com.cloud.vm.dao.UserVmDao;
|
||||||
|
|
||||||
public class UserVmStateListener implements StateListener<State, VirtualMachine.Event, VirtualMachine> {
|
public class UserVmStateListener implements StateListener<State, VirtualMachine.Event, VirtualMachine> {
|
||||||
|
|
||||||
@ -57,10 +56,9 @@ public class UserVmStateListener implements StateListener<State, VirtualMachine.
|
|||||||
@Inject protected UserVmDao _userVmDao;
|
@Inject protected UserVmDao _userVmDao;
|
||||||
@Inject protected UserVmManager _userVmMgr;
|
@Inject protected UserVmManager _userVmMgr;
|
||||||
@Inject protected ConfigurationDao _configDao;
|
@Inject protected ConfigurationDao _configDao;
|
||||||
|
private EventDistributor eventDistributor;
|
||||||
protected Logger logger = LogManager.getLogger(getClass());
|
protected Logger logger = LogManager.getLogger(getClass());
|
||||||
|
|
||||||
protected static EventBus s_eventBus = null;
|
|
||||||
|
|
||||||
public UserVmStateListener(UsageEventDao usageEventDao, NetworkDao networkDao, NicDao nicDao, ServiceOfferingDao offeringDao, UserVmDao userVmDao, UserVmManager userVmMgr,
|
public UserVmStateListener(UsageEventDao usageEventDao, NetworkDao networkDao, NicDao nicDao, ServiceOfferingDao offeringDao, UserVmDao userVmDao, UserVmManager userVmMgr,
|
||||||
ConfigurationDao configDao) {
|
ConfigurationDao configDao) {
|
||||||
this._usageEventDao = usageEventDao;
|
this._usageEventDao = usageEventDao;
|
||||||
@ -130,16 +128,16 @@ public class UserVmStateListener implements StateListener<State, VirtualMachine.
|
|||||||
if(!configValue)
|
if(!configValue)
|
||||||
return;
|
return;
|
||||||
try {
|
try {
|
||||||
s_eventBus = ComponentContext.getComponent(EventBus.class);
|
eventDistributor = ComponentContext.getComponent(EventDistributor.class);
|
||||||
} catch (NoSuchBeanDefinitionException nbe) {
|
} catch (NoSuchBeanDefinitionException nbe) {
|
||||||
return; // no provider is configured to provide events bus, so just return
|
return; // no provider is configured to provide events distributor, so just return
|
||||||
}
|
}
|
||||||
|
|
||||||
String resourceName = getEntityFromClassName(VirtualMachine.class.getName());
|
String resourceName = getEntityFromClassName(VirtualMachine.class.getName());
|
||||||
org.apache.cloudstack.framework.events.Event eventMsg =
|
org.apache.cloudstack.framework.events.Event eventMsg =
|
||||||
new org.apache.cloudstack.framework.events.Event(ManagementService.Name, EventCategory.RESOURCE_STATE_CHANGE_EVENT.getName(), event, resourceName,
|
new org.apache.cloudstack.framework.events.Event(ManagementService.Name, EventCategory.RESOURCE_STATE_CHANGE_EVENT.getName(), event, resourceName,
|
||||||
vo.getUuid());
|
vo.getUuid());
|
||||||
Map<String, String> eventDescription = new HashMap<String, String>();
|
Map<String, String> eventDescription = new HashMap<>();
|
||||||
eventDescription.put("resource", resourceName);
|
eventDescription.put("resource", resourceName);
|
||||||
eventDescription.put("id", vo.getUuid());
|
eventDescription.put("id", vo.getUuid());
|
||||||
eventDescription.put("old-state", oldState.name());
|
eventDescription.put("old-state", oldState.name());
|
||||||
@ -150,12 +148,7 @@ public class UserVmStateListener implements StateListener<State, VirtualMachine.
|
|||||||
eventDescription.put("eventDateTime", eventDate);
|
eventDescription.put("eventDateTime", eventDate);
|
||||||
|
|
||||||
eventMsg.setDescription(eventDescription);
|
eventMsg.setDescription(eventDescription);
|
||||||
try {
|
eventDistributor.publish(eventMsg);
|
||||||
s_eventBus.publish(eventMsg);
|
|
||||||
} catch (org.apache.cloudstack.framework.events.EventBusException e) {
|
|
||||||
logger.warn("Failed to publish state change event on the event bus.");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getEntityFromClassName(String entityClassName) {
|
private String getEntityFromClassName(String entityClassName) {
|
||||||
|
|||||||
@ -0,0 +1,28 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
package org.apache.cloudstack.webhook;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.acl.ControlledEntity;
|
||||||
|
|
||||||
|
public interface WebhookHelper {
|
||||||
|
void deleteWebhooksForAccount(long accountId);
|
||||||
|
|
||||||
|
List<? extends ControlledEntity> listWebhooksByAccount(long accountId);
|
||||||
|
}
|
||||||
@ -145,6 +145,10 @@
|
|||||||
<property name="staticNatElements" value="#{staticNatServiceProvidersRegistry.registered}" />
|
<property name="staticNatElements" value="#{staticNatServiceProvidersRegistry.registered}" />
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<bean id="eventDistributor" class="org.apache.cloudstack.framework.events.EventDistributorImpl" >
|
||||||
|
<property name="eventBuses" value="#{eventBusRegistry.registered}" />
|
||||||
|
</bean>
|
||||||
|
|
||||||
<bean id="hypervisorGuruManagerImpl" class="com.cloud.hypervisor.HypervisorGuruManagerImpl" >
|
<bean id="hypervisorGuruManagerImpl" class="com.cloud.hypervisor.HypervisorGuruManagerImpl" >
|
||||||
<property name="hvGuruList" value="#{hypervisorGurusRegistry.registered}" />
|
<property name="hvGuruList" value="#{hypervisorGurusRegistry.registered}" />
|
||||||
</bean>
|
</bean>
|
||||||
|
|||||||
@ -29,7 +29,8 @@ import org.apache.cloudstack.api.ApiCommandResourceType;
|
|||||||
import org.apache.cloudstack.context.CallContext;
|
import org.apache.cloudstack.context.CallContext;
|
||||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||||
import org.apache.cloudstack.framework.events.Event;
|
import org.apache.cloudstack.framework.events.Event;
|
||||||
import org.apache.cloudstack.framework.events.EventBus;
|
import org.apache.cloudstack.framework.events.EventBusException;
|
||||||
|
import org.apache.cloudstack.framework.events.EventDistributor;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@ -97,7 +98,7 @@ public class ActionEventUtilsTest {
|
|||||||
protected ConfigurationDao configDao;
|
protected ConfigurationDao configDao;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
protected EventBus eventBus;
|
protected EventDistributor eventDistributor;
|
||||||
|
|
||||||
private AccountVO account;
|
private AccountVO account;
|
||||||
private UserVO user;
|
private UserVO user;
|
||||||
@ -149,7 +150,7 @@ public class ActionEventUtilsTest {
|
|||||||
//Some basic mocks.
|
//Some basic mocks.
|
||||||
Mockito.when(configDao.getValue(Config.PublishActionEvent.key())).thenReturn("true");
|
Mockito.when(configDao.getValue(Config.PublishActionEvent.key())).thenReturn("true");
|
||||||
componentContextMocked = Mockito.mockStatic(ComponentContext.class);
|
componentContextMocked = Mockito.mockStatic(ComponentContext.class);
|
||||||
componentContextMocked.when(() -> ComponentContext.getComponent(EventBus.class)).thenReturn(eventBus);
|
componentContextMocked.when(() -> ComponentContext.getComponent(EventDistributor.class)).thenReturn(eventDistributor);
|
||||||
|
|
||||||
//Needed for persist to actually set an ID that can be returned from the ActionEventUtils
|
//Needed for persist to actually set an ID that can be returned from the ActionEventUtils
|
||||||
//methods.
|
//methods.
|
||||||
@ -166,14 +167,11 @@ public class ActionEventUtilsTest {
|
|||||||
});
|
});
|
||||||
|
|
||||||
//Needed to record events published on the bus.
|
//Needed to record events published on the bus.
|
||||||
Mockito.doAnswer(new Answer<Void>() {
|
Mockito.doAnswer((Answer<Map<String, EventBusException>>) invocation -> {
|
||||||
@Override public Void answer(InvocationOnMock invocation) throws Throwable {
|
Event event = (Event)invocation.getArguments()[0];
|
||||||
Event event = (Event)invocation.getArguments()[0];
|
publishedEvents.add(event);
|
||||||
publishedEvents.add(event);
|
return new HashMap<>();
|
||||||
return null;
|
}).when(eventDistributor).publish(Mockito.any(Event.class));
|
||||||
}
|
|
||||||
|
|
||||||
}).when(eventBus).publish(Mockito.any(Event.class));
|
|
||||||
|
|
||||||
account = new AccountVO("testaccount", 1L, "networkdomain", Account.Type.NORMAL, "uuid");
|
account = new AccountVO("testaccount", 1L, "networkdomain", Account.Type.NORMAL, "uuid");
|
||||||
account.setId(ACCOUNT_ID);
|
account.setId(ACCOUNT_ID);
|
||||||
|
|||||||
@ -16,20 +16,27 @@
|
|||||||
// under the License.
|
// under the License.
|
||||||
package com.cloud.projects;
|
package com.cloud.projects;
|
||||||
|
|
||||||
import com.cloud.projects.dao.ProjectDao;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.acl.ControlledEntity;
|
||||||
|
import org.apache.cloudstack.webhook.WebhookHelper;
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockedStatic;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import org.mockito.Spy;
|
import org.mockito.Spy;
|
||||||
import org.mockito.junit.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import com.cloud.projects.dao.ProjectDao;
|
||||||
import java.util.List;
|
import com.cloud.utils.component.ComponentContext;
|
||||||
|
|
||||||
|
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
@ -94,4 +101,31 @@ public class ProjectManagerImplTest {
|
|||||||
public void testUpdateProjectNameAndDisplayTextUpdateNameDisplayText() {
|
public void testUpdateProjectNameAndDisplayTextUpdateNameDisplayText() {
|
||||||
runUpdateProjectNameAndDisplayTextTest(true, true);
|
runUpdateProjectNameAndDisplayTextTest(true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeleteWebhooksForAccount() {
|
||||||
|
try (MockedStatic<ComponentContext> mockedComponentContext = Mockito.mockStatic(ComponentContext.class)) {
|
||||||
|
WebhookHelper webhookHelper = Mockito.mock(WebhookHelper.class);
|
||||||
|
List<ControlledEntity> webhooks = List.of(Mockito.mock(ControlledEntity.class),
|
||||||
|
Mockito.mock(ControlledEntity.class));
|
||||||
|
Mockito.doReturn(webhooks).when(webhookHelper).listWebhooksByAccount(Mockito.anyLong());
|
||||||
|
mockedComponentContext.when(() -> ComponentContext.getDelegateComponentOfType(WebhookHelper.class))
|
||||||
|
.thenReturn(webhookHelper);
|
||||||
|
Project project = Mockito.mock(Project.class);
|
||||||
|
Mockito.when(project.getProjectAccountId()).thenReturn(1L);
|
||||||
|
List<? extends ControlledEntity> result = projectManager.listWebhooksForProject(project);
|
||||||
|
Assert.assertEquals(2, result.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeleteWebhooksForAccountNoBean() {
|
||||||
|
try (MockedStatic<ComponentContext> mockedComponentContext = Mockito.mockStatic(ComponentContext.class)) {
|
||||||
|
mockedComponentContext.when(() -> ComponentContext.getDelegateComponentOfType(WebhookHelper.class))
|
||||||
|
.thenThrow(NoSuchBeanDefinitionException.class);
|
||||||
|
List<? extends ControlledEntity> result =
|
||||||
|
projectManager.listWebhooksForProject(Mockito.mock(Project.class));
|
||||||
|
Assert.assertTrue(CollectionUtils.isEmpty(result));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,25 +18,26 @@
|
|||||||
|
|
||||||
package com.cloud.template;
|
package com.cloud.template;
|
||||||
|
|
||||||
import com.cloud.dc.DataCenterVO;
|
import static org.mockito.Mockito.any;
|
||||||
import com.cloud.dc.dao.DataCenterDao;
|
import static org.mockito.Mockito.anyLong;
|
||||||
import com.cloud.event.EventTypes;
|
import static org.mockito.Mockito.doAnswer;
|
||||||
import com.cloud.event.UsageEventUtils;
|
import static org.mockito.Mockito.eq;
|
||||||
import com.cloud.event.UsageEventVO;
|
import static org.mockito.Mockito.mock;
|
||||||
import com.cloud.event.dao.UsageEventDao;
|
import static org.mockito.Mockito.times;
|
||||||
import com.cloud.exception.InvalidParameterValueException;
|
import static org.mockito.Mockito.when;
|
||||||
import com.cloud.org.Grouping;
|
|
||||||
import com.cloud.server.StatsCollector;
|
import java.lang.reflect.Field;
|
||||||
import com.cloud.storage.Storage.ImageFormat;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import com.cloud.storage.TemplateProfile;
|
import java.lang.reflect.Method;
|
||||||
import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
|
import java.util.ArrayList;
|
||||||
import com.cloud.storage.VMTemplateVO;
|
import java.util.Collections;
|
||||||
import com.cloud.storage.dao.VMTemplateZoneDao;
|
import java.util.HashMap;
|
||||||
import com.cloud.user.AccountVO;
|
import java.util.HashSet;
|
||||||
import com.cloud.user.ResourceLimitService;
|
import java.util.List;
|
||||||
import com.cloud.user.dao.AccountDao;
|
import java.util.Map;
|
||||||
import com.cloud.utils.component.ComponentContext;
|
import java.util.Set;
|
||||||
import com.cloud.utils.exception.CloudRuntimeException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
|
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
|
||||||
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory;
|
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory;
|
||||||
@ -46,8 +47,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.TemplateService.Templa
|
|||||||
import org.apache.cloudstack.framework.async.AsyncCallFuture;
|
import org.apache.cloudstack.framework.async.AsyncCallFuture;
|
||||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||||
import org.apache.cloudstack.framework.events.Event;
|
import org.apache.cloudstack.framework.events.Event;
|
||||||
import org.apache.cloudstack.framework.events.EventBus;
|
import org.apache.cloudstack.framework.events.EventDistributor;
|
||||||
import org.apache.cloudstack.framework.events.EventBusException;
|
|
||||||
import org.apache.cloudstack.framework.messagebus.MessageBus;
|
import org.apache.cloudstack.framework.messagebus.MessageBus;
|
||||||
import org.apache.cloudstack.secstorage.heuristics.HeuristicType;
|
import org.apache.cloudstack.secstorage.heuristics.HeuristicType;
|
||||||
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
|
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
|
||||||
@ -70,30 +70,30 @@ import org.mockito.invocation.InvocationOnMock;
|
|||||||
import org.mockito.junit.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import com.cloud.dc.DataCenterVO;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import com.cloud.dc.dao.DataCenterDao;
|
||||||
import java.lang.reflect.Method;
|
import com.cloud.event.EventTypes;
|
||||||
import java.util.ArrayList;
|
import com.cloud.event.UsageEventUtils;
|
||||||
import java.util.Collections;
|
import com.cloud.event.UsageEventVO;
|
||||||
import java.util.HashMap;
|
import com.cloud.event.dao.UsageEventDao;
|
||||||
import java.util.HashSet;
|
import com.cloud.exception.InvalidParameterValueException;
|
||||||
import java.util.List;
|
import com.cloud.org.Grouping;
|
||||||
import java.util.Map;
|
import com.cloud.server.StatsCollector;
|
||||||
import java.util.Set;
|
import com.cloud.storage.Storage.ImageFormat;
|
||||||
import java.util.concurrent.ExecutionException;
|
import com.cloud.storage.TemplateProfile;
|
||||||
|
import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
|
||||||
import static org.mockito.Mockito.any;
|
import com.cloud.storage.VMTemplateVO;
|
||||||
import static org.mockito.Mockito.anyLong;
|
import com.cloud.storage.dao.VMTemplateZoneDao;
|
||||||
import static org.mockito.Mockito.doAnswer;
|
import com.cloud.user.AccountVO;
|
||||||
import static org.mockito.Mockito.eq;
|
import com.cloud.user.ResourceLimitService;
|
||||||
import static org.mockito.Mockito.mock;
|
import com.cloud.user.dao.AccountDao;
|
||||||
import static org.mockito.Mockito.times;
|
import com.cloud.utils.component.ComponentContext;
|
||||||
import static org.mockito.Mockito.when;
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
public class HypervisorTemplateAdapterTest {
|
public class HypervisorTemplateAdapterTest {
|
||||||
@Mock
|
@Mock
|
||||||
EventBus _bus;
|
EventDistributor eventDistributor;
|
||||||
List<Event> events = new ArrayList<>();
|
List<Event> events = new ArrayList<>();
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
@ -168,7 +168,7 @@ public class HypervisorTemplateAdapterTest {
|
|||||||
closeable.close();
|
closeable.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public UsageEventUtils setupUsageUtils() throws EventBusException {
|
public UsageEventUtils setupUsageUtils() {
|
||||||
Mockito.when(_configDao.getValue(eq("publish.usage.events"))).thenReturn("true");
|
Mockito.when(_configDao.getValue(eq("publish.usage.events"))).thenReturn("true");
|
||||||
Mockito.when(_usageEventDao.persist(Mockito.any(UsageEventVO.class))).then(new Answer<Void>() {
|
Mockito.when(_usageEventDao.persist(Mockito.any(UsageEventVO.class))).then(new Answer<Void>() {
|
||||||
@Override public Void answer(InvocationOnMock invocation) throws Throwable {
|
@Override public Void answer(InvocationOnMock invocation) throws Throwable {
|
||||||
@ -180,16 +180,14 @@ public class HypervisorTemplateAdapterTest {
|
|||||||
|
|
||||||
Mockito.when(_usageEventDao.listAll()).thenReturn(usageEvents);
|
Mockito.when(_usageEventDao.listAll()).thenReturn(usageEvents);
|
||||||
|
|
||||||
doAnswer(new Answer<Void>() {
|
doAnswer((Answer<Void>) invocation -> {
|
||||||
@Override public Void answer(InvocationOnMock invocation) throws Throwable {
|
Event event = (Event)invocation.getArguments()[0];
|
||||||
Event event = (Event)invocation.getArguments()[0];
|
events.add(event);
|
||||||
events.add(event);
|
return null;
|
||||||
return null;
|
}).when(eventDistributor).publish(any(Event.class));
|
||||||
}
|
|
||||||
}).when(_bus).publish(any(Event.class));
|
|
||||||
|
|
||||||
componentContextMocked = Mockito.mockStatic(ComponentContext.class);
|
componentContextMocked = Mockito.mockStatic(ComponentContext.class);
|
||||||
when(ComponentContext.getComponent(eq(EventBus.class))).thenReturn(_bus);
|
when(ComponentContext.getComponent(eq(EventDistributor.class))).thenReturn(eventDistributor);
|
||||||
|
|
||||||
UsageEventUtils utils = new UsageEventUtils();
|
UsageEventUtils utils = new UsageEventUtils();
|
||||||
|
|
||||||
@ -257,7 +255,7 @@ public class HypervisorTemplateAdapterTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//@Test
|
//@Test
|
||||||
public void testEmitDeleteEventUuid() throws InterruptedException, ExecutionException, EventBusException {
|
public void testEmitDeleteEventUuid() throws InterruptedException, ExecutionException {
|
||||||
//All the mocks required for this test to work.
|
//All the mocks required for this test to work.
|
||||||
ImageStoreEntity store = mock(ImageStoreEntity.class);
|
ImageStoreEntity store = mock(ImageStoreEntity.class);
|
||||||
when(store.getId()).thenReturn(1l);
|
when(store.getId()).thenReturn(1l);
|
||||||
|
|||||||
@ -16,6 +16,38 @@
|
|||||||
// under the License.
|
// under the License.
|
||||||
package com.cloud.user;
|
package com.cloud.user;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.nullable;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
||||||
|
import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd;
|
||||||
|
import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd;
|
||||||
|
import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd;
|
||||||
|
import org.apache.cloudstack.api.response.UserTwoFactorAuthenticationSetupResponse;
|
||||||
|
import org.apache.cloudstack.auth.UserAuthenticator;
|
||||||
|
import org.apache.cloudstack.auth.UserAuthenticator.ActionOnFailedAuthentication;
|
||||||
|
import org.apache.cloudstack.auth.UserTwoFactorAuthenticator;
|
||||||
|
import org.apache.cloudstack.context.CallContext;
|
||||||
|
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||||
|
import org.apache.cloudstack.webhook.WebhookHelper;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.InOrder;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockedStatic;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
|
|
||||||
import com.cloud.acl.DomainChecker;
|
import com.cloud.acl.DomainChecker;
|
||||||
import com.cloud.api.auth.SetupUserTwoFactorAuthenticationCmd;
|
import com.cloud.api.auth.SetupUserTwoFactorAuthenticationCmd;
|
||||||
import com.cloud.domain.Domain;
|
import com.cloud.domain.Domain;
|
||||||
@ -28,40 +60,12 @@ import com.cloud.projects.Project;
|
|||||||
import com.cloud.projects.ProjectAccountVO;
|
import com.cloud.projects.ProjectAccountVO;
|
||||||
import com.cloud.user.Account.State;
|
import com.cloud.user.Account.State;
|
||||||
import com.cloud.utils.Pair;
|
import com.cloud.utils.Pair;
|
||||||
|
import com.cloud.utils.component.ComponentContext;
|
||||||
import com.cloud.utils.exception.CloudRuntimeException;
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
import com.cloud.vm.UserVmManagerImpl;
|
import com.cloud.vm.UserVmManagerImpl;
|
||||||
import com.cloud.vm.UserVmVO;
|
import com.cloud.vm.UserVmVO;
|
||||||
import com.cloud.vm.VMInstanceVO;
|
import com.cloud.vm.VMInstanceVO;
|
||||||
import com.cloud.vm.snapshot.VMSnapshotVO;
|
import com.cloud.vm.snapshot.VMSnapshotVO;
|
||||||
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
|
||||||
import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd;
|
|
||||||
import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd;
|
|
||||||
import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd;
|
|
||||||
import org.apache.cloudstack.api.response.UserTwoFactorAuthenticationSetupResponse;
|
|
||||||
import org.apache.cloudstack.auth.UserAuthenticator;
|
|
||||||
import org.apache.cloudstack.auth.UserAuthenticator.ActionOnFailedAuthentication;
|
|
||||||
import org.apache.cloudstack.auth.UserTwoFactorAuthenticator;
|
|
||||||
import org.apache.cloudstack.context.CallContext;
|
|
||||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.mockito.InOrder;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.MockedStatic;
|
|
||||||
import org.mockito.Mockito;
|
|
||||||
import org.mockito.junit.MockitoJUnitRunner;
|
|
||||||
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.UnknownHostException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.nullable;
|
|
||||||
|
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
public class AccountManagerImplTest extends AccountManagetImplTestBase {
|
public class AccountManagerImplTest extends AccountManagetImplTestBase {
|
||||||
@ -173,6 +177,7 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
|
|||||||
Mockito.when(_sshKeyPairDao.listKeyPairs(Mockito.anyLong(), Mockito.anyLong())).thenReturn(sshkeyList);
|
Mockito.when(_sshKeyPairDao.listKeyPairs(Mockito.anyLong(), Mockito.anyLong())).thenReturn(sshkeyList);
|
||||||
Mockito.when(_sshKeyPairDao.remove(Mockito.anyLong())).thenReturn(true);
|
Mockito.when(_sshKeyPairDao.remove(Mockito.anyLong())).thenReturn(true);
|
||||||
Mockito.when(userDataDao.removeByAccountId(Mockito.anyLong())).thenReturn(222);
|
Mockito.when(userDataDao.removeByAccountId(Mockito.anyLong())).thenReturn(222);
|
||||||
|
Mockito.doNothing().when(accountManagerImpl).deleteWebhooksForAccount(Mockito.anyLong());
|
||||||
|
|
||||||
Assert.assertTrue(accountManagerImpl.deleteUserAccount(42l));
|
Assert.assertTrue(accountManagerImpl.deleteUserAccount(42l));
|
||||||
// assert that this was a clean delete
|
// assert that this was a clean delete
|
||||||
@ -192,6 +197,7 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
|
|||||||
Mockito.when(_vmMgr.expunge(Mockito.any(UserVmVO.class))).thenReturn(false);
|
Mockito.when(_vmMgr.expunge(Mockito.any(UserVmVO.class))).thenReturn(false);
|
||||||
Mockito.lenient().when(_domainMgr.getDomain(Mockito.anyLong())).thenReturn(domain);
|
Mockito.lenient().when(_domainMgr.getDomain(Mockito.anyLong())).thenReturn(domain);
|
||||||
Mockito.lenient().when(securityChecker.checkAccess(Mockito.any(Account.class), Mockito.any(Domain.class))).thenReturn(true);
|
Mockito.lenient().when(securityChecker.checkAccess(Mockito.any(Account.class), Mockito.any(Domain.class))).thenReturn(true);
|
||||||
|
Mockito.doNothing().when(accountManagerImpl).deleteWebhooksForAccount(Mockito.anyLong());
|
||||||
|
|
||||||
Assert.assertTrue(accountManagerImpl.deleteUserAccount(42l));
|
Assert.assertTrue(accountManagerImpl.deleteUserAccount(42l));
|
||||||
// assert that this was NOT a clean delete
|
// assert that this was NOT a clean delete
|
||||||
@ -1032,4 +1038,24 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
|
|||||||
Assert.assertEquals(userAccountVOList.size(), userAccounts.size());
|
Assert.assertEquals(userAccountVOList.size(), userAccounts.size());
|
||||||
Assert.assertEquals(userAccountVOList.get(0), userAccounts.get(0));
|
Assert.assertEquals(userAccountVOList.get(0), userAccounts.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeleteWebhooksForAccount() {
|
||||||
|
try (MockedStatic<ComponentContext> mockedComponentContext = Mockito.mockStatic(ComponentContext.class)) {
|
||||||
|
WebhookHelper webhookHelper = Mockito.mock(WebhookHelper.class);
|
||||||
|
Mockito.doNothing().when(webhookHelper).deleteWebhooksForAccount(Mockito.anyLong());
|
||||||
|
mockedComponentContext.when(() -> ComponentContext.getDelegateComponentOfType(WebhookHelper.class))
|
||||||
|
.thenReturn(webhookHelper);
|
||||||
|
accountManagerImpl.deleteWebhooksForAccount(1L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeleteWebhooksForAccountNoBean() {
|
||||||
|
try (MockedStatic<ComponentContext> mockedComponentContext = Mockito.mockStatic(ComponentContext.class)) {
|
||||||
|
mockedComponentContext.when(() -> ComponentContext.getDelegateComponentOfType(WebhookHelper.class))
|
||||||
|
.thenThrow(NoSuchBeanDefinitionException.class);
|
||||||
|
accountManagerImpl.deleteWebhooksForAccount(1L);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
212
test/integration/smoke/test_webhook_delivery.py
Normal file
212
test/integration/smoke/test_webhook_delivery.py
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
""" BVT tests for webhooks delivery with a basic server
|
||||||
|
"""
|
||||||
|
# Import Local Modules
|
||||||
|
from marvin.cloudstackTestCase import cloudstackTestCase
|
||||||
|
from marvin.lib.base import (Account,
|
||||||
|
Domain,
|
||||||
|
Webhook,
|
||||||
|
SSHKeyPair)
|
||||||
|
from marvin.lib.common import (get_domain,
|
||||||
|
get_zone)
|
||||||
|
from marvin.lib.utils import (random_gen)
|
||||||
|
from marvin.cloudstackException import CloudstackAPIException
|
||||||
|
from nose.plugins.attrib import attr
|
||||||
|
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||||
|
import logging
|
||||||
|
# Import System modules
|
||||||
|
import time
|
||||||
|
import json
|
||||||
|
import socket
|
||||||
|
import _thread
|
||||||
|
|
||||||
|
|
||||||
|
_multiprocess_shared_ = True
|
||||||
|
deliveries_received = []
|
||||||
|
|
||||||
|
class WebhookReceiver(BaseHTTPRequestHandler):
|
||||||
|
"""
|
||||||
|
WebhookReceiver class to receive webhook events
|
||||||
|
"""
|
||||||
|
def _set_response(self):
|
||||||
|
self.send_response(200)
|
||||||
|
self.send_header('Content-type', 'text/html')
|
||||||
|
self.end_headers()
|
||||||
|
|
||||||
|
def do_POST(self):
|
||||||
|
content_length = int(self.headers['Content-Length'])
|
||||||
|
post_data = self.rfile.read(content_length)
|
||||||
|
post_data = post_data.decode('utf-8')
|
||||||
|
event_id = self.headers.get('X-CS-Event-ID')
|
||||||
|
print("POST request,\nPath: %s\nHeaders:\n%s\n\nBody:\n%s\n" %
|
||||||
|
(str(self.path), str(self.headers), post_data))
|
||||||
|
self._set_response()
|
||||||
|
global deliveries_received
|
||||||
|
if deliveries_received is None:
|
||||||
|
deliveries_received = []
|
||||||
|
deliveries_received.append({'event': event_id, 'payload': post_data})
|
||||||
|
if event_id != None:
|
||||||
|
self.wfile.write("Event with ID: {} successfully processed!".format(str(event_id)).encode('utf-8'))
|
||||||
|
else:
|
||||||
|
self.wfile.write("POST request for {}".format(self.path).encode('utf-8'))
|
||||||
|
|
||||||
|
class TestWebhookDelivery(cloudstackTestCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
testClient = super(TestWebhookDelivery, cls).getClsTestClient()
|
||||||
|
cls.apiclient = testClient.getApiClient()
|
||||||
|
cls.services = testClient.getParsedTestDataConfig()
|
||||||
|
cls.mgtSvrDetails = cls.config.__dict__["mgtSvr"][0].__dict__
|
||||||
|
|
||||||
|
# Get Zone, Domain and templates
|
||||||
|
cls.domain = get_domain(cls.apiclient)
|
||||||
|
cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests())
|
||||||
|
cls.logger = logging.getLogger('TestWebhookDelivery')
|
||||||
|
cls.logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
s.connect((cls.mgtSvrDetails["mgtSvrIp"], cls.mgtSvrDetails["port"]))
|
||||||
|
cls.server_ip = s.getsockname()[0]
|
||||||
|
s.close()
|
||||||
|
if cls.server_ip == "127.0.0.1":
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
s.connect(("8.8.8.8", 80))
|
||||||
|
cls.server_ip = s.getsockname()[0]
|
||||||
|
s.close()
|
||||||
|
# use random port for webhookreceiver server
|
||||||
|
s = socket.socket()
|
||||||
|
s.bind(('', 0))
|
||||||
|
cls.server_port = s.getsockname()[1]
|
||||||
|
s.close()
|
||||||
|
cls.webhook_receiver_url = "http://" + cls.server_ip + ":" + str(cls.server_port)
|
||||||
|
cls.logger.debug("Running Webhook receiver @ %s" % cls.webhook_receiver_url)
|
||||||
|
def startMgmtServer(tname, server):
|
||||||
|
cls.logger.debug("Starting WebhookReceiver")
|
||||||
|
try:
|
||||||
|
server.serve_forever()
|
||||||
|
except Exception: pass
|
||||||
|
cls.server = HTTPServer(('0.0.0.0', cls.server_port), WebhookReceiver)
|
||||||
|
_thread.start_new_thread(startMgmtServer, ("webhook-receiver", cls.server,))
|
||||||
|
|
||||||
|
cls._cleanup = []
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
if cls.server:
|
||||||
|
cls.server.socket.close()
|
||||||
|
global deliveries_received
|
||||||
|
deliveries_received = []
|
||||||
|
super(TestWebhookDelivery, cls).tearDownClass()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.cleanup = []
|
||||||
|
self.domain1 = Domain.create(
|
||||||
|
self.apiclient,
|
||||||
|
self.services["domain"])
|
||||||
|
self.cleanup.append(self.domain1)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super(TestWebhookDelivery, self).tearDown()
|
||||||
|
|
||||||
|
def popItemFromCleanup(self, item_id):
|
||||||
|
for idx, x in enumerate(self.cleanup):
|
||||||
|
if x.id == item_id:
|
||||||
|
self.cleanup.pop(idx)
|
||||||
|
break
|
||||||
|
|
||||||
|
def createDomainAccount(self, isDomainAdmin=False):
|
||||||
|
self.account = Account.create(
|
||||||
|
self.apiclient,
|
||||||
|
self.services["account"],
|
||||||
|
admin=isDomainAdmin,
|
||||||
|
domainid=self.domain1.id)
|
||||||
|
self.cleanup.append(self.account)
|
||||||
|
self.userapiclient = self.testClient.getUserApiClient(
|
||||||
|
UserName=self.account.name,
|
||||||
|
DomainName=self.account.domain
|
||||||
|
)
|
||||||
|
|
||||||
|
def createWebhook(self, apiclient, scope=None, domainid=None, account=None, payloadurl=None, description=None, sslverification=None, secretkey=None, state=None):
|
||||||
|
name = "Test-" + random_gen()
|
||||||
|
if payloadurl is None:
|
||||||
|
payloadurl = self.webhook_receiver_url
|
||||||
|
self.webhook = Webhook.create(
|
||||||
|
apiclient,
|
||||||
|
name=name,
|
||||||
|
payloadurl=payloadurl,
|
||||||
|
description=description,
|
||||||
|
scope=scope,
|
||||||
|
sslverification=sslverification,
|
||||||
|
secretkey=secretkey,
|
||||||
|
state=state,
|
||||||
|
domainid=domainid,
|
||||||
|
account=account
|
||||||
|
)
|
||||||
|
self.cleanup.append(self.webhook)
|
||||||
|
|
||||||
|
@attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
|
||||||
|
def test_01_webhook_deliveries(self):
|
||||||
|
global deliveries_received
|
||||||
|
self.createDomainAccount()
|
||||||
|
self.createWebhook(self.userapiclient)
|
||||||
|
self.keypair = SSHKeyPair.register(
|
||||||
|
self.userapiclient,
|
||||||
|
name="Test-" + random_gen(),
|
||||||
|
publickey="ssh-rsa: e6:9a:1e:b5:98:75:88:5d:56:bc:92:7b:43:48:05:b2"
|
||||||
|
)
|
||||||
|
self.logger.debug("Registered sshkeypair: %s" % str(self.keypair.__dict__))
|
||||||
|
time.sleep(2)
|
||||||
|
list_deliveries = self.webhook.list_deliveries(
|
||||||
|
self.userapiclient,
|
||||||
|
page=1,
|
||||||
|
pagesize=20
|
||||||
|
)
|
||||||
|
self.assertNotEqual(
|
||||||
|
list_deliveries,
|
||||||
|
None,
|
||||||
|
"Check webhook deliveries list"
|
||||||
|
)
|
||||||
|
self.assertTrue(
|
||||||
|
len(list_deliveries) > 0,
|
||||||
|
"Check webhook deliveries list length"
|
||||||
|
)
|
||||||
|
for delivery in list_deliveries:
|
||||||
|
self.assertEqual(
|
||||||
|
delivery.success,
|
||||||
|
True,
|
||||||
|
"Check webhook delivery success"
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
delivery.response,
|
||||||
|
("Event with ID: %s successfully processed!" % delivery.eventid),
|
||||||
|
"Check webhook delivery response"
|
||||||
|
)
|
||||||
|
delivery_matched = False
|
||||||
|
for received in deliveries_received:
|
||||||
|
if received['event'] == delivery.eventid:
|
||||||
|
self.assertEqual(
|
||||||
|
delivery.payload,
|
||||||
|
received['payload'],
|
||||||
|
"Check webhook delivery payload"
|
||||||
|
)
|
||||||
|
delivery_matched = True
|
||||||
|
self.assertTrue(
|
||||||
|
delivery_matched,
|
||||||
|
"Delivery for %s did not match with server" % delivery.id
|
||||||
|
)
|
||||||
392
test/integration/smoke/test_webhook_lifecycle.py
Normal file
392
test/integration/smoke/test_webhook_lifecycle.py
Normal file
@ -0,0 +1,392 @@
|
|||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
""" BVT tests for webhooks lifecycle functionalities
|
||||||
|
"""
|
||||||
|
# Import Local Modules
|
||||||
|
from marvin.cloudstackTestCase import cloudstackTestCase
|
||||||
|
from marvin.cloudstackAPI import (listEvents)
|
||||||
|
from marvin.lib.base import (Account,
|
||||||
|
Domain,
|
||||||
|
Webhook,
|
||||||
|
SSHKeyPair)
|
||||||
|
from marvin.lib.common import (get_domain,
|
||||||
|
get_zone)
|
||||||
|
from marvin.lib.utils import (random_gen)
|
||||||
|
from marvin.cloudstackException import CloudstackAPIException
|
||||||
|
from nose.plugins.attrib import attr
|
||||||
|
import logging
|
||||||
|
# Import System modules
|
||||||
|
import time
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
|
_multiprocess_shared_ = True
|
||||||
|
HTTP_PAYLOAD_URL = "http://smee.io/C9LPa7Ei3iB6Qj2"
|
||||||
|
HTTPS_PAYLOAD_URL = "https://smee.io/C9LPa7Ei3iB6Qj2"
|
||||||
|
|
||||||
|
class TestWebhooks(cloudstackTestCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
testClient = super(TestWebhooks, cls).getClsTestClient()
|
||||||
|
cls.apiclient = testClient.getApiClient()
|
||||||
|
cls.services = testClient.getParsedTestDataConfig()
|
||||||
|
|
||||||
|
# Get Zone, Domain and templates
|
||||||
|
cls.domain = get_domain(cls.apiclient)
|
||||||
|
cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests())
|
||||||
|
|
||||||
|
cls._cleanup = []
|
||||||
|
cls.logger = logging.getLogger('TestWebhooks')
|
||||||
|
cls.logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
super(TestWebhooks, cls).tearDownClass()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.cleanup = []
|
||||||
|
self.domain1 = Domain.create(
|
||||||
|
self.apiclient,
|
||||||
|
self.services["domain"])
|
||||||
|
self.cleanup.append(self.domain1)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super(TestWebhooks, self).tearDown()
|
||||||
|
|
||||||
|
def popItemFromCleanup(self, item_id):
|
||||||
|
for idx, x in enumerate(self.cleanup):
|
||||||
|
if x.id == item_id:
|
||||||
|
self.cleanup.pop(idx)
|
||||||
|
break
|
||||||
|
|
||||||
|
def createDomainAccount(self, isDomainAdmin=False):
|
||||||
|
self.account = Account.create(
|
||||||
|
self.apiclient,
|
||||||
|
self.services["account"],
|
||||||
|
admin=isDomainAdmin,
|
||||||
|
domainid=self.domain1.id)
|
||||||
|
self.cleanup.append(self.account)
|
||||||
|
self.userapiclient = self.testClient.getUserApiClient(
|
||||||
|
UserName=self.account.name,
|
||||||
|
DomainName=self.account.domain
|
||||||
|
)
|
||||||
|
|
||||||
|
def runWebhookLifecycleTest(self, apiclient, scope=None, domainid=None, account=None, normaluser=None, payloadurl=None, description=None, sslverification=None, secretkey=None, state=None, isdelete=True):
|
||||||
|
name = "Test-" + random_gen()
|
||||||
|
if payloadurl is None:
|
||||||
|
payloadurl = HTTP_PAYLOAD_URL
|
||||||
|
self.webhook = Webhook.create(
|
||||||
|
apiclient,
|
||||||
|
name=name,
|
||||||
|
payloadurl=payloadurl,
|
||||||
|
description=description,
|
||||||
|
scope=scope,
|
||||||
|
sslverification=sslverification,
|
||||||
|
secretkey=secretkey,
|
||||||
|
state=state,
|
||||||
|
domainid=domainid,
|
||||||
|
account=account
|
||||||
|
)
|
||||||
|
self.cleanup.append(self.webhook)
|
||||||
|
self.assertNotEqual(
|
||||||
|
self.webhook,
|
||||||
|
None,
|
||||||
|
"Check webhook created"
|
||||||
|
)
|
||||||
|
webhook_id = self.webhook.id
|
||||||
|
self.logger.debug("Created webhook: %s" % str(self.webhook.__dict__))
|
||||||
|
self.assertEqual(
|
||||||
|
name,
|
||||||
|
self.webhook.name,
|
||||||
|
"Check webhook name"
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
payloadurl,
|
||||||
|
self.webhook.payloadurl,
|
||||||
|
"Check webhook payloadurl"
|
||||||
|
)
|
||||||
|
if state is None:
|
||||||
|
state = 'Enabled'
|
||||||
|
self.assertEqual(
|
||||||
|
state,
|
||||||
|
self.webhook.state,
|
||||||
|
"Check webhook state"
|
||||||
|
)
|
||||||
|
if scope is None or normaluser is not None:
|
||||||
|
scope = 'Local'
|
||||||
|
self.assertEqual(
|
||||||
|
scope,
|
||||||
|
self.webhook.scope,
|
||||||
|
"Check webhook scope"
|
||||||
|
)
|
||||||
|
if sslverification is None:
|
||||||
|
sslverification = False
|
||||||
|
self.assertEqual(
|
||||||
|
sslverification,
|
||||||
|
self.webhook.sslverification,
|
||||||
|
"Check webhook sslverification"
|
||||||
|
)
|
||||||
|
if domainid is not None:
|
||||||
|
if normaluser is not None:
|
||||||
|
domainid = normaluser.domainid
|
||||||
|
self.assertEqual(
|
||||||
|
domainid,
|
||||||
|
self.webhook.domainid,
|
||||||
|
"Check webhook domainid"
|
||||||
|
)
|
||||||
|
if account is not None:
|
||||||
|
self.assertEqual(
|
||||||
|
account,
|
||||||
|
self.webhook.account,
|
||||||
|
"Check webhook account"
|
||||||
|
)
|
||||||
|
if description is not None:
|
||||||
|
self.assertEqual(
|
||||||
|
description,
|
||||||
|
self.webhook.description,
|
||||||
|
"Check webhook description"
|
||||||
|
)
|
||||||
|
if secretkey is not None:
|
||||||
|
self.assertEqual(
|
||||||
|
secretkey,
|
||||||
|
self.webhook.secretkey,
|
||||||
|
"Check webhook secretkey"
|
||||||
|
)
|
||||||
|
list_webhook = Webhook.list(
|
||||||
|
apiclient,
|
||||||
|
id=webhook_id
|
||||||
|
)
|
||||||
|
self.assertNotEqual(
|
||||||
|
list_webhook,
|
||||||
|
None,
|
||||||
|
"Check webhook list"
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
len(list_webhook),
|
||||||
|
1,
|
||||||
|
"Check webhook list length"
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
list_webhook[0].id,
|
||||||
|
webhook_id,
|
||||||
|
"Check webhook list item"
|
||||||
|
)
|
||||||
|
if isdelete == False:
|
||||||
|
return
|
||||||
|
self.webhook.delete(apiclient)
|
||||||
|
self.popItemFromCleanup(webhook_id)
|
||||||
|
list_webhook = Webhook.list(
|
||||||
|
apiclient,
|
||||||
|
id=webhook_id
|
||||||
|
)
|
||||||
|
self.assertTrue(
|
||||||
|
list_webhook is None or len(list_webhook) == 0,
|
||||||
|
"Check webhook list after delete"
|
||||||
|
)
|
||||||
|
|
||||||
|
@attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
|
||||||
|
def test_01_create_webhook_admin_local(self):
|
||||||
|
self.runWebhookLifecycleTest(self.apiclient)
|
||||||
|
|
||||||
|
@attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
|
||||||
|
def test_02_create_webhook_admin_domain(self):
|
||||||
|
self.runWebhookLifecycleTest(self.apiclient, 'Domain', self.domain1.id)
|
||||||
|
|
||||||
|
@attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
|
||||||
|
def test_03_create_webhook_admin_global(self):
|
||||||
|
self.runWebhookLifecycleTest(self.apiclient, 'Global')
|
||||||
|
|
||||||
|
@attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
|
||||||
|
def test_04_create_webhook_domainadmin_local(self):
|
||||||
|
self.createDomainAccount(True)
|
||||||
|
self.runWebhookLifecycleTest(self.userapiclient)
|
||||||
|
|
||||||
|
@attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
|
||||||
|
def test_05_create_webhook_domainadmin_subdomain(self):
|
||||||
|
self.createDomainAccount(True)
|
||||||
|
self.domain11 = Domain.create(
|
||||||
|
self.apiclient,
|
||||||
|
self.services["domain"],
|
||||||
|
parentdomainid=self.domain1.id)
|
||||||
|
self.cleanup.append(self.domain11)
|
||||||
|
self.runWebhookLifecycleTest(self.userapiclient, 'Domain', self.domain11.id)
|
||||||
|
|
||||||
|
@attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
|
||||||
|
def test_06_create_webhook_domainadmin_global_negative(self):
|
||||||
|
self.createDomainAccount(True)
|
||||||
|
try:
|
||||||
|
self.runWebhookLifecycleTest(self.userapiclient, 'Global')
|
||||||
|
except CloudstackAPIException as e:
|
||||||
|
self.assertTrue(
|
||||||
|
"errorText:Scope Global can not be specified for owner" in str(e),
|
||||||
|
"Check Global scope error check"
|
||||||
|
)
|
||||||
|
|
||||||
|
@attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
|
||||||
|
def test_07_create_webhook_user_local(self):
|
||||||
|
self.createDomainAccount()
|
||||||
|
self.runWebhookLifecycleTest(self.userapiclient)
|
||||||
|
|
||||||
|
@attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
|
||||||
|
def test_08_create_webhook_user_domain(self):
|
||||||
|
"""For normal user scope will always be Local irrespective of the passed value
|
||||||
|
"""
|
||||||
|
self.createDomainAccount()
|
||||||
|
self.runWebhookLifecycleTest(self.userapiclient, 'Domain', self.domain1.id, normaluser=self.account)
|
||||||
|
|
||||||
|
@attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
|
||||||
|
def test_09_create_webhook_user_gloabl(self):
|
||||||
|
"""For normal user scope will always be Local irrespective of the passed value
|
||||||
|
"""
|
||||||
|
self.createDomainAccount()
|
||||||
|
self.runWebhookLifecycleTest(self.userapiclient, 'Global', normaluser=self.account)
|
||||||
|
|
||||||
|
@attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
|
||||||
|
def test_10_create_webhook_admin_advanced(self):
|
||||||
|
self.createDomainAccount()
|
||||||
|
self.runWebhookLifecycleTest(
|
||||||
|
self.apiclient,
|
||||||
|
payloadurl=HTTPS_PAYLOAD_URL,
|
||||||
|
scope="Local",
|
||||||
|
description="Webhook",
|
||||||
|
sslverification=True,
|
||||||
|
secretkey="webhook",
|
||||||
|
state="Disabled",
|
||||||
|
domainid=self.domain1.id,
|
||||||
|
account=self.account.name
|
||||||
|
)
|
||||||
|
|
||||||
|
@attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
|
||||||
|
def test_11_update_webhook(self):
|
||||||
|
self.createDomainAccount()
|
||||||
|
self.runWebhookLifecycleTest(self.userapiclient, isdelete=False)
|
||||||
|
description = "Desc-" + random_gen()
|
||||||
|
secretkey = random_gen()
|
||||||
|
state = 'Disabled'
|
||||||
|
updated_webhook = self.webhook.update(
|
||||||
|
self.userapiclient,
|
||||||
|
description=description,
|
||||||
|
secretkey=secretkey,
|
||||||
|
state=state
|
||||||
|
)['webhook']
|
||||||
|
self.assertNotEqual(
|
||||||
|
updated_webhook,
|
||||||
|
None,
|
||||||
|
"Check updated webhook"
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
description,
|
||||||
|
updated_webhook.description,
|
||||||
|
"Check webhook description"
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
secretkey,
|
||||||
|
updated_webhook.secretkey,
|
||||||
|
"Check webhook secretkey"
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
state,
|
||||||
|
updated_webhook.state,
|
||||||
|
"Check webhook state"
|
||||||
|
)
|
||||||
|
|
||||||
|
@attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
|
||||||
|
def test_12_list_user_webhook_deliveries(self):
|
||||||
|
self.createDomainAccount()
|
||||||
|
self.runWebhookLifecycleTest(self.userapiclient, isdelete=False)
|
||||||
|
now = datetime.now() # current date and time
|
||||||
|
start_time = now.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
self.keypair = SSHKeyPair.register(
|
||||||
|
self.userapiclient,
|
||||||
|
name="Test-" + random_gen(),
|
||||||
|
publickey="ssh-rsa: e6:9a:1e:b5:98:75:88:5d:56:bc:92:7b:43:48:05:b2"
|
||||||
|
)
|
||||||
|
self.logger.debug("Registered sshkeypair: %s" % str(self.keypair.__dict__))
|
||||||
|
cmd = listEvents.listEventsCmd()
|
||||||
|
cmd.startdate = start_time
|
||||||
|
cmd.listall = True
|
||||||
|
events = self.apiclient.listEvents(cmd)
|
||||||
|
register_sshkeypair_event_count = 0
|
||||||
|
if events is not None:
|
||||||
|
for event in events:
|
||||||
|
if event.type == "REGISTER.SSH.KEYPAIR":
|
||||||
|
register_sshkeypair_event_count = register_sshkeypair_event_count + 1
|
||||||
|
time.sleep(5)
|
||||||
|
list_deliveries = self.webhook.list_deliveries(
|
||||||
|
self.userapiclient,
|
||||||
|
page=1,
|
||||||
|
pagesize=20
|
||||||
|
)
|
||||||
|
self.assertNotEqual(
|
||||||
|
list_deliveries,
|
||||||
|
None,
|
||||||
|
"Check webhook deliveries list"
|
||||||
|
)
|
||||||
|
self.assertTrue(
|
||||||
|
len(list_deliveries) > 0,
|
||||||
|
"Check webhook deliveries list length"
|
||||||
|
)
|
||||||
|
register_sshkeypair_delivery_count = 0
|
||||||
|
for delivery in list_deliveries:
|
||||||
|
if delivery.eventtype == "REGISTER.SSH.KEYPAIR":
|
||||||
|
register_sshkeypair_delivery_count = register_sshkeypair_delivery_count + 1
|
||||||
|
self.assertEqual(
|
||||||
|
register_sshkeypair_event_count,
|
||||||
|
register_sshkeypair_delivery_count,
|
||||||
|
"Check sshkeypair webhook deliveries count"
|
||||||
|
)
|
||||||
|
self.webhook.delete_deliveries(
|
||||||
|
self.userapiclient
|
||||||
|
)
|
||||||
|
list_deliveries = self.webhook.list_deliveries(
|
||||||
|
self.userapiclient
|
||||||
|
)
|
||||||
|
self.assertTrue(
|
||||||
|
list_deliveries is None or len(list_deliveries) == 0,
|
||||||
|
"Check webhook deliveries list after delete"
|
||||||
|
)
|
||||||
|
|
||||||
|
@attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], required_hardware="false")
|
||||||
|
def test_13_webhook_execute_delivery(self):
|
||||||
|
self.createDomainAccount()
|
||||||
|
self.runWebhookLifecycleTest(self.userapiclient, isdelete=False)
|
||||||
|
payload = "{ \"CloudStack\": \"Integration Test\" }"
|
||||||
|
delivery = self.webhook.execute_delivery(
|
||||||
|
self.userapiclient,
|
||||||
|
payload=payload
|
||||||
|
)
|
||||||
|
self.assertNotEqual(
|
||||||
|
delivery,
|
||||||
|
None,
|
||||||
|
"Check test webhook delivery"
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
self.webhook.id,
|
||||||
|
delivery.webhookid,
|
||||||
|
"Check test webhook delivery webhook"
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
payload,
|
||||||
|
delivery.payload,
|
||||||
|
"Check test webhook delivery payload"
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
self.webhook.id,
|
||||||
|
delivery.webhookid,
|
||||||
|
"Check test webhook delivery webhook"
|
||||||
|
)
|
||||||
@ -274,7 +274,9 @@ known_categories = {
|
|||||||
'deleteBucket': 'Object Store',
|
'deleteBucket': 'Object Store',
|
||||||
'listBuckets': 'Object Store',
|
'listBuckets': 'Object Store',
|
||||||
'listVmsForImport': 'Virtual Machine',
|
'listVmsForImport': 'Virtual Machine',
|
||||||
'importVm': 'Virtual Machine'
|
'importVm': 'Virtual Machine',
|
||||||
|
'Webhook': 'Webhook',
|
||||||
|
'Webhooks': 'Webhook'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -7227,3 +7227,65 @@ class Bucket:
|
|||||||
cmd.id = self.id
|
cmd.id = self.id
|
||||||
[setattr(cmd, k, v) for k, v in list(kwargs.items())]
|
[setattr(cmd, k, v) for k, v in list(kwargs.items())]
|
||||||
return apiclient.updateBucket(cmd)
|
return apiclient.updateBucket(cmd)
|
||||||
|
|
||||||
|
class Webhook:
|
||||||
|
"""Manage Webhook Life cycle"""
|
||||||
|
|
||||||
|
def __init__(self, items):
|
||||||
|
self.__dict__.update(items)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create(cls, apiclient, name, payloadurl, **kwargs):
|
||||||
|
"""Create Webhook"""
|
||||||
|
cmd = createWebhook.createWebhookCmd()
|
||||||
|
cmd.name = name
|
||||||
|
cmd.payloadurl = payloadurl
|
||||||
|
[setattr(cmd, k, v) for k, v in list(kwargs.items())]
|
||||||
|
|
||||||
|
return Webhook(apiclient.createWebhook(cmd).__dict__)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def list(cls, apiclient, **kwargs):
|
||||||
|
cmd = listWebhooks.listWebhooksCmd()
|
||||||
|
[setattr(cmd, k, v) for k, v in list(kwargs.items())]
|
||||||
|
if 'account' in list(kwargs.keys()) and 'domainid' in list(kwargs.keys()):
|
||||||
|
cmd.listall = True
|
||||||
|
return apiclient.listWebhooks(cmd)
|
||||||
|
|
||||||
|
def delete(self, apiclient):
|
||||||
|
"""Delete Webhook"""
|
||||||
|
cmd = deleteWebhook.deleteWebhookCmd()
|
||||||
|
cmd.id = self.id
|
||||||
|
apiclient.deleteWebhook(cmd)
|
||||||
|
|
||||||
|
def update(self, apiclient, **kwargs):
|
||||||
|
"""Update Webhook"""
|
||||||
|
|
||||||
|
cmd = updateWebhook.updateWebhookCmd()
|
||||||
|
cmd.id = self.id
|
||||||
|
[setattr(cmd, k, v) for k, v in list(kwargs.items())]
|
||||||
|
return apiclient.updateWebhook(cmd)
|
||||||
|
|
||||||
|
def list_deliveries(self, apiclient, **kwargs):
|
||||||
|
"""List Webhook Deliveries"""
|
||||||
|
|
||||||
|
cmd = listWebhookDeliveries.listWebhookDeliveriesCmd()
|
||||||
|
cmd.webhookid = self.id
|
||||||
|
[setattr(cmd, k, v) for k, v in list(kwargs.items())]
|
||||||
|
return apiclient.listWebhookDeliveries(cmd)
|
||||||
|
|
||||||
|
def execute_delivery(self, apiclient, **kwargs):
|
||||||
|
"""Execute Webhook Delivery"""
|
||||||
|
|
||||||
|
cmd = executeWebhookDelivery.executeWebhookDeliveryCmd()
|
||||||
|
cmd.webhookid = self.id
|
||||||
|
[setattr(cmd, k, v) for k, v in list(kwargs.items())]
|
||||||
|
return apiclient.executeWebhookDelivery(cmd)
|
||||||
|
|
||||||
|
def delete_deliveries(self, apiclient, **kwargs):
|
||||||
|
"""Delete Webhook Deliveries"""
|
||||||
|
|
||||||
|
cmd = deleteWebhookDelivery.deleteWebhookDeliveryCmd()
|
||||||
|
cmd.webhookid = self.id
|
||||||
|
[setattr(cmd, k, v) for k, v in list(kwargs.items())]
|
||||||
|
return apiclient.deleteWebhookDelivery(cmd)
|
||||||
|
|||||||
@ -60,6 +60,8 @@
|
|||||||
"label.action.bulk.release.public.ip.address": "Bulk release public IP addresses",
|
"label.action.bulk.release.public.ip.address": "Bulk release public IP addresses",
|
||||||
"label.action.cancel.maintenance.mode": "Cancel maintenance mode",
|
"label.action.cancel.maintenance.mode": "Cancel maintenance mode",
|
||||||
"label.action.change.password": "Change password",
|
"label.action.change.password": "Change password",
|
||||||
|
"label.action.clear.webhook.deliveries": "Clear deliveries",
|
||||||
|
"label.action.delete.webhook.deliveries": "Delete deliveries",
|
||||||
"label.action.configure.stickiness": "Stickiness",
|
"label.action.configure.stickiness": "Stickiness",
|
||||||
"label.action.copy.iso": "Copy ISO",
|
"label.action.copy.iso": "Copy ISO",
|
||||||
"label.action.copy.snapshot": "Copy Snapshot",
|
"label.action.copy.snapshot": "Copy Snapshot",
|
||||||
@ -583,6 +585,7 @@
|
|||||||
"label.create.tungsten.routing.policy": "Create Tungsten-Fabric routing policy",
|
"label.create.tungsten.routing.policy": "Create Tungsten-Fabric routing policy",
|
||||||
"label.create.user": "Create User",
|
"label.create.user": "Create User",
|
||||||
"label.create.vpn.connection": "Create VPN connection",
|
"label.create.vpn.connection": "Create VPN connection",
|
||||||
|
"label.create.webhook": "Create Webhook",
|
||||||
"label.created": "Created",
|
"label.created": "Created",
|
||||||
"label.creating": "Creating",
|
"label.creating": "Creating",
|
||||||
"label.creating.iprange": "Creating IP ranges",
|
"label.creating.iprange": "Creating IP ranges",
|
||||||
@ -603,6 +606,9 @@
|
|||||||
"label.data.disk": "Data disk",
|
"label.data.disk": "Data disk",
|
||||||
"label.data.disk.offering": "Data disk offering",
|
"label.data.disk.offering": "Data disk offering",
|
||||||
"label.date": "Date",
|
"label.date": "Date",
|
||||||
|
"label.datetime.filter.period": "From <b>{startDate}</b> to <b>{endDate}</b>",
|
||||||
|
"label.datetime.filter.starting": "Starting <b>{startDate}</b>.",
|
||||||
|
"label.datetime.filter.up.to": "Up to <b>{endDate}</b>.",
|
||||||
"label.day": "Day",
|
"label.day": "Day",
|
||||||
"label.day.of.month": "Day of month",
|
"label.day.of.month": "Day of month",
|
||||||
"label.day.of.week": "Day of week",
|
"label.day.of.week": "Day of week",
|
||||||
@ -672,6 +678,8 @@
|
|||||||
"label.delete.vpn.customer.gateway": "Delete VPN customer gateway",
|
"label.delete.vpn.customer.gateway": "Delete VPN customer gateway",
|
||||||
"label.delete.vpn.gateway": "Delete VPN gateway",
|
"label.delete.vpn.gateway": "Delete VPN gateway",
|
||||||
"label.delete.vpn.user": "Delete VPN User",
|
"label.delete.vpn.user": "Delete VPN User",
|
||||||
|
"label.delete.webhook": "Delete Webhook",
|
||||||
|
"label.delete.webhook.delivery": "Delete Webhook Delivery",
|
||||||
"label.deleteconfirm": "Please confirm that you would like to delete this",
|
"label.deleteconfirm": "Please confirm that you would like to delete this",
|
||||||
"label.deleting": "Deleting",
|
"label.deleting": "Deleting",
|
||||||
"label.deleting.failed": "Deleting failed",
|
"label.deleting.failed": "Deleting failed",
|
||||||
@ -730,6 +738,7 @@
|
|||||||
"label.disable.storage": "Disable storage pool",
|
"label.disable.storage": "Disable storage pool",
|
||||||
"label.disable.vpc.offering": "Disable VPC offering",
|
"label.disable.vpc.offering": "Disable VPC offering",
|
||||||
"label.disable.vpn": "Disable remote access VPN",
|
"label.disable.vpn": "Disable remote access VPN",
|
||||||
|
"label.disable.webhook": "Disable Webhook",
|
||||||
"label.disabled": "Disabled",
|
"label.disabled": "Disabled",
|
||||||
"label.disconnected": "Last disconnected",
|
"label.disconnected": "Last disconnected",
|
||||||
"label.disk": "Disk",
|
"label.disk": "Disk",
|
||||||
@ -837,6 +846,7 @@
|
|||||||
"label.enable.storage": "Enable storage pool",
|
"label.enable.storage": "Enable storage pool",
|
||||||
"label.enable.vpc.offering": "Enable VPC offering",
|
"label.enable.vpc.offering": "Enable VPC offering",
|
||||||
"label.enable.vpn": "Enable remote access VPN",
|
"label.enable.vpn": "Enable remote access VPN",
|
||||||
|
"label.enable.webhook": "Enable Webhook",
|
||||||
"label.enabled": "Enabled",
|
"label.enabled": "Enabled",
|
||||||
"label.encrypt": "Encrypt",
|
"label.encrypt": "Encrypt",
|
||||||
"label.encryptroot": "Encrypt Root Disk",
|
"label.encryptroot": "Encrypt Root Disk",
|
||||||
@ -872,6 +882,8 @@
|
|||||||
"label.esppolicy": "ESP policy",
|
"label.esppolicy": "ESP policy",
|
||||||
"label.esx.host": "ESX/ESXi host",
|
"label.esx.host": "ESX/ESXi host",
|
||||||
"label.event": "Event",
|
"label.event": "Event",
|
||||||
|
"label.eventid": "Event",
|
||||||
|
"label.eventtype": "Event type",
|
||||||
"label.event.archived": "Event(s) archived",
|
"label.event.archived": "Event(s) archived",
|
||||||
"label.event.deleted": "Event(s) deleted",
|
"label.event.deleted": "Event(s) deleted",
|
||||||
"label.event.timeline": "Event timeline",
|
"label.event.timeline": "Event timeline",
|
||||||
@ -987,6 +999,7 @@
|
|||||||
"label.hardware": "Hardware",
|
"label.hardware": "Hardware",
|
||||||
"label.hasrules":"FW rules defined",
|
"label.hasrules":"FW rules defined",
|
||||||
"label.hastate": "HA state",
|
"label.hastate": "HA state",
|
||||||
|
"label.headers": "Headers",
|
||||||
"label.header.backup.schedule": "You can set up recurring backup schedules by selecting from the available options below and applying your policy preference.",
|
"label.header.backup.schedule": "You can set up recurring backup schedules by selecting from the available options below and applying your policy preference.",
|
||||||
"label.header.volume.snapshot": "You can set up recurring Snapshot schedules by selecting from the available options below and applying your policy preference.",
|
"label.header.volume.snapshot": "You can set up recurring Snapshot schedules by selecting from the available options below and applying your policy preference.",
|
||||||
"label.header.volume.take.snapshot": "Please confirm that you want to take a Snapshot of this volume.",
|
"label.header.volume.take.snapshot": "Please confirm that you want to take a Snapshot of this volume.",
|
||||||
@ -1281,6 +1294,8 @@
|
|||||||
"label.managed.volumes": "Managed Volumes",
|
"label.managed.volumes": "Managed Volumes",
|
||||||
"label.managedstate": "Managed state",
|
"label.managedstate": "Managed state",
|
||||||
"label.management": "Management",
|
"label.management": "Management",
|
||||||
|
"label.managementserverid": "Management server",
|
||||||
|
"label.managementservername": "Management server",
|
||||||
"label.management.ips": "Management IP addresses",
|
"label.management.ips": "Management IP addresses",
|
||||||
"label.management.server": "Management server",
|
"label.management.server": "Management server",
|
||||||
"label.management.servers": "Management servers",
|
"label.management.servers": "Management servers",
|
||||||
@ -1562,6 +1577,7 @@
|
|||||||
"label.patp": "Palo Alto threat profile",
|
"label.patp": "Palo Alto threat profile",
|
||||||
"label.pavr": "Virtual router",
|
"label.pavr": "Virtual router",
|
||||||
"label.payload": "Payload",
|
"label.payload": "Payload",
|
||||||
|
"label.payloadurl": "Payload URL",
|
||||||
"label.pcidevice": "GPU",
|
"label.pcidevice": "GPU",
|
||||||
"label.pending.jobs": "Pending Jobs",
|
"label.pending.jobs": "Pending Jobs",
|
||||||
"label.per.account": "Per Account",
|
"label.per.account": "Per Account",
|
||||||
@ -1709,9 +1725,11 @@
|
|||||||
"label.readonly": "Read-Only",
|
"label.readonly": "Read-Only",
|
||||||
"label.reason": "Reason",
|
"label.reason": "Reason",
|
||||||
"label.reboot": "Reboot",
|
"label.reboot": "Reboot",
|
||||||
|
"label.recent.deliveries": "Recent deliveries",
|
||||||
"label.receivedbytes": "Bytes received",
|
"label.receivedbytes": "Bytes received",
|
||||||
"label.recover.vm": "Recover Instance",
|
"label.recover.vm": "Recover Instance",
|
||||||
"label.recovering": "Recovering",
|
"label.recovering": "Recovering",
|
||||||
|
"label.redeliver": "Redeliver",
|
||||||
"label.redirect": "Redirect to:",
|
"label.redirect": "Redirect to:",
|
||||||
"label.redirecturi": "Redirect URI",
|
"label.redirecturi": "Redirect URI",
|
||||||
"label.redundantrouter": "Redundant router",
|
"label.redundantrouter": "Redundant router",
|
||||||
@ -1790,6 +1808,7 @@
|
|||||||
"label.resources": "Resources",
|
"label.resources": "Resources",
|
||||||
"label.resourcestate": "Resource state",
|
"label.resourcestate": "Resource state",
|
||||||
"label.resourcetype": "Resource type",
|
"label.resourcetype": "Resource type",
|
||||||
|
"label.response": "Response",
|
||||||
"label.restart.network": "Restart Network",
|
"label.restart.network": "Restart Network",
|
||||||
"label.restart.vpc": "Restart VPC",
|
"label.restart.vpc": "Restart VPC",
|
||||||
"label.restartrequired": "Restart required",
|
"label.restartrequired": "Restart required",
|
||||||
@ -1998,6 +2017,7 @@
|
|||||||
"label.sshkeypair": "New SSH key pair",
|
"label.sshkeypair": "New SSH key pair",
|
||||||
"label.sshkeypairs": "SSH key pairs",
|
"label.sshkeypairs": "SSH key pairs",
|
||||||
"label.sslcertificates": "SSL certificates",
|
"label.sslcertificates": "SSL certificates",
|
||||||
|
"label.sslverification": "SSL verification",
|
||||||
"label.standard.us.keyboard": "Standard (US) keyboard",
|
"label.standard.us.keyboard": "Standard (US) keyboard",
|
||||||
"label.start": "Start",
|
"label.start": "Start",
|
||||||
"label.start.date.and.time": "Start date and time",
|
"label.start.date.and.time": "Start date and time",
|
||||||
@ -2118,6 +2138,8 @@
|
|||||||
"label.templatetype": "Template type",
|
"label.templatetype": "Template type",
|
||||||
"label.templateversion": "Template version",
|
"label.templateversion": "Template version",
|
||||||
"label.term.type": "Term type",
|
"label.term.type": "Term type",
|
||||||
|
"label.test": "Test",
|
||||||
|
"label.test.webhook.delivery": "Test Webhook Delivery",
|
||||||
"label.tftpdir": "TFTP root directory",
|
"label.tftpdir": "TFTP root directory",
|
||||||
"label.theme.alert": "The setting is only visible to the current browser. To apply the setting, please download the JSON file and replace its content in the `theme` section of the `config.json` file under the path: `/public/config.json`",
|
"label.theme.alert": "The setting is only visible to the current browser. To apply the setting, please download the JSON file and replace its content in the `theme` section of the `config.json` file under the path: `/public/config.json`",
|
||||||
"label.theme.color": "Theme color",
|
"label.theme.color": "Theme color",
|
||||||
@ -2221,6 +2243,7 @@
|
|||||||
"label.update.to": "updated to",
|
"label.update.to": "updated to",
|
||||||
"label.update.traffic.label": "Update traffic labels",
|
"label.update.traffic.label": "Update traffic labels",
|
||||||
"label.update.vmware.datacenter": "Update VMWare datacenter",
|
"label.update.vmware.datacenter": "Update VMWare datacenter",
|
||||||
|
"label.update.webhook": "Update Webhook",
|
||||||
"label.updating": "Updating",
|
"label.updating": "Updating",
|
||||||
"label.upgrade.router.newer.template": "Upgrade router to use newer Template",
|
"label.upgrade.router.newer.template": "Upgrade router to use newer Template",
|
||||||
"label.upgrading": "Upgrading",
|
"label.upgrading": "Upgrading",
|
||||||
@ -2399,6 +2422,10 @@
|
|||||||
"label.warn": "Warn",
|
"label.warn": "Warn",
|
||||||
"label.warn.upper": "WARN",
|
"label.warn.upper": "WARN",
|
||||||
"label.warning": "Warning",
|
"label.warning": "Warning",
|
||||||
|
"label.webhook": "Webhook",
|
||||||
|
"label.webhooks": "Webhooks",
|
||||||
|
"label.webhookname": "Webhook",
|
||||||
|
"label.webhook.deliveries": "Webhook deliveries",
|
||||||
"label.wednesday": "Wednesday",
|
"label.wednesday": "Wednesday",
|
||||||
"label.weekly": "Weekly",
|
"label.weekly": "Weekly",
|
||||||
"label.welcome": "Welcome",
|
"label.welcome": "Welcome",
|
||||||
@ -2547,6 +2574,7 @@
|
|||||||
"message.add.ip.v6.firewall.rule.failed": "Failed to add IPv6 firewall rule",
|
"message.add.ip.v6.firewall.rule.failed": "Failed to add IPv6 firewall rule",
|
||||||
"message.add.ip.v6.firewall.rule.processing": "Adding IPv6 firewall rule...",
|
"message.add.ip.v6.firewall.rule.processing": "Adding IPv6 firewall rule...",
|
||||||
"message.add.ip.v6.firewall.rule.success": "Added IPv6 firewall rule",
|
"message.add.ip.v6.firewall.rule.success": "Added IPv6 firewall rule",
|
||||||
|
"message.redeliver.webhook.delivery": "Redeliver this Webhook delivery",
|
||||||
"message.remove.ip.v6.firewall.rule.failed": "Failed to remove IPv6 firewall rule",
|
"message.remove.ip.v6.firewall.rule.failed": "Failed to remove IPv6 firewall rule",
|
||||||
"message.remove.ip.v6.firewall.rule.processing": "Removing IPv6 firewall rule...",
|
"message.remove.ip.v6.firewall.rule.processing": "Removing IPv6 firewall rule...",
|
||||||
"message.remove.ip.v6.firewall.rule.success": "Removed IPv6 firewall rule",
|
"message.remove.ip.v6.firewall.rule.success": "Removed IPv6 firewall rule",
|
||||||
@ -2658,12 +2686,14 @@
|
|||||||
"message.confirm.disable.provider": "Please confirm that you would like to disable this provider.",
|
"message.confirm.disable.provider": "Please confirm that you would like to disable this provider.",
|
||||||
"message.confirm.disable.storage": "Please confirm that you want to disable the storage pool.",
|
"message.confirm.disable.storage": "Please confirm that you want to disable the storage pool.",
|
||||||
"message.confirm.disable.vpc.offering": "Are you sure you want to disable this VPC offering?",
|
"message.confirm.disable.vpc.offering": "Are you sure you want to disable this VPC offering?",
|
||||||
|
"message.confirm.disable.webhook": "Please confirm that you want to disable this webhook.",
|
||||||
"message.confirm.enable.autoscale.vmgroup": "Please confirm that you want to enable this autoscale Instance group.",
|
"message.confirm.enable.autoscale.vmgroup": "Please confirm that you want to enable this autoscale Instance group.",
|
||||||
"message.confirm.enable.host": "Please confirm that you want to enable the host.",
|
"message.confirm.enable.host": "Please confirm that you want to enable the host.",
|
||||||
"message.confirm.enable.network.offering": "Are you sure you want to enable this Network offering?",
|
"message.confirm.enable.network.offering": "Are you sure you want to enable this Network offering?",
|
||||||
"message.confirm.enable.provider": "Please confirm that you would like to enable this provider.",
|
"message.confirm.enable.provider": "Please confirm that you would like to enable this provider.",
|
||||||
"message.confirm.enable.storage": "Please confirm that you want to enable the storage pool.",
|
"message.confirm.enable.storage": "Please confirm that you want to enable the storage pool.",
|
||||||
"message.confirm.enable.vpc.offering": "Are you sure you want to enable this VPC offering?",
|
"message.confirm.enable.vpc.offering": "Are you sure you want to enable this VPC offering?",
|
||||||
|
"message.confirm.enable.webhook": "Please confirm that you want to enable this webhook.",
|
||||||
"message.confirm.remove.firewall.rule": "Please confirm that you want to delete this Firewall Rule?",
|
"message.confirm.remove.firewall.rule": "Please confirm that you want to delete this Firewall Rule?",
|
||||||
"message.confirm.remove.ip.range": "Please confirm that you would like to remove this IP range.",
|
"message.confirm.remove.ip.range": "Please confirm that you would like to remove this IP range.",
|
||||||
"message.confirm.remove.network.offering": "Are you sure you want to remove this Network offering?",
|
"message.confirm.remove.network.offering": "Are you sure you want to remove this Network offering?",
|
||||||
@ -2745,6 +2775,8 @@
|
|||||||
"message.delete.vpn.connection": "Please confirm that you want to delete VPN connection.",
|
"message.delete.vpn.connection": "Please confirm that you want to delete VPN connection.",
|
||||||
"message.delete.vpn.customer.gateway": "Please confirm that you want to delete this VPN customer gateway.",
|
"message.delete.vpn.customer.gateway": "Please confirm that you want to delete this VPN customer gateway.",
|
||||||
"message.delete.vpn.gateway": "Please confirm that you want to delete this VPN Gateway.",
|
"message.delete.vpn.gateway": "Please confirm that you want to delete this VPN Gateway.",
|
||||||
|
"message.delete.webhook": "Please confirm that you want to delete this Webhook.",
|
||||||
|
"message.delete.webhook.delivery": "Please confirm that you want to delete this Webhook delivery.",
|
||||||
"message.deleting.firewall.policy": "Deleting Firewall Policy",
|
"message.deleting.firewall.policy": "Deleting Firewall Policy",
|
||||||
"message.deleting.node": "Deleting Node",
|
"message.deleting.node": "Deleting Node",
|
||||||
"message.deleting.vm": "Deleting Instance",
|
"message.deleting.vm": "Deleting Instance",
|
||||||
@ -2782,6 +2814,7 @@
|
|||||||
"message.disable.vpn": "Are you sure you want to disable VPN?",
|
"message.disable.vpn": "Are you sure you want to disable VPN?",
|
||||||
"message.disable.vpn.failed": "Failed to disable VPN.",
|
"message.disable.vpn.failed": "Failed to disable VPN.",
|
||||||
"message.disable.vpn.processing": "Disabling VPN...",
|
"message.disable.vpn.processing": "Disabling VPN...",
|
||||||
|
"message.disable.webhook.ssl.verification": "Disabling SSL verification is not recommended",
|
||||||
"message.discovering.feature": "Discovering features, please wait...",
|
"message.discovering.feature": "Discovering features, please wait...",
|
||||||
"message.disk.offering.created": "Disk offering created:",
|
"message.disk.offering.created": "Disk offering created:",
|
||||||
"message.disk.usage.info.data.points": "Each data point represents the difference in read/write data since the last data point.",
|
"message.disk.usage.info.data.points": "Each data point represents the difference in read/write data since the last data point.",
|
||||||
@ -2956,6 +2989,9 @@
|
|||||||
"message.error.zone.type": "Please select zone type.",
|
"message.error.zone.type": "Please select zone type.",
|
||||||
"message.error.linstor.resourcegroup": "Please enter the Linstor Resource-Group.",
|
"message.error.linstor.resourcegroup": "Please enter the Linstor Resource-Group.",
|
||||||
"message.error.fixed.offering.kvm": "It's not possible to scale up Instances that utilize KVM hypervisor with a fixed compute offering.",
|
"message.error.fixed.offering.kvm": "It's not possible to scale up Instances that utilize KVM hypervisor with a fixed compute offering.",
|
||||||
|
"message.error.create.webhook.local.account": "Account must be provided for creating a Webhook with Local scope.",
|
||||||
|
"message.error.create.webhook.name": "Name must be provided for creating a Webhook.",
|
||||||
|
"message.error.create.webhook.payloadurl": "Payload URL must be provided for creating a Webhook.",
|
||||||
"message.fail.to.delete": "Failed to delete.",
|
"message.fail.to.delete": "Failed to delete.",
|
||||||
"message.failed.to.add": "Failed to add",
|
"message.failed.to.add": "Failed to add",
|
||||||
"message.failed.to.assign.vms": "Failed to assign Instances",
|
"message.failed.to.assign.vms": "Failed to assign Instances",
|
||||||
@ -3215,6 +3251,7 @@
|
|||||||
"message.success.change.affinity.group": "Successfully changed affinity groups",
|
"message.success.change.affinity.group": "Successfully changed affinity groups",
|
||||||
"message.success.change.offering": "Successfully changed offering",
|
"message.success.change.offering": "Successfully changed offering",
|
||||||
"message.success.change.password": "Successfully changed password for User",
|
"message.success.change.password": "Successfully changed password for User",
|
||||||
|
"message.success.clear.webhook.deliveries": "Successfully cleared webhook deliveries",
|
||||||
"message.success.config.backup.schedule": "Successfully configured Instance backup schedule",
|
"message.success.config.backup.schedule": "Successfully configured Instance backup schedule",
|
||||||
"message.success.config.health.monitor": "Successfully Configure Health Monitor",
|
"message.success.config.health.monitor": "Successfully Configure Health Monitor",
|
||||||
"message.success.config.sticky.policy": "Successfully configured sticky policy",
|
"message.success.config.sticky.policy": "Successfully configured sticky policy",
|
||||||
@ -3231,6 +3268,7 @@
|
|||||||
"message.success.create.template": "Successfully created Template",
|
"message.success.create.template": "Successfully created Template",
|
||||||
"message.success.create.user": "Successfully created User",
|
"message.success.create.user": "Successfully created User",
|
||||||
"message.success.create.volume": "Successfully created volume",
|
"message.success.create.volume": "Successfully created volume",
|
||||||
|
"message.success.create.webhook": "Successfully created Webhook",
|
||||||
"message.success.delete": "Successfully deleted",
|
"message.success.delete": "Successfully deleted",
|
||||||
"message.success.delete.acl.rule": "Successfully removed ACL rule",
|
"message.success.delete.acl.rule": "Successfully removed ACL rule",
|
||||||
"message.success.delete.backup.schedule": "Successfully deleted configure Instance backup schedule",
|
"message.success.delete.backup.schedule": "Successfully deleted configure Instance backup schedule",
|
||||||
@ -3316,6 +3354,7 @@
|
|||||||
"message.update.autoscale.vm.profile.failed": "Failed to update autoscale Instance profile",
|
"message.update.autoscale.vm.profile.failed": "Failed to update autoscale Instance profile",
|
||||||
"message.update.condition.failed": "Failed to update condition",
|
"message.update.condition.failed": "Failed to update condition",
|
||||||
"message.update.condition.processing": "Updating condition...",
|
"message.update.condition.processing": "Updating condition...",
|
||||||
|
"message.test.webhook.delivery": "Test delivery to the Webhook with an optional payload",
|
||||||
"message.two.factor.authorization.failed": "Unable to verify 2FA with provided code, please retry.",
|
"message.two.factor.authorization.failed": "Unable to verify 2FA with provided code, please retry.",
|
||||||
"message.two.fa.auth": "Open the two-factor authentication app on your mobile device to view your authentication code.",
|
"message.two.fa.auth": "Open the two-factor authentication app on your mobile device to view your authentication code.",
|
||||||
"message.two.fa.auth.register.account": "Open the two-factor authentication application and scan the QR code add the User Account.",
|
"message.two.fa.auth.register.account": "Open the two-factor authentication application and scan the QR code add the User Account.",
|
||||||
@ -3404,6 +3443,7 @@
|
|||||||
"message.warn.filetype": "jpg, jpeg, png, bmp and svg are the only supported image formats.",
|
"message.warn.filetype": "jpg, jpeg, png, bmp and svg are the only supported image formats.",
|
||||||
"message.warn.importing.instance.without.nic": "WARNING: This Instance is being imported without NICs and many Network resources will not be available. Consider creating a NIC via vCenter before importing or as soon as the Instance is imported.",
|
"message.warn.importing.instance.without.nic": "WARNING: This Instance is being imported without NICs and many Network resources will not be available. Consider creating a NIC via vCenter before importing or as soon as the Instance is imported.",
|
||||||
"message.warn.zone.mtu.update": "Please note that this limit won't affect pre-existing Network’s MTU settings",
|
"message.warn.zone.mtu.update": "Please note that this limit won't affect pre-existing Network’s MTU settings",
|
||||||
|
"message.webhook.deliveries.time.filter": "Webhook deliveries list can be filtered based on date-time. Select 'Custom' for specifying start and end date range.",
|
||||||
"message.zone.creation.complete": "Zone creation complete.",
|
"message.zone.creation.complete": "Zone creation complete.",
|
||||||
"message.zone.detail.description": "Populate zone details.",
|
"message.zone.detail.description": "Populate zone details.",
|
||||||
"message.zone.detail.hint": "A zone is the largest organizational unit in CloudStack, and it typically corresponds to a single datacenter. Zones provide physical isolation and redundancy. A zone consists of one or more pods (each of which contains hosts and primary storage servers) and a secondary storage server which is shared by all pods in the zone.",
|
"message.zone.detail.hint": "A zone is the largest organizational unit in CloudStack, and it typically corresponds to a single datacenter. Zones provide physical isolation and redundancy. A zone consists of one or more pods (each of which contains hosts and primary storage servers) and a secondary storage server which is shared by all pods in the zone.",
|
||||||
|
|||||||
@ -22,12 +22,12 @@
|
|||||||
:model="form"
|
:model="form"
|
||||||
@finish="handleSubmit"
|
@finish="handleSubmit"
|
||||||
layout="vertical">
|
layout="vertical">
|
||||||
<div v-show="!(!showStartDate || !showEndDate) || (!showStartDate && !showEndDate)">
|
<div v-show="showAllDataOption && (!(!showStartDate || !showEndDate) || (!showStartDate && !showEndDate))">
|
||||||
<a-form-item :label="$t('label.all.available.data')" ref="allData" name="allData">
|
<a-form-item :label="$t('label.all.available.data')" ref="allData" name="allData">
|
||||||
<a-switch v-model:checked="allDataIsChecked" @change="onToggleAllData"/>
|
<a-switch v-model:checked="allDataIsChecked" @change="onToggleAllData"/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<div v-show="showAllDataAlert">
|
<div v-show="showAllDataAlert">
|
||||||
<a-alert :message="$t('message.alert.show.all.stats.data')" banner />
|
<a-alert :message="allDataMessage" banner />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-show="showStartDate">
|
<div v-show="showStartDate">
|
||||||
@ -64,7 +64,7 @@ import { ref, reactive, toRaw } from 'vue'
|
|||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'FilterStats',
|
name: 'DateTimeFilter',
|
||||||
emits: ['closeAction', 'onSubmit'],
|
emits: ['closeAction', 'onSubmit'],
|
||||||
props: {
|
props: {
|
||||||
startDateProp: {
|
startDateProp: {
|
||||||
@ -74,6 +74,14 @@ export default {
|
|||||||
endDateProp: {
|
endDateProp: {
|
||||||
type: [Date, String, Number],
|
type: [Date, String, Number],
|
||||||
required: false
|
required: false
|
||||||
|
},
|
||||||
|
showAllDataOption: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
allDataMessage: {
|
||||||
|
type: String,
|
||||||
|
value: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -103,6 +103,12 @@
|
|||||||
<div v-else-if="$route.meta.name === 'computeoffering' && offeringDetails.includes(item)">
|
<div v-else-if="$route.meta.name === 'computeoffering' && offeringDetails.includes(item)">
|
||||||
{{ dataResource.serviceofferingdetails[item] }}
|
{{ dataResource.serviceofferingdetails[item] }}
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else-if="item === 'headers'" style="white-space: pre-line;">
|
||||||
|
{{ dataResource[item] }}
|
||||||
|
</div>
|
||||||
|
<div v-else-if="item === 'payload'" style="white-space: pre-wrap;">
|
||||||
|
{{ JSON.stringify(JSON.parse(dataResource[item]), null, 4) || dataResource[item] }}
|
||||||
|
</div>
|
||||||
<div v-else>{{ dataResource[item] }}</div>
|
<div v-else>{{ dataResource[item] }}</div>
|
||||||
</div>
|
</div>
|
||||||
</a-list-item>
|
</a-list-item>
|
||||||
@ -120,6 +126,13 @@
|
|||||||
<div>{{ dataResource[item] }}</div>
|
<div>{{ dataResource[item] }}</div>
|
||||||
</div>
|
</div>
|
||||||
</a-list-item>
|
</a-list-item>
|
||||||
|
<a-list-item v-else-if="['startdate', 'enddate'].includes(item)">
|
||||||
|
<div>
|
||||||
|
<strong>{{ $t('label.' + item.replace('date', '.date.and.time'))}}</strong>
|
||||||
|
<br/>
|
||||||
|
<div>{{ $toLocaleDate(dataResource[item]) }}</div>
|
||||||
|
</div>
|
||||||
|
</a-list-item>
|
||||||
</template>
|
</template>
|
||||||
<HostInfo :resource="dataResource" v-if="$route.meta.name === 'host' && 'listHosts' in $store.getters.apis" />
|
<HostInfo :resource="dataResource" v-if="$route.meta.name === 'host' && 'listHosts' in $store.getters.apis" />
|
||||||
<DedicateData :resource="dataResource" v-if="dedicatedSectionActive" />
|
<DedicateData :resource="dataResource" v-if="dedicatedSectionActive" />
|
||||||
@ -174,7 +187,12 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
customDisplayItems () {
|
customDisplayItems () {
|
||||||
return ['ip6routes', 'privatemtu', 'publicmtu', 'provider']
|
var items = ['ip6routes', 'privatemtu', 'publicmtu', 'provider']
|
||||||
|
if (this.$route.meta.name === 'webhookdeliveries') {
|
||||||
|
items.push('startdate')
|
||||||
|
items.push('enddate')
|
||||||
|
}
|
||||||
|
return items
|
||||||
},
|
},
|
||||||
vnfAccessMethods () {
|
vnfAccessMethods () {
|
||||||
if (this.resource.templatetype === 'VNF' && ['vm', 'vnfapp'].includes(this.$route.meta.name)) {
|
if (this.resource.templatetype === 'VNF' && ['vm', 'vnfapp'].includes(this.$route.meta.name)) {
|
||||||
|
|||||||
@ -140,6 +140,12 @@
|
|||||||
<status class="status" :text="resource.resourcestate" displayText/>
|
<status class="status" :text="resource.resourcestate" displayText/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="resource-detail-item" v-if="('success' in resource) && $route.meta.name === 'webhookdeliveries'">
|
||||||
|
<div class="resource-detail-item__label">{{ $t('label.success') }}</div>
|
||||||
|
<div class="resource-detail-item__details">
|
||||||
|
<status class="status" :text="resource.success ? 'success' : 'error'"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="resource-detail-item" v-if="resource.id">
|
<div class="resource-detail-item" v-if="resource.id">
|
||||||
<div class="resource-detail-item__label">{{ $t('label.id') }}</div>
|
<div class="resource-detail-item__label">{{ $t('label.id') }}</div>
|
||||||
@ -672,6 +678,22 @@
|
|||||||
<span v-else>{{ resource.domain || resource.domainid }}</span>
|
<span v-else>{{ resource.domain || resource.domainid }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="resource-detail-item" v-if="resource.payloadurl">
|
||||||
|
<div class="resource-detail-item__label">{{ $t('label.payloadurl') }}</div>
|
||||||
|
<div class="resource-detail-item__details">
|
||||||
|
<link-outlined/>
|
||||||
|
<a v-if="!isStatic" :href="resource.payloadurl">{{ resource.payloadurl }}</a>
|
||||||
|
<span v-else>{{ resource.payloadurl }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="resource-detail-item" v-if="resource.webhookid">
|
||||||
|
<div class="resource-detail-item__label">{{ $t('label.webhook') }}</div>
|
||||||
|
<div class="resource-detail-item__details">
|
||||||
|
<node-index-outlined />
|
||||||
|
<router-link v-if="!isStatic && $router.resolve('/webhook/' + resource.webhookid).matched[0].redirect !== '/exception/404'" :to="{ path: '/webhook/' + resource.webhookid }">{{ resource.webhookname || resource.webhookid }}</router-link>
|
||||||
|
<span v-else>{{ resource.webhookname || resource.webhookid }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="resource-detail-item" v-if="resource.managementserverid">
|
<div class="resource-detail-item" v-if="resource.managementserverid">
|
||||||
<div class="resource-detail-item__label">{{ $t('label.management.servers') }}</div>
|
<div class="resource-detail-item__label">{{ $t('label.management.servers') }}</div>
|
||||||
<div class="resource-detail-item__details">
|
<div class="resource-detail-item__details">
|
||||||
|
|||||||
@ -23,7 +23,7 @@
|
|||||||
:dataSource="items"
|
:dataSource="items"
|
||||||
:rowKey="(record, idx) => record.id || record.name || record.usageType || idx + '-' + Math.random()"
|
:rowKey="(record, idx) => record.id || record.name || record.usageType || idx + '-' + Math.random()"
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
:rowSelection=" enableGroupAction() || $route.name === 'event' ? {selectedRowKeys: selectedRowKeys, onChange: onSelectChange, columnWidth: 30} : null"
|
:rowSelection="explicitlyAllowRowSelection || enableGroupAction() || $route.name === 'event' ? {selectedRowKeys: selectedRowKeys, onChange: onSelectChange, columnWidth: 30} : null"
|
||||||
:rowClassName="getRowClassName"
|
:rowClassName="getRowClassName"
|
||||||
style="overflow-y: auto"
|
style="overflow-y: auto"
|
||||||
>
|
>
|
||||||
@ -364,12 +364,47 @@
|
|||||||
<status :text="record.enabled ? record.enabled.toString() : 'false'" />
|
<status :text="record.enabled ? record.enabled.toString() : 'false'" />
|
||||||
{{ record.enabled ? 'Enabled' : 'Disabled' }}
|
{{ record.enabled ? 'Enabled' : 'Disabled' }}
|
||||||
</template>
|
</template>
|
||||||
<template v-if="['created', 'sent'].includes(column.key)">
|
<template v-if="['created', 'sent'].includes(column.key) || (['startdate'].includes(column.key) && ['webhook'].includes($route.path.split('/')[1]))">
|
||||||
{{ $toLocaleDate(text) }}
|
{{ $toLocaleDate(text) }}
|
||||||
</template>
|
</template>
|
||||||
<template v-if="['startdate', 'enddate'].includes(column.key) && ['vm', 'vnfapp'].includes($route.path.split('/')[1])">
|
<template v-if="['startdate', 'enddate'].includes(column.key) && ['vm', 'vnfapp'].includes($route.path.split('/')[1])">
|
||||||
{{ getDateAtTimeZone(text, record.timezone) }}
|
{{ getDateAtTimeZone(text, record.timezone) }}
|
||||||
</template>
|
</template>
|
||||||
|
<template v-if="column.key === 'payloadurl'">
|
||||||
|
<copy-label :label="text" />
|
||||||
|
</template>
|
||||||
|
<template v-if="column.key === 'eventtype'">
|
||||||
|
<router-link v-if="$router.resolve('/event/' + record.eventid).matched[0].redirect !== '/exception/404'" :to="{ path: '/event/' + record.eventid }">{{ text }}</router-link>
|
||||||
|
<span v-else>{{ text }}</span>
|
||||||
|
</template>
|
||||||
|
<template v-if="column.key === 'managementservername'">
|
||||||
|
<router-link v-if="$router.resolve('/managementserver/' + record.managementserverid).matched[0].redirect !== '/exception/404'" :to="{ path: '/managementserver/' + record.managementserverid }">{{ text }}</router-link>
|
||||||
|
<router-link v-else-if="$router.resolve('/managementservers/' + record.managementserverid).matched[0].redirect !== '/exception/404'" :to="{ path: '/managementservers/' + record.managementserverid }">{{ text }}</router-link>
|
||||||
|
<span v-else>{{ text }}</span>
|
||||||
|
</template>
|
||||||
|
<template v-if="column.key === 'payload'">
|
||||||
|
<router-link v-if="$router.resolve('/webhookdeliveries/' + record.id).matched[0].redirect !== '/exception/404'" :to="{ path: '/webhookdeliveries/' + record.id }">{{ getTrimmedText(text, 48) }}</router-link>
|
||||||
|
<span v-else> {{ getTrimmedText(text, 48) }} </span>
|
||||||
|
<QuickView
|
||||||
|
style="margin-left: 5px"
|
||||||
|
:actions="actions"
|
||||||
|
:resource="record"
|
||||||
|
:enabled="quickViewEnabled() && actions.length > 0"
|
||||||
|
@exec-action="$parent.execAction"/>
|
||||||
|
</template>
|
||||||
|
<template v-if="column.key === 'webhookname'">
|
||||||
|
<router-link v-if="$router.resolve('/webhook/' + record.webhookid).matched[0].redirect !== '/exception/404'" :to="{ path: '/webhook/' + record.webhookid }">{{ text }}</router-link>
|
||||||
|
<span v-else> {{ text }} </span>
|
||||||
|
</template>
|
||||||
|
<template v-if="column.key === 'success'">
|
||||||
|
<status :text="text ? 'success' : 'error'" />
|
||||||
|
</template>
|
||||||
|
<template v-if="column.key === 'response'">
|
||||||
|
<span> {{ getTrimmedText(text, 48) }} </span>
|
||||||
|
</template>
|
||||||
|
<template v-if="column.key === 'duration' && ['webhook', 'webhookdeliveries'].includes($route.path.split('/')[1])">
|
||||||
|
<span> {{ getDuration(record.startdate, record.enddate) }} </span>
|
||||||
|
</template>
|
||||||
<template v-if="column.key === 'order'">
|
<template v-if="column.key === 'order'">
|
||||||
<div class="shift-btns">
|
<div class="shift-btns">
|
||||||
<a-tooltip :name="text" placement="top">
|
<a-tooltip :name="text" placement="top">
|
||||||
@ -526,6 +561,10 @@ export default {
|
|||||||
actions: {
|
actions: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => []
|
||||||
|
},
|
||||||
|
explicitlyAllowRowSelection: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
inject: ['parentFetchData', 'parentToggleLoading'],
|
inject: ['parentFetchData', 'parentToggleLoading'],
|
||||||
@ -598,14 +637,15 @@ export default {
|
|||||||
'/project', '/account', 'buckets', 'objectstore',
|
'/project', '/account', 'buckets', 'objectstore',
|
||||||
'/zone', '/pod', '/cluster', '/host', '/storagepool', '/imagestore', '/systemvm', '/router', '/ilbvm', '/annotation',
|
'/zone', '/pod', '/cluster', '/host', '/storagepool', '/imagestore', '/systemvm', '/router', '/ilbvm', '/annotation',
|
||||||
'/computeoffering', '/systemoffering', '/diskoffering', '/backupoffering', '/networkoffering', '/vpcoffering',
|
'/computeoffering', '/systemoffering', '/diskoffering', '/backupoffering', '/networkoffering', '/vpcoffering',
|
||||||
'/tungstenfabric', '/oauthsetting', '/guestos', '/guestoshypervisormapping'].join('|'))
|
'/tungstenfabric', '/oauthsetting', '/guestos', '/guestoshypervisormapping', '/webhook', 'webhookdeliveries'].join('|'))
|
||||||
.test(this.$route.path)
|
.test(this.$route.path)
|
||||||
},
|
},
|
||||||
enableGroupAction () {
|
enableGroupAction () {
|
||||||
return ['vm', 'alert', 'vmgroup', 'ssh', 'userdata', 'affinitygroup', 'autoscalevmgroup', 'volume', 'snapshot',
|
return ['vm', 'alert', 'vmgroup', 'ssh', 'userdata', 'affinitygroup', 'autoscalevmgroup', 'volume', 'snapshot',
|
||||||
'vmsnapshot', 'backup', 'guestnetwork', 'vpc', 'publicip', 'vpnuser', 'vpncustomergateway', 'vnfapp',
|
'vmsnapshot', 'backup', 'guestnetwork', 'vpc', 'publicip', 'vpnuser', 'vpncustomergateway', 'vnfapp',
|
||||||
'project', 'account', 'systemvm', 'router', 'computeoffering', 'systemoffering',
|
'project', 'account', 'systemvm', 'router', 'computeoffering', 'systemoffering',
|
||||||
'diskoffering', 'backupoffering', 'networkoffering', 'vpcoffering', 'ilbvm', 'kubernetes', 'comment', 'buckets'
|
'diskoffering', 'backupoffering', 'networkoffering', 'vpcoffering', 'ilbvm', 'kubernetes', 'comment', 'buckets',
|
||||||
|
'webhook', 'webhookdeliveries'
|
||||||
].includes(this.$route.name)
|
].includes(this.$route.name)
|
||||||
},
|
},
|
||||||
getDateAtTimeZone (date, timezone) {
|
getDateAtTimeZone (date, timezone) {
|
||||||
@ -898,6 +938,19 @@ export default {
|
|||||||
case 'SecondaryStorageVm': return '/systemvm/'
|
case 'SecondaryStorageVm': return '/systemvm/'
|
||||||
default: return '/vm/'
|
default: return '/vm/'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
getTrimmedText (text, length) {
|
||||||
|
if (!text) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
return (text.length <= length) ? text : (text.substring(0, length - 3) + '...')
|
||||||
|
},
|
||||||
|
getDuration (startdate, enddate) {
|
||||||
|
if (!startdate || !enddate) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
var duration = Date.parse(enddate) - Date.parse(startdate)
|
||||||
|
return (duration > 0 ? duration / 1000.0 : 0) + ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -85,6 +85,9 @@
|
|||||||
</span>
|
</span>
|
||||||
<block-outlined v-else style="margin-right: 5px" />
|
<block-outlined v-else style="margin-right: 5px" />
|
||||||
</span>
|
</span>
|
||||||
|
<span v-if="(field.name.startsWith('managementserver'))">
|
||||||
|
<status :text="opt.state" />
|
||||||
|
</span>
|
||||||
{{ $t(opt.path || opt.name) }}
|
{{ $t(opt.path || opt.name) }}
|
||||||
</div>
|
</div>
|
||||||
</a-select-option>
|
</a-select-option>
|
||||||
@ -154,14 +157,17 @@
|
|||||||
<script>
|
<script>
|
||||||
import { ref, reactive, toRaw } from 'vue'
|
import { ref, reactive, toRaw } from 'vue'
|
||||||
import { api } from '@/api'
|
import { api } from '@/api'
|
||||||
|
import { isAdmin } from '@/role'
|
||||||
import TooltipButton from '@/components/widgets/TooltipButton'
|
import TooltipButton from '@/components/widgets/TooltipButton'
|
||||||
import ResourceIcon from '@/components/view/ResourceIcon'
|
import ResourceIcon from '@/components/view/ResourceIcon'
|
||||||
|
import Status from '@/components/widgets/Status'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SearchView',
|
name: 'SearchView',
|
||||||
components: {
|
components: {
|
||||||
TooltipButton,
|
TooltipButton,
|
||||||
ResourceIcon
|
ResourceIcon,
|
||||||
|
Status
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
searchFilters: {
|
searchFilters: {
|
||||||
@ -206,13 +212,7 @@ export default {
|
|||||||
if (to && to.query && 'q' in to.query) {
|
if (to && to.query && 'q' in to.query) {
|
||||||
this.searchQuery = to.query.q
|
this.searchQuery = to.query.q
|
||||||
}
|
}
|
||||||
this.isFiltered = false
|
this.updateIsFiltered()
|
||||||
this.searchFilters.some(item => {
|
|
||||||
if (this.searchParams[item]) {
|
|
||||||
this.isFiltered = true
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
@ -220,6 +220,7 @@ export default {
|
|||||||
if (this.$route && this.$route.query && 'q' in this.$route.query) {
|
if (this.$route && this.$route.query && 'q' in this.$route.query) {
|
||||||
this.searchQuery = this.$route.query.q
|
this.searchQuery = this.$route.query.q
|
||||||
}
|
}
|
||||||
|
this.updateIsFiltered()
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
styleSearch () {
|
styleSearch () {
|
||||||
@ -285,7 +286,7 @@ export default {
|
|||||||
}
|
}
|
||||||
if (['zoneid', 'domainid', 'imagestoreid', 'storageid', 'state', 'account', 'hypervisor', 'level',
|
if (['zoneid', 'domainid', 'imagestoreid', 'storageid', 'state', 'account', 'hypervisor', 'level',
|
||||||
'clusterid', 'podid', 'groupid', 'entitytype', 'accounttype', 'systemvmtype', 'scope', 'provider',
|
'clusterid', 'podid', 'groupid', 'entitytype', 'accounttype', 'systemvmtype', 'scope', 'provider',
|
||||||
'type'].includes(item)
|
'type', 'scope', 'managementserverid'].includes(item)
|
||||||
) {
|
) {
|
||||||
type = 'list'
|
type = 'list'
|
||||||
} else if (item === 'tags') {
|
} else if (item === 'tags') {
|
||||||
@ -319,6 +320,13 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (arrayField.includes('scope')) {
|
||||||
|
const scopeIndex = this.fields.findIndex(item => item.name === 'scope')
|
||||||
|
this.fields[scopeIndex].loading = true
|
||||||
|
this.fields[scopeIndex].opts = this.fetchScope()
|
||||||
|
this.fields[scopeIndex].loading = false
|
||||||
|
}
|
||||||
|
|
||||||
if (arrayField.includes('state')) {
|
if (arrayField.includes('state')) {
|
||||||
const stateIndex = this.fields.findIndex(item => item.name === 'state')
|
const stateIndex = this.fields.findIndex(item => item.name === 'state')
|
||||||
this.fields[stateIndex].loading = true
|
this.fields[stateIndex].loading = true
|
||||||
@ -397,6 +405,7 @@ export default {
|
|||||||
let podIndex = -1
|
let podIndex = -1
|
||||||
let clusterIndex = -1
|
let clusterIndex = -1
|
||||||
let groupIndex = -1
|
let groupIndex = -1
|
||||||
|
let managementServerIdIndex = -1
|
||||||
|
|
||||||
if (arrayField.includes('type')) {
|
if (arrayField.includes('type')) {
|
||||||
if (this.$route.path === '/alert') {
|
if (this.$route.path === '/alert') {
|
||||||
@ -464,6 +473,12 @@ export default {
|
|||||||
promises.push(await this.fetchInstanceGroups(searchKeyword))
|
promises.push(await this.fetchInstanceGroups(searchKeyword))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (arrayField.includes('managementserverid')) {
|
||||||
|
managementServerIdIndex = this.fields.findIndex(item => item.name === 'managementserverid')
|
||||||
|
this.fields[managementServerIdIndex].loading = true
|
||||||
|
promises.push(await this.fetchManagementServers(searchKeyword))
|
||||||
|
}
|
||||||
|
|
||||||
Promise.all(promises).then(response => {
|
Promise.all(promises).then(response => {
|
||||||
if (typeIndex > -1) {
|
if (typeIndex > -1) {
|
||||||
const types = response.filter(item => item.type === 'type')
|
const types = response.filter(item => item.type === 'type')
|
||||||
@ -525,6 +540,12 @@ export default {
|
|||||||
this.fields[groupIndex].opts = this.sortArray(groups[0].data)
|
this.fields[groupIndex].opts = this.sortArray(groups[0].data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (managementServerIdIndex > -1) {
|
||||||
|
const managementServers = response.filter(item => item.type === 'managementserverid')
|
||||||
|
if (managementServers && managementServers.length > 0) {
|
||||||
|
this.fields[managementServerIdIndex].opts = this.sortArray(managementServers[0].data)
|
||||||
|
}
|
||||||
|
}
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
if (typeIndex > -1) {
|
if (typeIndex > -1) {
|
||||||
this.fields[typeIndex].loading = false
|
this.fields[typeIndex].loading = false
|
||||||
@ -550,6 +571,9 @@ export default {
|
|||||||
if (groupIndex > -1) {
|
if (groupIndex > -1) {
|
||||||
this.fields[groupIndex].loading = false
|
this.fields[groupIndex].loading = false
|
||||||
}
|
}
|
||||||
|
if (managementServerIdIndex > -1) {
|
||||||
|
this.fields[managementServerIdIndex].loading = false
|
||||||
|
}
|
||||||
this.fillFormFieldValues()
|
this.fillFormFieldValues()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@ -757,6 +781,19 @@ export default {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
fetchManagementServers (searchKeyword) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
api('listManagementServers', { listAll: true, keyword: searchKeyword }).then(json => {
|
||||||
|
const managementservers = json.listmanagementserversresponse.managementserver
|
||||||
|
resolve({
|
||||||
|
type: 'managementserverid',
|
||||||
|
data: managementservers
|
||||||
|
})
|
||||||
|
}).catch(error => {
|
||||||
|
reject(error.response.headers['x-description'])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
fetchGuestNetworkTypes () {
|
fetchGuestNetworkTypes () {
|
||||||
const types = []
|
const types = []
|
||||||
if (this.apiName.indexOf('listNetworks') > -1) {
|
if (this.apiName.indexOf('listNetworks') > -1) {
|
||||||
@ -877,9 +914,30 @@ export default {
|
|||||||
}
|
}
|
||||||
return types
|
return types
|
||||||
},
|
},
|
||||||
|
fetchScope () {
|
||||||
|
const scope = []
|
||||||
|
if (this.apiName.indexOf('listWebhooks') > -1) {
|
||||||
|
scope.push({
|
||||||
|
id: 'Local',
|
||||||
|
name: 'label.local'
|
||||||
|
})
|
||||||
|
scope.push({
|
||||||
|
id: 'Domain',
|
||||||
|
name: 'label.domain'
|
||||||
|
})
|
||||||
|
if (isAdmin()) {
|
||||||
|
scope.push({
|
||||||
|
id: 'Global',
|
||||||
|
name: 'label.global'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return scope
|
||||||
|
},
|
||||||
fetchState () {
|
fetchState () {
|
||||||
|
var state = []
|
||||||
if (this.apiName.includes('listVolumes')) {
|
if (this.apiName.includes('listVolumes')) {
|
||||||
return [
|
state = [
|
||||||
{
|
{
|
||||||
id: 'Allocated',
|
id: 'Allocated',
|
||||||
name: 'label.allocated'
|
name: 'label.allocated'
|
||||||
@ -906,7 +964,7 @@ export default {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
} else if (this.apiName.includes('listKubernetesClusters')) {
|
} else if (this.apiName.includes('listKubernetesClusters')) {
|
||||||
return [
|
state = [
|
||||||
{
|
{
|
||||||
id: 'Created',
|
id: 'Created',
|
||||||
name: 'label.created'
|
name: 'label.created'
|
||||||
@ -956,8 +1014,19 @@ export default {
|
|||||||
name: 'label.error'
|
name: 'label.error'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
} else if (this.apiName.indexOf('listWebhooks') > -1) {
|
||||||
|
state = [
|
||||||
|
{
|
||||||
|
id: 'Enabled',
|
||||||
|
name: 'label.enabled'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'Disabled',
|
||||||
|
name: 'label.disabled'
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
return []
|
return state
|
||||||
},
|
},
|
||||||
fetchEntityType () {
|
fetchEntityType () {
|
||||||
const entityType = []
|
const entityType = []
|
||||||
@ -1052,6 +1121,13 @@ export default {
|
|||||||
},
|
},
|
||||||
changeFilter (filter) {
|
changeFilter (filter) {
|
||||||
this.$emit('change-filter', filter)
|
this.$emit('change-filter', filter)
|
||||||
|
},
|
||||||
|
updateIsFiltered () {
|
||||||
|
this.isFiltered = this.searchFilters.some(item => {
|
||||||
|
if (this.searchParams[item]) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,9 +22,10 @@
|
|||||||
:title="$t('label.select.period')"
|
:title="$t('label.select.period')"
|
||||||
:maskClosable="false"
|
:maskClosable="false"
|
||||||
:footer="null">
|
:footer="null">
|
||||||
<filter-stats
|
<date-time-filter
|
||||||
:startDateProp="startDate"
|
:startDateProp="startDate"
|
||||||
:endDateProp="endDate"
|
:endDateProp="endDate"
|
||||||
|
:allDataMessage="$t('message.alert.show.all.stats.data')"
|
||||||
@closeAction="closeAction"
|
@closeAction="closeAction"
|
||||||
@onSubmit="handleSubmit"/>
|
@onSubmit="handleSubmit"/>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
@ -251,7 +252,7 @@
|
|||||||
import { api } from '@/api'
|
import { api } from '@/api'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import 'chartjs-adapter-moment'
|
import 'chartjs-adapter-moment'
|
||||||
import FilterStats from './stats/FilterStats'
|
import DateTimeFilter from './DateTimeFilter'
|
||||||
import ResourceStatsInfo from './stats/ResourceStatsInfo'
|
import ResourceStatsInfo from './stats/ResourceStatsInfo'
|
||||||
import ResourceStatsLineChart from './stats/ResourceStatsLineChart'
|
import ResourceStatsLineChart from './stats/ResourceStatsLineChart'
|
||||||
|
|
||||||
@ -267,7 +268,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
FilterStats,
|
DateTimeFilter,
|
||||||
ResourceStatsInfo,
|
ResourceStatsInfo,
|
||||||
ResourceStatsLineChart
|
ResourceStatsLineChart
|
||||||
},
|
},
|
||||||
|
|||||||
278
ui/src/components/view/TestWebhookDeliveryView.vue
Normal file
278
ui/src/components/view/TestWebhookDeliveryView.vue
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="form-layout">
|
||||||
|
<div v-if="(resource || payloadUrl)">
|
||||||
|
<a-divider />
|
||||||
|
<a-collapse
|
||||||
|
v-model:activeKey="collapseKey">
|
||||||
|
<a-collapse-panel
|
||||||
|
:showArrow="isResponseNotEmpty"
|
||||||
|
key="1"
|
||||||
|
:collapsible="(resource || payloadUrl) ? 'enabled' : 'disabled'">
|
||||||
|
<template #header>
|
||||||
|
<a-row style="width: 100%;">
|
||||||
|
<a-col :span="22">
|
||||||
|
<tooltip-label :title="$t('label.status')" v-if="isNotShowStatus"/>
|
||||||
|
<status class="status" :text="response.success ? 'success' : 'error'" displayText v-else/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="2">
|
||||||
|
<div v-if="showActions">
|
||||||
|
<a-spin :spinning="loading" size="small" v-if="loading" />
|
||||||
|
<div v-else>
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
@click.stop="testWebhookDelivery">
|
||||||
|
<render-icon icon="reload-outlined" />
|
||||||
|
<div class="ant-btn__progress-overlay" :style="computedOverlayStyle"></div>
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</template>
|
||||||
|
<div v-if="isResponseNotEmpty">
|
||||||
|
<a-row>
|
||||||
|
<a-col :span="8">
|
||||||
|
<div class="response-detail-item" v-if="('success' in response)">
|
||||||
|
<div class="response-detail-item__label"><strong>{{ $t('label.success') }}</strong></div>
|
||||||
|
<div class="response-detail-item__details">
|
||||||
|
<status class="status" :text="response.success ? 'success' : 'error'"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<div class="response-detail-item" v-if="response.startdate && response.enddate">
|
||||||
|
<div class="response-detail-item__label"><strong>{{ $t('label.duration') }}</strong></div>
|
||||||
|
<div class="response-detail-item__details">
|
||||||
|
{{ responseDuration }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="8">
|
||||||
|
<div class="response-detail-item" v-if="response.managementserverid">
|
||||||
|
<div class="response-detail-item__label"><strong>{{ $t('label.managementserverid') }}</strong></div>
|
||||||
|
<div class="response-detail-item__details">
|
||||||
|
{{ response.managementservername }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-alert
|
||||||
|
:type="response.success ? 'success' : 'error'"
|
||||||
|
:showIcon="true"
|
||||||
|
:message="$t('label.response')"
|
||||||
|
:description="response.response ? response.response : 'Empty response'" />
|
||||||
|
</div>
|
||||||
|
</a-collapse-panel>
|
||||||
|
</a-collapse>
|
||||||
|
<a-divider />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { api } from '@/api'
|
||||||
|
import TooltipLabel from '@/components/widgets/TooltipLabel'
|
||||||
|
import Status from '@/components/widgets/Status'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TestWebhookDeliveryView',
|
||||||
|
components: {
|
||||||
|
TooltipLabel,
|
||||||
|
Status
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
resource: {
|
||||||
|
type: Object
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
payloadUrl: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
sslVerification: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
secretKey: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
showActions: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
response: {},
|
||||||
|
collapseKey: undefined,
|
||||||
|
loading: false,
|
||||||
|
testDeliveryInterval: null,
|
||||||
|
testDeliveryIntervalCouter: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeCreate () {
|
||||||
|
this.timedDeliveryWait = 4000
|
||||||
|
},
|
||||||
|
beforeUnmount () {
|
||||||
|
this.resetTestDeliveryInterval()
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isResponseNotEmpty () {
|
||||||
|
return this.response && Object.keys(this.response).length > 0
|
||||||
|
},
|
||||||
|
isNotShowStatus () {
|
||||||
|
return !this.isResponseNotEmpty ||
|
||||||
|
this.collapseKey === '1' ||
|
||||||
|
(Array.isArray(this.collapseKey) &&
|
||||||
|
this.collapseKey.length > 0 &&
|
||||||
|
this.collapseKey[0] === '1')
|
||||||
|
},
|
||||||
|
responseDuration () {
|
||||||
|
if (!this.response.startdate || !this.response.enddate) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
var duration = Date.parse(this.response.enddate) - Date.parse(this.response.startdate)
|
||||||
|
return (duration > 0 ? duration / 1000.0 : 0) + ''
|
||||||
|
},
|
||||||
|
computedOverlayStyle () {
|
||||||
|
var opacity = this.testDeliveryIntervalCouter <= 10.0 ? 0 : 0.3
|
||||||
|
var width = this.testDeliveryIntervalCouter
|
||||||
|
return 'opacity: ' + opacity + '; width: ' + width + '%;'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
validateUrl (url) {
|
||||||
|
const urlPattern = /^(http|https):\/\/[^ "]+$/
|
||||||
|
urlPattern.test(url)
|
||||||
|
},
|
||||||
|
resetTestDeliveryInterval () {
|
||||||
|
if (this.testDeliveryInterval) {
|
||||||
|
clearInterval(this.testDeliveryInterval)
|
||||||
|
}
|
||||||
|
this.testDeliveryIntervalCouter = 0
|
||||||
|
},
|
||||||
|
testWebhookDelivery () {
|
||||||
|
this.resetTestDeliveryInterval()
|
||||||
|
this.response = {}
|
||||||
|
this.loading = true
|
||||||
|
this.$emit('change-loading', this.loading)
|
||||||
|
var params = {}
|
||||||
|
if (this.resource) {
|
||||||
|
params.webhookid = this.resource.id
|
||||||
|
}
|
||||||
|
if (this.payload) {
|
||||||
|
params.payload = this.payload
|
||||||
|
}
|
||||||
|
if (this.payloadUrl) {
|
||||||
|
params.payloadUrl = this.payloadUrl
|
||||||
|
}
|
||||||
|
if (this.sslVerification) {
|
||||||
|
params.payload = this.sslVerification
|
||||||
|
}
|
||||||
|
if (this.secretKey) {
|
||||||
|
params.secretKey = this.secretKey
|
||||||
|
}
|
||||||
|
api('executeWebhookDelivery', params).then(response => {
|
||||||
|
this.response = response.executewebhookdeliveryresponse.webhookdelivery
|
||||||
|
this.$emit('update-success', response.success)
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
this.$emit('change-loading', this.loading)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
getNormalizedPayloadUrl () {
|
||||||
|
if (!this.payloadUrl || this.payloadUrl === '' || this.validateUrl(this.payloadUrl)) {
|
||||||
|
return this.payloadUrl
|
||||||
|
}
|
||||||
|
return 'http://' + this.payloadUrl
|
||||||
|
},
|
||||||
|
executeTestWebhookDeliveryOrReset () {
|
||||||
|
const url = this.getNormalizedPayloadUrl()
|
||||||
|
if (url) {
|
||||||
|
this.testWebhookDelivery()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.resetTestDeliveryInterval()
|
||||||
|
},
|
||||||
|
timedTestWebhookDelivery () {
|
||||||
|
const url = this.getNormalizedPayloadUrl()
|
||||||
|
this.resetTestDeliveryInterval()
|
||||||
|
this.testDeliveryInterval = setInterval(() => {
|
||||||
|
if (!url) {
|
||||||
|
this.resetTestDeliveryInterval()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.testDeliveryIntervalCouter = this.testDeliveryIntervalCouter + 1
|
||||||
|
if (this.testDeliveryIntervalCouter >= 100) {
|
||||||
|
this.executeTestWebhookDeliveryOrReset()
|
||||||
|
}
|
||||||
|
}, this.timedDeliveryWait / 100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.response-detail-item {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
word-break: break-all;
|
||||||
|
|
||||||
|
&__details {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&--start {
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
i {
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.anticon {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-btn .ant-btn__progress-overlay {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 5;
|
||||||
|
opacity: 0.3;
|
||||||
|
transition: all 0s ease;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
background-color: #666;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
526
ui/src/components/view/WebhookDeliveriesTab.vue
Normal file
526
ui/src/components/view/WebhookDeliveriesTab.vue
Normal file
@ -0,0 +1,526 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="showTimeFilterModal"
|
||||||
|
:title="$t('label.select.period')"
|
||||||
|
:maskClosable="false"
|
||||||
|
:footer="null">
|
||||||
|
<date-time-filter
|
||||||
|
:startDateProp="startDate"
|
||||||
|
:endDateProp="endDate"
|
||||||
|
:showAllDataOption="false"
|
||||||
|
@closeAction="closeTimeFilterAction"
|
||||||
|
@onSubmit="handleSubmitDateTimeFilter"/>
|
||||||
|
</a-modal>
|
||||||
|
<div class="filter-row">
|
||||||
|
<a-row>
|
||||||
|
<a-col :xs="24" :md="12">
|
||||||
|
<a-space direction="vertical">
|
||||||
|
<div>
|
||||||
|
<a-radio-group
|
||||||
|
v-model:value="durationSelectorValue"
|
||||||
|
buttonStyle="solid"
|
||||||
|
@change="handleTimeFilterChange">
|
||||||
|
<a-radio-button value="day">
|
||||||
|
{{ $t('label.duration.24hours') }}
|
||||||
|
</a-radio-button>
|
||||||
|
<a-radio-button value="all">
|
||||||
|
{{ $t('All') }}
|
||||||
|
</a-radio-button>
|
||||||
|
<a-radio-button value="custom">
|
||||||
|
{{ $t('label.duration.custom') }}
|
||||||
|
</a-radio-button>
|
||||||
|
</a-radio-group>
|
||||||
|
<InfoCircleOutlined class="info-icon" :title="$t('message.webhook.deliveries.time.filter')"/>
|
||||||
|
</div>
|
||||||
|
<div class="ant-tag" v-if="durationSelectorValue==='custom'">
|
||||||
|
<a-button @click="openTimeFilter()">
|
||||||
|
<FilterOutlined/>
|
||||||
|
</a-button>
|
||||||
|
<span v-html="formatedPeriod"></span>
|
||||||
|
</div>
|
||||||
|
</a-space>
|
||||||
|
</a-col>
|
||||||
|
<a-col :xs="24" :md="12" style="text-align: right; padding-right: 10px;">
|
||||||
|
<span>
|
||||||
|
<search-view
|
||||||
|
:searchFilters="searchFilters"
|
||||||
|
:searchParams="searchParams"
|
||||||
|
:apiName="'listWebhookDeliveries'"
|
||||||
|
@search="searchDelivieries"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
<a-button
|
||||||
|
v-if="('deleteWebhookDelivery' in $store.getters.apis) && ((selectedRowKeys && selectedRowKeys.length > 0) || (durationSelectorValue === 'all' && searchParamsIsEmpty))"
|
||||||
|
type="danger"
|
||||||
|
danger
|
||||||
|
style="width: 100%; margin-bottom: 15px"
|
||||||
|
@click="clearOrDeleteDeliveriesConfirmation()">
|
||||||
|
<template #icon><delete-outlined /></template>
|
||||||
|
{{ (selectedRowKeys && selectedRowKeys.length > 0) ? $t('label.action.delete.webhook.deliveries') : $t('label.action.clear.webhook.deliveries') }}
|
||||||
|
</a-button>
|
||||||
|
<list-view
|
||||||
|
:tabLoading="tabLoading"
|
||||||
|
:columns="columns"
|
||||||
|
:items="deliveries"
|
||||||
|
:actions="actions"
|
||||||
|
:columnKeys="columnKeys"
|
||||||
|
:explicitlyAllowRowSelection="true"
|
||||||
|
:selectedColumns="selectedColumnKeys"
|
||||||
|
ref="listview"
|
||||||
|
@update-selected-columns="updateSelectedColumns"
|
||||||
|
@refresh="this.fetchData"
|
||||||
|
@selection-change="updateSelectedRows"/>
|
||||||
|
<a-pagination
|
||||||
|
class="row-element"
|
||||||
|
style="margin-top: 10px"
|
||||||
|
size="small"
|
||||||
|
:current="page"
|
||||||
|
:pageSize="pageSize"
|
||||||
|
:total="totalCount"
|
||||||
|
:showTotal="total => `${$t('label.showing')} ${Math.min(total, 1+((page-1)*pageSize))}-${Math.min(page*pageSize, total)} ${$t('label.of')} ${total} ${$t('label.items')}`"
|
||||||
|
:pageSizeOptions="pageSizeOptions"
|
||||||
|
@change="changePage"
|
||||||
|
@showSizeChange="changePage"
|
||||||
|
showSizeChanger
|
||||||
|
showQuickJumper>
|
||||||
|
<template #buildOptionText="props">
|
||||||
|
<span>{{ props.value }} / {{ $t('label.page') }}</span>
|
||||||
|
</template>
|
||||||
|
</a-pagination>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { api } from '@/api'
|
||||||
|
import { isAdmin } from '@/role'
|
||||||
|
import { genericCompare } from '@/utils/sort.js'
|
||||||
|
import moment from 'moment'
|
||||||
|
import DateTimeFilter from './DateTimeFilter'
|
||||||
|
import SearchView from '@/components/view/SearchView'
|
||||||
|
import ListView from '@/components/view/ListView'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WebhookDeliveriesTab',
|
||||||
|
components: {
|
||||||
|
DateTimeFilter,
|
||||||
|
SearchView,
|
||||||
|
ListView
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
resource: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
tabLoading: false,
|
||||||
|
columnKeys: ['payload', 'eventtype', 'success', 'response', 'startdate', 'duration'],
|
||||||
|
selectedColumnKeys: ['payload', 'eventtype', 'success', 'duration'],
|
||||||
|
selectedRowKeys: [],
|
||||||
|
columns: [],
|
||||||
|
cols: [],
|
||||||
|
deliveries: [],
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
api: 'executeWebhookDelivery',
|
||||||
|
icon: 'retweet-outlined',
|
||||||
|
label: 'label.redeliver',
|
||||||
|
message: 'message.redeliver.webhook.delivery',
|
||||||
|
dataView: true,
|
||||||
|
popup: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
api: 'deleteWebhookDelivery',
|
||||||
|
icon: 'delete-outlined',
|
||||||
|
label: 'label.delete.webhook.delivery',
|
||||||
|
message: 'message.redeliver.webhook.delivery',
|
||||||
|
dataView: true,
|
||||||
|
popup: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
page: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
totalCount: 0,
|
||||||
|
durationSelectorValue: 'day',
|
||||||
|
showTimeFilterModal: false,
|
||||||
|
endDate: null,
|
||||||
|
startDate: this.get24hrStartDate(),
|
||||||
|
formatedPeriod: null,
|
||||||
|
searchFilters: [
|
||||||
|
'eventtype'
|
||||||
|
],
|
||||||
|
searchParams: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
pageSizeOptions () {
|
||||||
|
var sizes = [20, 50, 100, 200, this.$store.getters.defaultListViewPageSize]
|
||||||
|
if (this.device !== 'desktop') {
|
||||||
|
sizes.unshift(10)
|
||||||
|
}
|
||||||
|
return [...new Set(sizes)].sort(function (a, b) {
|
||||||
|
return a - b
|
||||||
|
}).map(String)
|
||||||
|
},
|
||||||
|
searchParamsIsEmpty () {
|
||||||
|
if (!this.searchParams) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return Object.keys(this.searchParams).length === 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
const routeQuery = this.$route.query
|
||||||
|
const usefulQueryParams = ['keyword', 'managementserverid', 'eventtype']
|
||||||
|
usefulQueryParams.forEach(queryParam => {
|
||||||
|
if (routeQuery[queryParam]) {
|
||||||
|
this.searchParams[queryParam] = routeQuery[queryParam]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (isAdmin()) {
|
||||||
|
this.columnKeys.splice(2, 0, 'managementservername')
|
||||||
|
this.selectedColumnKeys.splice(2, 0, 'managementservername')
|
||||||
|
this.searchFilters.push('managementserverid')
|
||||||
|
}
|
||||||
|
this.updateColumns()
|
||||||
|
this.pageSize = this.pageSizeOptions[0] * 1
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
resource: {
|
||||||
|
handler () {
|
||||||
|
this.fetchData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchData () {
|
||||||
|
if ('listview' in this.$refs && this.$refs.listview) {
|
||||||
|
this.$refs.listview.resetSelection()
|
||||||
|
}
|
||||||
|
this.fetchDeliveries()
|
||||||
|
},
|
||||||
|
fetchDeliveries () {
|
||||||
|
this.deliveries = []
|
||||||
|
if (!this.resource.id) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const params = {
|
||||||
|
page: this.page,
|
||||||
|
pagesize: this.pageSize,
|
||||||
|
webhookid: this.resource.id,
|
||||||
|
listall: true
|
||||||
|
}
|
||||||
|
if (this.startDate) {
|
||||||
|
params.startDate = moment(this.startDate).format()
|
||||||
|
}
|
||||||
|
if (this.endDate) {
|
||||||
|
params.endDate = moment(this.endDate).format()
|
||||||
|
}
|
||||||
|
if (this.searchParams?.searchQuery) {
|
||||||
|
params.keyword = this.searchParams.searchQuery
|
||||||
|
}
|
||||||
|
if (this.searchParams?.managementserverid) {
|
||||||
|
params.managementserverid = this.searchParams.managementserverid
|
||||||
|
}
|
||||||
|
if (this.searchParams?.eventtype) {
|
||||||
|
params.eventtype = this.searchParams.eventtype
|
||||||
|
}
|
||||||
|
this.tabLoading = true
|
||||||
|
api('listWebhookDeliveries', params).then(json => {
|
||||||
|
this.deliveries = []
|
||||||
|
this.totalCount = json?.listwebhookdeliveriesresponse?.count || 0
|
||||||
|
this.deliveries = json?.listwebhookdeliveriesresponse?.webhookdelivery || []
|
||||||
|
this.formatTimeFilterPeriod()
|
||||||
|
this.tabLoading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
changePage (page, pageSize) {
|
||||||
|
this.page = page
|
||||||
|
this.pageSize = pageSize
|
||||||
|
this.fetchDeliveries()
|
||||||
|
},
|
||||||
|
updateSelectedColumns (key) {
|
||||||
|
if (this.selectedColumnKeys.includes(key)) {
|
||||||
|
this.selectedColumnKeys = this.selectedColumnKeys.filter(x => x !== key)
|
||||||
|
} else {
|
||||||
|
this.selectedColumnKeys.push(key)
|
||||||
|
}
|
||||||
|
this.updateColumns()
|
||||||
|
},
|
||||||
|
updateColumns () {
|
||||||
|
this.columns = []
|
||||||
|
for (var columnKey of this.columnKeys) {
|
||||||
|
const key = columnKey
|
||||||
|
if (!this.selectedColumnKeys.includes(key)) continue
|
||||||
|
var title = this.$t('label.' + String(key).toLowerCase())
|
||||||
|
if (key === 'eventtype') {
|
||||||
|
title = this.$t('label.event')
|
||||||
|
}
|
||||||
|
this.columns.push({
|
||||||
|
key: key,
|
||||||
|
title: title,
|
||||||
|
dataIndex: key,
|
||||||
|
sorter: (a, b) => { return genericCompare(a[key] || '', b[key] || '') }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (this.columns.length > 0) {
|
||||||
|
this.columns[this.columns.length - 1].customFilterDropdown = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateSelectedRows (keys) {
|
||||||
|
this.selectedRowKeys = keys
|
||||||
|
},
|
||||||
|
clearOrDeleteDeliveriesConfirmation () {
|
||||||
|
const self = this
|
||||||
|
const title = (this.selectedRowKeys && this.selectedRowKeys.length > 0) ? this.$t('label.action.delete.webhook.deliveries') : this.$t('label.action.clear.webhook.deliveries')
|
||||||
|
this.$confirm({
|
||||||
|
title: title,
|
||||||
|
okText: this.$t('label.ok'),
|
||||||
|
okType: 'danger',
|
||||||
|
cancelText: this.$t('label.cancel'),
|
||||||
|
onOk () {
|
||||||
|
if (self.selectedRowKeys && self.selectedRowKeys.length > 0) {
|
||||||
|
self.deletedSelectedDeliveries()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.clearDeliveries()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
deletedSelectedDeliveries () {
|
||||||
|
const promises = []
|
||||||
|
this.selectedRowKeys.forEach(id => {
|
||||||
|
const params = {
|
||||||
|
id: id
|
||||||
|
}
|
||||||
|
promises.push(new Promise((resolve, reject) => {
|
||||||
|
api('deleteWebhookDelivery', params).then(json => {
|
||||||
|
return resolve(id)
|
||||||
|
}).catch(error => {
|
||||||
|
return reject(error)
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
const msg = this.$t('label.action.delete.webhook.deliveries')
|
||||||
|
this.$message.info({
|
||||||
|
content: msg,
|
||||||
|
duration: 3
|
||||||
|
})
|
||||||
|
this.tabLoading = true
|
||||||
|
Promise.all(promises).finally(() => {
|
||||||
|
this.tabLoading = false
|
||||||
|
this.fetchData()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
clearDeliveries () {
|
||||||
|
const params = {
|
||||||
|
webhookid: this.resource.id
|
||||||
|
}
|
||||||
|
this.tabLoading = true
|
||||||
|
api('deleteWebhookDelivery', params).then(json => {
|
||||||
|
this.$message.success(this.$t('message.success.clear.webhook.deliveries'))
|
||||||
|
this.fetchData()
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.tabLoading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
redeliverDeliveryConfirmation (item) {
|
||||||
|
const self = this
|
||||||
|
this.$confirm({
|
||||||
|
title: this.$t('label.redeliver') + ' ' + item.eventtype,
|
||||||
|
okText: this.$t('label.ok'),
|
||||||
|
okType: 'primary',
|
||||||
|
cancelText: this.$t('label.cancel'),
|
||||||
|
onOk () {
|
||||||
|
self.redeliverDelivery(item)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
redeliverDelivery (item) {
|
||||||
|
const params = {
|
||||||
|
id: item.id
|
||||||
|
}
|
||||||
|
this.tabLoading = true
|
||||||
|
api('executeWebhookDelivery', params).then(json => {
|
||||||
|
this.$message.success(this.$t('message.success.redeliver.webhook.delivery'))
|
||||||
|
this.fetchData()
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.tabLoading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
deleteDeliveryConfirmation (item) {
|
||||||
|
const self = this
|
||||||
|
this.$confirm({
|
||||||
|
title: this.$t('label.delete') + ' ' + item.eventtype,
|
||||||
|
okText: this.$t('label.ok'),
|
||||||
|
okType: 'primary',
|
||||||
|
cancelText: this.$t('label.cancel'),
|
||||||
|
onOk () {
|
||||||
|
self.deleteDelivery(item)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
deleteDelivery (item) {
|
||||||
|
const params = {
|
||||||
|
id: item.id
|
||||||
|
}
|
||||||
|
this.tabLoading = true
|
||||||
|
api('deleteWebhookDelivery', params).then(json => {
|
||||||
|
const message = `${this.$t('message.success.delete')} ${this.$t('label.webhook.delivery')}`
|
||||||
|
this.$message.success(message)
|
||||||
|
this.fetchData()
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.tabLoading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
execAction (action) {
|
||||||
|
if (action.api === 'executeWebhookDelivery') {
|
||||||
|
this.redeliverDeliveryConfirmation(action.resource)
|
||||||
|
} else if (action.api === 'deleteWebhookDelivery') {
|
||||||
|
this.deleteDeliveryConfirmation(action.resource)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
get24hrStartDate () {
|
||||||
|
const start = new Date()
|
||||||
|
start.setDate(start.getDate() - 1)
|
||||||
|
return start
|
||||||
|
},
|
||||||
|
handleTimeFilterChange () {
|
||||||
|
var start = this.startDate
|
||||||
|
var end = this.endDate
|
||||||
|
switch (this.durationSelectorValue) {
|
||||||
|
case 'day':
|
||||||
|
start = this.get24hrStartDate()
|
||||||
|
end = null
|
||||||
|
break
|
||||||
|
case 'all':
|
||||||
|
start = null
|
||||||
|
end = null
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (start !== this.startDate || end !== this.endDate) {
|
||||||
|
this.startDate = start
|
||||||
|
this.endDate = end
|
||||||
|
this.fetchData()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
openTimeFilter () {
|
||||||
|
this.showTimeFilterModal = true
|
||||||
|
},
|
||||||
|
formatTimeFilterPeriod () {
|
||||||
|
var formatedStartDate = null
|
||||||
|
var formatedEndDate = null
|
||||||
|
if (this.startDate) {
|
||||||
|
formatedStartDate = moment(this.startDate).format('MMM DD, YYYY') + ' at ' + moment(this.startDate).format('HH:mm:ss')
|
||||||
|
}
|
||||||
|
if (this.endDate) {
|
||||||
|
formatedEndDate = moment(this.endDate).format('MMM DD, YYYY') + ' at ' + moment(this.endDate).format('HH:mm:ss')
|
||||||
|
}
|
||||||
|
if (formatedStartDate && formatedEndDate) {
|
||||||
|
this.formatedPeriod = ' ' + this.$t('label.datetime.filter.period', { startDate: formatedStartDate, endDate: formatedEndDate })
|
||||||
|
} else if (formatedStartDate && !formatedEndDate) {
|
||||||
|
this.formatedPeriod = ' ' + this.$t('label.datetime.filter.starting', { startDate: formatedStartDate })
|
||||||
|
} else if (!formatedStartDate && formatedEndDate) {
|
||||||
|
this.formatedPeriod = ' ' + this.$t('label.datetime.filter.up.to', { endDate: formatedEndDate })
|
||||||
|
} else {
|
||||||
|
this.formatedPeriod = ' <b>' + this.$t('label.all.available.data') + '</b>'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleSubmitDateTimeFilter (values) {
|
||||||
|
if (values.startDate) {
|
||||||
|
this.startDate = new Date(values.startDate)
|
||||||
|
} else {
|
||||||
|
this.startDate = null
|
||||||
|
}
|
||||||
|
if (values.endDate) {
|
||||||
|
this.endDate = new Date(values.endDate)
|
||||||
|
} else {
|
||||||
|
this.endDate = null
|
||||||
|
}
|
||||||
|
this.showTimeFilterModal = false
|
||||||
|
this.fetchData()
|
||||||
|
},
|
||||||
|
closeTimeFilterAction () {
|
||||||
|
this.showTimeFilterModal = false
|
||||||
|
},
|
||||||
|
searchDelivieries (params) {
|
||||||
|
const query = Object.assign({}, this.$route.query)
|
||||||
|
for (const key in this.searchParams) {
|
||||||
|
delete query[key]
|
||||||
|
}
|
||||||
|
delete query.keyword
|
||||||
|
delete query.q
|
||||||
|
this.searchParams = params
|
||||||
|
if ('searchQuery' in this.searchParams) {
|
||||||
|
query.keyword = this.searchParams.searchQuery
|
||||||
|
} else {
|
||||||
|
Object.assign(query, this.searchParams)
|
||||||
|
}
|
||||||
|
this.fetchData()
|
||||||
|
if (JSON.stringify(query) === JSON.stringify(this.$route.query)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
history.pushState(
|
||||||
|
{},
|
||||||
|
null,
|
||||||
|
'#' + this.$route.path + '?' + Object.keys(query).map(key => {
|
||||||
|
return (
|
||||||
|
encodeURIComponent(key) + '=' + encodeURIComponent(query[key])
|
||||||
|
)
|
||||||
|
}).join('&')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.ant-tag {
|
||||||
|
padding: 0 7px 0 0;
|
||||||
|
}
|
||||||
|
.ant-select {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
.info-icon {
|
||||||
|
margin: 0 10px 0 5px;
|
||||||
|
}
|
||||||
|
.filter-row {
|
||||||
|
margin-bottom: 2.5%;
|
||||||
|
}
|
||||||
|
.filter-row-inner {
|
||||||
|
margin-top: 3%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -15,6 +15,7 @@
|
|||||||
// specific language governing permissions and limitations
|
// specific language governing permissions and limitations
|
||||||
// under the License.
|
// under the License.
|
||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
|
import { shallowRef, defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'tools',
|
name: 'tools',
|
||||||
@ -77,6 +78,154 @@ export default {
|
|||||||
resourceType: 'UserVm',
|
resourceType: 'UserVm',
|
||||||
permission: ['listInfrastructure', 'listVolumesForImport'],
|
permission: ['listInfrastructure', 'listVolumesForImport'],
|
||||||
component: () => import('@/views/tools/ManageVolumes.vue')
|
component: () => import('@/views/tools/ManageVolumes.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'webhook',
|
||||||
|
title: 'label.webhooks',
|
||||||
|
icon: 'node-index-outlined',
|
||||||
|
docHelp: 'adminguide/webhooks.html',
|
||||||
|
permission: ['listWebhooks'],
|
||||||
|
columns: () => {
|
||||||
|
const cols = ['name', 'payloadurl', 'state', 'created']
|
||||||
|
if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) {
|
||||||
|
cols.splice(3, 0, 'account', 'domain', 'scope')
|
||||||
|
}
|
||||||
|
if (store.getters.listAllProjects) {
|
||||||
|
cols.push('project')
|
||||||
|
}
|
||||||
|
return cols
|
||||||
|
},
|
||||||
|
details: ['name', 'id', 'description', 'scope', 'payloadurl', 'sslverification', 'secretkey', 'state', 'account', 'domainid'],
|
||||||
|
searchFilters: () => {
|
||||||
|
var filters = ['state']
|
||||||
|
if (['Admin', 'DomainAdmin'].includes(store.getters.userInfo.roletype)) {
|
||||||
|
filters.push('scope', 'domainid', 'account')
|
||||||
|
}
|
||||||
|
return filters
|
||||||
|
},
|
||||||
|
tabs: [
|
||||||
|
{
|
||||||
|
name: 'details',
|
||||||
|
component: shallowRef(defineAsyncComponent(() => import('@/components/view/DetailsTab.vue')))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'recent.deliveries',
|
||||||
|
component: shallowRef(defineAsyncComponent(() => import('@/components/view/WebhookDeliveriesTab.vue')))
|
||||||
|
}
|
||||||
|
],
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
api: 'createWebhook',
|
||||||
|
icon: 'plus-outlined',
|
||||||
|
label: 'label.create.webhook',
|
||||||
|
docHelp: 'adminguide/events.html#creating-webhooks',
|
||||||
|
listView: true,
|
||||||
|
popup: true,
|
||||||
|
component: shallowRef(defineAsyncComponent(() => import('@/views/tools/CreateWebhook.vue')))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
api: 'updateWebhook',
|
||||||
|
icon: 'edit-outlined',
|
||||||
|
label: 'label.update.webhook',
|
||||||
|
dataView: true,
|
||||||
|
popup: true,
|
||||||
|
args: ['name', 'description', 'payloadurl', 'sslverification', 'secretkey', 'state'],
|
||||||
|
mapping: {
|
||||||
|
state: {
|
||||||
|
options: ['Enabled', 'Disabled']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
api: 'updateWebhook',
|
||||||
|
icon: 'play-circle-outlined',
|
||||||
|
label: 'label.enable.webhook',
|
||||||
|
message: 'message.confirm.enable.webhook',
|
||||||
|
dataView: true,
|
||||||
|
groupAction: true,
|
||||||
|
popup: true,
|
||||||
|
defaultArgs: { state: 'Enabled' },
|
||||||
|
groupMap: (selection) => { return selection.map(x => { return { id: x } }) },
|
||||||
|
show: (record) => { return ['Disabled'].includes(record.state) }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
api: 'updateWebhook',
|
||||||
|
icon: 'pause-circle-outlined',
|
||||||
|
label: 'label.disable.webhook',
|
||||||
|
message: 'message.confirm.disable.webhook',
|
||||||
|
dataView: true,
|
||||||
|
groupAction: true,
|
||||||
|
popup: true,
|
||||||
|
defaultArgs: { state: 'Disabled' },
|
||||||
|
groupMap: (selection) => { return selection.map(x => { return { id: x } }) },
|
||||||
|
show: (record) => { return ['Enabled'].includes(record.state) }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
api: 'executeWebhookDelivery',
|
||||||
|
icon: 'right-square-outlined',
|
||||||
|
label: 'label.test.webhook.delivery',
|
||||||
|
message: 'message.test.webhook.delivery',
|
||||||
|
dataView: true,
|
||||||
|
popup: true,
|
||||||
|
component: shallowRef(defineAsyncComponent(() => import('@/views/tools/TestWebhookDelivery.vue')))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
api: 'deleteWebhook',
|
||||||
|
icon: 'delete-outlined',
|
||||||
|
label: 'label.delete.webhook',
|
||||||
|
message: 'message.delete.webhook',
|
||||||
|
dataView: true,
|
||||||
|
groupAction: true,
|
||||||
|
popup: true,
|
||||||
|
groupShow: (selectedItems, storegetters) => {
|
||||||
|
if (['Admin'].includes(storegetters.userInfo.roletype)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'webhookdeliveries',
|
||||||
|
title: 'label.webhook.deliveries',
|
||||||
|
icon: 'gateway-outlined',
|
||||||
|
hidden: true,
|
||||||
|
permission: ['listWebhookDeliveries'],
|
||||||
|
columns: () => {
|
||||||
|
const cols = ['payload', 'eventtype', 'webhookname', 'success', 'response', 'duration']
|
||||||
|
if (['Admin'].includes(store.getters.userInfo.roletype)) {
|
||||||
|
cols.splice(3, 0, 'managementservername')
|
||||||
|
}
|
||||||
|
return cols
|
||||||
|
},
|
||||||
|
details: () => {
|
||||||
|
const fields = ['id', 'eventid', 'eventtype', 'headers', 'payload', 'success', 'response', 'startdate', 'enddate']
|
||||||
|
if (['Admin'].includes(store.getters.userInfo.roletype)) {
|
||||||
|
fields.splice(1, 0, 'managementserverid', 'managementservername')
|
||||||
|
}
|
||||||
|
return fields
|
||||||
|
},
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
api: 'executeWebhookDelivery',
|
||||||
|
icon: 'retweet-outlined',
|
||||||
|
label: 'label.redeliver',
|
||||||
|
message: 'message.redeliver.webhook.delivery',
|
||||||
|
dataView: true,
|
||||||
|
popup: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
api: 'deleteWebhookDelivery',
|
||||||
|
icon: 'delete-outlined',
|
||||||
|
label: 'label.delete.webhook.delivery',
|
||||||
|
message: 'message.delete.webhook.delivery',
|
||||||
|
dataView: true,
|
||||||
|
groupAction: true,
|
||||||
|
popup: true,
|
||||||
|
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -120,6 +120,7 @@ import {
|
|||||||
MinusSquareOutlined,
|
MinusSquareOutlined,
|
||||||
MobileOutlined,
|
MobileOutlined,
|
||||||
MoreOutlined,
|
MoreOutlined,
|
||||||
|
NodeIndexOutlined,
|
||||||
NotificationOutlined,
|
NotificationOutlined,
|
||||||
NumberOutlined,
|
NumberOutlined,
|
||||||
LaptopOutlined,
|
LaptopOutlined,
|
||||||
@ -139,7 +140,9 @@ import {
|
|||||||
ReconciliationOutlined,
|
ReconciliationOutlined,
|
||||||
RedoOutlined,
|
RedoOutlined,
|
||||||
ReloadOutlined,
|
ReloadOutlined,
|
||||||
|
RetweetOutlined,
|
||||||
RightCircleOutlined,
|
RightCircleOutlined,
|
||||||
|
RightSquareOutlined,
|
||||||
RocketOutlined,
|
RocketOutlined,
|
||||||
SafetyCertificateOutlined,
|
SafetyCertificateOutlined,
|
||||||
SafetyOutlined,
|
SafetyOutlined,
|
||||||
@ -281,6 +284,7 @@ export default {
|
|||||||
app.component('MinusSquareOutlined', MinusSquareOutlined)
|
app.component('MinusSquareOutlined', MinusSquareOutlined)
|
||||||
app.component('MobileOutlined', MobileOutlined)
|
app.component('MobileOutlined', MobileOutlined)
|
||||||
app.component('MoreOutlined', MoreOutlined)
|
app.component('MoreOutlined', MoreOutlined)
|
||||||
|
app.component('NodeIndexOutlined', NodeIndexOutlined)
|
||||||
app.component('NotificationOutlined', NotificationOutlined)
|
app.component('NotificationOutlined', NotificationOutlined)
|
||||||
app.component('NumberOutlined', NumberOutlined)
|
app.component('NumberOutlined', NumberOutlined)
|
||||||
app.component('LaptopOutlined', LaptopOutlined)
|
app.component('LaptopOutlined', LaptopOutlined)
|
||||||
@ -300,7 +304,9 @@ export default {
|
|||||||
app.component('ReconciliationOutlined', ReconciliationOutlined)
|
app.component('ReconciliationOutlined', ReconciliationOutlined)
|
||||||
app.component('RedoOutlined', RedoOutlined)
|
app.component('RedoOutlined', RedoOutlined)
|
||||||
app.component('ReloadOutlined', ReloadOutlined)
|
app.component('ReloadOutlined', ReloadOutlined)
|
||||||
|
app.component('RetweetOutlined', RetweetOutlined)
|
||||||
app.component('RightCircleOutlined', RightCircleOutlined)
|
app.component('RightCircleOutlined', RightCircleOutlined)
|
||||||
|
app.component('RightSquareOutlined', RightSquareOutlined)
|
||||||
app.component('RocketOutlined', RocketOutlined)
|
app.component('RocketOutlined', RocketOutlined)
|
||||||
app.component('SafetyCertificateOutlined', SafetyCertificateOutlined)
|
app.component('SafetyCertificateOutlined', SafetyCertificateOutlined)
|
||||||
app.component('SafetyOutlined', SafetyOutlined)
|
app.component('SafetyOutlined', SafetyOutlined)
|
||||||
|
|||||||
@ -798,7 +798,7 @@ export default {
|
|||||||
this.projectView = Boolean(store.getters.project && store.getters.project.id)
|
this.projectView = Boolean(store.getters.project && store.getters.project.id)
|
||||||
this.hasProjectId = ['vm', 'vmgroup', 'ssh', 'affinitygroup', 'userdata', 'volume', 'snapshot', 'vmsnapshot', 'guestnetwork',
|
this.hasProjectId = ['vm', 'vmgroup', 'ssh', 'affinitygroup', 'userdata', 'volume', 'snapshot', 'vmsnapshot', 'guestnetwork',
|
||||||
'vpc', 'securitygroups', 'publicip', 'vpncustomergateway', 'template', 'iso', 'event', 'kubernetes',
|
'vpc', 'securitygroups', 'publicip', 'vpncustomergateway', 'template', 'iso', 'event', 'kubernetes',
|
||||||
'autoscalevmgroup', 'vnfapp'].includes(this.$route.name)
|
'autoscalevmgroup', 'vnfapp', 'webhook'].includes(this.$route.name)
|
||||||
|
|
||||||
if ((this.$route && this.$route.params && this.$route.params.id) || this.$route.query.dataView) {
|
if ((this.$route && this.$route.params && this.$route.params.id) || this.$route.query.dataView) {
|
||||||
this.dataView = true
|
this.dataView = true
|
||||||
@ -1553,13 +1553,16 @@ export default {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (input === undefined || input === null ||
|
if (input === undefined || input === null ||
|
||||||
(input === '' && !['updateStoragePool', 'updateHost', 'updatePhysicalNetwork', 'updateDiskOffering', 'updateNetworkOffering', 'updateServiceOffering', 'updateZone', 'updateAccount'].includes(action.api))) {
|
(input === '' && !['updateStoragePool', 'updateHost', 'updatePhysicalNetwork',
|
||||||
|
'updateDiskOffering', 'updateNetworkOffering', 'updateServiceOffering',
|
||||||
|
'updateZone', 'updateAccount', 'updateWebhook'].includes(action.api))) {
|
||||||
if (param.type === 'boolean') {
|
if (param.type === 'boolean') {
|
||||||
params[key] = false
|
params[key] = false
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if (input === '' && !['tags', 'hosttags', 'storagetags', 'dns2', 'ip6dns1', 'ip6dns2', 'internaldns2', 'networkdomain'].includes(key)) {
|
if (input === '' && !['tags', 'hosttags', 'storagetags', 'dns2', 'ip6dns1',
|
||||||
|
'ip6dns2', 'internaldns2', 'networkdomain', 'secretkey'].includes(key)) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if (action.mapping && key in action.mapping && action.mapping[key].options) {
|
if (action.mapping && key in action.mapping && action.mapping[key].options) {
|
||||||
|
|||||||
357
ui/src/views/tools/CreateWebhook.vue
Normal file
357
ui/src/views/tools/CreateWebhook.vue
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
// Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
// or more contributor license agreements. See the NOTICE file
|
||||||
|
// distributed with this work for additional information
|
||||||
|
// regarding copyright ownership. The ASF licenses this file
|
||||||
|
// to you under the Apache License, Version 2.0 (the
|
||||||
|
// "License"); you may not use this file except in compliance
|
||||||
|
// with the License. You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing,
|
||||||
|
// software distributed under the License is distributed on an
|
||||||
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
// KIND, either express or implied. See the License for the
|
||||||
|
// specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="form-layout" v-ctrl-enter="handleSubmit">
|
||||||
|
<a-spin :spinning="loading">
|
||||||
|
<a-form
|
||||||
|
:ref="formRef"
|
||||||
|
:model="form"
|
||||||
|
:rules="rules"
|
||||||
|
@finish="handleSubmit"
|
||||||
|
layout="vertical">
|
||||||
|
<a-form-item name="name" ref="name">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.name')" :tooltip="apiParams.name.description"/>
|
||||||
|
</template>
|
||||||
|
<a-input
|
||||||
|
v-model:value="form.name"
|
||||||
|
:placeholder="apiParams.name.description"
|
||||||
|
v-focus="true" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item name="description" ref="description">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.description')" :tooltip="apiParams.description.description"/>
|
||||||
|
</template>
|
||||||
|
<a-input
|
||||||
|
v-model:value="form.description"
|
||||||
|
:placeholder="apiParams.description.description"/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item name="scope" ref="scope" v-if="isAdminOrDomainAdmin">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.scope')" :tooltip="apiParams.scope.description"/>
|
||||||
|
</template>
|
||||||
|
<a-radio-group
|
||||||
|
v-model:value="form.scope"
|
||||||
|
buttonStyle="solid"
|
||||||
|
@change="handleScopeChange">
|
||||||
|
<a-radio-button value="Local">
|
||||||
|
{{ $t('label.local') }}
|
||||||
|
</a-radio-button>
|
||||||
|
<a-radio-button value="Domain">
|
||||||
|
{{ $t('label.domain') }}
|
||||||
|
</a-radio-button>
|
||||||
|
<a-radio-button value="Global" v-if="isAdmin">
|
||||||
|
{{ $t('label.global') }}
|
||||||
|
</a-radio-button>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item name="domainid" ref="domainid" v-if="isAdminOrDomainAdmin && ['Domain', 'Local'].includes(form.scope)">
|
||||||
|
<template #label :title="apiParams.domainid.description">
|
||||||
|
{{ $t('label.domainid') }}
|
||||||
|
<a-tooltip>
|
||||||
|
<info-circle-outlined style="color: rgba(0,0,0,.45)" />
|
||||||
|
</a-tooltip>
|
||||||
|
</template>
|
||||||
|
<a-select
|
||||||
|
id="domain-selection"
|
||||||
|
v-model:value="form.domainid"
|
||||||
|
showSearch
|
||||||
|
optionFilterProp="label"
|
||||||
|
:filterOption="(input, option) => {
|
||||||
|
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||||
|
}"
|
||||||
|
:loading="domainLoading"
|
||||||
|
:placeholder="apiParams.domainid.description"
|
||||||
|
@change="val => { handleDomainChanged(val) }">
|
||||||
|
<a-select-option v-for="opt in domains" :key="opt.id" :label="opt.path || opt.name || opt.description || ''">
|
||||||
|
{{ opt.path || opt.name || opt.description }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item name="account" ref="account" v-if="isAdminOrDomainAdmin && ['Local'].includes(form.scope) && form.domainid">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.account')" :tooltip="apiParams.account.description"/>
|
||||||
|
</template>
|
||||||
|
<a-select
|
||||||
|
v-model:value="form.account"
|
||||||
|
showSearch
|
||||||
|
optionFilterProp="label"
|
||||||
|
:filterOption="(input, option) => {
|
||||||
|
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||||
|
}"
|
||||||
|
:loading="accountLoading"
|
||||||
|
:placeholder="apiParams.account.description">
|
||||||
|
<a-select-option v-for="opt in accounts" :key="opt.id" :label="opt.name">
|
||||||
|
{{ opt.name }}
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item name="payloadurl" ref="payloadurl">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.payloadurl')" :tooltip="apiParams.payloadurl.description"/>
|
||||||
|
</template>
|
||||||
|
<a-input
|
||||||
|
v-model:value="form.payloadurl"
|
||||||
|
:placeholder="apiParams.payloadurl.description"
|
||||||
|
@change="handleParamUpdate"/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item name="sslverification" ref="sslverification" v-if="isPayloadUrlHttps">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.sslverification')" :tooltip="apiParams.sslverification.description"/>
|
||||||
|
</template>
|
||||||
|
<a-alert
|
||||||
|
v-if="!form.sslverification"
|
||||||
|
class="ssl-alert"
|
||||||
|
type="warning"
|
||||||
|
:message="$t('message.disable.webhook.ssl.verification')" />
|
||||||
|
<a-switch
|
||||||
|
v-model:checked="form.sslverification"
|
||||||
|
@change="handleParamUpdate" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item name="secretkey" ref="secretkey">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.secretkey')" :tooltip="apiParams.secretkey.description"/>
|
||||||
|
</template>
|
||||||
|
<a-input
|
||||||
|
v-model:value="form.secretkey"
|
||||||
|
:placeholder="apiParams.secretkey.description"
|
||||||
|
@change="handleParamUpdate"/>
|
||||||
|
</a-form-item>
|
||||||
|
<test-webhook-delivery-view
|
||||||
|
ref="dispatchview"
|
||||||
|
:payloadUrl="form.payloadurl"
|
||||||
|
:sslVerification="form.sslverification"
|
||||||
|
:secretKey="form.secretkey"
|
||||||
|
:showActions="!(!form.payloadurl)" />
|
||||||
|
<a-form-item name="state" ref="state">
|
||||||
|
<template #label>
|
||||||
|
<tooltip-label :title="$t('label.enabled')" :tooltip="apiParams.state.description"/>
|
||||||
|
</template>
|
||||||
|
<a-switch v-model:checked="form.state" />
|
||||||
|
</a-form-item>
|
||||||
|
<div :span="24" class="action-button">
|
||||||
|
<a-button @click="closeAction">{{ $t('label.cancel') }}</a-button>
|
||||||
|
<a-button :loading="loading" ref="submit" type="primary" @click="handleSubmit">{{ $t('label.ok') }}</a-button>
|
||||||
|
</div>
|
||||||
|
</a-form>
|
||||||
|
</a-spin>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ref, reactive, toRaw } from 'vue'
|
||||||
|
import { api } from '@/api'
|
||||||
|
import _ from 'lodash'
|
||||||
|
import { mixinForm } from '@/utils/mixin'
|
||||||
|
import TooltipLabel from '@/components/widgets/TooltipLabel'
|
||||||
|
import TestWebhookDeliveryView from '@/components/view/TestWebhookDeliveryView'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CreateWebhook',
|
||||||
|
mixins: [mixinForm],
|
||||||
|
components: {
|
||||||
|
TooltipLabel,
|
||||||
|
TestWebhookDeliveryView
|
||||||
|
},
|
||||||
|
props: {},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
domains: [],
|
||||||
|
domainLoading: false,
|
||||||
|
accounts: [],
|
||||||
|
accountLoading: false,
|
||||||
|
loading: false,
|
||||||
|
testDeliveryAllowed: false,
|
||||||
|
testDeliveryLoading: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeCreate () {
|
||||||
|
this.apiParams = this.$getApiParams('createWebhook')
|
||||||
|
},
|
||||||
|
created () {
|
||||||
|
this.initForm()
|
||||||
|
if (['Domain', 'Local'].includes(this.form.scope)) {
|
||||||
|
this.fetchDomainData()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isAdminOrDomainAdmin () {
|
||||||
|
return ['Admin', 'DomainAdmin'].includes(this.$store.getters.userInfo.roletype)
|
||||||
|
},
|
||||||
|
isAdmin () {
|
||||||
|
return ['Admin'].includes(this.$store.getters.userInfo.roletype)
|
||||||
|
},
|
||||||
|
isPayloadUrlHttps () {
|
||||||
|
if (this.form.payloadurl) {
|
||||||
|
return this.form.payloadurl.toLowerCase().startsWith('https://')
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
initForm () {
|
||||||
|
this.formRef = ref()
|
||||||
|
this.form = reactive({
|
||||||
|
scope: 'Local',
|
||||||
|
state: true,
|
||||||
|
sslverification: true
|
||||||
|
})
|
||||||
|
this.rules = reactive({
|
||||||
|
name: [{ required: true, message: this.$t('message.error.create.webhook.name') }],
|
||||||
|
payloadurl: [{ required: true, message: this.$t('message.error.create.webhook.payloadurl') }]
|
||||||
|
})
|
||||||
|
},
|
||||||
|
isValidValueForKey (obj, key) {
|
||||||
|
return key in obj && obj[key] != null
|
||||||
|
},
|
||||||
|
arrayHasItems (array) {
|
||||||
|
return array !== null && array !== undefined && Array.isArray(array) && array.length > 0
|
||||||
|
},
|
||||||
|
isObjectEmpty (obj) {
|
||||||
|
return !(obj !== null && obj !== undefined && Object.keys(obj).length > 0 && obj.constructor === Object)
|
||||||
|
},
|
||||||
|
updateTestDeliveryLoading (value) {
|
||||||
|
this.testDeliveryLoading = value
|
||||||
|
},
|
||||||
|
fetchDomainData () {
|
||||||
|
this.domainLoading = true
|
||||||
|
this.domains = [
|
||||||
|
{
|
||||||
|
id: null,
|
||||||
|
name: ''
|
||||||
|
}
|
||||||
|
]
|
||||||
|
this.form.domainid = null
|
||||||
|
this.form.account = null
|
||||||
|
api('listDomains', {}).then(json => {
|
||||||
|
const listdomains = json.listdomainsresponse.domain
|
||||||
|
this.domains = this.domains.concat(listdomains)
|
||||||
|
}).finally(() => {
|
||||||
|
this.domainLoading = false
|
||||||
|
if (this.arrayHasItems(this.domains)) {
|
||||||
|
this.form.domainid = null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fetchAccountData () {
|
||||||
|
this.accounts = []
|
||||||
|
this.form.account = null
|
||||||
|
if (!this.form.domainid) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.accountLoading = true
|
||||||
|
var params = {
|
||||||
|
domainid: this.form.domainid
|
||||||
|
}
|
||||||
|
api('listAccounts', params).then(json => {
|
||||||
|
const listAccounts = json.listaccountsresponse.account || []
|
||||||
|
this.accounts = listAccounts
|
||||||
|
}).finally(() => {
|
||||||
|
this.accountLoading = false
|
||||||
|
if (this.arrayHasItems(this.accounts)) {
|
||||||
|
this.form.account = this.accounts[0].id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleSubmit (e) {
|
||||||
|
e.preventDefault()
|
||||||
|
if (this.loading) return
|
||||||
|
this.formRef.value.validate().then(() => {
|
||||||
|
const formRaw = toRaw(this.form)
|
||||||
|
const values = this.handleRemoveFields(formRaw)
|
||||||
|
const params = {
|
||||||
|
name: values.name,
|
||||||
|
description: values.description,
|
||||||
|
payloadurl: values.payloadurl,
|
||||||
|
state: values.state ? 'Enabled' : 'Disabled'
|
||||||
|
}
|
||||||
|
if (this.isValidValueForKey(values, 'scope')) {
|
||||||
|
params.scope = values.scope
|
||||||
|
}
|
||||||
|
if (this.isValidValueForKey(values, 'sslverification')) {
|
||||||
|
params.sslverification = values.sslverification
|
||||||
|
}
|
||||||
|
if (this.isValidValueForKey(values, 'secretkey')) {
|
||||||
|
params.secretkey = values.secretkey
|
||||||
|
}
|
||||||
|
if (values.domainid) {
|
||||||
|
params.domainid = values.domainid
|
||||||
|
}
|
||||||
|
if (values.scope === 'Local' && values.domainid && !values.account) {
|
||||||
|
this.$notification.error({
|
||||||
|
message: this.$t('message.request.failed'),
|
||||||
|
description: this.$t('message.error.create.webhook.local.account')
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (values.account) {
|
||||||
|
const accountItem = _.find(this.accounts, (option) => option.id === values.account)
|
||||||
|
if (accountItem) {
|
||||||
|
params.account = accountItem.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.loading = true
|
||||||
|
api('createWebhook', params).then(json => {
|
||||||
|
this.$emit('refresh-data')
|
||||||
|
this.$notification.success({
|
||||||
|
message: this.$t('label.create.webhook'),
|
||||||
|
description: `${this.$t('message.success.create.webhook')} ${params.name}`
|
||||||
|
})
|
||||||
|
this.closeAction()
|
||||||
|
}).catch(error => {
|
||||||
|
this.$notifyError(error)
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
})
|
||||||
|
}).catch(error => {
|
||||||
|
this.formRef.value.scrollToField(error.errorFields[0].name)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
closeAction () {
|
||||||
|
this.$emit('close-action')
|
||||||
|
},
|
||||||
|
handleParamUpdate (e) {
|
||||||
|
this.$refs.dispatchview.timedTestWebhookDelivery()
|
||||||
|
},
|
||||||
|
handleScopeChange (e) {
|
||||||
|
if (['Domain', 'Local'].includes(this.form.scope)) {
|
||||||
|
this.fetchDomainData()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleDomainChanged (domainid) {
|
||||||
|
if (domainid) {
|
||||||
|
this.fetchAccountData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.form-layout {
|
||||||
|
width: 80vw;
|
||||||
|
|
||||||
|
@media (min-width: 700px) {
|
||||||
|
width: 650px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ssl-alert {
|
||||||
|
margin: 0 0 10px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user