Skip to content

Prevent attributes from being serialized #297

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
jaredcnance opened this issue Jun 11, 2018 · 3 comments
Closed

Prevent attributes from being serialized #297

jaredcnance opened this issue Jun 11, 2018 · 3 comments

Comments

@jaredcnance
Copy link
Contributor

From @rtablada:

For some models it would be helpful to be able to define which attrs are/aren't used for serializing vs deserializing. [...] Ideally we should be able to declare/override which attrs are used for fetch/create/update operations similar to JSON API resources http://jsonapi-resources.com/v0.9/guide/resources.html#Fetchable-Attributes (we probably can't reproduce this exact API since JADNC uses model decorators instead of a separate resource class)

Proposal

public UserAttributeFilter : IAttributeFilter<User> 
{
    // exclude the User.Password attribute from serialization
    public HashSet<AttrAttribute> ShouldInclude(HashSet<AttrAttribute> attributes)
      => return attributes
            .Where(attr => attr.InternalAttributeName != nameof(User.Password))
            .ToHashSet();
}
@rtablada
Copy link
Contributor

This could also be used to add attributes for some scenarios too? Or are you thinking all possible attrs have to be declared with Decorators and then the filter is only removing?

I think ShouldInclude could use some naming ❤️since that could be confused with ?include from sideloading.

@rtablada
Copy link
Contributor

Having macros/quick helpers for common situations like "DeserializeOnly", "BlockDeserialize", "SerializeOnly", "BlockSerialize" could be 👍 too

@jaredcnance
Copy link
Contributor Author

jaredcnance commented Jun 11, 2018

I need to think about "adding attributes" a bit...I'm not sure how we could accomplish that without multiple models. I'd be really interested to hear some more details on the helpers you described too.

Another option is to modify #112 to handle this. Not sure if this is a good direction, but one way we could do this is to define the following mapping steps:

  • Input Resource → Entity
  • Entity → Output Resource

Where the input, entity (database model), and output could all be the same model. This would make the service layer interfaces look like:

public interface IResourceService<TEntity> : IResourceService<TEntity, int> {}

public interface IResourceService<TEntity, TId> : IResourceService<TEntity, TEntity, TEntity, TId> {}

public interface IResourceService<TInput, TEntity, TOutput, TId>
{
  Task<TOutput> CreateAsync(TInput entity);
  Task<IEnumerable<TOutput>> GetAsync();
   // ...
}

And you would optionally provide a mapper (but I would expect most mapping to be automatic)

interface IResourceMapper {
    // input → entity
    TEntity Map(TInput input);

    // entity → output
    TOutput Map(TEntity model);
}

This seems to maximize flexibility, but may not have the greatest DX (compared to the previous suggestions). It could also allow us to make the Attr decorators optional and depend on the mapping step instead.

Example of case where input and output might differ and types can be automapped:

class FetchableUser : Identifiable {
  public string Name { get; set; }
}

class User : Identifiable {
  public string Name { get; set; }
  public string Password { get; set; }
}

class UsersController : JsonApiController<User, User, FetchableUser> {
  public PeopleController(
    /* ... */,
    IResourceService<User, User, FetchableUser> resourceService) 
      : base(resourceService)
    { }
}

Another example of when this makes sense is when we need to add something to the response:

POST /queue-jobs HTTP/1.1
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

{
  "data": {
    "type": "queue-jobs",
    "attributes": {
      "taskName": "SyncDevices"
    }
  }
}
HTTP/1.1 202 Accepted
Content-Type: application/vnd.api+json
Content-Location: https://example.com/queue-jobs/5234

{
  "data": {
    "type": "queue-jobs",
    "id": "5234",
    "attributes": {
      "taskName": "SyncDevices",
      "status": "Pending request, waiting other process"
    }
  }
}
public class QueueJob : Identifiable {
  public string TaskName { get; set; }
}

public class QueueJobEntity : Identifiable {
  public string TaskName { get; set; }
  public ProcessingStatus Status { get; set; }
}

public class QueueJobResponse : Identifiable {
  public string TaskName { get; set; }
  public string Status { get; set; }
}


public class QueueJobMapper : IResourceMapper<QueueJob, QueueJobEntity, QueueJobResponse> {
    // input → entity
    public QueueJobEntity Map(QueueJob input) {
       // you could use AutoMapper, but for the sake of the example
       return new QueueJobEntity {
              TaskName = input.TaskName,
              Status = ProcessingStatus.Pending
       };
    }

    // entity → output
    public QueueJobResponse Map(QueueJobEntity model) {
        switch(model.Status) { /* ... */ }
    }
}

public class QueueJobsController : JsonApiController<QueueJob, QueueJobEntity, QueueJobResponse>
{  /* ... * /  }

jaredcnance added a commit that referenced this issue Jul 3, 2018
feat(DocumentBuilder): allow for output attribute filtering
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

2 participants