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

RSDK-9174: add app wrappers #4561

Open
wants to merge 82 commits into
base: main
Choose a base branch
from

Conversation

purplenicole730
Copy link
Member

@purplenicole730 purplenicole730 commented Nov 18, 2024

Adding all the app wrappers.

Notes:

  • I did add multiple files to try and split up the file a little between the wrappers and types as there are just so many. I can easily consolidate app_proto_conversions.go into app_client.go if preferred.
  • There are also some shadow types that are used in ML Training in registry_proto_conversions.go. I kept this here since ML Training will be using this too, so I'll probably use this file and rename it as the ML Training client. Anything ML Training does not use when we introduce the ML Training wrappers will probably go back into app_proto_conversions.go.
  • Talking to @benjirewis and @jckras, we came to the conclusion that for TailRobotPartLogs, we can give the stream object itself to the user so that the user does what they want with it, whether they want to make it a channel or not. This seems to be common practice in Go.

@viambot viambot added the safe to test This pull request is marked safe to test from a trusted zone label Nov 21, 2024
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Nov 21, 2024
Copy link
Member

@stuqdog stuqdog left a comment

Choose a reason for hiding this comment

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

Echoing @benjirewis, thank you so much for putting in all this work! I'll admit I skimmed the portions that were fairly boilerplate-y, but this generally looks really good! Just a couple mostly small changes requested.

@@ -177,7 +177,7 @@ type DatabaseConnReturn struct {
HasDatabaseUser bool
}

// NewDataClient constructs a new DataClient using the connection passed in by the viamClient.
// NewDataClient constructs a new DataClient using the connection passed in by the Viam client.
Copy link
Member

Choose a reason for hiding this comment

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

(minor/nit) we should probably go with ViamClient here since that's the name of the type.

Comment on lines 466 to 468
// `role`` must be "owner" or "operator"
role string
// `resourceType` must be "organization", "location", or "robot"
Copy link
Member

Choose a reason for hiding this comment

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

If it's possible that a user constructs an APIKeyAuthorization directly (which I believe it is, given that it's a public struct), we probably shouldn't give them the option to pass the wrong value here. I realize we've already got a lot of types, but I'd consider adding AppAuthRole and AppResourceType enums that constrain what a user is allowed to say to valid inputs only.

resourceID string
}

func createAuthorization(orgID, identityID, identityType, role, resourceType, resourceID string) (*pb.Authorization, error) {
Copy link
Member

Choose a reason for hiding this comment

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

I'm a bit split on this one but I wonder if we should ask for an APIKeyAuthorization here instead of asking for role, resourceType, and resourceID as separate fields. It's not necessarily for creating an API key so we'd probably want to rename that struct, but it might simplify the user experience.

If we opt not to do that (and I'll defer to your opinion on this one), I think we should be sure to switch the types we ask for here to prevent invalid inputs (see above comment).

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm... That's a good point. Every time I think I settled on a decision, I change it.
I think sticking with APIKeyAuthorization is the move because I can't tell why these specific three would be grouped other than because that's what you specifically need for an API key. But yes, I will change the types.

}
}

// Model has the API and model of a model.
Copy link
Member

Choose a reason for hiding this comment

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

(minor) this comment is a little confusing/recursive. What is the model of a model?

Copy link
Member

Choose a reason for hiding this comment

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

return resp.Available, nil
}

// UpdateOrganizationOptions contains optional parameters for UpdateOrganization.
Copy link
Member

Choose a reason for hiding this comment

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

This is a bit of an annoying/heavy lift (sorry!), but we have a number of option structs that just have this explanatory comment ({T}Options contains optional parameters for {T}). I think most of the time what these parameters do is pretty self explanatory so more isn't necessary, but in at least a couple cases (firstRun from UpdateModuleOptions comes to mind) the intended purpose/use seems non-obvious. Can we go through and add brief side comments in cases where it's unclear?

Copy link
Member

Choose a reason for hiding this comment

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

Agree! I'm surprised the linter isn't yelling at you, actually, as it will usually require an "export comment" on every exported field of an exported struct.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, the linter requires you to have a comment for each struct, which is why that comment is there on all of them. But yes, will definitely look through one more time to see confusing types.

errorsOnly = true
)

var (
Copy link
Member

Choose a reason for hiding this comment

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

(q) just out of curiosity, why are these vars and not consts?

Copy link
Member Author

Choose a reason for hiding this comment

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

Some of them have to be pointers, and in golang, you can't take an address to a const.

Comment on lines 119 to 124
type UpdateOrganizationOptions struct {
Name *string
Namespace *string
Region *string
CID *string
}
Copy link
Member

Choose a reason for hiding this comment

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

I see here you are putting structs right above where they are being used rather than at the top. in this example it looks like they are doing that too - so I will change mine to mimic this

Copy link
Member

Choose a reason for hiding this comment

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

coming back to this actually, I did not make these changes in my PR yet. I def can but I am curious which way you think we should do it? I think that there are a lot of shadow types and structs in these files and I don't mind having them all at the top that way they are easy to access in one place, but if you prefer having them above the function I am happy to make that change :)

return organizations, nil
}

