diff --git a/.changeset/smooth-crews-rush.md b/.changeset/smooth-crews-rush.md new file mode 100644 index 00000000000..8ce679f830d --- /dev/null +++ b/.changeset/smooth-crews-rush.md @@ -0,0 +1,5 @@ +--- +'@audius/sdk': patch +--- + +add filtering support for manager endpoints diff --git a/packages/commands/README.md b/packages/commands/README.md index 3692562490f..948f1c94229 100644 --- a/packages/commands/README.md +++ b/packages/commands/README.md @@ -120,4 +120,20 @@ audius-cmd create-user-bank --mint usdc freddie_mercury audius-cmd mint-tokens --from freddie_mercury --mint usdc 10000000 audius-cmd purchase-content --type track --from freddie_mercury -``` \ No newline at end of file +``` + +**Managed accounts** + +``` +# Add a manager +audius-cmd add-manager --from freddie_mercury jim_beach + +# Accept request from manager's account +audius-cmd approve-manager-request --from jim_beach freddie_mercury + +# (OR) Reject request from manager's account +audius-cmd reject-manager-request --from jim_beach freddie_mercury + +# Remove manager +audius-cmd remove-manager --from freddie_mercury jim_beach +``` diff --git a/packages/commands/src/add-manager.mjs b/packages/commands/src/account-managers.mjs similarity index 50% rename from packages/commands/src/add-manager.mjs rename to packages/commands/src/account-managers.mjs index 11c161d24ca..4cfcdafbb60 100644 --- a/packages/commands/src/add-manager.mjs +++ b/packages/commands/src/account-managers.mjs @@ -1,13 +1,7 @@ -import fs from 'fs' - import chalk from 'chalk' import { program } from 'commander' -import { - initializeAudiusLibs, - initializeAudiusSdk, - parseUserId -} from './utils.mjs' +import { initializeAudiusLibs, initializeAudiusSdk } from './utils.mjs' program .command('add-manager') @@ -51,7 +45,7 @@ program }) program - .command('approve-manager') + .command('approve-manager-request') .description('Approve a pending manager request') .argument('[handle]', 'The handle of the user to be managed') .option('-f, --from ', 'The manager account handle') @@ -86,3 +80,77 @@ program process.exit(0) }) + +program + .command('reject-manager-request') + .description('Reject a pending manager request') + .argument('[handle]', 'The handle of the user to be managed') + .option('-f, --from ', 'The manager account handle') + .action(async (handle, { from }) => { + const audiusLibs = await initializeAudiusLibs(from) + // extract privkey and pubkey from hedgehog + // only works with accounts created via audius-cmd + const wallet = audiusLibs?.hedgehog?.getWallet() + const privKey = wallet?.getPrivateKeyString() + const pubKey = wallet?.getAddressString() + + // init sdk with priv and pub keys as api keys and secret + // this enables writes via sdk + const audiusSdk = await initializeAudiusSdk({ + apiKey: pubKey, + apiSecret: privKey + }) + + try { + const { + data: { id: userId } + } = await audiusSdk.users.getUserByHandle({ handle }) + const { + data: { id: managerUserId } + } = await audiusSdk.users.getUserByHandle({ handle: from }) + + await audiusSdk.grants.removeManager({ userId, managerUserId }) + console.log(chalk.green(`Manager request rejected.`)) + } catch (err) { + program.error(err.message) + } + + process.exit(0) + }) + +program + .command('remove-manager') + .description('Remove a manager') + .argument('[handle]', 'The handle of the manager') + .option('-f, --from ', 'The handle of the manager user') + .action(async (handle, { from }) => { + const audiusLibs = await initializeAudiusLibs(from) + // extract privkey and pubkey from hedgehog + // only works with accounts created via audius-cmd + const wallet = audiusLibs?.hedgehog?.getWallet() + const privKey = wallet?.getPrivateKeyString() + const pubKey = wallet?.getAddressString() + + // init sdk with priv and pub keys as api keys and secret + // this enables writes via sdk + const audiusSdk = await initializeAudiusSdk({ + apiKey: pubKey, + apiSecret: privKey + }) + + try { + const { + data: { id: userId } + } = await audiusSdk.users.getUserByHandle({ handle: from }) + const { + data: { id: managerUserId } + } = await audiusSdk.users.getUserByHandle({ handle }) + + await audiusSdk.grants.removeManager({ userId, managerUserId }) + console.log(chalk.green(`Manager removed.`)) + } catch (err) { + program.error(err.message) + } + + process.exit(0) + }) diff --git a/packages/commands/src/index.mjs b/packages/commands/src/index.mjs index 14ef5eff6dc..16ee0760a37 100755 --- a/packages/commands/src/index.mjs +++ b/packages/commands/src/index.mjs @@ -27,7 +27,7 @@ import './create-user-bank.mjs' import './purchase-content.mjs' import './route-tokens-to-user-bank.mjs' import './withdraw-tokens.mjs' -import './add-manager.mjs' +import './account-managers.mjs' import './claim-reward.mjs' async function main() { diff --git a/packages/discovery-provider/integration_tests/tasks/entity_manager/test_grant_entity_manager.py b/packages/discovery-provider/integration_tests/tasks/entity_manager/test_grant_entity_manager.py index fcf580fb3a6..2b75eb3887d 100644 --- a/packages/discovery-provider/integration_tests/tasks/entity_manager/test_grant_entity_manager.py +++ b/packages/discovery-provider/integration_tests/tasks/entity_manager/test_grant_entity_manager.py @@ -11,42 +11,56 @@ from src.utils.db_session import get_db new_grants_data = [ + # Grant from user 1 -> dev app F4C4 { "user_id": 1, "grantee_address": "0x3a388671bb4D6E1Ea08D79Ee191b40FB45A8F4C4", "is_user_grant": False, }, + # Grant from user 1 -> dev app 2e0d { "user_id": 1, "grantee_address": "0x04c9fc3784120f50932436f84c59aebebb12e0d", "is_user_grant": False, }, + # Grant from user 1 -> user 3 { "user_id": 1, "grantee_address": "user3Wallet", "is_user_grant": True, }, + # Grant from user 2 -> user 3 { "user_id": 2, "grantee_address": "user3Wallet", "is_user_grant": True, }, + # Grant from user 3 -> user 4 {"user_id": 3, "grantee_address": "user4Wallet", "is_user_grant": True}, + # Grant from user 1 -> user 4 { "user_id": 1, "grantee_address": "user4Wallet", "is_user_grant": True, }, + # Grant from user 5 -> user 1 { "user_id": 5, "grantee_address": "user1Wallet", "is_user_grant": True, }, + # Grant from user 4 -> user 5 { "user_id": 4, "grantee_address": "user5wallet", "is_user_grant": True, }, + # Grant from user 3 -> user 5 + { + "user_id": 3, + "grantee_address": "user5wallet", + "is_user_grant": True, + }, ] NUM_VALID_GRANTS = len(new_grants_data) + 1 @@ -174,6 +188,20 @@ def test_index_grant(app, mocker): ) }, ], + "CreateGrantTx9": [ + { + "args": AttributeDict( + { + "_entityId": 0, + "_entityType": EntityType.GRANT, + "_userId": new_grants_data[8]["user_id"], + "_metadata": f"""{{"grantee_address": "{new_grants_data[8]["grantee_address"]}"}}""", + "_action": Action.CREATE, + "_signer": f"user{new_grants_data[8]['user_id']}wallet", + } + ) + }, + ], } entity_manager_txs = [ @@ -522,6 +550,7 @@ def get_events_side_effect(_, tx_receipt): assert len(found_matches) == 2 for match in found_matches: assert match.is_approved == False + assert match.is_revoked == True # Invalid reject grant tx_receipts = { @@ -722,8 +751,8 @@ def get_events_side_effect(_, tx_receipt): { "_entityId": 0, "_entityType": EntityType.GRANT, - "_userId": new_grants_data[7]["user_id"], - "_metadata": f"""{{"grantee_address": "{new_grants_data[7]["grantee_address"]}"}}""", + "_userId": new_grants_data[8]["user_id"], + "_metadata": f"""{{"grantee_address": "{new_grants_data[8]["grantee_address"]}"}}""", "_action": Action.DELETE, "_signer": "user1wallet", } @@ -751,7 +780,7 @@ def get_events_side_effect(_, tx_receipt): # validate db records all_grants: List[Grant] = session.query(Grant).all() assert len(all_grants) == NUM_VALID_GRANTS - deleted_grants_indices = [0, 1, 2, 5, 7] + deleted_grants_indices = [0, 1, 2, 5, 8] for index in deleted_grants_indices: expected_grant = new_grants_data[index] found_matches = [ diff --git a/packages/discovery-provider/src/api/v1/users.py b/packages/discovery-provider/src/api/v1/users.py index 353fa534aee..e693b550d19 100644 --- a/packages/discovery-provider/src/api/v1/users.py +++ b/packages/discovery-provider/src/api/v1/users.py @@ -4,7 +4,7 @@ from eth_account.messages import encode_defunct from flask import Response, request -from flask_restx import Namespace, Resource, fields, reqparse +from flask_restx import Namespace, Resource, fields, inputs, reqparse from src.api.v1.helpers import ( DescriptiveArgument, @@ -2081,6 +2081,22 @@ def get(self, id): "managed_users_response", full_ns, fields.List(fields.Nested(managed_user)) ) +managed_users_route_parser = reqparse.RequestParser(argument_class=DescriptiveArgument) +managed_users_route_parser.add_argument( + "is_approved", + required=False, + type=inputs.boolean, + default=None, + description="If true, only show users where the management request has been accepted. If false, only show those where the request was rejected. If omitted, shows all users regardless of approval status.", +) +managed_users_route_parser.add_argument( + "is_revoked", + required=False, + type=inputs.boolean, + default=False, + description="If true, only show users where the management request has been revoked. If false, only show those with a pending or accepted request. Defaults to false.", +) + @full_ns.route("//managed_users") class ManagedUsers(Resource): @@ -2097,12 +2113,20 @@ class ManagedUsers(Resource): 500: "Server error", }, ) - @auth_middleware(require_auth=True) + @full_ns.expect(managed_users_route_parser) + @auth_middleware(managed_users_route_parser, require_auth=True) @full_ns.marshal_with(managed_users_response) def get(self, id, authed_user_id): user_id = decode_with_abort(id, full_ns) check_authorized(user_id, authed_user_id) - users = get_managed_users_with_grants(GetManagedUsersArgs(user_id=user_id)) + args = managed_users_route_parser.parse_args() + is_approved = args.get("is_approved", None) + is_revoked = args.get("is_revoked", False) + users = get_managed_users_with_grants( + GetManagedUsersArgs( + user_id=user_id, is_approved=is_approved, is_revoked=is_revoked + ) + ) users = list(map(format_managed_user, users)) return success_response(users) @@ -2128,14 +2152,22 @@ class Managers(Resource): 500: "Server error", }, ) - @auth_middleware(require_auth=True) + @full_ns.expect(managed_users_route_parser) + @auth_middleware(managed_users_route_parser, require_auth=True) @full_ns.marshal_with(managers_response) def get(self, id, authed_user_id): user_id = decode_with_abort(id, full_ns) check_authorized(user_id, authed_user_id) - args = GetUserManagersArgs(user_id=user_id) - managers = get_user_managers_with_grants(args) + args = managed_users_route_parser.parse_args() + logger.info(f"DEBUG::args: {args}") + is_approved = args.get("is_approved", None) + is_revoked = args.get("is_revoked", False) + managers = get_user_managers_with_grants( + GetUserManagersArgs( + user_id=user_id, is_approved=is_approved, is_revoked=is_revoked + ) + ) managers = list(map(format_user_manager, managers)) return success_response(managers) diff --git a/packages/discovery-provider/src/tasks/entity_manager/entities/grant.py b/packages/discovery-provider/src/tasks/entity_manager/entities/grant.py index 82a87e67729..199e521610c 100644 --- a/packages/discovery-provider/src/tasks/entity_manager/entities/grant.py +++ b/packages/discovery-provider/src/tasks/entity_manager/entities/grant.py @@ -211,6 +211,7 @@ def create_grant(params: ManageEntityParameters): ), # cast to assert non null (since we validated above) is_current=True, is_approved=None if grantee_type == "user" else True, + is_revoked=False, txhash=params.txhash, blockhash=params.event_blockhash, blocknumber=params.block_number, @@ -295,6 +296,7 @@ def reject_grant(params: ManageEntityParameters): ) rejected_grant.is_approved = False + rejected_grant.is_revoked = True validate_grant_record(rejected_grant) params.add_record(grant_key, rejected_grant) diff --git a/packages/libs/src/sdk/api/generated/default/apis/PlaylistsApi.ts b/packages/libs/src/sdk/api/generated/default/apis/PlaylistsApi.ts index 7168f730601..169e8720b18 100644 --- a/packages/libs/src/sdk/api/generated/default/apis/PlaylistsApi.ts +++ b/packages/libs/src/sdk/api/generated/default/apis/PlaylistsApi.ts @@ -65,7 +65,12 @@ export interface GetTrendingPlaylistsRequest { } export interface SearchPlaylistsRequest { - query: string; + query?: string; + genre?: Array; + sortMethod?: SearchPlaylistsSortMethodEnum; + mood?: Array; + includePurchaseable?: string; + hasDownloads?: string; } /** @@ -284,16 +289,32 @@ export class PlaylistsApi extends runtime.BaseAPI { * Search for a playlist */ async searchPlaylistsRaw(params: SearchPlaylistsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { - if (params.query === null || params.query === undefined) { - throw new runtime.RequiredError('query','Required parameter params.query was null or undefined when calling searchPlaylists.'); - } - const queryParameters: any = {}; if (params.query !== undefined) { queryParameters['query'] = params.query; } + if (params.genre) { + queryParameters['genre'] = params.genre; + } + + if (params.sortMethod !== undefined) { + queryParameters['sort_method'] = params.sortMethod; + } + + if (params.mood) { + queryParameters['mood'] = params.mood; + } + + if (params.includePurchaseable !== undefined) { + queryParameters['includePurchaseable'] = params.includePurchaseable; + } + + if (params.hasDownloads !== undefined) { + queryParameters['has_downloads'] = params.hasDownloads; + } + const headerParameters: runtime.HTTPHeaders = {}; const response = await this.request({ @@ -309,7 +330,7 @@ export class PlaylistsApi extends runtime.BaseAPI { /** * Search for a playlist */ - async searchPlaylists(params: SearchPlaylistsRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + async searchPlaylists(params: SearchPlaylistsRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { const response = await this.searchPlaylistsRaw(params, initOverrides); return await response.value(); } @@ -326,3 +347,12 @@ export const GetTrendingPlaylistsTimeEnum = { AllTime: 'allTime' } as const; export type GetTrendingPlaylistsTimeEnum = typeof GetTrendingPlaylistsTimeEnum[keyof typeof GetTrendingPlaylistsTimeEnum]; +/** + * @export + */ +export const SearchPlaylistsSortMethodEnum = { + Relevant: 'relevant', + Popular: 'popular', + Recent: 'recent' +} as const; +export type SearchPlaylistsSortMethodEnum = typeof SearchPlaylistsSortMethodEnum[keyof typeof SearchPlaylistsSortMethodEnum]; diff --git a/packages/libs/src/sdk/api/generated/default/apis/TracksApi.ts b/packages/libs/src/sdk/api/generated/default/apis/TracksApi.ts index 2de61c3ff59..8f011c4e86b 100644 --- a/packages/libs/src/sdk/api/generated/default/apis/TracksApi.ts +++ b/packages/libs/src/sdk/api/generated/default/apis/TracksApi.ts @@ -85,8 +85,17 @@ export interface InspectTrackRequest { } export interface SearchTracksRequest { - query: string; + query?: string; + genre?: Array; + sortMethod?: SearchTracksSortMethodEnum; + mood?: Array; onlyDownloadable?: string; + includePurchaseable?: string; + isPurchaseable?: string; + hasDownloads?: string; + key?: Array; + bpmMin?: string; + bpmMax?: string; } export interface StreamTrackRequest { @@ -419,20 +428,52 @@ export class TracksApi extends runtime.BaseAPI { * Search for a track or tracks */ async searchTracksRaw(params: SearchTracksRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { - if (params.query === null || params.query === undefined) { - throw new runtime.RequiredError('query','Required parameter params.query was null or undefined when calling searchTracks.'); - } - const queryParameters: any = {}; if (params.query !== undefined) { queryParameters['query'] = params.query; } + if (params.genre) { + queryParameters['genre'] = params.genre; + } + + if (params.sortMethod !== undefined) { + queryParameters['sort_method'] = params.sortMethod; + } + + if (params.mood) { + queryParameters['mood'] = params.mood; + } + if (params.onlyDownloadable !== undefined) { queryParameters['only_downloadable'] = params.onlyDownloadable; } + if (params.includePurchaseable !== undefined) { + queryParameters['includePurchaseable'] = params.includePurchaseable; + } + + if (params.isPurchaseable !== undefined) { + queryParameters['is_purchaseable'] = params.isPurchaseable; + } + + if (params.hasDownloads !== undefined) { + queryParameters['has_downloads'] = params.hasDownloads; + } + + if (params.key) { + queryParameters['key'] = params.key; + } + + if (params.bpmMin !== undefined) { + queryParameters['bpm_min'] = params.bpmMin; + } + + if (params.bpmMax !== undefined) { + queryParameters['bpm_max'] = params.bpmMax; + } + const headerParameters: runtime.HTTPHeaders = {}; const response = await this.request({ @@ -448,7 +489,7 @@ export class TracksApi extends runtime.BaseAPI { /** * Search for a track or tracks */ - async searchTracks(params: SearchTracksRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + async searchTracks(params: SearchTracksRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { const response = await this.searchTracksRaw(params, initOverrides); return await response.value(); } @@ -533,3 +574,12 @@ export const GetTrendingTracksTimeEnum = { AllTime: 'allTime' } as const; export type GetTrendingTracksTimeEnum = typeof GetTrendingTracksTimeEnum[keyof typeof GetTrendingTracksTimeEnum]; +/** + * @export + */ +export const SearchTracksSortMethodEnum = { + Relevant: 'relevant', + Popular: 'popular', + Recent: 'recent' +} as const; +export type SearchTracksSortMethodEnum = typeof SearchTracksSortMethodEnum[keyof typeof SearchTracksSortMethodEnum]; diff --git a/packages/libs/src/sdk/api/generated/default/apis/UsersApi.ts b/packages/libs/src/sdk/api/generated/default/apis/UsersApi.ts index 24030a91505..9918b3f579e 100644 --- a/packages/libs/src/sdk/api/generated/default/apis/UsersApi.ts +++ b/packages/libs/src/sdk/api/generated/default/apis/UsersApi.ts @@ -211,7 +211,10 @@ export interface GetUserIDFromWalletRequest { } export interface SearchUsersRequest { - query: string; + query?: string; + genre?: Array; + sortMethod?: SearchUsersSortMethodEnum; + isVerified?: string; } export interface VerifyIDTokenRequest { @@ -1090,16 +1093,24 @@ export class UsersApi extends runtime.BaseAPI { * Search for users that match the given query */ async searchUsersRaw(params: SearchUsersRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { - if (params.query === null || params.query === undefined) { - throw new runtime.RequiredError('query','Required parameter params.query was null or undefined when calling searchUsers.'); - } - const queryParameters: any = {}; if (params.query !== undefined) { queryParameters['query'] = params.query; } + if (params.genre) { + queryParameters['genre'] = params.genre; + } + + if (params.sortMethod !== undefined) { + queryParameters['sort_method'] = params.sortMethod; + } + + if (params.isVerified !== undefined) { + queryParameters['is_verified'] = params.isVerified; + } + const headerParameters: runtime.HTTPHeaders = {}; const response = await this.request({ @@ -1115,7 +1126,7 @@ export class UsersApi extends runtime.BaseAPI { /** * Search for users that match the given query */ - async searchUsers(params: SearchUsersRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + async searchUsers(params: SearchUsersRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { const response = await this.searchUsersRaw(params, initOverrides); return await response.value(); } @@ -1237,3 +1248,12 @@ export const GetTracksByUserFilterTracksEnum = { Unlisted: 'unlisted' } as const; export type GetTracksByUserFilterTracksEnum = typeof GetTracksByUserFilterTracksEnum[keyof typeof GetTracksByUserFilterTracksEnum]; +/** + * @export + */ +export const SearchUsersSortMethodEnum = { + Relevant: 'relevant', + Popular: 'popular', + Recent: 'recent' +} as const; +export type SearchUsersSortMethodEnum = typeof SearchUsersSortMethodEnum[keyof typeof SearchUsersSortMethodEnum]; diff --git a/packages/libs/src/sdk/api/generated/default/models/Playlist.ts b/packages/libs/src/sdk/api/generated/default/models/Playlist.ts index 84853e98f7e..7f1e0e6dca4 100644 --- a/packages/libs/src/sdk/api/generated/default/models/Playlist.ts +++ b/packages/libs/src/sdk/api/generated/default/models/Playlist.ts @@ -135,6 +135,12 @@ export interface Playlist { * @memberof Playlist */ upc?: string; + /** + * + * @type {number} + * @memberof Playlist + */ + trackCount: number; } /** @@ -151,6 +157,7 @@ export function instanceOfPlaylist(value: object): value is Playlist { isInstance = isInstance && "favoriteCount" in value && value["favoriteCount"] !== undefined; isInstance = isInstance && "totalPlayCount" in value && value["totalPlayCount"] !== undefined; isInstance = isInstance && "user" in value && value["user"] !== undefined; + isInstance = isInstance && "trackCount" in value && value["trackCount"] !== undefined; return isInstance; } @@ -180,6 +187,7 @@ export function PlaylistFromJSONTyped(json: any, ignoreDiscriminator: boolean): 'ddexApp': !exists(json, 'ddex_app') ? undefined : json['ddex_app'], 'access': !exists(json, 'access') ? undefined : AccessFromJSON(json['access']), 'upc': !exists(json, 'upc') ? undefined : json['upc'], + 'trackCount': json['track_count'], }; } @@ -207,6 +215,7 @@ export function PlaylistToJSON(value?: Playlist | null): any { 'ddex_app': value.ddexApp, 'access': AccessToJSON(value.access), 'upc': value.upc, + 'track_count': value.trackCount, }; } diff --git a/packages/libs/src/sdk/api/generated/full/apis/SearchApi.ts b/packages/libs/src/sdk/api/generated/full/apis/SearchApi.ts index eb80c7f6239..6af37435bb8 100644 --- a/packages/libs/src/sdk/api/generated/full/apis/SearchApi.ts +++ b/packages/libs/src/sdk/api/generated/full/apis/SearchApi.ts @@ -27,23 +27,39 @@ import { } from '../models'; export interface SearchRequest { - query: string; offset?: number; limit?: number; userId?: string; + query?: string; kind?: SearchKindEnum; includePurchaseable?: string; genre?: Array; + mood?: Array; + isVerified?: string; + hasDownloads?: string; + isPurchaseable?: string; + key?: Array; + bpmMin?: string; + bpmMax?: string; + sortMethod?: SearchSortMethodEnum; } export interface SearchAutocompleteRequest { - query: string; offset?: number; limit?: number; userId?: string; + query?: string; kind?: SearchAutocompleteKindEnum; includePurchaseable?: string; genre?: Array; + mood?: Array; + isVerified?: string; + hasDownloads?: string; + isPurchaseable?: string; + key?: Array; + bpmMin?: string; + bpmMax?: string; + sortMethod?: SearchAutocompleteSortMethodEnum; } /** @@ -56,10 +72,6 @@ export class SearchApi extends runtime.BaseAPI { * Get Users/Tracks/Playlists/Albums that best match the search query */ async searchRaw(params: SearchRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { - if (params.query === null || params.query === undefined) { - throw new runtime.RequiredError('query','Required parameter params.query was null or undefined when calling search.'); - } - const queryParameters: any = {}; if (params.offset !== undefined) { @@ -90,6 +102,38 @@ export class SearchApi extends runtime.BaseAPI { queryParameters['genre'] = params.genre; } + if (params.mood) { + queryParameters['mood'] = params.mood; + } + + if (params.isVerified !== undefined) { + queryParameters['is_verified'] = params.isVerified; + } + + if (params.hasDownloads !== undefined) { + queryParameters['has_downloads'] = params.hasDownloads; + } + + if (params.isPurchaseable !== undefined) { + queryParameters['is_purchaseable'] = params.isPurchaseable; + } + + if (params.key) { + queryParameters['key'] = params.key; + } + + if (params.bpmMin !== undefined) { + queryParameters['bpm_min'] = params.bpmMin; + } + + if (params.bpmMax !== undefined) { + queryParameters['bpm_max'] = params.bpmMax; + } + + if (params.sortMethod !== undefined) { + queryParameters['sort_method'] = params.sortMethod; + } + const headerParameters: runtime.HTTPHeaders = {}; const response = await this.request({ @@ -105,7 +149,7 @@ export class SearchApi extends runtime.BaseAPI { /** * Get Users/Tracks/Playlists/Albums that best match the search query */ - async search(params: SearchRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + async search(params: SearchRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { const response = await this.searchRaw(params, initOverrides); return await response.value(); } @@ -116,10 +160,6 @@ export class SearchApi extends runtime.BaseAPI { * Get Users/Tracks/Playlists/Albums that best match the search query */ async searchAutocompleteRaw(params: SearchAutocompleteRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise> { - if (params.query === null || params.query === undefined) { - throw new runtime.RequiredError('query','Required parameter params.query was null or undefined when calling searchAutocomplete.'); - } - const queryParameters: any = {}; if (params.offset !== undefined) { @@ -150,6 +190,38 @@ export class SearchApi extends runtime.BaseAPI { queryParameters['genre'] = params.genre; } + if (params.mood) { + queryParameters['mood'] = params.mood; + } + + if (params.isVerified !== undefined) { + queryParameters['is_verified'] = params.isVerified; + } + + if (params.hasDownloads !== undefined) { + queryParameters['has_downloads'] = params.hasDownloads; + } + + if (params.isPurchaseable !== undefined) { + queryParameters['is_purchaseable'] = params.isPurchaseable; + } + + if (params.key) { + queryParameters['key'] = params.key; + } + + if (params.bpmMin !== undefined) { + queryParameters['bpm_min'] = params.bpmMin; + } + + if (params.bpmMax !== undefined) { + queryParameters['bpm_max'] = params.bpmMax; + } + + if (params.sortMethod !== undefined) { + queryParameters['sort_method'] = params.sortMethod; + } + const headerParameters: runtime.HTTPHeaders = {}; const response = await this.request({ @@ -166,7 +238,7 @@ export class SearchApi extends runtime.BaseAPI { * Same as search but optimized for quicker response at the cost of some entity information. * Get Users/Tracks/Playlists/Albums that best match the search query */ - async searchAutocomplete(params: SearchAutocompleteRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { + async searchAutocomplete(params: SearchAutocompleteRequest = {}, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise { const response = await this.searchAutocompleteRaw(params, initOverrides); return await response.value(); } @@ -184,6 +256,15 @@ export const SearchKindEnum = { Albums: 'albums' } as const; export type SearchKindEnum = typeof SearchKindEnum[keyof typeof SearchKindEnum]; +/** + * @export + */ +export const SearchSortMethodEnum = { + Relevant: 'relevant', + Popular: 'popular', + Recent: 'recent' +} as const; +export type SearchSortMethodEnum = typeof SearchSortMethodEnum[keyof typeof SearchSortMethodEnum]; /** * @export */ @@ -195,3 +276,12 @@ export const SearchAutocompleteKindEnum = { Albums: 'albums' } as const; export type SearchAutocompleteKindEnum = typeof SearchAutocompleteKindEnum[keyof typeof SearchAutocompleteKindEnum]; +/** + * @export + */ +export const SearchAutocompleteSortMethodEnum = { + Relevant: 'relevant', + Popular: 'popular', + Recent: 'recent' +} as const; +export type SearchAutocompleteSortMethodEnum = typeof SearchAutocompleteSortMethodEnum[keyof typeof SearchAutocompleteSortMethodEnum]; diff --git a/packages/libs/src/sdk/api/generated/full/apis/UsersApi.ts b/packages/libs/src/sdk/api/generated/full/apis/UsersApi.ts index 086f3de1ab5..8ebe78d3f0c 100644 --- a/packages/libs/src/sdk/api/generated/full/apis/UsersApi.ts +++ b/packages/libs/src/sdk/api/generated/full/apis/UsersApi.ts @@ -158,10 +158,18 @@ export interface GetFollowingRequest { export interface GetManagedUsersRequest { id: string; + isApproved?: boolean; + isRevoked?: boolean; + encodedDataMessage?: string; + encodedDataSignature?: string; } export interface GetManagersRequest { id: string; + isApproved?: boolean; + isRevoked?: boolean; + encodedDataMessage?: string; + encodedDataSignature?: string; } export interface GetPurchasesRequest { @@ -803,8 +811,24 @@ export class UsersApi extends runtime.BaseAPI { const queryParameters: any = {}; + if (params.isApproved !== undefined) { + queryParameters['is_approved'] = params.isApproved; + } + + if (params.isRevoked !== undefined) { + queryParameters['is_revoked'] = params.isRevoked; + } + const headerParameters: runtime.HTTPHeaders = {}; + if (params.encodedDataMessage !== undefined && params.encodedDataMessage !== null) { + headerParameters['Encoded-Data-Message'] = String(params.encodedDataMessage); + } + + if (params.encodedDataSignature !== undefined && params.encodedDataSignature !== null) { + headerParameters['Encoded-Data-Signature'] = String(params.encodedDataSignature); + } + const response = await this.request({ path: `/users/{id}/managed_users`.replace(`{${"id"}}`, encodeURIComponent(String(params.id))), method: 'GET', @@ -834,8 +858,24 @@ export class UsersApi extends runtime.BaseAPI { const queryParameters: any = {}; + if (params.isApproved !== undefined) { + queryParameters['is_approved'] = params.isApproved; + } + + if (params.isRevoked !== undefined) { + queryParameters['is_revoked'] = params.isRevoked; + } + const headerParameters: runtime.HTTPHeaders = {}; + if (params.encodedDataMessage !== undefined && params.encodedDataMessage !== null) { + headerParameters['Encoded-Data-Message'] = String(params.encodedDataMessage); + } + + if (params.encodedDataSignature !== undefined && params.encodedDataSignature !== null) { + headerParameters['Encoded-Data-Signature'] = String(params.encodedDataSignature); + } + const response = await this.request({ path: `/users/{id}/managers`.replace(`{${"id"}}`, encodeURIComponent(String(params.id))), method: 'GET', diff --git a/packages/libs/src/sdk/api/generated/full/models/PlaylistFull.ts b/packages/libs/src/sdk/api/generated/full/models/PlaylistFull.ts index 63ddbfacc6b..fdca4da0604 100644 --- a/packages/libs/src/sdk/api/generated/full/models/PlaylistFull.ts +++ b/packages/libs/src/sdk/api/generated/full/models/PlaylistFull.ts @@ -159,6 +159,12 @@ export interface PlaylistFull { * @memberof PlaylistFull */ upc?: string; + /** + * + * @type {number} + * @memberof PlaylistFull + */ + trackCount: number; /** * * @type {number} @@ -249,12 +255,6 @@ export interface PlaylistFull { * @memberof PlaylistFull */ coverArtCids?: PlaylistArtwork; - /** - * - * @type {number} - * @memberof PlaylistFull - */ - trackCount: number; /** * * @type {boolean} @@ -295,6 +295,7 @@ export function instanceOfPlaylistFull(value: object): value is PlaylistFull { isInstance = isInstance && "favoriteCount" in value && value["favoriteCount"] !== undefined; isInstance = isInstance && "totalPlayCount" in value && value["totalPlayCount"] !== undefined; isInstance = isInstance && "user" in value && value["user"] !== undefined; + isInstance = isInstance && "trackCount" in value && value["trackCount"] !== undefined; isInstance = isInstance && "blocknumber" in value && value["blocknumber"] !== undefined; isInstance = isInstance && "followeeReposts" in value && value["followeeReposts"] !== undefined; isInstance = isInstance && "followeeFavorites" in value && value["followeeFavorites"] !== undefined; @@ -305,7 +306,6 @@ export function instanceOfPlaylistFull(value: object): value is PlaylistFull { isInstance = isInstance && "addedTimestamps" in value && value["addedTimestamps"] !== undefined; isInstance = isInstance && "userId" in value && value["userId"] !== undefined; isInstance = isInstance && "tracks" in value && value["tracks"] !== undefined; - isInstance = isInstance && "trackCount" in value && value["trackCount"] !== undefined; isInstance = isInstance && "isStreamGated" in value && value["isStreamGated"] !== undefined; return isInstance; @@ -336,6 +336,7 @@ export function PlaylistFullFromJSONTyped(json: any, ignoreDiscriminator: boolea 'ddexApp': !exists(json, 'ddex_app') ? undefined : json['ddex_app'], 'access': !exists(json, 'access') ? undefined : AccessFromJSON(json['access']), 'upc': !exists(json, 'upc') ? undefined : json['upc'], + 'trackCount': json['track_count'], 'blocknumber': json['blocknumber'], 'createdAt': !exists(json, 'created_at') ? undefined : json['created_at'], 'followeeReposts': ((json['followee_reposts'] as Array).map(RepostFromJSON)), @@ -351,7 +352,6 @@ export function PlaylistFullFromJSONTyped(json: any, ignoreDiscriminator: boolea 'coverArt': !exists(json, 'cover_art') ? undefined : json['cover_art'], 'coverArtSizes': !exists(json, 'cover_art_sizes') ? undefined : json['cover_art_sizes'], 'coverArtCids': !exists(json, 'cover_art_cids') ? undefined : PlaylistArtworkFromJSON(json['cover_art_cids']), - 'trackCount': json['track_count'], 'isStreamGated': json['is_stream_gated'], 'streamConditions': !exists(json, 'stream_conditions') ? undefined : AccessGateFromJSON(json['stream_conditions']), 'isScheduledRelease': !exists(json, 'is_scheduled_release') ? undefined : json['is_scheduled_release'], @@ -383,6 +383,7 @@ export function PlaylistFullToJSON(value?: PlaylistFull | null): any { 'ddex_app': value.ddexApp, 'access': AccessToJSON(value.access), 'upc': value.upc, + 'track_count': value.trackCount, 'blocknumber': value.blocknumber, 'created_at': value.createdAt, 'followee_reposts': ((value.followeeReposts as Array).map(RepostToJSON)), @@ -398,7 +399,6 @@ export function PlaylistFullToJSON(value?: PlaylistFull | null): any { 'cover_art': value.coverArt, 'cover_art_sizes': value.coverArtSizes, 'cover_art_cids': PlaylistArtworkToJSON(value.coverArtCids), - 'track_count': value.trackCount, 'is_stream_gated': value.isStreamGated, 'stream_conditions': AccessGateToJSON(value.streamConditions), 'is_scheduled_release': value.isScheduledRelease, diff --git a/packages/libs/src/sdk/api/generated/full/models/PlaylistFullWithoutTracks.ts b/packages/libs/src/sdk/api/generated/full/models/PlaylistFullWithoutTracks.ts index 090786d6502..cc154cd750c 100644 --- a/packages/libs/src/sdk/api/generated/full/models/PlaylistFullWithoutTracks.ts +++ b/packages/libs/src/sdk/api/generated/full/models/PlaylistFullWithoutTracks.ts @@ -159,6 +159,12 @@ export interface PlaylistFullWithoutTracks { * @memberof PlaylistFullWithoutTracks */ upc?: string; + /** + * + * @type {number} + * @memberof PlaylistFullWithoutTracks + */ + trackCount: number; /** * * @type {number} @@ -249,12 +255,6 @@ export interface PlaylistFullWithoutTracks { * @memberof PlaylistFullWithoutTracks */ coverArtCids?: PlaylistArtwork; - /** - * - * @type {number} - * @memberof PlaylistFullWithoutTracks - */ - trackCount: number; /** * * @type {boolean} @@ -295,6 +295,7 @@ export function instanceOfPlaylistFullWithoutTracks(value: object): value is Pla isInstance = isInstance && "favoriteCount" in value && value["favoriteCount"] !== undefined; isInstance = isInstance && "totalPlayCount" in value && value["totalPlayCount"] !== undefined; isInstance = isInstance && "user" in value && value["user"] !== undefined; + isInstance = isInstance && "trackCount" in value && value["trackCount"] !== undefined; isInstance = isInstance && "blocknumber" in value && value["blocknumber"] !== undefined; isInstance = isInstance && "followeeReposts" in value && value["followeeReposts"] !== undefined; isInstance = isInstance && "followeeFavorites" in value && value["followeeFavorites"] !== undefined; @@ -304,7 +305,6 @@ export function instanceOfPlaylistFullWithoutTracks(value: object): value is Pla isInstance = isInstance && "isPrivate" in value && value["isPrivate"] !== undefined; isInstance = isInstance && "addedTimestamps" in value && value["addedTimestamps"] !== undefined; isInstance = isInstance && "userId" in value && value["userId"] !== undefined; - isInstance = isInstance && "trackCount" in value && value["trackCount"] !== undefined; isInstance = isInstance && "isStreamGated" in value && value["isStreamGated"] !== undefined; return isInstance; @@ -335,6 +335,7 @@ export function PlaylistFullWithoutTracksFromJSONTyped(json: any, ignoreDiscrimi 'ddexApp': !exists(json, 'ddex_app') ? undefined : json['ddex_app'], 'access': !exists(json, 'access') ? undefined : AccessFromJSON(json['access']), 'upc': !exists(json, 'upc') ? undefined : json['upc'], + 'trackCount': json['track_count'], 'blocknumber': json['blocknumber'], 'createdAt': !exists(json, 'created_at') ? undefined : json['created_at'], 'followeeReposts': ((json['followee_reposts'] as Array).map(RepostFromJSON)), @@ -350,7 +351,6 @@ export function PlaylistFullWithoutTracksFromJSONTyped(json: any, ignoreDiscrimi 'coverArt': !exists(json, 'cover_art') ? undefined : json['cover_art'], 'coverArtSizes': !exists(json, 'cover_art_sizes') ? undefined : json['cover_art_sizes'], 'coverArtCids': !exists(json, 'cover_art_cids') ? undefined : PlaylistArtworkFromJSON(json['cover_art_cids']), - 'trackCount': json['track_count'], 'isStreamGated': json['is_stream_gated'], 'streamConditions': !exists(json, 'stream_conditions') ? undefined : AccessGateFromJSON(json['stream_conditions']), 'isScheduledRelease': !exists(json, 'is_scheduled_release') ? undefined : json['is_scheduled_release'], @@ -382,6 +382,7 @@ export function PlaylistFullWithoutTracksToJSON(value?: PlaylistFullWithoutTrack 'ddex_app': value.ddexApp, 'access': AccessToJSON(value.access), 'upc': value.upc, + 'track_count': value.trackCount, 'blocknumber': value.blocknumber, 'created_at': value.createdAt, 'followee_reposts': ((value.followeeReposts as Array).map(RepostToJSON)), @@ -397,7 +398,6 @@ export function PlaylistFullWithoutTracksToJSON(value?: PlaylistFullWithoutTrack 'cover_art': value.coverArt, 'cover_art_sizes': value.coverArtSizes, 'cover_art_cids': PlaylistArtworkToJSON(value.coverArtCids), - 'track_count': value.trackCount, 'is_stream_gated': value.isStreamGated, 'stream_conditions': AccessGateToJSON(value.streamConditions), 'is_scheduled_release': value.isScheduledRelease, diff --git a/packages/libs/src/sdk/api/grants/GrantsApi.ts b/packages/libs/src/sdk/api/grants/GrantsApi.ts index eb1fc08075e..ad543f78055 100644 --- a/packages/libs/src/sdk/api/grants/GrantsApi.ts +++ b/packages/libs/src/sdk/api/grants/GrantsApi.ts @@ -9,8 +9,6 @@ import { ApproveGrantRequest, AddManagerRequest, AddManagerSchema, - RejectGrantRequest, - RejectGrantSchema, CreateGrantRequest, CreateGrantSchema, RevokeGrantRequest, @@ -163,25 +161,4 @@ export class GrantsApi { auth: this.auth }) } - - /** - * Reject manager request - */ - async rejectGrant(params: RejectGrantRequest) { - const { userId, grantorUserId } = await parseParams( - 'rejectGrant', - RejectGrantSchema - )(params) - - return await this.entityManager.manageEntity({ - userId, - entityType: EntityType.GRANT, - entityId: 0, - action: Action.REJECT, - metadata: JSON.stringify({ - grantor_user_id: grantorUserId - }), - auth: this.auth - }) - } } diff --git a/packages/libs/src/sdk/api/grants/types.ts b/packages/libs/src/sdk/api/grants/types.ts index 142ba8d2cd2..415170edb31 100644 --- a/packages/libs/src/sdk/api/grants/types.ts +++ b/packages/libs/src/sdk/api/grants/types.ts @@ -41,10 +41,3 @@ export const ApproveGrantSchema = z.object({ }) export type ApproveGrantRequest = z.input - -export const RejectGrantSchema = z.object({ - userId: HashId, - grantorUserId: HashId -}) - -export type RejectGrantRequest = z.input