apiaryio / mson

Markdown Syntax for Object Notation
MIT License
900 stars 180 forks source link

dereference particular attribute of MSON model #52

Open geemus opened 8 years ago

geemus commented 8 years ago

I find myself wanting to just dereference particular attributes of models while working on things, rather than say including the whole model (this probably relates to api-blueprint-rfcs#3). There are two cases, in particular where this is coming up. I tried to see if there was some way to do this I was missing, but didn't have luck finding anything.

I imagine this might look something like Resource[attribute], similar to the array[type] syntax, but I don't really have strong feelings in particular about the specifics.

  1. I'd like to include only a subset as attributes for requests to create . For example, created_at/updated_at and uuid are part of the data model, but are usually assigned rather than passed in the request.
  2. I'd like to be able to refer to a different data models keys, in particular for exposing foreign keys.

In both cases, it is really just a subset (or single) attribute that I want to be able to pull in, rather than duplicating the definition. Both cases mirror how I've done this in pure json-schema via prmd before, fwiw.

Hope that is relatively clear, but do let me know if you have questions or I can further clarify. Thanks!

geemus commented 8 years ago

Seems perhaps the current recommended way might be to specify just the subset I care about as it's own object and include that into the other objects, alla: https://github.com/apiaryio/api-blueprint/blob/master/examples/10.%20Data%20Structures.md

So, for the examples above:

  1. there would be a data structure for just the mutable attributes to use here and then a full data structure that included both mutable and immutable (it gets yet more complicated if the parameters allowed between create/update are different, among other cases).
  2. there could also be an identity data structure for each model which could be included in cases such as this
geemus commented 8 years ago

For reference, here is the path I went down instead. This may be sufficient, just took me a bit to reach it.

FORMAT: 1A
HOST: http://example.com/

# API

API for example purposes

## Thing [/things]

### Create Thing [POST]

+ Request (application/json)

  + Body
    + Attributes (Thing Parameters)

+ Response 201 (application/json)

  + Attributes (Thing)

# Data Structures

## Shared (object)
+ created_at: `2012-01-01T12:00:00Z` (string) - when object was created
+ id: `01234567-89ab-cdef-0123-456789abcdef` (string, fixed) - unique id of object
+ updated_at: `2012-01-01T12:00:00Z` (string) - when object was updated

## Thing (object)
+ Include Shared
+ Include Thing Parameters
+ Include User Reference

## Thing Parameters (object)
+ name: "foo" (string, required) - name for thing

## User Reference(object)

+ user (object) - user that owns this object
  + id: `01234567-89ab-cdef-0123-456789abcdef` (string) - unique id of user
geemus commented 8 years ago

One more example came up as I continued.

  1. re-referencing the object id as a url param, like in /objects/{id}.
zdne commented 8 years ago

Hey @geemus, first of all thank you for this much feedback on various issues! I think this might really help and improve many things!

When it comes to referencing MSON elements on the sub-named type level we still haven't came to a solution. We were considering the JSON path-like approach but at this moment I feel that working with the mixin type could be the way to go.

For the time being your findings are correct - simply split the data into multiple parts (mutable, immutable) and them pull them in using Include. Note with the mixin type you can use type attributes like so - Include (A, required) (all properties pulled from A should be now required.


Note in your example you have a superfluous body section. I believe you wanted to do just

+ Request (application/json)
    + Attributes (Thing Parameters)

instead of

+ Request (application/json)

  + Body
    + Attributes (Thing Parameters)
zdne commented 8 years ago

Brainstorming here. We could come up with something like

## Thing [/things]
### Create Thing [POST]

+ Request (application/json)
    + Attributes (object)
        + Include (Thing.name)
        + Include (Thing.email)

+ Response 201 (application/json)

  + Attributes (Thing)

# Data Structures

## Shared (object)
+ created_at: `2012-01-01T12:00:00Z` (string) - when object was created
+ id: `01234567-89ab-cdef-0123-456789abcdef` (string, fixed) - unique id of object
+ updated_at: `2012-01-01T12:00:00Z` (string) - when object was updated

## Thing (object)
+ name: "foo" (string, required) - name for thing
+ email: "z@foo.com"
+ Include User Reference
+ Include Shared

## User Reference(object)

+ user (object) - user that owns this object
  + id: `01234567-89ab-cdef-0123-456789abcdef` (string) - unique id of user

Or even

+ Request (application/json)
    + Attributes (object)
        + Include (Thing, explicit)
                + name
                + email

Where explicit would mean that nothing is pulled in by default but you have to specify what properties to pull in.

Question is whether these approaches are worth exploring if there is some solution available...

Thoughts?

geemus commented 8 years ago

I could see either of those working, though I think I like the first include version a bit better than the one utilizing the explicit keyword. Another option I was thinking about was something like a singular version of Attributes. So maybe something like:

+ Request (application/json)
    + Attributes (object)
        + Attribute (Thing.name)

I had originally thought something like Attribute(Thing, name), but the dot syntax you suggest does seem cleaner. I don't think this really gains much over your version, and has the negative of adding a new keyword (though I suppose that should be balanced against adding more meaning/complexity to an existing keyword).

Can Attributes occur more than once in this context? Just wondering if allowing attributes to have a narrower scope might be good. Something like:

+ Request (application/json)
    + Attributes (Thing.email)
    + Attributes (Thing.name)

Kind of thinking "aloud" so to speak here. Seems like there are some positives/negatives on all of them and I'm not familiar enough with everything to be confident a couple of the options are even valid.

Thoughts?

Perni1984 commented 8 years ago

running into the same issue here - basically solving it with @geemus suggestion by splitting the data structures.

In contrary to what has been discussed here, repeating nearly a whole model in the request attributes, just for the sake of dereferencing id, created_at, updated_at doesn't look very DRY to me.

I would propose something like that:

+ Request (application/json)
    + Attributes (Thing)
        ~ id
        ~ created_at
        ~ updated_at
Perni1984 commented 8 years ago

btw. this seems like a duplicate of #25.

geemus commented 8 years ago

@Perni1984 that makes sense. I think with your suggestion and #25 are both slightly different than what I was talking about (though ideally this could support both). In particular I was looking to add things in a whitelist kind of way where I add each attribute individually, whereas I think you and #25 want more of a blacklist where all attributes are there by default and you can subtract the ones you do not want. Would that be an accurate assessment?

Perni1984 commented 8 years ago

@geemus: this is an accurate assessment. I must have misread your post.

geemus commented 8 years ago

No worries, I may have been more vague/unclear than I should have been. Figured this was a good time to try and be clear though.

phumke commented 8 years ago

I have to be a