Skip to content

Commit

Permalink
🌱 Omit associations during Create operations (#733)
Browse files Browse the repository at this point in the history
When inserting a new record, GORM will also attempt to insert records
into tables refered to by many-to-many relationships on the inserted
record. This commit attempts to ensure that associations are omitted
when inserting records, and then the associations are added to the join
tables separately.

Also fixes some spots where the Transaction handler was in the wrong
spot in the chain or missing entirely.

Fixes #727

---------

Signed-off-by: Sam Lucidi <[email protected]>
  • Loading branch information
mansam authored and dymurray committed Oct 11, 2024
1 parent 537840b commit 8f03da2
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 14 deletions.
39 changes: 31 additions & 8 deletions api/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,22 +58,22 @@ func (h ApplicationHandler) AddRoutes(e *gin.Engine) {
routeGroup.DELETE(ApplicationRoot, h.Delete)
// Tags
routeGroup = e.Group("/")
routeGroup.Use(Required("applications"))
routeGroup.Use(Required("applications"), Transaction)
routeGroup.GET(ApplicationTagsRoot, h.TagList)
routeGroup.GET(ApplicationTagsRoot+"/", h.TagList)
routeGroup.POST(ApplicationTagsRoot, h.TagAdd)
routeGroup.DELETE(ApplicationTagRoot, h.TagDelete)
routeGroup.PUT(ApplicationTagsRoot, h.TagReplace, Transaction)
routeGroup.PUT(ApplicationTagsRoot, h.TagReplace)
// Facts
routeGroup = e.Group("/")
routeGroup.Use(Required("applications.facts"))
routeGroup.Use(Required("applications.facts"), Transaction)
routeGroup.GET(ApplicationFactsRoot, h.FactGet)
routeGroup.GET(ApplicationFactsRoot+"/", h.FactGet)
routeGroup.POST(ApplicationFactsRoot, h.FactCreate)
routeGroup.GET(ApplicationFactRoot, h.FactGet)
routeGroup.PUT(ApplicationFactRoot, h.FactPut)
routeGroup.DELETE(ApplicationFactRoot, h.FactDelete)
routeGroup.PUT(ApplicationFactsRoot, h.FactPut, Transaction)
routeGroup.PUT(ApplicationFactsRoot, h.FactPut)
// Bucket
routeGroup = e.Group("/")
routeGroup.Use(Required("applications.bucket"))
Expand All @@ -84,11 +84,11 @@ func (h ApplicationHandler) AddRoutes(e *gin.Engine) {
routeGroup.DELETE(AppBucketContentRoot, h.BucketDelete)
// Stakeholders
routeGroup = e.Group("/")
routeGroup.Use(Required("applications.stakeholders"))
routeGroup.Use(Required("applications.stakeholders"), Transaction)
routeGroup.PUT(AppStakeholdersRoot, h.StakeholdersUpdate)
// Assessments
routeGroup = e.Group("/")
routeGroup.Use(Required("applications.assessments"))
routeGroup.Use(Required("applications.assessments"), Transaction)
routeGroup.GET(AppAssessmentsRoot, h.AssessmentList)
routeGroup.POST(AppAssessmentsRoot, h.AssessmentCreate)
}
Expand Down Expand Up @@ -210,11 +210,23 @@ func (h ApplicationHandler) Create(ctx *gin.Context) {
}
m := r.Model()
m.CreateUser = h.BaseHandler.CurrentUser(ctx)
result := h.DB(ctx).Omit("Tags").Create(m)
result := h.DB(ctx).Omit(clause.Associations).Create(m)
if result.Error != nil {
_ = ctx.Error(result.Error)
return
}
db := h.DB(ctx).Model(m)
err = db.Association("Identities").Replace(m.Identities)
if err != nil {
_ = ctx.Error(err)
return
}
db = h.DB(ctx).Model(m)
err = db.Association("Contributors").Replace(m.Contributors)
if err != nil {
_ = ctx.Error(err)
return
}

tags := []model.ApplicationTag{}
if len(r.Tags) > 0 {
Expand Down Expand Up @@ -1078,11 +1090,21 @@ func (h ApplicationHandler) AssessmentCreate(ctx *gin.Context) {
assessment.PrepareForApplication(resolver, application, m)
newAssessment = true
}
result = h.DB(ctx).Create(m)
result = h.DB(ctx).Omit(clause.Associations).Create(m)
if result.Error != nil {
_ = ctx.Error(result.Error)
return
}
err = h.DB(ctx).Model(m).Association("Stakeholders").Replace("Stakeholders", m.Stakeholders)
if err != nil {
_ = ctx.Error(err)
return
}
err = h.DB(ctx).Model(m).Association("StakeholderGroups").Replace("StakeholderGroups", m.StakeholderGroups)
if err != nil {
_ = ctx.Error(err)
return
}
if newAssessment {
metrics.AssessmentsInitiated.Inc()
}
Expand Down Expand Up @@ -1138,6 +1160,7 @@ func (r *Application) With(m *model.Application, tags []model.ApplicationTag) {
r.Identities,
ref)
}
r.Tags = []TagRef{}
for i := range tags {
ref := TagRef{}
ref.With(tags[i].TagID, tags[i].Tag.Name, tags[i].Source, false)
Expand Down
35 changes: 33 additions & 2 deletions api/archetype.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,33 @@ func (h ArchetypeHandler) Create(ctx *gin.Context) {
}
m := r.Model()
m.CreateUser = h.CurrentUser(ctx)
result := h.DB(ctx).Create(m)
result := h.DB(ctx).Omit(clause.Associations).Create(m)
if result.Error != nil {
_ = ctx.Error(result.Error)
return
}

err = h.DB(ctx).Model(m).Association("Stakeholders").Replace("Stakeholders", m.Stakeholders)
if err != nil {
_ = ctx.Error(err)
return
}
err = h.DB(ctx).Model(m).Association("StakeholderGroups").Replace("StakeholderGroups", m.StakeholderGroups)
if err != nil {
_ = ctx.Error(err)
return
}
err = h.DB(ctx).Model(m).Association("CriteriaTags").Replace("CriteriaTags", m.CriteriaTags)
if err != nil {
_ = ctx.Error(err)
return
}
err = h.DB(ctx).Model(m).Association("Tags").Replace("Tags", m.Tags)
if err != nil {
_ = ctx.Error(err)
return
}

archetypes := []model.Archetype{}
db := h.preLoad(h.DB(ctx), "Tags", "CriteriaTags")
result = db.Find(&archetypes)
Expand Down Expand Up @@ -319,11 +340,21 @@ func (h ArchetypeHandler) AssessmentCreate(ctx *gin.Context) {
assessment.PrepareForArchetype(resolver, archetype, m)
newAssessment = true
}
result = h.DB(ctx).Create(m)
result = h.DB(ctx).Omit(clause.Associations).Create(m)
if result.Error != nil {
_ = ctx.Error(result.Error)
return
}
err = h.DB(ctx).Model(m).Association("Stakeholders").Replace("Stakeholders", m.Stakeholders)
if err != nil {
_ = ctx.Error(err)
return
}
err = h.DB(ctx).Model(m).Association("StakeholderGroups").Replace("StakeholderGroups", m.StakeholderGroups)
if err != nil {
_ = ctx.Error(err)
return
}
if newAssessment {
metrics.AssessmentsInitiated.Inc()
}
Expand Down
12 changes: 11 additions & 1 deletion api/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,21 @@ func (h StakeholderGroupHandler) Create(ctx *gin.Context) {
}
m := r.Model()
m.CreateUser = h.BaseHandler.CurrentUser(ctx)
result := h.DB(ctx).Create(m)
result := h.DB(ctx).Omit(clause.Associations).Create(m)
if result.Error != nil {
_ = ctx.Error(result.Error)
return
}
err = h.DB(ctx).Model(m).Association("Stakeholders").Replace(m.Stakeholders)
if err != nil {
_ = ctx.Error(err)
return
}
err = h.DB(ctx).Model(m).Association("MigrationWaves").Replace(m.MigrationWaves)
if err != nil {
_ = ctx.Error(err)
return
}
r.With(m)

h.Respond(ctx, http.StatusCreated, r)
Expand Down
2 changes: 1 addition & 1 deletion api/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func (h IdentityHandler) AddRoutes(e *gin.Engine) {
routeGroup.GET(IdentitiesRoot+"/", h.setDecrypted, h.List)
routeGroup.POST(IdentitiesRoot, h.Create)
routeGroup.GET(IdentityRoot, h.setDecrypted, h.Get)
routeGroup.PUT(IdentityRoot, h.Update, Transaction)
routeGroup.PUT(IdentityRoot, Transaction, h.Update)
routeGroup.DELETE(IdentityRoot, h.Delete)
}

Expand Down
17 changes: 16 additions & 1 deletion api/migrationwave.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,26 @@ func (h MigrationWaveHandler) Create(ctx *gin.Context) {
}
m := r.Model()
m.CreateUser = h.CurrentUser(ctx)
result := h.DB(ctx).Create(m)
result := h.DB(ctx).Omit(clause.Associations).Create(m)
if result.Error != nil {
_ = ctx.Error(result.Error)
return
}
err = h.DB(ctx).Model(m).Association("Applications").Replace("Applications", m.Applications)
if err != nil {
_ = ctx.Error(err)
return
}
err = h.DB(ctx).Model(m).Association("Stakeholders").Replace("Stakeholders", m.Stakeholders)
if err != nil {
_ = ctx.Error(err)
return
}
err = h.DB(ctx).Model(m).Association("StakeholderGroups").Replace("StakeholderGroups", m.StakeholderGroups)
if err != nil {
_ = ctx.Error(err)
return
}
r.With(m)

h.Respond(ctx, http.StatusCreated, r)
Expand Down
22 changes: 21 additions & 1 deletion api/stakeholder.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,31 @@ func (h StakeholderHandler) Create(ctx *gin.Context) {
}
m := r.Model()
m.CreateUser = h.BaseHandler.CurrentUser(ctx)
result := h.DB(ctx).Create(m)
result := h.DB(ctx).Omit(clause.Associations).Create(m)
if result.Error != nil {
_ = ctx.Error(result.Error)
return
}
err = h.DB(ctx).Model(m).Association("Groups").Replace(m.Groups)
if err != nil {
_ = ctx.Error(err)
return
}
err = h.DB(ctx).Model(m).Association("Owns").Replace(m.Owns)
if err != nil {
_ = ctx.Error(err)
return
}
err = h.DB(ctx).Model(m).Association("Contributes").Replace(m.Contributes)
if err != nil {
_ = ctx.Error(err)
return
}
err = h.DB(ctx).Model(m).Association("MigrationWaves").Replace(m.MigrationWaves)
if err != nil {
_ = ctx.Error(err)
return
}
r.With(m)

h.Respond(ctx, http.StatusCreated, r)
Expand Down

0 comments on commit 8f03da2

Please sign in to comment.