Skip to content

Commit

Permalink
fix: add fields channel and network of IBPPeer API(#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
dayuy authored and Carrotzpc committed Feb 24, 2023
1 parent 8dc3e93 commit a858a46
Show file tree
Hide file tree
Showing 12 changed files with 201 additions and 33 deletions.
31 changes: 21 additions & 10 deletions src/channel/channel.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Injectable } from '@nestjs/common';
import { filter, find, isEqual, uniqWith } from 'lodash';
import { Injectable, Logger } from '@nestjs/common';
import { filter, find, isEqual, uniq, uniqWith } from 'lodash';
import { KubernetesService } from 'src/kubernetes/kubernetes.service';
import { CRD } from 'src/kubernetes/lib';
import { JwtAuth } from 'src/types';
Expand All @@ -12,6 +12,8 @@ import { Channel } from './models/channel.model';
export class ChannelService {
constructor(private readonly k8sService: KubernetesService) {}

private logger = new Logger('ChannelService');

format(channel: CRD.Channel): Channel {
return {
name: channel.metadata.name,
Expand All @@ -37,19 +39,28 @@ export class ChannelService {
return this.format(body);
}

async getChannelsByNames(auth: JwtAuth, names: string[]): Promise<Channel[]> {
const res = await Promise.allSettled(
uniq(names).map((n) => n && this.getChannel(auth, n)),
);
const chans = [];
res?.forEach((r) => {
if (r.status === 'fulfilled') {
chans.push(r.value);
} else {
this.logger.error('Failure', r.reason?.body);
}
});
return chans;
}

async createChannel(
auth: JwtAuth,
network: string,
channel: NewChannel,
): Promise<Channel> {
const {
name,
description,
initiator,
organizations,
peers, // TODO:必须是用户管理的组织(在members中)下的节点(提供接口)
policy,
} = channel;
const { name, description, initiator, organizations, peers, policy } =
channel;
const members = (organizations || [])
.concat(initiator)
.map((d) => ({ name: d }));
Expand Down
7 changes: 7 additions & 0 deletions src/common/utils/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { customAlphabet } from 'nanoid';
import { numbers, lowercase } from 'nanoid-dictionary';
import { TokenException } from './errors';
import type { JwtAuth, Request } from '../../types';
import { compact, uniq } from 'lodash';

/**
* 从 token 中解析用户认证信息
Expand Down Expand Up @@ -82,3 +83,9 @@ export const nanoid = customAlphabet(numbers + lowercase, 5);
* @returns
*/
export const genNanoid = (prefix: string) => `${prefix}-${nanoid()}`;

/**
* 多层级数组平铺去重
* @param {string[][]} arr
*/
export const flattenArr = (arr: string[][]) => uniq(compact(arr.flat()));
15 changes: 15 additions & 0 deletions src/ibppeer/ibppeer.gql
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
# 获取节点列表
query getIbppeers($organization: String!) {
ibppeers(organization: $organization) {
name
creationTimestamp
status
limits {
cpu
memory
}
channels
networks
}
}

# 创建节点
mutation createIbppeer($organization: String!) {
ibppeerCreate(organization: $organization) {
Expand Down
3 changes: 2 additions & 1 deletion src/ibppeer/ibppeer.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { Module } from '@nestjs/common';
import { IbppeerService } from './ibppeer.service';
import { IbppeerResolver } from './ibppeer.resolver';
import { ConfigmapModule } from 'src/configmap/configmap.module';
import { ChannelModule } from 'src/channel/channel.module';

@Module({
providers: [IbppeerService, IbppeerResolver],
exports: [IbppeerService],
imports: [ConfigmapModule],
imports: [ConfigmapModule, ChannelModule],
})
export class IbppeerModule {}
109 changes: 106 additions & 3 deletions src/ibppeer/ibppeer.resolver.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,41 @@
import { Args, Mutation, Resolver } from '@nestjs/graphql';
import {
Args,
Mutation,
Parent,
Query,
ResolveField,
Resolver,
} from '@nestjs/graphql';
import DataLoader from 'dataloader';
import { find } from 'lodash';
import { ChannelService } from 'src/channel/channel.service';
import { Loader } from 'src/common/dataloader';
import { Auth } from 'src/common/decorators/auth.decorator';
import { flattenArr } from 'src/common/utils';
import { FederationLoader } from 'src/federation/federation.loader';
import { Federation } from 'src/federation/models/federation.model';
import { Network } from 'src/network/models/network.model';
import { NetworkLoader } from 'src/network/network.loader';
import { Organization } from 'src/organization/models/organization.model';
import { OrganizationLoader } from 'src/organization/organization.loader';
import { JwtAuth } from 'src/types';
import { IbppeerService } from './ibppeer.service';
import { Ibppeer } from './models/ibppeer.model';

@Resolver()
@Resolver(() => Ibppeer)
export class IbppeerResolver {
constructor(private readonly ibppeerService: IbppeerService) {}
constructor(
private readonly ibppeerService: IbppeerService,
private readonly channelService: ChannelService,
) {}

@Query(() => [Ibppeer], { description: '获取组织下的节点列表' })
async ibppeers(
@Auth() auth: JwtAuth,
@Args('organization', { description: '所在组织' }) org: string,
): Promise<Ibppeer[]> {
return this.ibppeerService.getIbppeers(auth, org);
}

@Mutation(() => Ibppeer, { description: '创建IBPPeer节点' })
async ibppeerCreate(
Expand All @@ -15,4 +44,78 @@ export class IbppeerResolver {
): Promise<Ibppeer> {
return this.ibppeerService.createIbppeer(auth, org);
}

@ResolveField(() => [String], {
nullable: true,
description: '节点加入的通道',
})
async channels(
@Auth() auth: JwtAuth,
@Parent() ibppeer: Ibppeer,
@Loader(OrganizationLoader)
organizationLoader: DataLoader<Organization['name'], Organization>,
@Loader(FederationLoader)
fedLoader: DataLoader<Federation['name'], Federation>,
@Loader(NetworkLoader)
networkLoader: DataLoader<Network['name'], Network>,
): Promise<string[]> {
// org -> fed -> net -> chan -> peers => chan
const { namespace, name } = ibppeer;
const { federations } = await organizationLoader.load(namespace);
if (!federations || federations.length === 0) return;
const feds = await fedLoader.loadMany(federations);
const networkNames = (feds as Federation[]).map((fed) => fed?.networkNames);
if (!networkNames || networkNames.length === 0) return;
const nets = await networkLoader.loadMany(flattenArr(networkNames));
const channelNames = (nets as Network[]).map((net) => net.channelNames);
// TODO: channelLoader
const chans = await this.channelService.getChannelsByNames(
auth,
flattenArr(channelNames),
);
return chans
?.filter((chan) =>
chan?.peers?.find((p) => p.name === name && p.namespace === namespace),
)
?.map((chan) => chan.name);
}

// TODO: 优化合并?
@ResolveField(() => [String], {
nullable: true,
description: '节点加入的网络',
})
async networks(
@Auth() auth: JwtAuth,
@Parent() ibppeer: Ibppeer,
@Loader(OrganizationLoader)
organizationLoader: DataLoader<Organization['name'], Organization>,
@Loader(FederationLoader)
fedLoader: DataLoader<Federation['name'], Federation>,
@Loader(NetworkLoader)
networkLoader: DataLoader<Network['name'], Network>,
): Promise<string[]> {
// org -> fed -> net -> chan -> peers => chan => net
const { namespace, name } = ibppeer;
const { federations } = await organizationLoader.load(namespace);
if (!federations || federations.length === 0) return;
const feds = await fedLoader.loadMany(federations);
const networkNames = (feds as Federation[]).map((fed) => fed?.networkNames);
if (!networkNames || networkNames.length === 0) return;
const nets = await networkLoader.loadMany(flattenArr(networkNames));
const channelNames = (nets as Network[]).map((net) => net.channelNames);
// TODO: channelLoader
const chans = await this.channelService.getChannelsByNames(
auth,
flattenArr(channelNames),
);
const joinedChans = chans
?.filter((chan) =>
chan?.peers?.find((p) => p.name === name && p.namespace === namespace),
)
?.map((chan) => chan.name);
return (nets as Network[])
?.filter((net) => find(net.channelNames, (o) => joinedChans.includes(o)))
?.map((net) => net.name);
}
}
1 change: 1 addition & 0 deletions src/ibppeer/ibppeer.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export class IbppeerService {
).toISOString(),
limits: ibppeer.spec?.resources?.peer?.limits,
status: ibppeer.status?.type,
namespace: ibppeer.metadata?.namespace,
};
}

Expand Down
10 changes: 6 additions & 4 deletions src/ibppeer/models/ibppeer.model.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Field, ID, ObjectType } from '@nestjs/graphql';
import { Field, HideField, ID, ObjectType } from '@nestjs/graphql';
import { SpecResource } from 'src/common/models/spec-resource.model';
import { AnyObj } from 'src/types';
import { IbppeerStatus } from './ibppeer-status.enum';
Expand All @@ -11,12 +11,14 @@ export class Ibppeer {
/** 创建时间 */
creationTimestamp: string;

@HideField()
namespace?: string;

/** 加入的网络 */
// TODO
networks?: string[];

/** 加入的通道 */
// TODO
// org -> fed -> net -> chan -> peers
channels?: string[];

/** 节点配置 */
@Field(() => SpecResource, { description: '节点配置' })
Expand Down
4 changes: 1 addition & 3 deletions src/network/network.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,7 @@ export class NetworkResolver {
): Promise<Channel[]> {
const { channelNames } = network;
if (!channelNames || channelNames.length === 0) return;
return Promise.all(
channelNames.map((c) => this.channelService.getChannel(auth, c)),
);
return this.channelService.getChannelsByNames(auth, channelNames);
// TODO: list/channel 权限问题
// const cs = await channelLoader.loadMany(channelNames);
// return cs;
Expand Down
3 changes: 3 additions & 0 deletions src/organization/models/organization.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,7 @@ export class Organization {

/** 所有节点 */
ibppeers?: Ibppeer[];

/** 加入的通道 */
channels?: string[];
}
14 changes: 2 additions & 12 deletions src/organization/organization.gql
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,16 @@ query getOrganization($name: String!) {
reason
networks {
name
creationTimestamp
lastHeartbeatTime
expiredTime
clusterSize
organizations {
name
}
}
federations
channels
users {
name
isOrganizationAdmin
}
ibppeers {
name
creationTimestamp
creationTimestamp # TODO:去除,查询太多数据,分两个接口
status
limits {
cpu
Expand Down Expand Up @@ -79,10 +73,6 @@ mutation updateOrganization($name: String!, $organization: UpdateOrganization!)
admin
status
reason
users {
name
isOrganizationAdmin
}
}
}

Expand Down
22 changes: 22 additions & 0 deletions src/organization/organization.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import DataLoader from 'dataloader';
import { Loader } from 'src/common/dataloader';
import { Auth } from 'src/common/decorators/auth.decorator';
import { K8sV1Status } from 'src/common/models/k8s-v1-status.model';
import { flattenArr } from 'src/common/utils';
import { FederationLoader } from 'src/federation/federation.loader';
import { Federation } from 'src/federation/models/federation.model';
import { IbppeerService } from 'src/ibppeer/ibppeer.service';
Expand Down Expand Up @@ -145,4 +146,25 @@ export class OrganizationResolver {
const ibppeers = await this.ibppeerService.getIbppeers(auth, name);
return ibppeers;
}

@ResolveField(() => [String], {
nullable: true,
description: '组织加入的通道',
})
async channels(
@Parent() org: Organization,
@Loader(FederationLoader)
fedLoader: DataLoader<Federation['name'], Federation>,
@Loader(NetworkLoader)
networkLoader: DataLoader<Network['name'], Network>,
): Promise<string[]> {
const { federations } = org;
if (!federations || federations.length === 0) return;
const feds = await fedLoader.loadMany(federations);
const networkNames = (feds as Federation[]).map((fed) => fed?.networkNames);
if (!networkNames || networkNames.length === 0) return;
const nets = await networkLoader.loadMany(flattenArr(networkNames));
const channelNames = (nets as Network[]).map((net) => net.channelNames);
return flattenArr(channelNames);
}
}
15 changes: 15 additions & 0 deletions src/schema.gql
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ enum FederationStatus {
}

type Ibppeer {
"""加入的通道"""
channels: [String!]

"""创建时间"""
creationTimestamp: String!

Expand All @@ -97,6 +100,9 @@ type Ibppeer {
"""name"""
name: ID!

"""加入的网络"""
networks: [String!]

"""运行状态"""
status: IbppeerStatus
}
Expand Down Expand Up @@ -357,6 +363,9 @@ type Organization {
"""管理员"""
admin: String

"""加入的通道"""
channels: [String!]

"""创建时间"""
creationTimestamp: String!

Expand Down Expand Up @@ -487,6 +496,12 @@ type Query {
"""联盟列表"""
federations: [Federation!]!

"""获取组织下的节点列表"""
ibppeers(
"""所在组织"""
organization: String!
): [Ibppeer!]!

"""获取「创建/更新通道」时的可选节点列表"""
ibppeersForCreateChannel(
"""此通道的组织(包括发起者和配置成员)"""
Expand Down

0 comments on commit a858a46

Please sign in to comment.