| Conditions | 12 |
| Total Lines | 130 |
| Code Lines | 73 |
| Lines | 0 |
| Ratio | 0 % |
| Changes | 0 | ||
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
Complex classes like postgres.*Watch.getChanges often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
| 1 | package postgres |
||
| 219 | return nil, err |
||
| 220 | } |
||
| 221 | |||
| 222 | slog.DebugContext(ctx, "successfully retrieved recent transaction", slog.Any("ids", xids)) |
||
| 223 | return xids, nil |
||
| 224 | } |
||
| 225 | |||
| 226 | func (w *Watch) getBatchChanges(ctx context.Context, values []types.XID8, tenantID string) (*base.DataChanges, error) { |
||
| 227 | // Initialize a new TupleChanges instance. |
||
| 228 | changes := &base.DataChanges{} |
||
| 229 | |||
| 230 | // Log the batch of XID8 values. |
||
| 231 | slog.DebugContext(ctx, "retrieving changes for transactions", slog.Any("ids", values), slog.Any("tenant_id", tenantID)) |
||
| 232 | |||
| 233 | // Convert the XID8 values into a slice of interface{} for the query. |
||
| 234 | xidInterfaces := make([]interface{}, len(values)) |
||
| 235 | for i, xid := range values { |
||
| 236 | xidInterfaces[i] = xid |
||
| 237 | } |
||
| 238 | |||
| 239 | fmt.Println(xidInterfaces...) |
||
| 240 | |||
| 241 | // Construct the SQL SELECT statement for retrieving the changes from the RelationTuplesTable. |
||
| 242 | tbuilder := w.database.Builder.Select("entity_type, entity_id, relation, subject_type, subject_id, subject_relation, expired_tx_id"). |
||
| 243 | From(RelationTuplesTable). |
||
| 244 | Where(squirrel.Eq{"tenant_id": tenantID}). |
||
| 245 | Where(squirrel.Or{ |
||
| 246 | squirrel.Eq{"created_tx_id": xidInterfaces}, |
||
| 247 | squirrel.Eq{"expired_tx_id": xidInterfaces}, |
||
| 248 | }) |
||
| 249 | |||
| 250 | // Generate the SQL query and arguments. |
||
| 251 | tquery, targs, err := tbuilder.ToSql() |
||
| 252 | if err != nil { |
||
| 253 | slog.ErrorContext(ctx, "error while building sql query for relation tuples", slog.Any("error", err)) |
||
| 254 | return nil, err |
||
| 255 | } |
||
| 256 | |||
| 257 | slog.DebugContext(ctx, "executing sql query for relation tuples", slog.Any("query", tquery), slog.Any("arguments", targs)) |
||
| 258 | |||
| 259 | // Execute the SQL query and retrieve the result rows. |
||
| 260 | trows, err := w.database.ReadPool.Query(ctx, tquery, targs...) |
||
| 261 | if err != nil { |
||
| 262 | slog.ErrorContext(ctx, "failed to execute sql query for relation tuples", slog.Any("error", err)) |
||
| 263 | return nil, errors.New(base.ErrorCode_ERROR_CODE_EXECUTION.String()) |
||
| 264 | } |
||
| 265 | defer trows.Close() |
||
| 266 | |||
| 267 | // Construct the SQL SELECT statement for retrieving changes from the AttributesTable. |
||
| 268 | abuilder := w.database.Builder.Select("entity_type, entity_id, attribute, value, expired_tx_id"). |
||
| 269 | From(AttributesTable). |
||
| 270 | Where(squirrel.Eq{"tenant_id": tenantID}). |
||
| 271 | Where(squirrel.Or{ |
||
| 272 | squirrel.Eq{"created_tx_id": xidInterfaces}, |
||
| 273 | squirrel.Eq{"expired_tx_id": xidInterfaces}, |
||
| 274 | }) |
||
| 275 | |||
| 276 | // Generate the SQL query and arguments for attributes. |
||
| 277 | aquery, aargs, err := abuilder.ToSql() |
||
| 278 | if err != nil { |
||
| 279 | slog.ErrorContext(ctx, "error while building SQL query for attributes", slog.Any("error", err)) |
||
| 280 | return nil, err |
||
| 281 | } |
||
| 282 | |||
| 283 | slog.DebugContext(ctx, "executing sql query for attributes", slog.Any("query", aquery), slog.Any("arguments", aargs)) |
||
| 284 | |||
| 285 | // Execute the SQL query and retrieve the result rows. |
||
| 286 | arows, err := w.database.ReadPool.Query(ctx, aquery, aargs...) |
||
| 287 | if err != nil { |
||
| 288 | slog.ErrorContext(ctx, "error while executing SQL query for attributes", slog.Any("error", err)) |
||
| 289 | return nil, errors.New(base.ErrorCode_ERROR_CODE_EXECUTION.String()) |
||
| 290 | } |
||
| 291 | defer arows.Close() |
||
| 292 | |||
| 293 | // Set the snapshot token for the changes (encode the first XID as a reference). |
||
| 294 | changes.SnapToken = snapshot.Token{Value: values[0]}.Encode().String() |
||
| 295 | |||
| 296 | // Iterate through the result rows for relation tuples. |
||
| 297 | for trows.Next() { |
||
| 298 | var expiredXID types.XID8 |
||
| 299 | |||
| 300 | rt := storage.RelationTuple{} |
||
| 301 | // Scan the result row into a RelationTuple instance. |
||
| 302 | err = trows.Scan(&rt.EntityType, &rt.EntityID, &rt.Relation, &rt.SubjectType, &rt.SubjectID, &rt.SubjectRelation, &expiredXID) |
||
| 303 | if err != nil { |
||
| 304 | slog.ErrorContext(ctx, "error while scanning row for relation tuples", slog.Any("error", err)) |
||
| 305 | return nil, err |
||
| 306 | } |
||
| 307 | |||
| 308 | // Determine the operation type based on the expired transaction ID. |
||
| 309 | op := base.DataChange_OPERATION_CREATE |
||
| 310 | if containsXID(expiredXID, values) { |
||
| 311 | op = base.DataChange_OPERATION_DELETE |
||
| 312 | } |
||
| 313 | |||
| 314 | // Append the change to the list of changes. |
||
| 315 | changes.DataChanges = append(changes.DataChanges, &base.DataChange{ |
||
| 316 | Operation: op, |
||
| 317 | Type: &base.DataChange_Tuple{ |
||
| 318 | Tuple: rt.ToTuple(), |
||
| 319 | }, |
||
| 320 | }) |
||
| 321 | } |
||
| 322 | |||
| 323 | // Iterate through the result rows for attributes. |
||
| 324 | for arows.Next() { |
||
| 325 | var expiredXID types.XID8 |
||
| 326 | rt := storage.Attribute{} |
||
| 327 | var valueStr string |
||
| 328 | |||
| 329 | // Scan the result row into an Attribute instance. |
||
| 330 | err = arows.Scan(&rt.EntityType, &rt.EntityID, &rt.Attribute, &valueStr, &expiredXID) |
||
| 331 | if err != nil { |
||
| 332 | slog.ErrorContext(ctx, "error while scanning row for attributes", slog.Any("error", err)) |
||
| 333 | return nil, err |
||
| 334 | } |
||
| 335 | |||
| 336 | // Unmarshal the JSON data from `valueStr` into `rt.Value`. |
||
| 337 | rt.Value = &anypb.Any{} |
||
| 338 | unmarshaler := &jsonpb.Unmarshaler{} |
||
| 339 | err = unmarshaler.Unmarshal(strings.NewReader(valueStr), rt.Value) |
||
| 340 | if err != nil { |
||
| 341 | slog.ErrorContext(ctx, "failed to unmarshal attribute value", slog.Any("error", err)) |
||
| 342 | return nil, err |
||
| 343 | } |
||
| 344 | |||
| 345 | // Determine the operation type based on the expired transaction ID. |
||
| 346 | op := base.DataChange_OPERATION_CREATE |
||
| 347 | if containsXID(expiredXID, values) { |
||
| 348 | op = base.DataChange_OPERATION_DELETE |
||
| 349 | } |
||
| 375 |