Пример #1
0
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",
	}
}
Пример #2
0
// NewAPIToken returns a new APIToken object
func NewAPIToken() *APIToken {
	return &APIToken{
		Token:       wcg.NewUUID(),
		Description: "",
		CreatedAt:   time.Now(),
	}
}
Пример #3
0
// 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()
}
Пример #4
0
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
}
Пример #5
0
// 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
}
Пример #6
0
// 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
}