Merge "Add period field to notification methods"

This commit is contained in:
Jenkins 2016-06-09 02:43:51 +00:00 committed by Gerrit Code Review
commit b172585fa9
39 changed files with 831 additions and 200 deletions

1
.gitignore vendored
View File

@ -32,3 +32,4 @@ log/
db/config.yml
virtenv/*
.vagrant
AUTHORS

View File

@ -29,6 +29,9 @@ metricsTopic: metrics
# Topic for publishing domain events to
eventsTopic: events
validNotificationPeriods:
- 60
kafka:
brokerUris:
- "127.0.0.1:9092"

View File

@ -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

View File

@ -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`),

View File

@ -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");
@ -674,6 +674,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
}

View File

@ -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
}
````
___

View File

@ -26,6 +26,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;
@ -43,7 +45,8 @@ public class ApiConfig extends Configuration {
public int maxQueryLimit;
@NotEmpty
public String alarmStateTransitionsTopic = "alarm-state-transitions";
@NotEmpty
public List<Integer> validNotificationPeriods;
@Valid
@NotNull
public DataSourceFactory mysql;

View File

@ -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<Integer> 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;
}
}

View File

@ -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<Integer> 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;
}
}

View File

@ -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<Integer> 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);
}
}
}

View File

@ -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;
}
}

View File

@ -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<NotificationMethod> find(String tenantId, List<String> sortBy, String offset, int limit);
}

View File

@ -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()
);
}
}

View File

@ -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);
}
}
}

View File

@ -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<String> ALLOWED_SORT_BY = Arrays.asList("id", "name", "type",
"address", "updated_at",
"created_at");
private final List<Integer> 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);
}

View File

@ -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

View File

@ -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<Integer> 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<ConstraintViolation<CreateNotificationMethodCommand>> 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<ConstraintViolation<CreateNotificationMethodCommand>> 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<ConstraintViolation<CreateNotificationMethodCommand>> 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);
}
}

View File

@ -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")));
}

View File

@ -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<NotificationMethod> 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<NotificationMethod> 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)

View File

@ -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<NotificationMethod> 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);
}
}

View File

@ -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 =

View File

@ -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<String>) 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<NotificationMethod> 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));
}
}

View File

@ -0,0 +1 @@
{"name":"MyWebhook","type":"webhook","address":"http://somedomain.com","period":"60"}

View File

@ -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}

View File

@ -10,6 +10,9 @@ metricsTopic: metrics
# Topic for publishing domain events to
eventsTopic: events
validNotificationPeriods:
- 60
databaseConfiguration:
# vertica | influxdb
databaseType: influxdb

View File

@ -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`)

View File

@ -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)

View File

@ -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

View File

@ -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))

View File

@ -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:

View File

@ -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`)

View File

@ -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'

View File

@ -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)

View File

@ -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

View File

@ -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'),

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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)