How to search a non-primary attribute from DynamoDB with Spring Reactive (Webflux)? - spring-webflux

Suppose I have user table with fields userId, userName, password and some other fields where userId is the partition key. How could I write a query to find something from userName and not from userId? I am working with Spring webflux, so it also does not have the ReactiveCrudRepository for DynamoDb. My User table is something like this:
#Data
#NoArgsConstructor
#AllArgsConstructor
#DynamoDbBean
public class User {
private String userId;
private String userName;
private String password;
private String firstName;
private String lastName;
private String timestamp;
#DynamoDbPartitionKey
public String getUserId() {
return userId;
}
}
My repo class is:
#Repository
public class UserRepo {
private DynamoDbAsyncTable<User> userDynamoDbAsyncTable;
public UserRepo(DynamoDbAsyncTable<User> userDynamoDbAsyncTable) {
this.userDynamoDbAsyncTable = userDynamoDbAsyncTable;
}
// CREATE
public CompletableFuture<Void> save(User user) {
return userDynamoDbAsyncTable.putItem(user);
}
// READ
public CompletableFuture<User> getUserByID(String userId) {
return userDynamoDbAsyncTable.getItem(getKeyBuild(userId));
}
// UPDATE
public CompletableFuture<User> updateUser(User user) {
return userDynamoDbAsyncTable.updateItem(user);
}
// DELETE
public CompletableFuture<User> deleteUserById(String userId) {
return userDynamoDbAsyncTable.deleteItem(getKeyBuild(userId));
}
// GET_ALL_ITEM
public PagePublisher<User> getAllUser() {
return userDynamoDbAsyncTable.scan();
}
private Key getKeyBuild(String userId) {
return Key.builder().partitionValue(userId).build();
}
}
Is there any way to query database something like findByUserName("John") which returns the User object with "John" as a username?

You can't query without a partition key, if you have the partition key you can combine that with a sortKey, otherwise you will need to make your userName field a sortKey in Dynamo and use a GlobalSecondaryIndex to be able to search by userName alone.
#DynamoDbSortKey
public String getUserName() {
return serName;
}
You could use table.scan() but that's not recommended, the recommended way is to use a partition key + sortKey or sortKey + GlobalSecondaryIndex with the table method table.query().

Related

How to use ProtectedPersonalData attribute

I found the attribute class, ProtectedPersonalData (link), of ASP.NET Core Identity framework, but I can't seem to find any documentation on how to use it.
The documentation only says: Used to indicate that a something is considered personal data and should be protected.
At the end, I was able to encrypt the Identity User class fields (link) (e.g. email field), but not any property of an Identity User inheriting class.
public class ApplicationUser : IdentityUser {
[ProtectedPersonalData]
public string MyProperty { get; set; }
}
I added this to the Identity Config:
services.AddDefaultIdentity<ApplicationUser>(options => {
options.Stores.ProtectPersonalData = true;
})
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
Moreover, I implemented protector classes:
public class Lookup : ILookupProtector {
public string Protect(string keyId, string data) {
return new string(data?.Reverse().ToArray());
}
public string Unprotect(string keyId, string data) {
return new string(data?.Reverse().ToArray());
}
}
public class Protector : IPersonalDataProtector {
public string Protect(string data) {
return new string(data?.Reverse().ToArray());
}
public string Unprotect(string data) {
return new string(data?.Reverse().ToArray());
}
}
public class KeyRing : ILookupProtectorKeyRing {
public string this[string keyId] => "key";
public string CurrentKeyId => "key";
public IEnumerable<string> GetAllKeyIds() {
return new string[] { "key" };
}
}
It is possible to encrypt MyProperty field?
Please point me to information or provide some examples please.
Update:
I noticed that the code is never entering inside the Protect method for property MyProperty.
You need to add data annotation to the attribute qualifying as PersonalData, like this:
[ProtectedPersonalData]
[PersonalData]
public string Firstname { get; set; }
[ProtectedPersonalData]
[PersonalData]
public string Lastname { get; set; }
in order to activate the process you need register the services in your Startup.cs:
// ProtectedData
services.AddScoped<ILookupProtectorKeyRing, KeyRing>();
services.AddScoped<ILookupProtector, LookupProtector>();
services.AddScoped<IPersonalDataProtector, PersonalDataProtector>();
Example Repository
Here you can find an example repository with a project Blazor WASM with Microsoft Identity accounts and ProtectedData implementation.
https://github.com/nbiada/protecteddata-wasm-example

Jackson - Deserialize with JsonView

