Skip to content

Commit

Permalink
feat: setup create App module
Browse files Browse the repository at this point in the history
  • Loading branch information
DaniAkash committed Feb 29, 2024
1 parent 70d5e78 commit 2756448
Show file tree
Hide file tree
Showing 4 changed files with 289 additions and 28 deletions.
24 changes: 14 additions & 10 deletions src/client/lister.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ export class Lister extends BaseClient {
this.defaultPageSize = pageSize;
}

/**
* TODO: Implement the actual pagination logic
*/
async *listPagesGenerator<
TRequest extends jspb.Message,
TResponseObject extends { status?: Status.AsObject },
Expand Down Expand Up @@ -58,17 +55,24 @@ export class Lister extends BaseClient {
throw new Error(`Listing failed with response ${response}`);
}

// Process and yield response items
if (Object.keys(responseObject).length === 1) {
break;
} else {
yield response;
const dataListEntries = Object.entries(responseObject).find(
([key, value]) => key !== "status" && Array.isArray(value),
);

if (!dataListEntries) {
break; // If no data list is found, stop pagination
}

// Exit loop if pagination is not to be continued
if (pageNo !== undefined || perPage !== undefined) {
yield response;

const [, dataList] = dataListEntries;

// If the length of the data list is less than perPage, it means we've reached the end
// @ts-expect-error - TS doesn't know that data format is array
if (dataList.length < perPage) {
break;
}

page += 1;
}
}
Expand Down
117 changes: 100 additions & 17 deletions src/client/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,23 @@ import { Lister } from "./lister";
import { KWArgs } from "../utils/types";
import {
ListAppsRequest,
ListRunnersRequest,
MultiAppResponse,
MultiRunnerResponse,
PostAppsRequest,
} from "clarifai-nodejs-grpc/proto/clarifai/api/service_pb";
import { mapParamsToRequest, promisifyGrpcCall } from "../utils/misc";
import {
mapParamsToRequest,
mergeObjects,
promisifyGrpcCall,
} from "../utils/misc";
import { logger } from "../utils/logging";
import {
App,
Workflow,
} from "clarifai-nodejs-grpc/proto/clarifai/api/resources_pb";
import { App as ClarifaiApp } from "./app";
import { StatusCode } from "clarifai-nodejs-grpc/proto/clarifai/api/status/status_code_pb";

// interface UserAppID {
// userId?: string;
Expand All @@ -21,14 +35,13 @@ import { mapParamsToRequest, promisifyGrpcCall } from "../utils/misc";
// }

