mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 01:32:18 +02: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_multipleips_per_nic
|
||||
smoke/test_nested_virtualization
|
||||
smoke/test_set_sourcenat",
|
||||
smoke/test_set_sourcenat
|
||||
smoke/test_webhook_lifecycle",
|
||||
"smoke/test_network
|
||||
smoke/test_network_acl
|
||||
smoke/test_network_ipv6
|
||||
|
||||
@ -175,6 +175,8 @@ public class ApiConstants {
|
||||
public static final String END_IPV6 = "endipv6";
|
||||
public static final String END_PORT = "endport";
|
||||
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 EXTRA_CONFIG = "extraconfig";
|
||||
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_STATE = "hastate";
|
||||
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 HOST_ID = "hostid";
|
||||
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 KEEPALIVE_ENABLED = "keepaliveenabled";
|
||||
public static final String KERNEL_VERSION = "kernelversion";
|
||||
public static final String KEY = "key";
|
||||
public static final String LABEL = "label";
|
||||
public static final String LASTNAME = "lastname";
|
||||
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 PATH = "path";
|
||||
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_NAME = "podname";
|
||||
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 SCHEDULE = "schedule";
|
||||
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 SECONDARY_IP = "secondaryip";
|
||||
public static final String SECRET_KEY = "secretkey";
|
||||
public static final String SECURITY_GROUP_IDS = "securitygroupids";
|
||||
public static final String SECURITY_GROUP_NAMES = "securitygroupnames";
|
||||
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 SIGNATURE = "signature";
|
||||
public static final String SIGNATURE_VERSION = "signatureversion";
|
||||
public static final String SINCE = "since";
|
||||
public static final String SIZE = "size";
|
||||
public static final String SNAPSHOT = "snapshot";
|
||||
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_QUIESCEVM = "quiescevm";
|
||||
public static final String SOURCE_ZONE_ID = "sourcezoneid";
|
||||
public static final String SUITABLE_FOR_VM = "suitableforvirtualmachine";
|
||||
public static final String SUPPORTS_STORAGE_SNAPSHOT = "supportsstoragesnapshot";
|
||||
public static final String SSL_VERIFICATION = "sslverification";
|
||||
public static final String START_DATE = "startdate";
|
||||
public static final String START_ID = "startid";
|
||||
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 TAGS = "tags";
|
||||
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 TEMPLATE_FILTER = "templatefilter";
|
||||
public static final String TEMPLATE_ID = "templateid";
|
||||
@ -482,6 +488,7 @@ public class ApiConstants {
|
||||
public static final String USERNAME = "username";
|
||||
public static final String USER_CONFIGURABLE = "userconfigurable";
|
||||
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 UPDATE_IN_SEQUENCE = "updateinsequence";
|
||||
public static final String VALUE = "value";
|
||||
@ -561,6 +568,7 @@ public class ApiConstants {
|
||||
public static final String ALLOCATION_STATE = "allocationstate";
|
||||
public static final String MANAGED_STATE = "managedstate";
|
||||
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_ID = "storageid";
|
||||
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 WEBHOOK_ID = "webhookid";
|
||||
public static final String WEBHOOK_NAME = "webhookname";
|
||||
|
||||
/**
|
||||
* 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).
|
||||
|
||||
@ -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)
|
||||
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;
|
||||
|
||||
@Parameter(name = ApiConstants.TIMEZONE,
|
||||
|
||||
@ -98,7 +98,7 @@ public class BucketResponse extends BaseResponseWithTagInformation implements Co
|
||||
@Param(description = "Bucket Access Key")
|
||||
private String accessKey;
|
||||
|
||||
@SerializedName(ApiConstants.SECRET_KEY)
|
||||
@SerializedName(ApiConstants.USER_SECRET_KEY)
|
||||
@Param(description = "Bucket Secret Key")
|
||||
private String secretKey;
|
||||
|
||||
|
||||
@ -438,6 +438,11 @@
|
||||
<artifactId>cloud-mom-kafka</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloud-mom-webhook</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.cloudstack</groupId>
|
||||
<artifactId>cloud-framework-agent-lb</artifactId>
|
||||
|
||||
@ -288,10 +288,10 @@
|
||||
</bean>
|
||||
|
||||
<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" />
|
||||
</bean>
|
||||
|
||||
|
||||
<bean id="vpcProvidersRegistry"
|
||||
class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
|
||||
<property name="excludeKey" value="vpc.providers.exclude" />
|
||||
@ -358,4 +358,9 @@
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="eventBusRegistry"
|
||||
class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
|
||||
<property name="excludeKey" value="event.buses.exclude" />
|
||||
</bean>
|
||||
</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.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.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.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.dao.DataCenterDao;
|
||||
@ -50,6 +49,7 @@ public class UsageEventUtils {
|
||||
protected static Logger LOGGER = LogManager.getLogger(UsageEventUtils.class);
|
||||
protected static EventBus s_eventBus = null;
|
||||
protected static ConfigurationDao s_configDao;
|
||||
private static EventDistributor eventDistributor;
|
||||
|
||||
@Inject
|
||||
UsageEventDao usageEventDao;
|
||||
@ -207,9 +207,9 @@ public class UsageEventUtils {
|
||||
if( !configValue)
|
||||
return;
|
||||
try {
|
||||
s_eventBus = ComponentContext.getComponent(EventBus.class);
|
||||
eventDistributor = ComponentContext.getComponent(EventDistributor.class);
|
||||
} 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);
|
||||
@ -238,11 +238,7 @@ public class UsageEventUtils {
|
||||
|
||||
event.setDescription(eventDescription);
|
||||
|
||||
try {
|
||||
s_eventBus.publish(event);
|
||||
} catch (EventBusException e) {
|
||||
LOGGER.warn("Failed to publish usage event on the event bus.");
|
||||
}
|
||||
eventDistributor.publish(event);
|
||||
}
|
||||
|
||||
static final String Name = "management-server";
|
||||
|
||||
@ -25,11 +25,9 @@ import java.util.Map;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.framework.events.EventBus;
|
||||
import org.apache.cloudstack.framework.events.EventBusException;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.cloudstack.framework.events.EventDistributor;
|
||||
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.network.Network.Event;
|
||||
@ -43,7 +41,7 @@ public class NetworkStateListener implements StateListener<State, Event, Network
|
||||
@Inject
|
||||
private ConfigurationDao _configDao;
|
||||
|
||||
private static EventBus s_eventBus = null;
|
||||
private EventDistributor eventDistributor;
|
||||
|
||||
protected Logger logger = LogManager.getLogger(getClass());
|
||||
|
||||
@ -51,6 +49,10 @@ public class NetworkStateListener implements StateListener<State, Event, Network
|
||||
_configDao = configDao;
|
||||
}
|
||||
|
||||
public void setEventDistributor(EventDistributor eventDistributor) {
|
||||
this.eventDistributor = eventDistributor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preStateTransitionEvent(State oldState, Event event, State newState, Network vo, boolean status, Object opaque) {
|
||||
pubishOnEventBus(event.name(), "preStateTransitionEvent", vo, oldState, newState);
|
||||
@ -66,23 +68,20 @@ public class NetworkStateListener implements StateListener<State, Event, Network
|
||||
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 value = _configDao.getValue(configKey);
|
||||
boolean configValue = Boolean.parseBoolean(value);
|
||||
if(!configValue)
|
||||
return;
|
||||
try {
|
||||
s_eventBus = ComponentContext.getComponent(EventBus.class);
|
||||
} catch (NoSuchBeanDefinitionException nbe) {
|
||||
return; // no provider is configured to provide events bus, so just return
|
||||
if (eventDistributor == null) {
|
||||
setEventDistributor(ComponentContext.getComponent(EventDistributor.class));
|
||||
}
|
||||
|
||||
String resourceName = getEntityFromClassName(Network.class.getName());
|
||||
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());
|
||||
Map<String, String> eventDescription = new HashMap<String, String>();
|
||||
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<>();
|
||||
eventDescription.put("resource", resourceName);
|
||||
eventDescription.put("id", vo.getUuid());
|
||||
eventDescription.put("old-state", oldState.name());
|
||||
@ -92,11 +91,8 @@ public class NetworkStateListener implements StateListener<State, Event, Network
|
||||
eventDescription.put("eventDateTime", eventDate);
|
||||
|
||||
eventMsg.setDescription(eventDescription);
|
||||
try {
|
||||
s_eventBus.publish(eventMsg);
|
||||
} catch (EventBusException e) {
|
||||
logger.warn("Failed to publish state change event on the event bus.");
|
||||
}
|
||||
|
||||
eventDistributor.publish(eventMsg);
|
||||
}
|
||||
|
||||
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','nsx_mode', 'varchar(32) COMMENT "mode in which the network would route traffic"');
|
||||
|
||||
|
||||
-- Create table to persist quota email template configurations
|
||||
CREATE TABLE IF NOT EXISTS `cloud_usage`.`quota_email_configuration`(
|
||||
`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
|
||||
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;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.annotations.Expose;
|
||||
|
||||
public class Event {
|
||||
|
||||
@Expose(serialize = false, deserialize = false)
|
||||
Long eventId;
|
||||
@Expose(serialize = false, deserialize = false)
|
||||
String eventUuid;
|
||||
String eventCategory;
|
||||
String eventType;
|
||||
String eventSource;
|
||||
String resourceType;
|
||||
String resourceUUID;
|
||||
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) {
|
||||
this.eventCategory = eventCategory;
|
||||
this.eventType = eventType;
|
||||
this.eventSource = eventSource;
|
||||
this.resourceType = resourceType;
|
||||
this.resourceUUID = resourceUUID;
|
||||
setEventCategory(eventCategory);
|
||||
setEventType(eventType);
|
||||
setEventSource(eventSource);
|
||||
setResourceType(resourceType);
|
||||
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() {
|
||||
@ -68,7 +95,7 @@ public class Event {
|
||||
|
||||
public void setDescription(Object message) {
|
||||
Gson gson = new Gson();
|
||||
this.description = gson.toJson(message).toString();
|
||||
this.description = gson.toJson(message);
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
@ -90,4 +117,28 @@ public class Event {
|
||||
public String getResourceUUID() {
|
||||
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 {
|
||||
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
package org.apache.cloudstack.api.response;
|
||||
|
||||
import com.cloud.serializer.Param;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.BaseResponse;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import com.cloud.serializer.Param;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class ApiDiscoveryResponse extends BaseResponse {
|
||||
@ -64,6 +65,18 @@ public class ApiDiscoveryResponse extends BaseResponse {
|
||||
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) {
|
||||
this.name = name;
|
||||
}
|
||||
@ -123,4 +136,8 @@ public class ApiDiscoveryResponse extends BaseResponse {
|
||||
public Set<ApiResponseResponse> getApiResponse() {
|
||||
return apiResponse;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,12 +16,14 @@
|
||||
// under the License.
|
||||
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.BaseResponse;
|
||||
|
||||
import com.cloud.serializer.Param;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class ApiParameterResponse extends BaseResponse {
|
||||
@SerializedName(ApiConstants.NAME)
|
||||
@ -52,6 +54,8 @@ public class ApiParameterResponse extends BaseResponse {
|
||||
@Param(description = "comma separated related apis to get the parameter")
|
||||
private String related;
|
||||
|
||||
private transient List<RoleType> authorizedRoleTypes = null;
|
||||
|
||||
public ApiParameterResponse() {
|
||||
}
|
||||
|
||||
@ -87,4 +91,11 @@ public class ApiParameterResponse extends BaseResponse {
|
||||
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.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -28,21 +30,22 @@ import java.util.Set;
|
||||
import javax.inject.Inject;
|
||||
|
||||
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.BaseAsyncCmd;
|
||||
import org.apache.cloudstack.api.BaseAsyncCreateCmd;
|
||||
import org.apache.cloudstack.api.BaseCmd;
|
||||
import org.apache.cloudstack.api.BaseResponse;
|
||||
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.response.ApiDiscoveryResponse;
|
||||
import org.apache.cloudstack.api.response.ApiParameterResponse;
|
||||
import org.apache.cloudstack.api.response.ApiResponseResponse;
|
||||
import org.apache.cloudstack.api.response.ListResponse;
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.reflections.ReflectionUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
@ -215,6 +218,9 @@ public class ApiDiscoveryServiceImpl extends ComponentLifecycleBase implements A
|
||||
paramResponse.setSince(parameterAnnotation.since());
|
||||
}
|
||||
paramResponse.setRelated(parameterAnnotation.entityType()[0].getName());
|
||||
if (parameterAnnotation.authorized() != null) {
|
||||
paramResponse.setAuthorizedRoleTypes(Arrays.asList(parameterAnnotation.authorized()));
|
||||
}
|
||||
response.addParam(paramResponse);
|
||||
}
|
||||
}
|
||||
@ -247,6 +253,7 @@ public class ApiDiscoveryServiceImpl extends ComponentLifecycleBase implements A
|
||||
|
||||
if (user == null)
|
||||
return null;
|
||||
Account account = accountService.getAccount(user.getAccountId());
|
||||
|
||||
if (name != null) {
|
||||
if (!s_apiNameDiscoveryResponseMap.containsKey(name))
|
||||
@ -260,10 +267,9 @@ public class ApiDiscoveryServiceImpl extends ComponentLifecycleBase implements A
|
||||
return null;
|
||||
}
|
||||
}
|
||||
responseList.add(s_apiNameDiscoveryResponseMap.get(name));
|
||||
responseList.add(getApiDiscoveryResponseWithAccessibleParams(name, account));
|
||||
|
||||
} else {
|
||||
Account account = accountService.getAccount(user.getAccountId());
|
||||
if (account == null) {
|
||||
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) {
|
||||
responseList.add(s_apiNameDiscoveryResponseMap.get(apiName));
|
||||
responseList.add(getApiDiscoveryResponseWithAccessibleParams(apiName, account));
|
||||
}
|
||||
}
|
||||
response.setResponses(responseList);
|
||||
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
|
||||
public List<Class<?>> getCommands() {
|
||||
List<Class<?>> cmdList = new ArrayList<Class<?>>();
|
||||
|
||||
@ -60,6 +60,8 @@ public class InMemoryEventBus extends ManagerBase implements EventBus {
|
||||
if (subscriber == null || topic == null) {
|
||||
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();
|
||||
|
||||
subscribers.put(subscriberId, new Pair<EventTopic, EventSubscriber>(topic, subscriber));
|
||||
@ -68,6 +70,7 @@ public class InMemoryEventBus extends ManagerBase implements EventBus {
|
||||
|
||||
@Override
|
||||
public void unsubscribe(UUID subscriberId, EventSubscriber subscriber) throws EventBusException {
|
||||
logger.debug("unsubscribing '{}'", subscriberId);
|
||||
if (subscriberId == null) {
|
||||
throw new EventBusException("Cannot unregister a null subscriberId.");
|
||||
}
|
||||
@ -85,7 +88,9 @@ public class InMemoryEventBus extends ManagerBase implements EventBus {
|
||||
|
||||
@Override
|
||||
public void publish(Event event) throws EventBusException {
|
||||
logger.trace("publish '{}'", event.getDescription());
|
||||
if (subscribers == null || subscribers.isEmpty()) {
|
||||
logger.trace("no subscribers, no publish");
|
||||
return; // no subscriber to publish to, so just return
|
||||
}
|
||||
|
||||
|
||||
@ -87,19 +87,23 @@ public class KafkaEventBus extends ManagerBase implements EventBus {
|
||||
|
||||
@Override
|
||||
public UUID subscribe(EventTopic topic, EventSubscriber subscriber) throws EventBusException {
|
||||
logger.debug("subscribing '{}' to events of type '{}' from '{}'", subscriber.toString(), topic.getEventType(), topic.getEventSource());
|
||||
|
||||
/* NOOP */
|
||||
return UUID.randomUUID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribe(UUID subscriberId, EventSubscriber subscriber) throws EventBusException {
|
||||
logger.debug("unsubscribing '{}'", subscriberId);
|
||||
/* NOOP */
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publish(Event event) throws EventBusException {
|
||||
ProducerRecord<String, String> record = new ProducerRecord<String,String>(_topic, event.getResourceUUID(), event.getDescription());
|
||||
_producer.send(record);
|
||||
logger.trace("publish '{}'", event.getDescription());
|
||||
ProducerRecord<String, String> newRecord = new ProducerRecord<>(_topic, event.getResourceUUID(), event.getDescription());
|
||||
_producer.send(newRecord);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -185,11 +185,12 @@ public class RabbitMQEventBus extends ManagerBase implements EventBus {
|
||||
*/
|
||||
@Override
|
||||
public UUID subscribe(EventTopic topic, EventSubscriber subscriber) throws EventBusException {
|
||||
|
||||
if (subscriber == null || topic == null) {
|
||||
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
|
||||
// for on the queue used for the subscriber on the AMQP broker
|
||||
UUID queueId = UUID.randomUUID();
|
||||
@ -250,6 +251,7 @@ public class RabbitMQEventBus extends ManagerBase implements EventBus {
|
||||
|
||||
@Override
|
||||
public void unsubscribe(UUID subscriberId, EventSubscriber subscriber) throws EventBusException {
|
||||
logger.debug("unsubscribing '{}'", subscriberId);
|
||||
try {
|
||||
String classname = subscriber.getClass().getName();
|
||||
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
|
||||
@Override
|
||||
public void publish(Event event) throws EventBusException {
|
||||
logger.trace("publish '{}'", event.getDescription());
|
||||
|
||||
String routingKey = createRoutingKey(event);
|
||||
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;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
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.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.ActionEvents;
|
||||
@ -43,15 +41,20 @@ import com.cloud.server.ManagementService;
|
||||
import com.cloud.utils.component.ComponentContext;
|
||||
import com.cloud.utils.component.ComponentMethodInterceptor;
|
||||
|
||||
|
||||
@Component
|
||||
public class EventUtils {
|
||||
protected static Logger LOGGER = LogManager.getLogger(EventUtils.class);
|
||||
|
||||
protected static EventBus s_eventBus = null;
|
||||
private static EventDistributor eventDistributor;
|
||||
|
||||
public EventUtils() {
|
||||
}
|
||||
|
||||
public static void setEventDistributor(EventDistributor eventDistributorImpl) {
|
||||
eventDistributor = eventDistributorImpl;
|
||||
}
|
||||
|
||||
private static void publishOnMessageBus(String eventCategory, String eventType, String details, Event.State state) {
|
||||
|
||||
if (state != com.cloud.event.Event.State.Completed) {
|
||||
@ -59,7 +62,7 @@ public class EventUtils {
|
||||
}
|
||||
|
||||
try {
|
||||
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
|
||||
}
|
||||
@ -67,18 +70,12 @@ public class EventUtils {
|
||||
org.apache.cloudstack.framework.events.Event event =
|
||||
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("status", state.toString());
|
||||
eventDescription.put("details", details);
|
||||
event.setDescription(eventDescription);
|
||||
try {
|
||||
s_eventBus.publish(event);
|
||||
} catch (EventBusException evx) {
|
||||
String errMsg = "Failed to publish contrail event.";
|
||||
LOGGER.warn(errMsg, evx);
|
||||
}
|
||||
|
||||
eventDistributor.publish(event);
|
||||
}
|
||||
|
||||
public static class EventInterceptor implements ComponentMethodInterceptor, MethodInterceptor {
|
||||
@ -119,7 +116,7 @@ public class EventUtils {
|
||||
}
|
||||
|
||||
protected List<ActionEvent> getActionEvents(Method m) {
|
||||
List<ActionEvent> result = new ArrayList<ActionEvent>();
|
||||
List<ActionEvent> result = new ArrayList<>();
|
||||
|
||||
ActionEvents events = m.getAnnotation(ActionEvents.class);
|
||||
|
||||
|
||||
@ -79,6 +79,7 @@
|
||||
<module>event-bus/inmemory</module>
|
||||
<module>event-bus/kafka</module>
|
||||
<module>event-bus/rabbitmq</module>
|
||||
<module>event-bus/webhook</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.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.config.Configurable;
|
||||
import org.apache.cloudstack.framework.events.EventBus;
|
||||
import org.apache.cloudstack.framework.events.EventBusException;
|
||||
import org.apache.cloudstack.framework.events.EventDistributor;
|
||||
import org.apache.cloudstack.framework.jobs.AsyncJob;
|
||||
import org.apache.cloudstack.framework.jobs.AsyncJobManager;
|
||||
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.ResponseDate;
|
||||
import org.apache.http.protocol.ResponseServer;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
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]";
|
||||
|
||||
@Inject
|
||||
private AccountManager accountMgr;
|
||||
@Inject
|
||||
private APIAuthenticationManager authManager;
|
||||
@Inject
|
||||
private ApiDispatcher dispatcher;
|
||||
@Inject
|
||||
private DispatchChainFactory dispatchChainFactory;
|
||||
private AsyncJobManager asyncMgr;
|
||||
@Inject
|
||||
private AccountManager accountMgr;
|
||||
private DispatchChainFactory dispatchChainFactory;
|
||||
@Inject
|
||||
private DomainManager domainMgr;
|
||||
@Inject
|
||||
private DomainDao domainDao;
|
||||
@Inject
|
||||
private UUIDManager uuidMgr;
|
||||
@Inject
|
||||
private AsyncJobManager asyncMgr;
|
||||
@Inject
|
||||
private EntityManager entityMgr;
|
||||
@Inject
|
||||
private APIAuthenticationManager authManager;
|
||||
@Inject
|
||||
private ProjectDao projectDao;
|
||||
@Inject
|
||||
private UUIDManager uuidMgr;
|
||||
|
||||
private List<PluggableService> pluggableServices;
|
||||
|
||||
@ -224,6 +222,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
||||
@Inject
|
||||
private ApiAsyncJobDispatcher asyncDispatcher;
|
||||
|
||||
private EventDistributor eventDistributor = null;
|
||||
private static int s_workerCount = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
public void setEventDistributor(EventDistributor eventDistributor) {
|
||||
this.eventDistributor = eventDistributor;
|
||||
}
|
||||
|
||||
@MessageHandler(topic = AsyncJob.Topics.JOB_EVENT_PUBLISH)
|
||||
public void handleAsyncJobPublishEvent(String subject, String senderAddress, Object args) {
|
||||
assert (args != null);
|
||||
@ -322,12 +325,8 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
||||
|
||||
if (logger.isTraceEnabled())
|
||||
logger.trace("Handle asyjob publish event " + jobEvent);
|
||||
|
||||
EventBus eventBus = null;
|
||||
try {
|
||||
eventBus = ComponentContext.getComponent(EventBus.class);
|
||||
} catch (NoSuchBeanDefinitionException nbe) {
|
||||
return; // no provider is configured to provide events bus, so just return
|
||||
if (eventDistributor == null) {
|
||||
setEventDistributor(ComponentContext.getComponent(EventDistributor.class));
|
||||
}
|
||||
|
||||
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
|
||||
String info = job.getCmdInfo();
|
||||
String cmdEventType = "unknown";
|
||||
Map<String, Object> cmdInfoObj = new HashMap<String, Object>();
|
||||
Map<String, Object> cmdInfoObj = new HashMap<>();
|
||||
if (info != null) {
|
||||
Type type = new TypeToken<Map<String, String>>(){}.getType();
|
||||
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(),
|
||||
jobEvent, instanceType, instanceUuid);
|
||||
|
||||
Map<String, Object> eventDescription = new HashMap<String, Object>();
|
||||
Map<String, Object> eventDescription = new HashMap<>();
|
||||
eventDescription.put("command", job.getCmd());
|
||||
eventDescription.put("user", userJobOwner.getUuid());
|
||||
eventDescription.put("account", jobOwner.getUuid());
|
||||
@ -389,13 +388,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
||||
eventDescription.put("domainname", domain.getName());
|
||||
}
|
||||
event.setDescription(eventDescription);
|
||||
|
||||
try {
|
||||
eventBus.publish(event);
|
||||
} catch (EventBusException evx) {
|
||||
String errMsg = "Failed to publish async job event on the event bus.";
|
||||
logger.warn(errMsg, evx);
|
||||
}
|
||||
eventDistributor.publish(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -32,12 +32,11 @@ import org.apache.cloudstack.api.Identity;
|
||||
import org.apache.cloudstack.api.InternalIdentity;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
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.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
|
||||
import com.cloud.configuration.Config;
|
||||
@ -63,7 +62,7 @@ public class ActionEventUtils {
|
||||
private static AccountDao s_accountDao;
|
||||
private static ProjectDao s_projectDao;
|
||||
protected static UserDao s_userDao;
|
||||
protected static EventBus s_eventBus = null;
|
||||
private static EventDistributor eventDistributor;
|
||||
protected static EntityManager s_entityMgr;
|
||||
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) {
|
||||
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, true, description, resourceDetails.first(), resourceDetails.third(), null);
|
||||
Event event = persistActionEvent(userId, accountId, domainId, null, type, Event.State.Completed,
|
||||
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();
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
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, eventDisplayEnabled, description, resourceDetails.first(), resourceDetails.third(), startEventId);
|
||||
Event event = persistActionEvent(userId, accountId, null, null, type, Event.State.Scheduled,
|
||||
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();
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
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, eventDisplayEnabled, description, resourceDetails.first(), resourceDetails.third(), startEventId);
|
||||
Event event = persistActionEvent(userId, accountId, null, null, type, Event.State.Started,
|
||||
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();
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
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, eventDisplayEnabled, description, resourceDetails.first(), resourceDetails.third(), startEventId);
|
||||
Event event = persistActionEvent(userId, accountId, null, level, type, Event.State.Completed,
|
||||
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();
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
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, eventDisplayEnabled, description, resourceDetails.first(), resourceDetails.third(), null);
|
||||
Event event = persistActionEvent(userId, accountId, null, level, type, Event.State.Created,
|
||||
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();
|
||||
}
|
||||
|
||||
@ -193,20 +202,25 @@ public class ActionEventUtils {
|
||||
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 value = s_configDao.getValue(configKey);
|
||||
boolean configValue = Boolean.parseBoolean(value);
|
||||
if(!configValue)
|
||||
return;
|
||||
|
||||
try {
|
||||
s_eventBus = ComponentContext.getComponent(EventBus.class);
|
||||
eventDistributor = ComponentContext.getComponent(EventDistributor.class);
|
||||
} catch (NoSuchBeanDefinitionException nbe) {
|
||||
return; // no provider is configured to provide events bus, so just return
|
||||
}
|
||||
|
||||
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>();
|
||||
Project project = s_projectDao.findByProjectAccountId(accountId);
|
||||
@ -219,6 +233,9 @@ public class ActionEventUtils {
|
||||
return;
|
||||
if (project != null)
|
||||
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("account", account.getUuid());
|
||||
eventDescription.put("event", eventType);
|
||||
@ -234,11 +251,13 @@ public class ActionEventUtils {
|
||||
|
||||
event.setDescription(eventDescription);
|
||||
|
||||
try {
|
||||
s_eventBus.publish(event);
|
||||
} catch (EventBusException e) {
|
||||
LOGGER.warn("Failed to publish action event on the event bus.");
|
||||
}
|
||||
eventDistributor.publish(event);
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
@ -25,15 +25,13 @@ import java.util.Map;
|
||||
import javax.annotation.PostConstruct;
|
||||
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.Logger;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
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.dc.DataCenterVO;
|
||||
import com.cloud.dc.HostPodVO;
|
||||
@ -48,8 +46,8 @@ public class AlertGenerator {
|
||||
protected static Logger LOGGER = LogManager.getLogger(AlertGenerator.class);
|
||||
private static DataCenterDao s_dcDao;
|
||||
private static HostPodDao s_podDao;
|
||||
protected static EventBus s_eventBus = null;
|
||||
protected static ConfigurationDao s_configDao;
|
||||
protected static EventDistributor eventDistributor;
|
||||
|
||||
@Inject
|
||||
DataCenterDao dcDao;
|
||||
@ -76,9 +74,9 @@ public class AlertGenerator {
|
||||
if(!configValue)
|
||||
return;
|
||||
try {
|
||||
s_eventBus = ComponentContext.getComponent(EventBus.class);
|
||||
eventDistributor = ComponentContext.getComponent(EventDistributor.class);
|
||||
} 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 =
|
||||
@ -107,10 +105,6 @@ public class AlertGenerator {
|
||||
|
||||
event.setDescription(eventDescription);
|
||||
|
||||
try {
|
||||
s_eventBus.publish(event);
|
||||
} catch (EventBusException e) {
|
||||
LOGGER.warn("Failed to publish alert on the event bus.");
|
||||
}
|
||||
eventDistributor.publish(event);
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@ package com.cloud.projects;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -35,6 +36,7 @@ import javax.inject.Inject;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.acl.ProjectRole;
|
||||
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
||||
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.SMTPMailProperties;
|
||||
import org.apache.cloudstack.utils.mailing.SMTPMailSender;
|
||||
import org.apache.cloudstack.webhook.WebhookHelper;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.cloud.api.ApiDBUtils;
|
||||
@ -89,6 +93,7 @@ import com.cloud.user.ResourceLimitService;
|
||||
import com.cloud.user.User;
|
||||
import com.cloud.user.dao.AccountDao;
|
||||
import com.cloud.user.dao.UserDao;
|
||||
import com.cloud.utils.component.ComponentContext;
|
||||
import com.cloud.utils.component.ManagerBase;
|
||||
import com.cloud.utils.concurrency.NamedThreadFactory;
|
||||
import com.cloud.utils.db.DB;
|
||||
@ -163,6 +168,17 @@ public class ProjectManagerImpl extends ManagerBase implements ProjectManager, C
|
||||
private String senderAddress;
|
||||
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
|
||||
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<NetworkVO> networks = _networkDao.listByOwner(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())
|
||||
.map(entity -> entity.size() + " " + entity.get(0).getEntityType().getSimpleName() + " to clean up")
|
||||
.findFirst();
|
||||
|
||||
@ -26,11 +26,9 @@ import javax.annotation.PostConstruct;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
import org.apache.cloudstack.framework.events.EventBus;
|
||||
import org.apache.cloudstack.framework.events.EventBusException;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.cloudstack.framework.events.EventDistributor;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.cloud.configuration.Config;
|
||||
@ -47,12 +45,12 @@ import com.cloud.utils.fsm.StateMachine2;
|
||||
@Component
|
||||
public class SnapshotStateListener implements StateListener<State, Event, SnapshotVO> {
|
||||
|
||||
protected static EventBus s_eventBus = null;
|
||||
protected static ConfigurationDao s_configDao;
|
||||
|
||||
@Inject
|
||||
private ConfigurationDao configDao;
|
||||
|
||||
private EventDistributor eventDistributor = null;
|
||||
protected Logger logger = LogManager.getLogger(getClass());
|
||||
|
||||
public SnapshotStateListener() {
|
||||
@ -64,6 +62,10 @@ public class SnapshotStateListener implements StateListener<State, Event, Snapsh
|
||||
s_configDao = configDao;
|
||||
}
|
||||
|
||||
public void setEventDistributor(EventDistributor eventDistributor) {
|
||||
this.eventDistributor = eventDistributor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preStateTransitionEvent(State oldState, Event event, State newState, SnapshotVO vo, boolean status, Object opaque) {
|
||||
pubishOnEventBus(event.name(), "preStateTransitionEvent", vo, oldState, newState);
|
||||
@ -84,17 +86,15 @@ public class SnapshotStateListener implements StateListener<State, Event, Snapsh
|
||||
if(!configValue) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
s_eventBus = ComponentContext.getComponent(EventBus.class);
|
||||
} catch (NoSuchBeanDefinitionException nbe) {
|
||||
return; // no provider is configured to provide events bus, so just return
|
||||
if (eventDistributor == null) {
|
||||
setEventDistributor(ComponentContext.getComponent(EventDistributor.class));
|
||||
}
|
||||
|
||||
String resourceName = getEntityFromClassName(Snapshot.class.getName());
|
||||
org.apache.cloudstack.framework.events.Event eventMsg =
|
||||
new org.apache.cloudstack.framework.events.Event(ManagementService.Name, 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("id", vo.getUuid());
|
||||
eventDescription.put("old-state", oldState.name());
|
||||
@ -104,11 +104,7 @@ public class SnapshotStateListener implements StateListener<State, Event, Snapsh
|
||||
eventDescription.put("eventDateTime", eventDate);
|
||||
|
||||
eventMsg.setDescription(eventDescription);
|
||||
try {
|
||||
s_eventBus.publish(eventMsg);
|
||||
} catch (EventBusException e) {
|
||||
logger.warn("Failed to publish state change event on the event bus.");
|
||||
}
|
||||
eventDistributor.publish(eventMsg);
|
||||
}
|
||||
|
||||
private String getEntityFromClassName(String entityClassName) {
|
||||
|
||||
@ -22,35 +22,32 @@ import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
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.events.EventBus;
|
||||
import org.apache.cloudstack.framework.events.EventBusException;
|
||||
import org.apache.cloudstack.framework.events.EventDistributor;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import com.cloud.configuration.Config;
|
||||
import com.cloud.event.EventCategory;
|
||||
import com.cloud.event.EventTypes;
|
||||
import com.cloud.event.UsageEventUtils;
|
||||
import com.cloud.server.ManagementService;
|
||||
import com.cloud.storage.Volume;
|
||||
import com.cloud.storage.Volume.Event;
|
||||
import com.cloud.storage.Volume.State;
|
||||
import com.cloud.utils.component.ComponentContext;
|
||||
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> {
|
||||
|
||||
protected static EventBus s_eventBus = null;
|
||||
protected ConfigurationDao _configDao;
|
||||
protected VMInstanceDao _vmInstanceDao;
|
||||
|
||||
private EventDistributor eventDistributor;
|
||||
protected Logger logger = LogManager.getLogger(getClass());
|
||||
|
||||
public VolumeStateListener(ConfigurationDao configDao, VMInstanceDao vmInstanceDao) {
|
||||
@ -58,6 +55,10 @@ public class VolumeStateListener implements StateListener<State, Event, Volume>
|
||||
this._vmInstanceDao = vmInstanceDao;
|
||||
}
|
||||
|
||||
public void setEventDistributor(EventDistributor eventDistributor) {
|
||||
this.eventDistributor = eventDistributor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preStateTransitionEvent(State oldState, Event event, State newState, Volume vo, boolean status, Object opaque) {
|
||||
pubishOnEventBus(event.name(), "preStateTransitionEvent", vo, oldState, newState);
|
||||
@ -93,23 +94,21 @@ public class VolumeStateListener implements StateListener<State, Event, Volume>
|
||||
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 value = _configDao.getValue(configKey);
|
||||
boolean configValue = Boolean.parseBoolean(value);
|
||||
if(!configValue)
|
||||
return;
|
||||
try {
|
||||
s_eventBus = ComponentContext.getComponent(EventBus.class);
|
||||
} catch (NoSuchBeanDefinitionException nbe) {
|
||||
return; // no provider is configured to provide events bus, so just return
|
||||
if (eventDistributor == null) {
|
||||
setEventDistributor(ComponentContext.getComponent(EventDistributor.class));
|
||||
}
|
||||
|
||||
String resourceName = getEntityFromClassName(Volume.class.getName());
|
||||
org.apache.cloudstack.framework.events.Event eventMsg =
|
||||
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>();
|
||||
eventDescription.put("resource", resourceName);
|
||||
eventDescription.put("id", vo.getUuid());
|
||||
@ -120,11 +119,7 @@ public class VolumeStateListener implements StateListener<State, Event, Volume>
|
||||
eventDescription.put("eventDateTime", eventDate);
|
||||
|
||||
eventMsg.setDescription(eventDescription);
|
||||
try {
|
||||
s_eventBus.publish(eventMsg);
|
||||
} catch (EventBusException e) {
|
||||
logger.warn("Failed to state change event on the event bus.");
|
||||
}
|
||||
eventDistributor.publish(eventMsg);
|
||||
}
|
||||
|
||||
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.dao.UserDetailsDao;
|
||||
import org.apache.cloudstack.utils.baremetal.BaremetalUtils;
|
||||
import org.apache.cloudstack.webhook.WebhookHelper;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
|
||||
import com.cloud.api.ApiDBUtils;
|
||||
import com.cloud.api.auth.SetupUserTwoFactorAuthenticationCmd;
|
||||
@ -168,6 +170,7 @@ import com.cloud.utils.ConstantTimeComparator;
|
||||
import com.cloud.utils.NumbersUtil;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.Ternary;
|
||||
import com.cloud.utils.component.ComponentContext;
|
||||
import com.cloud.utils.component.Manager;
|
||||
import com.cloud.utils.component.ManagerBase;
|
||||
import com.cloud.utils.component.PluggableService;
|
||||
@ -426,6 +429,15 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
||||
_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
|
||||
public List<String> getApiNameList() {
|
||||
return apiNameList;
|
||||
@ -1105,6 +1117,9 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
||||
// Delete registered UserData
|
||||
userDataDao.removeByAccountId(accountId);
|
||||
|
||||
// Delete Webhooks
|
||||
deleteWebhooksForAccount(accountId);
|
||||
|
||||
return true;
|
||||
} catch (Exception ex) {
|
||||
logger.warn("Failed to cleanup account " + account + " due to ", ex);
|
||||
|
||||
@ -24,15 +24,11 @@ import java.util.Map;
|
||||
|
||||
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.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.event.EventCategory;
|
||||
@ -41,12 +37,15 @@ import com.cloud.event.UsageEventUtils;
|
||||
import com.cloud.event.dao.UsageEventDao;
|
||||
import com.cloud.network.dao.NetworkDao;
|
||||
import com.cloud.network.dao.NetworkVO;
|
||||
import com.cloud.server.ManagementService;
|
||||
import com.cloud.service.dao.ServiceOfferingDao;
|
||||
import com.cloud.utils.component.ComponentContext;
|
||||
import com.cloud.utils.fsm.StateListener;
|
||||
import com.cloud.utils.fsm.StateMachine2;
|
||||
import com.cloud.vm.VirtualMachine.Event;
|
||||
import com.cloud.vm.VirtualMachine.State;
|
||||
import com.cloud.vm.dao.NicDao;
|
||||
import com.cloud.vm.dao.UserVmDao;
|
||||
|
||||
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 UserVmManager _userVmMgr;
|
||||
@Inject protected ConfigurationDao _configDao;
|
||||
private EventDistributor eventDistributor;
|
||||
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,
|
||||
ConfigurationDao configDao) {
|
||||
this._usageEventDao = usageEventDao;
|
||||
@ -130,16 +128,16 @@ public class UserVmStateListener implements StateListener<State, VirtualMachine.
|
||||
if(!configValue)
|
||||
return;
|
||||
try {
|
||||
s_eventBus = ComponentContext.getComponent(EventBus.class);
|
||||
eventDistributor = ComponentContext.getComponent(EventDistributor.class);
|
||||
} 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());
|
||||
org.apache.cloudstack.framework.events.Event eventMsg =
|
||||
new org.apache.cloudstack.framework.events.Event(ManagementService.Name, 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("id", vo.getUuid());
|
||||
eventDescription.put("old-state", oldState.name());
|
||||
@ -150,12 +148,7 @@ public class UserVmStateListener implements StateListener<State, VirtualMachine.
|
||||
eventDescription.put("eventDateTime", eventDate);
|
||||
|
||||
eventMsg.setDescription(eventDescription);
|
||||
try {
|
||||
s_eventBus.publish(eventMsg);
|
||||
} catch (org.apache.cloudstack.framework.events.EventBusException e) {
|
||||
logger.warn("Failed to publish state change event on the event bus.");
|
||||
}
|
||||
|
||||
eventDistributor.publish(eventMsg);
|
||||
}
|
||||
|
||||
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}" />
|
||||
</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" >
|
||||
<property name="hvGuruList" value="#{hypervisorGurusRegistry.registered}" />
|
||||
</bean>
|
||||
|
||||
@ -29,7 +29,8 @@ import org.apache.cloudstack.api.ApiCommandResourceType;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||
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.Assert;
|
||||
import org.junit.Before;
|
||||
@ -97,7 +98,7 @@ public class ActionEventUtilsTest {
|
||||
protected ConfigurationDao configDao;
|
||||
|
||||
@Mock
|
||||
protected EventBus eventBus;
|
||||
protected EventDistributor eventDistributor;
|
||||
|
||||
private AccountVO account;
|
||||
private UserVO user;
|
||||
@ -149,7 +150,7 @@ public class ActionEventUtilsTest {
|
||||
//Some basic mocks.
|
||||
Mockito.when(configDao.getValue(Config.PublishActionEvent.key())).thenReturn("true");
|
||||
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
|
||||
//methods.
|
||||
@ -166,14 +167,11 @@ public class ActionEventUtilsTest {
|
||||
});
|
||||
|
||||
//Needed to record events published on the bus.
|
||||
Mockito.doAnswer(new Answer<Void>() {
|
||||
@Override public Void answer(InvocationOnMock invocation) throws Throwable {
|
||||
Event event = (Event)invocation.getArguments()[0];
|
||||
publishedEvents.add(event);
|
||||
return null;
|
||||
}
|
||||
|
||||
}).when(eventBus).publish(Mockito.any(Event.class));
|
||||
Mockito.doAnswer((Answer<Map<String, EventBusException>>) invocation -> {
|
||||
Event event = (Event)invocation.getArguments()[0];
|
||||
publishedEvents.add(event);
|
||||
return new HashMap<>();
|
||||
}).when(eventDistributor).publish(Mockito.any(Event.class));
|
||||
|
||||
account = new AccountVO("testaccount", 1L, "networkdomain", Account.Type.NORMAL, "uuid");
|
||||
account.setId(ACCOUNT_ID);
|
||||
|
||||
@ -16,20 +16,27 @@
|
||||
// under the License.
|
||||
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.Before;
|
||||
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.Spy;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.cloud.projects.dao.ProjectDao;
|
||||
import com.cloud.utils.component.ComponentContext;
|
||||
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
@ -94,4 +101,31 @@ public class ProjectManagerImplTest {
|
||||
public void testUpdateProjectNameAndDisplayTextUpdateNameDisplayText() {
|
||||
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;
|
||||
|
||||
import com.cloud.dc.DataCenterVO;
|
||||
import com.cloud.dc.dao.DataCenterDao;
|
||||
import com.cloud.event.EventTypes;
|
||||
import com.cloud.event.UsageEventUtils;
|
||||
import com.cloud.event.UsageEventVO;
|
||||
import com.cloud.event.dao.UsageEventDao;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.org.Grouping;
|
||||
import com.cloud.server.StatsCollector;
|
||||
import com.cloud.storage.Storage.ImageFormat;
|
||||
import com.cloud.storage.TemplateProfile;
|
||||
import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
|
||||
import com.cloud.storage.VMTemplateVO;
|
||||
import com.cloud.storage.dao.VMTemplateZoneDao;
|
||||
import com.cloud.user.AccountVO;
|
||||
import com.cloud.user.ResourceLimitService;
|
||||
import com.cloud.user.dao.AccountDao;
|
||||
import com.cloud.utils.component.ComponentContext;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.anyLong;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
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.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.config.dao.ConfigurationDao;
|
||||
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.apache.cloudstack.framework.messagebus.MessageBus;
|
||||
import org.apache.cloudstack.secstorage.heuristics.HeuristicType;
|
||||
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.stubbing.Answer;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.anyLong;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.when;
|
||||
import com.cloud.dc.DataCenterVO;
|
||||
import com.cloud.dc.dao.DataCenterDao;
|
||||
import com.cloud.event.EventTypes;
|
||||
import com.cloud.event.UsageEventUtils;
|
||||
import com.cloud.event.UsageEventVO;
|
||||
import com.cloud.event.dao.UsageEventDao;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.org.Grouping;
|
||||
import com.cloud.server.StatsCollector;
|
||||
import com.cloud.storage.Storage.ImageFormat;
|
||||
import com.cloud.storage.TemplateProfile;
|
||||
import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
|
||||
import com.cloud.storage.VMTemplateVO;
|
||||
import com.cloud.storage.dao.VMTemplateZoneDao;
|
||||
import com.cloud.user.AccountVO;
|
||||
import com.cloud.user.ResourceLimitService;
|
||||
import com.cloud.user.dao.AccountDao;
|
||||
import com.cloud.utils.component.ComponentContext;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class HypervisorTemplateAdapterTest {
|
||||
@Mock
|
||||
EventBus _bus;
|
||||
EventDistributor eventDistributor;
|
||||
List<Event> events = new ArrayList<>();
|
||||
|
||||
@Mock
|
||||
@ -168,7 +168,7 @@ public class HypervisorTemplateAdapterTest {
|
||||
closeable.close();
|
||||
}
|
||||
|
||||
public UsageEventUtils setupUsageUtils() throws EventBusException {
|
||||
public UsageEventUtils setupUsageUtils() {
|
||||
Mockito.when(_configDao.getValue(eq("publish.usage.events"))).thenReturn("true");
|
||||
Mockito.when(_usageEventDao.persist(Mockito.any(UsageEventVO.class))).then(new Answer<Void>() {
|
||||
@Override public Void answer(InvocationOnMock invocation) throws Throwable {
|
||||
@ -180,16 +180,14 @@ public class HypervisorTemplateAdapterTest {
|
||||
|
||||
Mockito.when(_usageEventDao.listAll()).thenReturn(usageEvents);
|
||||
|
||||
doAnswer(new Answer<Void>() {
|
||||
@Override public Void answer(InvocationOnMock invocation) throws Throwable {
|
||||
Event event = (Event)invocation.getArguments()[0];
|
||||
events.add(event);
|
||||
return null;
|
||||
}
|
||||
}).when(_bus).publish(any(Event.class));
|
||||
doAnswer((Answer<Void>) invocation -> {
|
||||
Event event = (Event)invocation.getArguments()[0];
|
||||
events.add(event);
|
||||
return null;
|
||||
}).when(eventDistributor).publish(any(Event.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();
|
||||
|
||||
@ -257,7 +255,7 @@ public class HypervisorTemplateAdapterTest {
|
||||
}
|
||||
|
||||
//@Test
|
||||
public void testEmitDeleteEventUuid() throws InterruptedException, ExecutionException, EventBusException {
|
||||
public void testEmitDeleteEventUuid() throws InterruptedException, ExecutionException {
|
||||
//All the mocks required for this test to work.
|
||||
ImageStoreEntity store = mock(ImageStoreEntity.class);
|
||||
when(store.getId()).thenReturn(1l);
|
||||
|
||||
@ -16,6 +16,38 @@
|
||||
// under the License.
|
||||
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.api.auth.SetupUserTwoFactorAuthenticationCmd;
|
||||
import com.cloud.domain.Domain;
|
||||
@ -28,40 +60,12 @@ import com.cloud.projects.Project;
|
||||
import com.cloud.projects.ProjectAccountVO;
|
||||
import com.cloud.user.Account.State;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.component.ComponentContext;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.vm.UserVmManagerImpl;
|
||||
import com.cloud.vm.UserVmVO;
|
||||
import com.cloud.vm.VMInstanceVO;
|
||||
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)
|
||||
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.remove(Mockito.anyLong())).thenReturn(true);
|
||||
Mockito.when(userDataDao.removeByAccountId(Mockito.anyLong())).thenReturn(222);
|
||||
Mockito.doNothing().when(accountManagerImpl).deleteWebhooksForAccount(Mockito.anyLong());
|
||||
|
||||
Assert.assertTrue(accountManagerImpl.deleteUserAccount(42l));
|
||||
// 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.lenient().when(_domainMgr.getDomain(Mockito.anyLong())).thenReturn(domain);
|
||||
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 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.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',
|
||||
'listBuckets': 'Object Store',
|
||||
'listVmsForImport': 'Virtual Machine',
|
||||
'importVm': 'Virtual Machine'
|
||||
'importVm': 'Virtual Machine',
|
||||
'Webhook': 'Webhook',
|
||||
'Webhooks': 'Webhook'
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -7227,3 +7227,65 @@ class Bucket:
|
||||
cmd.id = self.id
|
||||
[setattr(cmd, k, v) for k, v in list(kwargs.items())]
|
||||
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.cancel.maintenance.mode": "Cancel maintenance mode",
|
||||
"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.copy.iso": "Copy ISO",
|
||||
"label.action.copy.snapshot": "Copy Snapshot",
|
||||
@ -583,6 +585,7 @@
|
||||
"label.create.tungsten.routing.policy": "Create Tungsten-Fabric routing policy",
|
||||
"label.create.user": "Create User",
|
||||
"label.create.vpn.connection": "Create VPN connection",
|
||||
"label.create.webhook": "Create Webhook",
|
||||
"label.created": "Created",
|
||||
"label.creating": "Creating",
|
||||
"label.creating.iprange": "Creating IP ranges",
|
||||
@ -603,6 +606,9 @@
|
||||
"label.data.disk": "Data disk",
|
||||
"label.data.disk.offering": "Data disk offering",
|
||||
"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.of.month": "Day of month",
|
||||
"label.day.of.week": "Day of week",
|
||||
@ -672,6 +678,8 @@
|
||||
"label.delete.vpn.customer.gateway": "Delete VPN customer gateway",
|
||||
"label.delete.vpn.gateway": "Delete VPN gateway",
|
||||
"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.deleting": "Deleting",
|
||||
"label.deleting.failed": "Deleting failed",
|
||||
@ -730,6 +738,7 @@
|
||||
"label.disable.storage": "Disable storage pool",
|
||||
"label.disable.vpc.offering": "Disable VPC offering",
|
||||
"label.disable.vpn": "Disable remote access VPN",
|
||||
"label.disable.webhook": "Disable Webhook",
|
||||
"label.disabled": "Disabled",
|
||||
"label.disconnected": "Last disconnected",
|
||||
"label.disk": "Disk",
|
||||
@ -837,6 +846,7 @@
|
||||
"label.enable.storage": "Enable storage pool",
|
||||
"label.enable.vpc.offering": "Enable VPC offering",
|
||||
"label.enable.vpn": "Enable remote access VPN",
|
||||
"label.enable.webhook": "Enable Webhook",
|
||||
"label.enabled": "Enabled",
|
||||
"label.encrypt": "Encrypt",
|
||||
"label.encryptroot": "Encrypt Root Disk",
|
||||
@ -872,6 +882,8 @@
|
||||
"label.esppolicy": "ESP policy",
|
||||
"label.esx.host": "ESX/ESXi host",
|
||||
"label.event": "Event",
|
||||
"label.eventid": "Event",
|
||||
"label.eventtype": "Event type",
|
||||
"label.event.archived": "Event(s) archived",
|
||||
"label.event.deleted": "Event(s) deleted",
|
||||
"label.event.timeline": "Event timeline",
|
||||
@ -987,6 +999,7 @@
|
||||
"label.hardware": "Hardware",
|
||||
"label.hasrules":"FW rules defined",
|
||||
"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.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.",
|
||||
@ -1281,6 +1294,8 @@
|
||||
"label.managed.volumes": "Managed Volumes",
|
||||
"label.managedstate": "Managed state",
|
||||
"label.management": "Management",
|
||||
"label.managementserverid": "Management server",
|
||||
"label.managementservername": "Management server",
|
||||
"label.management.ips": "Management IP addresses",
|
||||
"label.management.server": "Management server",
|
||||
"label.management.servers": "Management servers",
|
||||
@ -1562,6 +1577,7 @@
|
||||
"label.patp": "Palo Alto threat profile",
|
||||
"label.pavr": "Virtual router",
|
||||
"label.payload": "Payload",
|
||||
"label.payloadurl": "Payload URL",
|
||||
"label.pcidevice": "GPU",
|
||||
"label.pending.jobs": "Pending Jobs",
|
||||
"label.per.account": "Per Account",
|
||||
@ -1709,9 +1725,11 @@
|
||||
"label.readonly": "Read-Only",
|
||||
"label.reason": "Reason",
|
||||
"label.reboot": "Reboot",
|
||||
"label.recent.deliveries": "Recent deliveries",
|
||||
"label.receivedbytes": "Bytes received",
|
||||
"label.recover.vm": "Recover Instance",
|
||||
"label.recovering": "Recovering",
|
||||
"label.redeliver": "Redeliver",
|
||||
"label.redirect": "Redirect to:",
|
||||
"label.redirecturi": "Redirect URI",
|
||||
"label.redundantrouter": "Redundant router",
|
||||
@ -1790,6 +1808,7 @@
|
||||
"label.resources": "Resources",
|
||||
"label.resourcestate": "Resource state",
|
||||
"label.resourcetype": "Resource type",
|
||||
"label.response": "Response",
|
||||
"label.restart.network": "Restart Network",
|
||||
"label.restart.vpc": "Restart VPC",
|
||||
"label.restartrequired": "Restart required",
|
||||
@ -1998,6 +2017,7 @@
|
||||
"label.sshkeypair": "New SSH key pair",
|
||||
"label.sshkeypairs": "SSH key pairs",
|
||||
"label.sslcertificates": "SSL certificates",
|
||||
"label.sslverification": "SSL verification",
|
||||
"label.standard.us.keyboard": "Standard (US) keyboard",
|
||||
"label.start": "Start",
|
||||
"label.start.date.and.time": "Start date and time",
|
||||
@ -2118,6 +2138,8 @@
|
||||
"label.templatetype": "Template type",
|
||||
"label.templateversion": "Template version",
|
||||
"label.term.type": "Term type",
|
||||
"label.test": "Test",
|
||||
"label.test.webhook.delivery": "Test Webhook Delivery",
|
||||
"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.color": "Theme color",
|
||||
@ -2221,6 +2243,7 @@
|
||||
"label.update.to": "updated to",
|
||||
"label.update.traffic.label": "Update traffic labels",
|
||||
"label.update.vmware.datacenter": "Update VMWare datacenter",
|
||||
"label.update.webhook": "Update Webhook",
|
||||
"label.updating": "Updating",
|
||||
"label.upgrade.router.newer.template": "Upgrade router to use newer Template",
|
||||
"label.upgrading": "Upgrading",
|
||||
@ -2399,6 +2422,10 @@
|
||||
"label.warn": "Warn",
|
||||
"label.warn.upper": "WARN",
|
||||
"label.warning": "Warning",
|
||||
"label.webhook": "Webhook",
|
||||
"label.webhooks": "Webhooks",
|
||||
"label.webhookname": "Webhook",
|
||||
"label.webhook.deliveries": "Webhook deliveries",
|
||||
"label.wednesday": "Wednesday",
|
||||
"label.weekly": "Weekly",
|
||||
"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.processing": "Adding 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.processing": "Removing 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.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.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.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.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.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.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?",
|
||||
@ -2745,6 +2775,8 @@
|
||||
"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.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.node": "Deleting Node",
|
||||
"message.deleting.vm": "Deleting Instance",
|
||||
@ -2782,6 +2814,7 @@
|
||||
"message.disable.vpn": "Are you sure you want to disable VPN?",
|
||||
"message.disable.vpn.failed": "Failed to disable 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.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.",
|
||||
@ -2956,6 +2989,9 @@
|
||||
"message.error.zone.type": "Please select zone type.",
|
||||
"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.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.failed.to.add": "Failed to add",
|
||||
"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.offering": "Successfully changed offering",
|
||||
"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.health.monitor": "Successfully Configure Health Monitor",
|
||||
"message.success.config.sticky.policy": "Successfully configured sticky policy",
|
||||
@ -3231,6 +3268,7 @@
|
||||
"message.success.create.template": "Successfully created Template",
|
||||
"message.success.create.user": "Successfully created User",
|
||||
"message.success.create.volume": "Successfully created volume",
|
||||
"message.success.create.webhook": "Successfully created Webhook",
|
||||
"message.success.delete": "Successfully deleted",
|
||||
"message.success.delete.acl.rule": "Successfully removed ACL rule",
|
||||
"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.condition.failed": "Failed to update 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.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.",
|
||||
@ -3404,6 +3443,7 @@
|
||||
"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.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.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.",
|
||||
|
||||
@ -22,12 +22,12 @@
|
||||
:model="form"
|
||||
@finish="handleSubmit"
|
||||
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-switch v-model:checked="allDataIsChecked" @change="onToggleAllData"/>
|
||||
</a-form-item>
|
||||
<div v-show="showAllDataAlert">
|
||||
<a-alert :message="$t('message.alert.show.all.stats.data')" banner />
|
||||
<a-alert :message="allDataMessage" banner />
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="showStartDate">
|
||||
@ -64,7 +64,7 @@ import { ref, reactive, toRaw } from 'vue'
|
||||
import moment from 'moment'
|
||||
|
||||
export default {
|
||||
name: 'FilterStats',
|
||||
name: 'DateTimeFilter',
|
||||
emits: ['closeAction', 'onSubmit'],
|
||||
props: {
|
||||
startDateProp: {
|
||||
@ -74,6 +74,14 @@ export default {
|
||||
endDateProp: {
|
||||
type: [Date, String, Number],
|
||||
required: false
|
||||
},
|
||||
showAllDataOption: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
allDataMessage: {
|
||||
type: String,
|
||||
value: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -103,6 +103,12 @@
|
||||
<div v-else-if="$route.meta.name === 'computeoffering' && offeringDetails.includes(item)">
|
||||
{{ dataResource.serviceofferingdetails[item] }}
|
||||
</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>
|
||||
</a-list-item>
|
||||
@ -120,6 +126,13 @@
|
||||
<div>{{ dataResource[item] }}</div>
|
||||
</div>
|
||||
</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>
|
||||
<HostInfo :resource="dataResource" v-if="$route.meta.name === 'host' && 'listHosts' in $store.getters.apis" />
|
||||
<DedicateData :resource="dataResource" v-if="dedicatedSectionActive" />
|
||||
@ -174,7 +187,12 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
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 () {
|
||||
if (this.resource.templatetype === 'VNF' && ['vm', 'vnfapp'].includes(this.$route.meta.name)) {
|
||||
|
||||
@ -140,6 +140,12 @@
|
||||
<status class="status" :text="resource.resourcestate" displayText/>
|
||||
</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__label">{{ $t('label.id') }}</div>
|
||||
@ -672,6 +678,22 @@
|
||||
<span v-else>{{ resource.domain || resource.domainid }}</span>
|
||||
</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__label">{{ $t('label.management.servers') }}</div>
|
||||
<div class="resource-detail-item__details">
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
:dataSource="items"
|
||||
:rowKey="(record, idx) => record.id || record.name || record.usageType || idx + '-' + Math.random()"
|
||||
: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"
|
||||
style="overflow-y: auto"
|
||||
>
|
||||
@ -364,12 +364,47 @@
|
||||
<status :text="record.enabled ? record.enabled.toString() : 'false'" />
|
||||
{{ record.enabled ? 'Enabled' : 'Disabled' }}
|
||||
</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) }}
|
||||
</template>
|
||||
<template v-if="['startdate', 'enddate'].includes(column.key) && ['vm', 'vnfapp'].includes($route.path.split('/')[1])">
|
||||
{{ getDateAtTimeZone(text, record.timezone) }}
|
||||
</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'">
|
||||
<div class="shift-btns">
|
||||
<a-tooltip :name="text" placement="top">
|
||||
@ -526,6 +561,10 @@ export default {
|
||||
actions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
explicitlyAllowRowSelection: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
inject: ['parentFetchData', 'parentToggleLoading'],
|
||||
@ -598,14 +637,15 @@ export default {
|
||||
'/project', '/account', 'buckets', 'objectstore',
|
||||
'/zone', '/pod', '/cluster', '/host', '/storagepool', '/imagestore', '/systemvm', '/router', '/ilbvm', '/annotation',
|
||||
'/computeoffering', '/systemoffering', '/diskoffering', '/backupoffering', '/networkoffering', '/vpcoffering',
|
||||
'/tungstenfabric', '/oauthsetting', '/guestos', '/guestoshypervisormapping'].join('|'))
|
||||
'/tungstenfabric', '/oauthsetting', '/guestos', '/guestoshypervisormapping', '/webhook', 'webhookdeliveries'].join('|'))
|
||||
.test(this.$route.path)
|
||||
},
|
||||
enableGroupAction () {
|
||||
return ['vm', 'alert', 'vmgroup', 'ssh', 'userdata', 'affinitygroup', 'autoscalevmgroup', 'volume', 'snapshot',
|
||||
'vmsnapshot', 'backup', 'guestnetwork', 'vpc', 'publicip', 'vpnuser', 'vpncustomergateway', 'vnfapp',
|
||||
'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)
|
||||
},
|
||||
getDateAtTimeZone (date, timezone) {
|
||||
@ -898,6 +938,19 @@ export default {
|
||||
case 'SecondaryStorageVm': return '/systemvm/'
|
||||
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>
|
||||
<block-outlined v-else style="margin-right: 5px" />
|
||||
</span>
|
||||
<span v-if="(field.name.startsWith('managementserver'))">
|
||||
<status :text="opt.state" />
|
||||
</span>
|
||||
{{ $t(opt.path || opt.name) }}
|
||||
</div>
|
||||
</a-select-option>
|
||||
@ -154,14 +157,17 @@
|
||||
<script>
|
||||
import { ref, reactive, toRaw } from 'vue'
|
||||
import { api } from '@/api'
|
||||
import { isAdmin } from '@/role'
|
||||
import TooltipButton from '@/components/widgets/TooltipButton'
|
||||
import ResourceIcon from '@/components/view/ResourceIcon'
|
||||
import Status from '@/components/widgets/Status'
|
||||
|
||||
export default {
|
||||
name: 'SearchView',
|
||||
components: {
|
||||
TooltipButton,
|
||||
ResourceIcon
|
||||
ResourceIcon,
|
||||
Status
|
||||
},
|
||||
props: {
|
||||
searchFilters: {
|
||||
@ -206,13 +212,7 @@ export default {
|
||||
if (to && to.query && 'q' in to.query) {
|
||||
this.searchQuery = to.query.q
|
||||
}
|
||||
this.isFiltered = false
|
||||
this.searchFilters.some(item => {
|
||||
if (this.searchParams[item]) {
|
||||
this.isFiltered = true
|
||||
return true
|
||||
}
|
||||
})
|
||||
this.updateIsFiltered()
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
@ -220,6 +220,7 @@ export default {
|
||||
if (this.$route && this.$route.query && 'q' in this.$route.query) {
|
||||
this.searchQuery = this.$route.query.q
|
||||
}
|
||||
this.updateIsFiltered()
|
||||
},
|
||||
computed: {
|
||||
styleSearch () {
|
||||
@ -285,7 +286,7 @@ export default {
|
||||
}
|
||||
if (['zoneid', 'domainid', 'imagestoreid', 'storageid', 'state', 'account', 'hypervisor', 'level',
|
||||
'clusterid', 'podid', 'groupid', 'entitytype', 'accounttype', 'systemvmtype', 'scope', 'provider',
|
||||
'type'].includes(item)
|
||||
'type', 'scope', 'managementserverid'].includes(item)
|
||||
) {
|
||||
type = 'list'
|
||||
} 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')) {
|
||||
const stateIndex = this.fields.findIndex(item => item.name === 'state')
|
||||
this.fields[stateIndex].loading = true
|
||||
@ -397,6 +405,7 @@ export default {
|
||||
let podIndex = -1
|
||||
let clusterIndex = -1
|
||||
let groupIndex = -1
|
||||
let managementServerIdIndex = -1
|
||||
|
||||
if (arrayField.includes('type')) {
|
||||
if (this.$route.path === '/alert') {
|
||||
@ -464,6 +473,12 @@ export default {
|
||||
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 => {
|
||||
if (typeIndex > -1) {
|
||||
const types = response.filter(item => item.type === 'type')
|
||||
@ -525,6 +540,12 @@ export default {
|
||||
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(() => {
|
||||
if (typeIndex > -1) {
|
||||
this.fields[typeIndex].loading = false
|
||||
@ -550,6 +571,9 @@ export default {
|
||||
if (groupIndex > -1) {
|
||||
this.fields[groupIndex].loading = false
|
||||
}
|
||||
if (managementServerIdIndex > -1) {
|
||||
this.fields[managementServerIdIndex].loading = false
|
||||
}
|
||||
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 () {
|
||||
const types = []
|
||||
if (this.apiName.indexOf('listNetworks') > -1) {
|
||||
@ -877,9 +914,30 @@ export default {
|
||||
}
|
||||
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 () {
|
||||
var state = []
|
||||
if (this.apiName.includes('listVolumes')) {
|
||||
return [
|
||||
state = [
|
||||
{
|
||||
id: 'Allocated',
|
||||
name: 'label.allocated'
|
||||
@ -906,7 +964,7 @@ export default {
|
||||
}
|
||||
]
|
||||
} else if (this.apiName.includes('listKubernetesClusters')) {
|
||||
return [
|
||||
state = [
|
||||
{
|
||||
id: 'Created',
|
||||
name: 'label.created'
|
||||
@ -956,8 +1014,19 @@ export default {
|
||||
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 () {
|
||||
const entityType = []
|
||||
@ -1052,6 +1121,13 @@ export default {
|
||||
},
|
||||
changeFilter (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')"
|
||||
:maskClosable="false"
|
||||
:footer="null">
|
||||
<filter-stats
|
||||
<date-time-filter
|
||||
:startDateProp="startDate"
|
||||
:endDateProp="endDate"
|
||||
:allDataMessage="$t('message.alert.show.all.stats.data')"
|
||||
@closeAction="closeAction"
|
||||
@onSubmit="handleSubmit"/>
|
||||
</a-modal>
|
||||
@ -251,7 +252,7 @@
|
||||
import { api } from '@/api'
|
||||
import moment from 'moment'
|
||||
import 'chartjs-adapter-moment'
|
||||
import FilterStats from './stats/FilterStats'
|
||||
import DateTimeFilter from './DateTimeFilter'
|
||||
import ResourceStatsInfo from './stats/ResourceStatsInfo'
|
||||
import ResourceStatsLineChart from './stats/ResourceStatsLineChart'
|
||||
|
||||
@ -267,7 +268,7 @@ export default {
|
||||
}
|
||||
},
|
||||
components: {
|
||||
FilterStats,
|
||||
DateTimeFilter,
|
||||
ResourceStatsInfo,
|
||||
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
|
||||
// under the License.
|
||||
import store from '@/store'
|
||||
import { shallowRef, defineAsyncComponent } from 'vue'
|
||||
|
||||
export default {
|
||||
name: 'tools',
|
||||
@ -77,6 +78,154 @@ export default {
|
||||
resourceType: 'UserVm',
|
||||
permission: ['listInfrastructure', 'listVolumesForImport'],
|
||||
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,
|
||||
MobileOutlined,
|
||||
MoreOutlined,
|
||||
NodeIndexOutlined,
|
||||
NotificationOutlined,
|
||||
NumberOutlined,
|
||||
LaptopOutlined,
|
||||
@ -139,7 +140,9 @@ import {
|
||||
ReconciliationOutlined,
|
||||
RedoOutlined,
|
||||
ReloadOutlined,
|
||||
RetweetOutlined,
|
||||
RightCircleOutlined,
|
||||
RightSquareOutlined,
|
||||
RocketOutlined,
|
||||
SafetyCertificateOutlined,
|
||||
SafetyOutlined,
|
||||
@ -281,6 +284,7 @@ export default {
|
||||
app.component('MinusSquareOutlined', MinusSquareOutlined)
|
||||
app.component('MobileOutlined', MobileOutlined)
|
||||
app.component('MoreOutlined', MoreOutlined)
|
||||
app.component('NodeIndexOutlined', NodeIndexOutlined)
|
||||
app.component('NotificationOutlined', NotificationOutlined)
|
||||
app.component('NumberOutlined', NumberOutlined)
|
||||
app.component('LaptopOutlined', LaptopOutlined)
|
||||
@ -300,7 +304,9 @@ export default {
|
||||
app.component('ReconciliationOutlined', ReconciliationOutlined)
|
||||
app.component('RedoOutlined', RedoOutlined)
|
||||
app.component('ReloadOutlined', ReloadOutlined)
|
||||
app.component('RetweetOutlined', RetweetOutlined)
|
||||
app.component('RightCircleOutlined', RightCircleOutlined)
|
||||
app.component('RightSquareOutlined', RightSquareOutlined)
|
||||
app.component('RocketOutlined', RocketOutlined)
|
||||
app.component('SafetyCertificateOutlined', SafetyCertificateOutlined)
|
||||
app.component('SafetyOutlined', SafetyOutlined)
|
||||
|
||||
@ -798,7 +798,7 @@ export default {
|
||||
this.projectView = Boolean(store.getters.project && store.getters.project.id)
|
||||
this.hasProjectId = ['vm', 'vmgroup', 'ssh', 'affinitygroup', 'userdata', 'volume', 'snapshot', 'vmsnapshot', 'guestnetwork',
|
||||
'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) {
|
||||
this.dataView = true
|
||||
@ -1553,13 +1553,16 @@ export default {
|
||||
continue
|
||||
}
|
||||
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') {
|
||||
params[key] = false
|
||||
}
|
||||
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
|
||||
}
|
||||
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