I am trying to restrict which properties from a JSON object are deserialised using Jackson JSONViews. The aim is to use this to prevent consumers of my API from submitting data that they shouldn't.
The problem is, I have either misunderstood JSONViews or I am doing something wrong. See below.
I started trying to do this in Spring but have noticed that even the simple test below doesn't work.
Account Class
public class Account {
#Id
private String id;
private String name;
private List<String> items;
private List<User> users;
#JsonView(AccountViews.Private.class)
public void setId(String id) {
this.id = id;
}
#JsonView(AccountViews.Public.class)
public void setName(String name) {
this.name = name;
}
#JsonView(AccountViews.Public.class)
public void setItems(List<String> items) {
this.items = items;
}
#JsonView(AccountViews.Private.class)
public void setUsers(List<User> users) {
this.users = users;
}
}
Views
public class AccountViews {
public interface Public {}
public interface Private extends Public {}
}
Test
#Test
public void testDeserialization(){
ObjectMapper mapper = new ObjectMapper();
mapper.disable(MapperFeature.DEFAULT_VIEW_INCLUSION);
Account account = mapper.readerWithView(AccountViews.Public.class).forType(Account.class).readValue("{ \"name\": \"account1\", \"items\": [\"item1\"], \"users\": [ { \"firstname\": \"user1_firstname\", \"lastname\": \"user1_lastname\" }] }");
assertEquals(account.getName(), "account1");
assertNull(account.getUsers());
}
Unforunately, the 2nd assertion fails because Users has a user object inside.
Basically, even though "users" is a property of Account, I don't want the value to be deserialized because I have used the JSONView (AccountViews.Public.class). However, whatever I try it always seems to be deserialized and is present on the account object.
Any help much appreciated.
Error
`java.lang.AssertionError: expected null, but was:<[User#609db43b]>
at org.junit.Assert.fail(Assert.java:88)
at org.junit.Assert.failNotNull(Assert.java:755)
at org.junit.Assert.assertNull(Assert.java:737)
at org.junit.Assert.assertNull(Assert.java:747)
at`

how do we return all attributes of a node with neo4jclient?

below code(search function) works fine.
public class BookItem
{
public string Title { get; set; }
public string OriginalTitle { get; set; }
}
public IEnumerable<dynamic> Search(string keyword)
{
/*MATCH (n:`Book`) RETURN n*/
var query = client
.Cypher
.Match("(n:Book)")
.Return(n => n.As<BookItem>());
return query.Results;
}
However, i don't want to declare a class like BookItem. I just want all results in a dynamic object. Is there a way to do that?
For example below code runs and returns empty object, it doesn't return any attributes..
public IEnumerable<dynamic> Search(string keyword)
{
/*MATCH (n:`Book`) RETURN n*/
var query = client
.Cypher
.Match("(n:Book)")
.Return(n => n.As<dynamic>());
return query.Results;
}
The basic gist is in the answer to this question: Casting nodes of an unknown type
What you end up returning is Node<string> and parsing using Json.net into a dynamic object, there is no direct way of just doing x.As<dynamic>() unfortunately.

AutoMapper Update Actions in ASP.NET MVC

