func (measurement *Measurement) Create(context *ctp.ApiContext) *ctp.HttpError { measurement.BuildLinks(context) measurement.Metric = ctp.ShortenLink(context.CtpBase, measurement.Metric) if !ctp.IsShortLink(measurement.Metric) { return ctp.NewBadRequestError("Invalid metric URL") } if !measurement.State.IsValid() { return ctp.NewBadRequestError("Invalid or missing state value") } if measurement.Result != nil { if err := measurementCheckResult(context, measurement); err != nil { return err } } else { if err := measurementCheckMetric(context, measurement); err != nil { return err } } if measurement.Objective != nil { if err := measurementObjectiveEvaluate(context, measurement); err != nil { return err } } if !ctp.CreateResource(context, "measurements", measurement) { return ctp.NewInternalServerError("Could not save measurement object") } return nil }
func measurementCheckMetric(context *ctp.ApiContext, item *Measurement) *ctp.HttpError { var metric Metric if item.Metric == "" { return ctp.NewBadRequestError("Missing metric attribute in measurement.") } metricParams, ok := ctp.ParseLink(context.CtpBase, "@/metrics/$", item.Metric) if !ok { return ctp.NewBadRequestError("Metric URL is incorrect in measurement") } if !ctp.LoadResource(context, "metrics", ctp.Base64Id(metricParams[0]), &metric) { return ctp.NewBadRequestErrorf("Metric %s does not exist", ctp.ExpandLink(context.CtpBase, item.Metric)) } return nil }
func measurementCheckResult(context *ctp.ApiContext, item *Measurement) *ctp.HttpError { var metric Metric if item.Metric == "" { return ctp.NewBadRequestError("Missing metric attribute in measurement.") } if item.Result == nil { return ctp.NewBadRequestError("Missing result attribute in measurement.") } metricParams, ok := ctp.ParseLink(context.CtpBase, "@/metrics/$", item.Metric) if !ok { return ctp.NewBadRequestError("Metric URL is incorrect in measurement") } if !ctp.LoadResource(context, "metrics", ctp.Base64Id(metricParams[0]), &metric) { return ctp.NewBadRequestErrorf("Metric %s does not exist", ctp.ExpandLink(context.CtpBase, item.Metric)) } for _, row := range item.Result.Value { if len(row) != len(metric.ResultFormat) { return ctp.NewBadRequestErrorf("Metric expects %d columns, but result value provides %d", len(metric.ResultFormat), len(row)) } for name, cell := range row { metricDetailFound := false for _, metricDetail := range metric.ResultFormat { if metricDetail.Name == name { metricDetailFound = true kind := reflect.ValueOf(cell).Kind() switch metricDetail.Type { case "number": if kind != reflect.Float64 { return ctp.NewBadRequestError("Metric expects a number, but result is of different type") } case "boolean": if kind != reflect.Bool { return ctp.NewBadRequestError("Metric expects a boolean, but result is of different type") } case "string": if kind != reflect.String { return ctp.NewBadRequestError("Metric expects a string, but result is of different type") } default: return ctp.NewBadRequestError("Metric type information is incorrect") } break // from for } } if !metricDetailFound { return ctp.NewBadRequestErrorf("Metric does not describe '%s', which appears in result", name) } } } return nil }
func (trigger *Trigger) Create(context *ctp.ApiContext) *ctp.HttpError { trigger.BuildLinks(context) trigger.Measurement = ctp.ShortenLink(context.CtpBase, trigger.Measurement) if !ctp.IsShortLink(trigger.Measurement) { return ctp.NewBadRequestError("Invalid measurement URL") } ok, err := triggerCheckCondition(context, trigger, nil) if err != nil { return ctp.NewBadRequestErrorf("%s", err.Error()) } if ok { triggerLogAndNotify(context, trigger, nil) } if !ctp.CreateResource(context, "triggers", trigger) { return ctp.NewHttpError(http.StatusInternalServerError, "Could not save object") } return nil }
func (measurement *Measurement) Update(context *ctp.ApiContext, update ctp.ResourceUpdater) *ctp.HttpError { measurement.BuildLinks(context) up, ok := update.(*Measurement) if !ok { return ctp.NewInternalServerError("Updated object is not a measurement") // should never happen } switch context.QueryParam { case "userActivated": switch up.State { case "activated": if measurement.State == "deactivated" { measurement.State = "pending" // FIXME: add backoffice logic for notification of state change? } case "deactivated": measurement.State = "deactivated" measurement.Result = nil default: return ctp.NewBadRequestError("state can only be 'activated' or 'deactivated'") } case "objective": measurement.Objective = up.Objective if err := measurementObjectiveEvaluate(context, measurement); err != nil { return err } case "result": if measurement.State == "deactivated" { return ctp.NewHttpError(http.StatusConflict, "Measurement is not in activated state.") } if measurement.State == "pending" { measurement.State = "activated" } if up.Result == nil { return ctp.NewBadRequestError("No result provided in request") } measurement.Result = up.Result if measurement.Result.UpdateTime.IsZero() { measurement.Result.UpdateTime = ctp.Now() } if err := measurementCheckMetric(context, measurement); err != nil { return err } if measurement.Objective != nil { if err := measurementObjectiveEvaluate(context, measurement); err != nil { return err } } measurementTriggersEvaluate(context, measurement) default: return ctp.NewBadRequestError("invalid query string") // should never happen, because already filtered in serve.go } if !ctp.UpdateResource(context, "measurements", measurement.Id, measurement) { return ctp.NewInternalServerError("Could not update measurement object") } return nil }