diff --git a/.gitignore b/.gitignore index 554ce08..7b7be40 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,6 @@ .vscode/ dist/ target/ -codegen/ __debug_bin node_modules/ diff --git a/api/message_bus/openapi.yaml b/api/message_bus/openapi.yaml index 3ee61f4..15f66aa 100644 --- a/api/message_bus/openapi.yaml +++ b/api/message_bus/openapi.yaml @@ -34,6 +34,10 @@ tags: description: |- Endpoint paths for subscribing to events and actions via SocketIO + - name: YSK Endpoints + description: |- + Endpoint paths for YSK card via RESTful API + - name: EventType description: |- @@ -53,6 +57,11 @@ tags: - name: PropertyType description: |- + + - name: YSK methods + description: |- + methods for managing YSK card + x-tagGroups: - name: Methods @@ -61,11 +70,13 @@ x-tagGroups: - Event methods - ActionType methods - Action methods + - YSK methods - name: Endpoints tags: - WebSocket Endpoints - SocketIO Endpoints + - YSK Endpoints - name: Schemas tags: @@ -337,6 +348,37 @@ paths: "200": description: | Polling continued from SocketIO + /ysk: + get: + description: |- + Get need display YSK card + operationId: getYskCard + tags: + - YSK Endpoints + responses: + "200": + $ref: "#/components/responses/ResponseGetYSKCardListOK" + "500": + $ref: "#/components/responses/ResponseInternalServerError" + /ysk/{id}: + delete: + description: |- + Not display YSK card + operationId: deleteYskCard + tags: + - YSK Endpoints + parameters: + - name: id + in: path + description: YSK card id + required: true + schema: + type: string + responses: + "200": + $ref: "#/components/responses/ResponseOK" + "500": + $ref: "#/components/responses/ResponseInternalServerError" components: securitySchemes: @@ -528,6 +570,17 @@ components: application/json: schema: $ref: "#/components/schemas/Action" + + ResponseGetYSKCardListOK: + description: OK + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/BaseResponse" + - properties: + data: + $ref: "#/components/schemas/YSKCardList" schemas: BaseResponse: @@ -668,3 +721,137 @@ components: type: string description: timestamp this action took place format: date-time + + YSKCardList: + type: array + items: + $ref: "#/components/schemas/YSKCard" + + YSKCard: + type: object + required: + - "id" + - "cardType" + - "renderType" + - "content" + properties: + id: + type: string + example: "1234567890" + cardType: + type: string + enum: + - "task" + - "long-notice" + - "short-notice" + renderType: + type: string + enum: + - "task" + - "list-notice" + - "icon-text-notice" + - "markdown-notice" + content: + $ref: "#/components/schemas/YSKCardContent" + + YSKCardContent: + type: object + required: + - "titleIcon" + - "titleText" + properties: + titleIcon: + $ref: "#/components/schemas/YSKCardIcon" + titleText: + type: string + example: "CasaOS" + bodyProgress: + $ref: "#/components/schemas/YSKCardProgress" + bodyIconWithText: + $ref: "#/components/schemas/YSKCardIconWithText" + bodyList: + type: array + items: + $ref: "#/components/schemas/YSKCardListItem" + footerActions: + type: array + items: + $ref: "#/components/schemas/YSKCardFooterAction" + + YSKCardProgress: + type: object + required: + - "label" + - "progress" + properties: + label: + type: string + example: "Installing jellyfin" + progress: + type: integer + example: 50 + + YSKCardIconWithText: + type: object + required: + - "icon" + - "description" + properties: + icon: + $ref: "#/components/schemas/YSKCardIcon" + description: + type: string + example: "CasaOS" + + YSKCardListItem: + type: object + required: + - "icon" + - "description" + - "rightText" + properties: + icon: + $ref: "#/components/schemas/YSKCardIcon" + description: + type: string + example: "CasaOS" + rightText: + type: string + example: "4 TB" + + YSKCardFooterAction: + type: object + required: + - "side" + - "style" + - "text" + - "messageBus" + properties: + side: + type: string + example: "View Details" + style: + type: string + example: "view-details" + text: + type: string + example: "https://casaos.com" + messageBus: + $ref: "#/components/schemas/YSKCardMessageBusAction" + + YSKCardMessageBusAction: + type: object + required: + - "key" + - "payload" + properties: + key: + type: string + example: "open-url" + payload: + type: string + example: "https://casaos.com" + + YSKCardIcon: + type: string + example: "https://raw.githubusercontent.com/IceWhaleTech/logo/main/casaos/casaos_banner_twilight_blue_800px.png" diff --git a/codegen/message_bus_api.go b/codegen/message_bus_api.go new file mode 100644 index 0000000..93d2863 --- /dev/null +++ b/codegen/message_bus_api.go @@ -0,0 +1,838 @@ +// Package codegen provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen version v1.12.4 DO NOT EDIT. +package codegen + +import ( + "bytes" + "compress/gzip" + "encoding/base64" + "fmt" + "net/http" + "net/url" + "path" + "strings" + "time" + + "github.com/deepmap/oapi-codegen/pkg/runtime" + "github.com/getkin/kin-openapi/openapi3" + "github.com/labstack/echo/v4" +) + +const ( + Access_tokenScopes = "access_token.Scopes" +) + +// Defines values for YSKCardCardType. +const ( + YSKCardCardTypeLongNotice YSKCardCardType = "long-notice" + YSKCardCardTypeShortNotice YSKCardCardType = "short-notice" + YSKCardCardTypeTask YSKCardCardType = "task" +) + +// Defines values for YSKCardRenderType. +const ( + YSKCardRenderTypeIconTextNotice YSKCardRenderType = "icon-text-notice" + YSKCardRenderTypeListNotice YSKCardRenderType = "list-notice" + YSKCardRenderTypeMarkdownNotice YSKCardRenderType = "markdown-notice" + YSKCardRenderTypeTask YSKCardRenderType = "task" +) + +// Action defines model for Action. +type Action struct { + // Name action name + Name string `json:"name"` + + // Properties event properties + Properties map[string]string `json:"properties"` + + // SourceID associated source id + SourceID string `json:"sourceID"` + + // Timestamp timestamp this action took place + Timestamp *time.Time `json:"timestamp,omitempty"` +} + +// ActionType defines model for ActionType. +type ActionType struct { + // Name action name + // + // (there is no naming convention for action names, but it is recommended to name each as structural and descriptive as possible) + Name string `json:"name"` + PropertyTypeList []PropertyType `json:"propertyTypeList"` + + // SourceID action source id to identify where the action will take + SourceID string `json:"sourceID"` +} + +// BaseResponse defines model for BaseResponse. +type BaseResponse struct { + // Message message returned by server side if there is any + Message *string `json:"message,omitempty"` +} + +// Event defines model for Event. +type Event struct { + // Name event name + Name string `json:"name"` + + // Properties event properties + Properties map[string]string `json:"properties"` + + // SourceID associated source id + SourceID string `json:"sourceID"` + + // Timestamp timestamp this event took place + Timestamp *time.Time `json:"timestamp,omitempty"` + + // Uuid event uuid + Uuid *string `json:"uuid,omitempty"` +} + +// EventType defines model for EventType. +type EventType struct { + // Name event name + // + // (there is no naming convention for event names, but it is recommended to name each as structural and descriptive as possible) + Name string `json:"name"` + PropertyTypeList []PropertyType `json:"propertyTypeList"` + + // SourceID event source id to identify where the event comes from + SourceID string `json:"sourceID"` +} + +// PropertyType defines model for PropertyType. +type PropertyType struct { + Description *string `json:"description,omitempty"` + Example *string `json:"example,omitempty"` + + // Name property name + // + // > It is recommended for a property name to be as descriptive as possible. One option is to prefix with a namespace. + // > - If the property is source specific, prefix with source ID. For example, `local-storage:vendor` + // > - Otherwise, prefix with `common:`. For example, `common:email` + // > + // > Some bad examples are `id`, `avail`, `blk`...which can be ambiguous and confusing. + Name string `json:"name"` +} + +// YSKCard defines model for YSKCard. +type YSKCard struct { + CardType YSKCardCardType `json:"cardType"` + Content YSKCardContent `json:"content"` + Id string `json:"id"` + RenderType YSKCardRenderType `json:"renderType"` +} + +// YSKCardCardType defines model for YSKCard.CardType. +type YSKCardCardType string + +// YSKCardRenderType defines model for YSKCard.RenderType. +type YSKCardRenderType string + +// YSKCardContent defines model for YSKCardContent. +type YSKCardContent struct { + BodyIconWithText *YSKCardIconWithText `json:"bodyIconWithText,omitempty"` + BodyList *[]YSKCardListItem `json:"bodyList,omitempty"` + BodyProgress *YSKCardProgress `json:"bodyProgress,omitempty"` + FooterActions *[]YSKCardFooterAction `json:"footerActions,omitempty"` + TitleIcon YSKCardIcon `json:"titleIcon"` + TitleText string `json:"titleText"` +} + +// YSKCardFooterAction defines model for YSKCardFooterAction. +type YSKCardFooterAction struct { + MessageBus YSKCardMessageBusAction `json:"messageBus"` + Side string `json:"side"` + Style string `json:"style"` + Text string `json:"text"` +} + +// YSKCardIcon defines model for YSKCardIcon. +type YSKCardIcon = string + +// YSKCardIconWithText defines model for YSKCardIconWithText. +type YSKCardIconWithText struct { + Description string `json:"description"` + Icon YSKCardIcon `json:"icon"` +} + +// YSKCardList defines model for YSKCardList. +type YSKCardList = []YSKCard + +// YSKCardListItem defines model for YSKCardListItem. +type YSKCardListItem struct { + Description string `json:"description"` + Icon YSKCardIcon `json:"icon"` + RightText string `json:"rightText"` +} + +// YSKCardMessageBusAction defines model for YSKCardMessageBusAction. +type YSKCardMessageBusAction struct { + Key string `json:"key"` + Payload string `json:"payload"` +} + +// YSKCardProgress defines model for YSKCardProgress. +type YSKCardProgress struct { + Label string `json:"label"` + Progress int `json:"progress"` +} + +// ActionName defines model for ActionName. +type ActionName = string + +// ActionNames defines model for ActionNames. +type ActionNames = []string + +// EventName defines model for EventName. +type EventName = string + +// EventNames defines model for EventNames. +type EventNames = []string + +// SourceID defines model for SourceID. +type SourceID = string + +// GetActionTypeOK defines model for GetActionTypeOK. +type GetActionTypeOK = ActionType + +// GetActionTypesOK defines model for GetActionTypesOK. +type GetActionTypesOK = []ActionType + +// GetEventTypeOK defines model for GetEventTypeOK. +type GetEventTypeOK = EventType + +// GetEventTypesOK defines model for GetEventTypesOK. +type GetEventTypesOK = []EventType + +// PublishEventOK defines model for PublishEventOK. +type PublishEventOK = Event + +// ResponseBadRequest defines model for ResponseBadRequest. +type ResponseBadRequest = BaseResponse + +// ResponseConflict defines model for ResponseConflict. +type ResponseConflict = BaseResponse + +// ResponseGetYSKCardListOK defines model for ResponseGetYSKCardListOK. +type ResponseGetYSKCardListOK struct { + Data *YSKCardList `json:"data,omitempty"` + + // Message message returned by server side if there is any + Message *string `json:"message,omitempty"` +} + +// ResponseInternalServerError defines model for ResponseInternalServerError. +type ResponseInternalServerError = BaseResponse + +// ResponseNotFound defines model for ResponseNotFound. +type ResponseNotFound = BaseResponse + +// ResponseOK defines model for ResponseOK. +type ResponseOK = BaseResponse + +// TriggerActionOK defines model for TriggerActionOK. +type TriggerActionOK = Action + +// PublishEvent event properties +type PublishEvent map[string]string + +// RegisterActionTypes defines model for RegisterActionTypes. +type RegisterActionTypes = []ActionType + +// RegisterEventTypes defines model for RegisterEventTypes. +type RegisterEventTypes = []EventType + +// TriggerAction action properties +type TriggerAction map[string]string + +// SubscribeActionWSParams defines parameters for SubscribeActionWS. +type SubscribeActionWSParams struct { + Names *ActionNames `form:"names,omitempty" json:"names,omitempty"` +} + +// TriggerActionJSONBody defines parameters for TriggerAction. +type TriggerActionJSONBody map[string]string + +// RegisterActionTypesJSONBody defines parameters for RegisterActionTypes. +type RegisterActionTypesJSONBody = []ActionType + +// SubscribeEventWSParams defines parameters for SubscribeEventWS. +type SubscribeEventWSParams struct { + Names *EventNames `form:"names,omitempty" json:"names,omitempty"` +} + +// PublishEventJSONBody defines parameters for PublishEvent. +type PublishEventJSONBody map[string]string + +// RegisterEventTypesJSONBody defines parameters for RegisterEventTypes. +type RegisterEventTypesJSONBody = []EventType + +// TriggerActionJSONRequestBody defines body for TriggerAction for application/json ContentType. +type TriggerActionJSONRequestBody TriggerActionJSONBody + +// RegisterActionTypesJSONRequestBody defines body for RegisterActionTypes for application/json ContentType. +type RegisterActionTypesJSONRequestBody = RegisterActionTypesJSONBody + +// PublishEventJSONRequestBody defines body for PublishEvent for application/json ContentType. +type PublishEventJSONRequestBody PublishEventJSONBody + +// RegisterEventTypesJSONRequestBody defines body for RegisterEventTypes for application/json ContentType. +type RegisterEventTypesJSONRequestBody = RegisterEventTypesJSONBody + +// ServerInterface represents all server handlers. +type ServerInterface interface { + // Subscribe to actions by source ID (WebSocket) + // (GET /action/{source_id}) + SubscribeActionWS(ctx echo.Context, sourceId SourceID, params SubscribeActionWSParams) error + // Trigger an action + // (POST /action/{source_id}/{name}) + TriggerAction(ctx echo.Context, sourceId SourceID, name ActionName) error + // List action types + // (GET /action_type) + GetActionTypes(ctx echo.Context) error + // Register one or more action types + // (POST /action_type) + RegisterActionTypes(ctx echo.Context) error + // Get action types by source ID + // (GET /action_type/{source_id}) + GetActionTypesBySourceID(ctx echo.Context, sourceId SourceID) error + // Get an action type by source ID and name + // (GET /action_type/{source_id}/{name}) + GetActionType(ctx echo.Context, sourceId SourceID, name ActionName) error + // Subscribe to events by source ID (WebSocket) + // (GET /event/{source_id}) + SubscribeEventWS(ctx echo.Context, sourceId SourceID, params SubscribeEventWSParams) error + // Publish an event + // (POST /event/{source_id}/{name}) + PublishEvent(ctx echo.Context, sourceId SourceID, name EventName) error + // List event types + // (GET /event_type) + GetEventTypes(ctx echo.Context) error + // Register one or more event types + // (POST /event_type) + RegisterEventTypes(ctx echo.Context) error + // Get event types by source ID + // (GET /event_type/{source_id}) + GetEventTypesBySourceID(ctx echo.Context, sourceId SourceID) error + // Get an event type by source ID and name + // (GET /event_type/{source_id}/{name}) + GetEventType(ctx echo.Context, sourceId SourceID, name EventName) error + // Subscribe to events and actions (SocketIO) + // (GET /socket.io) + SubscribeSIO(ctx echo.Context) error + // Poll events and actions (SocketIO) + // (POST /socket.io) + PollSIO(ctx echo.Context) error + // Subscribe to events and actions (SocketIO) + // (GET /socket.io/) + SubscribeSIO2(ctx echo.Context) error + // Poll events and actions (SocketIO) + // (POST /socket.io/) + PollSIO2(ctx echo.Context) error + + // (GET /ysk) + GetYskCard(ctx echo.Context) error + + // (DELETE /ysk/{id}) + DeleteYskCard(ctx echo.Context, id string) error +} + +// ServerInterfaceWrapper converts echo contexts to parameters. +type ServerInterfaceWrapper struct { + Handler ServerInterface +} + +// SubscribeActionWS converts echo context to params. +func (w *ServerInterfaceWrapper) SubscribeActionWS(ctx echo.Context) error { + var err error + // ------------- Path parameter "source_id" ------------- + var sourceId SourceID + + err = runtime.BindStyledParameterWithLocation("simple", false, "source_id", runtime.ParamLocationPath, ctx.Param("source_id"), &sourceId) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter source_id: %s", err)) + } + + ctx.Set(Access_tokenScopes, []string{""}) + + // Parameter object where we will unmarshal all parameters from the context + var params SubscribeActionWSParams + // ------------- Optional query parameter "names" ------------- + + err = runtime.BindQueryParameter("form", true, false, "names", ctx.QueryParams(), ¶ms.Names) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter names: %s", err)) + } + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.SubscribeActionWS(ctx, sourceId, params) + return err +} + +// TriggerAction converts echo context to params. +func (w *ServerInterfaceWrapper) TriggerAction(ctx echo.Context) error { + var err error + // ------------- Path parameter "source_id" ------------- + var sourceId SourceID + + err = runtime.BindStyledParameterWithLocation("simple", false, "source_id", runtime.ParamLocationPath, ctx.Param("source_id"), &sourceId) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter source_id: %s", err)) + } + + // ------------- Path parameter "name" ------------- + var name ActionName + + err = runtime.BindStyledParameterWithLocation("simple", false, "name", runtime.ParamLocationPath, ctx.Param("name"), &name) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter name: %s", err)) + } + + ctx.Set(Access_tokenScopes, []string{""}) + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.TriggerAction(ctx, sourceId, name) + return err +} + +// GetActionTypes converts echo context to params. +func (w *ServerInterfaceWrapper) GetActionTypes(ctx echo.Context) error { + var err error + + ctx.Set(Access_tokenScopes, []string{""}) + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.GetActionTypes(ctx) + return err +} + +// RegisterActionTypes converts echo context to params. +func (w *ServerInterfaceWrapper) RegisterActionTypes(ctx echo.Context) error { + var err error + + ctx.Set(Access_tokenScopes, []string{""}) + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.RegisterActionTypes(ctx) + return err +} + +// GetActionTypesBySourceID converts echo context to params. +func (w *ServerInterfaceWrapper) GetActionTypesBySourceID(ctx echo.Context) error { + var err error + // ------------- Path parameter "source_id" ------------- + var sourceId SourceID + + err = runtime.BindStyledParameterWithLocation("simple", false, "source_id", runtime.ParamLocationPath, ctx.Param("source_id"), &sourceId) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter source_id: %s", err)) + } + + ctx.Set(Access_tokenScopes, []string{""}) + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.GetActionTypesBySourceID(ctx, sourceId) + return err +} + +// GetActionType converts echo context to params. +func (w *ServerInterfaceWrapper) GetActionType(ctx echo.Context) error { + var err error + // ------------- Path parameter "source_id" ------------- + var sourceId SourceID + + err = runtime.BindStyledParameterWithLocation("simple", false, "source_id", runtime.ParamLocationPath, ctx.Param("source_id"), &sourceId) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter source_id: %s", err)) + } + + // ------------- Path parameter "name" ------------- + var name ActionName + + err = runtime.BindStyledParameterWithLocation("simple", false, "name", runtime.ParamLocationPath, ctx.Param("name"), &name) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter name: %s", err)) + } + + ctx.Set(Access_tokenScopes, []string{""}) + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.GetActionType(ctx, sourceId, name) + return err +} + +// SubscribeEventWS converts echo context to params. +func (w *ServerInterfaceWrapper) SubscribeEventWS(ctx echo.Context) error { + var err error + // ------------- Path parameter "source_id" ------------- + var sourceId SourceID + + err = runtime.BindStyledParameterWithLocation("simple", false, "source_id", runtime.ParamLocationPath, ctx.Param("source_id"), &sourceId) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter source_id: %s", err)) + } + + ctx.Set(Access_tokenScopes, []string{""}) + + // Parameter object where we will unmarshal all parameters from the context + var params SubscribeEventWSParams + // ------------- Optional query parameter "names" ------------- + + err = runtime.BindQueryParameter("form", true, false, "names", ctx.QueryParams(), ¶ms.Names) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter names: %s", err)) + } + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.SubscribeEventWS(ctx, sourceId, params) + return err +} + +// PublishEvent converts echo context to params. +func (w *ServerInterfaceWrapper) PublishEvent(ctx echo.Context) error { + var err error + // ------------- Path parameter "source_id" ------------- + var sourceId SourceID + + err = runtime.BindStyledParameterWithLocation("simple", false, "source_id", runtime.ParamLocationPath, ctx.Param("source_id"), &sourceId) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter source_id: %s", err)) + } + + // ------------- Path parameter "name" ------------- + var name EventName + + err = runtime.BindStyledParameterWithLocation("simple", false, "name", runtime.ParamLocationPath, ctx.Param("name"), &name) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter name: %s", err)) + } + + ctx.Set(Access_tokenScopes, []string{""}) + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.PublishEvent(ctx, sourceId, name) + return err +} + +// GetEventTypes converts echo context to params. +func (w *ServerInterfaceWrapper) GetEventTypes(ctx echo.Context) error { + var err error + + ctx.Set(Access_tokenScopes, []string{""}) + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.GetEventTypes(ctx) + return err +} + +// RegisterEventTypes converts echo context to params. +func (w *ServerInterfaceWrapper) RegisterEventTypes(ctx echo.Context) error { + var err error + + ctx.Set(Access_tokenScopes, []string{""}) + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.RegisterEventTypes(ctx) + return err +} + +// GetEventTypesBySourceID converts echo context to params. +func (w *ServerInterfaceWrapper) GetEventTypesBySourceID(ctx echo.Context) error { + var err error + // ------------- Path parameter "source_id" ------------- + var sourceId SourceID + + err = runtime.BindStyledParameterWithLocation("simple", false, "source_id", runtime.ParamLocationPath, ctx.Param("source_id"), &sourceId) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter source_id: %s", err)) + } + + ctx.Set(Access_tokenScopes, []string{""}) + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.GetEventTypesBySourceID(ctx, sourceId) + return err +} + +// GetEventType converts echo context to params. +func (w *ServerInterfaceWrapper) GetEventType(ctx echo.Context) error { + var err error + // ------------- Path parameter "source_id" ------------- + var sourceId SourceID + + err = runtime.BindStyledParameterWithLocation("simple", false, "source_id", runtime.ParamLocationPath, ctx.Param("source_id"), &sourceId) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter source_id: %s", err)) + } + + // ------------- Path parameter "name" ------------- + var name EventName + + err = runtime.BindStyledParameterWithLocation("simple", false, "name", runtime.ParamLocationPath, ctx.Param("name"), &name) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter name: %s", err)) + } + + ctx.Set(Access_tokenScopes, []string{""}) + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.GetEventType(ctx, sourceId, name) + return err +} + +// SubscribeSIO converts echo context to params. +func (w *ServerInterfaceWrapper) SubscribeSIO(ctx echo.Context) error { + var err error + + ctx.Set(Access_tokenScopes, []string{""}) + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.SubscribeSIO(ctx) + return err +} + +// PollSIO converts echo context to params. +func (w *ServerInterfaceWrapper) PollSIO(ctx echo.Context) error { + var err error + + ctx.Set(Access_tokenScopes, []string{""}) + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.PollSIO(ctx) + return err +} + +// SubscribeSIO2 converts echo context to params. +func (w *ServerInterfaceWrapper) SubscribeSIO2(ctx echo.Context) error { + var err error + + ctx.Set(Access_tokenScopes, []string{""}) + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.SubscribeSIO2(ctx) + return err +} + +// PollSIO2 converts echo context to params. +func (w *ServerInterfaceWrapper) PollSIO2(ctx echo.Context) error { + var err error + + ctx.Set(Access_tokenScopes, []string{""}) + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.PollSIO2(ctx) + return err +} + +// GetYskCard converts echo context to params. +func (w *ServerInterfaceWrapper) GetYskCard(ctx echo.Context) error { + var err error + + ctx.Set(Access_tokenScopes, []string{""}) + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.GetYskCard(ctx) + return err +} + +// DeleteYskCard converts echo context to params. +func (w *ServerInterfaceWrapper) DeleteYskCard(ctx echo.Context) error { + var err error + // ------------- Path parameter "id" ------------- + var id string + + err = runtime.BindStyledParameterWithLocation("simple", false, "id", runtime.ParamLocationPath, ctx.Param("id"), &id) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err)) + } + + ctx.Set(Access_tokenScopes, []string{""}) + + // Invoke the callback with all the unmarshalled arguments + err = w.Handler.DeleteYskCard(ctx, id) + return err +} + +// This is a simple interface which specifies echo.Route addition functions which +// are present on both echo.Echo and echo.Group, since we want to allow using +// either of them for path registration +type EchoRouter interface { + CONNECT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + DELETE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + GET(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + HEAD(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + OPTIONS(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + PATCH(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + POST(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + PUT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + TRACE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route +} + +// RegisterHandlers adds each server route to the EchoRouter. +func RegisterHandlers(router EchoRouter, si ServerInterface) { + RegisterHandlersWithBaseURL(router, si, "") +} + +// Registers handlers, and prepends BaseURL to the paths, so that the paths +// can be served under a prefix. +func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL string) { + + wrapper := ServerInterfaceWrapper{ + Handler: si, + } + + router.GET(baseURL+"/action/:source_id", wrapper.SubscribeActionWS) + router.POST(baseURL+"/action/:source_id/:name", wrapper.TriggerAction) + router.GET(baseURL+"/action_type", wrapper.GetActionTypes) + router.POST(baseURL+"/action_type", wrapper.RegisterActionTypes) + router.GET(baseURL+"/action_type/:source_id", wrapper.GetActionTypesBySourceID) + router.GET(baseURL+"/action_type/:source_id/:name", wrapper.GetActionType) + router.GET(baseURL+"/event/:source_id", wrapper.SubscribeEventWS) + router.POST(baseURL+"/event/:source_id/:name", wrapper.PublishEvent) + router.GET(baseURL+"/event_type", wrapper.GetEventTypes) + router.POST(baseURL+"/event_type", wrapper.RegisterEventTypes) + router.GET(baseURL+"/event_type/:source_id", wrapper.GetEventTypesBySourceID) + router.GET(baseURL+"/event_type/:source_id/:name", wrapper.GetEventType) + router.GET(baseURL+"/socket.io", wrapper.SubscribeSIO) + router.POST(baseURL+"/socket.io", wrapper.PollSIO) + router.GET(baseURL+"/socket.io/", wrapper.SubscribeSIO2) + router.POST(baseURL+"/socket.io/", wrapper.PollSIO2) + router.GET(baseURL+"/ysk", wrapper.GetYskCard) + router.DELETE(baseURL+"/ysk/:id", wrapper.DeleteYskCard) + +} + +// Base64 encoded, gzipped, json marshaled Swagger object +var swaggerSpec = []string{ + + "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", +} + +// GetSwagger returns the content of the embedded swagger specification file +// or error if failed to decode +func decodeSpec() ([]byte, error) { + zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) + if err != nil { + return nil, fmt.Errorf("error base64 decoding spec: %s", err) + } + zr, err := gzip.NewReader(bytes.NewReader(zipped)) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %s", err) + } + var buf bytes.Buffer + _, err = buf.ReadFrom(zr) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %s", err) + } + + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cached of a decoded swagger spec +func decodeSpecCached() func() ([]byte, error) { + data, err := decodeSpec() + return func() ([]byte, error) { + return data, err + } +} + +// Constructs a synthetic filesystem for resolving external references when loading openapi specifications. +func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { + var res = make(map[string]func() ([]byte, error)) + if len(pathToFile) > 0 { + res[pathToFile] = rawSpec + } + + return res +} + +// GetSwagger returns the Swagger specification corresponding to the generated code +// in this file. The external references of Swagger specification are resolved. +// The logic of resolving external references is tightly connected to "import-mapping" feature. +// Externally referenced files must be embedded in the corresponding golang packages. +// Urls can be supported but this task was out of the scope. +func GetSwagger() (swagger *openapi3.T, err error) { + var resolvePath = PathToRawSpec("") + + loader := openapi3.NewLoader() + loader.IsExternalRefsAllowed = true + loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) { + var pathToFile = url.String() + pathToFile = path.Clean(pathToFile) + getSpec, ok := resolvePath[pathToFile] + if !ok { + err1 := fmt.Errorf("path not found: %s", pathToFile) + return nil, err1 + } + return getSpec() + } + var specData []byte + specData, err = rawSpec() + if err != nil { + return + } + swagger, err = loader.LoadFromData(specData) + if err != nil { + return + } + return +} diff --git a/common/message.go b/common/message.go new file mode 100644 index 0000000..d25c96e --- /dev/null +++ b/common/message.go @@ -0,0 +1,51 @@ +package common + +import ( + "github.com/IceWhaleTech/CasaOS-MessageBus/codegen" + "github.com/samber/lo" +) + +const ( + SERVICENAME = "ysk" +) + +// common properties +var ( + PropertyTypeMessage = codegen.PropertyType{ + Name: "message", + Description: lo.ToPtr("message at different levels, typically for error"), + } +) + +// app properties +var ( + PropertyTypeCardID = codegen.PropertyType{ + Name: "card:id", + Description: lo.ToPtr("card id"), + Example: lo.ToPtr("task:application:install"), + } + + PropertyTypeCardBody = codegen.PropertyType{ + Name: "card:body", + Description: lo.ToPtr("card body"), + Example: lo.ToPtr("{xxxxxx}"), + } +) + +var ( + EventTypeYSKCardUpsert = codegen.EventType{ + SourceID: SERVICENAME, + Name: "ysk:card:upsert", + PropertyTypeList: []codegen.PropertyType{ + PropertyTypeCardBody, + }, + } + + EventTypeYSKCardDelete = codegen.EventType{ + SourceID: SERVICENAME, + Name: "ysk:card:delete", + PropertyTypeList: []codegen.PropertyType{ + PropertyTypeCardID, + }, + } +) diff --git a/go.mod b/go.mod index 0360962..baf8d3b 100644 --- a/go.mod +++ b/go.mod @@ -52,6 +52,7 @@ require ( github.com/pierrec/lz4/v4 v4.1.17 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/samber/lo v1.46.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect github.com/ulikunitz/xz v0.5.11 // indirect @@ -90,7 +91,7 @@ require ( golang.org/x/crypto v0.8.0 // indirect golang.org/x/net v0.9.0 // indirect golang.org/x/sys v0.7.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/text v0.16.0 // indirect gopkg.in/ini.v1 v1.67.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 0b9b8f8..caf8bf6 100644 --- a/go.sum +++ b/go.sum @@ -165,6 +165,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/samber/lo v1.46.0 h1:w8G+oaCPgz1PoCJztqymCFaKwXt+5cCXn51uPxExFfQ= +github.com/samber/lo v1.46.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -220,6 +222,8 @@ golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/main.go b/main.go index 5e5620a..e7470c3 100644 --- a/main.go +++ b/main.go @@ -88,6 +88,7 @@ func main() { defer cancel() services.Start(&ctx) + go services.YSKService.Start(true) // route swagger, err := codegen.GetSwagger() diff --git a/package.json b/package.json index c15b4ce..4dcd872 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { - "name": "casaos-messagebus-openapi", + "name": "@icewhale/casaos-messagebus-openapi", "version": "0.0.1", "scripts": { "build": "rm -rf dist && tsc && rm -rf generate", "generate:local": "openapi-generator-cli generate -g typescript-axios -i ./api/message_bus/openapi.yaml -o ./generate", "generate:npx": "npx @openapitools/openapi-generator-cli generate -g typescript-axios -i ./api/message_bus/openapi.yaml -o ./generate", "generate:ts": "npx openapi-typescript-codegen --input ./api/message_bus/openapi.yaml --output ./generate", - "start": "yarn generate:ts && yarn build" + "start": "yarn generate:local && yarn build" }, "homepage": "https://github.com/IceWhaleTech/CasaOS-MessageBus#readme", "description": "Casaos-MessageBus Typescript+Axios SDK", diff --git a/pkg/ysk/adapter.go b/pkg/ysk/adapter.go new file mode 100644 index 0000000..24ed264 --- /dev/null +++ b/pkg/ysk/adapter.go @@ -0,0 +1,125 @@ +package ysk + +import ( + "database/sql/driver" + "encoding/json" + "errors" + + "github.com/IceWhaleTech/CasaOS-MessageBus/codegen" +) + +type CartType string + +const ( + CardTypeTask CartType = "task" + CardTypeLongNote CartType = "long-notice" + CardTypeShortNote CartType = "short-notice" +) + +type RenderType string + +const ( + RenderTypeCardTask RenderType = "task" + RenderTypeCardListNotice RenderType = "list-notice" + RenderTypeCardIconTextNotice RenderType = "icon-text-notice" + RenderTypeCardMarkdownNotice RenderType = "markdown-notice" +) + +type ActionPosition string + +const ( + ActionPositionLeft ActionPosition = "left" + ActionPositionRight ActionPosition = "right" +) + +type YSKCard struct { + Id string `json:"id"` + CardType CartType `json:"cardType"` + RenderType RenderType `json:"renderType"` + Content YSKCardContent `json:"content"` +} + +func (yskCard YSKCard) WithProgress(label string, progress int) YSKCard { + if yskCard.Content.BodyProgress != nil { + yskCard.Content.BodyProgress = &YSKCardProgress{ + Label: label, + Progress: progress, + } + return yskCard + } + return yskCard +} + +type YSKCardContent struct { + TitleIcon string `json:"titleIcon" gorm:"column:title_icon"` + TitleText string `json:"titleText" gorm:"column:title_text"` + BodyProgress *YSKCardProgress `json:"bodyProgress,omitempty" gorm:"serializer:json"` + BodyIconWithText *YSKCardIconWithText `json:"bodyIconWithText,omitempty" gorm:"serializer:json"` + BodyList []YSKCardListItem `json:"bodyList,omitempty" gorm:"serializer:json"` + FooterActions []YSKCardFooterAction `json:"footerActions,omitempty" gorm:"serializer:json"` +} + +func (p YSKCardContent) Value() (driver.Value, error) { + return json.Marshal(p) +} + +func (p *YSKCardContent) Scan(value interface{}) error { + b, ok := value.([]byte) + if !ok { + return errors.New("type assertion to []byte failed") + } + + return json.Unmarshal(b, &p) +} + +type YSKCardProgress struct { + Label string + Progress int +} + +type YSKCardIconWithText struct { + Icon string + Description string +} + +type YSKCardListItem struct { + Icon string + Description string + RightText string +} + +type YSKCardFooterAction struct { + Side ActionPosition + Style string + Text string + MessageBus YSKCardMessageBusAction +} + +type YSKCardMessageBusAction struct { + Key string `json:"key"` + Payload string `json:"payload"` +} + +type YSKCardIcon = string + +func ToCodegenYSKCard(card YSKCard) (codegen.YSKCard, error) { + jsonBody, err := json.Marshal(card) + if err != nil { + return codegen.YSKCard{}, err + } + var yskCard codegen.YSKCard + err = json.Unmarshal(jsonBody, &yskCard) + + return yskCard, err +} + +func FromCodegenYSKCard(card codegen.YSKCard) (YSKCard, error) { + jsonBody, err := json.Marshal(card) + if err != nil { + return YSKCard{}, err + } + var yskCard YSKCard + err = json.Unmarshal(jsonBody, &yskCard) + + return yskCard, err +} diff --git a/pkg/ysk/adapter_test.go b/pkg/ysk/adapter_test.go new file mode 100644 index 0000000..8bb0a64 --- /dev/null +++ b/pkg/ysk/adapter_test.go @@ -0,0 +1,55 @@ +package ysk_test + +import ( + "encoding/json" + "reflect" + "testing" + + "github.com/IceWhaleTech/CasaOS-MessageBus/codegen" + "github.com/IceWhaleTech/CasaOS-MessageBus/pkg/ysk" + "gotest.tools/assert" +) + +func compareJSON(json1, json2 string) (bool, error) { + var v1, v2 interface{} + + if err := json.Unmarshal([]byte(json1), &v1); err != nil { + return false, err + } + + if err := json.Unmarshal([]byte(json2), &v2); err != nil { + return false, err + } + + return reflect.DeepEqual(v1, v2), nil +} + +func TestJsonIsSame(t *testing.T) { + domainObject := ysk.YSKCard{ + Id: "test-card", + CardType: ysk.CardTypeTask, + RenderType: ysk.RenderTypeCardIconTextNotice, + Content: ysk.YSKCardContent{ + TitleIcon: "test-icon", + TitleText: "test-title", + }, + } + codegenObject := codegen.YSKCard{ + Id: "test-card", + CardType: codegen.YSKCardCardTypeTask, + RenderType: codegen.YSKCardRenderTypeIconTextNotice, + Content: codegen.YSKCardContent{ + TitleIcon: "test-icon", + TitleText: "test-title", + }, + } + + json1, err := json.Marshal(domainObject) + assert.NilError(t, err) + json2, err := json.Marshal(codegenObject) + assert.NilError(t, err) + + equal, err := compareJSON(string(json1), string(json2)) + assert.NilError(t, err) + assert.Equal(t, equal, true) +} diff --git a/pkg/ysk/ysk.go b/pkg/ysk/ysk.go new file mode 100644 index 0000000..1ed16ec --- /dev/null +++ b/pkg/ysk/ysk.go @@ -0,0 +1,35 @@ +package ysk + +import ( + "context" + "encoding/json" + + "github.com/IceWhaleTech/CasaOS-MessageBus/common" +) + +func DefineCard(ctx context.Context, cardID string) YSKCard { + return YSKCard{} +} + +func NewYSKCard(ctx context.Context, YSKCard YSKCard, publish func(context.Context, string, string, map[string]string)) error { + yskCardBodyJSON, _ := json.Marshal(YSKCard) + publish(ctx, + common.SERVICENAME, + common.EventTypeYSKCardUpsert.Name, + map[string]string{ + common.PropertyTypeCardBody.Name: string(yskCardBodyJSON), + }, + ) + return nil +} + +func DeleteCard(ctx context.Context, cardID string, publish func(context.Context, string, string, map[string]string)) error { + // do something + publish(ctx, + common.SERVICENAME, + common.EventTypeYSKCardDelete.Name, + map[string]string{ + common.PropertyTypeCardID.Name: cardID, + }) + return nil +} diff --git a/pkg/ysk/ysk_test.go b/pkg/ysk/ysk_test.go new file mode 100644 index 0000000..5927e14 --- /dev/null +++ b/pkg/ysk/ysk_test.go @@ -0,0 +1,101 @@ +package ysk_test + +import ( + "context" + "testing" + "time" + + "github.com/IceWhaleTech/CasaOS-Common/utils/logger" + "github.com/IceWhaleTech/CasaOS-MessageBus/model" + "github.com/IceWhaleTech/CasaOS-MessageBus/pkg/ysk" + "github.com/IceWhaleTech/CasaOS-MessageBus/repository" + "github.com/IceWhaleTech/CasaOS-MessageBus/service" + "github.com/IceWhaleTech/CasaOS-MessageBus/utils" + "gotest.tools/assert" +) + +var ws *service.EventServiceWS + +func setup(t *testing.T) (*service.EventServiceWS, *service.YSKService, func()) { + repository, err := repository.NewDatabaseRepositoryInMemory() + assert.NilError(t, err) + s := service.NewServices(&repository) + wsService := s.EventServiceWS + yskService := s.YSKService + + ctx := context.Background() + go s.Start(&ctx) + return wsService, yskService, func() { + repository.Close() + } +} +func mockPublish(ctx context.Context, sourceID string, eventName string, body map[string]string) { + if ws != nil { + ws.Publish(model.Event{ + SourceID: sourceID, + Name: eventName, + Properties: body, + }) + } +} + +func TestUpdateProgress(t *testing.T) { + logger.LogInitConsoleOnly() + + wsService, yskService, cleanup := setup(t) + defer cleanup() + ws = wsService + + yskService.Start(false) + // wait for the service to start + time.Sleep(1 * time.Second) + + err := ysk.NewYSKCard(context.Background(), utils.ApplicationInstallProgress.WithProgress( + "Installing LinuxServer/Jellyfin", 25, + ), mockPublish) + assert.NilError(t, err) + + err = ysk.NewYSKCard(context.Background(), utils.ApplicationInstallProgress.WithProgress( + "Installing LinuxServer/Jellyfin", 50, + ), mockPublish) + assert.NilError(t, err) + + time.Sleep(1 * time.Second) + + cards, err := yskService.YskCardList(context.Background()) + assert.NilError(t, err) + assert.Equal(t, len(cards), 1) + + assert.NilError(t, err) + err = ysk.DeleteCard(context.Background(), utils.ApplicationInstallProgress.Id, mockPublish) + assert.NilError(t, err) + + time.Sleep(1 * time.Second) + + cards, err = yskService.YskCardList(context.Background()) + assert.NilError(t, err) + assert.Equal(t, len(cards), 0) +} + +func TestLongAndShortNoticeInsert(t *testing.T) { + logger.LogInitConsoleOnly() + + wsService, yskService, cleanup := setup(t) + defer cleanup() + ws = wsService + + yskService.Start(false) + // wait for the service to start + time.Sleep(1 * time.Second) + + err := ysk.NewYSKCard(context.Background(), utils.ZimaOSDataStationNotice, mockPublish) + assert.NilError(t, err) + err = ysk.NewYSKCard(context.Background(), utils.ApplicationUpdateNotice, mockPublish) + assert.NilError(t, err) + + time.Sleep(1 * time.Second) + + cards, err := yskService.YskCardList(context.Background()) + assert.NilError(t, err) + assert.Equal(t, len(cards), 1) +} diff --git a/repository/repository.go b/repository/repository.go index 80fb90f..5c114a5 100644 --- a/repository/repository.go +++ b/repository/repository.go @@ -1,6 +1,9 @@ package repository -import "github.com/IceWhaleTech/CasaOS-MessageBus/model" +import ( + "github.com/IceWhaleTech/CasaOS-MessageBus/model" + "github.com/IceWhaleTech/CasaOS-MessageBus/pkg/ysk" +) type Repository interface { GetEventTypes() ([]model.EventType, error) @@ -13,5 +16,9 @@ type Repository interface { GetActionTypesBySourceID(sourceID string) ([]model.ActionType, error) GetActionType(sourceID string, name string) (*model.ActionType, error) + GetYSKCardList() ([]ysk.YSKCard, error) + UpsertYSKCard(card ysk.YSKCard) error + DeleteYSKCard(id string) error + Close() } diff --git a/repository/repository_db.go b/repository/repository_db.go index bc7f3cf..760570c 100644 --- a/repository/repository_db.go +++ b/repository/repository_db.go @@ -5,6 +5,7 @@ import ( "github.com/IceWhaleTech/CasaOS-Common/utils/logger" "github.com/IceWhaleTech/CasaOS-MessageBus/model" + "github.com/IceWhaleTech/CasaOS-MessageBus/pkg/ysk" "github.com/glebarez/sqlite" "go.uber.org/zap" "gorm.io/gorm" @@ -15,6 +16,23 @@ type DatabaseRepository struct { db *gorm.DB } +func (r *DatabaseRepository) GetYSKCardList() ([]ysk.YSKCard, error) { + var cardList []ysk.YSKCard + if err := r.db.Find(&cardList).Error; err != nil { + return nil, err + } + return cardList, nil +} + +func (r *DatabaseRepository) UpsertYSKCard(card ysk.YSKCard) error { + return r.db.Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "id"}}, + UpdateAll: true, + }).Create(&card).Error +} +func (r *DatabaseRepository) DeleteYSKCard(id string) error { + return r.db.Delete(&ysk.YSKCard{Id: id}).Error +} func (r *DatabaseRepository) GetEventTypes() ([]model.EventType, error) { var eventTypes []model.EventType @@ -132,7 +150,10 @@ func NewDatabaseRepository(databaseFilePath string) (Repository, error) { c.SetMaxOpenConns(1) c.SetConnMaxIdleTime(1000 * time.Second) - if err := db.AutoMigrate(&model.EventType{}, &model.ActionType{}, &model.PropertyType{}); err != nil { + if err := db.AutoMigrate( + &model.EventType{}, &model.ActionType{}, &model.PropertyType{}, + &ysk.YSKCard{}, + ); err != nil { return nil, err } diff --git a/route/ysk.go b/route/ysk.go new file mode 100644 index 0000000..e5af283 --- /dev/null +++ b/route/ysk.go @@ -0,0 +1,41 @@ +package route + +import ( + "net/http" + + "github.com/IceWhaleTech/CasaOS-MessageBus/codegen" + "github.com/IceWhaleTech/CasaOS-MessageBus/pkg/ysk" + "github.com/labstack/echo/v4" + "github.com/samber/lo" +) + +func (r *APIRoute) DeleteYskCard(ctx echo.Context, id string) error { + err := r.services.YSKService.DeleteYSKCard(ctx.Request().Context(), id) + if err != nil { + return ctx.JSON(http.StatusInternalServerError, codegen.ResponseInternalServerError{ + Message: lo.ToPtr(err.Error()), + }) + } + return ctx.JSON(http.StatusOK, codegen.ResponseOK{ + Message: lo.ToPtr("success"), + }) +} + +func (r *APIRoute) GetYskCard(ctx echo.Context) error { + cardList, err := r.services.YSKService.YskCardList(ctx.Request().Context()) + if err != nil { + return ctx.JSON(http.StatusInternalServerError, codegen.ResponseInternalServerError{ + Message: lo.ToPtr(err.Error()), + }) + } + + return ctx.JSON(http.StatusOK, codegen.ResponseGetYSKCardListOK{ + Data: lo.ToPtr(lo.Map(cardList, func(yskCard ysk.YSKCard, _ int) codegen.YSKCard { + card, err := ysk.ToCodegenYSKCard(yskCard) + if err != nil { + return codegen.YSKCard{} + } + return card + })), + }) +} diff --git a/service/services.go b/service/services.go index 34d5e76..38cd26c 100644 --- a/service/services.go +++ b/service/services.go @@ -15,6 +15,8 @@ type Services struct { ActionServiceWS *ActionServiceWS SocketIOService *SocketIOService + + YSKService *YSKService } var ( @@ -34,12 +36,15 @@ func NewServices(repository *repository.Repository) Services { eventTypeService := NewEventTypeService(repository) actionTypeService := NewActionTypeService(repository) + eventServiceWS := NewEventServiceWS(eventTypeService) + return Services{ EventTypeService: eventTypeService, - EventServiceWS: NewEventServiceWS(eventTypeService), SocketIOService: NewSocketIOService(), + EventServiceWS: eventServiceWS, ActionTypeService: actionTypeService, ActionServiceWS: NewActionServiceWS(actionTypeService), + YSKService: NewYSKService(repository, eventServiceWS, eventTypeService), } } diff --git a/service/ysk.go b/service/ysk.go new file mode 100644 index 0000000..4772f41 --- /dev/null +++ b/service/ysk.go @@ -0,0 +1,116 @@ +package service + +import ( + "context" + "encoding/json" + "fmt" + "log" + + "github.com/IceWhaleTech/CasaOS-Common/utils/logger" + "github.com/IceWhaleTech/CasaOS-MessageBus/common" + "github.com/IceWhaleTech/CasaOS-MessageBus/model" + "github.com/IceWhaleTech/CasaOS-MessageBus/pkg/ysk" + "github.com/IceWhaleTech/CasaOS-MessageBus/repository" + "github.com/IceWhaleTech/CasaOS-MessageBus/utils" + "go.uber.org/zap" +) + +type YSKService struct { + repository *repository.Repository + ws *EventServiceWS + eventTypeService *EventTypeService +} + +func NewYSKService( + repository *repository.Repository, + ws *EventServiceWS, + ets *EventTypeService, +) *YSKService { + return &YSKService{ + repository: repository, + ws: ws, + eventTypeService: ets, + } +} + +func (s *YSKService) YskCardList(ctx context.Context) ([]ysk.YSKCard, error) { + cardList, err := (*s.repository).GetYSKCardList() + if err != nil { + return []ysk.YSKCard{}, err + } + return cardList, nil +} + +func (s *YSKService) UpsertYSKCard(ctx context.Context, yskCard ysk.YSKCard) error { + // don't store short notice cards + if yskCard.CardType == ysk.CardTypeShortNote { + return nil + } + err := (*s.repository).UpsertYSKCard(yskCard) + return err +} + +func (s *YSKService) DeleteYSKCard(ctx context.Context, id string) error { + return (*s.repository).DeleteYSKCard(id) +} + +func (s *YSKService) Start(init bool) { + if init { + + s.UpsertYSKCard(context.Background(), utils.ApplicationInstallProgress.WithProgress("Installing Jellyfin", 20)) + s.UpsertYSKCard(context.Background(), utils.ZimaOSDataStationNotice) + s.UpsertYSKCard(context.Background(), utils.ZimaOSFileManagementNotice) + s.UpsertYSKCard(context.Background(), utils.ZimaOSRemoteAccessNotice) + s.UpsertYSKCard(context.Background(), utils.DiskInsertNotice) + } + // register event + s.eventTypeService.RegisterEventType(model.EventType{ + SourceID: common.SERVICENAME, + Name: common.EventTypeYSKCardUpsert.Name, + }) + + s.eventTypeService.RegisterEventType(model.EventType{ + SourceID: common.SERVICENAME, + Name: common.EventTypeYSKCardDelete.Name, + }) + + channel, err := s.ws.Subscribe(common.SERVICENAME, []string{ + common.EventTypeYSKCardUpsert.Name, common.EventTypeYSKCardDelete.Name, + }) + if err != nil { + logger.Error("failed to subscribe to event", zap.Error(err)) + return + } + + go func() { + for { + select { + case event, ok := <-channel: + if !ok { + log.Println("channel closed") + } + switch event.Name { + case common.EventTypeYSKCardUpsert.Name: + var card ysk.YSKCard + err := json.Unmarshal([]byte(event.Properties[common.PropertyTypeCardBody.Name]), &card) + if err != nil { + logger.Error("failed to umarshal ysk card", zap.Error(err)) + continue + } + err = s.UpsertYSKCard(context.Background(), card) + if err != nil { + logger.Error("failed to upsert ysk card", zap.Error(err)) + } + case common.EventTypeYSKCardDelete.Name: + err = s.DeleteYSKCard(context.Background(), event.Properties[common.PropertyTypeCardID.Name]) + if err != nil { + logger.Error("failed to delete ysk card", zap.Error(err)) + } + default: + fmt.Println(event) + } + } + } + }() + +} diff --git a/service/ysk_test.go b/service/ysk_test.go new file mode 100644 index 0000000..d4c5621 --- /dev/null +++ b/service/ysk_test.go @@ -0,0 +1,82 @@ +package service_test + +import ( + "context" + "testing" + + "github.com/IceWhaleTech/CasaOS-MessageBus/pkg/ysk" + "github.com/IceWhaleTech/CasaOS-MessageBus/repository" + "github.com/IceWhaleTech/CasaOS-MessageBus/service" + "github.com/IceWhaleTech/CasaOS-MessageBus/utils" + "gotest.tools/assert" +) + +func setup(t *testing.T) (*service.YSKService, func()) { + repository, err := repository.NewDatabaseRepositoryInMemory() + assert.NilError(t, err) + + yskService := service.NewYSKService(&repository, nil, nil) + return yskService, func() { + repository.Close() + } +} + +func TestInsertAndGetCardList(t *testing.T) { + yskService, cleanup := setup(t) + defer cleanup() + + cardList, err := yskService.YskCardList(context.Background()) + assert.NilError(t, err) + assert.Equal(t, len(cardList), 0) + + cardInsertQueue := []ysk.YSKCard{ + utils.ApplicationInstallProgress, utils.ZimaOSDataStationNotice, + } + + for _, card := range cardInsertQueue { + err = yskService.UpsertYSKCard(context.Background(), card) + assert.NilError(t, err) + } + + cardList, err = yskService.YskCardList(context.Background()) + assert.NilError(t, err) + assert.Equal(t, len(cardList), 2) + + for _, card := range cardList { + if card.Id == utils.ApplicationInstallProgress.Id { + assert.DeepEqual(t, card, utils.ApplicationInstallProgress) + } else if card.Id == utils.ZimaOSDataStationNotice.Id { + assert.DeepEqual(t, card, utils.ZimaOSDataStationNotice) + } else { + t.Errorf("unexpected card: %v", card) + } + } +} + +func TestInsertAllTypeCardList(t *testing.T) { + yskService, cleanup := setup(t) + defer cleanup() + + cardList, err := yskService.YskCardList(context.Background()) + assert.NilError(t, err) + assert.Equal(t, len(cardList), 0) + + cardInsertQueue := []ysk.YSKCard{ + utils.ApplicationInstallProgress, utils.ZimaOSDataStationNotice, + // the notice is short. it didn't be stored + utils.ApplicationUpdateNotice, + utils.ApplicationInstallProgress.WithProgress("Installing LinuxServer/Jellyfin", 50), utils.ApplicationInstallProgress.WithProgress("Installing LinuxServer/Jellyfin", 55), + utils.ApplicationInstallProgress.WithProgress("Installing LinuxServer/Jellyfin", 80), utils.ApplicationInstallProgress.WithProgress("Installing LinuxServer/Jellyfin", 99), + utils.ApplicationUpdateNotice, + } + + for _, card := range cardInsertQueue { + err = yskService.UpsertYSKCard(context.Background(), card) + assert.NilError(t, err) + } + + cardList, err = yskService.YskCardList(context.Background()) + assert.NilError(t, err) + assert.Equal(t, len(cardList), 2) + +} diff --git a/utils/fixtures.go b/utils/fixtures.go new file mode 100644 index 0000000..ef3795e --- /dev/null +++ b/utils/fixtures.go @@ -0,0 +1,157 @@ +package utils + +import ( + "github.com/IceWhaleTech/CasaOS-MessageBus/pkg/ysk" +) + +var ( + ApplicationInstallProgress = ysk.YSKCard{ + Id: "task:application:install", + CardType: ysk.CardTypeTask, + RenderType: ysk.RenderTypeCardTask, + Content: ysk.YSKCardContent{ + TitleIcon: "jellyfin logo", + TitleText: "APP Installing", + BodyProgress: &ysk.YSKCardProgress{}, + BodyIconWithText: nil, + BodyList: nil, + FooterActions: nil, + }, + } + + ZimaOSDataStationNotice = ysk.YSKCard{ + Id: "long-notice:disk:insert", + CardType: ysk.CardTypeLongNote, + RenderType: ysk.RenderTypeCardIconTextNotice, + Content: ysk.YSKCardContent{ + TitleIcon: "ZimaOS-Logo", + TitleText: "创建数据工作站", + BodyProgress: nil, + BodyIconWithText: &ysk.YSKCardIconWithText{ + Icon: "disk", + Description: "通过添加硬盘驱动器或固态硬盘来增强你的个人主机,并建立自己的个人数据中心。", + }, + BodyList: nil, + FooterActions: []ysk.YSKCardFooterAction{ + { + Side: "Right", + Style: "primary", + Text: "创建数据工作站", + MessageBus: ysk.YSKCardMessageBusAction{ + Key: "open:disk:insert", + Payload: "{'type':'disk'}", + }, + }, + }, + }, + } + + ZimaOSRemoteAccessNotice = ysk.YSKCard{ + Id: "long-notice:remote:access", + CardType: ysk.CardTypeLongNote, + RenderType: ysk.RenderTypeCardIconTextNotice, + Content: ysk.YSKCardContent{ + TitleIcon: "ZimaOS-Logo", + TitleText: "远程访问", + BodyProgress: nil, + BodyIconWithText: &ysk.YSKCardIconWithText{ + Icon: "remote access", + Description: "通过远程访问,您可以随时随地访问您的个人主机。", + }, + BodyList: nil, + FooterActions: []ysk.YSKCardFooterAction{ + { + Side: "Right", + Style: "primary", + Text: "learn more", + MessageBus: ysk.YSKCardMessageBusAction{ + Key: "open:remote:access", + Payload: "{'type':'remote'}", + }, + }, + }, + }, + } + + ZimaOSFileManagementNotice = ysk.YSKCard{ + Id: "long-notice:file:management", + CardType: ysk.CardTypeLongNote, + RenderType: ysk.RenderTypeCardIconTextNotice, + Content: ysk.YSKCardContent{ + TitleIcon: "ZimaOS-Logo", + TitleText: "文件管理", + BodyProgress: nil, + BodyIconWithText: &ysk.YSKCardIconWithText{ + Icon: "file management", + Description: "使用Files来管理分散的数据,包括备份、 云存储、 NAS 或本地网络中的其他个人数据。", + }, + BodyList: nil, + FooterActions: []ysk.YSKCardFooterAction{ + { + Side: "Right", + Style: "primary", + Text: "learn more", + MessageBus: ysk.YSKCardMessageBusAction{ + Key: "open:file:management", + Payload: "{'type':'file'}", + }, + }, + }, + }, + } + + ApplicationUpdateNotice = ysk.YSKCard{ + Id: "short-notice:application:update", + CardType: ysk.CardTypeShortNote, + RenderType: ysk.RenderTypeCardListNotice, + Content: ysk.YSKCardContent{ + TitleIcon: "app store logo", + TitleText: "有应用更新", + BodyList: []ysk.YSKCardListItem{ + { + Icon: "jellyfin logo", + Description: "Jellyfin 10.7.0", + RightText: "2 days ago", + }, + { + Icon: "next-cloud logo", + Description: "NextCloud 10.7.0", + RightText: "2 days ago", + }, + }, + FooterActions: []ysk.YSKCardFooterAction{ + { + Side: "Right", + Style: "primary", + Text: "更新所有", + MessageBus: ysk.YSKCardMessageBusAction{ + Key: "open:application:update", + Payload: "{'type':'all'}", + }, + }, + }, + }, + } + + DiskInsertNotice = ysk.YSKCard{ + Id: "long-notice:disk:insert", + CardType: ysk.CardTypeLongNote, + RenderType: ysk.RenderTypeCardListNotice, + Content: ysk.YSKCardContent{ + TitleIcon: "disk logo", + TitleText: "硬盘插入", + BodyList: []ysk.YSKCardListItem{ + { + Icon: "disk", + Description: "ZimaOS-HD", + RightText: "2TB", + }, { + Icon: "disk", + Description: "Safe-Storage", + RightText: "2TB", + }, + }, + FooterActions: nil, + }, + } +)