Bootstrap copied BFF and local accounts with public Sendico contracts (#34)
This commit is contained in:
1 parent
67dc5b5b1e
commit
7062672a87
556 files changed
+42291
-777
No files matched your search
@@ -0,0 +1,166 @@
|
||||
package organization
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"git.sendico.io/tech/sxchnge/bff/pkg/db/repository"
|
||||
"git.sendico.io/tech/sxchnge/bff/pkg/db/repository/builder"
|
||||
"git.sendico.io/tech/sxchnge/bff/pkg/db/storable"
|
||||
"git.sendico.io/tech/sxchnge/bff/pkg/merrors"
|
||||
"git.sendico.io/tech/sxchnge/bff/pkg/model"
|
||||
"git.sendico.io/tech/sxchnge/bff/pkg/mutil/mzap"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// AcquireProvisioningLease claims an organization that is still being
|
||||
// provisioned. The update is atomic: it only matches a pending organization
|
||||
// that carries a provisioning record and whose lease has expired or is already
|
||||
// owned by this attempt, so two concurrent attempts cannot provision the same
|
||||
// organization. Requiring the pending state here (not only in the caller's
|
||||
// earlier read) closes the race with a concurrent finalization: an organization
|
||||
// that just went active stops matching even though its lease fields are gone.
|
||||
func (db *OrganizationDB) AcquireProvisioningLease(
|
||||
ctx context.Context,
|
||||
organizationRef bson.ObjectID,
|
||||
leaseOwner string,
|
||||
now time.Time,
|
||||
expiresAt time.Time,
|
||||
) (bool, error) {
|
||||
// A freshly created attempt carries no lease yet, so "missing" counts as free
|
||||
// alongside "expired"; otherwise the first attempt would block itself.
|
||||
unset := repository.Exists(repository.Field(model.OrganizationProvisioningLeaseField), false)
|
||||
expired := repository.Query().Comparison(repository.Field(model.OrganizationProvisioningLeaseField), builder.Lte, now)
|
||||
mine := repository.Query().Filter(repository.Field(model.OrganizationProvisioningLeaseOwnerField), leaseOwner)
|
||||
filter := repository.Query().
|
||||
Filter(repository.IDField(), organizationRef).
|
||||
Filter(repository.Field(model.OrganizationSignupStateField), string(model.OrganizationSignupStatePending)).
|
||||
Comparison(repository.Field(model.OrganizationProvisioningOwnerRefField), builder.Exists, true).
|
||||
Or(unset, expired, mine)
|
||||
|
||||
patch := repository.Patch().
|
||||
Set(repository.Field(model.OrganizationProvisioningLeaseOwnerField), leaseOwner).
|
||||
Set(repository.Field(model.OrganizationProvisioningLeaseField), expiresAt)
|
||||
|
||||
matched, err := db.DBImp.PatchMany(ctx, filter, patch)
|
||||
if err != nil {
|
||||
db.DBImp.Logger.Warn("Failed to acquire organization provisioning lease",
|
||||
zap.Error(err), mzap.OrgRef(organizationRef), zap.String("lease_owner", leaseOwner))
|
||||
return false, err
|
||||
}
|
||||
return matched > 0, nil
|
||||
}
|
||||
|
||||
// ReleaseProvisioningLease frees the lease so another attempt can take over
|
||||
// immediately instead of waiting out the expiry. It only matches the lease of
|
||||
// the given owner: a lease taken over by a newer attempt stays untouched.
|
||||
func (db *OrganizationDB) ReleaseProvisioningLease(
|
||||
ctx context.Context,
|
||||
organizationRef bson.ObjectID,
|
||||
leaseOwner string,
|
||||
) error {
|
||||
filter := repository.Query().
|
||||
Filter(repository.IDField(), organizationRef).
|
||||
Filter(repository.Field(model.OrganizationProvisioningLeaseOwnerField), leaseOwner)
|
||||
|
||||
patch := repository.Patch().
|
||||
Unset(repository.Field(model.OrganizationProvisioningLeaseOwnerField)).
|
||||
Unset(repository.Field(model.OrganizationProvisioningLeaseField))
|
||||
|
||||
if _, err := db.DBImp.PatchMany(ctx, filter, patch); err != nil {
|
||||
db.DBImp.Logger.Warn("Failed to release organization provisioning lease",
|
||||
zap.Error(err), mzap.OrgRef(organizationRef), zap.String("lease_owner", leaseOwner))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FinalizeProvisioning atomically activates a pending organization while the
|
||||
// given attempt still owns its provisioning lease. It reports false when the
|
||||
// organization is no longer pending or the lease was taken over, so a slow
|
||||
// attempt cannot finalize an organization another attempt now works on.
|
||||
// Only the lease is cleared: the provisioning provenance stays on the document
|
||||
// so a retry after a lost success response can recognize the activated
|
||||
// organization instead of creating a duplicate.
|
||||
func (db *OrganizationDB) FinalizeProvisioning(
|
||||
ctx context.Context,
|
||||
organizationRef bson.ObjectID,
|
||||
leaseOwner string,
|
||||
) (bool, error) {
|
||||
filter := repository.Query().
|
||||
Filter(repository.IDField(), organizationRef).
|
||||
Filter(repository.Field(model.OrganizationSignupStateField), string(model.OrganizationSignupStatePending)).
|
||||
Filter(repository.Field(model.OrganizationProvisioningLeaseOwnerField), leaseOwner)
|
||||
|
||||
patch := repository.Patch().
|
||||
Set(repository.Field(model.OrganizationSignupStateField), model.OrganizationSignupStateActive).
|
||||
Unset(repository.Field(model.OrganizationProvisioningLeaseOwnerField)).
|
||||
Unset(repository.Field(model.OrganizationProvisioningLeaseField))
|
||||
|
||||
matched, err := db.DBImp.PatchMany(ctx, filter, patch)
|
||||
if err != nil {
|
||||
db.DBImp.Logger.Warn("Failed to finalize organization provisioning",
|
||||
zap.Error(err), mzap.OrgRef(organizationRef), zap.String("lease_owner", leaseOwner))
|
||||
return false, err
|
||||
}
|
||||
return matched > 0, nil
|
||||
}
|
||||
|
||||
// FindPendingProvisioning returns the organization that a previous attempt left
|
||||
// pending for the same owner and name, if any. The name matches the immutable
|
||||
// provisioning snapshot, not the mutable display name, so a rename cannot
|
||||
// detach the organization from the retries of the create that produced it.
|
||||
func (db *OrganizationDB) FindPendingProvisioning(
|
||||
ctx context.Context,
|
||||
ownerRef bson.ObjectID,
|
||||
name string,
|
||||
) (*model.Organization, error) {
|
||||
query := repository.Query().
|
||||
Filter(repository.Field(model.OrganizationProvisioningOwnerRefField), ownerRef).
|
||||
Filter(repository.Field(model.OrganizationSignupStateField), string(model.OrganizationSignupStatePending)).
|
||||
Filter(repository.Field(model.OrganizationProvisioningNameField), name)
|
||||
|
||||
org := &model.Organization{}
|
||||
if err := db.DBImp.FindOne(ctx, query, org); err != nil {
|
||||
if errors.Is(err, merrors.ErrNoData) {
|
||||
return nil, nil
|
||||
}
|
||||
db.DBImp.Logger.Warn("Failed to look up pending organization provisioning",
|
||||
zap.Error(err), mzap.AccRef(ownerRef), zap.String("organization_name", name))
|
||||
return nil, err
|
||||
}
|
||||
return org, nil
|
||||
}
|
||||
|
||||
// FindActiveProvisioned returns the already activated organization a previous
|
||||
// attempt provisioned for the same owner and name, if any. It is the retry
|
||||
// barrier for the success that got lost on the wire: without it a repeated
|
||||
// create after a timed out but committed finalization would provision a
|
||||
// duplicate organization. The name matches the immutable provisioning
|
||||
// snapshot, so the barrier survives a rename of the activated organization.
|
||||
func (db *OrganizationDB) FindActiveProvisioned(
|
||||
ctx context.Context,
|
||||
ownerRef bson.ObjectID,
|
||||
name string,
|
||||
) (*model.Organization, error) {
|
||||
// Archived organizations release their name: they are excluded here and in
|
||||
// the unique index alike, so the checks and the constraint stay in step.
|
||||
query := repository.Query().
|
||||
Filter(repository.Field(model.OrganizationProvisioningOwnerRefField), ownerRef).
|
||||
Filter(repository.Field(model.OrganizationSignupStateField), string(model.OrganizationSignupStateActive)).
|
||||
Filter(repository.Field(model.OrganizationProvisioningNameField), name).
|
||||
Filter(repository.Field(storable.IsArchivedField), false)
|
||||
|
||||
org := &model.Organization{}
|
||||
if err := db.DBImp.FindOne(ctx, query, org); err != nil {
|
||||
if errors.Is(err, merrors.ErrNoData) {
|
||||
return nil, nil
|
||||
}
|
||||
db.DBImp.Logger.Warn("Failed to look up activated organization provisioning",
|
||||
zap.Error(err), mzap.AccRef(ownerRef), zap.String("organization_name", name))
|
||||
return nil, err
|
||||
}
|
||||
return org, nil
|
||||
}
|
||||
Reference in new issue
Block a user