LiveRamp / reslang

A language for describing resource-oriented APIs & turning them into Swagger or resource diagrams. Oriented around the concepts we want to expose in the APIs.
Apache License 2.0
23 stars 7 forks source link

Require "at least one of" attributes #10

Open lr-conrs opened 4 years ago

lr-conrs commented 4 years ago

I will explain my need by way of an example. Let's say you have different prices for something, depending on how it is used. For personal use, it is 4 dollars, and for commercial use, it is 4000.

We would like to capture this in a structure. (Apologies for pseudocode, please let me know if it is unclear)

mediaPrice {
    commercial: int 
    personal: int 
}

One option would be to use the result of https://github.com/LiveRamp/reslang/issues/9 and ensure the array has a minimum length. But, we actually lose out on some strictness - what happens if there is more than one commercial price in this array? Good design would require that state to be unrepresentable, which means usage of this solution is a compromise.

Thus, let's put some thought into how we could signal different required constraints.

liveandrew commented 4 years ago

reslang supports anyof through the union keyword

e.g. union MediaPrice { commercial: int personal: int }

in swagger this also requires a type field, but reslang adds this automagically

please see the reference manual here & ask me to add bits if they are not covered. docs probably need some work: https://github.com/LiveRamp/reslang/blob/master/docs/reference.md

liveandrew commented 4 years ago

please reopen if this doesn't meet your requirements or you would propose a different approach

lr-conrs commented 4 years ago

Please correct me if I'm still misinterpreting, but unions would be a good use-case for if we only wanted one of the two possible members - e.g. our price is either commercial, or personal, but we never have both.

The challenge above is capturing the case where we do indeed want to allow both, but also want to make sure at least one is around.

liveandrew commented 4 years ago

hmm i guess that's the oneof thing in swagger? do you have a business requirement for this or is this a nice to have thing? can you help out by showing your structure - is it the media price thing?

lr-conrs commented 4 years ago

Sorry, failed to reply here.

Scenario: We are an API for people to buy Segments through. Thus, every Segment in our system has at least one price.

Data:

price { 
  cost_per_click,
  cost_per_mille,
  tv_price,
  rev_share
}

There are different prices for different uses, just as above in the commercial versus personal example. Some Segments have multiple prices, as they can be used on multiple platforms, for example, on Google, we'd use cost_per_mille, but we're also going to go ahead and target these people on tv platforms, which pay the tv_price.

So, we know we need at least one price. However, we can't use the array approach, as nothing would prevent a segment from having more than one tv_price, which doesn't make any sense.

In order to make as unambigous an API as possible, having this would be great. The cost of not having it is client and server will have to manually validate against this case on top of checking against a schema.

liveandrew commented 4 years ago

fair enough. i'll add it - or you can help me add it per our discussions on wed.

constraints can help here, but there's no constraint language that can do everything. you'll always need to defensively implement server-side logic to check regardless, and that covers a huge number of use cases. you can't express "cost_per_click" must be greater than "cost_per_mille" unless the "tv_price" is set. well actually you can, but that's basically a full expression language like UML/OCL. and we all know how well that ended up ;-P

liveandrew commented 4 years ago

as an aside, if the server implements defensively, you don't need to have client side logic. that's my view...

lr-conrs commented 4 years ago

constraints can help here, but there's no constraint language that can do everything. Absolutely 😂

My thinking on the schemaside is to constrain as much as possible, but worry mostly about structural ambiguities. Sometimes by being clever with structures, you can eliminate situations you'd otherwise have to validate, similar in ways to solving a problem functionally rather than imperatively, or playing with type algebra. It quickly becomes a rabbit hole, though.

I think of the schema like one would think about grammar in English. The code still has to try and understand the meaning - and it might be gibberish - but it can rely on the structure being sound.

as an aside, if the server implements defensively, you don't need to have client side logic.

Yeah, I mean chances are a client won't bother running every response we give them through a schema checker, but they will likely have code/objects they generate based off it which is where they could get kind of silly. If we're saying one thing, but our structure allows another, it just makes it harder for them to write a robust deserializer.

Of course, if they're in a dynamic language, they probably won't do any of the above, but it's nice to leave the door open for people to be as strict as they can.