From c2f85438d6f3b0fd2e1f86d84eee6e9967025eb6 Mon Sep 17 00:00:00 2001 From: Michael James Hoppal Date: Wed, 8 Jun 2016 13:31:39 -0600 Subject: [PATCH] Add period field to notification methods This will allow periodic notifications Change-Id: Ifa7c1e499d4ac45d543285463036b8a058f70c26 --- .gitignore | 1 + devstack/files/monasca-api/api-config.yml | 3 + .../monasca-notification/notification.yaml | 6 +- devstack/files/schema/mon_mysql.sql | 3 +- devstack/plugin.sh | 3 +- docs/monasca-api-spec.md | 26 ++- java/src/main/java/monasca/api/ApiConfig.java | 5 +- .../CreateNotificationMethodCommand.java | 48 +++-- .../UpdateNotificationMethodCommand.java | 89 +++++++++ .../NotificationMethodValidation.java | 51 +++++ .../NotificationMethod.java | 17 +- .../NotificationMethodRepo.java | 6 +- .../NotificationMethodSqlRepoImpl.java | 10 +- .../NotificationMethodMySqlRepoImpl.java | 20 +- .../resource/NotificationMethodResource.java | 25 ++- java/src/main/resources/api-config.yml | 4 + .../command/CreateNotificationMethodTest.java | 80 ++++++-- .../domain/model/NotificationMethodTest.java | 4 +- ...tificationMethodSqlRepositoryImplTest.java | 25 +-- ...ficationMethodMySqlRepositoryImplTest.java | 35 +++- .../NotificationMethodIntegrationTest.java | 14 +- .../NotificationMethodResourceTest.java | 133 +++++++++--- .../newNotificationMethodWithPeriod.json | 1 + .../fixtures/notificationMethod.json | 2 +- .../src/test/resources/monasca-api-config.yml | 3 + .../persistence/mysql/notification_method.sql | 1 + .../mysql/notifications_repository.py | 13 +- .../repositories/notifications_repository.py | 6 +- .../common/repositories/sqla/models.py | 2 + .../sqla/notifications_repository.py | 10 +- monasca_api/tests/sqlite_alarm.sql | 1 + monasca_api/tests/test_nm_repository.py | 19 +- monasca_api/tests/test_validation.py | 78 +++++++- .../notifications_request_body_schema.py | 48 +++-- monasca_api/v2/reference/__init__.py | 6 +- monasca_api/v2/reference/notifications.py | 29 ++- .../services/monasca_client.py | 10 +- monasca_tempest_tests/tests/api/helpers.py | 5 +- .../tests/api/test_notification_methods.py | 189 +++++++++++++++++- 39 files changed, 831 insertions(+), 200 deletions(-) create mode 100644 java/src/main/java/monasca/api/app/command/UpdateNotificationMethodCommand.java create mode 100644 java/src/main/java/monasca/api/app/validation/NotificationMethodValidation.java create mode 100644 java/src/test/resources/fixtures/newNotificationMethodWithPeriod.json diff --git a/.gitignore b/.gitignore index 319171e4a..957d4e2b7 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,4 @@ log/ db/config.yml virtenv/* .vagrant +AUTHORS diff --git a/devstack/files/monasca-api/api-config.yml b/devstack/files/monasca-api/api-config.yml index a60ec7ca1..0074b87fa 100644 --- a/devstack/files/monasca-api/api-config.yml +++ b/devstack/files/monasca-api/api-config.yml @@ -29,6 +29,9 @@ metricsTopic: metrics # Topic for publishing domain events to eventsTopic: events +validNotificationPeriods: + - 60 + kafka: brokerUris: - "127.0.0.1:9092" diff --git a/devstack/files/monasca-notification/notification.yaml b/devstack/files/monasca-notification/notification.yaml index 78225b033..13b09ed7a 100644 --- a/devstack/files/monasca-notification/notification.yaml +++ b/devstack/files/monasca-notification/notification.yaml @@ -1,5 +1,5 @@ # -# (C) Copyright 2015 Hewlett Packard Enterprise Development Company LP +# (C) Copyright 2015,2016 Hewlett Packard Enterprise Development Company LP # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,6 +21,8 @@ kafka: alarm_topic: "alarm-state-transitions" notification_topic: "alarm-notifications" notification_retry_topic: "retry-notifications" + periodic: + 60: 60-seconds-notifications max_offset_lag: 600 # In seconds, undefined for none mysql: @@ -67,6 +69,8 @@ zookeeper: url: "127.0.0.1:2181" notification_path: "/notification/alarms" notification_retry_path: "/notification/retry" + periodic_path: + 60: /notification/60_seconds logging: # Used in logging.dictConfig version: 1 diff --git a/devstack/files/schema/mon_mysql.sql b/devstack/files/schema/mon_mysql.sql index 3ca50daa2..a6486d440 100644 --- a/devstack/files/schema/mon_mysql.sql +++ b/devstack/files/schema/mon_mysql.sql @@ -1,5 +1,5 @@ /* -* (C) Copyright 2015 Hewlett Packard Enterprise Development Company LP +* (C) Copyright 2015,2016 Hewlett Packard Enterprise Development Company LP * Copyright 2016 FUJITSU LIMITED * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -142,6 +142,7 @@ CREATE TABLE `notification_method` ( `name` varchar(250) COLLATE utf8mb4_unicode_ci DEFAULT NULL, `type` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL, `address` varchar(512) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `period` int NOT NULL DEFAULT 0, `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, PRIMARY KEY (`id`), diff --git a/devstack/plugin.sh b/devstack/plugin.sh index 75dcee363..2a4766089 100755 --- a/devstack/plugin.sh +++ b/devstack/plugin.sh @@ -1,5 +1,5 @@ # -# (C) Copyright 2015 Hewlett Packard Enterprise Development Company LP +# (C) Copyright 2015,2016 Hewlett Packard Enterprise Development Company LP # Copyright 2016 FUJITSU LIMITED # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -666,6 +666,7 @@ function install_schema { /opt/kafka/bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 12 --topic alarm-notifications /opt/kafka/bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 12 --topic stream-notifications /opt/kafka/bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 3 --topic retry-notifications + /opt/kafka/bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 3 --topic 60-seconds-notifications } diff --git a/docs/monasca-api-spec.md b/docs/monasca-api-spec.md index cece63b0f..d2813509f 100644 --- a/docs/monasca-api-spec.md +++ b/docs/monasca-api-spec.md @@ -1440,6 +1440,7 @@ None. * name (string(250), required) - A descriptive name of the notification method. * type (string(100), required) - The type of notification method (`EMAIL`, `WEBHOOK`, or `PAGERDUTY` ). * address (string(100), required) - The email/url address to notify. +* period (integer, optional) - The interval in seconds to periodically send the notification. Only can be set as a non zero value for WEBHOOK methods. Allowed periods for Webhooks by default are 0, 60. You can change allow periods for webhooks in the api config. The notification will continue to be sent at the defined interval until the alarm it is associated with changes state. #### Request Examples ``` @@ -1469,6 +1470,7 @@ Returns a JSON notification method object with the following fields: * name (string) - Name of notification method * type (string) - Type of notification method * address (string) - Address of notification method +* period (integer) - Period of notification method #### Response Examples ``` @@ -1482,7 +1484,8 @@ Returns a JSON notification method object with the following fields: ], "name":"Name of notification method", "type":"EMAIL", - "address":"john.doe@hp.com" + "address":"john.doe@hp.com", + "period":0 } ``` ___ @@ -1530,6 +1533,7 @@ Returns a JSON object with a 'links' array of links and an 'elements' array of n * name (string) - Name of notification method * type (string) - Type of notification method * address (string) - Address of notification method +* period (integer) - Period of notification method #### Response Examples ``` @@ -1555,7 +1559,8 @@ Returns a JSON object with a 'links' array of links and an 'elements' array of n ], "name": "Name of notification method", "type": "EMAIL", - "address": "john.doe@hp.com" + "address": "john.doe@hp.com", + "period": 0 }, { "id": "c60ec47e-5038-4bf1-9f95-4046c6e9a759", @@ -1566,8 +1571,9 @@ Returns a JSON object with a 'links' array of links and an 'elements' array of n } ], "name": "Name of notification method", - "type": "EMAIL", - "address": "jane.doe@hp.com" + "type": "WEBHOOK", + "address": "http://localhost:3333", + "period": 1 } ] } @@ -1610,6 +1616,7 @@ Returns a JSON notification method object with the following fields: * name (string) - Name of notification method * type (string) - Type of notification method * address (string) - Address of notification method +* period (integer) - Period of notification method #### Response Examples ``` @@ -1623,7 +1630,8 @@ Returns a JSON notification method object with the following fields: ], "name":"Name of notification method", "type":"EMAIL", - "address":"john.doe@hp.com" + "address":"john.doe@hp.com", + "period": 0 } ``` ___ @@ -1648,6 +1656,7 @@ None. * name (string(250), required) - A descriptive name of the notifcation method. * type (string(100), required) - The type of notification method (`EMAIL`, `WEBHOOK`, or `PAGERDUTY` ). * address (string(100), required) - The email/url address to notify. +* period (integer, optional) - The interval in seconds to periodically send the notification. Only can be set as a non zero value for WEBHOOK methods. Allowed periods for Webhooks by default are 0, 60. You can change allow periods for webhooks in the api config. The notification will continue to be sent at the defined interval until the alarm it is associated with changes state. #### Request Examples ```` @@ -1660,7 +1669,8 @@ Cache-Control: no-cache { "name":"New name of notification method", "type":"EMAIL", - "address":"jane.doe@hp.com" + "address":"jane.doe@hp.com", + "period":0 } ```` @@ -1677,6 +1687,7 @@ Returns a JSON notification method object with the following fields: * name (string) - Name of notification method * type (string) - Type of notification method * address (string) - Address of notification method +* period (integer) - Period of notification method #### Response Examples ```` @@ -1690,7 +1701,8 @@ Returns a JSON notification method object with the following fields: ], "name":"New name of notification method", "type":"EMAIL", - "address":"jane.doe@hp.com" + "address":"jane.doe@hp.com", + "period":0 } ```` ___ diff --git a/java/src/main/java/monasca/api/ApiConfig.java b/java/src/main/java/monasca/api/ApiConfig.java index 64ba2c3b2..a9d6addc2 100644 --- a/java/src/main/java/monasca/api/ApiConfig.java +++ b/java/src/main/java/monasca/api/ApiConfig.java @@ -25,6 +25,8 @@ import io.dropwizard.db.DataSourceFactory; import org.hibernate.validator.constraints.NotEmpty; +import java.util.List; + import javax.validation.Valid; import javax.validation.constraints.NotNull; @@ -42,7 +44,8 @@ public class ApiConfig extends Configuration { public int maxQueryLimit; @NotEmpty public String alarmStateTransitionsTopic = "alarm-state-transitions"; - + @NotEmpty + public List validNotificationPeriods; @Valid @NotNull public DataSourceFactory mysql; diff --git a/java/src/main/java/monasca/api/app/command/CreateNotificationMethodCommand.java b/java/src/main/java/monasca/api/app/command/CreateNotificationMethodCommand.java index 50a121f55..cb4bbac6a 100644 --- a/java/src/main/java/monasca/api/app/command/CreateNotificationMethodCommand.java +++ b/java/src/main/java/monasca/api/app/command/CreateNotificationMethodCommand.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Hewlett-Packard Development Company, L.P. + * (C) Copyright 2014-2016 Hewlett Packard Enterprise Development Company LP * * Licensed 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 @@ -15,12 +15,12 @@ package monasca.api.app.command; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; -import org.apache.commons.validator.routines.EmailValidator; -import org.apache.commons.validator.routines.UrlValidator; import org.hibernate.validator.constraints.NotEmpty; +import java.util.List; + +import monasca.api.app.validation.NotificationMethodValidation; import monasca.api.domain.model.notificationmethod.NotificationMethodType; -import monasca.api.resource.exception.Exceptions; public class CreateNotificationMethodCommand { @NotEmpty @@ -31,13 +31,15 @@ public class CreateNotificationMethodCommand { @NotEmpty @Size(min = 1, max = 512) public String address; + public String period; - public CreateNotificationMethodCommand() {} + public CreateNotificationMethodCommand() {this.period = "0";} - public CreateNotificationMethodCommand(String name, NotificationMethodType type, String address) { + public CreateNotificationMethodCommand(String name, NotificationMethodType type, String address, String period) { this.name = name; this.type = type; this.address = address; + this.period = period == null ? "0" : period; } @Override @@ -59,26 +61,28 @@ public class CreateNotificationMethodCommand { return false; } else if (!name.equals(other.name)) return false; + if (period == null) { + if (other.period != null) + return false; + } else if (!period.equals(other.period)) + return false; if (type != other.type) return false; return true; } - public void validate() { - switch (type) { - case EMAIL : { - if (!EmailValidator.getInstance(true).isValid(address)) - throw Exceptions.unprocessableEntity("Address %s is not of correct format", address); - }; break; - case WEBHOOK : { - String[] schemes = {"http","https"}; - UrlValidator urlValidator = new UrlValidator(schemes, UrlValidator.ALLOW_LOCAL_URLS | UrlValidator.ALLOW_2_SLASHES); - if (!urlValidator.isValid(address)) - throw Exceptions.unprocessableEntity("Address %s is not of correct format", address); - }; break; - case PAGERDUTY : { - // No known validation for PAGERDUTY type at this time - }; break; - } + public void validate(List validPeriods) { + NotificationMethodValidation.validate(type, address, period, validPeriods); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((type == null) ? 0 : type.hashCode()); + result = prime * result + ((address == null) ? 0 : address.hashCode()); + result = prime * result + ((period == null) ? 0 : period.hashCode()); + return result; } } diff --git a/java/src/main/java/monasca/api/app/command/UpdateNotificationMethodCommand.java b/java/src/main/java/monasca/api/app/command/UpdateNotificationMethodCommand.java new file mode 100644 index 000000000..ec7eb3239 --- /dev/null +++ b/java/src/main/java/monasca/api/app/command/UpdateNotificationMethodCommand.java @@ -0,0 +1,89 @@ +/* + * (C) Copyright 2016 Hewlett Packard Enterprise Development Company LP + * + * Licensed 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 monasca.api.app.command; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import org.hibernate.validator.constraints.NotEmpty; + +import java.util.List; + +import monasca.api.app.validation.NotificationMethodValidation; +import monasca.api.domain.model.notificationmethod.NotificationMethodType; + +public class UpdateNotificationMethodCommand { + @NotEmpty + @Size(min = 1, max = 250) + public String name; + @NotNull + public NotificationMethodType type; + @NotEmpty + @Size(min = 1, max = 512) + public String address; + @NotNull + public String period; + + public UpdateNotificationMethodCommand() {} + + public UpdateNotificationMethodCommand(String name, NotificationMethodType type, String address, String period) { + this.name = name; + this.type = type; + this.address = address; + this.period = period; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + UpdateNotificationMethodCommand other = (UpdateNotificationMethodCommand) obj; + if (address == null) { + if (other.address != null) + return false; + } else if (!address.equals(other.address)) + return false; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + if (period == null) { + if (other.period != null) + return false; + } else if (!period.equals(other.period)) + return false; + if (type != other.type) + return false; + return true; + } + + public void validate(List validPeriods) { + NotificationMethodValidation.validate(type, address, period, validPeriods); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((type == null) ? 0 : type.hashCode()); + result = prime * result + ((address == null) ? 0 : address.hashCode()); + result = prime * result + ((period == null) ? 0 : period.hashCode()); + return result; + } +} diff --git a/java/src/main/java/monasca/api/app/validation/NotificationMethodValidation.java b/java/src/main/java/monasca/api/app/validation/NotificationMethodValidation.java new file mode 100644 index 000000000..bb08b5e41 --- /dev/null +++ b/java/src/main/java/monasca/api/app/validation/NotificationMethodValidation.java @@ -0,0 +1,51 @@ +/* + * (C) Copyright 2016 Hewlett Packard Enterprise Development Company LP + * + * Licensed 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 monasca.api.app.validation; + +import monasca.api.domain.model.notificationmethod.NotificationMethodType; +import monasca.api.resource.exception.Exceptions; + +import org.apache.commons.validator.routines.EmailValidator; +import org.apache.commons.validator.routines.UrlValidator; + +import java.util.List; + +public class NotificationMethodValidation { + + public static void validate(NotificationMethodType type, String address, String period, + List validPeriods) { + int convertedPeriod = Validation.parseAndValidateNumber(period, "period"); + switch (type) { + case EMAIL : { + if (!EmailValidator.getInstance(true).isValid(address)) + throw Exceptions.unprocessableEntity("Address %s is not of correct format", address); + if (convertedPeriod != 0) + throw Exceptions.unprocessableEntity("Period can not be non zero for Email"); + } break; + case WEBHOOK : { + String[] schemes = {"http","https"}; + UrlValidator urlValidator = new UrlValidator(schemes, UrlValidator.ALLOW_LOCAL_URLS | UrlValidator.ALLOW_2_SLASHES); + if (!urlValidator.isValid(address)) + throw Exceptions.unprocessableEntity("Address %s is not of correct format", address); + } break; + case PAGERDUTY : { + if (convertedPeriod != 0) + throw Exceptions.unprocessableEntity("Period can not be non zero for Pagerduty"); + } break; + } + if (convertedPeriod != 0 && !validPeriods.contains(convertedPeriod)){ + throw Exceptions.unprocessableEntity("%d is not a valid period", convertedPeriod); + } + } +} diff --git a/java/src/main/java/monasca/api/domain/model/notificationmethod/NotificationMethod.java b/java/src/main/java/monasca/api/domain/model/notificationmethod/NotificationMethod.java index c6a4a30b3..f23b58822 100644 --- a/java/src/main/java/monasca/api/domain/model/notificationmethod/NotificationMethod.java +++ b/java/src/main/java/monasca/api/domain/model/notificationmethod/NotificationMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Hewlett-Packard Development Company, L.P. + * (C) Copyright 2014-2016 Hewlett Packard Enterprise Development Company LP * * Licensed 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 @@ -24,14 +24,16 @@ public class NotificationMethod extends AbstractEntity implements Linked { private String name; private NotificationMethodType type; private String address; + private int period; public NotificationMethod() {} - public NotificationMethod(String id, String name, NotificationMethodType type, String address) { + public NotificationMethod(String id, String name, NotificationMethodType type, String address, int period) { this.id = id; this.name = name; this.type = type; this.address = address; + this.period = period; } @Override @@ -53,6 +55,8 @@ public class NotificationMethod extends AbstractEntity implements Linked { return false; } else if (!name.equals(other.name)) return false; + if (period != other.period) + return false; if (type != other.type) return false; return true; @@ -78,6 +82,10 @@ public class NotificationMethod extends AbstractEntity implements Linked { return type; } + public int getPeriod() { + return period; + } + @Override public int hashCode() { final int prime = 31; @@ -85,6 +93,7 @@ public class NotificationMethod extends AbstractEntity implements Linked { result = prime * result + ((address == null) ? 0 : address.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + ((type == null) ? 0 : type.hashCode()); + result = prime * result + period; return result; } @@ -107,4 +116,8 @@ public class NotificationMethod extends AbstractEntity implements Linked { public void setType(NotificationMethodType type) { this.type = type; } + + public void setPeriod(int period) { + this.period = period; + } } diff --git a/java/src/main/java/monasca/api/domain/model/notificationmethod/NotificationMethodRepo.java b/java/src/main/java/monasca/api/domain/model/notificationmethod/NotificationMethodRepo.java index 76aac5557..bda1553d2 100644 --- a/java/src/main/java/monasca/api/domain/model/notificationmethod/NotificationMethodRepo.java +++ b/java/src/main/java/monasca/api/domain/model/notificationmethod/NotificationMethodRepo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Hewlett-Packard Development Company, L.P. + * (C) Copyright 2014-2016 Hewlett Packard Enterprise Development Company LP * * Licensed 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 @@ -22,7 +22,7 @@ import monasca.api.domain.exception.EntityNotFoundException; */ public interface NotificationMethodRepo { NotificationMethod create(String tenantId, String name, NotificationMethodType type, - String address); + String address, int period); /** * @throws EntityNotFoundException if a notification method cannot be found for the @@ -44,7 +44,7 @@ public interface NotificationMethodRepo { * {@code notificationMethodId} */ NotificationMethod update(String tenantId, String notificationMethodId, String name, - NotificationMethodType type, String address); + NotificationMethodType type, String address, int period); List find(String tenantId, List sortBy, String offset, int limit); } diff --git a/java/src/main/java/monasca/api/infrastructure/persistence/hibernate/NotificationMethodSqlRepoImpl.java b/java/src/main/java/monasca/api/infrastructure/persistence/hibernate/NotificationMethodSqlRepoImpl.java index 4e8f5ece8..93d38ab57 100644 --- a/java/src/main/java/monasca/api/infrastructure/persistence/hibernate/NotificationMethodSqlRepoImpl.java +++ b/java/src/main/java/monasca/api/infrastructure/persistence/hibernate/NotificationMethodSqlRepoImpl.java @@ -1,5 +1,6 @@ /* * Copyright 2015 FUJITSU LIMITED + * (C) Copyright 2016 Hewlett Packard Enterprise Development Company LP * * Licensed 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 @@ -53,7 +54,7 @@ public class NotificationMethodSqlRepoImpl @Override public NotificationMethod create(String tenantId, String name, NotificationMethodType type, - String address) { + String address, int period) { Transaction tx = null; Session session = null; try { @@ -73,6 +74,7 @@ public class NotificationMethodSqlRepoImpl name, AlarmNotificationMethodType.valueOf(type.name()), address, + period, now, now ); @@ -158,7 +160,7 @@ public class NotificationMethodSqlRepoImpl @Override public NotificationMethod update(String tenantId, String notificationMethodId, String name, - NotificationMethodType type, String address) { + NotificationMethodType type, String address, int period) { Session session = null; Transaction tx = null; try { @@ -180,6 +182,7 @@ public class NotificationMethodSqlRepoImpl db.setName(name); db.setType(AlarmNotificationMethodType.valueOf(type.name())); db.setAddress(address); + db.setPeriod(period); db.setUpdatedAt(this.getUTCNow()); session.save(db); @@ -270,7 +273,8 @@ public class NotificationMethodSqlRepoImpl db.getId(), db.getName(), NotificationMethodType.valueOf(db.getType().name()), - db.getAddress() + db.getAddress(), + db.getPeriod() ); } } diff --git a/java/src/main/java/monasca/api/infrastructure/persistence/mysql/NotificationMethodMySqlRepoImpl.java b/java/src/main/java/monasca/api/infrastructure/persistence/mysql/NotificationMethodMySqlRepoImpl.java index 5b6461d8e..031101eda 100644 --- a/java/src/main/java/monasca/api/infrastructure/persistence/mysql/NotificationMethodMySqlRepoImpl.java +++ b/java/src/main/java/monasca/api/infrastructure/persistence/mysql/NotificationMethodMySqlRepoImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Hewlett-Packard Development Company, L.P. + * (C) Copyright 2014-2016 Hewlett Packard Enterprise Development Company LP * * Licensed 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 @@ -54,7 +54,7 @@ public class NotificationMethodMySqlRepoImpl implements NotificationMethodRepo { @Override public NotificationMethod create(String tenantId, String name, - NotificationMethodType type, String address) { + NotificationMethodType type, String address, int period) { try (Handle h = db.open()) { h.begin(); if (getNotificationIdForTenantIdAndName(h,tenantId, name) != null) @@ -63,11 +63,11 @@ public class NotificationMethodMySqlRepoImpl implements NotificationMethodRepo { String id = UUID.randomUUID().toString(); h.insert( - "insert into notification_method (id, tenant_id, name, type, address, created_at, updated_at) values (?, ?, ?, ?, ?, NOW(), NOW())", - id, tenantId, name, type.toString(), address); + "insert into notification_method (id, tenant_id, name, type, address, period, created_at, updated_at) values (?, ?, ?, ?, ?, ?, NOW(), NOW())", + id, tenantId, name, type.toString(), address, period); LOG.debug("Creating notification method {} for {}", name, tenantId); h.commit(); - return new NotificationMethod(id, name, type, address); + return new NotificationMethod(id, name, type, address, period); } } @@ -112,7 +112,7 @@ public class NotificationMethodMySqlRepoImpl implements NotificationMethodRepo { try (Handle h = db.open()) { String rawQuery = - " SELECT nm.id, nm.tenant_id, nm.name, nm.type, nm.address, nm.created_at, nm.updated_at " + " SELECT nm.id, nm.tenant_id, nm.name, nm.type, nm.address, nm.period, nm.created_at, nm.updated_at " + "FROM notification_method as nm " + "WHERE tenant_id = :tenantId %1$s %2$s %3$s"; @@ -174,7 +174,7 @@ public class NotificationMethodMySqlRepoImpl implements NotificationMethodRepo { @Override public NotificationMethod update(String tenantId, String notificationMethodId, String name, - NotificationMethodType type, String address) { + NotificationMethodType type, String address, int period) { try (Handle h = db.open()) { h.begin(); String notificationID = getNotificationIdForTenantIdAndName(h,tenantId, name); @@ -185,13 +185,13 @@ public class NotificationMethodMySqlRepoImpl implements NotificationMethodRepo { if (h .update( - "update notification_method set name = ?, type = ?, address = ?, updated_at = NOW() " + "update notification_method set name = ?, type = ?, address = ?, period = ?, updated_at = NOW() " + "where tenant_id = ? and id = ?", - name, type.name(), address, tenantId, notificationMethodId) == 0) + name, type.name(), address, period, tenantId, notificationMethodId) == 0) throw new EntityNotFoundException("No notification method exists for %s", notificationMethodId); h.commit(); - return new NotificationMethod(notificationMethodId, name, type, address); + return new NotificationMethod(notificationMethodId, name, type, address, period); } } } diff --git a/java/src/main/java/monasca/api/resource/NotificationMethodResource.java b/java/src/main/java/monasca/api/resource/NotificationMethodResource.java index e8cb9d185..7bde467e5 100644 --- a/java/src/main/java/monasca/api/resource/NotificationMethodResource.java +++ b/java/src/main/java/monasca/api/resource/NotificationMethodResource.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Hewlett-Packard Development Company, L.P. + * (C) Copyright 2014-2016 Hewlett Packard Enterprise Development Company LP * * Licensed 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 @@ -37,7 +37,9 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; +import monasca.api.ApiConfig; import monasca.api.app.command.CreateNotificationMethodCommand; +import monasca.api.app.command.UpdateNotificationMethodCommand; import monasca.api.app.validation.Validation; import monasca.api.domain.model.notificationmethod.NotificationMethod; import monasca.api.domain.model.notificationmethod.NotificationMethodRepo; @@ -53,11 +55,16 @@ public class NotificationMethodResource { private final static List ALLOWED_SORT_BY = Arrays.asList("id", "name", "type", "address", "updated_at", "created_at"); + private final List validPeriods; + @Inject - public NotificationMethodResource(NotificationMethodRepo repo, PersistUtils persistUtils) { + public NotificationMethodResource(ApiConfig config, NotificationMethodRepo repo, + PersistUtils persistUtils) { this.repo = repo; this.persistUtils = persistUtils; + this.validPeriods = config.validNotificationPeriods == null ? Arrays.asList(0, 60): + config.validNotificationPeriods; } @POST @@ -66,10 +73,12 @@ public class NotificationMethodResource { @Produces(MediaType.APPLICATION_JSON) public Response create(@Context UriInfo uriInfo, @HeaderParam("X-Tenant-Id") String tenantId, @Valid CreateNotificationMethodCommand command) { - command.validate(); + command.validate(this.validPeriods); + int period = Validation.parseAndValidateNumber(command.period, "period"); NotificationMethod notificationMethod = - Links.hydrate(repo.create(tenantId, command.name, command.type, command.address), uriInfo, + Links.hydrate(repo.create(tenantId, command.name, command.type, + command.address, period), uriInfo, false); return Response.created(URI.create(notificationMethod.getId())).entity(notificationMethod) .build(); @@ -112,11 +121,13 @@ public class NotificationMethodResource { public NotificationMethod update(@Context UriInfo uriInfo, @HeaderParam("X-Tenant-Id") String tenantId, @PathParam("notification_method_id") String notificationMethodId, - @Valid CreateNotificationMethodCommand command) { - command.validate(); + @Valid UpdateNotificationMethodCommand command) { + command.validate(this.validPeriods); + int period = Validation.parseAndValidateNumber(command.period, "period"); return Links.hydrate( - repo.update(tenantId, notificationMethodId, command.name, command.type, command.address), + repo.update(tenantId, notificationMethodId, command.name, command.type, + command.address, period), uriInfo, true); } diff --git a/java/src/main/resources/api-config.yml b/java/src/main/resources/api-config.yml index c6c4b4385..67e824123 100644 --- a/java/src/main/resources/api-config.yml +++ b/java/src/main/resources/api-config.yml @@ -14,6 +14,10 @@ eventsTopic: events # If not set or set to any value less than or equal to 0, then defaults to 10,000. maxQueryLimit: 10000 +# Valid periods for notification +validNotificationPeriods: + - 60 + kafka: brokerUris: - 192.168.10.6:9092 diff --git a/java/src/test/java/monasca/api/app/command/CreateNotificationMethodTest.java b/java/src/test/java/monasca/api/app/command/CreateNotificationMethodTest.java index d45a65216..6c068404c 100644 --- a/java/src/test/java/monasca/api/app/command/CreateNotificationMethodTest.java +++ b/java/src/test/java/monasca/api/app/command/CreateNotificationMethodTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Hewlett-Packard Development Company, L.P. + * (C) Copyright 2014-2016 Hewlett Packard Enterprise Development Company LP * * Licensed 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 @@ -17,6 +17,8 @@ package monasca.api.app.command; import static monasca.common.dropwizard.JsonHelpers.jsonFixture; import static org.testng.Assert.assertEquals; +import java.util.Arrays; +import java.util.List; import java.util.Set; import javax.validation.ConstraintViolation; @@ -39,6 +41,7 @@ import monasca.api.domain.model.notificationmethod.NotificationMethodType; public class CreateNotificationMethodTest extends AbstractModelTest { private static Validator validator; + private List validPeriods = Arrays.asList(0, 60); @BeforeClass public static void setUp() { @@ -48,7 +51,7 @@ public class CreateNotificationMethodTest extends AbstractModelTest { public void shouldDeserializeFromJson() throws Exception { CreateNotificationMethodCommand newNotificationMethod = - new CreateNotificationMethodCommand("MyEmail", NotificationMethodType.EMAIL, "a@b"); + new CreateNotificationMethodCommand("MyEmail", NotificationMethodType.EMAIL, "a@b", "0"); String json = jsonFixture("fixtures/newNotificationMethod.json"); CreateNotificationMethodCommand other = fromJson(json, CreateNotificationMethodCommand.class); @@ -57,17 +60,26 @@ public class CreateNotificationMethodTest extends AbstractModelTest { public void shouldDeserializeFromJsonLowerCaseEnum() throws Exception { CreateNotificationMethodCommand newNotificationMethod = - new CreateNotificationMethodCommand("MyEmail", NotificationMethodType.EMAIL, "a@b"); + new CreateNotificationMethodCommand("MyEmail", NotificationMethodType.EMAIL, "a@b", "0"); String json = jsonFixture("fixtures/newNotificationMethodWithLowercaseEnum.json"); CreateNotificationMethodCommand other = fromJson(json, CreateNotificationMethodCommand.class); assertEquals(other, newNotificationMethod); } + public void shouldDeserializeFromJsonDefinedPeriod() throws Exception { + CreateNotificationMethodCommand newNotificationMethod = + new CreateNotificationMethodCommand("MyWebhook", NotificationMethodType.WEBHOOK, "http://somedomain.com", "60"); + + String json = jsonFixture("fixtures/newNotificationMethodWithPeriod.json"); + CreateNotificationMethodCommand other = fromJson(json, CreateNotificationMethodCommand.class); + assertEquals(other, newNotificationMethod); + } + @Test(expectedExceptions = JsonMappingException.class) public void shouldDeserializeFromJsonEnumError() throws Exception { CreateNotificationMethodCommand newNotificationMethod = - new CreateNotificationMethodCommand("MyEmail", NotificationMethodType.EMAIL, "a@b"); + new CreateNotificationMethodCommand("MyEmail", NotificationMethodType.EMAIL, "a@b", "0"); String json = jsonFixture("fixtures/newNotificationMethodWithInvalidEnum.json"); CreateNotificationMethodCommand other = fromJson(json, CreateNotificationMethodCommand.class); @@ -76,36 +88,54 @@ public class CreateNotificationMethodTest extends AbstractModelTest { public void testValidationForEmail() { CreateNotificationMethodCommand newNotificationMethod = - new CreateNotificationMethodCommand("MyEmail", NotificationMethodType.EMAIL, "name@domain.com"); - newNotificationMethod.validate(); + new CreateNotificationMethodCommand("MyEmail", NotificationMethodType.EMAIL, "name@domain.com", "0"); + newNotificationMethod.validate(validPeriods); } @Test(expectedExceptions = WebApplicationException.class) public void testValidationExceptionForEmail() throws Exception { CreateNotificationMethodCommand newNotificationMethod = - new CreateNotificationMethodCommand("MyEmail", NotificationMethodType.EMAIL, "name@domain."); + new CreateNotificationMethodCommand("MyEmail", NotificationMethodType.EMAIL, "name@domain.", "0"); + newNotificationMethod.validate(validPeriods); + } - newNotificationMethod.validate(); + @Test(expectedExceptions = WebApplicationException.class) + public void testValidationExceptionForNonZeroPeriodForEmail() { + CreateNotificationMethodCommand newNotificationMethod = + new CreateNotificationMethodCommand("MyEmail", NotificationMethodType.EMAIL, "name@domain.", "60"); + newNotificationMethod.validate(validPeriods); } public void testValidationForWebhook() { CreateNotificationMethodCommand newNotificationMethod = - new CreateNotificationMethodCommand("MyWebhook", NotificationMethodType.WEBHOOK, "http://somedomain.com"); - newNotificationMethod.validate(); + new CreateNotificationMethodCommand("MyWebhook", NotificationMethodType.WEBHOOK, "http://somedomain.com", "0"); + newNotificationMethod.validate(validPeriods); + } + + public void testValidationNonZeroPeriodForWebhook() { + CreateNotificationMethodCommand newNotificationMethod = + new CreateNotificationMethodCommand("MyWebhook", NotificationMethodType.WEBHOOK, "http://somedomain.com", "60"); + newNotificationMethod.validate(validPeriods); } @Test(expectedExceptions = WebApplicationException.class) public void testValidationExceptionForWebhook() throws Exception { CreateNotificationMethodCommand newNotificationMethod = - new CreateNotificationMethodCommand("MyWebhook", NotificationMethodType.WEBHOOK, "ftp://localhost"); - - newNotificationMethod.validate(); + new CreateNotificationMethodCommand("MyWebhook", NotificationMethodType.WEBHOOK, "ftp://localhost", "0"); + newNotificationMethod.validate(validPeriods); } public void testValidationForPagerduty() { CreateNotificationMethodCommand newNotificationMethod = - new CreateNotificationMethodCommand("MyPagerduty", NotificationMethodType.PAGERDUTY, "nzH2LVRdMzun11HNC2oD"); - newNotificationMethod.validate(); + new CreateNotificationMethodCommand("MyPagerduty", NotificationMethodType.PAGERDUTY, "nzH2LVRdMzun11HNC2oD", "0"); + newNotificationMethod.validate(validPeriods); + } + + @Test(expectedExceptions = WebApplicationException.class) + public void testValidationExceptionForNonZeroPeriodForPagerDuty() { + CreateNotificationMethodCommand newNotificationMethod = + new CreateNotificationMethodCommand("MyPagerduty", NotificationMethodType.PAGERDUTY, "nzH2LVRdMzun11HNC2oD", "60"); + newNotificationMethod.validate(validPeriods); } public void testValidationForMaxNameAddress() { @@ -114,7 +144,7 @@ public class CreateNotificationMethodTest extends AbstractModelTest { String address = "http://" + StringUtils.repeat("A", 502) + ".io"; assertEquals(address.length(), 512); CreateNotificationMethodCommand newNotificationMethod = - new CreateNotificationMethodCommand(name, NotificationMethodType.WEBHOOK, address); + new CreateNotificationMethodCommand(name, NotificationMethodType.WEBHOOK, address, "0"); Set> constraintViolations = validator.validate(newNotificationMethod); @@ -125,7 +155,7 @@ public class CreateNotificationMethodTest extends AbstractModelTest { String name = StringUtils.repeat("A", 251); assertEquals(name.length(), 251); CreateNotificationMethodCommand newNotificationMethod = - new CreateNotificationMethodCommand(name, NotificationMethodType.WEBHOOK, "http://somedomain.com"); + new CreateNotificationMethodCommand(name, NotificationMethodType.WEBHOOK, "http://somedomain.com", "0"); Set> constraintViolations = validator.validate(newNotificationMethod); @@ -138,7 +168,7 @@ public class CreateNotificationMethodTest extends AbstractModelTest { String address = "http://" + StringUtils.repeat("A", 503) + ".io"; assertEquals(address.length(), 513); CreateNotificationMethodCommand newNotificationMethod = - new CreateNotificationMethodCommand("MyWebhook", NotificationMethodType.WEBHOOK, address); + new CreateNotificationMethodCommand("MyWebhook", NotificationMethodType.WEBHOOK, address, "0"); Set> constraintViolations = validator.validate(newNotificationMethod); @@ -146,4 +176,18 @@ public class CreateNotificationMethodTest extends AbstractModelTest { assertEquals(constraintViolations.iterator().next().getMessage(), "size must be between 1 and 512"); } + + @Test(expectedExceptions = WebApplicationException.class) + public void testValidationExceptionForNonIntPeriod() { + CreateNotificationMethodCommand newNotificationMethod = + new CreateNotificationMethodCommand("MyEmail", NotificationMethodType.EMAIL, "name@domain.com", "interval"); + newNotificationMethod.validate(validPeriods); + } + + @Test(expectedExceptions = WebApplicationException.class) + public void testValidationExceptionForInvalidPeriod() { + CreateNotificationMethodCommand newNotificationMethod = + new CreateNotificationMethodCommand("MyWebhook", NotificationMethodType.WEBHOOK, "http://somedomain.com", "10"); + newNotificationMethod.validate(validPeriods); + } } diff --git a/java/src/test/java/monasca/api/domain/model/NotificationMethodTest.java b/java/src/test/java/monasca/api/domain/model/NotificationMethodTest.java index 69213dc7d..af862ae05 100644 --- a/java/src/test/java/monasca/api/domain/model/NotificationMethodTest.java +++ b/java/src/test/java/monasca/api/domain/model/NotificationMethodTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Hewlett-Packard Development Company, L.P. + * (C) Copyright 2014-2016 Hewlett Packard Enterprise Development Company LP * * Licensed 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 @@ -31,7 +31,7 @@ public class NotificationMethodTest extends AbstractModelTest { public NotificationMethodTest() { notificationMethod = - new NotificationMethod("123", "MyEmail", NotificationMethodType.EMAIL, "a@b"); + new NotificationMethod("123", "MyEmail", NotificationMethodType.EMAIL, "a@b", 0); notificationMethod.setLinks(Arrays.asList(new Link("self", "https://cloudsvc.example.com/v1.0"))); } diff --git a/java/src/test/java/monasca/api/infrastructure/persistence/hibernate/NotificationMethodSqlRepositoryImplTest.java b/java/src/test/java/monasca/api/infrastructure/persistence/hibernate/NotificationMethodSqlRepositoryImplTest.java index 442b615f2..018be5e43 100644 --- a/java/src/test/java/monasca/api/infrastructure/persistence/hibernate/NotificationMethodSqlRepositoryImplTest.java +++ b/java/src/test/java/monasca/api/infrastructure/persistence/hibernate/NotificationMethodSqlRepositoryImplTest.java @@ -1,5 +1,6 @@ /* * Copyright 2015 FUJITSU LIMITED + * (C) Copyright 2016 Hewlett Packard Enterprise Development Company LP * * Licensed 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 @@ -72,9 +73,9 @@ public class NotificationMethodSqlRepositoryImplTest { session.beginTransaction(); NotificationMethodDb notificationMethodDb1 = - new NotificationMethodDb("123", "444", "MyEmail", AlarmNotificationMethodType.EMAIL, "a@b", new DateTime(), new DateTime()); + new NotificationMethodDb("123", "444", "MyEmail", AlarmNotificationMethodType.EMAIL, "a@b", 0, new DateTime(), new DateTime()); NotificationMethodDb notificationMethodDb2 = - new NotificationMethodDb("124", "444", "OtherEmail", AlarmNotificationMethodType.EMAIL, "a@b", new DateTime(), new DateTime()); + new NotificationMethodDb("124", "444", "OtherEmail", AlarmNotificationMethodType.EMAIL, "a@b", 0, new DateTime(), new DateTime()); session.save(notificationMethodDb1); session.save(notificationMethodDb2); @@ -90,7 +91,7 @@ public class NotificationMethodSqlRepositoryImplTest { @Test(groups = "orm") public void shouldCreate() { - NotificationMethod nmA = repo.create("555", "MyEmail", NotificationMethodType.EMAIL, "a@b"); + NotificationMethod nmA = repo.create("555", "MyEmail", NotificationMethodType.EMAIL, "a@b", 0); NotificationMethod nmB = repo.findById("555", nmA.getId()); assertEquals(nmA, nmB); @@ -107,25 +108,25 @@ public class NotificationMethodSqlRepositoryImplTest { public void shouldFind() { List nms1 = repo.find("444", null, null, 1); - assertEquals(nms1, Arrays.asList(new NotificationMethod("123", "MyEmail", NotificationMethodType.EMAIL, "a@b"), new NotificationMethod("124", - "OtherEmail", NotificationMethodType.EMAIL, "a@b"))); + assertEquals(nms1, Arrays.asList(new NotificationMethod("123", "MyEmail", NotificationMethodType.EMAIL, "a@b", 0), new NotificationMethod("124", + "OtherEmail", NotificationMethodType.EMAIL, "a@b", 0))); List nms2 = repo.find("444", null, "123", 1); - assertEquals(nms2, Collections.singletonList(new NotificationMethod("124", "OtherEmail", NotificationMethodType.EMAIL, "a@b"))); + assertEquals(nms2, Collections.singletonList(new NotificationMethod("124", "OtherEmail", NotificationMethodType.EMAIL, "a@b", 0))); } @Test(groups = "orm") public void shouldUpdate() { - repo.update("444", "123", "Foo", NotificationMethodType.EMAIL, "abc"); + repo.update("444", "123", "Foo", NotificationMethodType.EMAIL, "abc", 0); NotificationMethod nm = repo.findById("444", "123"); - assertEquals(nm, new NotificationMethod("123", "Foo", NotificationMethodType.EMAIL, "abc")); + assertEquals(nm, new NotificationMethod("123", "Foo", NotificationMethodType.EMAIL, "abc", 0)); } @Test(groups = "orm") public void shouldUpdateReturnValue() { - NotificationMethod nm = repo.update("444", "123", "Foo", NotificationMethodType.EMAIL, "abc"); + NotificationMethod nm = repo.update("444", "123", "Foo", NotificationMethodType.EMAIL, "abc", 0); NotificationMethod foundNotificationMethod = repo.findById("444", "123"); assertEquals(nm, foundNotificationMethod); @@ -144,16 +145,16 @@ public class NotificationMethodSqlRepositoryImplTest { @Test(groups = "orm") public void shouldUpdateDuplicateWithSameValues() { - repo.update("444", "123", "Foo", NotificationMethodType.EMAIL, "abc"); + repo.update("444", "123", "Foo", NotificationMethodType.EMAIL, "abc", 0); NotificationMethod nm = repo.findById("444", "123"); - assertEquals(nm, new NotificationMethod("123", "Foo", NotificationMethodType.EMAIL, "abc")); + assertEquals(nm, new NotificationMethod("123", "Foo", NotificationMethodType.EMAIL, "abc", 0)); } @Test(groups = "orm", expectedExceptions = EntityExistsException.class) public void shouldNotUpdateDuplicateWithSameName() { - repo.update("444", "124", "MyEmail", NotificationMethodType.EMAIL, "abc"); + repo.update("444", "124", "MyEmail", NotificationMethodType.EMAIL, "abc", 0); } @Test(groups = "orm", expectedExceptions = WebApplicationException.class) diff --git a/java/src/test/java/monasca/api/infrastructure/persistence/mysql/NotificationMethodMySqlRepositoryImplTest.java b/java/src/test/java/monasca/api/infrastructure/persistence/mysql/NotificationMethodMySqlRepositoryImplTest.java index d704c00b4..bbc9752f5 100644 --- a/java/src/test/java/monasca/api/infrastructure/persistence/mysql/NotificationMethodMySqlRepositoryImplTest.java +++ b/java/src/test/java/monasca/api/infrastructure/persistence/mysql/NotificationMethodMySqlRepositoryImplTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Hewlett-Packard Development Company, L.P. + * (C) Copyright 2014-2016 Hewlett Packard Enterprise Development Company LP * * Licensed 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 @@ -67,8 +67,15 @@ public class NotificationMethodMySqlRepositoryImplTest { .execute("insert into notification_method (id, tenant_id, name, type, address, created_at, updated_at) values ('124', '444', 'OtherEmail', 'EMAIL', 'a@b', NOW(), NOW())"); } - public void shouldCreate() { - NotificationMethod nmA = repo.create("555", "MyEmail", NotificationMethodType.EMAIL, "a@b"); + public void shouldCreateEmail() { + NotificationMethod nmA = repo.create("555", "MyEmail", NotificationMethodType.EMAIL, "a@b", 0); + NotificationMethod nmB = repo.findById("555", nmA.getId()); + + assertEquals(nmA, nmB); + } + + public void shouldCreateWebhookNonZeroPeriod() { + NotificationMethod nmA = repo.create("555", "MyWebhook", NotificationMethodType.WEBHOOK, "http://localhost:33", 60); NotificationMethod nmB = repo.findById("555", nmA.getId()); assertEquals(nmA, nmB); @@ -92,15 +99,23 @@ public class NotificationMethodMySqlRepositoryImplTest { List nms = repo.find("444", null, null, 1); assertEquals(nms, Arrays.asList(new NotificationMethod("123", "MyEmail", - NotificationMethodType.EMAIL, "a@b"),new NotificationMethod("124", "OtherEmail", - NotificationMethodType.EMAIL, "a@b"))); + NotificationMethodType.EMAIL, "a@b", 0),new NotificationMethod("124", "OtherEmail", + NotificationMethodType.EMAIL, "a@b", 0))); } public void shouldUpdate() { - repo.update("444", "123", "Foo", NotificationMethodType.EMAIL, "abc"); + repo.update("444", "123", "Foo", NotificationMethodType.EMAIL, "abc", 0); NotificationMethod nm = repo.findById("444", "123"); - assertEquals(nm, new NotificationMethod("123", "Foo", NotificationMethodType.EMAIL, "abc")); + assertEquals(nm, new NotificationMethod("123", "Foo", NotificationMethodType.EMAIL, "abc", 0)); + } + + public void shouldUpdateWebhookWithNonZeroPeriod() { + NotificationMethod nmOriginal = repo.create("555", "MyWebhook", NotificationMethodType.WEBHOOK, "http://localhost:33", 0); + repo.update("555", nmOriginal.getId(), "MyWebhook", NotificationMethodType.WEBHOOK, "http://localhost:33", 60); + NotificationMethod nmUpdated = repo.findById("555", nmOriginal.getId()); + + assertEquals(nmUpdated.getPeriod(), 60); } public void shouldDeleteById() { @@ -114,16 +129,16 @@ public class NotificationMethodMySqlRepositoryImplTest { } public void shouldUpdateDuplicateWithSameValues() { - repo.update("444", "123", "Foo", NotificationMethodType.EMAIL, "abc"); + repo.update("444", "123", "Foo", NotificationMethodType.EMAIL, "abc", 0); NotificationMethod nm = repo.findById("444", "123"); - assertEquals(nm, new NotificationMethod("123", "Foo", NotificationMethodType.EMAIL, "abc")); + assertEquals(nm, new NotificationMethod("123", "Foo", NotificationMethodType.EMAIL, "abc", 0)); } @Test(expectedExceptions = EntityExistsException.class) public void shouldNotUpdateDuplicateWithSameName() { - repo.update("444", "124", "MyEmail", NotificationMethodType.EMAIL, "abc"); + repo.update("444", "124", "MyEmail", NotificationMethodType.EMAIL, "abc", 0); } } diff --git a/java/src/test/java/monasca/api/integration/NotificationMethodIntegrationTest.java b/java/src/test/java/monasca/api/integration/NotificationMethodIntegrationTest.java index 8f96e9475..256e38b1e 100644 --- a/java/src/test/java/monasca/api/integration/NotificationMethodIntegrationTest.java +++ b/java/src/test/java/monasca/api/integration/NotificationMethodIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Hewlett-Packard Development Company, L.P. + * (C) Copyright 2014-2016 Hewlett Packard Enterprise Development Company LP * * Licensed 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 @@ -50,6 +50,7 @@ public class NotificationMethodIntegrationTest extends AbstractMonApiResourceTes private DBI db; private NotificationMethod notificationMethod; private NotificationMethodRepo repo; + private ApiConfig config; @Override protected void setupResources() throws Exception { @@ -59,9 +60,8 @@ public class NotificationMethodIntegrationTest extends AbstractMonApiResourceTes handle .execute("insert into notification_method (id, tenant_id, name, type, address, created_at, updated_at) values ('29387234', 'notification-method-test', 'MyEmaila', 'EMAIL', 'a@b', NOW(), NOW())"); db.close(handle); - repo = new NotificationMethodMySqlRepoImpl(db, new PersistUtils()); - addResources(new NotificationMethodResource(repo, new PersistUtils())); + addResources(new NotificationMethodResource(config, repo, new PersistUtils())); } @BeforeTest @@ -77,7 +77,7 @@ public class NotificationMethodIntegrationTest extends AbstractMonApiResourceTes // Fixtures notificationMethod = - new NotificationMethod("123", "Joe's Email", NotificationMethodType.EMAIL, "a@b"); + new NotificationMethod("123", "Joe's Email", NotificationMethodType.EMAIL, "a@b", 0); } public void shouldCreate() throws Exception { @@ -89,7 +89,7 @@ public class NotificationMethodIntegrationTest extends AbstractMonApiResourceTes .post( ClientResponse.class, new CreateNotificationMethodCommand(notificationMethod.getName(), - notificationMethod.getType(), notificationMethod.getAddress())); + notificationMethod.getType(), notificationMethod.getAddress(), "0")); NotificationMethod newNotificationMethod = response.getEntity(NotificationMethod.class); String location = response.getHeaders().get("Location").get(0); @@ -107,7 +107,7 @@ public class NotificationMethodIntegrationTest extends AbstractMonApiResourceTes .header("X-Tenant-Id", TENANT_ID) .type(MediaType.APPLICATION_JSON) .post(ClientResponse.class, - new CreateNotificationMethodCommand("MyEmail", NotificationMethodType.EMAIL, "a@b")); + new CreateNotificationMethodCommand("MyEmail", NotificationMethodType.EMAIL, "a@b", "0")); assertEquals(response.getStatus(), 409); } @@ -115,7 +115,7 @@ public class NotificationMethodIntegrationTest extends AbstractMonApiResourceTes public void shouldDelete() { NotificationMethod newMethod = repo.create(TENANT_ID, notificationMethod.getName(), notificationMethod.getType(), - notificationMethod.getAddress()); + notificationMethod.getAddress(), 0); assertNotNull(repo.findById(TENANT_ID, newMethod.getId())); ClientResponse response = diff --git a/java/src/test/java/monasca/api/resource/NotificationMethodResourceTest.java b/java/src/test/java/monasca/api/resource/NotificationMethodResourceTest.java index a2d9882e0..1030eeecf 100644 --- a/java/src/test/java/monasca/api/resource/NotificationMethodResourceTest.java +++ b/java/src/test/java/monasca/api/resource/NotificationMethodResourceTest.java @@ -1,6 +1,6 @@ /* - * Copyright (c) 2014 Hewlett-Packard Development Company, L.P. - * + * (C) Copyright 2014-2016 Hewlett Packard Enterprise Development Company LP + * * Licensed 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 * @@ -16,6 +16,8 @@ package monasca.api.resource; import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.GenericType; + +import monasca.api.ApiConfig; import monasca.api.app.command.CreateNotificationMethodCommand; import monasca.api.domain.exception.EntityNotFoundException; import monasca.api.domain.model.common.Paged; @@ -28,11 +30,13 @@ import monasca.api.resource.exception.ErrorMessages; import org.testng.annotations.Test; import javax.ws.rs.core.MediaType; + import java.util.Arrays; import java.util.List; import java.util.Map; import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.*; @@ -42,29 +46,32 @@ import static org.testng.Assert.*; public class NotificationMethodResourceTest extends AbstractMonApiResourceTest { private NotificationMethod notificationMethod, notificationMethodWebhook, notificationMethodPagerduty; private NotificationMethodRepo repo; + private ApiConfig config; @Override protected void setupResources() throws Exception { super.setupResources(); notificationMethod = - new NotificationMethod("123", "Joe's Email", NotificationMethodType.EMAIL, "a@b"); + new NotificationMethod("123", "Joe's Email", NotificationMethodType.EMAIL, "a@b", 0); notificationMethodWebhook = - new NotificationMethod("1234", "MyWh", NotificationMethodType.WEBHOOK, "http://localhost"); + new NotificationMethod("1234", "MyWh", NotificationMethodType.WEBHOOK, "http://localhost", 60); notificationMethodPagerduty = - new NotificationMethod("12345", "MyPd", NotificationMethodType.PAGERDUTY, "nzH2LVRdMzun11HNC2oD"); + new NotificationMethod("12345", "MyPd", NotificationMethodType.PAGERDUTY, "nzH2LVRdMzun11HNC2oD", 0); repo = mock(NotificationMethodRepo.class); - when(repo.create(eq("abc"), eq("MyEmail"), eq(NotificationMethodType.EMAIL), anyString())) + when(repo.create(eq("abc"), eq("MyEmail"), eq(NotificationMethodType.EMAIL), anyString(), eq(0))) .thenReturn(notificationMethod); - when(repo.create(eq("abc"), eq("MyWh"), eq(NotificationMethodType.WEBHOOK), anyString())) + when(repo.create(eq("abc"), eq("MyWh"), eq(NotificationMethodType.WEBHOOK), anyString(), anyInt())) .thenReturn(notificationMethodWebhook); - when(repo.create(eq("abc"), eq("MyPd"), eq(NotificationMethodType.PAGERDUTY), anyString())) + when(repo.create(eq("abc"), eq("MyPd"), eq(NotificationMethodType.PAGERDUTY), anyString(), eq(0))) .thenReturn(notificationMethodPagerduty); when(repo.findById(eq("abc"), eq("123"))).thenReturn(notificationMethod); when(repo.find(eq("abc"), (List) anyList(), anyString(), anyInt())) .thenReturn(Arrays.asList(notificationMethod)); - addResources(new NotificationMethodResource(repo, new PersistUtils())); + config = mock(ApiConfig.class); + config.validNotificationPeriods = Arrays.asList(0, 60); + addResources(new NotificationMethodResource(config, repo, new PersistUtils())); } public void shouldCreate() { @@ -74,31 +81,31 @@ public class NotificationMethodResourceTest extends AbstractMonApiResourceTest { .header("X-Tenant-Id", "abc") .header("Content-Type", MediaType.APPLICATION_JSON) .post(ClientResponse.class, - new CreateNotificationMethodCommand("MyEmail", NotificationMethodType.EMAIL, "a@a.com")); + new CreateNotificationMethodCommand("MyEmail", NotificationMethodType.EMAIL, "a@a.com", "0")); NotificationMethod newNotificationMethod = response.getEntity(NotificationMethod.class); String location = response.getHeaders().get("Location").get(0); assertEquals(response.getStatus(), 201); assertEquals(location, "/v2.0/notification-methods/" + newNotificationMethod.getId()); assertEquals(newNotificationMethod, notificationMethod); - verify(repo).create(eq("abc"), eq("MyEmail"), eq(NotificationMethodType.EMAIL), anyString()); + verify(repo).create(eq("abc"), eq("MyEmail"), eq(NotificationMethodType.EMAIL), anyString(), eq(0)); } public void shouldUpdate() { when( repo.update(eq("abc"), anyString(), anyString(), any(NotificationMethodType.class), - anyString())).thenReturn(notificationMethod); + anyString(), eq(0))).thenReturn(notificationMethod); ClientResponse response = client() .resource("/v2.0/notification-methods/123") .header("X-Tenant-Id", "abc") .header("Content-Type", MediaType.APPLICATION_JSON) .put(ClientResponse.class, - new CreateNotificationMethodCommand("Foo", NotificationMethodType.EMAIL, "a@a.com")); + new CreateNotificationMethodCommand("Foo", NotificationMethodType.EMAIL, "a@a.com", "0")); assertEquals(response.getStatus(), 200); verify(repo).update(eq("abc"), eq("123"), eq("Foo"), eq(NotificationMethodType.EMAIL), - eq("a@a.com")); + eq("a@a.com"), eq(0)); } public void should422OnBadEnum() { @@ -108,7 +115,7 @@ public class NotificationMethodResourceTest extends AbstractMonApiResourceTest { .header("X-Tenant-Id", "abc") .header("Content-Type", MediaType.APPLICATION_JSON) .post(ClientResponse.class, - new CreateNotificationMethodCommand("MyEmail", null, "a@b")); + new CreateNotificationMethodCommand("MyEmail", null, "a@b", "0")); String e = response.getEntity(String.class); ErrorMessages.assertThat(e).matches("unprocessable_entity", 422, @@ -122,7 +129,7 @@ public class NotificationMethodResourceTest extends AbstractMonApiResourceTest { .header("X-Tenant-Id", "abc") .header("Content-Type", MediaType.APPLICATION_JSON) .post(ClientResponse.class, - new CreateNotificationMethodCommand("MyEmail", NotificationMethodType.EMAIL, "a@")); + new CreateNotificationMethodCommand("MyEmail", NotificationMethodType.EMAIL, "a@", "0")); ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422, "Address a@ is not of correct format"); @@ -135,7 +142,7 @@ public class NotificationMethodResourceTest extends AbstractMonApiResourceTest { .header("X-Tenant-Id", "abc") .header("Content-Type", MediaType.APPLICATION_JSON) .post(ClientResponse.class, - new CreateNotificationMethodCommand("MyEmail", NotificationMethodType.EMAIL, "a@f ,")); + new CreateNotificationMethodCommand("MyEmail", NotificationMethodType.EMAIL, "a@f ,", "0")); ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422, "Address a@f , is not of correct format"); @@ -148,7 +155,7 @@ public class NotificationMethodResourceTest extends AbstractMonApiResourceTest { .header("X-Tenant-Id", "abc") .header("Content-Type", MediaType.APPLICATION_JSON) .post(ClientResponse.class, - new CreateNotificationMethodCommand("MyEmail", NotificationMethodType.EMAIL, "")); + new CreateNotificationMethodCommand("MyEmail", NotificationMethodType.EMAIL, "", "0")); ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422, "[address may not be empty (was )"); @@ -166,7 +173,7 @@ public class NotificationMethodResourceTest extends AbstractMonApiResourceTest { "01234567889012345678890123456788901234567889012345678890123456788901234567889012345678890123456788901234567889" + "01234567889012345678890123456788901234567889012345678890123456788901234567889012345678890123456788901234567889" + "01234567889012345678890123456788901234567889012345678890123456788901234567889012345678890123456788901234567889", - NotificationMethodType.EMAIL, "a@b")); + NotificationMethodType.EMAIL, "a@b", "0")); ErrorMessages.assertThat(response.getEntity(String.class)).matches("unprocessable_entity", 422, "[name size must be between 1 and 250"); @@ -194,13 +201,69 @@ public class NotificationMethodResourceTest extends AbstractMonApiResourceTest { + "12345678901234567890123456789012345678901234567890" + "12345678901234567890123456789012345678901234567890" + "12345678901234567890123456789012345678901234567890" - + "123456789012345678901234567890")); + + "123456789012345678901234567890", "0")); String e = response.getEntity(String.class); ErrorMessages.assertThat(e).matches("unprocessable_entity", 422, "[address size must be between 1 and 512"); } + public void should422OnNonIntPeriod() { + ClientResponse response = + client() + .resource("/v2.0/notification-methods") + .header("X-Tenant-Id", "abc") + .header("Content-Type", MediaType.APPLICATION_JSON) + .post(ClientResponse.class, + new CreateNotificationMethodCommand("MyEmail", NotificationMethodType.EMAIL, "a@a.com", "not a int")); + + String e = response.getEntity(String.class); + ErrorMessages.assertThat(e).matches("unprocessable_entity", 422, + "period (not a int) must be valid number"); + } + + public void should422OnNonZeroPeriodForEmail() { + ClientResponse response = + client() + .resource("/v2.0/notification-methods") + .header("X-Tenant-Id", "abc") + .header("Content-Type", MediaType.APPLICATION_JSON) + .post(ClientResponse.class, + new CreateNotificationMethodCommand("MyEmail", NotificationMethodType.EMAIL, "a@a.com", "60")); + + String e = response.getEntity(String.class); + ErrorMessages.assertThat(e).matches("unprocessable_entity", 422, + "Period can not be non zero for Email"); + } + + public void should422OnNonZeroPeriodForPagerduty() { + ClientResponse response = + client().resource("/v2.0/notification-methods") + .header("X-Tenant-Id", "abc") + .header("Content-Type", MediaType.APPLICATION_JSON) + .post(ClientResponse.class, + new CreateNotificationMethodCommand("MyPd", NotificationMethodType.PAGERDUTY, + "http://localhost", "60")); + + String e = response.getEntity(String.class); + ErrorMessages.assertThat(e).matches("unprocessable_entity", 422, + "Period can not be non zero for Pagerduty"); + } + + public void should422OnInvalidPeriodForWebhook() { + ClientResponse response = + client().resource("/v2.0/notification-methods") + .header("X-Tenant-Id", "abc") + .header("Content-Type", MediaType.APPLICATION_JSON) + .post(ClientResponse.class, + new CreateNotificationMethodCommand("MyWh", NotificationMethodType.WEBHOOK, + "http://localhost", "5")); + + String e = response.getEntity(String.class); + ErrorMessages.assertThat(e).matches("unprocessable_entity", 422, + "5 is not a valid period"); + } + public void shouldList() { @@ -213,7 +276,7 @@ public class NotificationMethodResourceTest extends AbstractMonApiResourceTest { nm = new NotificationMethod((String) lhm.get("id"), (String) lhm.get("name"), NotificationMethodType.fromJson((String) lhm.get("type")), - (String) lhm.get("address")); + (String) lhm.get("address"), 0); List notificationMethods = Arrays.asList(nm); assertEquals(notificationMethods, Arrays.asList(notificationMethod)); @@ -278,7 +341,7 @@ public class NotificationMethodResourceTest extends AbstractMonApiResourceTest { .header("X-Tenant-Id", "abc") .header("Content-Type", MediaType.APPLICATION_JSON) .post(NotificationMethod.class, - new CreateNotificationMethodCommand(null, null, "8675309")); + new CreateNotificationMethodCommand(null, null, "8675309", "0")); fail(); } catch (Exception e) { assertTrue(e.getMessage().contains("422")); @@ -301,7 +364,7 @@ public class NotificationMethodResourceTest extends AbstractMonApiResourceTest { .header("Content-Type", MediaType.APPLICATION_JSON) .post(ClientResponse.class, new CreateNotificationMethodCommand("MyWh", NotificationMethodType.WEBHOOK, - "http://localhost")); + "http://localhost", "0")); NotificationMethod newNotificationMethod = response.getEntity(NotificationMethod.class); String location = response.getHeaders().get("Location").get(0); @@ -309,7 +372,25 @@ public class NotificationMethodResourceTest extends AbstractMonApiResourceTest { assertEquals(response.getStatus(), 201); assertEquals(location, "/v2.0/notification-methods/" + newNotificationMethod.getId()); assertEquals(newNotificationMethod, notificationMethodWebhook); - verify(repo).create(eq("abc"), eq("MyWh"), eq(NotificationMethodType.WEBHOOK), anyString()); + verify(repo).create(eq("abc"), eq("MyWh"), eq(NotificationMethodType.WEBHOOK), anyString(), eq(0)); + } + + public void shouldCreateWebhookNotificationWithNonZeroPeriod() { + ClientResponse response = + client().resource("/v2.0/notification-methods") + .header("X-Tenant-Id", "abc") + .header("Content-Type", MediaType.APPLICATION_JSON) + .post(ClientResponse.class, + new CreateNotificationMethodCommand("MyWh", NotificationMethodType.WEBHOOK, + "http://localhost", "60")); + + NotificationMethod newNotificationMethod = response.getEntity(NotificationMethod.class); + String location = response.getHeaders().get("Location").get(0); + + assertEquals(response.getStatus(), 201); + assertEquals(location, "/v2.0/notification-methods/" + newNotificationMethod.getId()); + assertEquals(newNotificationMethod, notificationMethodWebhook); + verify(repo).create(eq("abc"), eq("MyWh"), eq(NotificationMethodType.WEBHOOK), anyString(), eq(60)); } public void shouldCreatePagerdutyNotification() { @@ -319,7 +400,7 @@ public class NotificationMethodResourceTest extends AbstractMonApiResourceTest { .header("Content-Type", MediaType.APPLICATION_JSON) .post(ClientResponse.class, new CreateNotificationMethodCommand("MyPd", NotificationMethodType.PAGERDUTY, - "http://localhost")); + "http://localhost", "0")); NotificationMethod newNotificationMethod = response.getEntity(NotificationMethod.class); String location = response.getHeaders().get("Location").get(0); @@ -327,6 +408,6 @@ public class NotificationMethodResourceTest extends AbstractMonApiResourceTest { assertEquals(response.getStatus(), 201); assertEquals(location, "/v2.0/notification-methods/" + newNotificationMethod.getId()); assertEquals(newNotificationMethod, notificationMethodPagerduty); - verify(repo).create(eq("abc"), eq("MyPd"), eq(NotificationMethodType.PAGERDUTY), anyString()); + verify(repo).create(eq("abc"), eq("MyPd"), eq(NotificationMethodType.PAGERDUTY), anyString(), eq(0)); } } diff --git a/java/src/test/resources/fixtures/newNotificationMethodWithPeriod.json b/java/src/test/resources/fixtures/newNotificationMethodWithPeriod.json new file mode 100644 index 000000000..51ead08e0 --- /dev/null +++ b/java/src/test/resources/fixtures/newNotificationMethodWithPeriod.json @@ -0,0 +1 @@ +{"name":"MyWebhook","type":"webhook","address":"http://somedomain.com","period":"60"} \ No newline at end of file diff --git a/java/src/test/resources/fixtures/notificationMethod.json b/java/src/test/resources/fixtures/notificationMethod.json index 5bc983088..546041352 100644 --- a/java/src/test/resources/fixtures/notificationMethod.json +++ b/java/src/test/resources/fixtures/notificationMethod.json @@ -1 +1 @@ -{"id":"123","links":[{"rel":"self","href":"https://cloudsvc.example.com/v1.0"}],"name":"MyEmail","type":"EMAIL","address":"a@b"} +{"id":"123","links":[{"rel":"self","href":"https://cloudsvc.example.com/v1.0"}],"name":"MyEmail","type":"EMAIL","address":"a@b", "period":0} diff --git a/java/src/test/resources/monasca-api-config.yml b/java/src/test/resources/monasca-api-config.yml index 5e4a079c7..a8ed87618 100644 --- a/java/src/test/resources/monasca-api-config.yml +++ b/java/src/test/resources/monasca-api-config.yml @@ -10,6 +10,9 @@ metricsTopic: metrics # Topic for publishing domain events to eventsTopic: events +validNotificationPeriods: + - 60 + databaseConfiguration: # vertica | influxdb #databaseType: influxdb diff --git a/java/src/test/resources/monasca/api/infrastructure/persistence/mysql/notification_method.sql b/java/src/test/resources/monasca/api/infrastructure/persistence/mysql/notification_method.sql index c2da2eeba..54f7ceee3 100644 --- a/java/src/test/resources/monasca/api/infrastructure/persistence/mysql/notification_method.sql +++ b/java/src/test/resources/monasca/api/infrastructure/persistence/mysql/notification_method.sql @@ -4,6 +4,7 @@ CREATE TABLE `notification_method` ( `name` varchar(250) NOT NULL DEFAULT '', `type` varchar(10) NOT NULL DEFAULT 'EMAIL' check type in ('EMAIL', 'WEBHOOK', 'PAGERDUTY'), `address` varchar(100) NOT NULL DEFAULT '', + `period` int NOT NULL DEFAULT 0, `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, PRIMARY KEY (`id`) diff --git a/monasca_api/common/repositories/mysql/notifications_repository.py b/monasca_api/common/repositories/mysql/notifications_repository.py index 8014b1e2b..0f21d574f 100644 --- a/monasca_api/common/repositories/mysql/notifications_repository.py +++ b/monasca_api/common/repositories/mysql/notifications_repository.py @@ -1,4 +1,4 @@ -# Copyright 2014 Hewlett-Packard +# (C) Copyright 2014-2016 Hewlett Packard Enterprise Development Company LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain @@ -31,7 +31,7 @@ class NotificationsRepository(mysql_repository.MySQLRepository, super(NotificationsRepository, self).__init__() def create_notification(self, tenant_id, name, - notification_type, address): + notification_type, address, period): cnxn, cursor = self._get_cnxn_cursor_tuple() @@ -58,15 +58,17 @@ class NotificationsRepository(mysql_repository.MySQLRepository, name, type, address, + period, created_at, updated_at - ) values (%s, %s, %s, % s, %s, %s, %s)""" + ) values (%s, %s, %s, %s, %s, %s, %s, %s)""" parms = [notification_id, tenant_id, name.encode('utf8'), notification_type.encode('utf8'), address.encode('utf8'), + period, now, now] @@ -166,7 +168,7 @@ class NotificationsRepository(mysql_repository.MySQLRepository, @mysql_repository.mysql_try_catch_block def update_notification( - self, id, tenant_id, name, type, address): + self, id, tenant_id, name, type, address, period): cnxn, cursor = self._get_cnxn_cursor_tuple() @@ -178,11 +180,12 @@ class NotificationsRepository(mysql_repository.MySQLRepository, set name = %s, type = %s, address = %s, + period = %s, updated_at = %s where tenant_id = %s and id = %s""" parms = [name.encode('utf8'), type.encode('utf8'), address.encode( - 'utf8'), now, tenant_id, id] + 'utf8'), period, now, tenant_id, id] cursor.execute(query, parms) diff --git a/monasca_api/common/repositories/notifications_repository.py b/monasca_api/common/repositories/notifications_repository.py index 6f61d1e68..1e9bbe9e5 100644 --- a/monasca_api/common/repositories/notifications_repository.py +++ b/monasca_api/common/repositories/notifications_repository.py @@ -1,4 +1,4 @@ -# Copyright 2014 Hewlett-Packard +# (C) Copyright 2014-2016 Hewlett Packard Enterprise Development Company LP # Copyright 2016 FUJITSU LIMITED # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -23,7 +23,7 @@ class NotificationsRepository(object): @abc.abstractmethod def create_notification(self, tenant_id, name, notification_type, - address): + address, period): return @abc.abstractmethod @@ -40,5 +40,5 @@ class NotificationsRepository(object): @abc.abstractmethod def update_notification(self, notification_id, tenant_id, name, notification_type, - address): + address, period): return diff --git a/monasca_api/common/repositories/sqla/models.py b/monasca_api/common/repositories/sqla/models.py index 5aa94e166..521457e6f 100644 --- a/monasca_api/common/repositories/sqla/models.py +++ b/monasca_api/common/repositories/sqla/models.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # Copyright 2015 Robin Hood # Copyright 2016 FUJITSU LIMITED +# (C) Copyright 2016 Hewlett Packard Enterprise Development Company LP # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -74,6 +75,7 @@ def create_nm_model(metadata=None): Column('name', String(250)), Column('type', String(20)), Column('address', String(512)), + Column('period', Integer), Column('created_at', DateTime), Column('updated_at', DateTime)) diff --git a/monasca_api/common/repositories/sqla/notifications_repository.py b/monasca_api/common/repositories/sqla/notifications_repository.py index 05c03a622..bd69e9d0b 100644 --- a/monasca_api/common/repositories/sqla/notifications_repository.py +++ b/monasca_api/common/repositories/sqla/notifications_repository.py @@ -1,4 +1,4 @@ -# Copyright 2014 Hewlett-Packard +# (C) Copyright 2014,2016 Hewlett Packard Enterprise Development Company LP # Copyright 2016 FUJITSU LIMITED # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -58,6 +58,7 @@ class NotificationsRepository(sql_repository.SQLRepository, name=bindparam('b_name'), type=bindparam('b_type'), address=bindparam('b_address'), + period=bindparam('b_period'), created_at=bindparam('b_created_at'), updated_at=bindparam('b_updated_at'))) @@ -72,6 +73,7 @@ class NotificationsRepository(sql_repository.SQLRepository, name=bindparam('b_name'), type=bindparam('b_type'), address=bindparam('b_address'), + period=bindparam('b_period'), updated_at=bindparam('b_updated_at'))) self._select_nm_id_query = (select([nm]) @@ -85,7 +87,7 @@ class NotificationsRepository(sql_repository.SQLRepository, nm.c.name == bindparam('b_name')))) def create_notification(self, tenant_id, name, - notification_type, address): + notification_type, address, period): with self._db_engine.connect() as conn: row = conn.execute(self._select_nm_count_name_query, @@ -105,6 +107,7 @@ class NotificationsRepository(sql_repository.SQLRepository, b_name=name.encode('utf8'), b_type=notification_type.encode('utf8'), b_address=address.encode('utf8'), + b_period=period, b_created_at=now, b_updated_at=now) @@ -186,7 +189,7 @@ class NotificationsRepository(sql_repository.SQLRepository, b_name=name.encode('utf8')).fetchone() @sql_repository.sql_try_catch_block - def update_notification(self, notification_id, tenant_id, name, notification_type, address): + def update_notification(self, notification_id, tenant_id, name, notification_type, address, period): with self._db_engine.connect() as conn: now = datetime.datetime.utcnow() @@ -196,6 +199,7 @@ class NotificationsRepository(sql_repository.SQLRepository, b_name=name.encode('utf8'), b_type=notification_type.encode('utf8'), b_address=address.encode('utf8'), + b_period=period, b_updated_at=now) if cursor.rowcount < 1: diff --git a/monasca_api/tests/sqlite_alarm.sql b/monasca_api/tests/sqlite_alarm.sql index 2f3291e27..73cad0285 100644 --- a/monasca_api/tests/sqlite_alarm.sql +++ b/monasca_api/tests/sqlite_alarm.sql @@ -19,6 +19,7 @@ CREATE TABLE `notification_method` ( `name` varchar(250) DEFAULT NULL, `type` varchar(20) NOT NULL, `address` varchar(512) DEFAULT NULL, + `period` int NOT NULL DEFAULT 0, `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL, PRIMARY KEY (`id`) diff --git a/monasca_api/tests/test_nm_repository.py b/monasca_api/tests/test_nm_repository.py index bde3deaf9..1b3b10724 100644 --- a/monasca_api/tests/test_nm_repository.py +++ b/monasca_api/tests/test_nm_repository.py @@ -1,5 +1,6 @@ # Copyright 2015 Cray # Copyright 2016 FUJITSU LIMITED +# (C) Copyright 2016 Hewlett Packard Enterprise Development Company LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain @@ -57,6 +58,7 @@ class TestNotificationMethodRepoDB(testtools.TestCase, fixtures.TestWithFixtures name=bindparam('name'), type=bindparam('type'), address=bindparam('address'), + period=bindparam('period'), created_at=bindparam('created_at'), updated_at=bindparam('updated_at'))) @@ -80,6 +82,7 @@ class TestNotificationMethodRepoDB(testtools.TestCase, fixtures.TestWithFixtures 'name': 'MyEmail', 'type': 'EMAIL', 'address': 'a@b', + 'period': 0, 'created_at': datetime.datetime.now(), 'updated_at': datetime.datetime.now()}, {'id': '124', @@ -87,6 +90,7 @@ class TestNotificationMethodRepoDB(testtools.TestCase, fixtures.TestWithFixtures 'name': 'OtherEmail', 'type': 'EMAIL', 'address': 'a@b', + 'period': 0, 'created_at': datetime.datetime.now(), 'updated_at': datetime.datetime.now()}] @@ -125,7 +129,8 @@ class TestNotificationMethodRepoDB(testtools.TestCase, fixtures.TestWithFixtures nmA = self.repo.create_notification('555', 'MyEmail', 'EMAIL', - 'a@b') + 'a@b', + 0) nmB = self.repo.list_notification('555', nmA) self.assertEqual(nmA, nmB['id']) @@ -135,7 +140,8 @@ class TestNotificationMethodRepoDB(testtools.TestCase, fixtures.TestWithFixtures '555', 'MyEmail', 'EMAIL', - 'a@b') + 'a@b', + 0) def test_should_exists(self): from monasca_api.common.repositories import exceptions @@ -159,7 +165,7 @@ class TestNotificationMethodRepoDB(testtools.TestCase, fixtures.TestWithFixtures def test_update(self): import copy - self.repo.update_notification('123', '444', 'Foo', 'EMAIL', 'abc') + self.repo.update_notification('123', '444', 'Foo', 'EMAIL', 'abc', 0) nm = self.repo.list_notification('444', '123') new_nm = copy.deepcopy(self.default_nms[0]) new_nm['name'] = 'Foo' @@ -175,7 +181,8 @@ class TestNotificationMethodRepoDB(testtools.TestCase, fixtures.TestWithFixtures 'no really tenant', '', '', - '') + '', + 0) def test_should_delete(self): from monasca_api.common.repositories import exceptions @@ -187,8 +194,8 @@ class TestNotificationMethodRepoDB(testtools.TestCase, fixtures.TestWithFixtures def test_should_update_duplicate_with_same_values(self): import copy - self.repo.update_notification('123', '444', 'Foo', 'EMAIL', 'abc') - self.repo.update_notification('123', '444', 'Foo', 'EMAIL', 'abc') + self.repo.update_notification('123', '444', 'Foo', 'EMAIL', 'abc', 0) + self.repo.update_notification('123', '444', 'Foo', 'EMAIL', 'abc', 0) nm = self.repo.list_notification('444', '123') new_nm = copy.deepcopy(self.default_nms[0]) new_nm['name'] = 'Foo' diff --git a/monasca_api/tests/test_validation.py b/monasca_api/tests/test_validation.py index 8182538e7..5e492f61f 100644 --- a/monasca_api/tests/test_validation.py +++ b/monasca_api/tests/test_validation.py @@ -245,59 +245,92 @@ class TestConvertTimeString(unittest.TestCase): ValueError, helpers._convert_time_string, date_time_string) +valid_periods = [0, 60] + class TestNotificationValidation(unittest.TestCase): def test_validation_for_email(self): notification = {"name": "MyEmail", "type": "EMAIL", "address": "name@domain.com"} try: - schemas_notifications.validate(notification) + schemas_notifications.parse_and_validate(notification, valid_periods) except schemas_exceptions.ValidationException: self.fail("shouldn't happen") - def test_validation_exception_for_email(self): + def test_validation_exception_for_invalid_email_address(self): notification = {"name": "MyEmail", "type": "EMAIL", "address": "name@domain."} with self.assertRaises(schemas_exceptions.ValidationException) as ve: - schemas_notifications.validate(notification) + schemas_notifications.parse_and_validate(notification, valid_periods) ex = ve.exception self.assertEqual("Address name@domain. is not of correct format", ex.message) + def test_validation_exception_for_invalid_period_for_email(self): + notification = {"name": "MyEmail", "type": "EMAIL", "address": "name@domain.com", "period": "60"} + with self.assertRaises(schemas_exceptions.ValidationException) as ve: + schemas_notifications.parse_and_validate(notification, valid_periods) + ex = ve.exception + self.assertEqual("Period can only be set with webhooks", ex.message) + def test_validation_for_webhook(self): notification = {"name": "MyWebhook", "type": "WEBHOOK", "address": "http://somedomain.com"} try: - schemas_notifications.validate(notification) + schemas_notifications.parse_and_validate(notification, valid_periods) + except schemas_exceptions.ValidationException: + self.fail("shouldn't happen") + + def test_validation_for_webhook_non_zero_period(self): + notification = {"name": "MyWebhook", "type": "WEBHOOK", "address": "http://somedomain.com", + "period": 60} + try: + schemas_notifications.parse_and_validate(notification, valid_periods) except schemas_exceptions.ValidationException: self.fail("shouldn't happen") def test_validation_exception_for_webhook_no_scheme(self): notification = {"name": "MyWebhook", "type": "WEBHOOK", "address": "//somedomain.com"} with self.assertRaises(schemas_exceptions.ValidationException) as ve: - schemas_notifications.validate(notification) + schemas_notifications.parse_and_validate(notification, valid_periods) ex = ve.exception self.assertEqual("Address //somedomain.com does not have URL scheme", ex.message) def test_validation_exception_for_webhook_no_netloc(self): notification = {"name": "MyWebhook", "type": "WEBHOOK", "address": "http://"} with self.assertRaises(schemas_exceptions.ValidationException) as ve: - schemas_notifications.validate(notification) + schemas_notifications.parse_and_validate(notification, valid_periods) ex = ve.exception self.assertEqual("Address http:// does not have network location", ex.message) def test_validation_exception_for_webhook_invalid_scheme(self): notification = {"name": "MyWebhook", "type": "WEBHOOK", "address": "ftp://somedomain.com"} with self.assertRaises(schemas_exceptions.ValidationException) as ve: - schemas_notifications.validate(notification) + schemas_notifications.parse_and_validate(notification, valid_periods) ex = ve.exception self.assertEqual("Address ftp://somedomain.com scheme is not in ['http', 'https']", ex.message) + def test_validation_exception_for_webhook_invalid_period(self): + notification = {"name": "MyWebhook", "type": "WEBHOOK", "address": "//somedomain.com", + "period": "10"} + with self.assertRaises(schemas_exceptions.ValidationException) as ve: + schemas_notifications.parse_and_validate(notification, valid_periods) + ex = ve.exception + self.assertEqual("10 is not a valid period", ex.message) + def test_validation_for_pagerduty(self): notification = {"name": "MyPagerduty", "type": "PAGERDUTY", "address": "nzH2LVRdMzun11HNC2oD"} try: - schemas_notifications.validate(notification) + schemas_notifications.parse_and_validate(notification, valid_periods) except schemas_exceptions.ValidationException: self.fail("shouldn't happen") + def test_validation_exception_for_invalid_period_for_pagerduty(self): + notification = {"name": "MyPagerduty", "type": "PAGERDUTY", + "address": "nzH2LVRdMzun11HNC2oD", "period": 60} + with self.assertRaises(schemas_exceptions.ValidationException) as ve: + schemas_notifications.parse_and_validate(notification, valid_periods) + ex = ve.exception + self.assertEqual("Period can only be set with webhooks", ex.message) + def test_validation_for_max_name_address(self): name = "A" * 250 self.assertEqual(250, len(name)) @@ -305,7 +338,7 @@ class TestNotificationValidation(unittest.TestCase): self.assertEqual(512, len(address)) notification = {"name": name, "type": "WEBHOOK", "address": address} try: - schemas_notifications.validate(notification) + schemas_notifications.parse_and_validate(notification, valid_periods) except schemas_exceptions.ValidationException: self.fail("shouldn't happen") @@ -315,7 +348,7 @@ class TestNotificationValidation(unittest.TestCase): notification = {"name": name, "type": "WEBHOOK", "address": "http://somedomain.com"} self.assertRaises( schemas_exceptions.ValidationException, - schemas_notifications.validate, notification) + schemas_notifications.parse_and_validate, notification, valid_periods) def test_validation_exception_for_exceeded_address_length(self): address = "http://" + "A" * 503 + ".io" @@ -323,4 +356,27 @@ class TestNotificationValidation(unittest.TestCase): notification = {"name": "MyWebhook", "type": "WEBHOOK", "address": address} self.assertRaises( schemas_exceptions.ValidationException, - schemas_notifications.validate, notification) + schemas_notifications.parse_and_validate, notification, valid_periods) + + def test_validation_exception_for_invalid_period_float(self): + notification = {"name": "MyWebhook", "type": "WEBHOOK", "address": "//somedomain.com", + "period": 1.2} + with self.assertRaises(schemas_exceptions.ValidationException) as ve: + schemas_notifications.parse_and_validate(notification, valid_periods) + ex = ve.exception + self.assertEqual("expected int for dictionary value @ data['period']", ex.message) + + def test_validation_exception_for_invalid_period_non_int(self): + notification = {"name": "MyWebhook", "type": "WEBHOOK", "address": "//somedomain.com", + "period": "zero"} + with self.assertRaises(schemas_exceptions.ValidationException) as ve: + schemas_notifications.parse_and_validate(notification, valid_periods) + ex = ve.exception + self.assertEqual("Period zero must be a valid integer", ex.message) + + def test_validation_exception_for_missing_period(self): + notification = {"name": "MyEmail", "type": "EMAIL", "address": "name@domain."} + with self.assertRaises(schemas_exceptions.ValidationException) as ve: + schemas_notifications.parse_and_validate(notification, valid_periods, require_all=True) + ex = ve.exception + self.assertEqual("Period is required", ex.message) diff --git a/monasca_api/v2/common/schemas/notifications_request_body_schema.py b/monasca_api/v2/common/schemas/notifications_request_body_schema.py index 2efb4fff0..e01e1e455 100644 --- a/monasca_api/v2/common/schemas/notifications_request_body_schema.py +++ b/monasca_api/v2/common/schemas/notifications_request_body_schema.py @@ -1,4 +1,4 @@ -# Copyright 2014 Hewlett-Packard +# (C) Copyright 2014-2016 Hewlett Packard Enterprise Development Company LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain @@ -15,7 +15,12 @@ from oslo_log import log import six.moves.urllib.parse as urlparse from validate_email import validate_email -import voluptuous +from voluptuous import All +from voluptuous import Any +from voluptuous import Length +from voluptuous import Marker +from voluptuous import Required +from voluptuous import Schema from monasca_api.v2.common.schemas import exceptions @@ -24,32 +29,39 @@ LOG = log.getLogger(__name__) schemes = ['http', 'https'] notification_schema = { - voluptuous.Required('name'): voluptuous.Schema( - voluptuous.All(voluptuous.Any(str, unicode), - voluptuous.Length(max=250))), - voluptuous.Required('type'): voluptuous.Schema( - voluptuous.Any("EMAIL", "email", "WEBHOOK", "webhook", - "PAGERDUTY", "pagerduty")), - voluptuous.Required('address'): voluptuous.Schema( - voluptuous.All(voluptuous.Any(str, unicode), - voluptuous.Length(max=512)))} + Required('name'): Schema(All(Any(str, unicode), Length(max=250))), + Required('type'): Schema(Any("EMAIL", "email", "WEBHOOK", "webhook", "PAGERDUTY", "pagerduty")), + Required('address'): Schema(All(Any(str, unicode), Length(max=512))), + Marker('period'): All(Any(int, str))} -request_body_schema = voluptuous.Schema(voluptuous.Any(notification_schema)) +request_body_schema = Schema(Any(notification_schema)) -def validate(msg): +def parse_and_validate(msg, valid_periods, require_all=False): try: request_body_schema(msg) except Exception as ex: LOG.debug(ex) raise exceptions.ValidationException(str(ex)) + if 'period' not in msg: + if require_all: + raise exceptions.ValidationException("Period is required") + else: + msg['period'] = 0 + else: + msg['period'] = _parse_and_validate_period(msg['period'], valid_periods) + notification_type = str(msg['type']).upper() + if notification_type == 'EMAIL': _validate_email(msg['address']) elif notification_type == 'WEBHOOK': _validate_url(msg['address']) + if notification_type != 'WEBHOOK' and msg['period'] != 0: + raise exceptions.ValidationException("Period can only be set with webhooks") + def _validate_email(address): if not validate_email(address): @@ -70,3 +82,13 @@ def _validate_url(address): if parsed.scheme not in schemes: raise exceptions.ValidationException("Address {} scheme is not in {}" .format(address, schemes)) + + +def _parse_and_validate_period(period, valid_periods): + try: + period = int(period) + except Exception: + raise exceptions.ValidationException("Period {} must be a valid integer".format(period)) + if period != 0 and period not in valid_periods: + raise exceptions.ValidationException("{} is not a valid period".format(period)) + return period diff --git a/monasca_api/v2/reference/__init__.py b/monasca_api/v2/reference/__init__.py index 7b03041c8..8c126092d 100644 --- a/monasca_api/v2/reference/__init__.py +++ b/monasca_api/v2/reference/__init__.py @@ -1,5 +1,6 @@ # Copyright 2014 IBM Corp. # Copyright 2016 FUJITSU LIMITED +# (C) Copyright 2016 Hewlett Packard Enterprise Development Company LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain @@ -28,11 +29,12 @@ used in kafka_publisher, but the original settings were at the api/server.py which I think is at the wrong place. I move these settings here for now, we need to have a bit more re-engineering to get it right. """ -global_opts = [cfg.StrOpt('region', help='Region that API is running in')] +global_opts = [cfg.StrOpt('region', help='Region that API is running in'), + cfg.ListOpt('valid_notification_periods', default=[0, 60], + help='Valid periods for notification methods')] cfg.CONF.register_opts(global_opts) - security_opts = [cfg.ListOpt('default_authorized_roles', default=['admin'], help='Roles that are allowed full access to the ' 'API'), diff --git a/monasca_api/v2/reference/notifications.py b/monasca_api/v2/reference/notifications.py index 594398086..3b8ff35b2 100644 --- a/monasca_api/v2/reference/notifications.py +++ b/monasca_api/v2/reference/notifications.py @@ -1,4 +1,4 @@ -# Copyright 2014 Hewlett-Packard +# (C) Copyright 2014-2016 Hewlett Packard Enterprise Development Company LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain @@ -39,15 +39,16 @@ class Notifications(notifications_api_v2.NotificationsV2API): cfg.CONF.security.default_authorized_roles) self._notifications_repo = simport.load( cfg.CONF.repositories.notifications_driver)() + self.valid_periods = cfg.CONF.valid_notification_periods - def _validate_notification(self, notification): + def _parse_and_validate_notification(self, notification, require_all=False): """Validates the notification :param notification: An event object. :raises falcon.HTTPBadRequest """ try: - schemas_notifications.validate(notification) + schemas_notifications.parse_and_validate(notification, self.valid_periods, require_all=require_all) except schemas_exceptions.ValidationException as ex: LOG.debug(ex) raise falcon.HTTPBadRequest('Bad Request', ex.message) @@ -75,6 +76,7 @@ class Notifications(notifications_api_v2.NotificationsV2API): name = notification['name'] notification_type = notification['type'].upper() address = notification['address'] + period = notification['period'] self._validate_name_not_conflicting(tenant_id, name) @@ -82,12 +84,14 @@ class Notifications(notifications_api_v2.NotificationsV2API): tenant_id, name, notification_type, - address) + address, + period) return self._create_notification_response(notification_id, name, notification_type, address, + period, uri) @resource.resource_try_catch_block @@ -96,27 +100,31 @@ class Notifications(notifications_api_v2.NotificationsV2API): name = notification['name'] notification_type = notification['type'].upper() address = notification['address'] + period = notification['period'] self._validate_name_not_conflicting(tenant_id, name, expected_id=notification_id) self._notifications_repo.update_notification(notification_id, tenant_id, name, notification_type, - address) + address, + period) return self._create_notification_response(notification_id, name, notification_type, address, + period, uri) def _create_notification_response(self, id, name, type, - address, uri): + address, period, uri): response = { 'id': id, 'name': name, 'type': type, - 'address': address + 'address': address, + 'period': period } return helpers.add_links_to_resource(response, uri) @@ -147,7 +155,8 @@ class Notifications(notifications_api_v2.NotificationsV2API): u'id': notification_row['id'], u'name': notification_row['name'], u'type': notification_row['type'], - u'address': notification_row['address'] + u'address': notification_row['address'], + u'period': notification_row['period'] } helpers.add_links_to_resource(result, uri) @@ -164,7 +173,7 @@ class Notifications(notifications_api_v2.NotificationsV2API): helpers.validate_json_content_type(req) helpers.validate_authorization(req, self._default_authorized_roles) notification = helpers.read_http_resource(req) - self._validate_notification(notification) + self._parse_and_validate_notification(notification) tenant_id = helpers.get_tenant_id(req) result = self._create_notification(tenant_id, notification, req.uri) res.body = helpers.dumpit_utf8(result) @@ -210,7 +219,7 @@ class Notifications(notifications_api_v2.NotificationsV2API): helpers.validate_json_content_type(req) helpers.validate_authorization(req, self._default_authorized_roles) notification = helpers.read_http_resource(req) - self._validate_notification(notification) + self._parse_and_validate_notification(notification, require_all=True) tenant_id = helpers.get_tenant_id(req) result = self._update_notification(notification_method_id, tenant_id, notification, req.uri) diff --git a/monasca_tempest_tests/services/monasca_client.py b/monasca_tempest_tests/services/monasca_client.py index b7e3d98d2..12810512f 100644 --- a/monasca_tempest_tests/services/monasca_client.py +++ b/monasca_tempest_tests/services/monasca_client.py @@ -75,7 +75,8 @@ class MonascaClient(rest_client.RestClient): def create_notification_method(self, name=None, type=None, - address=None): + address=None, + period=None): uri = 'notification-methods' request_body = {} if name is not None: @@ -84,6 +85,8 @@ class MonascaClient(rest_client.RestClient): request_body['type'] = type if address is not None: request_body['address'] = address + if period is not None: + request_body['period'] = period resp, response_body = self.post(uri, json.dumps(request_body)) return resp, json.loads(response_body) @@ -108,7 +111,8 @@ class MonascaClient(rest_client.RestClient): id, name=None, type=None, - address=None): + address=None, + period=None): uri = 'notification-methods/' + id request_body = {} if name is not None: @@ -117,6 +121,8 @@ class MonascaClient(rest_client.RestClient): request_body['type'] = type if address is not None: request_body['address'] = address + if period is not None: + request_body['period'] = period resp, response_body = self.put(uri, json.dumps(request_body)) return resp, json.loads(response_body) diff --git a/monasca_tempest_tests/tests/api/helpers.py b/monasca_tempest_tests/tests/api/helpers.py index 2995e35e9..0f963c0d0 100644 --- a/monasca_tempest_tests/tests/api/helpers.py +++ b/monasca_tempest_tests/tests/api/helpers.py @@ -52,7 +52,8 @@ def create_metric(name='name-1', def create_notification(name=data_utils.rand_name('notification-'), type='EMAIL', - address='john.doe@domain.com'): + address='john.doe@domain.com', + period=0): notification = {} if name is not None: notification['name'] = name @@ -60,6 +61,8 @@ def create_notification(name=data_utils.rand_name('notification-'), notification['type'] = type if address is not None: notification['address'] = address + if period is not None: + notification['period'] = period return notification diff --git a/monasca_tempest_tests/tests/api/test_notification_methods.py b/monasca_tempest_tests/tests/api/test_notification_methods.py index 45f5b0611..72bef3664 100644 --- a/monasca_tempest_tests/tests/api/test_notification_methods.py +++ b/monasca_tempest_tests/tests/api/test_notification_methods.py @@ -48,11 +48,39 @@ class TestNotificationMethods(base.BaseMonascaTest): delete_notification_method(id) self.assertEqual(204, resp.status) + @test.attr(type="gate") + def test_create_notification_method_period_not_defined(self): + notification = helpers.create_notification(period=None) + resp, response_body = self.monasca_client.create_notifications( + notification) + self.assertEqual(201, resp.status) + id = response_body['id'] + + resp, response_body = self.monasca_client.\ + delete_notification_method(id) + self.assertEqual(204, resp.status) + + @test.attr(type="gate") + def test_create_webhook_notification_method_with_non_zero_period(self): + name = data_utils.rand_name('notification-') + notification = helpers.create_notification(name=name, + type='WEBHOOK', + address='http://localhost/test01', + period=60) + resp, response_body = self.monasca_client.create_notifications( + notification) + self.assertEqual(201, resp.status) + id = response_body['id'] + + resp, response_body = self.monasca_client.\ + delete_notification_method(id) + self.assertEqual(204, resp.status) + @test.attr(type="gate") @test.attr(type=['negative']) def test_create_notification_method_with_no_name(self): notification = helpers.create_notification(name=None) - self.assertRaises((exceptions.BadRequest,exceptions.UnprocessableEntity), + self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity), self.monasca_client.create_notifications, notification) @@ -60,7 +88,7 @@ class TestNotificationMethods(base.BaseMonascaTest): @test.attr(type=['negative']) def test_create_notification_method_with_no_type(self): notification = helpers.create_notification(type=None) - self.assertRaises((exceptions.BadRequest,exceptions.UnprocessableEntity), + self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity), self.monasca_client.create_notifications, notification) @@ -68,7 +96,7 @@ class TestNotificationMethods(base.BaseMonascaTest): @test.attr(type=['negative']) def test_create_notification_method_with_no_address(self): notification = helpers.create_notification(address=None) - self.assertRaises((exceptions.BadRequest,exceptions.UnprocessableEntity), + self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity), self.monasca_client.create_notifications, notification) @@ -99,6 +127,50 @@ class TestNotificationMethods(base.BaseMonascaTest): self.monasca_client.create_notifications, notification) + @test.attr(type="gate") + @test.attr(type=['negative']) + def test_create_notification_method_with_invalid_float_period(self): + notification = helpers.create_notification(period=1.2) + self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity), + self.monasca_client.create_notifications, + notification) + + @test.attr(type="gate") + @test.attr(type=['negative']) + def test_create_notification_method_with_invalid_string_period(self): + notification = helpers.create_notification(period='random') + self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity), + self.monasca_client.create_notifications, + notification) + + @test.attr(type="gate") + @test.attr(type=['negative']) + def test_create_email_notification_method_with_invalid_non_zero_period(self): + notification = helpers.create_notification(period=60) + self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity), + self.monasca_client.create_notifications, + notification) + + @test.attr(type="gate") + @test.attr(type=['negative']) + def test_create_pagerduty_notification_method_with_invalid_non_zero_period(self): + notification = helpers.create_notification(type='PAGERDUTY', + address='test03@localhost', + period=60) + self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity), + self.monasca_client.create_notifications, + notification) + + @test.attr(type="gate") + @test.attr(type=['negative']) + def test_create_webhook_notification_method_with_invalid_period(self): + notification = helpers.create_notification(type='WEBHOOK', + address='http://localhost/test01', + period=10) + self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity), + self.monasca_client.create_notifications, + notification) + @test.attr(type="gate") def test_list_notification_methods(self): notification = helpers.create_notification() @@ -112,7 +184,7 @@ class TestNotificationMethods(base.BaseMonascaTest): self.assertTrue(set(['links', 'elements']) == set(response_body)) elements = response_body['elements'] element = elements[0] - self.assertTrue(set(['id', 'links', 'name', 'type', 'address']) == + self.assertTrue(set(['id', 'links', 'name', 'type', 'address', 'period']) == set(element)) self.assertTrue(type(element['id']) is unicode) self.assertTrue(type(element['links']) is list) @@ -359,7 +431,8 @@ class TestNotificationMethods(base.BaseMonascaTest): resp, response_body = self.monasca_client.\ update_notification_method(id, new_name, type=response_body['type'], - address=response_body['address']) + address=response_body['address'], + period=response_body['period']) self.assertEqual(200, resp.status) self.assertEqual(new_name, response_body['name']) resp, response_body = self.monasca_client.\ @@ -380,7 +453,8 @@ class TestNotificationMethods(base.BaseMonascaTest): self.monasca_client.\ update_notification_method(id, name=response_body['name'], type=new_type, - address=response_body['address']) + address=response_body['address'], + period=response_body['period']) self.assertEqual(200, resp.status) self.assertEqual(new_type, response_body['type']) resp, response_body = self.monasca_client.\ @@ -401,7 +475,8 @@ class TestNotificationMethods(base.BaseMonascaTest): update_notification_method(id, name=response_body['name'], type=response_body['type'], - address=new_address) + address=new_address, + period=0) self.assertEqual(200, resp.status) self.assertEqual(new_address, response_body['address']) resp, response_body = \ @@ -422,7 +497,7 @@ class TestNotificationMethods(base.BaseMonascaTest): self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity), self.monasca_client.update_notification_method, id, name=new_name_long, type=response_body['type'], - address=response_body['address']) + address=response_body['address'], period=response_body['period']) resp, response_body = \ self.monasca_client.delete_notification_method(id) self.assertEqual(204, resp.status) @@ -439,7 +514,7 @@ class TestNotificationMethods(base.BaseMonascaTest): self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity), self.monasca_client.update_notification_method, id, name=response_body['name'], type='random', - address=response_body['address']) + address=response_body['address'], period=0) resp, response_body = \ self.monasca_client.delete_notification_method(id) self.assertEqual(204, resp.status) @@ -458,7 +533,7 @@ class TestNotificationMethods(base.BaseMonascaTest): self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity), self.monasca_client.update_notification_method, id, name=response_body['name'], type=response_body['type'], - address=new_address_long) + address=new_address_long, period=response_body['period']) resp, response_body = \ self.monasca_client.delete_notification_method(id) self.assertEqual(204, resp.status) @@ -489,3 +564,97 @@ class TestNotificationMethods(base.BaseMonascaTest): resp, response_body = self.monasca_client.\ delete_notification_method(response_body['id']) self.assertEqual(204, resp.status) + + @test.attr(type="gate") + @test.attr(type=['negative']) + def test_update_email_notification_method_with_nonzero_period(self): + name = data_utils.rand_name('notification-') + notification = helpers.create_notification(name=name) + resp, response_body = self.monasca_client.create_notifications( + notification) + id = response_body['id'] + self.assertEqual(201, resp.status) + self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity), + self.monasca_client.update_notification_method, id, + name=response_body['name'], type=response_body['type'], + address=response_body['address'], period=60) + resp, response_body = \ + self.monasca_client.delete_notification_method(id) + self.assertEqual(204, resp.status) + + @test.attr(type="gate") + @test.attr(type=['negative']) + def test_update_webhook_notification_method_to_email_with_nonzero_period(self): + name = data_utils.rand_name('notification-') + notification = helpers.create_notification(name=name, + type='WEBHOOK', + address='http://localhost/test01', + period=60) + resp, response_body = self.monasca_client.create_notifications( + notification) + id = response_body['id'] + self.assertEqual(201, resp.status) + self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity), + self.monasca_client.update_notification_method, id, + name=response_body['name'], type='EMAIL', + address='test@localhost', period=response_body['period']) + resp, response_body = \ + self.monasca_client.delete_notification_method(id) + self.assertEqual(204, resp.status) + + @test.attr(type="gate") + @test.attr(type=['negative']) + def test_update_webhook_notification_method_to_pagerduty_with_nonzero_period(self): + name = data_utils.rand_name('notification-') + notification = helpers.create_notification(name=name, + type='WEBHOOK', + address='http://localhost/test01', + period=60) + resp, response_body = self.monasca_client.create_notifications( + notification) + id = response_body['id'] + self.assertEqual(201, resp.status) + self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity), + self.monasca_client.update_notification_method, id, + name=response_body['name'], type='PAGERDUTY', + address='test@localhost', period=response_body['period']) + resp, response_body = \ + self.monasca_client.delete_notification_method(id) + self.assertEqual(204, resp.status) + + @test.attr(type="gate") + @test.attr(type=['negative']) + def test_update_notification_method_with_non_int_period(self): + name = data_utils.rand_name('notification-') + notification = helpers.create_notification(name=name) + resp, response_body = self.monasca_client.create_notifications( + notification) + id = response_body['id'] + self.assertEqual(201, resp.status) + self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity), + self.monasca_client.update_notification_method, id, + name=response_body['name'], type=response_body['type'], + address=response_body['name'], period='zero') + resp, response_body = \ + self.monasca_client.delete_notification_method(id) + self.assertEqual(204, resp.status) + + @test.attr(type="gate") + @test.attr(type=['negative']) + def test_update_webhook_notification_method_with_invalid_period(self): + name = data_utils.rand_name('notification-') + notification = helpers.create_notification(name=name, + type='WEBHOOK', + address='http://localhost/test01', + period=60) + resp, response_body = self.monasca_client.create_notifications( + notification) + id = response_body['id'] + self.assertEqual(201, resp.status) + self.assertRaises((exceptions.BadRequest, exceptions.UnprocessableEntity), + self.monasca_client.update_notification_method, id, + name=response_body['name'], type=response_body['type'], + address=response_body['address'], period=5) + resp, response_body = \ + self.monasca_client.delete_notification_method(id) + self.assertEqual(204, resp.status)