Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/great-lemons-draw.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@audius/sdk": minor
---

Add register dev app key endpoint
55 changes: 13 additions & 42 deletions packages/sdk/src/sdk/api/developer-apps/DeveloperAppsApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,22 @@ export class DeveloperAppsApi extends GeneratedDeveloperAppsApi {
? (privateKey as string).slice(2).toLowerCase()
: (privateKey as string).toLowerCase()

// Create api_access_key for the relay-created app (user from auth headers; indexer may lag, so retry)
const bearerToken = await this.createAccessKeyWithRetry({
await this.registerDeveloperAppAPIKey({
address,
userId: userId.toString()
userId: userId.toString(),
metadata: { apiSecret }
})

const path = `/developer-apps/${encodeURIComponent(address)}/access-keys`
const res = await this.request({
path,
method: 'POST',
headers: {},
query: { user_id: userId.toString() }
})
const json = (await res.json()) as { api_access_key: string }
const bearerToken = json.api_access_key ?? ''

return {
...response,
apiKey,
Expand All @@ -111,45 +121,6 @@ export class DeveloperAppsApi extends GeneratedDeveloperAppsApi {
}
}

/**
* Call POST /developer-apps/{address}/access-keys to create the first api_access_key
* for a relay-created app. User is identified from request auth headers. Retries on 404
* while indexer processes the CreateDeveloperApp tx.
*/
private async createAccessKeyWithRetry(params: {
address: string
userId: string
}): Promise<string> {
const { address, userId } = params
const maxAttempts = 5
const delayMs = 2000

for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
const path = `/developer-apps/${encodeURIComponent(address)}/access-keys`
const res = await this.request({
path,
method: 'POST',
headers: {},
query: { user_id: userId }
})
const json = (await res.json()) as { api_access_key: string }
return json.api_access_key ?? ''
} catch (e: unknown) {
const status =
e && typeof e === 'object' && 'response' in e
? (e as { response?: { status?: number } }).response?.status
: undefined
if (status === 404 && attempt < maxAttempts) {
await new Promise((resolve) => setTimeout(resolve, delayMs))
continue
}
throw e
}
}
return ''
}

/**
* Get developer apps with api_access_keys (bearer tokens).
* Uses include=metrics to fetch bearer tokens from the API.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,8 @@ models/ReceiveTipNotification.ts
models/ReceiveTipNotificationAction.ts
models/ReceiveTipNotificationActionData.ts
models/RedeemAmountResponse.ts
models/RegisterApiKeyRequestBody.ts
models/RegisterApiKeyResponse.ts
models/Related.ts
models/RelatedArtistResponse.ts
models/Remix.ts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import type {
DeactivateAccessKeyResponse,
DeveloperAppResponse,
DeveloperAppsResponse,
RegisterApiKeyRequestBody,
RegisterApiKeyResponse,
UpdateDeveloperAppRequestBody,
WriteResponse,
} from '../models';
Expand All @@ -41,6 +43,10 @@ import {
DeveloperAppResponseToJSON,
DeveloperAppsResponseFromJSON,
DeveloperAppsResponseToJSON,
RegisterApiKeyRequestBodyFromJSON,
RegisterApiKeyRequestBodyToJSON,
RegisterApiKeyResponseFromJSON,
RegisterApiKeyResponseToJSON,
UpdateDeveloperAppRequestBodyFromJSON,
UpdateDeveloperAppRequestBodyToJSON,
WriteResponseFromJSON,
Expand Down Expand Up @@ -77,6 +83,12 @@ export interface GetDeveloperAppsRequest {
include?: GetDeveloperAppsIncludeEnum;
}

export interface RegisterDeveloperAppAPIKeyRequest {
userId: string;
address: string;
metadata: RegisterApiKeyRequestBody;
}

export interface UpdateDeveloperAppRequest {
userId: string;
address: string;
Expand Down Expand Up @@ -320,6 +332,63 @@ export class DeveloperAppsApi extends runtime.BaseAPI {
return await response.value();
}

/**
* @hidden
* Register api_key and api_secret in api_keys table for developer apps created via entity manager transactions. Use when the client sends raw ManageEntity tx instead of POST /developer-apps. Inserts with rps=10, rpm=500000. Requires the app to exist in developer_apps and belong to the authenticated user.
*/
async registerDeveloperAppAPIKeyRaw(params: RegisterDeveloperAppAPIKeyRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<RegisterApiKeyResponse>> {
if (params.userId === null || params.userId === undefined) {
throw new runtime.RequiredError('userId','Required parameter params.userId was null or undefined when calling registerDeveloperAppAPIKey.');
}

if (params.address === null || params.address === undefined) {
throw new runtime.RequiredError('address','Required parameter params.address was null or undefined when calling registerDeveloperAppAPIKey.');
}

if (params.metadata === null || params.metadata === undefined) {
throw new runtime.RequiredError('metadata','Required parameter params.metadata was null or undefined when calling registerDeveloperAppAPIKey.');
}

const queryParameters: any = {};

if (params.userId !== undefined) {
queryParameters['user_id'] = params.userId;
}

const headerParameters: runtime.HTTPHeaders = {};

headerParameters['Content-Type'] = 'application/json';

if (this.configuration && (this.configuration.username !== undefined || this.configuration.password !== undefined)) {
headerParameters["Authorization"] = "Basic " + btoa(this.configuration.username + ":" + this.configuration.password);
}
if (this.configuration && this.configuration.accessToken) {
const token = this.configuration.accessToken;
const tokenString = await token("BearerAuth", []);

if (tokenString) {
headerParameters["Authorization"] = `Bearer ${tokenString}`;
}
}
const response = await this.request({
path: `/developer-apps/{address}/register-api-key`.replace(`{${"address"}}`, encodeURIComponent(String(params.address))),
method: 'POST',
headers: headerParameters,
query: queryParameters,
body: RegisterApiKeyRequestBodyToJSON(params.metadata),
}, initOverrides);

return new runtime.JSONApiResponse(response, (jsonValue) => RegisterApiKeyResponseFromJSON(jsonValue));
}

/**
* Register api_key and api_secret in api_keys table for developer apps created via entity manager transactions. Use when the client sends raw ManageEntity tx instead of POST /developer-apps. Inserts with rps=10, rpm=500000. Requires the app to exist in developer_apps and belong to the authenticated user.
*/
async registerDeveloperAppAPIKey(params: RegisterDeveloperAppAPIKeyRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<RegisterApiKeyResponse> {
const response = await this.registerDeveloperAppAPIKeyRaw(params, initOverrides);
return await response.value();
}

/**
* @hidden
* Updates a developer app. Indexer validates grants.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
/**
* API
* Audius V1 API
*
* The version of the OpenAPI document: 1.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/

import { exists, mapValues } from '../runtime';
/**
*
* @export
* @interface RegisterApiKeyRequestBody
*/
export interface RegisterApiKeyRequestBody {
/**
* The API secret (private key hex) for the developer app
* @type {string}
* @memberof RegisterApiKeyRequestBody
*/
apiSecret: string;
}

/**
* Check if a given object implements the RegisterApiKeyRequestBody interface.
*/
export function instanceOfRegisterApiKeyRequestBody(value: object): value is RegisterApiKeyRequestBody {
let isInstance = true;
isInstance = isInstance && "apiSecret" in value && value["apiSecret"] !== undefined;

return isInstance;
}

export function RegisterApiKeyRequestBodyFromJSON(json: any): RegisterApiKeyRequestBody {
return RegisterApiKeyRequestBodyFromJSONTyped(json, false);
}

export function RegisterApiKeyRequestBodyFromJSONTyped(json: any, ignoreDiscriminator: boolean): RegisterApiKeyRequestBody {
if ((json === undefined) || (json === null)) {
return json;
}
return {

'apiSecret': json['api_secret'],
};
}

export function RegisterApiKeyRequestBodyToJSON(value?: RegisterApiKeyRequestBody | null): any {
if (value === undefined) {
return undefined;
}
if (value === null) {
return null;
}
return {

'api_secret': value.apiSecret,
};
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
/**
* API
* Audius V1 API
*
* The version of the OpenAPI document: 1.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/

import { exists, mapValues } from '../runtime';
/**
*
* @export
* @interface RegisterApiKeyResponse
*/
export interface RegisterApiKeyResponse {
/**
* Whether the registration was successful
* @type {boolean}
* @memberof RegisterApiKeyResponse
*/
success?: boolean;
}

/**
* Check if a given object implements the RegisterApiKeyResponse interface.
*/
export function instanceOfRegisterApiKeyResponse(value: object): value is RegisterApiKeyResponse {
let isInstance = true;

return isInstance;
}

export function RegisterApiKeyResponseFromJSON(json: any): RegisterApiKeyResponse {
return RegisterApiKeyResponseFromJSONTyped(json, false);
}

export function RegisterApiKeyResponseFromJSONTyped(json: any, ignoreDiscriminator: boolean): RegisterApiKeyResponse {
if ((json === undefined) || (json === null)) {
return json;
}
return {

'success': !exists(json, 'success') ? undefined : json['success'],
};
}

export function RegisterApiKeyResponseToJSON(value?: RegisterApiKeyResponse | null): any {
if (value === undefined) {
return undefined;
}
if (value === null) {
return null;
}
return {

'success': value.success,
};
}

2 changes: 2 additions & 0 deletions packages/sdk/src/sdk/api/generated/default/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,8 @@ export * from './ReceiveTipNotification';
export * from './ReceiveTipNotificationAction';
export * from './ReceiveTipNotificationActionData';
export * from './RedeemAmountResponse';
export * from './RegisterApiKeyRequestBody';
export * from './RegisterApiKeyResponse';
export * from './Related';
export * from './RelatedArtistResponse';
export * from './Remix';
Expand Down