export class User extends Lister {
// private logger: Logger;
private logger;

constructor(kwargs: KWArgs = {}) {
super({ kwargs });
// this.logger = getLogger("INFO", __filename);
this.logger = logger;
}

// Convert generator functions to async functions returning Promises of arrays
async *listApps({
params = {},
pageNo,
Expand All @@ -42,7 +55,7 @@ export class User extends Lister {
| Record<string, never>;
pageNo?: number;
perPage?: number;
}): AsyncGenerator<MultiAppResponse, void, unknown> {
}): AsyncGenerator<MultiAppResponse.AsObject, void, unknown> {
const listApps = promisifyGrpcCall(
this.STUB.client.listApps,
this.STUB.client,
Expand All @@ -56,21 +69,91 @@ export class User extends Lister {
pageNo,
perPage,
)) {
yield item;
yield item.toObject();
}
}

async *listRunners({
params = {},
pageNo,
perPage,
}: {
params?:
| Omit<
Partial<ListRunnersRequest.AsObject>,
"userAppId" | "pageNo" | "perPage"
>
| Record<string, never>;
pageNo?: number;
perPage?: number;
}): AsyncGenerator<MultiRunnerResponse.AsObject, void, unknown> {
const listRunners = promisifyGrpcCall(
this.STUB.client.listRunners,
this.STUB.client,
);
const request = new ListRunnersRequest();
mapParamsToRequest(params, request);

for await (const item of this.listPagesGenerator(
listRunners,
request,
pageNo,
perPage,
)) {
yield item.toObject();
}
}

// async listRunners(
// filterBy: Record<string, any> = {},
// pageNo?: number,
// perPage?: number,
// ) {}

// async createApp(
// appId: string,
// baseWorkflow: string = "Empty",
// kwargs: Record<string, any> = {},
// ) {}
async createApp({
appId,
baseWorkflow = "Empty",
kwargs = {},
}: {
appId: string;
baseWorkflow: string;
kwargs: KWArgs;
}) {
const workflow = new Workflow();
workflow.setId(baseWorkflow);
workflow.setAppId("main");
workflow.setUserId("clarifai");

const app = new App();
app.setId(appId);
app.setDefaultWorkflow(workflow);

const request = new PostAppsRequest();
request.setUserAppId(this.userAppId);
request.setAppsList([app]);

const postApps = promisifyGrpcCall(
this.STUB.client.postApps,
this.STUB.client,
);

const response = await this.grpcRequest(postApps, request);

const responseObject = response.toObject();

if (responseObject.status?.code !== StatusCode.SUCCESS) {
throw new Error(
`Failed to create app: ${responseObject.status?.description}`,
);
}

this.logger.info(
`App created. Status Code: ${responseObject.status?.code}`,
);

kwargs = mergeObjects(kwargs, {
userId: this.userAppId.getUserId(),
base: this.base,
pat: this.pat,
appId: responseObject.appsList?.[0]?.id,
});

return new ClarifaiApp({ kwargs });
}

// TypeScript does not have a direct equivalent to Python's __getattr__, so this functionality may need to be implemented differently if required.

Expand Down
161 changes: 161 additions & 0 deletions src/urls/helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import { URL } from "url";

interface ClarifaiAuthHelper {
ui: string;
}

export class ClarifaiUrlHelper {
private auth: ClarifaiAuthHelper;
private moduleManagerImvId: string;

/**
* Creates an instance of ClarifaiUrlHelper.
* @param auth A ClarifaiAuthHelper object.
* @param moduleManagerImvId ID for the module manager install, default is "module_manager_install".
*/
constructor(
auth: ClarifaiAuthHelper,
moduleManagerImvId: string = "module_manager_install",
) {
this.auth = auth;
this.moduleManagerImvId = moduleManagerImvId;
}

/**
* Getter for the auth property.
*/
getAuth(): ClarifaiAuthHelper {
return this.auth;
}

/**
* Constructs a URL for module UI based on given parameters.
* @param userId User ID.
* @param appId Application ID.
* @param moduleId Module ID.
* @param moduleVersionId Module Version ID.
* @returns A string representing the module UI URL.
*/
moduleUiUrl(
userId: string,
appId: string,
moduleId: string,
moduleVersionId: string,
): string {
return `${this.auth.ui}/${userId}/${appId}/modules/${moduleId}/versions/${moduleVersionId}`;
}

/**
* Constructs a URL for module installation UI.
* @param destUserId Destination User ID.
* @param destAppId Destination Application ID.
* @param moduleUrl Module URL.
* @returns A string representing the module install UI URL.
*/
moduleInstallUiUrl(
destUserId: string,
destAppId: string,
moduleUrl: string,
): string {
return `${this.auth.ui}/${destUserId}/${destAppId}/installed_module_versions/${this.moduleManagerImvId}/install?install=${moduleUrl}`;
}

/**
* Constructs a URL for IMV UI.
* @param destUserId Destination User ID.
* @param destAppId Destination Application ID.
* @param imvId IMV ID.
* @returns A string representing the IMV UI URL.
*/
imvUiUrl(destUserId: string, destAppId: string, imvId: string): string {
return `${this.auth.ui}/${destUserId}/${destAppId}/installed_module_versions/${imvId}`;
}

/**
* Constructs a URL to the resource in the community.
* @param userId User ID.
* @param appId Application ID.
* @param resourceType Type of resource.
* @param resourceId Resource ID.
* @param versionId (Optional) Version of the resource.
* @returns A string representing the URL to the resource.
*/
clarifaiUrl(
userId: string,
appId: string,
resourceType: string,
resourceId: string,
versionId?: string,
): string {
const validTypes = [
"modules",
"models",
"concepts",
"inputs",
"workflows",
"tasks",
"installed_module_versions",
];
if (!validTypes.includes(resourceType)) {
throw new Error(
`resourceType must be one of ${validTypes.join(", ")} but was ${resourceType}`,
);
}
if (versionId === undefined) {
return `${this.auth.ui}/${userId}/${appId}/${resourceType}/${resourceId}`;
}
return `${this.auth.ui}/${userId}/${appId}/${resourceType}/${resourceId}/versions/${versionId}`;
}

/**
* Splits a Clarifai app URL into its component parts.
* @param url The Clarifai app URL.
* @returns A tuple containing userId and appId.
*/
static splitClarifaiAppUrl(url: string): [string, string] {
const o = new URL(url);
const parts = o.pathname.split("/").filter((part) => part.length > 0);
if (parts.length !== 3) {
throw new Error(
`Provided url must have 2 parts after the domain name. The current parts are: ${parts}`,
);
}
return [parts[0], parts[1]];
}

/**
* Splits a Clarifai URL into its component parts, including optional resource version.
* @param url The Clarifai URL.
* @returns A tuple containing userId, appId, resourceType, resourceId, and optionally resourceVersionId.
*/
static splitClarifaiUrl(
url: string,
): [string, string, string, string, string?] {
const o = new URL(url);
const parts = o.pathname.split("/").filter((part) => part.length > 0);
if (parts.length !== 5 && parts.length !== 7) {
throw new Error(
"Provided url must have 4 or 6 parts after the domain name.",
);
}
const [userId, appId, resourceType, resourceId] = parts.slice(1, 5);
const resourceVersionId = parts.length === 7 ? parts[6] : undefined;
return [userId, appId, resourceType, resourceId, resourceVersionId];
}

/**
* Splits a module UI URL into its component IDs.
* @param install The module UI URL.
* @returns A tuple containing userId, appId, moduleId, and moduleVersionId.
*/
static splitModuleUiUrl(install: string): [string, string, string, string] {
const [userId, appId, resourceType, resourceId, resourceVersionId] =
this.splitClarifaiUrl(install);
if (resourceType !== "modules" || resourceVersionId === undefined) {
throw new Error(
"Provided install url must have 6 parts after the domain name.",
);
}
return [userId, appId, resourceId, resourceVersionId];
}
}
15 changes: 14 additions & 1 deletion src/utils/misc.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { V2Client } from "clarifai-nodejs-grpc/proto/clarifai/api/service_grpc_pb";
import { UserError } from "../errors";
import { GrpcWithCallback } from "./types";
import { GrpcWithCallback, KWArgs } from "./types";
import { grpc } from "clarifai-nodejs-grpc";

/**
Expand Down Expand Up @@ -72,3 +72,16 @@ export function promisifyGrpcCall<TRequest, TResponse>(
});
};
}

export function mergeObjects(obj1: KWArgs, obj2: KWArgs): KWArgs {
const result = { ...obj1 };

type KnownKey = keyof KWArgs;
Object.entries(obj2).forEach(([key, value]) => {
if (value) {
result[key as KnownKey] = value;
}
});

return result;
}

0 comments on commit 2756448

Please sign in to comment.