Skip to content

Commit

Permalink
Add ability to copy data source REST v2.
Browse files Browse the repository at this point in the history
As per #55
  • Loading branch information
terrypacker committed Jul 24, 2019
1 parent 53c2b25 commit 70b11cf
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 0 deletions.
1 change: 1 addition & 0 deletions Mango API/RELEASE-NOTES
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
*Version 3.6.2*
* Fix bug where bookends were not being applied to Simplified data ranges with no values in them
* Add copy endpoint for data sources PUT /rest/v2/data-sources/copy/

*Version 3.6.1*
* Improve performance by reducing garbage generation for point value rollup requests on the v2 endpoints
Expand Down
81 changes: 81 additions & 0 deletions Mango API/api-test/dataSource.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,39 @@
*/

const config = require('@infinite-automation/mango-client/test/setup');
const uuidV4 = require('uuid/v4');

describe('Data source service', () => {
before('Login', config.login);

const newDataPoint = (xid, dsXid, rollupType, simplifyType, simplifyValue) => {
return new DataPoint({
xid: xid,
enabled: true,
name: 'Point values test',
deviceName: 'Point values test',
dataSourceXid : dsXid,
pointLocator : {
startValue : '0',
modelType : 'PL.VIRTUAL',
dataType : 'NUMERIC',
changeType : 'NO_CHANGE',
settable: true
},
textRenderer: {
type: 'textRendererAnalog',
format: '0.00',
suffix: '',
useUnitAsSuffix: false,
unit: '',
renderedUnit: ''
},
rollup: rollupType,
simplifyType: simplifyType,
simplifyTolerance: simplifyValue
});
};

it('Gets the internal data source', () => {
return DataSource.get('internal_mango_monitoring_ds').then(ds => {
assert.equal(ds.xid, 'internal_mango_monitoring_ds');
Expand Down Expand Up @@ -80,5 +109,57 @@ describe('Data source service', () => {
assert.equal(dsList[0].name, 'Mango Internal');
});
});

it('Copies a data source and points', function() {
this.ds = new DataSource({
xid: uuidV4(),
name: 'Mango client test',
enabled: true,
modelType: 'VIRTUAL',
pollPeriod: { periods: 5, type: 'HOURS' },
purgeSettings: { override: false, frequency: { periods: 1, type: 'YEARS' } },
alarmLevels: { POLL_ABORTED: 'URGENT' },
editPermission: null
});

return this.ds.save().then((savedDs) => {
assert.strictEqual(savedDs.name, 'Mango client test');
assert.isNumber(savedDs.id);
}).then(() => {
this.testPoint1 = newDataPoint(uuidV4(), this.ds.xid, 'FIRST', 'NONE', 0);
this.testPoint2 = newDataPoint(uuidV4(), this.ds.xid, 'FIRST', 'NONE', 0);
this.testPoint3 = newDataPoint(uuidV4(), this.ds.xid, 'COUNT', 'TOLERANCE', 10.0);
this.testPoint4 = newDataPoint(uuidV4(), this.ds.xid, 'COUNT', 'NONE', 0);
return Promise.all([this.testPoint1.save(), this.testPoint2.save(), this.testPoint3.save(), this.testPoint4.save()]);
}).then(() => {
return client.restRequest({
path: `/rest/v2/data-sources/copy/${this.ds.xid}`,
params: {
copyName: this.ds.name + '-copy',
copyXid: this.ds.xid + '-copy',
copyDeviceName: 'Mango client copy device name',
copyPoints: true,
enabled: false
},
method: 'PUT'
}).then(response => {
assert.strictEqual(response.data.xid, this.ds.xid + '-copy');
assert.strictEqual(response.data.name, this.ds.name + '-copy');
assert.strictEqual(response.data.enabled, false);
return client.restRequest({
path: `/rest/v2/data-points?dataSourceXid=${this.ds.xid}-copy`,
method: 'GET'
}).then(response => {
assert.strictEqual(response.data.total, 4);
response.data.items.forEach((dp, i) => {
assert.strictEqual(dp.deviceName, 'Mango client copy device name');
});
});
});
});
});

after('Deletes the copied virtual data source and its points', function() {
return this.ds.delete();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
Expand All @@ -36,14 +37,19 @@
import com.infiniteautomation.mango.rest.v2.patch.PatchVORequestBody;
import com.infiniteautomation.mango.spring.service.DataSourceService;
import com.infiniteautomation.mango.util.RQLUtils;
import com.infiniteautomation.mango.util.exception.ValidationException;
import com.serotonin.db.pair.LongLongPair;
import com.serotonin.m2m2.Common;
import com.serotonin.m2m2.db.dao.DataPointDao;
import com.serotonin.m2m2.db.dao.DataSourceDao;
import com.serotonin.m2m2.i18n.ProcessResult;
import com.serotonin.m2m2.i18n.TranslatableMessage;
import com.serotonin.m2m2.rt.dataSource.DataSourceRT;
import com.serotonin.m2m2.rt.dataSource.PollingDataSource;
import com.serotonin.m2m2.vo.User;
import com.serotonin.m2m2.vo.dataSource.DataSourceVO;
import com.serotonin.m2m2.web.MediaTypes;
import com.serotonin.validation.StringValidation;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
Expand Down Expand Up @@ -251,6 +257,76 @@ public Map<String, Object> exportDataSource(
return export;
}

@ApiOperation(value = "Copy data source", notes="Copy the data source and its points with optional new XID and Name.")
@RequestMapping(method = RequestMethod.PUT, value = "/copy/{xid}")
public ResponseEntity<AbstractDataSourceModel<?>> copy(
@PathVariable String xid,
@ApiParam(value = "Copy's new XID", required = false, defaultValue="null", allowMultiple = false)
@RequestParam(required=false, defaultValue="null") String copyXid,
@ApiParam(value = "Copy's name", required = false, defaultValue="null", allowMultiple = false)
@RequestParam(required=false, defaultValue="null") String copyName,
@ApiParam(value = "Device name for copied points", required = false, defaultValue="null", allowMultiple = false)
@RequestParam(required=false, defaultValue="null") String copyDeviceName,
@ApiParam(value = "Enable/disabled state of data source", required = false, defaultValue="false", allowMultiple = false)
@RequestParam(required=false, defaultValue="false") boolean enabled,
@ApiParam(value = "Copy data points", required = false, defaultValue="false", allowMultiple = false)
@RequestParam(required=false, defaultValue="false") boolean copyPoints,

@AuthenticationPrincipal User user,
UriComponentsBuilder builder) {
//TODO Mango 3.7 move this logic to service
T existing = service.get(xid, user);

//Determine the new name
String newName;
if(StringUtils.isEmpty(copyName)) {
newName = StringUtils.abbreviate(
TranslatableMessage.translate(Common.getTranslations(), "common.copyPrefix", existing.getName()), 40);
}else {
newName = copyName;
}
//Determine the new xid
String newXid;
if(StringUtils.isEmpty(copyXid)) {
newXid = DataSourceDao.getInstance().generateUniqueXid();
}else {
newXid = copyXid;
}

String newDeviceName;
if(StringUtils.isEmpty(copyDeviceName)) {
newDeviceName = existing.getName();
}else {
newDeviceName = copyDeviceName;
}
//Ensure device name is valid
if (StringValidation.isLengthGreaterThan(newDeviceName, 255)) {
ProcessResult result = new ProcessResult();
result.addMessage("deviceName", new TranslatableMessage("validate.notLongerThan", 255));
throw new ValidationException(result);
}

T copy = existing.copy();
copy.setId(Common.NEW_ID);
copy.setName(newName);
copy.setXid(newXid);
copy.setEnabled(enabled);
copy.ensureValid();

//Save it
Common.runtimeManager.saveDataSource(copy);

if(copyPoints) {
DataSourceDao.getInstance().copyDataSourcePoints(existing.getId(), copy.getId(), newDeviceName);
}

URI location = builder.path("/data-sources/{xid}").buildAndExpand(newXid).toUri();
HttpHeaders headers = new HttpHeaders();
headers.setLocation(location);

return new ResponseEntity<>(map.apply(service.get(newXid, user), user), headers, HttpStatus.OK);
}

/**
* Perform a query
* @param rql
Expand Down

0 comments on commit 70b11cf

Please sign in to comment.