pkg/popup/types.go (100 lines of code) (raw):

// Copyright 2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package popup import ( "fmt" "sync" "github.com/GoogleCloudPlatform/khi/pkg/common" ) var PopupOptionRedirectTargetKey = "redirectTo" var NoCurrentPopup = fmt.Errorf("no active current popup") var CurrentPopupIsntMatchingWithGivenId = fmt.Errorf("given id is not matching with the current popup") // PopupForm is an abstract interface to represent the type to define the form shown from backend to frontend. type PopupForm interface { // GetMetadata return the metadata type needed to show the form to frontend GetMetadata() PopupFormMetadata // Validate receives the input from frontend and returns validation result Validate(req *PopupAnswerResponse) string } // PopupFormMetadata contains data needed for showing input ui on frontend side. type PopupFormMetadata struct { // The title of this form Title string // Type of input field. Currently, only `text` or `popup_redirect` is the supported value. Type string // Description of this form. Description string Placeholder string // The other option values of the request. Options map[string]string `json:"options"` } // PopupFormRequest is a popup display request that is actually passed to the frontend. type PopupFormRequest struct { Id string `json:"id"` Title string `json:"title"` Type string `json:"type"` Description string `json:"description"` Placeholder string `json:"placeholder"` Options map[string]string `json:"options"` } // PopupAnswerResponse is the container of the data to validate/answer shown popup form. type PopupAnswerResponse struct { Id string `json:"id"` Value string `json:"value"` } // PopupAnswerValidationResult is the type passed from the frontend to validate the popup. type PopupAnswerValidationResult struct { Id string `json:"id"` ValidationError string `json:"validationError"` } // PopupManager receives questions shown to user from frontend. type PopupManager struct { newPopupLock sync.Mutex popupWaiter sync.WaitGroup popupResult string currentPopupRequest *PopupFormRequest currentPopup PopupForm } func NewPopupManager() *PopupManager { return &PopupManager{ newPopupLock: sync.Mutex{}, popupWaiter: sync.WaitGroup{}, popupResult: "", currentPopupRequest: nil, currentPopup: nil, } } // ShowPopup shows the popup UI on frontend side and wait until receiving the input. func (p *PopupManager) ShowPopup(popup PopupForm) (string, error) { id := common.NewUUID() metadata := popup.GetMetadata() p.newPopupLock.Lock() defer p.newPopupLock.Unlock() p.currentPopup = popup p.currentPopupRequest = &PopupFormRequest{ Id: id, Title: metadata.Title, Type: metadata.Type, Description: metadata.Description, Placeholder: metadata.Placeholder, Options: metadata.Options, } p.popupWaiter = sync.WaitGroup{} p.popupWaiter.Add(1) p.popupWaiter.Wait() return p.popupResult, nil } // GetCurrentPopup returns currently active popup request data needed in frontend side to show the popup func (p *PopupManager) GetCurrentPopup() *PopupFormRequest { return p.currentPopupRequest } // Validate receives form input and check if the request is valid to receive. If it was not valid, it returns validation error in string. func (p *PopupManager) Validate(request *PopupAnswerResponse) (*PopupAnswerValidationResult, error) { if p.currentPopupRequest == nil { return nil, NoCurrentPopup } if p.currentPopupRequest.Id != request.Id { return nil, CurrentPopupIsntMatchingWithGivenId } return &PopupAnswerValidationResult{ Id: request.Id, ValidationError: p.currentPopup.Validate(request), }, nil } // Answer determine the result of the form. This method assume the request is already validated before. func (p *PopupManager) Answer(request *PopupAnswerResponse) error { if p.currentPopupRequest == nil { return NoCurrentPopup } if p.currentPopupRequest.Id != request.Id { return CurrentPopupIsntMatchingWithGivenId } p.popupResult = request.Value p.currentPopup = nil p.currentPopupRequest = nil p.popupWaiter.Done() return nil } var Instance *PopupManager = NewPopupManager()