// Save updates the given tracker in storage.
// returns NotFoundError, ConversionError or InternalError
func (r *GormTrackerRepository) Save(ctx context.Context, t app.Tracker) (*app.Tracker, error) {
	res := Tracker{}
	id, err := strconv.ParseUint(t.ID, 10, 64)
	if err != nil || id == 0 {
		return nil, NotFoundError{entity: "tracker", ID: t.ID}
	}

	log.Info(ctx, map[string]interface{}{
		"pkg":       "remoteworkitem",
		"trackerID": id,
	}, "Looking for a tracker repository with id ", id)

	tx := r.db.First(&res, id)
	if tx.RecordNotFound() {
		log.Error(ctx, map[string]interface{}{
			"trackerID": id,
		}, "tracker repository not found")

		return nil, NotFoundError{entity: "tracker", ID: t.ID}
	}
	_, present := RemoteWorkItemImplRegistry[t.Type]
	// Ensure we support this remote tracker.
	if present != true {
		return nil, BadParameterError{parameter: "type", value: t.Type}
	}

	newT := Tracker{
		ID:   id,
		URL:  t.URL,
		Type: t.Type}

	if err := tx.Save(&newT).Error; err != nil {
		log.Error(ctx, map[string]interface{}{
			"trackerID": newT.ID,
			"err":       err,
		}, "unable to save tracker repository")
		return nil, InternalError{simpleError{err.Error()}}
	}

	log.Info(ctx, map[string]interface{}{
		"pkg":     "remoteworkitem",
		"tracker": newT.ID,
	}, "Tracker repository successfully updated")

	t2 := app.Tracker{
		ID:   strconv.FormatUint(id, 10),
		URL:  t.URL,
		Type: t.Type}

	return &t2, nil
}
Example #2
0
// migrateToNextVersion migrates the database to the nextVersion.
// If the database is already at nextVersion or higher, the nextVersion
// will be set to the actual next version.
func migrateToNextVersion(tx *sql.Tx, nextVersion *int64, m migrations) error {
	// Obtain exclusive transaction level advisory that doesn't depend on any table.
	// Once obtained, the lock is held for the remainder of the current transaction.
	// (There is no UNLOCK TABLE command; locks are always released at transaction end.)
	if _, err := tx.Exec("SELECT pg_advisory_xact_lock($1)", AdvisoryLockID); err != nil {
		return errs.Errorf("Failed to acquire lock: %s\n", err)
	}

	// Determine current version and adjust the outmost loop
	// iterator variable "version"
	currentVersion, err := getCurrentVersion(tx)
	if err != nil {
		return errs.WithStack(err)
	}
	*nextVersion = currentVersion + 1
	if *nextVersion >= int64(len(m)) {
		// No further updates to apply (this is NOT an error)
		log.Info(nil, map[string]interface{}{
			"pkg":            "migration",
			"nextVersion":    *nextVersion,
			"currentVersion": currentVersion,
		}, "Current version %d. Nothing to update.", currentVersion)
		return nil
	}

	log.Info(nil, map[string]interface{}{
		"pkg":            "migration",
		"nextVersion":    *nextVersion,
		"currentVersion": currentVersion,
	}, "Attempt to update DB to version ", *nextVersion)

	// Apply all the updates of the next version
	for j := range m[*nextVersion] {
		if err := m[*nextVersion][j](tx); err != nil {
			return errs.Errorf("Failed to execute migration of step %d of version %d: %s\n", j, *nextVersion, err)
		}
	}

	if _, err := tx.Exec("INSERT INTO version(version) VALUES($1)", *nextVersion); err != nil {
		return errs.Errorf("Failed to update DB to version %d: %s\n", *nextVersion, err)
	}

	log.Info(nil, map[string]interface{}{
		"pkg":            "migration",
		"nextVersion":    *nextVersion,
		"currentVersion": currentVersion,
	}, "Successfully updated DB to version ", *nextVersion)

	return nil
}
Example #3
0
// Delete deletes the work item link with the given id
// returns NotFoundError or InternalError
func (r *GormWorkItemLinkRepository) Delete(ctx context.Context, ID string) error {
	id, err := satoriuuid.FromString(ID)
	if err != nil {
		// treat as not found: clients don't know it must be a UUID
		return errors.NewNotFoundError("work item link", ID)
	}
	var link = WorkItemLink{
		ID: id,
	}
	log.Info(ctx, map[string]interface{}{
		"pkg":   "link",
		"wilID": ID,
	}, "Deleting the work item link repository")

	db := r.db.Delete(&link)
	if db.Error != nil {
		log.Error(ctx, map[string]interface{}{
			"wilID": ID,
			"err":   db.Error,
		}, "unable to delete work item link repository")
		return errors.NewInternalError(db.Error.Error())
	}
	if db.RowsAffected == 0 {
		return errors.NewNotFoundError("work item link", id.String())
	}
	return nil
}
// LoadTypeFromDB return work item type for the given id
func (r *GormWorkItemTypeRepository) LoadTypeFromDB(ctx context.Context, name string) (*WorkItemType, error) {
	log.Logger().Infoln("Loading work item type", name)
	res, ok := cache.Get(name)
	if !ok {
		log.Info(ctx, map[string]interface{}{
			"pkg":  "workitem",
			"type": name,
		}, "Work item type doesn't exist in the cache. Loading from DB...")
		res = WorkItemType{}

		db := r.db.Model(&res).Where("name=?", name).First(&res)
		if db.RecordNotFound() {
			log.Error(ctx, map[string]interface{}{
				"witName": name,
			}, "work item type repository not found")
			return nil, errors.NewNotFoundError("work item type", name)
		}
		if err := db.Error; err != nil {
			return nil, errors.NewInternalError(err.Error())
		}
		cache.Put(res)
	}

	return &res, nil
}
// List returns tracker selected by the given criteria.Expression, starting with start (zero-based) and returning at most limit items
func (r *GormTrackerRepository) List(ctx context.Context, criteria criteria.Expression, start *int, limit *int) ([]*app.Tracker, error) {
	where, parameters, err := workitem.Compile(criteria)
	if err != nil {
		return nil, BadParameterError{"expression", criteria}
	}

	log.Info(ctx, map[string]interface{}{
		"pkg":   "remoteworkitem",
		"query": where,
	}, "Executing tracker repository query...")

	var rows []Tracker
	db := r.db.Where(where, parameters...)
	if start != nil {
		db = db.Offset(*start)
	}
	if limit != nil {
		db = db.Limit(*limit)
	}
	if err := db.Find(&rows).Error; err != nil {
		return nil, errors.WithStack(err)
	}
	result := make([]*app.Tracker, len(rows))

	for i, tracker := range rows {
		t := app.Tracker{
			ID:   strconv.FormatUint(tracker.ID, 10),
			URL:  tracker.URL,
			Type: tracker.Type}
		result[i] = &t
	}
	return result, nil
}
// Load returns the tracker configuration for the given id
// returns NotFoundError, ConversionError or InternalError
func (r *GormTrackerRepository) Load(ctx context.Context, ID string) (*app.Tracker, error) {
	id, err := strconv.ParseUint(ID, 10, 64)
	if err != nil || id == 0 {
		// treating this as a not found error: the fact that we're using number internal is implementation detail
		return nil, NotFoundError{"tracker", ID}
	}

	log.Info(ctx, map[string]interface{}{
		"pkg":       "remoteworkitem",
		"trackerID": id,
	}, "Loading tracker repository...")

	res := Tracker{}
	tx := r.db.First(&res, id)
	if tx.RecordNotFound() {
		log.Error(ctx, map[string]interface{}{
			"trackerID": ID,
		}, "tracker repository not found")

		return nil, NotFoundError{"tracker", ID}
	}
	if tx.Error != nil {
		return nil, InternalError{simpleError{fmt.Sprintf("error while loading: %s", tx.Error.Error())}}
	}
	t := app.Tracker{
		ID:   strconv.FormatUint(res.ID, 10),
		URL:  res.URL,
		Type: res.Type}

	return &t, nil
}
// Create creates a new tracker configuration in the repository
// returns BadParameterError, ConversionError or InternalError
func (r *GormTrackerRepository) Create(ctx context.Context, url string, typeID string) (*app.Tracker, error) {
	//URL Validation
	isValid := govalidator.IsURL(url)
	if isValid != true {
		return nil, BadParameterError{parameter: "url", value: url}
	}

	_, present := RemoteWorkItemImplRegistry[typeID]
	// Ensure we support this remote tracker.
	if present != true {
		return nil, BadParameterError{parameter: "type", value: typeID}
	}
	t := Tracker{
		URL:  url,
		Type: typeID}
	tx := r.db
	if err := tx.Create(&t).Error; err != nil {
		return nil, InternalError{simpleError{err.Error()}}
	}
	log.Info(ctx, map[string]interface{}{
		"pkg":     "remoteworkitem",
		"tracker": t,
	}, "Tracker reposity created")

	t2 := app.Tracker{
		ID:   strconv.FormatUint(t.ID, 10),
		URL:  url,
		Type: typeID}

	return &t2, nil
}
Example #8
0
// Save updates the given space in the db. Version must be the same as the one in the stored version
// returns NotFoundError, BadParameterError, VersionConflictError or InternalError
func (r *GormRepository) Save(ctx context.Context, p *Space) (*Space, error) {
	pr := Space{}
	tx := r.db.Where("id=?", p.ID).First(&pr)
	oldVersion := p.Version
	p.Version++
	if tx.RecordNotFound() {
		// treating this as a not found error: the fact that we're using number internal is implementation detail
		return nil, errors.NewNotFoundError("space", p.ID.String())
	}
	if err := tx.Error; err != nil {
		return nil, errors.NewInternalError(err.Error())
	}
	tx = tx.Where("Version = ?", oldVersion).Save(p)
	if err := tx.Error; err != nil {
		if gormsupport.IsCheckViolation(tx.Error, "spaces_name_check") {
			return nil, errors.NewBadParameterError("Name", p.Name).Expected("not empty")
		}
		if gormsupport.IsUniqueViolation(tx.Error, "spaces_name_idx") {
			return nil, errors.NewBadParameterError("Name", p.Name).Expected("unique")
		}
		return nil, errors.NewInternalError(err.Error())
	}
	if tx.RowsAffected == 0 {
		return nil, errors.NewVersionConflictError("version conflict")
	}

	log.Info(ctx, map[string]interface{}{
		"pkg":     "space",
		"spaceID": p.ID,
	}, "space updated successfully")
	return p, nil
}
// Load returns the tracker query for the given id
// returns NotFoundError, ConversionError or InternalError
func (r *GormTrackerQueryRepository) Load(ctx context.Context, ID string) (*app.TrackerQuery, error) {
	id, err := strconv.ParseUint(ID, 10, 64)
	if err != nil || id == 0 {
		// treating this as a not found error: the fact that we're using number internal is implementation detail
		return nil, NotFoundError{"tracker query", ID}
	}

	log.Info(ctx, map[string]interface{}{
		"pkg":            "remoteworkitem",
		"trackerQueryid": id,
	}, "Loading the tracker query")

	res := TrackerQuery{}
	if r.db.First(&res, id).RecordNotFound() {
		log.Error(ctx, map[string]interface{}{
			"trackerID": id,
		}, "tracker resource not found")
		return nil, NotFoundError{"tracker query", ID}
	}
	tq := app.TrackerQuery{
		ID:        strconv.FormatUint(res.ID, 10),
		Query:     res.Query,
		Schedule:  res.Schedule,
		TrackerID: strconv.FormatUint(res.TrackerID, 10)}

	return &tq, nil
}
Example #10
0
// Delete implements application.WorkItemRepository
func (r *UndoableWorkItemRepository) Delete(ctx context.Context, ID string) error {
	id, err := strconv.ParseUint(ID, 10, 64)
	if err != nil {
		// treating this as a not found error: the fact that we're using number internal is implementation detail
		return errors.NewNotFoundError("work item", ID)
	}

	log.Info(ctx, map[string]interface{}{
		"pkg": "workitem",
		"id":  id,
	}, "Loading work iteme")

	old := WorkItem{}
	db := r.wrapped.db.First(&old, id)
	if db.Error != nil {
		return errors.NewInternalError(fmt.Sprintf("could not load %s, %s", ID, db.Error.Error()))
	}

	err = r.wrapped.Delete(ctx, ID)
	if err == nil {
		r.undo.Append(func(db *gorm.DB) error {
			old.DeletedAt = nil
			db = db.Save(&old)
			return db.Error
		})
	}
	return errs.WithStack(err)
}
Example #11
0
// Load returns the work item link type for the given ID.
// Returns NotFoundError, ConversionError or InternalError
func (r *GormWorkItemLinkTypeRepository) Load(ctx context.Context, ID string) (*app.WorkItemLinkTypeSingle, error) {
	id, err := satoriuuid.FromString(ID)
	if err != nil {
		// treat as not found: clients don't know it must be a UUID
		return nil, errors.NewNotFoundError("work item link type", ID)
	}
	log.Info(ctx, map[string]interface{}{
		"pkg":    "link",
		"wiltID": ID,
	}, "Loading work item link type")
	res := WorkItemLinkType{}
	db := r.db.Model(&res).Where("id=?", ID).First(&res)
	if db.RecordNotFound() {
		log.Error(ctx, map[string]interface{}{
			"wiltID": ID,
		}, "work item link type not found")
		return nil, errors.NewNotFoundError("work item link type", id.String())
	}
	if db.Error != nil {
		return nil, errors.NewInternalError(db.Error.Error())
	}
	// Convert the created link type entry into a JSONAPI response
	result := ConvertLinkTypeFromModel(res)

	return &result, nil
}
// Delete deletes the work item link category with the given id
// returns NotFoundError or InternalError
func (r *GormWorkItemLinkCategoryRepository) Delete(ctx context.Context, ID string) error {
	id, err := satoriuuid.FromString(ID)
	if err != nil {
		// treat as not found: clients don't know it must be a UUID
		return errors.NewNotFoundError("work item link category", ID)
	}

	var cat = WorkItemLinkCategory{
		ID: id,
	}

	log.Info(ctx, map[string]interface{}{
		"pkg":    "link",
		"wilcID": ID,
	}, "Work item link category to delete")

	db := r.db.Delete(&cat)
	if db.Error != nil {
		return errors.NewInternalError(db.Error.Error())
	}

	if db.RowsAffected == 0 {
		return errors.NewNotFoundError("work item link category", id.String())
	}
	return nil
}
Example #13
0
func createOrUpdateType(typeName string, extendedTypeName *string, fields map[string]app.FieldDefinition, ctx context.Context, witr *workitem.GormWorkItemTypeRepository, db *gorm.DB) error {
	wit, err := witr.LoadTypeFromDB(ctx, typeName)
	cause := errs.Cause(err)
	switch cause.(type) {
	case errors.NotFoundError:
		_, err := witr.Create(ctx, extendedTypeName, typeName, fields)
		if err != nil {
			return errs.WithStack(err)
		}
	case nil:
		log.Info(ctx, map[string]interface{}{
			"pkg":      "migration",
			"typeName": typeName,
		}, "Work item type %s exists, will update/overwrite the fields only and parentPath", typeName)

		path := typeName
		convertedFields, err := workitem.TEMPConvertFieldTypesToModel(fields)
		if extendedTypeName != nil {
			log.Info(ctx, map[string]interface{}{
				"pkg":              "migration",
				"typeName":         typeName,
				"extendedTypeName": *extendedTypeName,
			}, "Work item type %s extends another type %v will copy fields from the extended type", typeName, *extendedTypeName)

			extendedWit, err := witr.LoadTypeFromDB(ctx, *extendedTypeName)
			if err != nil {
				return errs.WithStack(err)
			}
			path = extendedWit.Path + workitem.GetTypePathSeparator() + path

			//load fields from the extended type
			err = loadFields(ctx, extendedWit, convertedFields)
			if err != nil {
				return errs.WithStack(err)
			}
		}

		if err != nil {
			return errs.WithStack(err)
		}
		wit.Fields = convertedFields
		wit.Path = path
		db = db.Save(wit)
		return db.Error
	}
	return nil
}
Example #14
0
// Migrate executes the required migration of the database on startup.
// For each successful migration, an entry will be written into the "version"
// table, that states when a certain version was reached.
func Migrate(db *sql.DB) error {
	var err error

	if db == nil {
		return errs.Errorf("Database handle is nil\n")
	}

	m := getMigrations()

	var tx *sql.Tx
	for nextVersion := int64(0); nextVersion < int64(len(m)) && err == nil; nextVersion++ {

		tx, err = db.Begin()
		if err != nil {
			return errs.Errorf("Failed to start transaction: %s\n", err)
		}

		err = migrateToNextVersion(tx, &nextVersion, m)

		if err != nil {
			oldErr := err
			log.Info(nil, map[string]interface{}{
				"pkg":         "migration",
				"nextVersion": nextVersion,
				"migrations":  m,
				"err":         err,
			}, "Rolling back transaction due to: ", err)

			if err = tx.Rollback(); err != nil {
				log.Error(nil, map[string]interface{}{
					"nextVersion": nextVersion,
					"migrations":  m,
					"err":         err,
				}, "error while rolling back transaction: ", err)
				return errs.Errorf("Error while rolling back transaction: %s\n", err)
			}
			return oldErr
		}

		if err = tx.Commit(); err != nil {
			log.Error(nil, map[string]interface{}{
				"migrations": m,
				"err":        err,
			}, "error during transaction commit: ", err)
			return errs.Errorf("Error during transaction commit: %s\n", err)
		}

	}

	if err != nil {
		log.Error(nil, map[string]interface{}{
			"migrations": m,
			"err":        err,
		}, "migration failed with error: ", err)
		return errs.Errorf("Migration failed with error: %s\n", err)
	}

	return nil
}
// Create creates a new tracker query in the repository
// returns BadParameterError, ConversionError or InternalError
func (r *GormTrackerQueryRepository) Create(ctx context.Context, query string, schedule string, tracker string) (*app.TrackerQuery, error) {
	tid, err := strconv.ParseUint(tracker, 10, 64)
	if err != nil || tid == 0 {
		// treating this as a not found error: the fact that we're using number internal is implementation detail
		return nil, NotFoundError{"tracker", tracker}
	}

	log.Info(ctx, map[string]interface{}{
		"pkg":       "remoteworkitem",
		"trackerID": tid,
	}, "Tracker ID to be created")

	tq := TrackerQuery{
		Query:     query,
		Schedule:  schedule,
		TrackerID: tid}
	tx := r.db
	if err := tx.Create(&tq).Error; err != nil {
		log.Error(ctx, map[string]interface{}{
			"trackerID":    tid,
			"trackerQuery": query,
		}, "unable to create the tracker query")
		return nil, InternalError{simpleError{err.Error()}}
	}

	tq2 := app.TrackerQuery{
		ID:        strconv.FormatUint(tq.ID, 10),
		Query:     query,
		Schedule:  schedule,
		TrackerID: tracker}

	log.Info(ctx, map[string]interface{}{
		"pkg":          "remoteworkitem",
		"trackerID":    tid,
		"trackerQuery": tq,
	}, "Created tracker query")

	return &tq2, nil
}
Example #16
0
// Save updates the given work item link in storage. Version must be the same as the one int the stored version.
// returns NotFoundError, VersionConflictError, ConversionError or InternalError
func (r *GormWorkItemLinkRepository) Save(ctx context.Context, lt app.WorkItemLinkSingle) (*app.WorkItemLinkSingle, error) {
	res := WorkItemLink{}
	if lt.Data.ID == nil {
		return nil, errors.NewBadParameterError("work item link", nil)
	}
	db := r.db.Model(&res).Where("id=?", *lt.Data.ID).First(&res)
	if db.RecordNotFound() {
		log.Error(ctx, map[string]interface{}{
			"wilID": *lt.Data.ID,
		}, "work item link not found")
		return nil, errors.NewNotFoundError("work item link", *lt.Data.ID)
	}
	if db.Error != nil {
		log.Error(ctx, map[string]interface{}{
			"wilID": *lt.Data.ID,
			"err":   db.Error,
		}, "unable to find work item link")
		return nil, errors.NewInternalError(db.Error.Error())
	}
	if lt.Data.Attributes.Version == nil || res.Version != *lt.Data.Attributes.Version {
		return nil, errors.NewVersionConflictError("version conflict")
	}
	if err := ConvertLinkToModel(lt, &res); err != nil {
		return nil, errs.WithStack(err)
	}
	res.Version = res.Version + 1
	if err := r.ValidateCorrectSourceAndTargetType(ctx, res.SourceID, res.TargetID, res.LinkTypeID); err != nil {
		return nil, errs.WithStack(err)
	}
	db = r.db.Save(&res)
	if db.Error != nil {
		log.Error(ctx, map[string]interface{}{
			"wilID": res.ID,
			"err":   db.Error,
		}, "unable to save work item link")
		return nil, errors.NewInternalError(db.Error.Error())
	}

	log.Info(ctx, map[string]interface{}{
		"pkg":   "link",
		"wilID": res.ID,
	}, "Work item link updated")
	result := ConvertLinkFromModel(res)
	return &result, nil
}
Example #17
0
// LoadTypeFromDB return work item link type for the given ID
func (r *GormWorkItemLinkTypeRepository) LoadTypeFromDBByID(ctx context.Context, ID satoriuuid.UUID) (*WorkItemLinkType, error) {
	log.Info(ctx, map[string]interface{}{
		"pkg":    "link",
		"wiltID": ID.String(),
	}, "Loading work item link type with ID ", ID)

	res := WorkItemLinkType{}
	db := r.db.Model(&res).Where("ID=?", ID.String()).First(&res)
	if db.RecordNotFound() {
		log.Error(ctx, map[string]interface{}{
			"wiltID": ID.String(),
		}, "work item link type not found")
		return nil, errors.NewNotFoundError("work item link type", ID.String())
	}
	if db.Error != nil {
		return nil, errors.NewInternalError(db.Error.Error())
	}
	return &res, nil
}
// LoadCategoryFromDB return work item link category for the name
func (r *GormWorkItemLinkCategoryRepository) LoadCategoryFromDB(ctx context.Context, name string) (*WorkItemLinkCategory, error) {
	log.Info(ctx, map[string]interface{}{
		"pkg":          "link",
		"categoryName": name,
	}, "Loading work item link category: %s", name)

	res := WorkItemLinkCategory{}
	db := r.db.Model(&res).Where("name=?", name).First(&res)
	if db.RecordNotFound() {
		log.Error(ctx, map[string]interface{}{
			"wilcName": name,
		}, "work item link category not found")
		return nil, errors.NewNotFoundError("work item link category", name)
	}
	if db.Error != nil {
		return nil, errors.NewInternalError(db.Error.Error())
	}
	return &res, nil
}
Example #19
0
// Create creates a new Space in the db
// returns BadParameterError or InternalError
func (r *GormRepository) Create(ctx context.Context, space *Space) (*Space, error) {
	space.ID = satoriuuid.NewV4()

	tx := r.db.Create(space)
	if err := tx.Error; err != nil {
		if gormsupport.IsCheckViolation(tx.Error, "spaces_name_check") {
			return nil, errors.NewBadParameterError("Name", space.Name).Expected("not empty")
		}
		if gormsupport.IsUniqueViolation(tx.Error, "spaces_name_idx") {
			return nil, errors.NewBadParameterError("Name", space.Name).Expected("unique")
		}
		return nil, errors.NewInternalError(err.Error())
	}

	log.Info(ctx, map[string]interface{}{
		"pkg":     "space",
		"spaceID": space.ID,
	}, "Space created successfully")
	return space, nil
}
Example #20
0
func printUserInfo() {
	u, err := user.Current()
	if err != nil {
		log.Warn(nil, map[string]interface{}{
			"err": fmt.Sprintf("%+v", err),
		}, "failed to get current user")
	} else {
		log.Info(nil, map[string]interface{}{
			"username": u.Username,
			"uuid":     u.Uid,
		}, "Running as user name '%s' with UID %s.", u.Username, u.Uid)
		/*
			g, err := user.LookupGroupId(u.Gid)
			if err != nil {
				fmt.Printf("Failed to lookup group: %", err.Error())
			} else {
				fmt.Printf("Running with group \"%s\" with GID %s.\n", g.Name, g.Gid)
			}
		*/
	}
}
Example #21
0
// LoadTypeFromDB return work item link type for the given name in the correct link category
// NOTE: Two link types can coexist with different categoryIDs.
func (r *GormWorkItemLinkTypeRepository) LoadTypeFromDBByNameAndCategory(ctx context.Context, name string, categoryId satoriuuid.UUID) (*WorkItemLinkType, error) {
	log.Info(ctx, map[string]interface{}{
		"pkg":        "link",
		"wiltName":   name,
		"categoryId": categoryId,
	}, "Loading work item link type %s with category ID %s", name, categoryId.String())

	res := WorkItemLinkType{}
	db := r.db.Model(&res).Where("name=? AND link_category_id=?", name, categoryId.String()).First(&res)
	if db.RecordNotFound() {
		log.Error(ctx, map[string]interface{}{
			"wiltName":   name,
			"categoryId": categoryId.String(),
		}, "work item link type not found")
		return nil, errors.NewNotFoundError("work item link type", name)
	}
	if db.Error != nil {
		return nil, errors.NewInternalError(db.Error.Error())
	}
	return &res, nil
}
Example #22
0
func createOrUpdateWorkItemLinkCategory(ctx context.Context, linkCatRepo *link.GormWorkItemLinkCategoryRepository, name string, description string) error {
	cat, err := linkCatRepo.LoadCategoryFromDB(ctx, name)
	cause := errs.Cause(err)
	switch cause.(type) {
	case errors.NotFoundError:
		_, err := linkCatRepo.Create(ctx, &name, &description)
		if err != nil {
			return errs.WithStack(err)
		}
	case nil:
		log.Info(ctx, map[string]interface{}{
			"pkg":      "migration",
			"category": name,
		}, "Work item link category %s exists, will update/overwrite the description", name)

		cat.Description = &description
		linkCat := link.ConvertLinkCategoryFromModel(*cat)
		_, err = linkCatRepo.Save(ctx, linkCat)
		return errs.WithStack(err)
	}
	return nil
}
Example #23
0
func createOrUpdateWorkItemLinkType(ctx context.Context, linkCatRepo *link.GormWorkItemLinkCategoryRepository, linkTypeRepo *link.GormWorkItemLinkTypeRepository, name, description, topology, forwardName, reverseName, sourceTypeName, targetTypeName, linkCatName string) error {
	cat, err := linkCatRepo.LoadCategoryFromDB(ctx, linkCatName)
	if err != nil {
		return errs.WithStack(err)
	}

	linkType, err := linkTypeRepo.LoadTypeFromDBByNameAndCategory(ctx, name, cat.ID)
	lt := link.WorkItemLinkType{
		Name:           name,
		Description:    &description,
		Topology:       topology,
		ForwardName:    forwardName,
		ReverseName:    reverseName,
		SourceTypeName: sourceTypeName,
		TargetTypeName: targetTypeName,
		LinkCategoryID: cat.ID,
	}

	cause := errs.Cause(err)
	switch cause.(type) {
	case errors.NotFoundError:
		_, err := linkTypeRepo.Create(ctx, lt.Name, lt.Description, lt.SourceTypeName, lt.TargetTypeName, lt.ForwardName, lt.ReverseName, lt.Topology, lt.LinkCategoryID)
		if err != nil {
			return errs.WithStack(err)
		}
	case nil:
		log.Info(ctx, map[string]interface{}{
			"pkg":  "migration",
			"wilt": name,
		}, "Work item link type %s exists, will update/overwrite all fields", name)

		lt.ID = linkType.ID
		lt.Version = linkType.Version
		_, err = linkTypeRepo.Save(ctx, link.ConvertLinkTypeFromModel(lt))
		return errs.WithStack(err)
	}
	return nil
}
// LoadFromDB returns the work item with the given ID in model representation.
func (r *GormWorkItemRepository) LoadFromDB(ctx context.Context, ID string) (*WorkItem, error) {
	id, err := strconv.ParseUint(ID, 10, 64)
	if err != nil || id == 0 {
		// treating this as a not found error: the fact that we're using number internal is implementation detail
		return nil, errors.NewNotFoundError("work item", ID)
	}
	log.Info(nil, map[string]interface{}{
		"pkg":  "workitem",
		"wiID": ID,
	}, "Loading work item")

	res := WorkItem{}
	tx := r.db.First(&res, id)
	if tx.RecordNotFound() {
		log.Error(nil, map[string]interface{}{
			"wiID": ID,
		}, "work item not found")
		return nil, errors.NewNotFoundError("work item", ID)
	}
	if tx.Error != nil {
		return nil, errors.NewInternalError(tx.Error.Error())
	}
	return &res, nil
}
Example #25
0
// Perform performs authenticatin
func (keycloak *KeycloakOAuthProvider) Perform(ctx *app.AuthorizeLoginContext) error {
	state := ctx.Params.Get("state")
	code := ctx.Params.Get("code")
	referer := ctx.RequestData.Header.Get("Referer")

	if code != "" {
		// After redirect from oauth provider

		// validate known state
		var knownReferer string
		defer func() {
			delete(stateReferer, state)
		}()

		knownReferer = stateReferer[state]
		if state == "" || knownReferer == "" {
			log.Error(ctx, map[string]interface{}{
				"state":   state,
				"referer": knownReferer,
			}, "state or known referer was empty")

			jerrors, _ := jsonapi.ErrorToJSONAPIErrors(goa.ErrUnauthorized("State or known referer was empty"))
			return ctx.Unauthorized(jerrors)
		}

		keycloakToken, err := keycloak.config.Exchange(ctx, code)
		if err != nil || keycloakToken.AccessToken == "" {
			log.Error(ctx, map[string]interface{}{
				"code": code,
				"err":  err,
			}, "keycloak exchange operation failed")
			return redirectWithError(ctx, knownReferer, InvalidCodeError)
		}

		_, _, err = keycloak.CreateKeycloakUser(keycloakToken.AccessToken, ctx)
		if err != nil {
			log.Error(ctx, map[string]interface{}{
				"token": keycloakToken.AccessToken,
				"err":   err,
			}, "failed to create a user and KeyCloak identity using the access token")
			return redirectWithError(ctx, knownReferer, err.Error())

		}

		referelURL, err := url.Parse(knownReferer)
		if err != nil {
			return redirectWithError(ctx, knownReferer, err.Error())
		}

		err = encodeToken(referelURL, keycloakToken)
		if err != nil {
			return redirectWithError(ctx, knownReferer, err.Error())
		}
		ctx.ResponseData.Header().Set("Location", referelURL.String())
		return ctx.TemporaryRedirect()
	}

	// First time access, redirect to oauth provider

	// store referer id to state for redirect later
	log.Info(ctx, map[string]interface{}{
		"pkg":     "login",
		"referer": referer,
	}, "Got Request from!")

	state = uuid.NewV4().String()

	mapLock.Lock()
	defer mapLock.Unlock()

	stateReferer[state] = referer

	keycloak.config.RedirectURL = rest.AbsoluteURL(ctx.RequestData, "/api/login/authorize")

	redirectURL := keycloak.config.AuthCodeURL(state, oauth2.AccessTypeOnline)

	ctx.ResponseData.Header().Set("Location", redirectURL)
	return ctx.TemporaryRedirect()
}
// Map a remote work item into an ALM work item and persist it into the database.
func convert(db *gorm.DB, tID int, item TrackerItemContent, provider string) (*app.WorkItem, error) {
	remoteID := item.ID
	content := string(item.Content)

	wir := workitem.NewWorkItemRepository(db)
	ti := TrackerItem{Item: content, RemoteItemID: remoteID, TrackerID: uint64(tID)}

	// Converting the remote item to a local work item
	remoteTrackerItemMethodRef, ok := RemoteWorkItemImplRegistry[provider]
	if !ok {
		return nil, BadParameterError{parameter: provider, value: provider}
	}
	remoteTrackerItem, err := remoteTrackerItemMethodRef(ti)
	if err != nil {
		return nil, InternalError{simpleError{message: " Error parsing the tracker data "}}
	}
	workItem, err := Map(remoteTrackerItem, WorkItemKeyMaps[provider])
	if err != nil {
		return nil, ConversionError{simpleError{message: " Error mapping to local work item "}}
	}

	// Get the remote item identifier ( which is currently the url ) to check if the work item exists in the database.
	workItemRemoteID := workItem.Fields[workitem.SystemRemoteItemID]

	sqlExpression := criteria.Equals(criteria.Field(workitem.SystemRemoteItemID), criteria.Literal(workItemRemoteID))

	var newWorkItem *app.WorkItem

	// Querying the database
	existingWorkItems, _, err := wir.List(context.Background(), sqlExpression, nil, nil)
	if err != nil {
		return nil, err
	}

	if len(existingWorkItems) != 0 {
		log.Info(nil, map[string]interface{}{
			"pkg":      "remoteworkitem",
			"workitem": workItem,
		}, "Workitem exists, will be updated")

		existingWorkItem := existingWorkItems[0]
		for key, value := range workItem.Fields {
			existingWorkItem.Fields[key] = value
		}
		newWorkItem, err = wir.Save(context.Background(), *existingWorkItem)
		if err != nil {
			log.Error(nil, map[string]interface{}{
				"existingWorkitem": existingWorkItem,
				"err":              err,
			}, "unable to update the work item")
		}
	} else {
		log.Info(nil, map[string]interface{}{
			"pkg":           "remoteworkitem",
			"sqlExpression": sqlExpression,
			"err":           err,
		}, "Work item not found , will now create new work item")
		c := workItem.Fields[workitem.SystemCreator]
		var creator string
		if c != nil {
			creator = c.(string)
		}
		newWorkItem, err = wir.Create(context.Background(), workitem.SystemBug, workItem.Fields, creator)
		if err != nil {
			log.Error(nil, map[string]interface{}{
				"creator":            creator,
				"workItem.Fields":    workItem.Fields,
				"workitem.SystemBug": workitem.SystemBug,
				"err":                err,
			}, "unable to create the work item")
		}
	}
	return newWorkItem, errors.WithStack(err)
}
// Save updates the given tracker query in storage.
// returns NotFoundError, ConversionError or InternalError
func (r *GormTrackerQueryRepository) Save(ctx context.Context, tq app.TrackerQuery) (*app.TrackerQuery, error) {
	res := TrackerQuery{}
	id, err := strconv.ParseUint(tq.ID, 10, 64)
	if err != nil || id == 0 {
		return nil, NotFoundError{entity: "trackerquery", ID: tq.ID}
	}

	tid, err := strconv.ParseUint(tq.TrackerID, 10, 64)
	if err != nil || tid == 0 {
		// treating this as a not found error: the fact that we're using number internal is implementation detail
		return nil, NotFoundError{"tracker", tq.TrackerID}
	}

	log.Info(ctx, map[string]interface{}{
		"pkg":       "remoteworkitem",
		"trackerID": id,
	}, "looking tracker query")

	tx := r.db.First(&res, id)
	if tx.RecordNotFound() {
		log.Error(ctx, map[string]interface{}{
			"trackerID": id,
		}, "tracker query not found")

		return nil, NotFoundError{entity: "TrackerQuery", ID: tq.ID}
	}
	if tx.Error != nil {
		return nil, InternalError{simpleError{fmt.Sprintf("could not load tracker query: %s", tx.Error.Error())}}
	}

	tx = r.db.First(&Tracker{}, tid)
	if tx.RecordNotFound() {
		log.Error(ctx, map[string]interface{}{
			"trackerID": id,
		}, "tracker ID not found")
		return nil, NotFoundError{entity: "tracker", ID: tq.TrackerID}
	}
	if tx.Error != nil {
		return nil, InternalError{simpleError{fmt.Sprintf("could not load tracker: %s", tx.Error.Error())}}
	}

	newTq := TrackerQuery{
		ID:        id,
		Schedule:  tq.Schedule,
		Query:     tq.Query,
		TrackerID: tid}

	if err := tx.Save(&newTq).Error; err != nil {
		log.Error(ctx, map[string]interface{}{
			"trackerQuery": tq.Query,
			"trackerID":    tid,
			"err":          err,
		}, "unable to save the tracker query")
		return nil, InternalError{simpleError{err.Error()}}
	}

	log.Info(ctx, map[string]interface{}{
		"pkg":          "remoteworkitem",
		"trackerQuery": newTq,
	}, "Updated tracker query")

	t2 := app.TrackerQuery{
		ID:        tq.ID,
		Schedule:  tq.Schedule,
		Query:     tq.Query,
		TrackerID: tq.TrackerID}

	return &t2, nil
}
// Save updates the given work item link category in storage. Version must be the same as the one int the stored version.
// returns NotFoundError, VersionConflictError, ConversionError or InternalError
func (r *GormWorkItemLinkCategoryRepository) Save(ctx context.Context, linkCat app.WorkItemLinkCategorySingle) (*app.WorkItemLinkCategorySingle, error) {
	res := WorkItemLinkCategory{}
	if linkCat.Data.ID == nil {
		return nil, errors.NewBadParameterError("data.id", linkCat.Data.ID)
	}
	id, err := satoriuuid.FromString(*linkCat.Data.ID)
	if err != nil {
		log.Error(ctx, map[string]interface{}{
			"wilcID": *linkCat.Data.ID,
			"err":    err,
		}, "error when converting %s to UUID: %s", *linkCat.Data.ID, err.Error())
		// treat as not found: clients don't know it must be a UUID
		return nil, errors.NewNotFoundError("work item link category", id.String())
	}

	if linkCat.Data.Type != EndpointWorkItemLinkCategories {
		return nil, errors.NewBadParameterError("data.type", linkCat.Data.Type).Expected(EndpointWorkItemLinkCategories)
	}

	// If the name is not nil, it MUST NOT be empty
	if linkCat.Data.Attributes.Name != nil && *linkCat.Data.Attributes.Name == "" {
		return nil, errors.NewBadParameterError("data.attributes.name", *linkCat.Data.Attributes.Name)
	}

	db := r.db.Model(&res).Where("id=?", *linkCat.Data.ID).First(&res)
	if db.RecordNotFound() {
		log.Error(ctx, map[string]interface{}{
			"wilcID": *linkCat.Data.ID,
		}, "work item link category not found")
		return nil, errors.NewNotFoundError("work item link category", id.String())
	}
	if db.Error != nil {
		log.Error(ctx, map[string]interface{}{
			"wilcID": *linkCat.Data.ID,
			"err":    db.Error,
		}, "unable to find work item link category")
		return nil, errors.NewInternalError(db.Error.Error())
	}
	if linkCat.Data.Attributes.Version == nil || res.Version != *linkCat.Data.Attributes.Version {
		return nil, errors.NewVersionConflictError("version conflict")
	}

	newLinkCat := WorkItemLinkCategory{
		ID:      id,
		Version: *linkCat.Data.Attributes.Version + 1,
	}

	if linkCat.Data.Attributes.Name != nil {
		newLinkCat.Name = *linkCat.Data.Attributes.Name
	}
	if linkCat.Data.Attributes.Description != nil {
		newLinkCat.Description = linkCat.Data.Attributes.Description
	}

	db = db.Save(&newLinkCat)
	if db.Error != nil {
		log.Error(ctx, map[string]interface{}{
			"wilcID": newLinkCat.ID,
			"err":    db.Error,
		}, "unable to save work item link category repository")
		return nil, errors.NewInternalError(db.Error.Error())
	}
	log.Info(ctx, map[string]interface{}{
		"pkg":             "link",
		"wilcID":          newLinkCat.ID,
		"newLinkCategory": newLinkCat,
	}, "Work item link category updated")
	result := ConvertLinkCategoryFromModel(newLinkCat)
	return &result, nil
}
// Save updates the given work item in storage. Version must be the same as the one int the stored version
// returns NotFoundError, VersionConflictError, ConversionError or InternalError
func (r *GormWorkItemRepository) Save(ctx context.Context, wi app.WorkItem) (*app.WorkItem, error) {
	res := WorkItem{}
	id, err := strconv.ParseUint(wi.ID, 10, 64)
	if err != nil || id == 0 {
		return nil, errors.NewNotFoundError("work item", wi.ID)
	}

	log.Info(ctx, map[string]interface{}{
		"pkg":  "workitem",
		"wiID": wi.ID,
	}, "Looking for id for the work item repository")
	tx := r.db.First(&res, id)
	if tx.RecordNotFound() {
		log.Error(ctx, map[string]interface{}{
			"wiID": wi.ID,
		}, "work item repository not found")
		return nil, errors.NewNotFoundError("work item", wi.ID)
	}
	if tx.Error != nil {
		return nil, errors.NewInternalError(err.Error())
	}
	if res.Version != wi.Version {
		return nil, errors.NewVersionConflictError("version conflict")
	}

	wiType, err := r.wir.LoadTypeFromDB(ctx, wi.Type)
	if err != nil {
		return nil, errors.NewBadParameterError("Type", wi.Type)
	}

	res.Version = res.Version + 1
	res.Type = wi.Type
	res.Fields = Fields{}

	for fieldName, fieldDef := range wiType.Fields {
		if fieldName == SystemCreatedAt {
			continue
		}
		fieldValue := wi.Fields[fieldName]
		var err error
		res.Fields[fieldName], err = fieldDef.ConvertToModel(fieldName, fieldValue)
		if err != nil {
			return nil, errors.NewBadParameterError(fieldName, fieldValue)
		}
	}

	tx = tx.Where("Version = ?", wi.Version).Save(&res)
	if err := tx.Error; err != nil {
		log.Error(ctx, map[string]interface{}{
			"wiID": wi.ID,
			"err":  err,
		}, "unable to save the work item repository")
		return nil, errors.NewInternalError(err.Error())
	}
	if tx.RowsAffected == 0 {
		return nil, errors.NewVersionConflictError("version conflict")
	}
	log.Info(ctx, map[string]interface{}{
		"pkg":  "workitem",
		"wiID": wi.ID,
	}, "Updated work item repository")
	return convertWorkItemModelToApp(wiType, &res)
}
// extracted this function from List() in order to close the rows object with "defer" for more readability
// workaround for https://github.com/lib/pq/issues/81
func (r *GormWorkItemRepository) listItemsFromDB(ctx context.Context, criteria criteria.Expression, start *int, limit *int) ([]WorkItem, uint64, error) {
	where, parameters, compileError := Compile(criteria)
	if compileError != nil {
		return nil, 0, errors.NewBadParameterError("expression", criteria)
	}

	log.Info(ctx, map[string]interface{}{
		"pkg":        "workitem",
		"where":      where,
		"parameters": parameters,
	}, "Executing query : '%s' with params %v", where, parameters)

	db := r.db.Model(&WorkItem{}).Where(where, parameters...)
	orgDB := db
	if start != nil {
		if *start < 0 {
			return nil, 0, errors.NewBadParameterError("start", *start)
		}
		db = db.Offset(*start)
	}
	if limit != nil {
		if *limit <= 0 {
			return nil, 0, errors.NewBadParameterError("limit", *limit)
		}
		db = db.Limit(*limit)
	}
	db = db.Select("count(*) over () as cnt2 , *")

	rows, err := db.Rows()
	if err != nil {
		return nil, 0, errs.WithStack(err)
	}
	defer rows.Close()

	result := []WorkItem{}
	columns, err := rows.Columns()
	if err != nil {
		return nil, 0, errors.NewInternalError(err.Error())
	}

	// need to set up a result for Scan() in order to extract total count.
	var count uint64
	var ignore interface{}
	columnValues := make([]interface{}, len(columns))

	for index := range columnValues {
		columnValues[index] = &ignore
	}
	columnValues[0] = &count
	first := true

	for rows.Next() {
		value := WorkItem{}
		db.ScanRows(rows, &value)
		if first {
			first = false
			if err = rows.Scan(columnValues...); err != nil {
				return nil, 0, errors.NewInternalError(err.Error())
			}
		}
		result = append(result, value)

	}
	if first {
		// means 0 rows were returned from the first query (maybe becaus of offset outside of total count),
		// need to do a count(*) to find out total
		orgDB := orgDB.Select("count(*)")
		rows2, err := orgDB.Rows()
		defer rows2.Close()
		if err != nil {
			return nil, 0, errs.WithStack(err)
		}
		rows2.Next() // count(*) will always return a row
		rows2.Scan(&count)
	}
	return result, count, nil
}