Permalink
Browse files

add option to delete context (#1093)

* add option to delete context

* fix integration tests

* update swagger

* use async methods for deleting
  • Loading branch information...
nataly87s committed Feb 1, 2019
1 parent 04942ff commit 5f412bc3cbdbad92bc75a1d92de94b45feec0b6f
@@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Couchbase;
using Tweek.Engine.DataTypes;
using Tweek.Engine.Drivers.Context;

@@ -37,6 +38,14 @@ public async Task RemoveFromContext(Identity identity, string key)
if (!deleteResult.Success && deleteResult.Message != "Status code: SubDocPathNotFound [192]") throw deleteResult.Exception ?? new Exception("Error deleting context property") { Data = { { "Identity_Key", identityKey } ,{ "Property", key } } };
}

public async Task DeleteContext(Identity identity)
{
var identityKey = GetKey(identity);
var bucket = GetOrOpenBucket();
var result = await bucket.RemoveAsync(identityKey);
if (!result.Success && !(result.Exception is DocumentDoesNotExistException)) throw result.Exception ?? new Exception("Error deleting context") { Data = { { "Identity_Key", identityKey } } };
}

public async Task AppendContext(Identity identity, Dictionary<string, JsonValue> context)
{
var key = GetKey(identity);
@@ -81,12 +90,5 @@ public async Task AppendContext(Identity identity, Dictionary<string, JsonValue>
throw new AggregateException(document.Exception ?? new Exception(document.Message),
replica.Exception ?? new Exception(replica.Message));
}

public async Task RemoveIdentityContext(Identity identity)
{
await GetOrOpenBucket().RemoveAsync(GetKey(identity));
}


}
}
@@ -64,7 +64,7 @@ public async Task AppendContext(Identity identity, Dictionary<string, JsonValue>
var collection = GetCollection(identity);
var filter = Builders<Dictionary<string, object>>.Filter.Eq("_id", id);
var result = await (await collection.FindAsync(filter)).SingleOrDefaultAsync();
return ConversionUtils.ToJsonValues(result);
return result == null ? new Dictionary<string, JsonValue>() : ConversionUtils.ToJsonValues(result);
}

public async Task RemoveFromContext(Identity identity, string key)
@@ -76,6 +76,14 @@ public async Task RemoveFromContext(Identity identity, string key)
await collection.FindOneAndUpdateAsync<Dictionary<string, object>>(filter, update);
}

public async Task DeleteContext(Identity identity)
{
var id = GetKey(identity);
var collection = GetCollection(identity);
var filter = Builders<Dictionary<string, object>>.Filter.Eq("_id", id);
await collection.DeleteOneAsync(filter);
}

private static Dictionary<string, object> ToObjects(Dictionary<string, JsonValue> input)
{
var result = new Dictionary<string, object>(input.Count);
@@ -89,5 +89,12 @@ public async Task RemoveFromContext(Identity identity, string key)
var id = GetKey(identity);
await db.HashDeleteAsync(id, key);
}

public async Task DeleteContext(Identity identity)
{
var db = mRedisConnection.GetDatabase();
var id = GetKey(identity);
await db.KeyDeleteAsync(id);
}
}
}
@@ -114,5 +114,26 @@ public async Task ContexctCreated_ThenContextAppended_CreationDateDoesntChange()
Assert.Contains(CREATION_DATE, result.Keys);
Assert.Equal(creationDate, result[CREATION_DATE]);
}

[Fact]
public async Task ContextCreated_ThenContextDelete_IdentityDeleted()
{
var testIdentity = TestIdentity;
await Driver.AppendContext(testIdentity, TestContext);
var result = await Driver.GetContext(testIdentity);
Assert.Contains(CREATION_DATE, result.Keys);

await Driver.DeleteContext(testIdentity);
result = await Driver.GetContext(testIdentity);
Assert.Equal(0, result.Count);
}

[Fact]
public async Task ContextDoesntExist_DeleteContext_DoesntThrow()
{
var testIdentity = TestIdentity;
await Driver.DeleteContext(testIdentity);
await Driver.DeleteContext(testIdentity);
}
}
}
@@ -9,5 +9,6 @@ public interface IContextWriter
{
Task AppendContext(Identity identity, Dictionary<string, JsonValue> context);
Task RemoveFromContext(Identity identity, string key);
Task DeleteContext(Identity identity);
}
}
@@ -64,5 +64,7 @@ public async Task AppendContext(Identity identity, Dictionary<string, JsonValue>
public Task<Dictionary<string, JsonValue>> GetContext(Identity identity) => _child.GetContext(identity);

public Task RemoveFromContext(Identity identity, string key) => _child.RemoveFromContext(identity, key);

public Task DeleteContext(Identity identity) => _child.DeleteContext(identity);
}
}
@@ -57,6 +57,11 @@ public async Task RemoveFromContext(Identity identity, string key)
var currentContext = _dictionary[identity];
currentContext.Remove(key);
}

public async Task DeleteContext(Identity identity)
{
_dictionary.Remove(identity);
}
}

internal class InMemoryTestDriver : ITestDriver
@@ -124,6 +124,31 @@ paths:
responses:
'200':
description: Ok
delete:
operationId: deleteContext
summary: Delete identity context
description: |
require permissions:
- "context/{identityType}/*" (with limitation based on context policy and context identifier )
tags:
- context
parameters:
- name: identityType
in: path
description: the type of the identity - for example user
required: true
schema:
type: string
- name: identityId
in: path
description: the identifier of the identity - for example jaime
required: true
schema:
type: string
responses:
'200':
description: Ok

