Skip to content

Commit

Permalink
Define common components for oneof parity across protobuf and OAS.
Browse files Browse the repository at this point in the history
  • Loading branch information
rofrankel committed Jul 29, 2024
1 parent c625048 commit d127a56
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 0 deletions.
21 changes: 21 additions & 0 deletions json_schema/oneof.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
$schema: https://json-schema.org/draft/2020-12/schema
$id: https://aep.dev/component.oneof.json
title: x-aep-oneof
description: |
Represents a mutual exclusion constraint over multiple properties.
It is an error for any properties specified by a oneof to be required.
type: object
required: [properties]
additionalProperties: false
properties:
properties:
description: |
A set of properties that are mutually exclusive.
type: array
items:
type: string
required:
description: |
If set, exactly one property from the `properties` array must be present on the object.
type: string
21 changes: 21 additions & 0 deletions proto/aep-api/aep/api/oneof_behavior.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
syntax = "proto3";

package aep.api;

option cc_enable_arenas = true;
option go_package = "aep.dev/api";
option java_multiple_files = true;
option java_outer_classname = "OneOfBehaviorProto";
option java_package = "dev.aep.api";
option objc_class_prefix = "AEP";

import "google/protobuf/descriptor.proto";

// Used to specify the behavior of a `oneof`.
message OneOfBehavior {
// If set, one field within the `oneof` must be set.
//
// When unset, the `oneof` is implicitly optional (the default behavior in
// protobuf).
bool required = 1;
}
65 changes: 65 additions & 0 deletions schemas/oneof.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Oneof

As used by the [AEPs](http://aep.dev), a `oneof` represents a set of mutually
exclusive fields.

## Schema

Rather than a generic schema for `oneof`, we provide common components
supplementing the language features of specific IDLs. Using these enables
uniform behavior (and JSON serialization) across IDLs.

### protobuf

Protobuf's built-in `oneof` supports mutual exclusion of fields. However, it
does not have a way for an API indicate that at least one of the fields must be
set. For this purpose, a protobuf `oneof` may be annotated with
[`aep.api.OneOfBehavior`](../proto/aep-api/aep/api/oneof_behavior.proto):

```proto
message Document {
// ...
// The owner of the document.
oneof owner {
// The user who owns the document.
string user = 1 [(google.api.resource_reference) = {
type: "apis.example.com/User",
}];
// The group that owns the document.
string group = 2 [(google.api.resource_reference) = {
type: "apis.example.com/Group",
}];
} [(aep.api.OneOfBehavior) = {required: true}];
}
```

### OAS

OAS 3 has a concept called `oneOf`, but it has different semantics than the
`oneof` defined by this document; the OAS concept says that a single field must
match exactly one of multiple possible schemas, rather than specifying mutual
exclusion over multiple properties.

In order to represent a `oneof` in OAS, in a way that produces JSON equivalent
to that of protobuf, we define an extension `x-aep-oneof`:

```yaml
components:
schemas:
Document:
type: object
properties:
user: { type: string }
group: { type: string }
x-aep-oneof:
name: owner
properties: [user, group]
required: true
```
The `x-aep-oneof` extension is used to indicate that at least one of the fields
in the `oneOf` array must be set.

**Note:** OAS extension not yet defined.

0 comments on commit d127a56

Please sign in to comment.