syntax = "proto3";

package com.github.oslokommune.oslonokkelen.adapter.proto;

// The role of the manifest is to describe what the adapter can
// do and the state things are in right now.
message AdapterManifest {

  // Can be used to optimize scraping. Backend can pass last known version
  // when scraping and if nothing has changed the adapter is allowed to return
  // an empty 304 not modified response to avoid re-transferring the same data
  // Oslonøkkelen already knows.
  int64 version = 1;

  // Collection of doors, lamps...
  repeated Thing things = 2;

  // Declaration of errors might returned from the adapter.
  // By declaring error possible error codes up front administrators of
  // Oslonøkkelen can write and change human readable messages for different
  // errors without changing the implementation of the adapter.
  repeated ErrorCodeDescription error_code_descriptions = 3;

  // Examples:
  //  * front-door
  //  * outdoor-lighting
  message Thing {

    // Url safe id
    string id = 1;

    // Short one line description of what it does
    string description = 2;

    // Optional.
    // Determines who can administer this thing in Oslonøkkelen key studio.
    string admin_role = 3;

    reserved 4;

    // What the thing can do
    repeated Action actions = 5;

    // Some things are equipped with sensors allowing us to observe state like
    // locked, unlocked, open, close, offline, online etc. Implementing this can
    // help administrators of Oslonøkkelen to configure alerts when things go
    // offline, remain open too long etc.
    repeated ThingState state = 6;

  }

  // Examples:
  //  * unlock
  //  * open
  message Action {

    // Url safe id
    string id = 1;

    // Short one line description of what it does
    string description = 2;

    // Required input attachments.
    // Example: The public service Gjenbruksid requires the users ssn to work.
    repeated AttachmentType required_input_attachment_types = 3;

    // Possible output
    repeated AttachmentType possible_output_attachment_types = 4;

    reserved 5;

  }


  // Example
  // -------
  // Code:         offline
  // Description:  System x has been offline for y hours
  message ErrorCodeDescription {

    // Url safe code.
    string code = 1;

    // Really short human readable description explaining
    // when this code will be returned.
    string description = 2;

  }

}


// Will be submitted to the /execute endpoint.
message ActionRequest {

  // Useful for correlation of requests across systems
  string request_id = 5;

  // Identifies a thing in the manifest
  // Example: front-door
  string thing_id = 1;

  // Identifies an action associated with the thing.
  // Example: open
  string action_id = 2;

  // Most action request won't have any attachments as most
  // adapters have no need to know who executed an action.
  repeated Attachment attachments = 3;

  // The app will have enforce a timeout. The adapter should try
  // hard to return within this time budget. Returning a timeout
  // error is better then not returning anything.
  int32 time_budget_millis = 4;

}

// Your adapter should return this as a response to requests submitted
// to the /execute endpoint _after_ the adapter is done executing the action.
message ActionResponse {

  // Indicates whether the action succeeded or not.
  Status status = 1;

  // Any attachments produced by the action.
  //
  // Example: By returning the `ErrorDescription` after an error you can make
  //          troubleshooting a lot easier for administrators of Oslonøkkelen.
  repeated Attachment attachments = 2;



  enum Status {

    // All good!
    SUCCESS = 0;

    // Something really crashed bad..
    // No need to automatically try again.
    ERROR_PERMANENT = 1;

    // Something like temporary network exceptions.
    // Automatic retry might work!
    ERROR_TEMPORARY = 2;

    // Access denied.
    // No need retrying with the same input.
    DENIED = 3;

  }

}

// Different actions can require certain input
enum AttachmentType {

  // Request attachment.
  // Useful for authorization.
  // Try to avoid depending on this if possible as it comes
  // with a lot of GDPR related requirements.
  NORWEGIAN_FODSELSNUMMER = 0;

  // Response attachment. Useful for sending something like a
  // member number to users of Oslonøkkelen.
  CODE = 1;

  reserved 2;

  // Response attachment.
  // Allows the adapter to send a human readable message back
  // to the user of the app responsible for issuing the request.
  END_USER_MESSAGE = 3;

  // Response attachment.
  // Technical error information useful for debugging and troubleshooting.
  ERROR_DESCRIPTION = 4;

  // Response attachment.
  // Technical information explaining why an action request was denied.
  // Should only be used for action responses with status = denied.
  DENIED_REASON = 5;

}

