Skip to content

Commit

Permalink
redisstore: Delete endpoints for related links and tags for samples a…
Browse files Browse the repository at this point in the history
…nd aspects (#293)

* delete rlinks of sample
added tests for aspect tag delete
delete aspect rlinks tests

* changes based on annys comment
  • Loading branch information
pallavi2209 authored and annyhe committed Mar 22, 2017
1 parent 4a64237 commit 5c75891
Show file tree
Hide file tree
Showing 7 changed files with 450 additions and 14 deletions.
31 changes: 19 additions & 12 deletions api/v1/controllers/samples.js
Original file line number Diff line number Diff line change
Expand Up @@ -250,19 +250,26 @@ module.exports = {
deleteSampleRelatedLinks(req, res, next) {
const resultObj = { reqStartTime: new Date() };
const params = req.swagger.params;
u.findByKey(helper, params)
.then((o) => u.isWritable(req, o,
featureToggles.isFeatureEnabled('enforceWritePermission')))
.then((o) => {
let jsonData = [];
if (params.relName) {
jsonData =
u.deleteAJsonArrayElement(o.relatedLinks, params.relName.value);
}
let delRlinksPromise;
if (featureToggles.isFeatureEnabled(constants.featureName) &&
helper.modelName === 'Sample') {
delRlinksPromise = redisModelSample.deleteSampleRelatedLinks(params);
} else {
delRlinksPromise = u.findByKey(helper, params)
.then((o) => u.isWritable(req, o,
featureToggles.isFeatureEnabled('enforceWritePermission')))
.then((o) => {
let jsonData = [];
if (params.relName) {
jsonData =
u.deleteAJsonArrayElement(o.relatedLinks, params.relName.value);
}

return o.update({ relatedLinks: jsonData });
})
.then((o) => {
return o.update({ relatedLinks: jsonData });
});
}

delRlinksPromise.then((o) => {
resultObj.dbTime = new Date() - resultObj.reqStartTime;
const retval = u.responsify(o, helper, req.method);

Expand Down
59 changes: 59 additions & 0 deletions cache/models/samples.js
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,65 @@ module.exports = {
});
},

/**
* Delete sample related links
* @param {Object} params - Request parameters
* @returns {Promise} - Resolves to a sample object
*/
deleteSampleRelatedLinks(params) {
const sampleName = params.key.value;
let currSampObj;
let aspectObj;

return redisOps.getHashPromise(sampleType, sampleName)
.then((sampObj) => {
if (!sampObj) {
throw new redisErrors.ResourceNotFoundError({
explanation: 'Sample not found.',
});
}

currSampObj = sampObj;
const aspectName = sampleName.split('|')[ONE];
return redisOps.getHashPromise(aspectType, aspectName);
})
.then((aspObj) => {
if (!aspObj) {
throw new redisErrors.ResourceNotFoundError({
explanation: 'Aspect not found.',
});
}

let updatedRlinks = [];
if (params.relName) { // delete only this related link
const currRlinks = JSON.parse(currSampObj.relatedLinks);
updatedRlinks = u.deleteAJsonArrayElement(
currRlinks, params.relName.value
);
}

// if no change in related links, then return the object.
if (JSON.stringify(updatedRlinks) ===
JSON.stringify(currSampObj.relatedLinks)) {
Promise.resolve(cleanAddAspectToSample(currSampObj, aspObj));
}

const hmsetObj = {};
hmsetObj.relatedLinks = updatedRlinks;
hmsetObj.updatedAt = new Date().toString();

// stringify arrays
constants.fieldsToStringify.sample.forEach((field) => {
if (hmsetObj[field]) {
hmsetObj[field] = JSON.stringify(hmsetObj[field]);
}
});
return redisOps.setHashMultiPromise(sampleType, sampleName, hmsetObj);
})
.then(() => redisOps.getHashPromise(sampleType, sampleName))
.then((updatedSamp) => cleanAddAspectToSample(updatedSamp, aspectObj));
},

/**
* Patch sample. First get sample, if not found, throw error, else get aspect.
* Update request body with required fields based on value and related links
Expand Down
2 changes: 1 addition & 1 deletion db/model/aspect.js
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ module.exports = function aspect(seq, dataTypes) {
* on aspectStore and the aspect hash.
* 2. if the aspect is updated to published, add an entry to the
* aspectStore and create the aspect hash
* 3. if the aspect is updated to unpublihsed, delete the entry in the
* 3. if the aspect is updated to unpublished, delete the entry in the
* aspectStore, delete the aspect hash and delete the related samples
* 4. if the aspect that is updated is already published, update the
* the aspect with the new values.
Expand Down
2 changes: 1 addition & 1 deletion tests/cache/models/aspects/aspectCRUD.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

/**
* tests/cache/models/aspect/aspectCRUD.js
* tests/cache/models/aspects/aspectCRUD.js
*/
'use strict'; // eslint-disable-line strict

Expand Down
128 changes: 128 additions & 0 deletions tests/cache/models/aspects/deleteRelatedLinks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/**
* Copyright (c) 2017, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or
* https://opensource.org/licenses/BSD-3-Clause
*/

/**
* tests/cache/models/aspects/deleteRelatedLinks.js
*/
'use strict'; // eslint-disable-line strict

const supertest = require('supertest');
const api = supertest(require('../../../../index').app);
const constants = require('../../../../api/v1/constants');
const tu = require('../../../testUtils');
const rtu = require('../redisTestUtil');
const expect = require('chai').expect;
const Aspect = tu.db.Aspect;
const allDeletePath = '/v1/aspects/{key}/relatedLinks';
const oneDeletePath = '/v1/aspects/{key}/relatedLinks/{akey}';
const redisOps = require('../../../../cache/redisOps');
const objectType = require('../../../../cache/sampleStore')
.constants.objectType;
const samstoinit = rtu.samstoinit;
const ZERO = 0;
const ONE = 1;

describe('api: aspects: DELETE RelatedLinks', () => {
let token;
let i;
let name;

const n = {
name: `${tu.namePrefix}ASPECTNAME`,
timeout: '110s',
relatedLinks: [
{ name: 'rlink0', url: 'https://samples.com' },
{ name: 'rlink1', url: 'https://samples.com' },
],
isPublished: true,
};

before((done) => {
tu.toggleOverride('enableRedisSampleStore', true);
tu.createToken()
.then((returnedToken) => {
token = returnedToken;
done();
})
.catch(done);
});

beforeEach((done) => {
Aspect.create(n)
.then((asp) => {
i = asp.id;
name = asp.name;
return samstoinit.eradicate();
})
.then(() => samstoinit.init())
.then(() => done())
.catch(done);
});
afterEach(rtu.forceDelete);
after(() => tu.toggleOverride('enableRedisSampleStore', false));

it('delete all related links', (done) => {
api.delete(allDeletePath.replace('{key}', i))
.set('Authorization', token)
.expect(constants.httpStatus.OK)
.end((err, res) => {
if (err) {
done(err);
}

expect(res.body.relatedLinks).to.have.length(ZERO);
redisOps.getHashPromise(objectType.aspect, n.name)
.then((aspect) => {
expect(JSON.parse(aspect.relatedLinks)).to.have.length(ZERO);
done();
});
});
});

it('delete one relatedLink', (done) => {
api.delete(oneDeletePath.replace('{key}', i).replace('{akey}', 'rlink0'))
.set('Authorization', token)
.expect(constants.httpStatus.OK)
.end((err, res) => {
if (err) {
done(err);
}

expect(res.body.relatedLinks).to.have.length(ONE);
expect(res.body.relatedLinks).to.have.deep.property('[0].name', 'rlink1');
redisOps.getHashPromise(objectType.aspect, n.name)
.then((aspect) => {
const rlinks = JSON.parse(aspect.relatedLinks);
expect(rlinks).to.have.length(ONE);
expect(rlinks).to.have.deep.property('[0].name', 'rlink1');
done();
});
});
});

it('delete related link by name', (done) => {
api.delete(oneDeletePath.replace('{key}', name).replace('{akey}', 'rlink0'))
.set('Authorization', token)
.expect(constants.httpStatus.OK)
.end((err, res) => {
if (err) {
done(err);
}

expect(res.body.relatedLinks).to.have.length(ONE);
expect(res.body.relatedLinks).to.have.deep.property('[0].name', 'rlink1');
redisOps.getHashPromise(objectType.aspect, n.name)
.then((aspect) => {
const rlinks = JSON.parse(aspect.relatedLinks);
expect(rlinks).to.have.length(ONE);
expect(rlinks).to.have.deep.property('[0].name', 'rlink1');
done();
});
});
});
});
141 changes: 141 additions & 0 deletions tests/cache/models/aspects/deleteTags.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/**
* Copyright (c) 2017, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or
* https://opensource.org/licenses/BSD-3-Clause
*/

/**
* tests/cache/models/aspects/deleteTags.js
*/
'use strict'; // eslint-disable-line strict

const supertest = require('supertest');
const api = supertest(require('../../../../index').app);
const constants = require('../../../../api/v1/constants');
const tu = require('../../../testUtils');
const rtu = require('../redisTestUtil');
const expect = require('chai').expect;
const Aspect = tu.db.Aspect;
const allDeletePath = '/v1/aspects/{key}/tags';
const oneDeletePath = '/v1/aspects/{key}/tags/{akey}';
const redisOps = require('../../../../cache/redisOps');
const objectType = require('../../../../cache/sampleStore')
.constants.objectType;
const samstoinit = rtu.samstoinit;

describe(`api: redisStore: aspects: DELETE tags`, () => {
let token;
let aspId;
let aspName;
const tag0 = 'tag0';

const n = {
name: `${tu.namePrefix}ASPECTNAME`,
timeout: '110s',
tags: ['tag0', 'tag1'],
isPublished: true,
};

before((done) => {
tu.toggleOverride('enableRedisSampleStore', true);
tu.createToken()
.then((returnedToken) => {
token = returnedToken;
done();
})
.catch(done);
});

beforeEach((done) => {
Aspect.create(n)
.then((asp) => {
aspId = asp.id;
aspName = asp.name;
return samstoinit.eradicate();
})
.then(() => samstoinit.init())
.then(() => done())
.catch(done);
});
afterEach(rtu.forceDelete);
after(() => tu.toggleOverride('enableRedisSampleStore', false));

it('delete all tags', (done) => {
api.delete(allDeletePath.replace('{key}', aspId))
.set('Authorization', token)
.expect(constants.httpStatus.OK)
.end((err, res) => {
if (err) {
done(err);
}
expect(res.body.tags).to.have.length(0);
redisOps.getHashPromise(objectType.aspect, n.name)
.then((aspect) => {
expect(JSON.parse(aspect.tags)).to.have.length(0);
done();
});
});
});

it('delete one tag', (done) => {
api.delete(oneDeletePath.replace('{key}', aspId).replace('{akey}', tag0))
.set('Authorization', token)
.expect(constants.httpStatus.OK)
.end((err, res) => {
if (err) {
done(err);
}
expect(res.body.tags).to.have.length(1);
expect(res.body.tags).to.have.members(['tag1']);
redisOps.getHashPromise(objectType.aspect, n.name)
.then((aspect) => {
const tags = JSON.parse(aspect.tags);
expect(tags).to.have.length(1);
expect(tags).to.have.members(['tag1']);
done();
});
});
});

it('delete tag by name', (done) => {
api.delete(oneDeletePath.replace('{key}', aspName).replace('{akey}', tag0))
.set('Authorization', token)
.expect(constants.httpStatus.OK)
.end((err, res) => {
if (err) {
done(err);
}
expect(res.body.tags).to.have.length(1);
expect(res.body.tags).to.have.members(['tag1']);
redisOps.getHashPromise(objectType.aspect, n.name)
.then((aspect) => {
const tags = JSON.parse(aspect.tags);
expect(tags).to.have.length(1);
expect(tags).to.have.members(['tag1']);
done();
});
});
});

it('no error if tag not found, no update on tags', (done) => {
api.delete(oneDeletePath.replace('{key}', aspId).replace('{akey}', 'x'))
.set('Authorization', token)
.expect(constants.httpStatus.OK)
.end((err, res) => {
if (err) {
done(err);
}
expect(res.body.tags).to.have.length(2);
expect(res.body.tags).to.have.members(['tag1', 'tag0']);
redisOps.getHashPromise(objectType.aspect, n.name)
.then((aspect) => {
const tags = JSON.parse(aspect.tags);
expect(tags).to.have.length(2);
expect(tags).to.have.members(['tag1', 'tag0']);
done();
});
});
});
});
Loading

0 comments on commit 5c75891

Please sign in to comment.