diff --git a/aep/general/0003/aep.md b/aep/general/0003/aep.md index f0415d16..9a0669be 100644 --- a/aep/general/0003/aep.md +++ b/aep/general/0003/aep.md @@ -11,6 +11,8 @@ The following terminology **must** be used consistently throughout AEPs. Application Programming Interface. This can be a local interface (such as an SDK) or a Network API (defined below). +APIs define one or more operations upon resource types. + ### API Backend A set of servers and related infrastructure that implements the business logic @@ -33,24 +35,28 @@ Examples of clients include the following: ### API Definition -A well-structured representation of an API Service. +A well-structured representation of an API. ### API Endpoint -Refers to a network address that an API Service uses to handle incoming API -Requests. One API Service may have multiple API Service Endpoints, such as -`https://pubsub.example.com` and `https://content-pubsub.example.com`. +Refers to a network address that an API uses to handle incoming requests. One +API may have multiple endpoints, such as `https://pubsub.example.com` and +`https://content-pubsub.example.com`. ### API Gateway One or more services that together provide common functionality across API -Services, such as load balancing and authentication. +services, such as load balancing and authentication. ### API Method An individual operation within an API. It is typically represented in Protocol Buffers by an `rpc` definition, or in HTTP via a `method` and a `path`. +### API Name + +The name by which to refer to an API. + ### API Request A single invocation of an API Method. It is often used as the unit for billing, @@ -58,11 +64,16 @@ logging, monitoring, and rate limiting. ### API Resource -An object upon which one or more API methods operate. +An entity upon which one or more methods can operate. ### API Resource Type -An API resource type represents a category of that consumes and API, +The type of a API resource. It is globally unique within an API. + +### API Service + +An implementation of an API, exposing API methods on one or more network +addresses. ### Consumer diff --git a/aep/general/0004/aep.md b/aep/general/0004/aep.md new file mode 100644 index 00000000..5f8641e5 --- /dev/null +++ b/aep/general/0004/aep.md @@ -0,0 +1,138 @@ +# Resource types + +Most APIs expose _resources_ (their primary nouns) which users are able to +create, retrieve, and manipulate. APIs are allowed to name their resource types +as they see fit, and are only required to ensure uniqueness within that API. +This means that it is possible (and often desirable) for different APIs to use +the same type name. For example, a Memcache and Redis API would both want to +use `Instance` as a type name. + +When mapping the relationships between APIs and their resources, however, it +becomes important to have a single, globally-unique type name. Additionally, +tools such as Kubernetes or GraphQL interact with APIs from multiple providers. + +## Guidance + +APIs **must** define a resource type for each resource in the API, according to +the following pattern: `{API Name}/{Type Name}`. The type name: + +- **must** Only contain ASCII alphanumeric characters. +- **must** Start with a lowercase letter. +- **must** Be of the singular form of the noun. +- **must** Use kebab case. +- For Kubernetes, the type name when converted to UpperCamelCase **must** match + the [object][] name. +- For OpenAPI, the type name when converted to UpperCamelCase **must** match + the name of the schema representing the object. +- For protobuf, the type name when converted to UpperCamelCase **must** match + the name of the protobuf message. + +### Examples + +Examples of resource types include: + +- `networking.istio.io/instance` +- `pubsub.example.com/topic` +- `pubsub.example.com/subscription` +- `spanner.example.com/database` +- `spanner.example.com/instance` +- `apis.example.com/user/user-event` + +### Annotating resource types + +APIs **must** annotate the resource types for each resource in the API + +{% tab proto %} + +For protobuf, use the [`google.api.resource`][resource] annotation: + +```proto +// A representation of a user event. +message Topic { + option (google.api.resource) = { + type: "user.example.com/user-event" + singular: "user-event" + plural: "user-events" + // define one or more patterns, e.g. if a resource has more than one parent. + pattern: "projects/{project}/user-events/{user-event}" + pattern: "folder/{folder}/user-events/{user-event}" + pattern: "users/{user}/events/{user-event}" + }; + + // Name and other fields... +``` + +{% tab oas %} + +For OpenAPI 3.0, use the `x-aep-resource` extension: + +```json +{ + "type": "object", + "x-aep-resource": { + "singular": "user-event", + "plural": "user-events", + "patterns": [ + "projects/{project}/user-events/{user-event}", + "folder/{folder}/user-events/{user-event}", + "users/{user}/events/{user-event}" + ] + } +} +``` + +{% endtabs %} + +- The `singular` field **must** be the kebab-case singular type name. +- The `plural` field **must** be the kebab-case plural of the singular. + +The `pattern` field **must** match the `pattern` rule in the following grammar, +expressed as [EBNF][EBNF]: + +```ebnf +pattern = element, { "/", element }; +element = variable | literal; +variable = "{", literal, "}"; +``` + +Where `literal` matches the regex `[a-z][a-z0-9\-]*[a-z0-9]`. + +- Patterns **must** match the possible [resource paths][resource-paths] of the + resource. +- Pattern variables (the segments within braces) **must** match the singular of + the resource whose id is being matched by that value. + +#### Pattern uniqueness + +If multiple patterns are defined within a resource, the patterns defined **must +not** overlap in the set of resource paths that they can match. In other words, +a resource path may match at most one of the patterns. + +For example, the following two patterns would not be valid for the same +resource: + +- `user/{user}` +- `user/{user_part_1}~{user_part_2}` + +## Rationale + +### Singular and Plural + +Well-defined singular and plurals of a resource enable clients to determine the +proper name to use in code and documentation. + +google.aip.dev uses UpperCamelCase for resource types, while aep.dev uses +kebab-case. This is to enforce better consistency in the representation of +various multipart strings, as collection identifiers use kebab case. + + +[resource-paths]: /resource-paths +[API Group]: https://kubernetes.io/docs/concepts/overview/kubernetes-api/#api-groups +[nested collections]: ./0122.md#collection-identifiers +[Object]: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#types-kinds +[resource]: https://github.com/googleapis/googleapis/blob/master/google/api/resource.proto +[service configuration]: https://github.com/googleapis/googleapis/blob/master/google/api/service.proto +[EBNF]: https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form + + +## Changelog diff --git a/aep/general/0004/aep.yaml b/aep/general/0004/aep.yaml new file mode 100644 index 00000000..8b51a809 --- /dev/null +++ b/aep/general/0004/aep.yaml @@ -0,0 +1,7 @@ +--- +id: 4 +slug: resource-types +state: approved +created: 2023-11-15 +placement: + category: resources diff --git a/common-components/json_schema/x-aep-resource.yaml b/common-components/json_schema/x-aep-resource.yaml new file mode 100644 index 00000000..5ee3ce8b --- /dev/null +++ b/common-components/json_schema/x-aep-resource.yaml @@ -0,0 +1,31 @@ +$schema: https://json-schema.org/draft/2020-12/schema +$id: https://aep.dev/common-components/json_schema/x-aep-resource.yaml +title: AEP Resource +description: | + Describes a resource exposed by an API. +type: object +required: + - type + - singular + - plural + - pattern +additionalProperties: false +properties: + type: + type: string + description: | + A string that describes the resource type. + singular: + type: string + description: | + The singular of the resource. + plural: + type: string + description: | + The plural of the resource. + pattern: + description: | + A list of possible resource paths that are used to identify the resource. + type: array + items: + type: string