raml-spec icon indicating copy to clipboard operation
raml-spec copied to clipboard

Define 'readonly', 'writeonly' attributes for DataType parameters

Open Skorpyon opened this issue 8 years ago • 12 comments

Is It any way how define readonly writeonly parameters in Models?

Basic life example is Model that have id field that included it instance representation, but should excluded from PUT and PATCH requests.

Skorpyon avatar Mar 09 '17 21:03 Skorpyon

I have the same. Want to indicate

  1. attributes which should be excluded from PUT and PATCH (readonly)
  2. attributes which should be excluded from GET (writeonly)

Ideally I can also indicate readoptional, writeoptional, createoptional

goergen avatar May 05 '17 09:05 goergen

I also would like to see a readonly facet for a property declaration.

NSchoemaker avatar Jul 20 '17 14:07 NSchoemaker

I already return to Swagger, so it is not very important for me more. Issues in RAML closing so "fast", so, thank you, no need more.

Skorpyon avatar Jul 20 '17 14:07 Skorpyon

@Skorpyon I am sorry that you feel that way! I hope you understand that these issues can't be closed fast. There is an entire life cycle in between. As an example, between Swagger 2.0 and OAS 3.0 was a very long time as well.

But I understand your decision.

sichvoge avatar Jul 20 '17 14:07 sichvoge

Is there any workaround for this by now?

Jeehut avatar Feb 08 '18 15:02 Jeehut

I have the same question, I've posted a question on the RAML forum this is a bit wider in scope, but stems from this issue: https://forum.raml.org/t/the-state-of-raml/2149

p-bakker avatar Feb 28 '18 07:02 p-bakker

I am not sure if this will ever be part of the RAML spec but to be clear, the spec already provides for a distinction between properties that are readable and properties that are writable.

This is a blog post that explains how it works: https://medium.com/raml-api/using-raml-inheritance-to-design-better-schemas-9ce75155df7c

The bottom line is that RAML has a powerful type system that can be leveraged to re-use schemas or pieces of schemas across requests and responses. IMHO, this makes for a far more readable API definition because as a human, I can tell at first glance which properties any given request or response takes or returns. And this, without having to think whether I am in a "read" or "write" context.

jstoiko avatar Mar 10 '18 00:03 jstoiko

Although I tend to agree with Jonathan that through inheritance you are able to reflect those needs, I think inlining properties might not work for every case and will probably introduce a lot of repetitive code. RAML was designed to minimize that.

For example, assume you have more than just the id field that needs to be put for every GET method. Inlining would mean that you need to put these properties in each, like that:

#%RAML 1.0
title: Invoice API
mediaType: application/json

types:
  Invoice:
    properties:
      amount:
        type: number
        minimum: 0
      billTo:

/invoices:
  get:
    responses:
      200:
        body:
          type: Invoice
          properties:
            id: number
            bla: string
            blabla: string
  post:
    body:
      type: Invoice
/x:
  get:
    responses:
      200:
        body:
          type: Invoice
          properties:
            id: number
            bla: string
            blabla: string
/y:
  get:
    responses:
      200:
        body:
          type: Invoice
          properties:
            id: number
            bla: string
            blabla: string

This is very common.

Now, assume you need to add a new property. Furthermore, how can you make sure to have this really added to all GET methods in your entire API document?

One option might be to move out stuff that is different in a specific method into its own type and inherit from a base type. But I am not sure if that's the best solution either.

Only my 2 cents on this topic. :)

Otherwise, Jonathan is right of course.

sichvoge avatar Mar 24 '18 07:03 sichvoge

@sichvoge Did you know you can inherit from multiple types in RAML?

Using your example, you could do this:

#%RAML 1.0
title: Invoice API
mediaType: application/json

types:
  BlaObject:
    properties:
      id: number
      bla: string
      blabla: string
  Invoice:
    properties:
      amount:
        type: number
        minimum: 0
      billTo:

/invoices:
  get:
    responses:
      200:
        body:
          type: [ Invoice, BlaObject ]
  post:
    body:
      type: Invoice
/x:
  get:
    responses:
      200:
        body:
          type: [ Invoice, BlaObject ]
/y:
  get:
    responses:
      200:
        body:
          type: [ Invoice, BlaObject ]

Note that I placed BlaObject after Invoice because I want the fields of BlaObject to appear before the fields of Invoice. I don't know why it's reversed like this, but it was (at least using raml2html).

Read more about multiple inheritance in the docs.

Jeehut avatar Mar 26 '18 08:03 Jeehut

Hi @Dschee, thanks for the note and yes I know that very well. Have been in the team writing the spec ;)

sichvoge avatar Mar 26 '18 13:03 sichvoge

The above serve as workarounds, but there's an added complexity here. If you go down the path of in-lining additions to the types in the endpoints, you end up with garbage when you generate code, as there is no name associated with the type you are dynamically creating on the fly. The end result is poor usability for your end users. Additionally, the fields only show up in the API Endpoint, not in the type, if you are just viewing the type. Given the above example, if I look at the Invoice type, I don't see all properties that are associated with an invoice, as some are type specific.

On the other hand, if I create types for all of these possible combinations, you end up with "type pollution". There are too many types that are too similar to each other and become difficult to ensure you are using the proper ones.

Finally, if you are a provider of APIs, then you run in to the problem that you may be creating APIs for users to extend, in which case, defining a read only property at the base type level a) gives you the ability to ensure it is added in all appropriate end points and b) gives you an opportunity to validate customer extensions of your API.

FWIW: a better possible implementation would be to have additional properties on a property, similar to min/max, that would restrict the HTTP methods it would be allowed under, or the ability to override and mask a set of properties in an endpoint.

rspremulli-sf avatar Jul 03 '21 19:07 rspremulli-sf

Actually, another use case for this requirement. Having properties that are defined as immutable and computed. Immutable properties are properties that, once set, cannot be modified. An example of this might be a type enum, where a type would be incompatible with other types so once set, the resouce cannot be changed to a different type of resource. We have this for coupon codes in our system. We have two variants: one where the user supplies the coupon codes, and another where the supply a prefix and a number and the system generates the codes. When the coupon resource is created, the user specifies the type, but due to the different properties and configurations for each type, the user is not permitted to change it.

A computed property is essentially the same as read only one. It would never be allowed in a body, but can always show up in a response. An example of this might be a computed total, or metadata information recorded onto to the resource by the endpoint to identify who made a change and when

rspremulli-sf avatar Jul 03 '21 20:07 rspremulli-sf