TheThingsNetwork / lorawan-stack

The Things Stack, an Open Source LoRaWAN Network Server
https://www.thethingsindustries.com/stack/
Apache License 2.0
937 stars 302 forks source link

Extend Account Search API to include more information besides the ID #6406

Open ryaplots opened 11 months ago

ryaplots commented 11 months ago

Summary

Follow up to https://github.com/TheThingsNetwork/lorawan-stack/pull/6162/#discussion_r1272433615 in the contacts PR.

Extend the API to also return the entity name.

Current Situation

Currently the searchAccounts API returns only the account ID.

Why do we need this? Who uses it, and when?

Would be nice to also have access to the entity name, for example, through this API.

Proposed Implementation

No response

Contributing

Code of Conduct

KrishnaIyer commented 9 months ago

more information besides the ID

@ryaplots: What additional information do you need?

ryaplots commented 9 months ago

more information besides the ID

@ryaplots: What additional information do you need?

So right now the searchAccounts API returns only the account ID. Would be nice if it returned, the attributes as well (name and description)

adriansmares commented 9 months ago

The context here is that accounts are polymorphic entities - they are either users or organizations. It is possible search accounts, but the only thing which is returned back will be the identifiers for the entity.

The use case here is that organizations or users generally have a name which would be helpful to be rendered when one tries to add someone as a collaborator or contact to an entity - if you need a clear mental image, think of the Github 'invite member' screen: you see both their name and their account ID.

The common fields are:

Not all of these fields are public, so for non-admin users we need to be careful on how we handle them. Luckily the baseline work has been done already - we have the public paths and can filter with them:

https://github.com/TheThingsNetwork/lorawan-stack/blob/894c503de81b6a9a362be4621d593baef2edba4c/pkg/ttnpb/public.go#L139-L158 https://github.com/TheThingsNetwork/lorawan-stack/blob/894c503de81b6a9a362be4621d593baef2edba4c/pkg/ttnpb/public.go#L120-L137

The conditions upon which to filter also exist in SearchUser/SearchOrganization: https://github.com/TheThingsNetwork/lorawan-stack/blob/894c503de81b6a9a362be4621d593baef2edba4c/pkg/identityserver/registry_search.go#L371-L378

So the task looks as follows:

  1. Add a field mask to SearchAccountsRequest.
  2. Add a common fields message to SearchAccountsResponse, containing the common fields depending on the rights of the calling user. Add a repeated field for the account fields.
  3. Add support in the storage layer (bunstore) to join the original table and retrieve these attributes.
  4. Filter them out at RPC layer. If the caller is not an admin we shouldn't even pass the fields to the storage layer in order to avoid doing joins for no reason.

The response message can look something like:

message SearchAccountsResponse {
  repeated OrganizationOrUserIdentifiers account_ids = 1;

  message Account {
    option (thethings.flags.message) = {
      select: true,
      set: false
    };
    // The identifiers of the account. These are public and can be seen by any authenticated user in the network.
    oneof ids {
      option (validate.required) = true;
      OrganizationIdentifiers organization_ids = 1;
      UserIdentifiers user_ids = 2;
    }
    // When the account was created. This information is public and can be seen by any authenticated user in the network.
    google.protobuf.Timestamp created_at = 3 [(thethings.flags.field) = {
      select: false,
      set: false
    }];
    // When the account was last updated. This information is public and can be seen by any authenticated user in the network.
    google.protobuf.Timestamp updated_at = 4 [(thethings.flags.field) = {
      select: false,
      set: false
    }];
    // When the account was deleted. This information is public and can be seen by any authenticated user in the network.
    google.protobuf.Timestamp deleted_at = 5 [(thethings.flags.field) = {
      select: true,
      set: false
    }];
    // The name of the account. This information is public and can be seen by any authenticated user in the network.
    string name = 6 [(validate.rules).string.max_len = 50];
    // A description for the account. This information is public for user accounts and can be seen by any authenticated user in the network.
    string description = 7 [(validate.rules).string.max_len = 2000];
    // Key-value attributes for this account.
    map<string, string> attributes = 8 [(validate.rules).map = {
      max_pairs: 10,
      keys: {
        string: {
          pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$",
          max_len: 36
        }
      },
      values: {
        string: {max_len: 200}
      }
    }];
  }
  repeated Account accounts = 2;
}