'/context/{identityType}/{identityId}/{prop}':
delete:
operationId: deleteContextProperty
@@ -31,6 +31,7 @@
"bluebird": "^3.5.1",
"fast-json-patch": "^2.0.6",
"minio": "^4.0.0",
"node-fetch": "^2.0.0"
"node-fetch": "^2.0.0",
"uuid": "^3.3.2"
}
}
@@ -0,0 +1,33 @@
const uuid = require('uuid/v4');
const { init: initClients } = require('../../utils/clients');

const identityType = 'user';

describe('tweek api - delete context', () => {
let clients;
before(async () => {
clients = await initClients();
});

it('should delete context', async () => {
const identityId = 'delete_context_user';
const url = `/api/v2/context/${identityType}/${identityId}`;

await clients.gateway
.post(url)
.send({ prop: 'value' })
.expect(200);

await clients.gateway.delete(url).expect(200);

await clients.gateway.get(url).expect(200, {});
});

it("should succeed deleting a context that doesn't exist", async () => {
const identityId = uuid();

const url = `/api/v2/context/${identityType}/${identityId}`;

await clients.gateway.delete(url).expect(200);
});
});

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
@@ -43,6 +43,12 @@ public Task RemoveFromContext(Identity identity, string key)
return Task.CompletedTask;
}

public Task DeleteContext(Identity identity)
{
_data.Remove(GetKey(identity));
return Task.CompletedTask;
}

private static Dictionary<string, JsonValue> Merge(Dictionary<string, JsonValue> item, Dictionary<string, JsonValue> context)
{
var result = new Dictionary<string,JsonValue>();
@@ -97,5 +97,13 @@ public async Task GivenMockReadersAndWriters_RemoveFromContextCalled_DoesntThrow
A.CallTo(() => _someOtherFakeDriver.RemoveFromContext(A<Identity>.That.IsEqualTo(_fakeIdentity), A<string>.That.IsEqualTo("some_key"))).MustHaveHappened();
}

[Fact]
public async Task GivenMockReadersAndWriters_DeleteContextCalled_DoesntThrow()
{
Setup();
await _multiContextDriver.DeleteContext(_fakeIdentity);
A.CallTo(() => _someFakeDriver.DeleteContext(A<Identity>.That.IsEqualTo(_fakeIdentity))).MustHaveHappened();
A.CallTo(() => _someOtherFakeDriver.DeleteContext(A<Identity>.That.IsEqualTo(_fakeIdentity))).MustHaveHappened();
}
}
}
@@ -77,6 +77,25 @@ public async Task<ActionResult> DeleteFromContext([FromRoute] string identityTyp
return Ok();
}

/// <summary>
/// Delete identity from context db
/// </summary>
/// <param name="identityType">the type of the identity - for example user</param>
/// <param name="identityId">the identifier of the identity to delete - for example jaime</param>
/// <returns>Result status</returns>
/// <response code="200">Success</response>
/// <response code="403">Access denied</response>
[HttpDelete("{identityType}/{identityId}")]
[ProducesResponseType(typeof(void), (int)HttpStatusCode.OK)]
[ProducesResponseType(typeof(void), (int)HttpStatusCode.Forbidden)]
public async Task<ActionResult> DeleteContext([FromRoute] string identityType, [FromRoute] string identityId)
{
if (!_checkAccess(User, new Identity(identityType, identityId))) return Forbid();
var identity = new Identity(identityType, identityId);
await _contextDriver.DeleteContext(identity);
return Ok();
}

[HttpGet("{identityType}/{identityId}")]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task<ActionResult> GetContext([FromRoute] string identityType, [FromRoute] string identityId)
@@ -17,6 +17,7 @@ public class TimedContextDriver : IContextDriver
private readonly TimerOptions _getContextTimer;
private readonly TimerOptions _appendContextTimer;
private readonly TimerOptions _removeContextTimer;
private readonly TimerOptions _deleteTimer;

public TimedContextDriver(IContextDriver contextDriver, IMetrics metrics, string timerContext = "ContextDriver")
{
@@ -25,6 +26,7 @@ public TimedContextDriver(IContextDriver contextDriver, IMetrics metrics, string
_getContextTimer = timerContext.GetTimer("Get");
_appendContextTimer = timerContext.GetTimer("Append");
_removeContextTimer = timerContext.GetTimer("Remove");
_deleteTimer = timerContext.GetTimer("Delete");
}

public async Task<Dictionary<string, JsonValue>> GetContext(Identity identity)
@@ -50,5 +52,13 @@ public async Task RemoveFromContext(Identity identity, string key)
await _contextDriver.RemoveFromContext(identity, key);
}
}

public async Task DeleteContext(Identity identity)
{
using (_metrics.Measure.Timer.Time(_deleteTimer))
{
await _contextDriver.DeleteContext(identity);
}
}
}
}
@@ -59,5 +59,10 @@ public async Task RemoveFromContext(Identity identity, string key)
{
await Task.WhenAll(_writers.Select(contextDriver => contextDriver.RemoveFromContext(identity, key)));
}

public async Task DeleteContext(Identity identity)
{
await Task.WhenAll(_writers.Select(contextDriver => contextDriver.DeleteContext(identity)));
}
}
}

0 comments on commit 5f412bc

Please sign in to comment.