package kiosk import ( "encoding/json" "time" ) // The registry API v1 contract. The summercms.io registry encodes its // responses with these types and the summer tool and the admin decode // them, so no other copy of the shape exists. Rules: every response is an // envelope object (data, meta or error); clients ignore unknown fields; // changes within v1 are additive only. The API is GET-only for clients, // needs no credentials and answers with open CORS and // Cache-Control: public, max-age=300 plus an ETag. // RegistryAPIPath is the base path of the registry API v1: // // GET {base}/plugins?q=&category= ListResponse // GET {base}/plugins/{id} DetailResponse // GET {base}/plugins/{id}/versions VersionsResponse, newest first // POST {base}/ingest IngestRequest -> 202 IngestResponse (authenticated) // // Errors answer with an ErrorResponse: 404 not_found, 400 invalid_query. const RegistryAPIPath = "/api/registry/v1" // Error codes of a RegistryError. const ( // RegistryErrorNotFound is the code of a 404: no such plugin. RegistryErrorNotFound = "not_found" // RegistryErrorInvalidQuery is the code of a 400: a bad query parameter. RegistryErrorInvalidQuery = "invalid_query" ) // RegistryPlugin is one plugin in the registry list. type RegistryPlugin struct { ID string `json:"id"` Name string `json:"name"` Description string `json:"description"` Module string `json:"module"` Package string `json:"package"` Repository string `json:"repository"` Category string `json:"category"` Featured bool `json:"featured"` IconURL string `json:"icon_url"` LatestVersion string `json:"latest_version"` LatestFramework string `json:"latest_framework"` UpdatedAt time.Time `json:"updated_at"` } // RegistryPluginDetail is one plugin with its README, screenshots and the // requirements of its latest version. type RegistryPluginDetail struct { RegistryPlugin ReadmeMarkdown string `json:"readme_markdown"` ReadmeHTML string `json:"readme_html"` Screenshots []RegistryScreenshot `json:"screenshots"` Requires Requires `json:"requires"` } // RegistryScreenshot is one screenshot of a plugin listing. type RegistryScreenshot struct { URL string `json:"url"` Caption string `json:"caption"` } // RegistryVersion is one tagged release of a plugin, with the minimums // its plugin.yaml declared at that tag. type RegistryVersion struct { Version string `json:"version"` Framework string `json:"framework"` Requires Requires `json:"requires"` Module string `json:"module"` Package string `json:"package"` Commit string `json:"commit"` TaggedAt time.Time `json:"tagged_at"` } // ListResponse answers GET {base}/plugins. type ListResponse struct { Data []RegistryPlugin `json:"data"` Meta ListMeta `json:"meta"` } // ListMeta carries the number of plugins that matched. type ListMeta struct { Total int `json:"total"` } // DetailResponse answers GET {base}/plugins/{id}. type DetailResponse struct { Data RegistryPluginDetail `json:"data"` } // VersionsResponse answers GET {base}/plugins/{id}/versions, newest first. type VersionsResponse struct { Data []RegistryVersion `json:"data"` } // ErrorResponse is the body of every registry error. type ErrorResponse struct { Error RegistryError `json:"error"` } // RegistryError is a stable code and a human-readable message. type RegistryError struct { Code string `json:"code"` Message string `json:"message"` } // IngestRequest is the body of POST {base}/ingest. type IngestRequest struct { Repository string `json:"repository"` } // IngestResponse answers an accepted ingest request (202). type IngestResponse struct { Data IngestResult `json:"data"` } // IngestResult names the repository queued for ingestion. type IngestResult struct { Repository string `json:"repository"` Queued bool `json:"queued"` } // MarshalJSON encodes the map as an object, {} when it is empty, so the // registry never sends null for requires. func (r Requires) MarshalJSON() ([]byte, error) { if r == nil { return []byte("{}"), nil } return json.Marshal(map[string]string(r)) }