From 9dada0c089be5799dad99e4d323f1a384559cef5 Mon Sep 17 00:00:00 2001 From: Rishabh Mishra Date: Mon, 8 Jan 2024 09:57:50 +0530 Subject: [PATCH] refactor: use list api member count instead of calling get api for each item (#445) --- sdks/js/packages/core/client/V1Beta1.ts | 281 +++++++++++------- .../js/packages/core/client/data-contracts.ts | 71 +++++ .../components/organization/project/index.tsx | 5 +- .../organization/project/projects.columns.tsx | 63 +--- .../components/organization/teams/index.tsx | 3 +- .../organization/teams/teams.columns.tsx | 33 +- .../react/hooks/useOrganizationProjects.ts | 9 +- .../core/react/hooks/useOrganizationTeams.ts | 14 +- 8 files changed, 275 insertions(+), 204 deletions(-) diff --git a/sdks/js/packages/core/client/V1Beta1.ts b/sdks/js/packages/core/client/V1Beta1.ts index a845594c3..462056d69 100644 --- a/sdks/js/packages/core/client/V1Beta1.ts +++ b/sdks/js/packages/core/client/V1Beta1.ts @@ -118,6 +118,7 @@ import { V1Beta1GetServiceUserKeyResponse, V1Beta1GetServiceUserResponse, V1Beta1GetSubscriptionResponse, + V1Beta1GetUpcomingInvoiceResponse, V1Beta1GetUserResponse, V1Beta1GroupRequestBody, V1Beta1JoinOrganizationResponse, @@ -134,6 +135,7 @@ import { V1Beta1ListGroupPreferencesResponse, V1Beta1ListGroupUsersResponse, V1Beta1ListGroupsResponse, + V1Beta1ListInvoicesResponse, V1Beta1ListMetaSchemasResponse, V1Beta1ListNamespacesResponse, V1Beta1ListOrganizationAdminsResponse, @@ -151,6 +153,7 @@ import { V1Beta1ListOrganizationsResponse, V1Beta1ListPermissionsResponse, V1Beta1ListPlansResponse, + V1Beta1ListPlatformUsersResponse, V1Beta1ListPoliciesResponse, V1Beta1ListPreferencesResponse, V1Beta1ListProjectAdminsResponse, @@ -311,7 +314,24 @@ export class V1Beta1 extends HttpClient + this.request({ + path: `/v1beta1/admin/platform/users`, + method: 'GET', + secure: true, + format: 'json', + ...params + }); + /** + * @description Adds a user to the platform. * * @tags Platform * @name AdminServiceAddPlatformUser @@ -364,10 +384,19 @@ export class V1Beta1 extends HttpClient + adminServiceListRelations = ( + query?: { + /** The subject to filter by. */ + subject?: string; + /** The object to filter by. */ + object?: string; + }, + params: RequestParams = {} + ) => this.request({ path: `/v1beta1/admin/relations`, method: 'GET', + query: query, secure: true, format: 'json', ...params @@ -708,32 +737,6 @@ export class V1Beta1 extends HttpClient - this.request({ - path: `/v1beta1/billing/${billingId}/transactions`, - method: 'GET', - query: query, - secure: true, - format: 'json', - ...params - }); /** * @description List all features of a platform. * @@ -1073,80 +1076,6 @@ export class V1Beta1 extends HttpClient - this.request({ - path: `/v1beta1/organization/${orgId}/auditlogs`, - method: 'GET', - query: query, - secure: true, - format: 'json', - ...params - }); - /** - * @description Create new audit logs in a batch. - * - * @tags AuditLog - * @name FrontierServiceCreateOrganizationAuditLogs - * @summary Create audit log - * @request POST:/v1beta1/organization/{orgId}/auditlogs - * @secure - */ - frontierServiceCreateOrganizationAuditLogs = ( - orgId: string, - body: { - logs?: V1Beta1AuditLog[]; - }, - params: RequestParams = {} - ) => - this.request({ - path: `/v1beta1/organization/${orgId}/auditlogs`, - method: 'POST', - body: body, - secure: true, - type: ContentType.Json, - format: 'json', - ...params - }); - /** - * @description Get an audit log by ID. - * - * @tags AuditLog - * @name FrontierServiceGetOrganizationAuditLog - * @summary Get audit log - * @request GET:/v1beta1/organization/{orgId}/auditlogs/{id} - * @secure - */ - frontierServiceGetOrganizationAuditLog = (orgId: string, id: string, params: RequestParams = {}) => - this.request({ - path: `/v1beta1/organization/${orgId}/auditlogs/${id}`, - method: 'GET', - secure: true, - format: 'json', - ...params - }); /** * @description Returns a list of organizations. It can be filtered by userID or organization state. * @@ -1351,6 +1280,7 @@ export class V1Beta1 extends HttpClient *Possible values:* `enabled` or `disabled` */ state?: string; + withMemberCount?: boolean; }, params: RequestParams = {} ) => @@ -1447,6 +1377,80 @@ export class V1Beta1 extends HttpClient + this.request({ + path: `/v1beta1/organizations/${orgId}/auditlogs`, + method: 'GET', + query: query, + secure: true, + format: 'json', + ...params + }); + /** + * @description Create new audit logs in a batch. + * + * @tags AuditLog + * @name FrontierServiceCreateOrganizationAuditLogs + * @summary Create audit log + * @request POST:/v1beta1/organizations/{orgId}/auditlogs + * @secure + */ + frontierServiceCreateOrganizationAuditLogs = ( + orgId: string, + body: { + logs?: V1Beta1AuditLog[]; + }, + params: RequestParams = {} + ) => + this.request({ + path: `/v1beta1/organizations/${orgId}/auditlogs`, + method: 'POST', + body: body, + secure: true, + type: ContentType.Json, + format: 'json', + ...params + }); + /** + * @description Get an audit log by ID. + * + * @tags AuditLog + * @name FrontierServiceGetOrganizationAuditLog + * @summary Get audit log + * @request GET:/v1beta1/organizations/{orgId}/auditlogs/{id} + * @secure + */ + frontierServiceGetOrganizationAuditLog = (orgId: string, id: string, params: RequestParams = {}) => + this.request({ + path: `/v1beta1/organizations/${orgId}/auditlogs/${id}`, + method: 'GET', + secure: true, + format: 'json', + ...params + }); /** * @description List billing accounts of an organization. * @@ -1564,6 +1568,40 @@ export class V1Beta1 extends HttpClient + this.request({ + path: `/v1beta1/organizations/${orgId}/billing/${billingId}/invoices`, + method: 'GET', + secure: true, + format: 'json', + ...params + }); + /** + * @description Get the upcoming invoice of a billing account. + * + * @tags Invoice + * @name FrontierServiceGetUpcomingInvoice + * @summary Get upcoming invoice + * @request GET:/v1beta1/organizations/{orgId}/billing/{billingId}/invoices/upcoming + * @secure + */ + frontierServiceGetUpcomingInvoice = (orgId: string, billingId: string, params: RequestParams = {}) => + this.request({ + path: `/v1beta1/organizations/${orgId}/billing/${billingId}/invoices/upcoming`, + method: 'GET', + secure: true, + format: 'json', + ...params + }); /** * @description List subscriptions of a billing account. * @@ -1642,6 +1680,32 @@ export class V1Beta1 extends HttpClient + this.request({ + path: `/v1beta1/organizations/${orgId}/billing/${billingId}/transactions`, + method: 'GET', + query: query, + secure: true, + format: 'json', + ...params + }); /** * @description Report a new billing usage for a billing account. * @@ -1678,10 +1742,18 @@ export class V1Beta1 extends HttpClient + frontierServiceGetBillingAccount = ( + orgId: string, + id: string, + query?: { + withPaymentMethods?: boolean; + }, + params: RequestParams = {} + ) => this.request({ path: `/v1beta1/organizations/${orgId}/billing/${id}`, method: 'GET', + query: query, secure: true, format: 'json', ...params @@ -1866,6 +1938,7 @@ export class V1Beta1 extends HttpClient @@ -3530,6 +3603,7 @@ export class V1Beta1 extends HttpClient @@ -3648,6 +3722,7 @@ export class V1Beta1 extends HttpClient diff --git a/sdks/js/packages/core/client/data-contracts.ts b/sdks/js/packages/core/client/data-contracts.ts index 09b046d88..79075df0e 100644 --- a/sdks/js/packages/core/client/data-contracts.ts +++ b/sdks/js/packages/core/client/data-contracts.ts @@ -525,6 +525,7 @@ export interface V1Beta1Feature { prices?: V1Beta1Price[]; /** @format int64 */ creditAmount?: string; + behavior?: string; metadata?: object; /** @format date-time */ createdAt?: string; @@ -540,12 +541,15 @@ export interface V1Beta1FeatureRequestBody { prices?: V1Beta1Price[]; /** @format int64 */ creditAmount?: string; + behavior?: string; metadata?: object; } export interface V1Beta1GetBillingAccountResponse { /** Billing account */ billingAccount?: V1Beta1BillingAccount; + /** List of payment methods */ + paymentMethods?: V1Beta1PaymentMethod[]; } export interface V1Beta1GetBillingBalanceResponse { @@ -637,6 +641,11 @@ export interface V1Beta1GetSubscriptionResponse { subscription?: V1Beta1Subscription; } +export interface V1Beta1GetUpcomingInvoiceResponse { + /** Upcoming invoice */ + invoice?: V1Beta1Invoice; +} + export interface V1Beta1GetUserResponse { user?: V1Beta1User; } @@ -660,6 +669,12 @@ export interface V1Beta1Group { */ updatedAt?: string; users?: V1Beta1User[]; + /** + * The number of members explicitly added in the project. + * @format int32 + * @example 2 + */ + membersCount?: number; } export interface V1Beta1GroupRequestBody { @@ -716,6 +731,30 @@ export interface V1Beta1Invitation { roleIds?: string[]; } +export interface V1Beta1Invoice { + id?: string; + customerId?: string; + providerId?: string; + state?: string; + currency?: string; + /** @format int64 */ + amount?: string; + hostedUrl?: string; + /** + * The date on which payment for this invoice is due + * @format date-time + */ + dueDate?: string; + /** + * The date when this invoice is in effect. + * @format date-time + */ + effectiveAt?: string; + metadata?: object; + /** @format date-time */ + createdAt?: string; +} + /** JSON Web Key as specified in RFC 7517 */ export interface V1Beta1JSONWebKey { /** Key Type. */ @@ -819,6 +858,11 @@ export interface V1Beta1ListGroupsResponse { groups?: V1Beta1Group[]; } +export interface V1Beta1ListInvoicesResponse { + /** List of invoices */ + invoices?: V1Beta1Invoice[]; +} + export interface V1Beta1ListMetaSchemasResponse { metaschemas?: V1Beta1MetaSchema[]; } @@ -896,6 +940,11 @@ export interface V1Beta1ListPlansResponse { plans?: V1Beta1Plan[]; } +export interface V1Beta1ListPlatformUsersResponse { + users?: V1Beta1User[]; + serviceusers?: V1Beta1ServiceUser[]; +} + export interface V1Beta1ListPoliciesResponse { policies?: V1Beta1Policy[]; } @@ -1104,6 +1153,22 @@ export interface V1Beta1OrganizationRequestBody { avatar?: string; } +export interface V1Beta1PaymentMethod { + id?: string; + customerId?: string; + providerId?: string; + type?: string; + cardBrand?: string; + cardLast4?: string; + /** @format int64 */ + cardExpiryMonth?: string; + /** @format int64 */ + cardExpiryYear?: string; + metadata?: object; + /** @format date-time */ + createdAt?: string; +} + export interface V1Beta1Permission { id?: string; name?: string; @@ -1305,6 +1370,12 @@ export interface V1Beta1Project { * @example "2023-06-07T05:39:56.961Z" */ updatedAt?: string; + /** + * The number of members explicitly added in the project. + * @format int32 + * @example 2 + */ + membersCount?: number; } export interface V1Beta1ProjectRequestBody { diff --git a/sdks/js/packages/core/react/components/organization/project/index.tsx b/sdks/js/packages/core/react/components/organization/project/index.tsx index ed3e16ab7..9f00eb137 100644 --- a/sdks/js/packages/core/react/components/organization/project/index.tsx +++ b/sdks/js/packages/core/react/components/organization/project/index.tsx @@ -33,7 +33,10 @@ export default function WorkspaceProjects() { projects, userAccessOnProject, refetch - } = useOrganizationProjects({ showInhreitedProjects: showOrgProjects }); + } = useOrganizationProjects({ + showInhreitedProjects: showOrgProjects, + withMemberCount: true + }); const { activeOrganization: organization } = useFrontier(); const routerState = useRouterState(); diff --git a/sdks/js/packages/core/react/components/organization/project/projects.columns.tsx b/sdks/js/packages/core/react/components/organization/project/projects.columns.tsx index 352fb5e37..ab0019e88 100644 --- a/sdks/js/packages/core/react/components/organization/project/projects.columns.tsx +++ b/sdks/js/packages/core/react/components/organization/project/projects.columns.tsx @@ -6,9 +6,7 @@ import { import { DropdownMenu, Text } from '@raystack/apsara'; import { Link } from '@tanstack/react-router'; import type { ColumnDef } from '@tanstack/react-table'; -import { useCallback, useEffect, useState } from 'react'; import Skeleton from 'react-loading-skeleton'; -import { useFrontier } from '~/react/contexts/FrontierContext'; import { V1Beta1Project } from '~/src'; export const getColumns: ( @@ -46,11 +44,11 @@ export const getColumns: ( // }, { header: 'Members', - accessorKey: 'members', + accessorKey: 'members_count', cell: isLoading ? () => : ({ row, getValue }) => { - return ; + return {getValue()} members; } }, { @@ -140,61 +138,4 @@ const ProjectActions = ({ ) : null; }; -interface ProjectMembersProps { - projectId?: string; -} -const ProjectMembers = ({ projectId }: ProjectMembersProps) => { - const { client } = useFrontier(); - const [members, setMembers] = useState([]); - const [isMembersLoading, setIsMembersLoading] = useState(false); - - const [teams, setTeams] = useState([]); - const [isTeamsLoading, setIsTeamsLoading] = useState(false); - - const getProjectMembers = useCallback(async () => { - if (!projectId) return; - try { - setIsMembersLoading(true); - const { - // @ts-ignore - data: { users } - } = await client?.frontierServiceListProjectUsers(projectId); - setMembers(users); - } catch (err) { - console.error(err); - } finally { - setIsMembersLoading(false); - } - }, [client, projectId]); - - const getProjectTeams = useCallback(async () => { - if (!projectId) return; - try { - setIsTeamsLoading(true); - - const { - // @ts-ignore - data: { groups } - } = await client?.frontierServiceListProjectGroups(projectId); - setTeams(groups); - } catch (err) { - console.error(err); - } finally { - setIsTeamsLoading(false); - } - }, [client, projectId]); - - useEffect(() => { - getProjectMembers(); - getProjectTeams(); - }, [client, getProjectMembers, getProjectTeams]); - - const isLoading = isMembersLoading || isTeamsLoading; - return isLoading ? ( - Loading... - ) : ( - - {members.length} members{teams.length ? `, ${teams.length} teams` : ''} - - ); }; diff --git a/sdks/js/packages/core/react/components/organization/teams/index.tsx b/sdks/js/packages/core/react/components/organization/teams/index.tsx index 24fc90c10..daf9b3aa6 100644 --- a/sdks/js/packages/core/react/components/organization/teams/index.tsx +++ b/sdks/js/packages/core/react/components/organization/teams/index.tsx @@ -52,7 +52,8 @@ export default function WorkspaceTeams() { refetch } = useOrganizationTeams({ withPermissions: ['update', 'delete'], - showOrgTeams + showOrgTeams, + withMemberCount: true }); const { activeOrganization: organization } = useFrontier(); diff --git a/sdks/js/packages/core/react/components/organization/teams/teams.columns.tsx b/sdks/js/packages/core/react/components/organization/teams/teams.columns.tsx index 37739e77a..6b0f1cb67 100644 --- a/sdks/js/packages/core/react/components/organization/teams/teams.columns.tsx +++ b/sdks/js/packages/core/react/components/organization/teams/teams.columns.tsx @@ -6,9 +6,7 @@ import { import { DropdownMenu, Text } from '@raystack/apsara'; import { Link } from '@tanstack/react-router'; import type { ColumnDef } from '@tanstack/react-table'; -import { useEffect, useState } from 'react'; import Skeleton from 'react-loading-skeleton'; -import { useFrontier } from '~/react/contexts/FrontierContext'; import { V1Beta1Group } from '~/src'; import styles from '../organization.module.css'; @@ -35,12 +33,10 @@ export const getColumns: ( }, { header: 'Members', - accessorKey: 'members', + accessorKey: 'members_count', cell: isLoading ? () => - : ({ row, getValue }) => ( - - ) + : ({ row, getValue }) => {getValue()} members }, { header: '', @@ -110,28 +106,3 @@ const TeamActions = ({ ) : null; }; - -interface TeamMembersProps { - teamId?: string; - orgId?: string; -} -const TeamMembers = ({ teamId, orgId }: TeamMembersProps) => { - const { client, user } = useFrontier(); - const [members, setMembers] = useState([]); - - useEffect(() => { - async function getTeamMembers() { - if (!teamId) return; - if (!orgId) return; - - const { - // @ts-ignore - data: { users } - } = await client?.frontierServiceListGroupUsers(orgId, teamId); - setMembers(users); - } - getTeamMembers(); - }, [client, orgId, teamId]); - - return {members.length} members; -}; diff --git a/sdks/js/packages/core/react/hooks/useOrganizationProjects.ts b/sdks/js/packages/core/react/hooks/useOrganizationProjects.ts index e8fd45810..b78efd6a4 100644 --- a/sdks/js/packages/core/react/hooks/useOrganizationProjects.ts +++ b/sdks/js/packages/core/react/hooks/useOrganizationProjects.ts @@ -3,10 +3,12 @@ import { useFrontier } from '../contexts/FrontierContext'; interface useOrganizationProjectsProps { showInhreitedProjects?: boolean; + withMemberCount?: boolean; } export const useOrganizationProjects = ({ - showInhreitedProjects = false + showInhreitedProjects = false, + withMemberCount = false }: useOrganizationProjectsProps) => { const [isProjectsLoading, setIsProjectsLoading] = useState(false); const [projects, setProjects] = useState([]); @@ -25,7 +27,8 @@ export const useOrganizationProjects = ({ org_id: organization?.id, withPermissions: ['update', 'delete'], // @ts-ignore - nonInherited: !showInhreitedProjects + nonInherited: !showInhreitedProjects, + withMemberCount: withMemberCount }); setProjects(projects); setAccessPairs(access_pairs); @@ -34,7 +37,7 @@ export const useOrganizationProjects = ({ } finally { setIsProjectsLoading(false); } - }, [client, organization?.id, showInhreitedProjects]); + }, [client, organization?.id, showInhreitedProjects, withMemberCount]); useEffect(() => { getProjects(); diff --git a/sdks/js/packages/core/react/hooks/useOrganizationTeams.ts b/sdks/js/packages/core/react/hooks/useOrganizationTeams.ts index f189b453b..531dda9c1 100644 --- a/sdks/js/packages/core/react/hooks/useOrganizationTeams.ts +++ b/sdks/js/packages/core/react/hooks/useOrganizationTeams.ts @@ -5,11 +5,13 @@ import { V1Beta1Group } from '~/src'; interface useOrganizationTeamsProps { withPermissions?: string[]; showOrgTeams?: boolean; + withMemberCount?: boolean; } export const useOrganizationTeams = ({ withPermissions = [], - showOrgTeams = false + showOrgTeams = false, + withMemberCount = false }: useOrganizationTeamsProps) => { const [teams, setTeams] = useState([]); const [isTeamsLoading, setIsTeamsLoading] = useState(false); @@ -25,11 +27,15 @@ export const useOrganizationTeams = ({ // @ts-ignore data: { groups = [], access_pairs = [] } } = showOrgTeams - ? await client?.frontierServiceListOrganizationGroups(organization?.id) + ? await client?.frontierServiceListOrganizationGroups( + organization?.id, + { withMemberCount: withMemberCount } + ) : await client?.frontierServiceListCurrentUserGroups({ // @ts-ignore org_id: organization?.id, - withPermissions + withPermissions, + withMemberCount: withMemberCount }); setTeams(groups); setAccessPairs(access_pairs); @@ -38,7 +44,7 @@ export const useOrganizationTeams = ({ } finally { setIsTeamsLoading(false); } - }, [client, organization?.id, showOrgTeams]); + }, [client, organization?.id, showOrgTeams, withMemberCount]); useEffect(() => { getTeams();