This is probably quite straight forward for some, however I'm a bit confused and can't find a decent example. Say I'm using view models and my POST action takes in that view model. Typically I would do something along the following lines:
[HttpPost]
public ActionResult Update(UserViewModel uvm)
{
User user = Mapper.Map<UserViewModel, User>(uvm);
_repository.Update(user);
return RedirectToAction("Index");
}
Although this isn't the full picture. The mapping would work fine, however if I were to just update what I've mapped then it'd get rid of valuable data in the database because of course in this case I'm not updating the password or other details.
My repository looks something like this:
public void Update(User user)
{
User u = Session.QueryOver<User>().Where(x => x.UserName == user.UserName).SingleOrDefault();
if (u == null)
throw new Exception("User not found");
u.Forename = user.Forename;
u.Surname = user.Surname;
u.EmailAddress = user.EmailAddress;
}
[I'm using NHibernate so it'll save the object back to the DB once the session is closed (after the request has finished) automatically for me.]
So my question is, in my repository should I load the "User" entity, then update the values I want, and then save it back, or is there another method to do this? The reason I ask is because it seems a bit... "manual" if you see what I mean? Perhaps it is correct, but I just wanted to see opinions of those with more experience in this area.
Cheers
I use the following approach:
[HttpPost]
public ActionResult Update(UserViewModel uvm)
{
User user = _userRepository.FindById(uvm.Id);
user.Forename = uvm.Forename;
user.Surname = uvm.Surname;
user.EmailAddress = uvm.EmailAddress;
_userRepository.Update(user);
return RedirectToAction("Index");
}
UPDATE:
To address the comments about AutoMapper here's how to proceed:
Let's take for example the following classes:
public class UserViewModel
{
public string Forename { get; set; }
public string Surname { get; set; }
public string EmailAddress { get; set; }
}
public class User
{
public string Forename { get; set; }
public string Surname { get; set; }
public string EmailAddress { get; set; }
public string Password { get; set; }
}
We don't want to modify the user password in the UI. So we express our intention to AutoMapper:
Mapper
.CreateMap<UserViewModel, User>()
.ForMember(dest => dest.Password, opt => opt.Ignore());
and then:
[HttpPost]
public ActionResult Update(UserViewModel uvm)
{
// Fetch the original model we would like to update
User user = _userRepository.FindById(uvm.Id);
Mapper.Map(uvm, user);
// At this stage the user model will have its
// Forename, Surname and EmailAddress properties
// updated from the view model and its Password property
// will remain the one we got from the repository
_userRepository.Update(user);
return RedirectToAction("Index");
}
UPDATE 2:
To address the question in the comments about configuring AutoMapper I usually use Profiles:
public class UsersProfile : Profile
{
protected override void Configure()
{
Mapper
.CreateMap<UserViewModel, User>()
.ForMember(dest => dest.Password, opt => opt.Ignore());
Mapper
.CreateMap<User, UserViewModel>();
}
}
and then have a registry class which registers all the mappers:
public class MappingsRegistry
{
public static void Configure()
{
Mapper.AddProfile(new UsersProfile());
Mapper.AddProfile(new SomeOtherProfile());
...
}
}
which is called in Application_Start:
MappingsRegistry.Configure();
Finally my controllers have a reference to the mapping engine:
public class UsersController : Controller
{
private readonly IUsersRepository _repository;
private readonly IMappingEngine _mappingEngine;
public ContratsFCController(IUsersRepository repository, IMappingEngine mapperEngine)
{
_repository = repository;
_mapperEngine = mapperEngine;
}
[AutoMap(typeof(User), typeof(UserViewModel))]
public ActionResult Update(int id)
{
var user = _repository.FindById(id);
return View(user);
}
[HttpPost]
public ActionResult Update(UserViewModel uvm)
{
if (!ModelState.IsValid)
{
return View(uvm);
}
var user = _repository.FindById(uvm.Id);
_mapperEngine.Map(uvm, user);
_repository.Update(user);
return RedirectToAction("Index");
}
}
Now all that's left is to instruct your DI framework to pass the Mapper.Engine property to the constructor and in your unit tests obviously substitute them with an appropriate mock.

Delete one row from the database

I'm having a problem deleting just one row. I can insert and delete the whole table. I'm not able to understand the whole ID thing in order to delete just one row. I was looking at some examples, but I couldn't get it. It’s driving me crazy.
Here is the SQLite class;
public class datahelper {
private static final String DATABASE_NAME = "table.db";
private static final int DATABASE_VERSION = 1;
private static final String TABLE_NAME = "table1";
private Context context;
private SQLiteDatabase db;
private SQLiteStatement insertStmt;
private static final String INSERT =
"insert into " + TABLE_NAME + "(name) values (?)";
public datahelper(Context context) {
this.context = context;
OpenHelper openHelper = new OpenHelper(this.context);
this.db = openHelper.getWritableDatabase();
this.insertStmt = this.db.compileStatement(INSERT);
}
public long insert(String name) {
this.insertStmt.bindString(1, name);
return this.insertStmt.executeInsert();
}
public long insert2(String name) {
this.insertStmt2.bindString(1, name);
return this.insertStmt2.executeInsert();
}
public void deleteAll() {
this.db.delete(TABLE_NAME, null, null);
}
private static class OpenHelper extends SQLiteOpenHelper {
OpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + TABLE_NAME +
" (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)");
}
}
Execute the query
DELETE FROM TABLE_NAME WHERE id = SOMEVALUE
It looks like you are using this API, which provides this delete method. My guess is that you would do this:
public void delete(int id) {
this.db.delete(TABLE_NAME, 'id = ?', new String[] { id.toString() });
}
(Original answer...)
Use a DELETE statement with a WHERE clause that deletes only the row with the id you want to remove:
DELETE FROM <tablename> WHERE id = ?
Of course, you need to know the id in order to do this. SQLite provides a function — sqlite3_last_insert_rowid() — that you can call immediately after an INSERT. If your API doesn't provide this function directly, you can get it indirectly via the equivalent SQL function:
SELECT last_insert_rowid()
Alernatively if you want to delete a certain name (assuming it's unique):
DELETE FROM <tablename> WHERE name = ?