cosmos-sdk/schema/diff/diff.go
Aaron Craelius ae40e809b9
refactor(schema)!: rename ObjectType -> StateObjectType (#21691)
Co-authored-by: cool-developer <51834436+cool-develope@users.noreply.github.com>
2024-09-16 08:17:52 +00:00

123 lines
4.3 KiB
Go

package diff
import "cosmossdk.io/schema"
// ModuleSchemaDiff represents the difference between two module schemas.
type ModuleSchemaDiff struct {
// AddedStateObjectTypes is a list of object types that were added.
AddedStateObjectTypes []schema.StateObjectType
// ChangedStateObjectTypes is a list of object types that were changed.
ChangedStateObjectTypes []StateObjectTypeDiff
// RemovedStateObjectTypes is a list of object types that were removed.
RemovedStateObjectTypes []schema.StateObjectType
// AddedEnumTypes is a list of enum types that were added.
AddedEnumTypes []schema.EnumType
// ChangedEnumTypes is a list of enum types that were changed.
ChangedEnumTypes []EnumTypeDiff
// RemovedEnumTypes is a list of enum types that were removed.
RemovedEnumTypes []schema.EnumType
}
// CompareModuleSchemas compares an old and a new module schemas and returns the difference between them.
// If the schemas are equivalent, the Empty method of the returned ModuleSchemaDiff will return true.
//
// Indexer implementations can use these diffs to perform automatic schema migration.
// The specific supported changes that a specific indexer supports are defined by that indexer implementation.
// However, as a general rule, it is suggested that indexers support the following changes to module schemas:
// - Adding object types
// - Adding enum types
// - Adding nullable value fields to object types
// - Adding enum values to enum types
//
// These changes are officially considered "compatible" changes, and the HasCompatibleChanges method of the returned
// ModuleSchemaDiff will return true if only compatible changes are present.
// Module authors can use the above guidelines as a reference point for what changes are generally
// considered safe to make to a module schema without breaking existing indexers.
func CompareModuleSchemas(oldSchema, newSchema schema.ModuleSchema) ModuleSchemaDiff {
diff := ModuleSchemaDiff{}
oldSchema.StateObjectTypes(func(oldObj schema.StateObjectType) bool {
newObj, found := newSchema.LookupStateObjectType(oldObj.Name)
if !found {
diff.RemovedStateObjectTypes = append(diff.RemovedStateObjectTypes, oldObj)
return true
}
objDiff := compareObjectType(oldObj, newObj)
if !objDiff.Empty() {
diff.ChangedStateObjectTypes = append(diff.ChangedStateObjectTypes, objDiff)
}
return true
})
newSchema.StateObjectTypes(func(newObj schema.StateObjectType) bool {
_, found := oldSchema.LookupStateObjectType(newObj.TypeName())
if !found {
diff.AddedStateObjectTypes = append(diff.AddedStateObjectTypes, newObj)
}
return true
})
oldSchema.EnumTypes(func(oldEnum schema.EnumType) bool {
newEnum, found := newSchema.LookupEnumType(oldEnum.Name)
if !found {
diff.RemovedEnumTypes = append(diff.RemovedEnumTypes, oldEnum)
return true
}
enumDiff := compareEnumType(oldEnum, newEnum)
if !enumDiff.Empty() {
diff.ChangedEnumTypes = append(diff.ChangedEnumTypes, enumDiff)
}
return true
})
newSchema.EnumTypes(func(newEnum schema.EnumType) bool {
_, found := oldSchema.LookupEnumType(newEnum.TypeName())
if !found {
diff.AddedEnumTypes = append(diff.AddedEnumTypes, newEnum)
}
return true
})
return diff
}
func (m ModuleSchemaDiff) Empty() bool {
return len(m.AddedStateObjectTypes) == 0 &&
len(m.ChangedStateObjectTypes) == 0 &&
len(m.RemovedStateObjectTypes) == 0 &&
len(m.AddedEnumTypes) == 0 &&
len(m.ChangedEnumTypes) == 0 &&
len(m.RemovedEnumTypes) == 0
}
// HasCompatibleChanges returns true if the diff contains only compatible changes.
// Compatible changes are changes that are generally safe to make to a module schema without breaking existing indexers
// and indexers should aim to automatically migrate to such changes.
// See the CompareModuleSchemas function for a list of changes that are considered compatible.
func (m ModuleSchemaDiff) HasCompatibleChanges() bool {
// object and enum types can be added but not removed
// changed object and enum types must have compatible changes
if len(m.RemovedStateObjectTypes) != 0 || len(m.RemovedEnumTypes) != 0 {
return false
}
for _, objectType := range m.ChangedStateObjectTypes {
if !objectType.HasCompatibleChanges() {
return false
}
}
for _, enumType := range m.ChangedEnumTypes {
if !enumType.HasCompatibleChanges() {
return false
}
}
return true
}