Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement backend support for tag colors #4362

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

haller33
Copy link
Contributor

@haller33 haller33 commented Nov 26, 2024

feat(devices): implement backend support for tag colors

@haller33 haller33 self-assigned this Nov 26, 2024
@haller33 haller33 force-pushed the feat/tag-color-support branch 9 times, most recently from 26d50d8 to 4654758 Compare December 4, 2024 16:46
@haller33 haller33 force-pushed the feat/tag-color-support branch 13 times, most recently from 6bb4533 to 954abfa Compare December 9, 2024 13:20
@haller33 haller33 marked this pull request as ready for review December 9, 2024 13:20
@haller33 haller33 requested a review from a team as a code owner December 9, 2024 13:20
@haller33 haller33 force-pushed the feat/tag-color-support branch 4 times, most recently from 79005c0 to fdee49e Compare December 9, 2024 18:14
@haller33 haller33 added status/under-testing Tests broadened and removed status/require-tests labels Dec 9, 2024
@haller33 haller33 force-pushed the feat/tag-color-support branch 3 times, most recently from 124a7b8 to b5e4846 Compare December 9, 2024 18:28
Copy link
Contributor

@heiytor heiytor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also noticed that some of the newly added Tag methods don’t have tests. Could you implement them?

Comment on lines 36 to 40
for _, i := range list {
if i.Name == item {
return true
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use the ContainsFunc method from the slices package here.

Comment on lines +73 to +74
tag, err := s.db.Collection("devices").
UpdateOne(ctx, bson.M{"uid": uid}, bson.M{"$set": bson.M{"tags": tags}})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually check if MatchedCount is 0 and return an ErrNoDocuments. I believe it’s appropriate to do the same here to maintain consistent behavior.

Copy link
Contributor Author

@haller33 haller33 Dec 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have checked, this not use of the MatchedCount has not been made by myself(and have not been intended to be changed), this could led for minimal changes in method behavior, they will remain consistent as to the rest of the other methods return value.

Comment on lines 119 to 126
func (s *Store) VerifyTagExist(ctx context.Context, tagName, tenant string) (bool, error) {
tag := new(models.Tags)

cursor, err := s.db.Collection("tags").Find(ctx,
bson.M{"name": tagName, "tenant_id": tenant})
if err != nil {
return false, FromMongoError(err)
}

for cursor.Next(ctx) {
err := cursor.Decode(tag)
if err != nil {
return false, err
}

if err != nil {
return false, FromMongoError(err)
}

if tag.Name == "" {
return false, nil
}
}

return true, nil
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it expected to have multiple tags with the same name for the same namespace? If not, you could use FindOne instead of Find.

Additionally, there’s a duplicate err != nil check here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually this has been the first idea, but if there is nothing that you are looking for, so they return a error, that means that there is 'no data on collection' and this is a ambiguous error, for a single data or not existing on the collection, so, by not trying to handle a error as empty return data, is better to use the find and deal with empty array; the additional check will be changed

Copy link
Contributor

@heiytor heiytor Dec 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO, using Find here is an over-complicated solution and handling lists when we will always have at most one item is unnecessary. In the end, what you're doing is effectively the same as:

tag := new(models.Tags)
if err := s.db.Collection("tags").FindOne(ctx, bson.M{"name": tagName, "tenant_id": tenant}).Decode(tag); err != nil {
    return false, FromMongoError(err)
}

And when using the method, you can simply check:

if _, err := store.VerifyTagExist(ctx, tagName, tenantID); err != nil {
    if errors.Is(err, ErrNoDocuments) {
        // The tag does not exist here
    } else {
       // Another error happens
    }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, this is understandable, but, this kind of defies the idea of VerifyTagExist, the purpose of VerifyTagExist is to return a boolean value indicating whether a tag exists, while we could just use error handling instead, that would go against the method's intended design, the method was created to simply check and return true/false, not to handle business logic through error handling passing, is understandable that this is the pattern on the code base (for the ErrNoDocuments alone), but still is awkward to use the error as the intended return since there is already a boolean return value to provide that information

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe that returning an error instead of a boolean constitutes handling business logic. I maintain my opinion that using an array to handle a tag that will always contain at most one item is overly complicated.

If the method name no longer aligns with its original purpose, you could rename it to TagsGetOne and have it return the model itself instead of a boolean. In this case, you can simply check if the error is ErrNoDocuments and ignore the tag. The behavior would remain the same.

If the goal is just to check for the tag's existence, you could return something like FromMongoError(err) == ErrNoDocuments.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, if you doesn't agree with that, feel free to resolve the conversation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, you can keep your opinion, but I must say that it's not 'overly complicated', since actually mixing an error with the intended information of a method is not a good pattern/idea (there is a clear separation of what is data, a boolean value, and what is an actual error). indeed, I also will keep the code on this way, if you wish so, since the behavior is the same, and you are just giving an opinion, but again, I'm not criticizing your opinion, just talking about the best interface and logic that fits VerifyTagExist.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed with @gustavosbarreto, please refactor this method to something like the following:

func (s *Store) TagGet(ctx context.Context, tagName, tenant string) (*models.Tags, error) {
    tag := new(models.Tags)
    if err := s.db.Collection("tags").FindOne(ctx, bson.M{"name": tagName, "tenant_id": tenant}).Decode(tag); err != nil {
        return nil, FromMongoError(err)
    }

    return tag, nil
}

When checking whether the tag exists or not, do:

if _, err := store.TagGet(ctx, tagName, tenantID); err != nil {
    if errors.Is(err, ErrNoDocuments) {
        // Tag not found
    } else {
       // Another error happens
    }
}

Comment on lines 104 to 112
res, err := s.TagsBulkDeleteTag(sessCtx, tenant, currentTag)
if err != nil {
return res, FromMongoError(err)
}

tag.Name = newTag

_, err2 := s.db.Collection("tags", options.Collection().SetWriteConcern(writeconcern.Majority())).
InsertOne(sessCtx, tag)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need to delete and reinsert a list of tags instead of updating them?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has to do with a way to simplify behavior in development as an atomic way and the old logic that no longer applies, since your observation, it really is better now to just update, thank you

}

for cursor.Next(ctx) {
if err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The err variable is not updated within the loop

})
if err != nil {
return int64(0), FromMongoError(err)
}

return count.(int64), nil
}

func (s *Store) VerifyTagExist(ctx context.Context, tagName, tenant string) (bool, error) {
tag := new(models.Tags)
Copy link
Member

@gustavosbarreto gustavosbarreto Dec 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Decode method is never called to populate the tag variable with the data retrieved from db.

return false, FromMongoError(err)
}

if tag.Name == "" {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check is unnecessary since the presence of a document inherently confirms the tag's existence.

func (s *Store) VerifyTagExist(ctx context.Context, tagName, tenant string) (bool, error) {
tag := new(models.Tags)

cursor, err := s.db.Collection("tags").Find(ctx,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cursor is not explicitly closed, which could lead to resource leaks.

@haller33 haller33 force-pushed the feat/tag-color-support branch 2 times, most recently from 9a3af19 to f6b5f3e Compare December 11, 2024 19:50
@haller33 haller33 force-pushed the feat/tag-color-support branch from f6b5f3e to f9e95ed Compare December 11, 2024 19:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants