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

Draft: Expand odata #74

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

Conversation

cakoolen
Copy link
Contributor

When retrieving information from Balena using the odata protocol it is possible to $extend embedded structures. The odata object is used to store either the ID and the deferred URI to allow the caller to determine how to obtain the data but using the As method combined with the $extend query it is possible to retrieve all the information using a single query.

Example:

response, err := client.Application.GetWithQuery(ctx, "$expand=is_for__device_type&$filter=is_directly_accessible_by__user/any(dau:1%20eq%201)")

can be used to retrieve all applications the current user has access to and populate the IsForDeviceType field on the fly.

To retrieve this information the caller now can do:

var deviceType []*DeviceTypeResponse
_ = response.IsForDeviceType.As(&deviceType)

And retrieve the data already transmitted.

This can also be nested:

response, _ := client.Application.GetWithQuery(ctx, "$expand=is_for__device_type($expand=is_of__cpu_architecture&$filter=is_directly_accessible_by__user/any(dau:1%20eq%201)+and+is_of__class+eq+'fleet'")

The raw json data is stored as an embedded object inside the odata Object such that it can be unmarshalled at request.

@cakoolen
Copy link
Contributor Author

cakoolen commented Jan 16, 2023

Since implementing this, I also figured out a different way of implementing this that would be much nicer to the application at hand but way more work to implement. It would require me to replace all the *odata.Object members of the different structs with a dedicated type that can unmarshal either the deferred data or the actual data depending on the situation. This might look something like this:

type DeviceTypeResponse struct {
	ID                  int64                 `json:"id,omitempty"`
	Slug                string                `json:"slug,omitempty"`
	Name                string                `json:"name,omitempty"`
	IsPrivate           bool                  `json:"is_private,omitempty"`
	IsOfCPUArchitecture *CPUArchitectureOData `json:"is_of__cpu_architecture,omitempty"`
	BelongsToFamily     *BelongsToFamilyOData `json:"belongs_to__device_family,omitempty"`
}

type DeviceTypeOData struct {
	D []*DeviceTypeResponse
	*odata.Object
}

func (d *DeviceTypeOData) UnmarshalJSON(data []byte) error {
	o := new(odata.Object)
	if err := json.Unmarshal(data, o); err == nil {
		d.Object = o
		return nil
	}

	return json.Unmarshal(data, &d.D)
}

Using this method, most client code can still remain the same since the *odata.Object is embedded in the struct and its fields will be seamlessly available to the caller. Though for the advanced callers using the $expand query it's possible to access the retrieved slices directly though the D member of the struct.

@cakoolen cakoolen changed the title Expand odata Draft: Expand odata Jan 16, 2023
cakoolen and others added 9 commits January 24, 2023 07:57
These fields were missing while implementing an application using this
sdk.
When retrieving information from Balena using the odata protocol it is
possible to $extend embedded structures. The odata object is used to
store either the ID and the deferred URI to allow the caller to determine
how to obtain the data but using the As method combined with the
$extend query it is possible to retrieve all the information using a
single query.

Example:
  ```
  response, err := client.Application.GetWithQuery(ctx, "$expand=is_for__device_type&$filter=is_directly_accessible_by__user/any(dau:1%20eq%201)")
  ```

  can be used to retrieve all applications the current user has access to
  and populate the `IsForDeviceType` field on the fly.

  To retrieve this information the caller now can do

  ```
  var deviceType []*DeviceTypeResponse
  _ = response.IsForDeviceType.As(&deviceType)
  ```

	And retrieve the data already transmitted.

This can also be nested:
  ```
  response, _ := client.Application.GetWithQuery(ctx, "$expand=is_for__device_type($expand=is_of__cpu_architecture&$filter=is_directly_accessible_by__user/any(dau:1%20eq%201)+and+is_of__class+eq+'fleet'")
  ```

The raw json data is stored as an embedded object inside the odata
`Object` such that it can be unmarshalled at request.
@alethenorio
Copy link
Contributor

@cakoolen just a heads up that given my current bandwidth for these PRs I am prioritizing the other more straight forward PRs as this need a little more attention from me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants