diff --git a/conf/db/upgrade/V5.5.12__schema.sql b/conf/db/upgrade/V5.5.12__schema.sql index ea21c75f925..58d0e6ee187 100644 --- a/conf/db/upgrade/V5.5.12__schema.sql +++ b/conf/db/upgrade/V5.5.12__schema.sql @@ -62,4 +62,30 @@ WHERE `uuid` IN ( ) AND `metricName` = 'VRouterCPUAverageUsedUtilization'; +-- ZSTAC-80472: Resource notification webhook tables +CREATE TABLE IF NOT EXISTS `zstack`.`ResNotifySubscriptionVO` ( + `uuid` VARCHAR(32) NOT NULL UNIQUE, + `name` VARCHAR(255) DEFAULT NULL, + `description` VARCHAR(2048) DEFAULT NULL, + `resourceTypes` TEXT DEFAULT NULL, + `eventTypes` VARCHAR(256) DEFAULT NULL, + `type` VARCHAR(32) NOT NULL DEFAULT 'WEBHOOK', + `state` VARCHAR(32) NOT NULL DEFAULT 'Enabled', + `accountUuid` VARCHAR(32) DEFAULT NULL, + `lastOpDate` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `createDate` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00', + PRIMARY KEY (`uuid`), + INDEX `idx_ResNotifySubscriptionVO_accountUuid` (`accountUuid`), + INDEX `idx_ResNotifySubscriptionVO_type_state` (`type`, `state`), + CONSTRAINT `fkResNotifySubscriptionVOResourceVO` FOREIGN KEY (`uuid`) REFERENCES `ResourceVO` (`uuid`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE IF NOT EXISTS `zstack`.`ResNotifyWebhookRefVO` ( + `uuid` VARCHAR(32) NOT NULL UNIQUE, + `webhookUrl` TEXT NOT NULL, + `secret` VARCHAR(256) DEFAULT NULL, + `customHeaders` TEXT, + PRIMARY KEY (`uuid`), + CONSTRAINT `fk_ResNotifyWebhookRefVO_ResNotifySubscriptionVO` + FOREIGN KEY (`uuid`) REFERENCES `ResNotifySubscriptionVO`(`uuid`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/core/src/main/java/org/zstack/core/db/DatabaseFacade.java b/core/src/main/java/org/zstack/core/db/DatabaseFacade.java index 155aca5162b..efb4e22b307 100755 --- a/core/src/main/java/org/zstack/core/db/DatabaseFacade.java +++ b/core/src/main/java/org/zstack/core/db/DatabaseFacade.java @@ -84,4 +84,6 @@ public interface DatabaseFacade { String getDbVersion(); void installEntityLifeCycleCallback(Class entityClass, EntityEvent evt, EntityLifeCycleCallback cb); + + void uninstallEntityLifeCycleCallback(Class entityClass, EntityEvent evt, EntityLifeCycleCallback cb); } diff --git a/core/src/main/java/org/zstack/core/db/DatabaseFacadeImpl.java b/core/src/main/java/org/zstack/core/db/DatabaseFacadeImpl.java index d0a92426313..5a7b0c34c31 100755 --- a/core/src/main/java/org/zstack/core/db/DatabaseFacadeImpl.java +++ b/core/src/main/java/org/zstack/core/db/DatabaseFacadeImpl.java @@ -1,5 +1,7 @@ package org.zstack.core.db; +import com.google.common.collect.Maps; +import java.util.concurrent.CopyOnWriteArrayList; import org.hibernate.exception.ConstraintViolationException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.NestedExceptionUtils; @@ -66,7 +68,7 @@ class EntityInfo { Field eoSoftDeleteColumn; Class eoClass; Class voClass; - Map listeners = new HashMap(); + Map> listeners = Maps.newConcurrentMap(); EntityInfo(Class voClazz) { voClass = voClazz; @@ -82,12 +84,16 @@ class EntityInfo { EO at = (EO) voClazz.getAnnotation(EO.class); if (at != null) { eoClass = at.EOClazz(); - DebugUtils.Assert(eoClass != null, String.format("cannot find EO entity specified by VO entity[%s]", voClazz.getName())); + DebugUtils.Assert(eoClass != null, + String.format("cannot find EO entity specified by VO entity[%s]", voClazz.getName())); eoPrimaryKeyField = FieldUtils.getAnnotatedField(Id.class, eoClass); - DebugUtils.Assert(eoPrimaryKeyField != null, String.format("cannot find primary key field(@Id annotated) in EO entity[%s]", eoClass.getName())); + DebugUtils.Assert(eoPrimaryKeyField != null, String + .format("cannot find primary key field(@Id annotated) in EO entity[%s]", eoClass.getName())); eoPrimaryKeyField.setAccessible(true); eoSoftDeleteColumn = FieldUtils.getField(at.softDeletedColumn(), eoClass); - DebugUtils.Assert(eoSoftDeleteColumn != null, String.format("cannot find soft delete column[%s] in EO entity[%s]", at.softDeletedColumn(), eoClass.getName())); + DebugUtils.Assert(eoSoftDeleteColumn != null, + String.format("cannot find soft delete column[%s] in EO entity[%s]", at.softDeletedColumn(), + eoClass.getName())); eoSoftDeleteColumn.setAccessible(true); } @@ -108,7 +114,8 @@ private void buildSoftDeletionCascade() { for (final SoftDeletionCascade at : ats.value()) { final Class parent = at.parent(); if (!parent.isAnnotationPresent(Entity.class)) { - throw new CloudRuntimeException(String.format("class[%s] has annotation @SoftDeletionCascade but its parent class[%s] is not annotated by @Entity", + throw new CloudRuntimeException(String.format( + "class[%s] has annotation @SoftDeletionCascade but its parent class[%s] is not annotated by @Entity", voClass, parent)); } @@ -131,7 +138,8 @@ public List getEntityClassForSoftDeleteEntityExtension() { @Override @Transactional public void postSoftDelete(Collection entityIds, Class entityClass) { - String sql = String.format("delete from %s me where me.%s in (:ids)", voClass.getSimpleName(), at.joinColumn()); + String sql = String.format("delete from %s me where me.%s in (:ids)", voClass.getSimpleName(), + at.joinColumn()); Query q = getEntityManager().createQuery(sql); q.setParameter("ids", entityIds); q.executeUpdate(); @@ -148,7 +156,8 @@ private void buildInheritanceDeletionExtension() { final Class parent = voClass.getSuperclass(); if (!parent.isAnnotationPresent(Entity.class)) { - throw new CloudRuntimeException(String.format("class[%s] has annotation @PrimaryKeyJoinColumn but its parent class[%s] is not annotated by @Entity", + throw new CloudRuntimeException(String.format( + "class[%s] has annotation @PrimaryKeyJoinColumn but its parent class[%s] is not annotated by @Entity", voClass, parent)); } @@ -245,7 +254,8 @@ private void updateEO(Object entity, RuntimeException de) { } SQLIntegrityConstraintViolationException me = (SQLIntegrityConstraintViolationException) rootCause; - if (!(me.getErrorCode() == 1062 && "23000".equals(me.getSQLState()) && me.getMessage().contains("PRIMARY"))) { + if (!(me.getErrorCode() == 1062 && "23000".equals(me.getSQLState()) + && me.getMessage().contains("PRIMARY"))) { throw de; } @@ -253,9 +263,12 @@ private void updateEO(Object entity, RuntimeException de) { throw de; } - // at this point, the error is caused by a update tried on VO entity which has been soft deleted. This is mostly - // caused by a deletion cascade(e.g deleting host will cause vm running on it to be deleted, and deleting vm is trying to return capacity - // to host which has been soft deleted, because vm deletion is executed in async manner). In this case, we make the update to EO table + // at this point, the error is caused by a update tried on VO entity which has + // been soft deleted. This is mostly + // caused by a deletion cascade(e.g deleting host will cause vm running on it to + // be deleted, and deleting vm is trying to return capacity + // to host which has been soft deleted, because vm deletion is executed in async + // manner). In this case, we make the update to EO table Object idval = getEOPrimaryKeyValue(entity); Object eo = getEntityManager().find(eoClass, idval); @@ -360,8 +373,10 @@ private void hardDelete(Collection ids) { @Transactional private void nativeSqlDelete(Collection ids) { - // native sql can avoid JPA cascades a deletion to parent entity when deleting a child entity - String sql = String.format("delete from %s where %s in (:ids)", voClass.getSimpleName(), voPrimaryKeyField.getName()); + // native sql can avoid JPA cascades a deletion to parent entity when deleting a + // child entity + String sql = String.format("delete from %s where %s in (:ids)", voClass.getSimpleName(), + voPrimaryKeyField.getName()); Query q = getEntityManager().createNativeQuery(sql); q.setParameter("ids", ids); q.executeUpdate(); @@ -418,7 +433,8 @@ List listByPrimaryKeys(Collection ids, int offset, int length) { sql = String.format("select e from %s e", voClass.getSimpleName()); query = getEntityManager().createQuery(sql, voClass); } else { - sql = String.format("select e from %s e where e.%s in (:ids)", voClass.getSimpleName(), voPrimaryKeyField.getName()); + sql = String.format("select e from %s e where e.%s in (:ids)", voClass.getSimpleName(), + voPrimaryKeyField.getName()); query = getEntityManager().createQuery(sql, voClass); query.setParameter("ids", ids); } @@ -429,7 +445,8 @@ List listByPrimaryKeys(Collection ids, int offset, int length) { @Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW) boolean isExist(Object id) { - String sql = String.format("select count(*) from %s ref where ref.%s = :id", voClass.getSimpleName(), voPrimaryKeyField.getName()); + String sql = String.format("select count(*) from %s ref where ref.%s = :id", voClass.getSimpleName(), + voPrimaryKeyField.getName()); TypedQuery q = getEntityManager().createQuery(sql, Long.class); q.setParameter("id", id); q.setMaxResults(1); @@ -438,13 +455,25 @@ boolean isExist(Object id) { } void installLifeCycleCallback(EntityEvent evt, EntityLifeCycleCallback l) { - listeners.put(evt, l); + List cbs = listeners.computeIfAbsent(evt, k -> new CopyOnWriteArrayList<>()); + if (!cbs.contains(l)) { + cbs.add(l); + } + } + + void uninstallLifeCycleCallback(EntityEvent evt, EntityLifeCycleCallback l) { + List cbs = listeners.get(evt); + if (cbs != null) { + cbs.remove(l); + } } void fireLifeCycleEvent(EntityEvent evt, Object o) { - EntityLifeCycleCallback cb = listeners.get(evt); - if (cb != null) { - cb.entityLifeCycleEvent(evt, o); + List cbs = listeners.get(evt); + if (cbs != null) { + for (EntityLifeCycleCallback cb : cbs) { + cb.entityLifeCycleEvent(evt, o); + } } } } @@ -491,7 +520,8 @@ public CriteriaBuilder getCriteriaBuilder() { @Override public SimpleQuery createQuery(Class entityClass) { - assert entityClass.isAnnotationPresent(Entity.class) : entityClass.getName() + " is not annotated by JPA @Entity"; + assert entityClass.isAnnotationPresent(Entity.class) + : entityClass.getName() + " is not annotated by JPA @Entity"; return new SimpleQueryImpl(entityClass); } @@ -530,7 +560,6 @@ public void removeByPrimaryKeys(Collection priKeys, Class entityClazz) { getEntityInfo(entityClazz).removeByPrimaryKeys(priKeys); } - @Override public T updateAndRefresh(T entity) { return (T) getEntityInfo(entity.getClass()).updateAndRefresh(entity); @@ -636,7 +665,9 @@ public void entityForTranscationCallback(Operation op, Class... entityClass) sb.append(c.getName()).append(","); } - String err = String.format("entityForTranscationCallback is called but transcation is not active. Did you forget adding @Transactional to method??? [operation: %s, entity classes: %s]", op, sb.toString()); + String err = String.format( + "entityForTranscationCallback is called but transcation is not active. Did you forget adding @Transactional to method??? [operation: %s, entity classes: %s]", + op, sb.toString()); logger.warn(err); } } @@ -668,7 +699,8 @@ public long generateSequenceNumber(Class seqTable) { try { Field id = seqTable.getDeclaredField("id"); if (id == null) { - throw new CloudRuntimeException(String.format("sequence VO[%s] must have 'id' field", seqTable.getName())); + throw new CloudRuntimeException( + String.format("sequence VO[%s] must have 'id' field", seqTable.getName())); } Object vo = seqTable.newInstance(); vo = persistAndRefresh(vo); @@ -770,7 +802,7 @@ public void eoCleanup(Class VOClazz) { @Override @DeadlockAutoRestart public void eoCleanup(Class VOClazz, Object id) { - if(id == null) { + if (id == null) { throw new RuntimeException(String.format("Cleanup %s EO fail, id is null", VOClazz.getSimpleName())); } @@ -798,7 +830,7 @@ public boolean start() { } private void buildEntityInfo() { - BeanUtils.reflections.getTypesAnnotatedWith(Entity.class).forEach(clz-> { + BeanUtils.reflections.getTypesAnnotatedWith(Entity.class).forEach(clz -> { entityInfoMap.put(clz, new EntityInfo(clz)); }); } @@ -820,7 +852,8 @@ private void populateExtensions() { } } - for (SoftDeleteEntityByEOExtensionPoint ext : pluginRgty.getExtensionList(SoftDeleteEntityByEOExtensionPoint.class)) { + for (SoftDeleteEntityByEOExtensionPoint ext : pluginRgty + .getExtensionList(SoftDeleteEntityByEOExtensionPoint.class)) { for (Class eoClass : ext.getEOClassForSoftDeleteEntityExtension()) { List exts = softDeleteByEOExtensions.get(eoClass); if (exts == null) { @@ -873,6 +906,20 @@ public void installEntityLifeCycleCallback(Class clz, EntityEvent evt, EntityLif } } + @Override + public void uninstallEntityLifeCycleCallback(Class clz, EntityEvent evt, EntityLifeCycleCallback cb) { + if (clz != null) { + EntityInfo info = entityInfoMap.get(clz); + if (info != null) { + info.uninstallLifeCycleCallback(evt, cb); + } + } else { + for (EntityInfo info : entityInfoMap.values()) { + info.uninstallLifeCycleCallback(evt, cb); + } + } + } + @Override public boolean stop() { return true; @@ -881,7 +928,8 @@ public boolean stop() { void entityEvent(EntityEvent evt, Object entity) { EntityInfo info = entityInfoMap.get(entity.getClass()); if (info == null) { - logger.warn(String.format("cannot find EntityInfo for the class[%s], not entity events will be fired", entity.getClass())); + logger.warn(String.format("cannot find EntityInfo for the class[%s], not entity events will be fired", + entity.getClass())); return; } diff --git a/sdk/src/main/java/SourceClassMap.java b/sdk/src/main/java/SourceClassMap.java index bb413e5ac31..33aff694e9d 100644 --- a/sdk/src/main/java/SourceClassMap.java +++ b/sdk/src/main/java/SourceClassMap.java @@ -872,6 +872,10 @@ public class SourceClassMap { put("org.zstack.zwatch.monitorgroup.entity.MonitorGroupTemplateRefInventory", "org.zstack.sdk.zwatch.monitorgroup.entity.MonitorGroupTemplateRefInventory"); put("org.zstack.zwatch.monitorgroup.entity.MonitorGroupTemplateRefVO", "org.zstack.sdk.zwatch.monitorgroup.entity.MonitorGroupTemplateRefVO"); put("org.zstack.zwatch.monitorgroup.entity.MonitorTemplateInventory", "org.zstack.sdk.zwatch.monitorgroup.entity.MonitorTemplateInventory"); + put("org.zstack.zwatch.resnotify.ResNotifySubscriptionInventory", "org.zstack.sdk.zwatch.resnotify.ResNotifySubscriptionInventory"); + put("org.zstack.zwatch.resnotify.ResNotifySubscriptionState", "org.zstack.sdk.zwatch.resnotify.ResNotifySubscriptionState"); + put("org.zstack.zwatch.resnotify.ResNotifyType", "org.zstack.sdk.zwatch.resnotify.ResNotifyType"); + put("org.zstack.zwatch.resnotify.ResNotifyWebhookRefInventory", "org.zstack.sdk.zwatch.resnotify.ResNotifyWebhookRefInventory"); put("org.zstack.zwatch.ruleengine.ComparisonOperator", "org.zstack.sdk.zwatch.ruleengine.ComparisonOperator"); put("org.zstack.zwatch.thirdparty.entity.SNSEndpointThirdpartyAlertHistoryInventory", "org.zstack.sdk.zwatch.thirdparty.entity.SNSEndpointThirdpartyAlertHistoryInventory"); put("org.zstack.zwatch.thirdparty.entity.ThirdpartyOriginalAlertInventory", "org.zstack.sdk.zwatch.thirdparty.entity.ThirdpartyOriginalAlertInventory"); @@ -1748,6 +1752,10 @@ public class SourceClassMap { put("org.zstack.sdk.zwatch.monitorgroup.entity.MonitorGroupTemplateRefInventory", "org.zstack.zwatch.monitorgroup.entity.MonitorGroupTemplateRefInventory"); put("org.zstack.sdk.zwatch.monitorgroup.entity.MonitorGroupTemplateRefVO", "org.zstack.zwatch.monitorgroup.entity.MonitorGroupTemplateRefVO"); put("org.zstack.sdk.zwatch.monitorgroup.entity.MonitorTemplateInventory", "org.zstack.zwatch.monitorgroup.entity.MonitorTemplateInventory"); + put("org.zstack.sdk.zwatch.resnotify.ResNotifySubscriptionInventory", "org.zstack.zwatch.resnotify.ResNotifySubscriptionInventory"); + put("org.zstack.sdk.zwatch.resnotify.ResNotifySubscriptionState", "org.zstack.zwatch.resnotify.ResNotifySubscriptionState"); + put("org.zstack.sdk.zwatch.resnotify.ResNotifyType", "org.zstack.zwatch.resnotify.ResNotifyType"); + put("org.zstack.sdk.zwatch.resnotify.ResNotifyWebhookRefInventory", "org.zstack.zwatch.resnotify.ResNotifyWebhookRefInventory"); put("org.zstack.sdk.zwatch.ruleengine.ComparisonOperator", "org.zstack.zwatch.ruleengine.ComparisonOperator"); put("org.zstack.sdk.zwatch.thirdparty.entity.SNSEndpointThirdpartyAlertHistoryInventory", "org.zstack.zwatch.thirdparty.entity.SNSEndpointThirdpartyAlertHistoryInventory"); put("org.zstack.sdk.zwatch.thirdparty.entity.ThirdpartyOriginalAlertInventory", "org.zstack.zwatch.thirdparty.entity.ThirdpartyOriginalAlertInventory"); diff --git a/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/DeleteResNotifySubscriptionAction.java b/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/DeleteResNotifySubscriptionAction.java new file mode 100644 index 00000000000..9266477e583 --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/DeleteResNotifySubscriptionAction.java @@ -0,0 +1,104 @@ +package org.zstack.sdk.zwatch.resnotify; + +import java.util.HashMap; +import java.util.Map; +import org.zstack.sdk.*; + +public class DeleteResNotifySubscriptionAction extends AbstractAction { + + private static final HashMap parameterMap = new HashMap<>(); + + private static final HashMap nonAPIParameterMap = new HashMap<>(); + + public static class Result { + public ErrorCode error; + public org.zstack.sdk.zwatch.resnotify.DeleteResNotifySubscriptionResult value; + + public Result throwExceptionIfError() { + if (error != null) { + throw new ApiException( + String.format("error[code: %s, description: %s, details: %s, globalErrorCode: %s]", error.code, error.description, error.details, error.globalErrorCode) + ); + } + + return this; + } + } + + @Param(required = true, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.lang.String uuid; + + @Param(required = false) + public java.lang.String deleteMode = "Permissive"; + + @Param(required = false) + public java.util.List systemTags; + + @Param(required = false) + public java.util.List userTags; + + @Param(required = false) + public String sessionId; + + @Param(required = false) + public String accessKeyId; + + @Param(required = false) + public String accessKeySecret; + + @Param(required = false) + public String requestIp; + + @NonAPIParam + public long timeout = -1; + + @NonAPIParam + public long pollingInterval = -1; + + + private Result makeResult(ApiResult res) { + Result ret = new Result(); + if (res.error != null) { + ret.error = res.error; + return ret; + } + + org.zstack.sdk.zwatch.resnotify.DeleteResNotifySubscriptionResult value = res.getResult(org.zstack.sdk.zwatch.resnotify.DeleteResNotifySubscriptionResult.class); + ret.value = value == null ? new org.zstack.sdk.zwatch.resnotify.DeleteResNotifySubscriptionResult() : value; + + return ret; + } + + public Result call() { + ApiResult res = ZSClient.call(this); + return makeResult(res); + } + + public void call(final Completion completion) { + ZSClient.call(this, new InternalCompletion() { + @Override + public void complete(ApiResult res) { + completion.complete(makeResult(res)); + } + }); + } + + protected Map getParameterMap() { + return parameterMap; + } + + protected Map getNonAPIParameterMap() { + return nonAPIParameterMap; + } + + protected RestInfo getRestInfo() { + RestInfo info = new RestInfo(); + info.httpMethod = "DELETE"; + info.path = "/zwatch/resnotify/subscriptions/{uuid}"; + info.needSession = true; + info.needPoll = true; + info.parameterName = ""; + return info; + } + +} diff --git a/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/DeleteResNotifySubscriptionResult.java b/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/DeleteResNotifySubscriptionResult.java new file mode 100644 index 00000000000..01e30f49416 --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/DeleteResNotifySubscriptionResult.java @@ -0,0 +1,7 @@ +package org.zstack.sdk.zwatch.resnotify; + + + +public class DeleteResNotifySubscriptionResult { + +} diff --git a/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/QueryResNotifySubscriptionAction.java b/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/QueryResNotifySubscriptionAction.java new file mode 100644 index 00000000000..5f4451557aa --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/QueryResNotifySubscriptionAction.java @@ -0,0 +1,75 @@ +package org.zstack.sdk.zwatch.resnotify; + +import java.util.HashMap; +import java.util.Map; +import org.zstack.sdk.*; + +public class QueryResNotifySubscriptionAction extends QueryAction { + + private static final HashMap parameterMap = new HashMap<>(); + + private static final HashMap nonAPIParameterMap = new HashMap<>(); + + public static class Result { + public ErrorCode error; + public org.zstack.sdk.zwatch.resnotify.QueryResNotifySubscriptionResult value; + + public Result throwExceptionIfError() { + if (error != null) { + throw new ApiException( + String.format("error[code: %s, description: %s, details: %s, globalErrorCode: %s]", error.code, error.description, error.details, error.globalErrorCode) + ); + } + + return this; + } + } + + + + private Result makeResult(ApiResult res) { + Result ret = new Result(); + if (res.error != null) { + ret.error = res.error; + return ret; + } + + org.zstack.sdk.zwatch.resnotify.QueryResNotifySubscriptionResult value = res.getResult(org.zstack.sdk.zwatch.resnotify.QueryResNotifySubscriptionResult.class); + ret.value = value == null ? new org.zstack.sdk.zwatch.resnotify.QueryResNotifySubscriptionResult() : value; + + return ret; + } + + public Result call() { + ApiResult res = ZSClient.call(this); + return makeResult(res); + } + + public void call(final Completion completion) { + ZSClient.call(this, new InternalCompletion() { + @Override + public void complete(ApiResult res) { + completion.complete(makeResult(res)); + } + }); + } + + protected Map getParameterMap() { + return parameterMap; + } + + protected Map getNonAPIParameterMap() { + return nonAPIParameterMap; + } + + protected RestInfo getRestInfo() { + RestInfo info = new RestInfo(); + info.httpMethod = "GET"; + info.path = "/zwatch/resnotify/subscriptions"; + info.needSession = true; + info.needPoll = false; + info.parameterName = ""; + return info; + } + +} diff --git a/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/QueryResNotifySubscriptionResult.java b/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/QueryResNotifySubscriptionResult.java new file mode 100644 index 00000000000..93cd0d2caeb --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/QueryResNotifySubscriptionResult.java @@ -0,0 +1,22 @@ +package org.zstack.sdk.zwatch.resnotify; + + + +public class QueryResNotifySubscriptionResult { + public java.util.List inventories; + public void setInventories(java.util.List inventories) { + this.inventories = inventories; + } + public java.util.List getInventories() { + return this.inventories; + } + + public java.lang.Long total; + public void setTotal(java.lang.Long total) { + this.total = total; + } + public java.lang.Long getTotal() { + return this.total; + } + +} diff --git a/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/ResNotifySubscriptionInventory.java b/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/ResNotifySubscriptionInventory.java new file mode 100644 index 00000000000..550d258854b --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/ResNotifySubscriptionInventory.java @@ -0,0 +1,97 @@ +package org.zstack.sdk.zwatch.resnotify; + +import org.zstack.sdk.zwatch.resnotify.ResNotifyType; +import org.zstack.sdk.zwatch.resnotify.ResNotifySubscriptionState; +import org.zstack.sdk.zwatch.resnotify.ResNotifyWebhookRefInventory; + +public class ResNotifySubscriptionInventory { + + public java.lang.String uuid; + public void setUuid(java.lang.String uuid) { + this.uuid = uuid; + } + public java.lang.String getUuid() { + return this.uuid; + } + + public java.lang.String name; + public void setName(java.lang.String name) { + this.name = name; + } + public java.lang.String getName() { + return this.name; + } + + public java.lang.String description; + public void setDescription(java.lang.String description) { + this.description = description; + } + public java.lang.String getDescription() { + return this.description; + } + + public java.lang.String resourceTypes; + public void setResourceTypes(java.lang.String resourceTypes) { + this.resourceTypes = resourceTypes; + } + public java.lang.String getResourceTypes() { + return this.resourceTypes; + } + + public java.lang.String eventTypes; + public void setEventTypes(java.lang.String eventTypes) { + this.eventTypes = eventTypes; + } + public java.lang.String getEventTypes() { + return this.eventTypes; + } + + public ResNotifyType type; + public void setType(ResNotifyType type) { + this.type = type; + } + public ResNotifyType getType() { + return this.type; + } + + public ResNotifySubscriptionState state; + public void setState(ResNotifySubscriptionState state) { + this.state = state; + } + public ResNotifySubscriptionState getState() { + return this.state; + } + + public java.lang.String accountUuid; + public void setAccountUuid(java.lang.String accountUuid) { + this.accountUuid = accountUuid; + } + public java.lang.String getAccountUuid() { + return this.accountUuid; + } + + public java.sql.Timestamp createDate; + public void setCreateDate(java.sql.Timestamp createDate) { + this.createDate = createDate; + } + public java.sql.Timestamp getCreateDate() { + return this.createDate; + } + + public java.sql.Timestamp lastOpDate; + public void setLastOpDate(java.sql.Timestamp lastOpDate) { + this.lastOpDate = lastOpDate; + } + public java.sql.Timestamp getLastOpDate() { + return this.lastOpDate; + } + + public ResNotifyWebhookRefInventory webhookRef; + public void setWebhookRef(ResNotifyWebhookRefInventory webhookRef) { + this.webhookRef = webhookRef; + } + public ResNotifyWebhookRefInventory getWebhookRef() { + return this.webhookRef; + } + +} diff --git a/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/ResNotifySubscriptionState.java b/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/ResNotifySubscriptionState.java new file mode 100644 index 00000000000..18351896246 --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/ResNotifySubscriptionState.java @@ -0,0 +1,6 @@ +package org.zstack.sdk.zwatch.resnotify; + +public enum ResNotifySubscriptionState { + Enabled, + Disabled, +} diff --git a/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/ResNotifyType.java b/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/ResNotifyType.java new file mode 100644 index 00000000000..5a00aa5b80e --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/ResNotifyType.java @@ -0,0 +1,6 @@ +package org.zstack.sdk.zwatch.resnotify; + +public enum ResNotifyType { + WEBHOOK, + WEBSOCKET, +} diff --git a/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/ResNotifyWebhookRefInventory.java b/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/ResNotifyWebhookRefInventory.java new file mode 100644 index 00000000000..cf2c73bc674 --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/ResNotifyWebhookRefInventory.java @@ -0,0 +1,31 @@ +package org.zstack.sdk.zwatch.resnotify; + + + +public class ResNotifyWebhookRefInventory { + + public java.lang.String uuid; + public void setUuid(java.lang.String uuid) { + this.uuid = uuid; + } + public java.lang.String getUuid() { + return this.uuid; + } + + public java.lang.String webhookUrl; + public void setWebhookUrl(java.lang.String webhookUrl) { + this.webhookUrl = webhookUrl; + } + public java.lang.String getWebhookUrl() { + return this.webhookUrl; + } + + public java.lang.String customHeaders; + public void setCustomHeaders(java.lang.String customHeaders) { + this.customHeaders = customHeaders; + } + public java.lang.String getCustomHeaders() { + return this.customHeaders; + } + +} diff --git a/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/SubscribeResNotifyAction.java b/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/SubscribeResNotifyAction.java new file mode 100644 index 00000000000..05e5af5d9de --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/SubscribeResNotifyAction.java @@ -0,0 +1,128 @@ +package org.zstack.sdk.zwatch.resnotify; + +import java.util.HashMap; +import java.util.Map; +import org.zstack.sdk.*; + +public class SubscribeResNotifyAction extends AbstractAction { + + private static final HashMap parameterMap = new HashMap<>(); + + private static final HashMap nonAPIParameterMap = new HashMap<>(); + + public static class Result { + public ErrorCode error; + public org.zstack.sdk.zwatch.resnotify.SubscribeResNotifyResult value; + + public Result throwExceptionIfError() { + if (error != null) { + throw new ApiException( + String.format("error[code: %s, description: %s, details: %s, globalErrorCode: %s]", error.code, error.description, error.details, error.globalErrorCode) + ); + } + + return this; + } + } + + @Param(required = false, maxLength = 255, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.lang.String name; + + @Param(required = false, maxLength = 1024, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.lang.String description; + + @Param(required = false, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.util.List resourceTypes; + + @Param(required = false, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.util.List eventTypes; + + @Param(required = false, validValues = {"WEBHOOK"}, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.lang.String type = "WEBHOOK"; + + @Param(required = true, maxLength = 2048, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.lang.String webhookUrl; + + @Param(required = false, maxLength = 256, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.lang.String secret; + + @Param(required = false, maxLength = 2048, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.lang.String customHeaders; + + @Param(required = false) + public java.lang.String resourceUuid; + + @Param(required = false, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.util.List tagUuids; + + @Param(required = false) + public java.util.List systemTags; + + @Param(required = false) + public java.util.List userTags; + + @Param(required = false) + public String sessionId; + + @Param(required = false) + public String accessKeyId; + + @Param(required = false) + public String accessKeySecret; + + @Param(required = false) + public String requestIp; + + @NonAPIParam + public long timeout = -1; + + @NonAPIParam + public long pollingInterval = -1; + + + private Result makeResult(ApiResult res) { + Result ret = new Result(); + if (res.error != null) { + ret.error = res.error; + return ret; + } + + org.zstack.sdk.zwatch.resnotify.SubscribeResNotifyResult value = res.getResult(org.zstack.sdk.zwatch.resnotify.SubscribeResNotifyResult.class); + ret.value = value == null ? new org.zstack.sdk.zwatch.resnotify.SubscribeResNotifyResult() : value; + + return ret; + } + + public Result call() { + ApiResult res = ZSClient.call(this); + return makeResult(res); + } + + public void call(final Completion completion) { + ZSClient.call(this, new InternalCompletion() { + @Override + public void complete(ApiResult res) { + completion.complete(makeResult(res)); + } + }); + } + + protected Map getParameterMap() { + return parameterMap; + } + + protected Map getNonAPIParameterMap() { + return nonAPIParameterMap; + } + + protected RestInfo getRestInfo() { + RestInfo info = new RestInfo(); + info.httpMethod = "POST"; + info.path = "/zwatch/resnotify/subscriptions"; + info.needSession = true; + info.needPoll = true; + info.parameterName = "params"; + return info; + } + +} diff --git a/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/SubscribeResNotifyResult.java b/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/SubscribeResNotifyResult.java new file mode 100644 index 00000000000..60ba0b47c8e --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/SubscribeResNotifyResult.java @@ -0,0 +1,14 @@ +package org.zstack.sdk.zwatch.resnotify; + +import org.zstack.sdk.zwatch.resnotify.ResNotifySubscriptionInventory; + +public class SubscribeResNotifyResult { + public ResNotifySubscriptionInventory inventory; + public void setInventory(ResNotifySubscriptionInventory inventory) { + this.inventory = inventory; + } + public ResNotifySubscriptionInventory getInventory() { + return this.inventory; + } + +} diff --git a/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/UpdateResNotifySubscriptionAction.java b/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/UpdateResNotifySubscriptionAction.java new file mode 100644 index 00000000000..3ec79ae0a35 --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/UpdateResNotifySubscriptionAction.java @@ -0,0 +1,125 @@ +package org.zstack.sdk.zwatch.resnotify; + +import java.util.HashMap; +import java.util.Map; +import org.zstack.sdk.*; + +public class UpdateResNotifySubscriptionAction extends AbstractAction { + + private static final HashMap parameterMap = new HashMap<>(); + + private static final HashMap nonAPIParameterMap = new HashMap<>(); + + public static class Result { + public ErrorCode error; + public org.zstack.sdk.zwatch.resnotify.UpdateResNotifySubscriptionResult value; + + public Result throwExceptionIfError() { + if (error != null) { + throw new ApiException( + String.format("error[code: %s, description: %s, details: %s, globalErrorCode: %s]", error.code, error.description, error.details, error.globalErrorCode) + ); + } + + return this; + } + } + + @Param(required = true, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.lang.String uuid; + + @Param(required = false, maxLength = 255, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.lang.String name; + + @Param(required = false, maxLength = 1024, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.lang.String description; + + @Param(required = false, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.util.List resourceTypes; + + @Param(required = false, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.util.List eventTypes; + + @Param(required = false, validValues = {"Enabled","Disabled"}, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.lang.String state; + + @Param(required = false, maxLength = 2048, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.lang.String webhookUrl; + + @Param(required = false, maxLength = 256, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.lang.String secret; + + @Param(required = false, maxLength = 2048, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.lang.String customHeaders; + + @Param(required = false) + public java.util.List systemTags; + + @Param(required = false) + public java.util.List userTags; + + @Param(required = false) + public String sessionId; + + @Param(required = false) + public String accessKeyId; + + @Param(required = false) + public String accessKeySecret; + + @Param(required = false) + public String requestIp; + + @NonAPIParam + public long timeout = -1; + + @NonAPIParam + public long pollingInterval = -1; + + + private Result makeResult(ApiResult res) { + Result ret = new Result(); + if (res.error != null) { + ret.error = res.error; + return ret; + } + + org.zstack.sdk.zwatch.resnotify.UpdateResNotifySubscriptionResult value = res.getResult(org.zstack.sdk.zwatch.resnotify.UpdateResNotifySubscriptionResult.class); + ret.value = value == null ? new org.zstack.sdk.zwatch.resnotify.UpdateResNotifySubscriptionResult() : value; + + return ret; + } + + public Result call() { + ApiResult res = ZSClient.call(this); + return makeResult(res); + } + + public void call(final Completion completion) { + ZSClient.call(this, new InternalCompletion() { + @Override + public void complete(ApiResult res) { + completion.complete(makeResult(res)); + } + }); + } + + protected Map getParameterMap() { + return parameterMap; + } + + protected Map getNonAPIParameterMap() { + return nonAPIParameterMap; + } + + protected RestInfo getRestInfo() { + RestInfo info = new RestInfo(); + info.httpMethod = "PUT"; + info.path = "/zwatch/resnotify/subscriptions/{uuid}/actions"; + info.needSession = true; + info.needPoll = true; + info.parameterName = "updateResNotifySubscription"; + return info; + } + +} diff --git a/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/UpdateResNotifySubscriptionResult.java b/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/UpdateResNotifySubscriptionResult.java new file mode 100644 index 00000000000..8f5b2f62157 --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/zwatch/resnotify/UpdateResNotifySubscriptionResult.java @@ -0,0 +1,14 @@ +package org.zstack.sdk.zwatch.resnotify; + +import org.zstack.sdk.zwatch.resnotify.ResNotifySubscriptionInventory; + +public class UpdateResNotifySubscriptionResult { + public ResNotifySubscriptionInventory inventory; + public void setInventory(ResNotifySubscriptionInventory inventory) { + this.inventory = inventory; + } + public ResNotifySubscriptionInventory getInventory() { + return this.inventory; + } + +} diff --git a/testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy b/testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy index 396e175189c..af8379f56e4 100644 --- a/testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy +++ b/testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy @@ -56760,6 +56760,116 @@ abstract class ApiHelper { } + def deleteResNotifySubscription(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.zwatch.resnotify.DeleteResNotifySubscriptionAction.class) Closure c) { + def a = new org.zstack.sdk.zwatch.resnotify.DeleteResNotifySubscriptionAction() + a.sessionId = Test.currentEnvSpec?.session?.uuid + c.resolveStrategy = Closure.OWNER_FIRST + c.delegate = a + c() + + + if (System.getProperty("apipath") != null) { + if (a.apiId == null) { + a.apiId = Platform.uuid + } + + def tracker = new ApiPathTracker(a.apiId) + def out = errorOut(a.call()) + def path = tracker.getApiPath() + if (!path.isEmpty()) { + Test.apiPaths[a.class.name] = path.join(" --->\n") + } + + return out + } else { + return errorOut(a.call()) + } + } + + + def queryResNotifySubscription(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.zwatch.resnotify.QueryResNotifySubscriptionAction.class) Closure c) { + def a = new org.zstack.sdk.zwatch.resnotify.QueryResNotifySubscriptionAction() + a.sessionId = Test.currentEnvSpec?.session?.uuid + c.resolveStrategy = Closure.OWNER_FIRST + c.delegate = a + c() + + a.conditions = a.conditions.collect { it.toString() } + + + if (System.getProperty("apipath") != null) { + if (a.apiId == null) { + a.apiId = Platform.uuid + } + + def tracker = new ApiPathTracker(a.apiId) + def out = errorOut(a.call()) + def path = tracker.getApiPath() + if (!path.isEmpty()) { + Test.apiPaths[a.class.name] = path.join(" --->\n") + } + + return out + } else { + return errorOut(a.call()) + } + } + + + def subscribeResNotify(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.zwatch.resnotify.SubscribeResNotifyAction.class) Closure c) { + def a = new org.zstack.sdk.zwatch.resnotify.SubscribeResNotifyAction() + a.sessionId = Test.currentEnvSpec?.session?.uuid + c.resolveStrategy = Closure.OWNER_FIRST + c.delegate = a + c() + + + if (System.getProperty("apipath") != null) { + if (a.apiId == null) { + a.apiId = Platform.uuid + } + + def tracker = new ApiPathTracker(a.apiId) + def out = errorOut(a.call()) + def path = tracker.getApiPath() + if (!path.isEmpty()) { + Test.apiPaths[a.class.name] = path.join(" --->\n") + } + + return out + } else { + return errorOut(a.call()) + } + } + + + def updateResNotifySubscription(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.zwatch.resnotify.UpdateResNotifySubscriptionAction.class) Closure c) { + def a = new org.zstack.sdk.zwatch.resnotify.UpdateResNotifySubscriptionAction() + a.sessionId = Test.currentEnvSpec?.session?.uuid + c.resolveStrategy = Closure.OWNER_FIRST + c.delegate = a + c() + + + if (System.getProperty("apipath") != null) { + if (a.apiId == null) { + a.apiId = Platform.uuid + } + + def tracker = new ApiPathTracker(a.apiId) + def out = errorOut(a.call()) + def path = tracker.getApiPath() + if (!path.isEmpty()) { + Test.apiPaths[a.class.name] = path.join(" --->\n") + } + + return out + } else { + return errorOut(a.call()) + } + } + + def addThirdpartyPlatform(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.zwatch.thirdparty.api.AddThirdpartyPlatformAction.class) Closure c) { def a = new org.zstack.sdk.zwatch.thirdparty.api.AddThirdpartyPlatformAction() a.sessionId = Test.currentEnvSpec?.session?.uuid