// Action requests and responses can contain attachments.
message Attachment {

  oneof value {

    // Can be used to return things like membership numbers.
    // The app has optional support for formatting qr and barcodes.
    Code code = 1;

    // Technical error description used for logging and debugging.
    // This won't be displayed to the end user of the app.
    ErrorDescription error_description = 3;

    // The content of this will be displayed to the end user.
    // If you decide to return this you should take great care
    // coming up with useful and ideally actionable messages
    // in Norwegian.
    EndUserMessage end_user_message = 4;

    // Request attachment. Will only be sent to pre-approved actions.
    // Mostly used for public services like Gjenbruksid.
    NorwegianFodselsnummer norwegian_fodselsnummer = 5;

    // Says something about why an action request was denied.
    DeniedReason denied_reason = 6;
  }

  message Code {

    // Url safe string identifying the code.
    //
    // Examples:
    //  * gjenbruksid
    //  * lånekortnummer
    string id = 1;

    // The actual code
    string code = 2;

    // ISO-8601
    string expires_at = 3;

    // Is the app required to verify a cached code? Or can it assume
    // that it never expires before the expire date?
    bool must_verify = 4;

    // Optional text to be displayed above the code
    string header_text = 5;

    // Optional text to be displayed under the code
    string footer_text = 6;

  }

  message ErrorDescription {

    // Something like: unknown-user or db-connection-error
    // Should be declared in the action manifest to allow
    // admins to map error codes to human readable messages.
    string code = 1;

    // Technical error message.
    // Will not be shown to the end user.
    string debugMessage = 2;

    // Some errors like timeouts might be resolved by the user
    // trying again while others are more permanent. Set this to
    // true if you think that a quick retry might work.
    bool permanent = 3;


  }

  // Says something about why an action request was denied.
  message DeniedReason {

    // Something like: unknown-user
    // Should be declared in the action manifest to allow
    // admins to map error codes to human readable messages.
    string code = 1;

    string debugMessage = 2;

  }

  // Provides an option to send a message back to the
  // person who executed the action. Keep it short.
  message EndUserMessage {

    // Human readable message for end user.
    TextContent message = 1;

    // Optional link (to website)
    string link = 2;

    // Optional link name. Will be ignored without a link.
    // Single line and max 20 characters.
    string linkName = 3;

  }

  message NorwegianFodselsnummer {
    string number = 1;
  }

}

message TextContent {

  string message = 1;
  ContentType contentType = 2;

  enum ContentType {
    PLAIN_TEXT = 0;
    MARKDOWN = 1;
  }

}

// Some things are equipped with sensors allowing us to observe state like
// locked, unlocked, open, close, offline, online etc. Implementing this can
// help administrators of Oslonøkkelen to configure alerts when things go
// offline, remain open too long etc.
message ThingState {

  // ISO-8601
  string last_update = 1;

  oneof value {

    // Whether the door/window is in an open position or not
    // (not to be confused with locked).
    Open open = 2;

    // Whether the door/window is locked or not.
    // (not to be confused with open).
    Locked locked = 3;

    // Used to indicate the healthiness of an action related to a thing.
    //
    // Example: The device responsible for unlocking (action) a door (thing)
    //          bas been offline for 30 minutes.
    ActionHealth action_health = 5;

    // Allows a few lines of technical system logs related to a thing to
    // be communicated to Oslonøkkelen
    DebugLog debug_log = 7;

  }

  reserved 4;
  reserved 6;

  // Used to communicate whether the door is in the open position.
  enum Open {
    OPEN = 0;
    CLOSED = 1;
  }

  // Used to communicate whether the door is locked.
  enum Locked {
    LOCKED = 0;
    UNLOCKED = 1;
  }

  // A single door can be equipped with multiple devices that
  // can fail separately. A door can have a working door pump action
  // at the same time the device responsible for unlocking it is broken.
  message ActionHealth {

    // Must match the id of one of the actions of the thing
    string action_id = 1;

    // True if everything seems to be working
    bool healthy = 2;

    // Debug message to help administrators debug the problem.
    // Will never be presented to end users.
    string debug_message = 3;

  }


  // Used to communicate a few lines of technical logs to Oslonøkkelen.
  message DebugLog {

    repeated Line lines = 1;

    message Line {
      int64 timestamp_epoch_millis = 1;
      Level level = 2;
      string message = 3;
    }

    enum Level {
      DEBUG = 0;
      INFO = 1;
      WARNING = 2;
      ERROR = 3;
    }

  }

}