func NewTestRecord() *wcg.LogRecord { return &wcg.LogRecord{ Level: wcg.LogLevelTrace, Timestamp: time.Date(1984, 9, 22, 12, 01, 28, 20, time.UTC), SourceFile: "sourec.go", SourceLine: 1, SourceStack: []string{"stack1"}, Goroutine: 1, RequestID: wcg.NewUUID(), SessionID: wcg.NewUUID(), UserID: "user1", Data: nil, Text: "This is a test", } }
// NewAPIToken returns a new APIToken object func NewAPIToken() *APIToken { return &APIToken{ Token: wcg.NewUUID(), Description: "", CreatedAt: time.Now(), } }
// TemporaryAllow is a syntax to allow the user to pass the gate `key`. func TemporaryAllow(req *httptest.TestRequest, key string, f func()) { // new user var user wcg.User if req.Request.User == nil { user = &TestUser{ id: wcg.NewUUID(), } req.Request.User = user } else { user = req.Request.User } var g *models.Gate _, value := entities.Gate.Get().Key(key).Cache(true).MustOne(req.Request) if value == nil { if !gates.Exists(key) { req.Request.Logger.Warnf("You are allowing %q gate key temporary on the request but that gate key does not exist.", key) } g = &models.Gate{ Key: key, } } else { g = value.(*models.Gate) } oldUIDs := g.UIDs g.UIDs = append(g.UIDs, user.ID()) gates.PutMulti(req.Request, g) defer func() { if logging { req.Request.Logger.Infof("<< TemporaryAllow %q", key) } g.UIDs = oldUIDs gates.PutMulti(req.Request, g) }() if logging { req.Request.Logger.Infof(">> TemporaryAllow %q", key) } f() }
func (kind *Kind) updateIDField(ent interface{}, idvalue string) string { entValue := reflect.Indirect(reflect.ValueOf(ent)) idfield := entValue.FieldByName(kind.IDFieldName) keystr := idfield.String() idfieldType := idfield.Type() if keystr != "" { // already have an ID value return keystr } // if idvalue is not specified, use UUID. if idvalue == "" { idvalue = string(wcg.NewUUID()) } if idfieldType == _UUIDType && wcg.IsUUID(idvalue) { idfield.Set(reflect.ValueOf(wcg.UUID(idvalue))) } else { idfield.Set(reflect.ValueOf(idvalue)) } return idvalue }
// Define endpoints to process an async task. func (helper *AsyncAPIHelper) Define(path string) *AsyncAPIConfig { if !strings.HasSuffix(path, "/") { panic(fmt.Errorf("Task endpoint must end with '/' (%s)", path)) } api := helper.app.API() // /path/to/something/ => api-path-to-something p := api.Path(path) name := strings.Replace(p[1:len(p)-1], ":", "", -1) // remove parameters name = strings.Replace(name, "/", "-", -1) // replace '/' with '-' config := &AsyncAPIConfig{ Queue: helper.app.Queue().DefinePush(name), app: helper.app, path: path, } // Endpoint that trigger the task. api.POST(path, Handler( func(req *wcg.Request) response.Response { for _, h := range config.triggerHandlers { resp := h.Handle(req) if resp != nil { req.Logger.Debugf("Async Task is not triggered.") return resp } } task := &models.AsyncAPITask{} task.ID = wcg.NewUUID() task.Path = path task.Query = req.HTTPRequest().URL.RawQuery task.Status = models.AsyncAPIStatusReady entities.AsyncAPITask.Put().Key(string(task.ID)).MustUpdate(req, task) // Push a task taskPath := fmt.Sprintf("%s%s.json?%s", req.URL().Path, string(task.ID), req.HTTPRequest().URL.Query().Encode()) if err := config.Queue.PushTask(req, taskPath, nil); err != nil { return response.InternalServerError(req, err) } req.Logger.Infof("Triggered an async task [id:%s path:%s]", task.ID, taskPath) return response.NewJSONResponseWithStatus( AsyncTaskTriggerResponse{task.ID}, 201, ) }, )) // Endpoint that executes the task in background // This endpoint must be called by PushQueue api.POST(path+":taskid.json", Handler( func(req *wcg.Request) response.Response { if !request.IsTask(req) { req.Logger.Warnf( "Non system call to the async task endpoint: UserAgent=%s, IP=%s", req.HTTPRequest().UserAgent(), req.HTTPRequest().RemoteAddr, ) return response.APIOK } _, one := entities.AsyncAPITask.Get().Key(req.Param("taskid")).MustOne(req) if one == nil { req.Logger.Warnf( "Task %q not found: UserAgent=%s, IP=%s", req.Param("taskid"), req.HTTPRequest().UserAgent(), req.HTTPRequest().RemoteAddr, ) return response.APIOK } task := one.(*models.AsyncAPITask) logger := wcg.NewLoggerWithPrefix(req, fmt.Sprintf("[AsyncTask: %s (queue: %s)]", task.ID, name)) if task.Status != models.AsyncAPIStatusReady && task.Status != models.AsyncAPIStatusRunning { logger.Warnf("Task is not ready: Status=%s", task.Status) return response.APIOK } if task.Status == models.AsyncAPIStatusReady { task.StartAt = lib.Now() task.Status = models.AsyncAPIStatusRunning entities.AsyncAPITask.Put().Key(req.Param("taskid")).MustUpdate(req, task) } var err error var progress *models.AsyncAPITaskProgress var resp response.Response func() { defer func() { if x := recover(); x != nil { logger.Errorf("Unhandle error recovered from the task: %v", x) err = fmt.Errorf("%v", x) } }() progress, err = config.processHandler(req, task) }() if progress != nil { task.Progress = append(task.Progress, *progress) } if progress != nil && progress.Next != nil { // update the entity (Progress field) entities.AsyncAPITask.Put().Key(req.Param("taskid")).MustUpdate(req, task) // Push a next request into the queue // This is not required in test environment as test code manually call the next request (see testhelper.AsyncTaskTestRunner#Run code) // The test environment uses the response JSON to call the next URL and that response is used only for this purpose. // If the code cannot push task for the next request, we should stop the task as failure. logger.Infof("Task needs to call recursively: Next parameter is %v", progress.Next) if !req.IsTest() { err = config.Queue.PushTask(req, fmt.Sprintf("%s?%s", req.URL().Path, progress.Next.Encode()), nil) } if err == nil { return response.NewJSONResponse(progress.Next) } // if an error occurrs by PushTask, just stopping the task and update the datastore as failure. } // finished the task task.FinishAt = lib.Now() tt := task.FinishAt.Sub(task.StartAt) if err == nil { task.Status = models.AsyncAPIStatusSuccess logger.Infof("Task finished successfully (time: %s)", tt.String()) resp = response.APIOK } else { task.Error = err.Error() task.Status = models.AsyncAPIStatusFailure logger.Errorf("Task failed (time: %s): %s", tt.String(), task.Error) resp = response.APIInternalServerError } entities.AsyncAPITask.Put().Key(req.Param("taskid")).MustUpdate(req, task) return resp }, )) // Endpoint that returns the task status. api.GET(path+":taskid.json", Handler( func(req *wcg.Request) response.Response { for _, h := range config.triggerHandlers { resp := h.Handle(req) if resp != nil { return resp } } _, one := entities.AsyncAPITask.Get().Key(req.Param("taskid")).MustOne(req) if one == nil { return response.NotFound(req) } task := one.(*models.AsyncAPITask) return response.NewJSONResponse( AsyncTaskMonitorResponse{ ID: task.ID, Status: task.Status, Progress: task.LastProgress(), }, ) }, )) return config }
// Cache creates a cache into `albumName`` func (cacher *ImageCacher) Cache(url string) (*models.ImageCache, error) { imageCacheKind := cacher.kind get := imageCacheKind.Get().Key(url) _, v := get.MustOne(cacher.req) if v != nil { return v.(*models.ImageCache), nil } cacher.logger.Debugf("Selecting album ...") album, err := cacher.selectAlbum() if err != nil { return nil, err } cacher.logger.Infof("Start caching %q", url) resp, err := cacher.openImageStream(url) if err != nil { return nil, err } defer resp.Body.Close() imageID := wcg.NewUUID() mediaInfo := media.NewUploadMediaInfo(string(imageID)) mediaInfo.Summary = url var photo *media.Media err = retry.Do(func() error { var err error photo, err = cacher.client.UploadMedia( "", album.ID, mediaInfo, resp.Header.Get("content-type"), resp.ContentLength, resp.Body, ) return err }, retry.Interval(5*time.Second), retry.MaxRetries(5)) if err != nil { return nil, fmt.Errorf("Failed to upload a media into album:%s: %v", album.ID, err) } // select content URL if len(photo.Contents) == 0 { return nil, fmt.Errorf("No contents are available google photo.") } image := &models.ImageCache{ URL: url, GPhotoAlbumID: photo.AlbumID, GPhotoID: photo.ID, Width: photo.Width, Height: photo.Height, } // select better content based on 'width' for _, c := range photo.Contents { if image.GPhotoURL == "" || c.Width > image.Width { image.GPhotoURL = c.URL image.Width = c.Width image.Height = c.Height } } _, v = imageCacheKind.Put().Key(url).MustUpdate(cacher.req, image) cacher.logger.Infof( "created - GPHOTO Path: %s/%s URL: %s (from %s)", image.GPhotoAlbumID, image.GPhotoID, image.GPhotoURL, image.URL, ) return v.(*models.ImageCache), nil }