diff --git a/docs/apidocs.swagger.json b/docs/apidocs.swagger.json index 75c6fb48c..5c4c8fd7b 100644 --- a/docs/apidocs.swagger.json +++ b/docs/apidocs.swagger.json @@ -3,7 +3,7 @@ "info": { "title": "Permify API", "description": "Permify is an open source authorization service for creating fine-grained and scalable authorization systems.", - "version": "v0.6.1", + "version": "v0.6.2", "contact": { "name": "API Support", "url": "https://github.com/Permify/permify/issues", diff --git a/internal/info.go b/internal/info.go index 2896ce7ff..b19b9a437 100644 --- a/internal/info.go +++ b/internal/info.go @@ -20,7 +20,7 @@ var Identifier = xid.New().String() */ const ( // Version is the last release of the Permify (e.g. v0.1.0) - Version = "v0.6.1" + Version = "v0.6.2" // Banner is the view for terminal. Banner = ` diff --git a/internal/storage/postgres/dataReader.go b/internal/storage/postgres/dataReader.go index 2381da645..fc5960345 100644 --- a/internal/storage/postgres/dataReader.go +++ b/internal/storage/postgres/dataReader.go @@ -61,8 +61,9 @@ func (r *DataReader) QueryRelationships(ctx context.Context, tenantID string, fi return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_INTERNAL) } - // Rollback the transaction in case of any error. - defer utils.Rollback(tx) + defer func() { + _ = tx.Rollback() + }() // Build the relationships query based on the provided filter and snapshot value. var args []interface{} @@ -107,7 +108,7 @@ func (r *DataReader) QueryRelationships(ctx context.Context, tenantID string, fi return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_EXECUTION) } - slog.Info("Successfully retrieved relationship tuples from the database.") + slog.Info("Successfully retrieved relation tuples from the database.") // Return a TupleIterator created from the TupleCollection. return collection.CreateTupleIterator(), nil @@ -135,8 +136,9 @@ func (r *DataReader) ReadRelationships(ctx context.Context, tenantID string, fil return nil, nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_INTERNAL) } - // Rollback the transaction in case of any error. - defer utils.Rollback(tx) + defer func() { + _ = tx.Rollback() + }() // Build the relationships query based on the provided filter, snapshot value, and pagination settings. builder := r.database.Builder.Select("id, entity_type, entity_id, relation, subject_type, subject_id, subject_relation").From(RelationTuplesTable).Where(squirrel.Eq{"tenant_id": tenantID}) @@ -202,7 +204,7 @@ func (r *DataReader) ReadRelationships(ctx context.Context, tenantID string, fil return nil, nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_EXECUTION) } - slog.Info("Successfully read relationships from database.") + slog.Info("Successfully read relation tuples from database.") // Return the results and encoded continuous token for pagination. if len(tuples) > int(pagination.PageSize()) { @@ -233,8 +235,9 @@ func (r *DataReader) QuerySingleAttribute(ctx context.Context, tenantID string, return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_INTERNAL) } - // Rollback the transaction in case of any error. - defer utils.Rollback(tx) + defer func() { + _ = tx.Rollback() + }() // Build the relationships query based on the provided filter and snapshot value. var args []interface{} @@ -309,8 +312,9 @@ func (r *DataReader) QueryAttributes(ctx context.Context, tenantID string, filte return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_INTERNAL) } - // Rollback the transaction in case of any error. - defer utils.Rollback(tx) + defer func() { + _ = tx.Rollback() + }() // Build the relationships query based on the provided filter and snapshot value. var args []interface{} @@ -397,8 +401,9 @@ func (r *DataReader) ReadAttributes(ctx context.Context, tenantID string, filter return nil, nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_INTERNAL) } - // Rollback the transaction in case of any error. - defer utils.Rollback(tx) + defer func() { + _ = tx.Rollback() + }() // Build the relationships query based on the provided filter, snapshot value, and pagination settings. builder := r.database.Builder.Select("id, entity_type, entity_id, attribute, value").From(AttributesTable).Where(squirrel.Eq{"tenant_id": tenantID}) @@ -508,8 +513,9 @@ func (r *DataReader) QueryUniqueEntities(ctx context.Context, tenantID, name, sn return nil, nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_INTERNAL) } - // Rollback the transaction in case of any error. - defer utils.Rollback(tx) + defer func() { + _ = tx.Rollback() + }() query := utils.BulkEntityFilterQuery(tenantID, name, st.(snapshot.Token).Value.Uint) @@ -595,8 +601,9 @@ func (r *DataReader) QueryUniqueSubjectReferences(ctx context.Context, tenantID return nil, nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_INTERNAL) } - // Rollback the transaction in case of any error. - defer utils.Rollback(tx) + defer func() { + _ = tx.Rollback() + }() // Build the relationships query based on the provided filter, snapshot value, and pagination settings. builder := r.database.Builder. @@ -693,14 +700,13 @@ func (r *DataReader) HeadSnapshot(ctx context.Context, tenantID string) (token.S } // Execute the query and retrieve the highest transaction ID. - row := r.database.DB.QueryRowContext(ctx, query, args...) - err = row.Scan(&xid) + err = r.database.DB.QueryRowContext(ctx, query, args...).Scan(&xid) if err != nil { // If no rows are found, return a snapshot token with a value of 0. if errors.Is(err, sql.ErrNoRows) { return snapshot.Token{Value: types.XID8{Uint: 0}}, nil } - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_EXECUTION) + return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_SCAN) } slog.Info("Successfully retrieved latest snapshot token") diff --git a/internal/storage/postgres/dataWriter.go b/internal/storage/postgres/dataWriter.go index f2e99feef..c591b6037 100644 --- a/internal/storage/postgres/dataWriter.go +++ b/internal/storage/postgres/dataWriter.go @@ -41,601 +41,649 @@ func NewDataWriter(database *db.Postgres) *DataWriter { } } -func (w *DataWriter) Write(ctx context.Context, tenantID string, tupleCollection *database.TupleCollection, attributeCollection *database.AttributeCollection) (token token.EncodedSnapToken, err error) { +// Write method writes a collection of tuples and attributes to the database for a specific tenant. +// It returns an EncodedSnapToken upon successful write or an error if the write fails. +func (w *DataWriter) Write( + ctx context.Context, + tenantID string, + tupleCollection *database.TupleCollection, + attributeCollection *database.AttributeCollection, +) (token token.EncodedSnapToken, err error) { + // Start a new tracing span for this operation. ctx, span := tracer.Start(ctx, "data-writer.write") - defer span.End() + defer span.End() // Ensure that the span is ended when the function returns. + // Log the start of a data write operation. slog.Info("Writing data to the database. TenantID: ", slog.String("tenant_id", tenantID), "Max Retries: ", slog.Any("max_retries", w.maxRetries)) + // Check if the total number of tuples and attributes exceeds the maximum allowed per write. if len(tupleCollection.GetTuples())+len(attributeCollection.GetAttributes()) > w.maxDataPerWrite { return nil, errors.New("max data per write exceeded") } + // Retry loop for handling transient errors like serialization issues. for i := 0; i <= w.maxRetries; i++ { - var tx *sql.Tx - tx, err = w.database.DB.BeginTx(ctx, &w.txOptions) + // Attempt to write the data to the database. + tkn, err := w.write(ctx, tenantID, tupleCollection, attributeCollection) if err != nil { - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_INTERNAL) + // Check if the error is due to serialization, and if so, retry. + if strings.Contains(err.Error(), "could not serialize") { + slog.Warn("Serialization error occurred. Retrying...", slog.String("tenant_id", tenantID), slog.Int("retry", i)) + continue // Retry the operation. + } + // If the error is not serialization-related, handle it and return. + return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_DATASTORE) } + // If the write is successful, return the token. + return tkn, err + } + + // Log an error if the operation failed after reaching the maximum number of retries. + slog.Error("Failed to write data to the database. Max retries reached. Aborting operation. ", slog.Any("error", errors.New(base.ErrorCode_ERROR_CODE_ERROR_MAX_RETRIES.String()))) + + // Return an error indicating that the maximum number of retries has been reached. + return nil, errors.New(base.ErrorCode_ERROR_CODE_ERROR_MAX_RETRIES.String()) +} + +// Delete method removes data from the database based on the provided tuple and attribute filters. +// It returns an EncodedSnapToken upon successful deletion or an error if the deletion fails. +func (w *DataWriter) Delete( + ctx context.Context, + tenantID string, + tupleFilter *base.TupleFilter, + attributeFilter *base.AttributeFilter, +) (token.EncodedSnapToken, error) { + // Start a new tracing span for this delete operation. + ctx, span := tracer.Start(ctx, "data-writer.delete") + defer span.End() // Ensure that the span is ended when the function returns. - slog.Debug("Inserting transaction record for tenant: ", slog.String("tenant_id", tenantID)) + // Log the start of a data deletion operation. + slog.Info("Deleting data from the database. TenantID: ", slog.String("tenant_id", tenantID), "Max Retries: ", slog.Any("max_retries", w.maxRetries)) - transaction := w.database.Builder.Insert("transactions"). - Columns("tenant_id"). - Values(tenantID). - Suffix("RETURNING id").RunWith(tx) + // Retry loop for handling transient errors like serialization issues. + for i := 0; i <= w.maxRetries; i++ { + // Attempt to delete the data from the database. + tkn, err := w.delete(ctx, tenantID, tupleFilter, attributeFilter) if err != nil { - utils.Rollback(tx) - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_SQL_BUILDER) + // Check if the error is due to serialization, and if so, retry. + if strings.Contains(err.Error(), "could not serialize") { + slog.Warn("Serialization error occurred. Retrying...", slog.String("tenant_id", tenantID), slog.Int("retry", i)) + continue // Retry the operation. + } + // If the error is not serialization-related, handle it and return. + return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_DATASTORE) } + // If the delete operation is successful, return the token. + return tkn, err + } + + // Log an error if the operation failed after reaching the maximum number of retries. + slog.Error("Failed to delete data from the database. Max retries reached. Aborting operation. ", slog.Any("error", errors.New(base.ErrorCode_ERROR_CODE_ERROR_MAX_RETRIES.String()))) + + // Return an error indicating that the maximum number of retries has been reached. + return nil, errors.New(base.ErrorCode_ERROR_CODE_ERROR_MAX_RETRIES.String()) +} + +// RunBundle executes a bundle of operations in the context of a given tenant. +// It returns an EncodedSnapToken upon successful completion or an error if the operation fails. +func (w *DataWriter) RunBundle( + ctx context.Context, + tenantID string, + arguments map[string]string, + b *base.DataBundle, +) (token.EncodedSnapToken, error) { + // Start a new tracing span for this operation. + ctx, span := tracer.Start(ctx, "data-writer.run-bundle") + defer span.End() // Ensure that the span is ended when the function returns. + + // Log the start of running a bundle operation. + slog.Info("Running bundle from the database. TenantID: ", slog.String("tenant_id", tenantID), "Max Retries: ", slog.Any("max_retries", w.maxRetries)) - var xid types.XID8 - err = transaction.QueryRowContext(ctx).Scan(&xid) + // Retry loop for handling transient errors like serialization issues. + for i := 0; i <= w.maxRetries; i++ { + // Attempt to run the bundle operation. + tkn, err := w.runBundle(ctx, tenantID, arguments, b) if err != nil { - utils.Rollback(tx) - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_EXECUTION) + // Check if the error is due to serialization, and if so, retry. + if strings.Contains(err.Error(), "could not serialize") { + slog.Warn("Serialization error occurred. Retrying...", slog.String("tenant_id", tenantID), slog.Int("retry", i)) + continue // Retry the operation. + } + // If the error is not serialization-related, handle it and return. + return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_DATASTORE) } + // If the operation is successful, return the token. + return tkn, err + } - slog.Debug("Retrieved transaction: ", slog.Any("transaction", transaction), "for tenant: ", slog.Any("tenant_id", tenantID)) + // Log an error if the operation failed after reaching the maximum number of retries. + slog.Error("Failed to run bundle from the database. Max retries reached. Aborting operation. ", slog.Any("error", errors.New(base.ErrorCode_ERROR_CODE_ERROR_MAX_RETRIES.String()))) - slog.Debug("Processing tuples and executing insert query. ") + // Return an error indicating that the maximum number of retries has been reached. + return nil, errors.New(base.ErrorCode_ERROR_CODE_ERROR_MAX_RETRIES.String()) +} - if len(tupleCollection.GetTuples()) > 0 { +// write handles the database writing of tuple and attribute collections for a given tenant. +// It returns an EncodedSnapToken upon successful write or an error if the write fails. +func (w *DataWriter) write( + ctx context.Context, + tenantID string, + tupleCollection *database.TupleCollection, + attributeCollection *database.AttributeCollection, +) (token token.EncodedSnapToken, err error) { + var tx *sql.Tx + tx, err = w.database.DB.BeginTx(ctx, &w.txOptions) + if err != nil { + return nil, err + } - tuplesInsertBuilder := w.database.Builder.Insert(RelationTuplesTable).Columns("entity_type, entity_id, relation, subject_type, subject_id, subject_relation, created_tx_id, tenant_id") + defer func() { + _ = tx.Rollback() + }() - deleteClauses := squirrel.Or{} + transaction := w.database.Builder.Insert("transactions"). + Columns("tenant_id"). + Values(tenantID). + Suffix("RETURNING id").RunWith(tx) + if err != nil { + return nil, err + } - titer := tupleCollection.CreateTupleIterator() - for titer.HasNext() { - t := titer.GetNext() - srelation := t.GetSubject().GetRelation() - if srelation == tuple.ELLIPSIS { - srelation = "" - } + var xid types.XID8 + err = transaction.QueryRowContext(ctx).Scan(&xid) + if err != nil { + return nil, err + } - // Build the condition for this tuple. - condition := squirrel.Eq{ - "entity_type": t.GetEntity().GetType(), - "entity_id": t.GetEntity().GetId(), - "relation": t.GetRelation(), - "subject_type": t.GetSubject().GetType(), - "subject_id": t.GetSubject().GetId(), - "subject_relation": srelation, - } + slog.Debug("Retrieved transaction: ", slog.Any("transaction", transaction), "for tenant: ", slog.Any("tenant_id", tenantID)) - // Add the condition to the OR slice. - deleteClauses = append(deleteClauses, condition) + slog.Debug("Processing tuples and executing insert query. ") - tuplesInsertBuilder = tuplesInsertBuilder.Values(t.GetEntity().GetType(), t.GetEntity().GetId(), t.GetRelation(), t.GetSubject().GetType(), t.GetSubject().GetId(), srelation, xid, tenantID) - } + if len(tupleCollection.GetTuples()) > 0 { - tDeleteBuilder := w.database.Builder.Update(RelationTuplesTable).Set("expired_tx_id", xid).Where(squirrel.Eq{ - "expired_tx_id": "0", - "tenant_id": tenantID, - }).Where(deleteClauses) + tuplesInsertBuilder := w.database.Builder.Insert(RelationTuplesTable).Columns("entity_type, entity_id, relation, subject_type, subject_id, subject_relation, created_tx_id, tenant_id") - var tdquery string - var tdargs []interface{} + deleteClauses := squirrel.Or{} - tdquery, tdargs, err = tDeleteBuilder.ToSql() - if err != nil { - utils.Rollback(tx) - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_SQL_BUILDER) + titer := tupleCollection.CreateTupleIterator() + for titer.HasNext() { + t := titer.GetNext() + srelation := t.GetSubject().GetRelation() + if srelation == tuple.ELLIPSIS { + srelation = "" } - _, err = tx.ExecContext(ctx, tdquery, tdargs...) - if err != nil { - utils.Rollback(tx) - if strings.Contains(err.Error(), "could not serialize") { - continue - } - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_EXECUTION) + // Build the condition for this tuple. + condition := squirrel.Eq{ + "entity_type": t.GetEntity().GetType(), + "entity_id": t.GetEntity().GetId(), + "relation": t.GetRelation(), + "subject_type": t.GetSubject().GetType(), + "subject_id": t.GetSubject().GetId(), + "subject_relation": srelation, } - var tiquery string - var tiargs []interface{} + // Add the condition to the OR slice. + deleteClauses = append(deleteClauses, condition) - tiquery, tiargs, err = tuplesInsertBuilder.ToSql() - if err != nil { - utils.Rollback(tx) - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_SQL_BUILDER) - } - - _, err = tx.ExecContext(ctx, tiquery, tiargs...) - if err != nil { - utils.Rollback(tx) - if strings.Contains(err.Error(), "could not serialize") { - continue - } - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_EXECUTION) - } + tuplesInsertBuilder = tuplesInsertBuilder.Values(t.GetEntity().GetType(), t.GetEntity().GetId(), t.GetRelation(), t.GetSubject().GetType(), t.GetSubject().GetId(), srelation, xid, tenantID) } - if len(attributeCollection.GetAttributes()) > 0 { + tDeleteBuilder := w.database.Builder.Update(RelationTuplesTable).Set("expired_tx_id", xid).Where(squirrel.Eq{ + "expired_tx_id": "0", + "tenant_id": tenantID, + }).Where(deleteClauses) - attributesInsertBuilder := w.database.Builder.Insert(AttributesTable).Columns("entity_type, entity_id, attribute, value, created_tx_id, tenant_id") + var tdquery string + var tdargs []interface{} - deleteClauses := squirrel.Or{} + tdquery, tdargs, err = tDeleteBuilder.ToSql() + if err != nil { + return nil, err + } + + _, err = tx.ExecContext(ctx, tdquery, tdargs...) + if err != nil { + return nil, err + } - aiter := attributeCollection.CreateAttributeIterator() - for aiter.HasNext() { - a := aiter.GetNext() + var tiquery string + var tiargs []interface{} - m := jsonpb.Marshaler{} - jsonStr, err := m.MarshalToString(a.GetValue()) - if err != nil { - utils.Rollback(tx) - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_INVALID_ARGUMENT) - } + tiquery, tiargs, err = tuplesInsertBuilder.ToSql() + if err != nil { + return nil, err + } - // Build the condition for this tuple. - condition := squirrel.Eq{ - "entity_type": a.GetEntity().GetType(), - "entity_id": a.GetEntity().GetId(), - "attribute": a.GetAttribute(), - } + _, err = tx.ExecContext(ctx, tiquery, tiargs...) + if err != nil { + return nil, err + } + } - // Add the condition to the OR slice. - deleteClauses = append(deleteClauses, condition) + if len(attributeCollection.GetAttributes()) > 0 { - attributesInsertBuilder = attributesInsertBuilder.Values(a.GetEntity().GetType(), a.GetEntity().GetId(), a.GetAttribute(), jsonStr, xid, tenantID) - } + attributesInsertBuilder := w.database.Builder.Insert(AttributesTable).Columns("entity_type, entity_id, attribute, value, created_tx_id, tenant_id") - aDeleteBuilder := w.database.Builder.Update(AttributesTable).Set("expired_tx_id", xid).Where(squirrel.Eq{ - "expired_tx_id": "0", - "tenant_id": tenantID, - }).Where(deleteClauses) + deleteClauses := squirrel.Or{} - var adquery string - var adargs []interface{} + aiter := attributeCollection.CreateAttributeIterator() + for aiter.HasNext() { + a := aiter.GetNext() - adquery, adargs, err = aDeleteBuilder.ToSql() + m := jsonpb.Marshaler{} + jsonStr, err := m.MarshalToString(a.GetValue()) if err != nil { - utils.Rollback(tx) - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_SQL_BUILDER) + return nil, err } - _, err = tx.ExecContext(ctx, adquery, adargs...) - if err != nil { - utils.Rollback(tx) - if strings.Contains(err.Error(), "could not serialize") { - continue - } - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_EXECUTION) + // Build the condition for this attribute. + condition := squirrel.Eq{ + "entity_type": a.GetEntity().GetType(), + "entity_id": a.GetEntity().GetId(), + "attribute": a.GetAttribute(), } - var aquery string - var aargs []interface{} + // Add the condition to the OR slice. + deleteClauses = append(deleteClauses, condition) - aquery, aargs, err = attributesInsertBuilder.ToSql() - if err != nil { - utils.Rollback(tx) - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_SQL_BUILDER) - } + attributesInsertBuilder = attributesInsertBuilder.Values(a.GetEntity().GetType(), a.GetEntity().GetId(), a.GetAttribute(), jsonStr, xid, tenantID) + } - _, err = tx.ExecContext(ctx, aquery, aargs...) - if err != nil { - utils.Rollback(tx) - if strings.Contains(err.Error(), "could not serialize") { - continue - } - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_EXECUTION) - } + aDeleteBuilder := w.database.Builder.Update(AttributesTable).Set("expired_tx_id", xid).Where(squirrel.Eq{ + "expired_tx_id": "0", + "tenant_id": tenantID, + }).Where(deleteClauses) + + var adquery string + var adargs []interface{} + + adquery, adargs, err = aDeleteBuilder.ToSql() + if err != nil { + return nil, err } - if err = tx.Commit(); err != nil { - utils.Rollback(tx) - if strings.Contains(err.Error(), "could not serialize") { - continue - } - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_EXECUTION) + _, err = tx.ExecContext(ctx, adquery, adargs...) + if err != nil { + return nil, err } - slog.Info("Data successfully written to the database.") + var aquery string + var aargs []interface{} - return snapshot.NewToken(xid).Encode(), nil + aquery, aargs, err = attributesInsertBuilder.ToSql() + if err != nil { + return nil, err + } + + _, err = tx.ExecContext(ctx, aquery, aargs...) + if err != nil { + return nil, err + } } - slog.Error("Failed to write data to the database. Max retries reached. Aborting operation. ", slog.Any("error", errors.New(base.ErrorCode_ERROR_CODE_ERROR_MAX_RETRIES.String()))) + if err = tx.Commit(); err != nil { + return nil, err + } - return nil, errors.New(base.ErrorCode_ERROR_CODE_ERROR_MAX_RETRIES.String()) + slog.Info("Data successfully written to the database.") + + return snapshot.NewToken(xid).Encode(), nil } -func (w *DataWriter) Delete(ctx context.Context, tenantID string, tupleFilter *base.TupleFilter, attributeFilter *base.AttributeFilter) (token token.EncodedSnapToken, err error) { - ctx, span := tracer.Start(ctx, "data-writer.delete") - defer span.End() +// delete handles the deletion of tuples and attributes from the database based on provided filters. +// It returns an EncodedSnapToken upon successful deletion or an error if the deletion fails. +func (w *DataWriter) delete( + ctx context.Context, + tenantID string, + tupleFilter *base.TupleFilter, + attributeFilter *base.AttributeFilter, +) (token token.EncodedSnapToken, err error) { + var tx *sql.Tx + tx, err = w.database.DB.BeginTx(ctx, &w.txOptions) + if err != nil { + return nil, err + } - slog.Info("Deleting data from the database. TenantID: ", slog.String("tenant_id", tenantID), "Max Retries: ", slog.Any("max_retries", w.maxRetries)) + defer func() { + _ = tx.Rollback() + }() - for i := 0; i <= w.maxRetries; i++ { - var tx *sql.Tx - tx, err = w.database.DB.BeginTx(ctx, &w.txOptions) + transaction := w.database.Builder.Insert("transactions"). + Columns("tenant_id"). + Values(tenantID). + Suffix("RETURNING id").RunWith(tx) + if err != nil { + return nil, err + } + + var xid types.XID8 + err = transaction.QueryRowContext(ctx).Scan(&xid) + if err != nil { + return nil, err + } + + slog.Debug("Retrieved transaction: ", slog.Any("transaction", transaction), "for tenant: ", slog.Any("tenant_id", tenantID)) + + slog.Debug("Processing tuple and executing update query. ") + + if !validation.IsTupleFilterEmpty(tupleFilter) { + tbuilder := w.database.Builder.Update(RelationTuplesTable).Set("expired_tx_id", xid).Where(squirrel.Eq{"expired_tx_id": "0", "tenant_id": tenantID}) + tbuilder = utils.TuplesFilterQueryForUpdateBuilder(tbuilder, tupleFilter) + + var tquery string + var targs []interface{} + + tquery, targs, err = tbuilder.ToSql() + if err != nil { + return nil, err + } + + _, err = tx.ExecContext(ctx, tquery, targs...) if err != nil { - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_INTERNAL) + return nil, err } + } + + slog.Debug("Processing attribute and executing update query.") + + if !validation.IsAttributeFilterEmpty(attributeFilter) { + abuilder := w.database.Builder.Update(AttributesTable).Set("expired_tx_id", xid).Where(squirrel.Eq{"expired_tx_id": "0", "tenant_id": tenantID}) + abuilder = utils.AttributesFilterQueryForUpdateBuilder(abuilder, attributeFilter) - slog.Debug("Deleting transaction record for tenant: ", slog.String("tenant_id", tenantID)) + var aquery string + var aargs []interface{} - transaction := w.database.Builder.Insert("transactions"). - Columns("tenant_id"). - Values(tenantID). - Suffix("RETURNING id").RunWith(tx) + aquery, aargs, err = abuilder.ToSql() if err != nil { - utils.Rollback(tx) - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_SQL_BUILDER) + return nil, err } - var xid types.XID8 - err = transaction.QueryRowContext(ctx).Scan(&xid) + _, err = tx.ExecContext(ctx, aquery, aargs...) if err != nil { - utils.Rollback(tx) - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_EXECUTION) + return nil, err } + } + + if err = tx.Commit(); err != nil { + return nil, err + } + + slog.Info("Data successfully deleted from the database.") - slog.Debug("Retrieved transaction: ", slog.Any("transaction", transaction), "for tenant: ", slog.Any("tenant_id", tenantID)) + return snapshot.NewToken(xid).Encode(), nil +} - slog.Debug("Processing tuple and executing update query. ") +// runBundle executes a series of operations defined in a DataBundle within a single database transaction. +// It returns an EncodedSnapToken upon successful execution of all operations or an error if any operation fails. +func (w *DataWriter) runBundle( + ctx context.Context, + tenantID string, + arguments map[string]string, + b *base.DataBundle, +) (token token.EncodedSnapToken, err error) { + var tx *sql.Tx + tx, err = w.database.DB.BeginTx(ctx, &w.txOptions) + if err != nil { + return nil, err + } - if !validation.IsTupleFilterEmpty(tupleFilter) { - tbuilder := w.database.Builder.Update(RelationTuplesTable).Set("expired_tx_id", xid).Where(squirrel.Eq{"expired_tx_id": "0", "tenant_id": tenantID}) - tbuilder = utils.TuplesFilterQueryForUpdateBuilder(tbuilder, tupleFilter) + defer func() { + _ = tx.Rollback() + }() - var tquery string - var targs []interface{} + transaction := w.database.Builder.Insert("transactions"). + Columns("tenant_id"). + Values(tenantID). + Suffix("RETURNING id").RunWith(tx) + if err != nil { + return nil, err + } - tquery, targs, err = tbuilder.ToSql() - if err != nil { - utils.Rollback(tx) - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_SQL_BUILDER) - } + var xid types.XID8 + err = transaction.QueryRowContext(ctx).Scan(&xid) + if err != nil { + return nil, err + } - _, err = tx.ExecContext(ctx, tquery, targs...) - if err != nil { - utils.Rollback(tx) - if strings.Contains(err.Error(), "could not serialize") { - continue - } - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_EXECUTION) - } + slog.Debug("Retrieved transaction: ", slog.Any("transaction", transaction), "for tenant: ", slog.Any("tenant_id", tenantID)) + + for _, op := range b.GetOperations() { + tb, ab, err := bundle.Operation(arguments, op) + if err != nil { + return nil, err } - slog.Debug("Processing attribute and executing update query.") + err = w.runOperation(ctx, tx, xid, tenantID, tb, ab) + if err != nil { + return nil, err + } + } - if !validation.IsAttributeFilterEmpty(attributeFilter) { - abuilder := w.database.Builder.Update(AttributesTable).Set("expired_tx_id", xid).Where(squirrel.Eq{"expired_tx_id": "0", "tenant_id": tenantID}) - abuilder = utils.AttributesFilterQueryForUpdateBuilder(abuilder, attributeFilter) + if err := tx.Commit(); err != nil { + return nil, err + } - var aquery string - var aargs []interface{} + return snapshot.NewToken(xid).Encode(), nil +} - aquery, aargs, err = abuilder.ToSql() - if err != nil { - utils.Rollback(tx) - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_SQL_BUILDER) +// runOperation processes and executes database operations defined in TupleBundle and AttributeBundle within a given transaction. +func (w *DataWriter) runOperation( + ctx context.Context, + tx *sql.Tx, + xid types.XID8, + tenantID string, + tb database.TupleBundle, + ab database.AttributeBundle, +) (err error) { + slog.Debug("Processing bundles queries. ") + + if len(tb.Write.GetTuples()) > 0 { + + tuplesInsertBuilder := w.database.Builder.Insert(RelationTuplesTable).Columns("entity_type, entity_id, relation, subject_type, subject_id, subject_relation, created_tx_id, tenant_id") + + deleteClauses := squirrel.Or{} + + titer := tb.Write.CreateTupleIterator() + for titer.HasNext() { + t := titer.GetNext() + srelation := t.GetSubject().GetRelation() + if srelation == tuple.ELLIPSIS { + srelation = "" } - _, err = tx.ExecContext(ctx, aquery, aargs...) - if err != nil { - utils.Rollback(tx) - if strings.Contains(err.Error(), "could not serialize") { - continue - } - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_EXECUTION) + // Build the condition for this tuple. + condition := squirrel.Eq{ + "entity_type": t.GetEntity().GetType(), + "entity_id": t.GetEntity().GetId(), + "relation": t.GetRelation(), + "subject_type": t.GetSubject().GetType(), + "subject_id": t.GetSubject().GetId(), + "subject_relation": srelation, } - } - if err = tx.Commit(); err != nil { - utils.Rollback(tx) - if strings.Contains(err.Error(), "could not serialize") { - continue - } - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_EXECUTION) - } + // Add the condition to the OR slice. + deleteClauses = append(deleteClauses, condition) - slog.Info("Data successfully deleted from the database.") + tuplesInsertBuilder = tuplesInsertBuilder.Values(t.GetEntity().GetType(), t.GetEntity().GetId(), t.GetRelation(), t.GetSubject().GetType(), t.GetSubject().GetId(), srelation, xid, tenantID) + } - return snapshot.NewToken(xid).Encode(), nil - } + tDeleteBuilder := w.database.Builder.Update(RelationTuplesTable).Set("expired_tx_id", xid).Where(squirrel.Eq{ + "expired_tx_id": "0", + "tenant_id": tenantID, + }).Where(deleteClauses) - slog.Error("Failed to delete data from the database. Max retries reached. Aborting operation. ", slog.Any("error", errors.New(base.ErrorCode_ERROR_CODE_ERROR_MAX_RETRIES.String()))) + var tdquery string + var tdargs []interface{} - return nil, errors.New(base.ErrorCode_ERROR_CODE_ERROR_MAX_RETRIES.String()) -} + tdquery, tdargs, err = tDeleteBuilder.ToSql() + if err != nil { + return err + } -func (w *DataWriter) RunBundle(ctx context.Context, tenantID string, arguments map[string]string, b *base.DataBundle) (token token.EncodedSnapToken, err error) { - ctx, span := tracer.Start(ctx, "data-writer.run-bundle") - defer span.End() + _, err = tx.ExecContext(ctx, tdquery, tdargs...) + if err != nil { + return err + } - slog.Info("Running bundle. TenantID: ", slog.String("tenant_id", tenantID)) + var tiquery string + var tiargs []interface{} - for _, op := range b.GetOperations() { + tiquery, tiargs, err = tuplesInsertBuilder.ToSql() + if err != nil { + return err + } - tb, ab, err := bundle.Operation(arguments, op) + _, err = tx.ExecContext(ctx, tiquery, tiargs...) if err != nil { - return nil, err + return err } + } - for i := 0; i <= w.maxRetries; i++ { - var tx *sql.Tx - tx, err = w.database.DB.BeginTx(ctx, &w.txOptions) - if err != nil { - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_INTERNAL) - } + if len(ab.Write.GetAttributes()) > 0 { - slog.Debug("Inserting transaction record for tenant: ", slog.String("tenant_id", tenantID)) + attributesInsertBuilder := w.database.Builder.Insert(AttributesTable).Columns("entity_type, entity_id, attribute, value, created_tx_id, tenant_id") - transaction := w.database.Builder.Insert("transactions"). - Columns("tenant_id"). - Values(tenantID). - Suffix("RETURNING id").RunWith(tx) - if err != nil { - utils.Rollback(tx) - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_SQL_BUILDER) - } + deleteClauses := squirrel.Or{} + + aiter := ab.Write.CreateAttributeIterator() + for aiter.HasNext() { + a := aiter.GetNext() - var xid types.XID8 - err = transaction.QueryRowContext(ctx).Scan(&xid) + m := jsonpb.Marshaler{} + jsonStr, err := m.MarshalToString(a.GetValue()) if err != nil { - utils.Rollback(tx) - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_EXECUTION) + return err } - slog.Debug("Retrieved transaction: ", slog.Any("transaction", transaction), "for tenant: ", slog.Any("tenant_id", tenantID)) - - slog.Debug("Processing bundles queries. ") - - if len(tb.Write.GetTuples()) > 0 { - - tuplesInsertBuilder := w.database.Builder.Insert(RelationTuplesTable).Columns("entity_type, entity_id, relation, subject_type, subject_id, subject_relation, created_tx_id, tenant_id") - - deleteClauses := squirrel.Or{} - - titer := tb.Write.CreateTupleIterator() - for titer.HasNext() { - t := titer.GetNext() - srelation := t.GetSubject().GetRelation() - if srelation == tuple.ELLIPSIS { - srelation = "" - } - - // Build the condition for this tuple. - condition := squirrel.Eq{ - "entity_type": t.GetEntity().GetType(), - "entity_id": t.GetEntity().GetId(), - "relation": t.GetRelation(), - "subject_type": t.GetSubject().GetType(), - "subject_id": t.GetSubject().GetId(), - "subject_relation": srelation, - } - - // Add the condition to the OR slice. - deleteClauses = append(deleteClauses, condition) - - tuplesInsertBuilder = tuplesInsertBuilder.Values(t.GetEntity().GetType(), t.GetEntity().GetId(), t.GetRelation(), t.GetSubject().GetType(), t.GetSubject().GetId(), srelation, xid, tenantID) - } - - tDeleteBuilder := w.database.Builder.Update(RelationTuplesTable).Set("expired_tx_id", xid).Where(squirrel.Eq{ - "expired_tx_id": "0", - "tenant_id": tenantID, - }).Where(deleteClauses) - - var tdquery string - var tdargs []interface{} - - tdquery, tdargs, err = tDeleteBuilder.ToSql() - if err != nil { - utils.Rollback(tx) - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_SQL_BUILDER) - } - - _, err = tx.ExecContext(ctx, tdquery, tdargs...) - if err != nil { - utils.Rollback(tx) - if strings.Contains(err.Error(), "could not serialize") { - continue - } - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_EXECUTION) - } - - var tiquery string - var tiargs []interface{} - - tiquery, tiargs, err = tuplesInsertBuilder.ToSql() - if err != nil { - utils.Rollback(tx) - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_SQL_BUILDER) - } - - _, err = tx.ExecContext(ctx, tiquery, tiargs...) - if err != nil { - utils.Rollback(tx) - if strings.Contains(err.Error(), "could not serialize") { - continue - } - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_EXECUTION) - } + // Build the condition for this tuple. + condition := squirrel.Eq{ + "entity_type": a.GetEntity().GetType(), + "entity_id": a.GetEntity().GetId(), + "attribute": a.GetAttribute(), } - if len(ab.Write.GetAttributes()) > 0 { - - attributesInsertBuilder := w.database.Builder.Insert(AttributesTable).Columns("entity_type, entity_id, attribute, value, created_tx_id, tenant_id") - - deleteClauses := squirrel.Or{} - - aiter := ab.Write.CreateAttributeIterator() - for aiter.HasNext() { - a := aiter.GetNext() - - m := jsonpb.Marshaler{} - jsonStr, err := m.MarshalToString(a.GetValue()) - if err != nil { - utils.Rollback(tx) - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_INVALID_ARGUMENT) - } - - // Build the condition for this tuple. - condition := squirrel.Eq{ - "entity_type": a.GetEntity().GetType(), - "entity_id": a.GetEntity().GetId(), - "attribute": a.GetAttribute(), - } - - // Add the condition to the OR slice. - deleteClauses = append(deleteClauses, condition) - - attributesInsertBuilder = attributesInsertBuilder.Values(a.GetEntity().GetType(), a.GetEntity().GetId(), a.GetAttribute(), jsonStr, xid, tenantID) - } - - tDeleteBuilder := w.database.Builder.Update(AttributesTable).Set("expired_tx_id", xid).Where(squirrel.Eq{ - "expired_tx_id": "0", - "tenant_id": tenantID, - }).Where(deleteClauses) - - var adquery string - var adargs []interface{} - - adquery, adargs, err = tDeleteBuilder.ToSql() - if err != nil { - utils.Rollback(tx) - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_SQL_BUILDER) - } - - _, err = tx.ExecContext(ctx, adquery, adargs...) - if err != nil { - utils.Rollback(tx) - if strings.Contains(err.Error(), "could not serialize") { - continue - } - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_EXECUTION) - } - - var aquery string - var aargs []interface{} - - aquery, aargs, err = attributesInsertBuilder.ToSql() - if err != nil { - utils.Rollback(tx) - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_SQL_BUILDER) - } - - _, err = tx.ExecContext(ctx, aquery, aargs...) - if err != nil { - utils.Rollback(tx) - if strings.Contains(err.Error(), "could not serialize") { - continue - } - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_EXECUTION) - } - } + // Add the condition to the OR slice. + deleteClauses = append(deleteClauses, condition) - if len(tb.Delete.GetTuples()) > 0 { - - deleteClauses := squirrel.Or{} - - titer := tb.Delete.CreateTupleIterator() - for titer.HasNext() { - t := titer.GetNext() - srelation := t.GetSubject().GetRelation() - if srelation == tuple.ELLIPSIS { - srelation = "" - } - - // Build the condition for this tuple. - condition := squirrel.Eq{ - "entity_type": t.GetEntity().GetType(), - "entity_id": t.GetEntity().GetId(), - "relation": t.GetRelation(), - "subject_type": t.GetSubject().GetType(), - "subject_id": t.GetSubject().GetId(), - "subject_relation": srelation, - } - - // Add the condition to the OR slice. - deleteClauses = append(deleteClauses, condition) - } - - tDeleteBuilder := w.database.Builder.Update(RelationTuplesTable).Set("expired_tx_id", xid).Where(squirrel.Eq{ - "expired_tx_id": "0", - "tenant_id": tenantID, - }).Where(deleteClauses) - - var tquery string - var targs []interface{} - - tquery, targs, err = tDeleteBuilder.ToSql() - if err != nil { - utils.Rollback(tx) - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_SQL_BUILDER) - } - - _, err = tx.ExecContext(ctx, tquery, targs...) - if err != nil { - utils.Rollback(tx) - if strings.Contains(err.Error(), "could not serialize") { - continue - } - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_EXECUTION) - } - } + attributesInsertBuilder = attributesInsertBuilder.Values(a.GetEntity().GetType(), a.GetEntity().GetId(), a.GetAttribute(), jsonStr, xid, tenantID) + } - if len(ab.Delete.GetAttributes()) > 0 { + tDeleteBuilder := w.database.Builder.Update(AttributesTable).Set("expired_tx_id", xid).Where(squirrel.Eq{ + "expired_tx_id": "0", + "tenant_id": tenantID, + }).Where(deleteClauses) - deleteClauses := squirrel.Or{} + var adquery string + var adargs []interface{} - aiter := ab.Delete.CreateAttributeIterator() - for aiter.HasNext() { - a := aiter.GetNext() + adquery, adargs, err = tDeleteBuilder.ToSql() + if err != nil { + return err + } - // Build the condition for this tuple. - condition := squirrel.Eq{ - "entity_type": a.GetEntity().GetType(), - "entity_id": a.GetEntity().GetId(), - "attribute": a.GetAttribute(), - } + _, err = tx.ExecContext(ctx, adquery, adargs...) + if err != nil { + return err + } - // Add the condition to the OR slice. - deleteClauses = append(deleteClauses, condition) + var aquery string + var aargs []interface{} - } + aquery, aargs, err = attributesInsertBuilder.ToSql() + if err != nil { + return err + } - aDeleteBuilder := w.database.Builder.Update(AttributesTable).Set("expired_tx_id", xid).Where(squirrel.Eq{ - "expired_tx_id": "0", - "tenant_id": tenantID, - }).Where(deleteClauses) + _, err = tx.ExecContext(ctx, aquery, aargs...) + if err != nil { + return err + } + } - var tquery string - var targs []interface{} + if len(tb.Delete.GetTuples()) > 0 { - tquery, targs, err = aDeleteBuilder.ToSql() - if err != nil { - utils.Rollback(tx) - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_SQL_BUILDER) - } + deleteClauses := squirrel.Or{} - _, err = tx.ExecContext(ctx, tquery, targs...) - if err != nil { - utils.Rollback(tx) - if strings.Contains(err.Error(), "could not serialize") { - continue - } - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_EXECUTION) - } + titer := tb.Delete.CreateTupleIterator() + for titer.HasNext() { + t := titer.GetNext() + srelation := t.GetSubject().GetRelation() + if srelation == tuple.ELLIPSIS { + srelation = "" } - if err = tx.Commit(); err != nil { - utils.Rollback(tx) - if strings.Contains(err.Error(), "could not serialize") { - continue - } - return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_EXECUTION) + // Build the condition for this tuple. + condition := squirrel.Eq{ + "entity_type": t.GetEntity().GetType(), + "entity_id": t.GetEntity().GetId(), + "relation": t.GetRelation(), + "subject_type": t.GetSubject().GetType(), + "subject_id": t.GetSubject().GetId(), + "subject_relation": srelation, } - slog.Info("Bundle successfully worked.") + // Add the condition to the OR slice. + deleteClauses = append(deleteClauses, condition) + } - return snapshot.NewToken(xid).Encode(), nil + tDeleteBuilder := w.database.Builder.Update(RelationTuplesTable).Set("expired_tx_id", xid).Where(squirrel.Eq{ + "expired_tx_id": "0", + "tenant_id": tenantID, + }).Where(deleteClauses) + + var tquery string + var targs []interface{} + + tquery, targs, err = tDeleteBuilder.ToSql() + if err != nil { + return err + } + + _, err = tx.ExecContext(ctx, tquery, targs...) + if err != nil { + return err + } + } + + if len(ab.Delete.GetAttributes()) > 0 { + + deleteClauses := squirrel.Or{} + + aiter := ab.Delete.CreateAttributeIterator() + for aiter.HasNext() { + a := aiter.GetNext() + + // Build the condition for this tuple. + condition := squirrel.Eq{ + "entity_type": a.GetEntity().GetType(), + "entity_id": a.GetEntity().GetId(), + "attribute": a.GetAttribute(), + } + + // Add the condition to the OR slice. + deleteClauses = append(deleteClauses, condition) + + } + + aDeleteBuilder := w.database.Builder.Update(AttributesTable).Set("expired_tx_id", xid).Where(squirrel.Eq{ + "expired_tx_id": "0", + "tenant_id": tenantID, + }).Where(deleteClauses) + + var tquery string + var targs []interface{} + + tquery, targs, err = aDeleteBuilder.ToSql() + if err != nil { + return err + } + + _, err = tx.ExecContext(ctx, tquery, targs...) + if err != nil { + return err } } - return nil, err + return nil } diff --git a/internal/storage/postgres/schemaReader.go b/internal/storage/postgres/schemaReader.go index 46e70d26c..4a754c7c0 100644 --- a/internal/storage/postgres/schemaReader.go +++ b/internal/storage/postgres/schemaReader.go @@ -7,7 +7,6 @@ import ( "log/slog" "github.com/Masterminds/squirrel" - "go.opentelemetry.io/otel/codes" "github.com/Permify/permify/internal/schema" "github.com/Permify/permify/internal/storage" @@ -62,12 +61,7 @@ func (r *SchemaReader) ReadSchema(ctx context.Context, tenantID, version string) sd := storage.SchemaDefinition{} err = rows.Scan(&sd.Name, &sd.SerializedDefinition, &sd.Version) if err != nil { - span.RecordError(err) - span.SetStatus(codes.Error, err.Error()) - - slog.Error("Error scanning rows: ", slog.Any("error", err)) - - return nil, err + return nil, utils.HandleError(span, err, base.ErrorCode_ERROR_CODE_SCAN) } definitions = append(definitions, sd.Serialized()) } diff --git a/internal/storage/postgres/utils/common.go b/internal/storage/postgres/utils/common.go index 66447142b..99dd138b9 100644 --- a/internal/storage/postgres/utils/common.go +++ b/internal/storage/postgres/utils/common.go @@ -1,7 +1,6 @@ package utils import ( - "database/sql" "fmt" "log/slog" @@ -116,13 +115,8 @@ func GenerateGCQuery(table string, value uint64) squirrel.DeleteBuilder { return deleteBuilder.Where(expiredZeroExpr).Where(beforeExpr) } -// Rollback - Rollbacks a transaction and logs the error -func Rollback(tx *sql.Tx) { - if err := tx.Rollback(); !errors.Is(err, sql.ErrTxDone) && err != nil { - slog.Error("failed to rollback transaction", err) - } -} - +// HandleError records an error in the given span, logs the error, and returns a standardized error. +// This function is used for consistent error handling across different parts of the application. func HandleError(span trace.Span, err error, errorCode base.ErrorCode) error { // Record the error on the span span.RecordError(err) diff --git a/pkg/pb/base/v1/errors.pb.go b/pkg/pb/base/v1/errors.pb.go index 5c0e7a187..0090ed172 100644 --- a/pkg/pb/base/v1/errors.pb.go +++ b/pkg/pb/base/v1/errors.pb.go @@ -84,6 +84,7 @@ const ( ErrorCode_ERROR_CODE_ROLLBACK ErrorCode = 5010 ErrorCode_ERROR_CODE_EXCLUSION_REQUIRES_MORE_THAN_ONE_FUNCTION ErrorCode = 5011 ErrorCode_ERROR_CODE_NOT_IMPLEMENTED ErrorCode = 5012 + ErrorCode_ERROR_CODE_DATASTORE ErrorCode = 5013 ) // Enum value maps for ErrorCode. @@ -146,6 +147,7 @@ var ( 5010: "ERROR_CODE_ROLLBACK", 5011: "ERROR_CODE_EXCLUSION_REQUIRES_MORE_THAN_ONE_FUNCTION", 5012: "ERROR_CODE_NOT_IMPLEMENTED", + 5013: "ERROR_CODE_DATASTORE", } ErrorCode_value = map[string]int32{ "ERROR_CODE_UNSPECIFIED": 0, @@ -205,6 +207,7 @@ var ( "ERROR_CODE_ROLLBACK": 5010, "ERROR_CODE_EXCLUSION_REQUIRES_MORE_THAN_ONE_FUNCTION": 5011, "ERROR_CODE_NOT_IMPLEMENTED": 5012, + "ERROR_CODE_DATASTORE": 5013, } ) @@ -301,7 +304,7 @@ var file_base_v1_errors_proto_rawDesc = []byte{ 0x2e, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x2a, 0xaf, 0x11, 0x0a, 0x09, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x64, 0x65, + 0x67, 0x65, 0x2a, 0xca, 0x11, 0x0a, 0x09, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x24, 0x0a, 0x1f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x4d, 0x49, 0x53, 0x53, 0x49, @@ -440,16 +443,18 @@ var file_base_v1_errors_proto_rawDesc = []byte{ 0x48, 0x41, 0x4e, 0x5f, 0x4f, 0x4e, 0x45, 0x5f, 0x46, 0x55, 0x4e, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x93, 0x27, 0x12, 0x1f, 0x0a, 0x1a, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x4e, 0x4f, 0x54, 0x5f, 0x49, 0x4d, 0x50, 0x4c, 0x45, 0x4d, 0x45, 0x4e, 0x54, 0x45, - 0x44, 0x10, 0x94, 0x27, 0x42, 0x89, 0x01, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x2e, 0x62, 0x61, 0x73, - 0x65, 0x2e, 0x76, 0x31, 0x42, 0x0b, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x50, 0x72, 0x6f, 0x74, - 0x6f, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x50, 0x65, 0x72, 0x6d, 0x69, 0x66, 0x79, 0x2f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x66, 0x79, 0x2f, - 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x61, 0x73, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x62, - 0x61, 0x73, 0x65, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x42, 0x58, 0x58, 0xaa, 0x02, 0x07, 0x42, 0x61, - 0x73, 0x65, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x07, 0x42, 0x61, 0x73, 0x65, 0x5c, 0x56, 0x31, 0xe2, - 0x02, 0x13, 0x42, 0x61, 0x73, 0x65, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x08, 0x42, 0x61, 0x73, 0x65, 0x3a, 0x3a, 0x56, 0x31, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x44, 0x10, 0x94, 0x27, 0x12, 0x19, 0x0a, 0x14, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x5f, 0x43, 0x4f, + 0x44, 0x45, 0x5f, 0x44, 0x41, 0x54, 0x41, 0x53, 0x54, 0x4f, 0x52, 0x45, 0x10, 0x95, 0x27, 0x42, + 0x89, 0x01, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x2e, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x76, 0x31, 0x42, + 0x0b, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x30, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x50, 0x65, 0x72, 0x6d, 0x69, + 0x66, 0x79, 0x2f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x66, 0x79, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, + 0x62, 0x2f, 0x62, 0x61, 0x73, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x62, 0x61, 0x73, 0x65, 0x76, 0x31, + 0xa2, 0x02, 0x03, 0x42, 0x58, 0x58, 0xaa, 0x02, 0x07, 0x42, 0x61, 0x73, 0x65, 0x2e, 0x56, 0x31, + 0xca, 0x02, 0x07, 0x42, 0x61, 0x73, 0x65, 0x5c, 0x56, 0x31, 0xe2, 0x02, 0x13, 0x42, 0x61, 0x73, + 0x65, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0xea, 0x02, 0x08, 0x42, 0x61, 0x73, 0x65, 0x3a, 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( diff --git a/pkg/pb/base/v1/openapi.pb.go b/pkg/pb/base/v1/openapi.pb.go index 3a0347053..4f0a606e6 100644 --- a/pkg/pb/base/v1/openapi.pb.go +++ b/pkg/pb/base/v1/openapi.pb.go @@ -46,7 +46,7 @@ var file_base_v1_openapi_proto_rawDesc = []byte{ 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x66, 0x79, 0x2f, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x66, 0x79, 0x2f, 0x62, 0x6c, 0x6f, 0x62, 0x2f, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x2f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, - 0x32, 0x06, 0x76, 0x30, 0x2e, 0x36, 0x2e, 0x31, 0x2a, 0x01, 0x02, 0x32, 0x10, 0x61, 0x70, 0x70, + 0x32, 0x06, 0x76, 0x30, 0x2e, 0x36, 0x2e, 0x32, 0x2a, 0x01, 0x02, 0x32, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x5a, 0x23, 0x0a, 0x21, 0x0a, 0x0a, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x41, 0x75, 0x74, 0x68, 0x12, diff --git a/proto/base/v1/errors.proto b/proto/base/v1/errors.proto index f4e107936..a02ea2bd9 100644 --- a/proto/base/v1/errors.proto +++ b/proto/base/v1/errors.proto @@ -11,7 +11,6 @@ enum ErrorCode { ERROR_CODE_UNAUTHENTICATED = 1002; ERROR_CODE_MISSING_TENANT_ID = 1003; - // validation ERROR_CODE_VALIDATION = 2000; ERROR_CODE_UNDEFINED_CHILD_TYPE = 2002; @@ -70,6 +69,7 @@ enum ErrorCode { ERROR_CODE_ROLLBACK = 5010; ERROR_CODE_EXCLUSION_REQUIRES_MORE_THAN_ONE_FUNCTION = 5011; ERROR_CODE_NOT_IMPLEMENTED = 5012; + ERROR_CODE_DATASTORE = 5013; } // ErrorResponse diff --git a/proto/base/v1/openapi.proto b/proto/base/v1/openapi.proto index 6c23a2c1e..cf1e8a9fd 100644 --- a/proto/base/v1/openapi.proto +++ b/proto/base/v1/openapi.proto @@ -9,7 +9,7 @@ option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = { info: { title: "Permify API"; description: "Permify is an open source authorization service for creating fine-grained and scalable authorization systems."; - version: "v0.6.1"; + version: "v0.6.2"; contact: { name: "API Support"; url: "https://github.com/Permify/permify/issues";