// GetOrganization gets an organization.
Copy link
Member

Choose a reason for hiding this comment

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

might be overkill to add this but maybe "GetOrganization gets an organization gets an organization based on a given orgID"?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think this should be fine. While the python SDK does expand on this specific function, there are also functions like get_location() that the Python SDK uses #Get a location

}

// UpdateOrganization updates an organization.
func (c *AppClient) UpdateOrganization(ctx context.Context, orgID string, opts UpdateOrganizationOptions) (*Organization, error) {
Copy link
Member

Choose a reason for hiding this comment

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

iirc I believe we want to change the opts parameter to a pointer, opts * UpdateOrganizationOptions, because we want to explicitly allow nil values since these are optional fields.

Copy link
Member

Choose a reason for hiding this comment

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

Agree!

ParentLocationID *string
}

// CreateLocation creates a location.
Copy link
Member

Choose a reason for hiding this comment

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

it is a little unclear what the parameters here are being used for, maybe add something like:

// CreateLocation creates a new location with the specified name under the given orgID. 
// Optionally, the location can be placed under a new parent location specified in the options.

}

// CreateLocation creates a location.
func (c *AppClient) CreateLocation(ctx context.Context, orgID, name string, opts CreateLocationOptions) (*Location, error) {
Copy link
Member

Choose a reason for hiding this comment

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

same thing here about passing opts as pointer (and the ones below)

Copy link
Member

@benjirewis benjirewis left a comment

Choose a reason for hiding this comment

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

Awesome! Just a few nits.

return resp.Available, nil
}

// UpdateOrganizationOptions contains optional parameters for UpdateOrganization.
Copy link
Member

Choose a reason for hiding this comment

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

Agree! I'm surprised the linter isn't yelling at you, actually, as it will usually require an "export comment" on every exported field of an exported struct.

}

// UpdateOrganization updates an organization.
func (c *AppClient) UpdateOrganization(ctx context.Context, orgID string, opts UpdateOrganizationOptions) (*Organization, error) {
Copy link
Member

Choose a reason for hiding this comment

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

Agree!

stream pb.AppService_TailRobotPartLogsClient
}

// Next gets the next robot part log entry.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// Next gets the next robot part log entry.
// Next gets the next slice of robot part log entries.

}
}

// Model has the API and model of a model.
Copy link
Member

Choose a reason for hiding this comment

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

}

// SharedSecretState specifies if the secret is enabled, disabled, or unspecified.
type SharedSecretState int32
Copy link
Member

Choose a reason for hiding this comment

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

[nit] This and the enums in this PR can just be int, I think (int32 or int64 depending on your system.) Using int32 implies some need for a smaller int that probably is not necessary.

Auth *LocationAuth
Organizations []*LocationOrganization
CreatedOn *timestamppb.Timestamp
RobotCount int32
Copy link
Member

Choose a reason for hiding this comment

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

I would also prefer int > int32 inside structs/as parameters.

Copy link
Member Author

Choose a reason for hiding this comment

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

Switching these int types are a bigger struggle than I thought, but hopefully I figured it out.

Copy link
Member

@jckras jckras left a comment

Choose a reason for hiding this comment

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

overall I think this looks great! The one thing that stood out to me was that the optional parameters should probably be passed as pointers. Another thing to consider is adding in checks to ensure we are not passing nil values to proto requests. Unless the .proto file defines the variable as an optional *string but I think for the most part it doesn't (I could be wrong)

Comment on lines 119 to 124
type UpdateOrganizationOptions struct {
Name *string
Namespace *string
Region *string
CID *string
}
Copy link
Member

Choose a reason for hiding this comment

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

coming back to this actually, I did not make these changes in my PR yet. I def can but I am curious which way you think we should do it? I think that there are a lot of shadow types and structs in these files and I don't mind having them all at the top that way they are easy to access in one place, but if you prefer having them above the function I am happy to make that change :)

Comment on lines +128 to +134
resp, err := c.client.UpdateOrganization(ctx, &pb.UpdateOrganizationRequest{
OrganizationId: orgID,
Name: opts.Name,
PublicNamespace: opts.Namespace,
Region: opts.Region,
Cid: opts.CID,
})
Copy link
Member

Choose a reason for hiding this comment

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

I added a check to all of the optional parameters that said

if options.SomeParam != nil {
		metadata.SomeParam = *options.SomeParam
	}

I don't know if this is necessary but I was nervous about a request taking in a nil value. I was hoping that by adding in the check it would just omit this field and give it a zero-value instead. again, not sure if this was needed but sharing jic

Copy link
Member Author

Choose a reason for hiding this comment

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

Passing in nil to pointers is a common practice in Go. If SomeParam is nil, metadata.SomeParam = *options.SomeParam would be passing in a pointer to nil, which I think actually causes problems.

@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Nov 22, 2024
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Nov 22, 2024
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Nov 22, 2024
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Nov 22, 2024
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Nov 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
safe to test This pull request is marked safe to test from a trusted zone
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants