# A MetadataProvider is one account with one metadata provider, in the
# namespace of the libraries that name it. It holds the Secret for its
# key and states the facts it may serve. A Library's spec.sources
# orders the providers it asks. The operator checks each provider once
# per pass with one cheap call and reports the answer in its status.
# The key reaches an enricher container through a secretKeyRef, and
# never through the catalog or a status.
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: metadataproviders.library.liken.sh
spec:
  group: library.liken.sh
  names:
    kind: MetadataProvider
    listKind: MetadataProviderList
    plural: metadataproviders
    singular: metadataprovider
    categories: [media]
  scope: Namespaced
  versions:
    - name: v1alpha1
      served: true
      storage: true
      subresources:
        status: {}
      additionalPrinterColumns:
        - name: Provider
          type: string
          jsonPath: .status.provider
        - name: Ready
          type: string
          jsonPath: .status.conditions[?(@.type=="Ready")].status
        - name: Reason
          type: string
          jsonPath: .status.conditions[?(@.type=="Ready")].reason
        - name: Age
          type: date
          jsonPath: .metadata.creationTimestamp
      schema:
        openAPIV3Schema:
          type: object
          description: "One account with one metadata provider, named by the Libraries of its namespace in spec.sources."
          properties:
            spec:
              type: object
              description: "The provider this account is with, and the facts it may serve. A spec that names no facts serves every fact the operator knows how to ask this provider for."
              # The block names the provider, the way a Library's kind
              # names its settings block. Each provider is one block
              # here, and the rule below holds an account to one of
              # them: an account is a key, and a key is with one
              # provider.
              x-kubernetes-validations:
                - rule: >-
                    [has(self.tmdb), has(self.omdb), has(self.fanart),
                    has(self.tvmaze)].exists_one(block, block)
                  message: >-
                    A MetadataProvider names exactly one provider block:
                    tmdb, omdb, fanart, or tvmaze.
              properties:
                tmdb:
                  type: object
                  required: [secretRef]
                  description: "The account is with The Movie Database, which serves movies, series, and people."
                  properties:
                    secretRef:
                      type: object
                      required: [name]
                      description: "The Secret in this namespace that holds the credential, and the key inside it. Either credential TMDb issues works: a v3 API key of 32 hex characters, or a v4 read access token."
                      properties:
                        name:
                          type: string
                          minLength: 1
                          description: "The Secret's name, in this provider's own namespace."
                        key:
                          type: string
                          minLength: 1
                          default: token
                          description: "The key inside that Secret. When omitted, token."
                omdb:
                  type: object
                  required: [secretRef]
                  description: "The account is with OMDb. OMDb answers on an IMDb id, and it serves the plot, the US certification, and the ratings of IMDb, Rotten Tomatoes, and Metacritic."
                  properties:
                    secretRef:
                      type: object
                      required: [name]
                      description: "The Secret in this namespace that holds the OMDb key, and the key inside it. The free tier of a key is a thousand calls a day."
                      properties:
                        name:
                          type: string
                          minLength: 1
                          description: "The Secret's name, in this provider's own namespace."
                        key:
                          type: string
                          minLength: 1
                          default: token
                          description: "The key inside that Secret. When omitted, token."
                fanart:
                  type: object
                  required: [secretRef]
                  description: "The account is with Fanart.tv, which serves art alone. It is the only provider of the clearart, the banner, the landscape, the discart, and the season banner."
                  properties:
                    secretRef:
                      type: object
                      required: [name]
                      description: "The Secret in this namespace that holds the Fanart.tv project key, and the key inside it."
                      properties:
                        name:
                          type: string
                          minLength: 1
                          description: "The Secret's name, in this provider's own namespace."
                        key:
                          type: string
                          minLength: 1
                          default: token
                          description: "The key inside that Secret. When omitted, token."
                tvmaze:
                  type: object
                  description: "The account is with TVmaze, which serves series alone and needs no account. The block is empty, and its presence says that the operator may ask TVmaze."
                facts:
                  type: array
                  x-kubernetes-list-type: atomic
                  minItems: 1
                  maxItems: 30
                  description: "The facts this account may serve, from the fixed vocabulary. The list narrows what the operator knows how to ask this provider for. Omit it to serve all of that. A Library asks this provider only for a fact that status.facts lists."
                  items:
                    type: string
                    # The fact vocabulary of the enrichment design. A
                    # fact is one gap in the catalog, one name in a
                    # container's LIBRARY_FACTS, and one attempts file
                    # it writes.
                    enum:
                      - probe
                      - arrival
                      - trickplay
                      - identity
                      - overview
                      - certification
                      - rating.tmdb
                      - rating.imdb
                      - rating.rottentomatoes
                      - rating.metacritic
                      - credits
                      - poster
                      - backdrop
                      - logo
                      - clearart
                      - banner
                      - landscape
                      - discart
                      - season-poster
                      - season-banner
                      - episode-thumb
                      - contributor.ids
                      - contributor.biography
                      - contributor.headshot
            status:
              type: object
              description: "What the operator's own check found, written only by the library operator."
              properties:
                provider:
                  type: string
                  description: "The provider block this account names. The PROVIDER column reads it here, because no printer column can read which block a spec holds."
                facts:
                  type: array
                  x-kubernetes-list-type: atomic
                  maxItems: 30
                  description: "The facts this provider serves right now: what the operator knows how to ask this provider for, narrowed by spec.facts. The list is empty while the Ready condition is not True, because a provider the operator cannot reach serves nothing."
                  items:
                    type: string
                lastRefusal:
                  type: string
                  format: date-time
                  description: "When the provider last refused the key. It stands after the key works again, so a person reads that it once failed."
                conditions:
                  type: array
                  description: "Ready is True with the reason Reachable when the provider answered the operator's check, and False with the reason NoSecret, Refused, or Unreachable. Unreachable is a check that got no answer at all, and its message is the error the check read."
                  x-kubernetes-list-type: map
                  x-kubernetes-list-map-keys: [type]
                  items:
                    type: object
                    required: [type, status, lastTransitionTime]
                    properties:
                      type:
                        type: string
                        maxLength: 316
                        pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
                        description: >-
                          The check this entry reports, in CamelCase. It
                          is the key of this list.
                      status:
                        type: string
                        enum: ["True", "False", "Unknown"]
                        description: >-
                          The verdict. True is the good verdict, and
                          Unknown means the operator cannot tell yet.
                      observedGeneration:
                        type: integer
                        format: int64
                        minimum: 0
                        description: >-
                          The metadata.generation this condition judged.
                      reason:
                        type: string
                        maxLength: 1024
                        minLength: 1
                        pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
                        description: >-
                          One CamelCase word for why the condition holds
                          this verdict, meant for a program to match on.
                      message:
                        type: string
                        maxLength: 32768
                        description: >-
                          The same answer in a sentence a person reads.
                      lastTransitionTime:
                        type: string
                        format: date-time
                        description: >-
                          When the verdict last changed. It moves only
                          when the status flips.
