lodash#castArray TypeScript Examples

The following examples show how to use lodash#castArray. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: filter.helper.ts    From typeorm-query-builder-wrapper with MIT License 6 votes vote down vote up
public notIn(exclude: string[] | number[] | string) {
    if (typeof exclude === 'string') {
      return this.completeWhere(
        OPERATOR.SQL.OPERATOR_NOT_IN,
        exclude,
        { quoteString: false },
      );
    }

    if (isArray(exclude) && exclude.length === 0) {
      throw new Error('Argument of NOT IN have to be non-empty array.')
    }

    // If comparing strings, must escape them as strings in the query.
    this.escapeStringArray(castArray(exclude as string[]));

    return this.completeWhere(
      OPERATOR.SQL.OPERATOR_NOT_IN,
      `(${exclude.join(', ')})`,
      { quoteString: false },
    );
  }
Example #2
Source File: multiple-columns-card.editor.tsx    From next-basics with GNU General Public License v3.0 6 votes vote down vote up
export function MultipleColumnsCardEditor({
  nodeUid,
}: EditorComponentProps): React.ReactElement {
  const node = useBuilderNode<MultipleColumnsCardProperties>({ nodeUid });

  const { gridColumns } = node.$$parsedProperties;

  const columns =
    castArray(gridColumns).reduce((str, value) => {
      if (typeof value === "string") {
        str += `${value} `;
      } else if (typeof value === "number") {
        str += `${value}fr `;
      }
      return str;
    }, "") || "1fr";

  return (
    <EditorContainer nodeUid={nodeUid}>
      <div>
        <SlotContainer
          nodeUid={nodeUid}
          slotName="content"
          showOutlineIfEmpty
          dropZoneBodyStyle={{ display: "grid", gridTemplateColumns: columns }}
          slotContainerStyle={{ padding: "20px" }}
          slotContentLayout={EditorSlotContentLayout.GRID}
        />
      </div>
    </EditorContainer>
  );
}
Example #3
Source File: filter.helper.ts    From typeorm-query-builder-wrapper with MIT License 6 votes vote down vote up
public in(include: string[] | number[] | string) {
    if (typeof include === 'string') {
      return this.completeWhere(
        OPERATOR.SQL.OPERATOR_IN,
        include,
        { quoteString: false },
      );
    }

    if (isArray(include) && include.length === 0) {
      throw new Error('Argument of IN have to be non-empty array.')
    }

    // If comparing strings, must escape them as strings in the query.
    this.escapeStringArray(castArray(include as string[]));

    return this.completeWhere(
      OPERATOR.SQL.OPERATOR_IN,
      `(${include.join(', ')})`,
      { quoteString: false },
    );
  }
Example #4
Source File: AccountsTask.ts    From guardian with Apache License 2.0 6 votes vote down vote up
async start<T extends BaseSubstrateGuardian>(guardian: T) {
    const { apiRx } = await guardian.isReady();

    const { account } = this.arguments;

    return from(castArray(account)).pipe(
      mergeMap((account) => apiRx.query.system.account(account).pipe(map(mapResult(account))))
    );
  }
Example #5
Source File: getProcessedEvents.ts    From next-basics with GNU General Public License v3.0 6 votes vote down vote up
export function processHandlers(
  eventList: BrickEventHandler | BrickEventHandler[]
): BrickEventHandler[] {
  const _processHandlers = (handler: BrickEventHandlerCallback): void => {
    for (const [name, events] of Object.entries(handler)) {
      const list = castArray(events);
      handler[name as keyof BrickEventHandlerCallback] = list;
      list.forEach((item) => {
        if (item.callback) {
          _processHandlers(item.callback);
        }
      });
    }
  };

  const processedList = castArray(eventList);
  processedList.forEach((handler) => {
    if ((handler as UseProviderEventHandler).callback) {
      _processHandlers((handler as UseProviderEventHandler).callback);
    }
  });

  return processedList;
}
Example #6
Source File: conversation.request.ts    From linkedin-private-api with MIT License 6 votes vote down vote up
getConversations({
    recipients,
    createdBefore,
  }: {
    recipients?: ProfileId | ProfileId[];
    createdBefore?: Date;
  }): Promise<GetConversationsResponse> {
    const queryParams = {
      keyVersion: 'LEGACY_INBOX',
      ...(recipients && { q: 'participants', recipients: castArray(recipients) }),
      ...(createdBefore && { createdBefore: createdBefore.getTime() }),
    };

    return this.request.get<GetConversationsResponse>('messaging/conversations', {
      params: queryParams,
    });
  }
Example #7
Source File: PricesTask.ts    From guardian with Apache License 2.0 6 votes vote down vote up
async start<T extends BaseSubstrateGuardian>(guardian: T) {
    const { apiRx } = await guardian.isReady();

    const { key, period } = this.arguments;
    if (key === 'all') {
      return observeRPC<Vec<ITuple<[Codec, Option<TimestampedValue>]>>>(
        apiRx.rpc.oracle.getAllValues,
        ['Aggregated'],
        period
      ).pipe(
        mergeMap((result): Observable<Price> => {
          return from(result).pipe(
            filter(([, item]) => item.isSome),
            map(([key, item]) => ({
              key: key.toString(),
              value: getValueFromTimestampValue(item.unwrap()).toString()
            }))
          );
        })
      );
    }

    const keys = castArray(key).map((x) => apiRx.createType('CurrencyId', x));
    return from(keys).pipe(
      mergeMap((key) =>
        from(getOraclePrice(apiRx, period)(key)).pipe(
          map((price) => ({ key: key.toString(), value: price.toFixed(0) }))
        )
      ),
      filter((price) => price.value.length > 0)
    );
  }
Example #8
Source File: index.ts    From excalideck with MIT License 6 votes vote down vote up
async expectToSee(...expectations: string[]) {
        const stepTitle = [
            "Expect to see:",
            castArray(expectations)
                .map((expectation) => `${expectation}`)
                .join(", "),
        ].join(" ");
        await test.step(stepTitle, async () => {
            expect(await this.page.screenshot()).toMatchSnapshot(
                this.issueNextSnapshotName()
            );
        });
    }
Example #9
Source File: BalancesTask.ts    From guardian with Apache License 2.0 6 votes vote down vote up
async start<T extends BaseSubstrateGuardian>(guardian: T) {
    const { apiRx } = await guardian.isReady();

    const { account, currencyId } = this.arguments;

    const currencyIds = castArray(currencyId).map((x) => apiRx.createType('CurrencyId', x));

    const pairs = createAccountCurrencyIdPairs(account, currencyIds);
    return from(pairs).pipe(mergeMap(({ account, currencyId }) => this.getBalance(apiRx, account, currencyId)));
  }
Example #10
Source File: TraderInfoTask.ts    From guardian with Apache License 2.0 6 votes vote down vote up
getBalances = (apiRx: ApiRx) => (account: string, poolId: number | number[] | 'all') => {
  return apiRx.query.marginProtocol.balances.entries(account).pipe(
    mergeMap((entries) =>
      entries.filter(([storageKey]) => {
        if (poolId === 'all') return true;
        const [, id] = storageKey.args;
        return castArray(poolId).includes(id.toNumber());
      })
    )
  );
}
Example #11
Source File: TraderInfoTask.ts    From guardian with Apache License 2.0 6 votes vote down vote up
async start(guardian: LaminarGuardian) {
    const { apiRx } = await guardian.isReady();

    const accounts = castArray(this.arguments.account);

    const getBalances$ = getBalances(apiRx);

    return from(accounts).pipe(
      switchMap((account) => {
        return getBalances$(account, this.arguments.poolId).pipe(
          mergeMap(([storageKey, balance]) => {
            const [account, poolId] = storageKey.args;
            return getTraderState(apiRx, account, poolId, this.arguments.period).pipe(
              map(
                ({
                  equity,
                  unrealized_pl: unrealizedPL,
                  margin_level: marginLevel,
                  margin_held: marginHeld,
                  free_margin: freeMargin
                }) => {
                  return {
                    balance: balance.toString(),
                    freeMargin: freeMargin.toString(),
                    marginHeld: marginHeld.toString(),
                    unrealizedPl: unrealizedPL.toString(),
                    accumulatedSwap: equity.sub(new BN(balance)).sub(unrealizedPL).toString(),
                    equity: equity.toString(),
                    marginLevel: marginLevel.toString(),
                    totalLeveragedPosition: equity.mul(unit).div(marginLevel).toString()
                  };
                }
              )
            );
          })
        );
      })
    );
  }
Example #12
Source File: PositionsByTraderTask.ts    From guardian with Apache License 2.0 6 votes vote down vote up
async start(guardian: LaminarGuardian) {
    const { apiRx } = await guardian.isReady();

    const accounts = castArray(this.arguments.account);

    const getSwap = accumulatedSwap(apiRx);
    const getUnrealizedPL = unrealizedPL(apiRx);

    return from(accounts).pipe(
      switchMap((account) => apiRx.query.marginProtocol.positionsByTrader.entries(account)),
      mergeAll(),
      filter(([, value]) => !value.isEmpty),
      mergeMap(([storageKey]) => {
        const positionId = storageKey.args[1][1];
        return apiRx.query.marginProtocol.positions(positionId).pipe(
          filter((x) => x.isSome),
          map((x) => x.unwrap()),
          mergeMap((position) => combineLatest([of(position), getSwap(position), getUnrealizedPL(position)])),
          map(([position, swap, profit]) => {
            const { owner, poolId, pair, leverage, marginHeld } = position;
            return {
              account: owner.toString(),
              liquidityPoolId: poolId.toString(),
              positionId: positionId.toString(),
              pair: {
                base: pair.base.toString(),
                quote: pair.quote.toString()
              },
              leverage: leverage.toString(),
              marginHeld: marginHeld.toString(),
              accumulatedSwap: swap ? swap.toFixed(0) : '',
              profit: profit ? profit.toFixed(0) : ''
            };
          })
        );
      })
    );
  }
Example #13
Source File: test-generate.ts    From prisma-nestjs-graphql with MIT License 5 votes vote down vote up
/**
 * Get generator options after run prisma generate.
 */
async function createGeneratorOptions(
  schema: string,
  options?: string[] | string,
  provider: 'postgresql' | 'mongodb' = 'postgresql',
): Promise<GeneratorOptions & { prismaClientDmmf: DMMF.Document }> {
  const schemaHeader = `
        datasource db {
            provider = "${provider}"
            url = env("DATABASE_URL")
        }
        generator client {
            provider        = "prisma-client-js"
            previewFeatures = ["filterJson", "fullTextSearch", "referentialIntegrity", "extendedIndexes", "fullTextIndex"]
        }
    `;
  // eslint-disable-next-line prefer-rest-params
  const hash = createHash(generatorVersion, schemaHeader, arguments);
  const cacheFile = `${cachePath}/options-${hash}.js`;
  if (!fs.existsSync(cacheFile)) {
    const schemaFile = `${cachePath}/schema-${hash}.prisma`;
    const schemaContent = `
            ${schemaHeader}
            generator proxy {
                provider = "node -r ts-node/register/transpile-only src/test/proxy-generator.ts"
                output = "."
                hash = "${hash}"
                ${castArray(options).join('\n')}
            }
            ${schema}
        `;
    fs.writeFileSync(schemaFile, schemaContent);

    await new Promise((resolve, reject) => {
      const proc = exec(
        `node node_modules/prisma/build/index.js generate --schema=${schemaFile}`,
      );
      if (!proc.stderr) {
        throw new Error('Generate error');
      }
      proc.stdout?.pipe(process.stdout);
      proc.stderr.on('data', data => {
        reject(String(data));
      });
      proc.on('error', reject);
      proc.on('exit', code => {
        code === 0 ? resolve(0) : reject();
      });
    });
  }
  // eslint-disable-next-line unicorn/prefer-module
  return require(cacheFile);
}
Example #14
Source File: PoolInfoTask.ts    From guardian with Apache License 2.0 5 votes vote down vote up
setup = async (apiRx: ApiRx, tokens: string[]) => {
  const getPools = (poolId: number | number[] | 'all') => {
    return apiRx.query.baseLiquidityPoolsForMargin.pools.entries().pipe(
      mergeMap((entries) => {
        return entries.filter(([storageKey]) => {
          if (poolId === 'all') return true;
          const [id] = storageKey.args;
          return castArray(poolId).includes(id.toNumber());
        });
      })
    );
  };

  const getTradingPairOptions = (poolId: LiquidityPoolId, getPairId: Function): Observable<TraderPairOptions[]> => {
    return apiRx.query.marginLiquidityPools.poolTradingPairOptions.entries(poolId).pipe(
      mergeMap((x) => x),
      map(([storageKey, options]) => {
        const [, tradingPair] = storageKey.args;
        return apiRx.query.marginLiquidityPools.tradingPairOptions(tradingPair).pipe(
          map((tradingPairOptions) => {
            const maxSpread = tradingPairOptions.maxSpread;
            let askSpread = options.askSpread.unwrapOrDefault();
            let bidSpread = options.bidSpread.unwrapOrDefault();

            if (maxSpread.isSome) {
              askSpread = apiRx.createType('FixedU128', BN.max(maxSpread.unwrap(), askSpread));
              bidSpread = apiRx.createType('FixedU128', BN.max(maxSpread.unwrap(), bidSpread));
            }

            return {
              pair: {
                base: tradingPair.base.toString(),
                quote: tradingPair.quote.toString()
              },
              pairId: getPairId(tradingPair),
              enabledTrades: options.enabledTrades.toJSON(),
              askSpread: askSpread.toString(),
              bidSpread: bidSpread.toString()
            };
          })
        );
      }),
      combineLatestAll()
    );
  };

  const getPairId = (pair: { base: string; quote: string }): string => {
    const baseToken = tokens.find((x) => pair.base === x);
    const quoteToken = tokens.find((x) => pair.quote === x);
    return `${baseToken || pair.base}${quoteToken || pair.quote}`;
  };

  return { getPools, getTradingPairOptions, getPairId };
}
Example #15
Source File: LiquidityPoolTask.ts    From guardian with Apache License 2.0 5 votes vote down vote up
getSyntheticPools = (apiRx: ApiRx) => (poolId: number | number[] | 'all') => {
  const upcomingPools$ = apiRx.query.baseLiquidityPoolsForSynthetic.nextPoolId().pipe(
    pairwise(),
    filter(([, next]) => !next.isZero()),
    switchMap(([prev, next]) => range(prev.toNumber(), next.toNumber())),
    distinctUntilChanged(),
    mergeMap((poolId) =>
      combineLatest([
        of(poolId.toString()),
        apiRx.query.baseLiquidityPoolsForSynthetic.pools(poolId).pipe(
          filter((x) => x.isSome),
          map((x) => x.unwrap())
        )
      ])
    )
  );

  if (poolId === 'all') {
    return apiRx.query.baseLiquidityPoolsForSynthetic.pools.entries().pipe(
      mergeMap((x) => x),
      filter(([, value]) => value.isSome),
      mergeMap(
        ([
          {
            args: [poolId]
          },
          pool
        ]) => combineLatest([of(poolId.toString()), of(pool.unwrap())])
      ),
      concatWith(upcomingPools$)
    );
  } else {
    return of(castArray(poolId)).pipe(
      mergeMap((x) => x),
      switchMap((poolId) =>
        combineLatest([of(poolId.toString()), apiRx.query.baseLiquidityPoolsForSynthetic.pools(poolId)])
      ),
      filter(([, pool]) => pool.isSome),
      mergeMap(([poolId, value]) => combineLatest([of(poolId), of(value.unwrap())]))
    );
  }
}
Example #16
Source File: helpers.ts    From guardian with Apache License 2.0 5 votes vote down vote up
createAccountCurrencyIdPairs = <CurrencyId>(
  account: string | string[],
  currencyId: CurrencyId | CurrencyId[]
): { account: string; currencyId: CurrencyId }[] => {
  return castArray(account).flatMap((account) => castArray(currencyId).map((currencyId) => ({ account, currencyId })));
}
Example #17
Source File: SyntheticPoolsTask.ts    From guardian with Apache License 2.0 5 votes vote down vote up
private getPoolIds(ethereumApi: EthereumApi, poolId: string | string[]) {
    if (poolId === 'all') {
      return ethereumApi.synthetic.allPoolIds().pipe(concatAll());
    }
    return from(castArray(poolId));
  }
Example #18
Source File: PoolsTask.ts    From guardian with Apache License 2.0 5 votes vote down vote up
async start(guardian: AcalaGuardian) {
    const { apiRx, getTokenPrecision } = await guardian.isReady();

    const { currencyId } = this.arguments;

    const stableCoin = apiRx.consts.cdpEngine.getStableCurrencyId;
    const nativeCoin = apiRx.consts.currencies.getNativeCurrencyId;

    let pairs: TradingPair[];

    if (currencyId === 'all') {
      const tradingPair = await lastValueFrom(apiRx.query.dex.liquidityPool.entries().pipe(take(1)));

      pairs = tradingPair
        .map(([key, value]) => {
          const [balance1, balance2] = value;

          if (balance1 && balance2) {
            return key.args[0];
          }

          return undefined;
        })
        .filter((p): p is TradingPair => p !== undefined);
    } else {
      const currencies = castArray(currencyId);
      pairs = currencies
        .map((x) => apiRx.createType('CurrencyId', x))
        .map((currencyId) => {
          return currencyId.eq(nativeCoin)
            ? apiRx.createType('TradingPair', [currencyId, stableCoin])
            : apiRx.createType('TradingPair', [stableCoin, currencyId]);
        });
    }

    // ignore non-token currency
    // TODO: support any currency
    pairs = pairs.filter(([base, quote]) => {
      return base.isToken && quote.isToken && !base.eq(quote);
    });

    return from(pairs).pipe(
      mergeMap((pair) =>
        apiRx.query.dex.liquidityPool(pair).pipe(
          map(([base, other]) => {
            const [baseCurrency, otherCurrency] = pair;
            const basePrecision = getTokenPrecision(baseCurrency.asToken.toString());
            assert(basePrecision);
            const otherPrecision = getTokenPrecision(otherCurrency.asToken.toString());
            assert(otherPrecision);
            const _base = FixedPointNumber.fromInner(base.toString(), basePrecision);
            const _other = FixedPointNumber.fromInner(other.toString(), otherPrecision);
            const price = baseCurrency.eq(stableCoin) ? _base.div(_other) : _other.div(_base);
            price.setPrecision(18);
            return {
              currencyId: pair.toString(),
              price: price._getInner().toFixed(0),
              baseLiquidity: base.toString(),
              otherLiquidity: other.toString()
            };
          })
        )
      )
    );
  }
Example #19
Source File: CollateralAuctionsTask.ts    From guardian with Apache License 2.0 5 votes vote down vote up
async start(guardian: AcalaGuardian) {
    const { apiRx } = await guardian.isReady();

    const { account, currencyId } = this.arguments;

    let currencies: CurrencyId[] = [];
    const whitelist = apiRx.consts.cdpEngine.collateralCurrencyIds;

    // make sure provided currency id is whitelisted
    if (currencyId !== 'all') {
      currencies = castArray(currencyId).map((x) => apiRx.createType('CurrencyId', x));
      currencies.forEach((id) => {
        if (!whitelist.find((x) => x.eq(id))) throw Error('Collateral currency id not allowed!');
      });
    } else {
      currencies = whitelist;
    }

    const includesAccount = includesArgument<string>(account);
    const includesCurrency = includesArgument<CurrencyId>(currencies);

    const upcomingAuctions$ = apiRx.query.auction.auctionsIndex().pipe(
      pairwise(),
      filter(([, next]) => !next.isZero()),
      switchMap(([prev, next]) => range(prev.toNumber(), next.toNumber())),
      distinctUntilChanged(),
      mergeMap((auctionId) => {
        return combineLatest([
          of(auctionId),
          apiRx.query.auctionManager.collateralAuctions(auctionId),
          apiRx.query.auction.auctions(auctionId)
        ]);
      })
    );

    return apiRx.query.auctionManager.collateralAuctions.entries().pipe(
      mergeMap((entry) => entry),
      mergeMap((entry) => {
        const [storageKey, maybecollateralAuction] = entry;
        const [auctionId] = storageKey.args;
        return combineLatest([of(auctionId), of(maybecollateralAuction), apiRx.query.auction.auctions(auctionId)]);
      }),
      concatWith(upcomingAuctions$),
      filter(([, maybecollateralAuction, maybeAuction]) => {
        if (maybecollateralAuction.isNone) return false;
        if (maybeAuction.isNone) return false;

        const { refundRecipient, currencyId } = maybecollateralAuction.unwrap();

        if (!includesAccount(refundRecipient.toString())) return false;
        if (!includesCurrency(currencyId)) return false;

        return true;
      }),
      map(([auctionId, maybecollateralAuction, maybeAuction]) => {
        const collateralAuction = maybecollateralAuction.unwrap();
        const auction = maybeAuction.unwrap();

        const [lastBidder, lastBid] = auction.bid.isSome ? auction.bid.unwrap() : [];

        return {
          account: collateralAuction.refundRecipient.toString(),
          currencyId: collateralAuction.currencyId.asToken.toString(),
          auctionId: Number(auctionId.toString()),
          initialAmount: collateralAuction.initialAmount.toString(),
          amount: collateralAuction.amount.toString(),
          target: collateralAuction.target.toString(),
          startTime: Number(collateralAuction.startTime.toString()),
          endTime: auction.end.isSome ? Number(auction.end.toString()) : undefined,
          lastBidder: lastBidder && lastBidder.toString(),
          lastBid: lastBid && lastBid.toString()
        };
      }),
      drr()
    );
  }
Example #20
Source File: paramsSerializer.ts    From linkedin-private-api with MIT License 5 votes vote down vote up
encodeFilter = (value: string | string[], key: string) => encodeURIComponent(`${key}->${castArray(value).join('|')}`)
Example #21
Source File: LoansTask.ts    From guardian with Apache License 2.0 4 votes vote down vote up
async start(guardian: AcalaGuardian) {
    const { apiRx, getTokenPrecision } = await guardian.isReady();

    const { account, currencyId } = this.arguments;

    const stableCoin = apiRx.consts.cdpEngine.getStableCurrencyId;
    const stableCoinPrice = apiRx.consts.prices.stableCurrencyFixedPrice;
    const collateralCurrencyIds = apiRx.consts.cdpEngine.collateralCurrencyIds;

    let currencyIds: CurrencyId[];

    if (currencyId === 'all') {
      currencyIds = apiRx.createType<Vec<CurrencyId>>('Vec<CurrencyId>', collateralCurrencyIds);
    } else {
      currencyIds = castArray(currencyId).map((x) => apiRx.createType('CurrencyId', x));
    }

    // ignore non-token collaterals
    // TODO: support any currency
    currencyIds = currencyIds.filter((x) => x.isToken);

    // create {account, currencyId} paris
    const pairs = createAccountCurrencyIdPairs<CurrencyId>(account, currencyIds);

    const oraclePrice = getOraclePrice(apiRx);

    return from(pairs).pipe(
      mergeMap(({ currencyId, account }) =>
        apiRx.query.loans.positions(currencyId, account).pipe(
          mergeMap((position) => combineLatest([of(position), apiRx.query.cdpEngine.debitExchangeRate(currencyId)])),
          mergeMap(([position, debitExchangeRate]) => {
            const exchangeRate = debitExchangeRate.isSome
              ? debitExchangeRate.unwrap()
              : apiRx.consts.cdpEngine.defaultDebitExchangeRate;
            return combineLatest([of(position), of(exchangeRate), oraclePrice(currencyId)]);
          }),
          filter(([position, exchangeRate, collateralPrice]) => {
            const stableCoinPrecision = getTokenPrecision(stableCoin.asToken.toString());
            const collateralPrecision = getTokenPrecision(currencyId.asToken.toString());

            const collateral = FixedPointNumber.fromInner(position.collateral.toString(), collateralPrecision);
            const collateralUSD = FixedPointNumber.fromInner(collateralPrice).times(collateral);

            const debit = FixedPointNumber.fromInner(position.debit.toString(), stableCoinPrecision);
            if (debit.isZero()) return false;

            const debitsUSD = debit
              .times(FixedPointNumber.fromInner(exchangeRate.toString()))
              .times(FixedPointNumber.fromInner(stableCoinPrice.toString()));

            const collateralRatio = collateralUSD.div(debitsUSD);

            if (collateralRatio.isNaN()) return false;

            return true;
          }),
          map(([position, exchangeRate, collateralPrice]) => {
            const stableCoinPrecision = getTokenPrecision(stableCoin.asToken.toString());
            assert(stableCoinPrecision);
            const collateralPrecision = getTokenPrecision(currencyId.asToken.toString());
            assert(collateralPrecision);

            const collateral = FixedPointNumber.fromInner(position.collateral.toString(), collateralPrecision);
            const collateralUSD = FixedPointNumber.fromInner(collateralPrice).times(collateral);

            const debit = FixedPointNumber.fromInner(position.debit.toString(), stableCoinPrecision);

            const debitsUSD = debit
              .times(FixedPointNumber.fromInner(exchangeRate.toString()))
              .times(FixedPointNumber.fromInner(stableCoinPrice.toString()));

            const collateralRatio = collateralUSD.div(debitsUSD);

            // set precision back stable coin
            debitsUSD.setPrecision(stableCoinPrecision);

            return {
              account,
              currencyId: currencyId.toString(),
              debits: position.debit.toString(),
              debitsUSD: debitsUSD._getInner().toFixed(0),
              collaterals: position.collateral.toString(),
              collateralRatio: collateralRatio.toString()
            };
          })
        )
      )
    );
  }
Example #22
Source File: covertFormValueToEvent.ts    From next-basics with GNU General Public License v3.0 4 votes vote down vote up
export function covertFormValueToEvent(
  formValue: EventFormField
): BrickEventHandler {
  if (formValue.handlerType === HandlerType.BuiltinAction) {
    //特殊处理 history.push / segueId.push
    if (["segue.push", "history.push"].includes(formValue.action)) {
      const restArgs =
        formValue.args !== undefined
          ? safeLoadField(formValue.args, "args")
          : [];
      return {
        action: formValue.action as string,
        ...safeLoadFields({
          if: formValue.if,
          callback: formValue.callback,
        }),
        args: [
          formValue.action === "segue.push"
            ? formValue.segueId
            : formValue.path,
          ...(isNil(restArgs) ? [] : castArray(restArgs)),
        ],
      } as BuiltinBrickEventHandler;
    }

    return {
      action: formValue.action,
      ...safeLoadFields({
        if: formValue.if,
        args: formValue.args,
        callback: formValue.callback,
      }),
    } as BuiltinBrickEventHandler;
  } else if (formValue.handlerType === HandlerType.UseProvider) {
    const useProvider =
      formValue.providerType === "provider"
        ? formValue.provider
        : formValue.flow;

    const loadFields = safeLoadFields({
      if: formValue.if,
      args: formValue.args,
      poll: formValue.poll,
      callback: formValue.callback,
    });

    if (loadFields.poll && formValue.pollEnabled) {
      set(loadFields, "poll.enabled", formValue.pollEnabled);
    }

    return {
      useProvider,
      ...(formValue.useProviderMethod === "resolve"
        ? {}
        : { method: formValue.useProviderMethod }),
      ...loadFields,
    } as UseProviderEventHandler;
  } else if (
    formValue.handlerType === HandlerType.CustomBrick &&
    formValue.brickEventType === CustomBrickEventType.ExecuteMethod
  ) {
    return {
      ...(formValue.selectorType === "target"
        ? { target: formValue.brickSelector }
        : { targetRef: formValue.brickSelector }),
      method: formValue.method,
      ...safeLoadFields({
        if: formValue.if,
        args: formValue.args,
        callback: formValue.callback,
      }),
    } as ExecuteCustomBrickEventHandler;
  } else if (
    formValue.handlerType === HandlerType.CustomBrick &&
    formValue.brickEventType === CustomBrickEventType.SetProps
  ) {
    return {
      ...(formValue.selectorType === "target"
        ? { target: formValue.brickSelector }
        : { targetRef: formValue.brickSelector }),
      ...safeLoadFields({
        if: formValue.if,
        properties: formValue.properties,
      }),
    } as SetPropsCustomBrickEventHandler;
  }
}
Example #23
Source File: input-type.ts    From prisma-nestjs-graphql with MIT License 4 votes vote down vote up
export function inputType(
  args: EventArguments & {
    inputType: InputType;
    fileType: string;
    classDecoratorName: string;
  },
) {
  const {
    classDecoratorName,
    classTransformerTypeModels,
    config,
    eventEmitter,
    fieldSettings,
    fileType,
    getModelName,
    getSourceFile,
    inputType,
    models,
    removeTypes,
    typeNames,
  } = args;

  typeNames.add(inputType.name);

  const importDeclarations = new ImportDeclarationMap();
  const sourceFile = getSourceFile({
    name: inputType.name,
    type: fileType,
  });
  const classStructure: ClassDeclarationStructure = {
    kind: StructureKind.Class,
    isExported: true,
    name: inputType.name,
    decorators: [
      {
        name: classDecoratorName,
        arguments: [],
      },
    ],
    properties: [],
  };
  const modelName = getModelName(inputType.name) || '';
  const model = models.get(modelName);
  const modelFieldSettings = model && fieldSettings.get(model.name);
  const moduleSpecifier = '@nestjs/graphql';

  // console.log('sourceFile.getBaseName()', sourceFile.getBaseName());

  importDeclarations
    .set('Field', {
      namedImports: [{ name: 'Field' }],
      moduleSpecifier,
    })
    .set(classDecoratorName, {
      namedImports: [{ name: classDecoratorName }],
      moduleSpecifier,
    });

  const useInputType = config.useInputType.find(x =>
    inputType.name.includes(x.typeName),
  );

  for (const field of inputType.fields) {
    field.inputTypes = field.inputTypes.filter(t => !removeTypes.has(String(t.type)));
    eventEmitter.emitSync('BeforeGenerateField', field, args);

    const { inputTypes, isRequired, name } = field;

    if (inputTypes.length === 0) {
      // No types
      continue;
    }

    const usePattern = useInputType?.ALL || useInputType?.[name];
    const graphqlInputType = getGraphqlInputType(inputTypes, usePattern);
    const { isList, location, type } = graphqlInputType;
    const typeName = String(type);
    const settings = modelFieldSettings?.get(name);
    const propertySettings = settings?.getPropertyType({
      name: inputType.name,
      input: true,
    });
    const modelField = model?.fields.find(f => f.name === name);
    const isCustomsApplicable = typeName === modelField?.type;
    const propertyType = castArray(
      propertySettings?.name ||
        getPropertyType({
          location,
          type: typeName,
        }),
    );
    const property = propertyStructure({
      name,
      isNullable: !isRequired,
      propertyType,
      isList,
    });
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    classStructure.properties!.push(property);

    if (propertySettings) {
      importDeclarations.create({ ...propertySettings });
    } else if (propertyType.includes('Decimal')) {
      importDeclarations.add('Decimal', '@prisma/client/runtime');
    }

    // Get graphql type
    let graphqlType: string;
    const shouldHideField =
      settings?.shouldHideField({
        name: inputType.name,
        input: true,
      }) ||
      config.decorate.some(
        d =>
          d.name === 'HideField' &&
          d.from === '@nestjs/graphql' &&
          d.isMatchField(name) &&
          d.isMatchType(inputType.name),
      );

    const fieldType = settings?.getFieldType({
      name: inputType.name,
      input: true,
    });

    if (fieldType && isCustomsApplicable && !shouldHideField) {
      graphqlType = fieldType.name;
      importDeclarations.create({ ...fieldType });
    } else {
      // Import property type class
      const graphqlImport = getGraphqlImport({
        config,
        sourceFile,
        location,
        typeName,
        getSourceFile,
      });

      graphqlType = graphqlImport.name;
      let referenceName = propertyType[0];
      if (location === 'enumTypes') {
        referenceName = last(referenceName.split(' ')) as string;
      }

      if (
        graphqlImport.specifier &&
        !importDeclarations.has(graphqlImport.name) &&
        graphqlImport.name !== inputType.name
        // ((graphqlImport.name !== inputType.name && !shouldHideField) ||
        //     (shouldHideField && referenceName === graphqlImport.name))
      ) {
        importDeclarations.set(graphqlImport.name, {
          namedImports: [{ name: graphqlImport.name }],
          moduleSpecifier: graphqlImport.specifier,
        });
      }
    }

    ok(property.decorators, 'property.decorators is undefined');

    if (shouldHideField) {
      importDeclarations.add('HideField', '@nestjs/graphql');
      property.decorators.push({ name: 'HideField', arguments: [] });
    } else {
      // Generate `@Field()` decorator
      property.decorators.push({
        name: 'Field',
        arguments: [
          isList ? `() => [${graphqlType}]` : `() => ${graphqlType}`,
          JSON5.stringify({
            ...settings?.fieldArguments(),
            nullable: !isRequired,
          }),
        ],
      });

      // Debug
      // if (classStructure.name === 'XCreateInput') {
      //   console.log({
      //     field,
      //     property,
      //     modelField,
      //     graphqlInputType,
      //     'args.inputType': args.inputType,
      //     'classStructure.name': classStructure.name,
      //     classTransformerTypeModels,
      //     modelName,
      //     graphqlType,
      //   });
      // }

      if (graphqlType === 'GraphQLDecimal') {
        importDeclarations.add('transformToDecimal', 'prisma-graphql-type-decimal');
        importDeclarations.add('Transform', 'class-transformer');
        importDeclarations.add('Type', 'class-transformer');

        property.decorators.push(
          {
            name: 'Type',
            arguments: ['() => Object'],
          },
          {
            name: 'Transform',
            arguments: ['transformToDecimal'],
          },
        );
      } else if (
        location === 'inputObjectTypes' &&
        (modelField?.type === 'Decimal' ||
          [
            'connect',
            'connectOrCreate',
            'create',
            'createMany',
            'data',
            'delete',
            'deleteMany',
            'disconnect',
            'set',
            'update',
            'updateMany',
            'upsert',
            'where',
          ].includes(name) ||
          classTransformerTypeModels.has(getModelName(graphqlType) || ''))
      ) {
        importDeclarations.add('Type', 'class-transformer');
        property.decorators.push({ name: 'Type', arguments: [`() => ${graphqlType}`] });
      }

      if (isCustomsApplicable) {
        for (const options of settings || []) {
          if (
            (options.kind === 'Decorator' && options.input && options.match?.(name)) ??
            true
          ) {
            property.decorators.push({
              name: options.name,
              arguments: options.arguments as string[],
            });
            ok(options.from, "Missed 'from' part in configuration or field setting");
            importDeclarations.create(options);
          }
        }
      }

      for (const decorate of config.decorate) {
        if (decorate.isMatchField(name) && decorate.isMatchType(inputType.name)) {
          property.decorators.push({
            name: decorate.name,
            arguments: decorate.arguments?.map(x => pupa(x, { propertyType })),
          });
          importDeclarations.create(decorate);
        }
      }
    }

    eventEmitter.emitSync('ClassProperty', property, {
      location,
      isList,
      propertyType,
    });
  }

  sourceFile.set({
    statements: [...importDeclarations.toStatements(), classStructure],
  });
}
Example #24
Source File: model-output-type.ts    From prisma-nestjs-graphql with MIT License 4 votes vote down vote up
export function modelOutputType(outputType: OutputType, args: EventArguments) {
  const { getSourceFile, models, config, modelFields, fieldSettings, eventEmitter } =
    args;
  const model = models.get(outputType.name);
  ok(model, `Cannot find model by name ${outputType.name}`);

  const sourceFile = getSourceFile({
    name: outputType.name,
    type: 'model',
  });
  const sourceFileStructure = sourceFile.getStructure();
  const exportDeclaration = getExportDeclaration(
    model.name,
    sourceFileStructure.statements as StatementStructures[],
  );
  const importDeclarations = new ImportDeclarationMap();
  const classStructure: ClassDeclarationStructure = {
    kind: StructureKind.Class,
    isExported: true,
    name: outputType.name,
    decorators: [
      {
        name: 'ObjectType',
        arguments: [],
      },
    ],
    properties: [],
  };
  (sourceFileStructure.statements as StatementStructures[]).push(classStructure);
  ok(classStructure.decorators, 'classStructure.decorators is undefined');
  const decorator = classStructure.decorators.find(d => d.name === 'ObjectType');
  ok(decorator, 'ObjectType decorator not found');

  let modelSettings: ObjectSettings | undefined;
  // Get model settings from documentation
  if (model.documentation) {
    const objectTypeOptions: PlainObject = {};
    const { documentation, settings } = createObjectSettings({
      text: model.documentation,
      config,
    });
    if (documentation) {
      if (!classStructure.leadingTrivia) {
        classStructure.leadingTrivia = createComment(documentation);
      }
      objectTypeOptions.description = documentation;
    }
    decorator.arguments = settings.getObjectTypeArguments(objectTypeOptions);
    modelSettings = settings;
  }

  importDeclarations.add('Field', nestjsGraphql);
  importDeclarations.add('ObjectType', nestjsGraphql);

  for (const field of outputType.fields) {
    let fileType = 'model';
    const { location, isList, type, namespace } = field.outputType;

    let outputTypeName = String(type);
    if (namespace !== 'model') {
      fileType = 'output';
      outputTypeName = getOutputTypeName(outputTypeName);
    }
    const modelField = modelFields.get(model.name)?.get(field.name);
    const settings = fieldSettings.get(model.name)?.get(field.name);
    const fieldType = settings?.getFieldType({
      name: outputType.name,
      output: true,
    });
    const propertySettings = settings?.getPropertyType({
      name: outputType.name,
      output: true,
    });

    const propertyType = castArray(
      propertySettings?.name ||
        getPropertyType({
          location,
          type: outputTypeName,
        }),
    );

    // For model we keep only one type
    propertyType.splice(1, propertyType.length);

    if (field.isNullable && !isList) {
      propertyType.push('null');
    }

    let graphqlType: string;

    if (fieldType) {
      graphqlType = fieldType.name;
      importDeclarations.create({ ...fieldType });
    } else {
      const graphqlImport = getGraphqlImport({
        config,
        sourceFile,
        fileType,
        location,
        isId: modelField?.isId,
        noTypeId: config.noTypeId,
        typeName: outputTypeName,
        getSourceFile,
      });

      graphqlType = graphqlImport.name;

      if (graphqlImport.name !== outputType.name && graphqlImport.specifier) {
        importDeclarations.add(graphqlImport.name, graphqlImport.specifier);
      }
    }

    const property = propertyStructure({
      name: field.name,
      isNullable: field.isNullable,
      hasExclamationToken: true,
      hasQuestionToken: location === 'outputObjectTypes',
      propertyType,
      isList,
    });

    if (typeof property.leadingTrivia === 'string' && modelField?.documentation) {
      property.leadingTrivia += createComment(modelField.documentation, settings);
    }

    classStructure.properties?.push(property);

    if (propertySettings) {
      importDeclarations.create({ ...propertySettings });
    } else if (propertyType.includes('Decimal')) {
      importDeclarations.add('Decimal', '@prisma/client/runtime');
    }

    ok(property.decorators, 'property.decorators is undefined');

    if (settings?.shouldHideField({ name: outputType.name, output: true })) {
      importDeclarations.add('HideField', nestjsGraphql);
      property.decorators.push({ name: 'HideField', arguments: [] });
    } else {
      // Generate `@Field()` decorator
      property.decorators.push({
        name: 'Field',
        arguments: [
          isList ? `() => [${graphqlType}]` : `() => ${graphqlType}`,
          JSON5.stringify({
            ...settings?.fieldArguments(),
            nullable: Boolean(field.isNullable),
            defaultValue: ['number', 'string', 'boolean'].includes(
              typeof modelField?.default,
            )
              ? modelField?.default
              : undefined,
            description: modelField?.documentation,
          }),
        ],
      });

      for (const setting of settings || []) {
        if (shouldBeDecorated(setting) && (setting.match?.(field.name) ?? true)) {
          property.decorators.push({
            name: setting.name,
            arguments: setting.arguments as string[],
          });
          ok(setting.from, "Missed 'from' part in configuration or field setting");
          importDeclarations.create(setting);
        }
      }

      for (const decorate of config.decorate) {
        if (decorate.isMatchField(field.name) && decorate.isMatchType(outputTypeName)) {
          property.decorators.push({
            name: decorate.name,
            arguments: decorate.arguments?.map(x => pupa(x, { propertyType })),
          });
          importDeclarations.create(decorate);
        }
      }
    }

    eventEmitter.emitSync('ClassProperty', property, {
      location,
      isList,
      propertyType,
    });
  }

  // Generate class decorators from model settings
  for (const setting of modelSettings || []) {
    // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
    if (shouldBeDecorated(setting)) {
      classStructure.decorators.push({
        name: setting.name,
        arguments: setting.arguments as string[],
      });
      importDeclarations.create(setting);
    }
  }

  if (exportDeclaration) {
    sourceFile.set({
      statements: [exportDeclaration, '\n', classStructure],
    });
    const classDeclaration = sourceFile.getClassOrThrow(model.name);
    const commentedText = classDeclaration
      .getText()
      .split('\n')
      .map(x => `// ${x}`);
    classDeclaration.remove();
    sourceFile.addStatements(['\n', ...commentedText]);
  } else {
    sourceFile.set({
      statements: [...importDeclarations.toStatements(), classStructure],
    });
  }
}
Example #25
Source File: output-type.ts    From prisma-nestjs-graphql with MIT License 4 votes vote down vote up
export function outputType(outputType: OutputType, args: EventArguments) {
  const { getSourceFile, models, eventEmitter, fieldSettings, getModelName, config } =
    args;
  const importDeclarations = new ImportDeclarationMap();

  const fileType = 'output';
  const modelName = getModelName(outputType.name) || '';
  const model = models.get(modelName);
  const isAggregateOutput =
    model &&
    /(?:Count|Avg|Sum|Min|Max)AggregateOutputType$/.test(outputType.name) &&
    String(outputType.name).startsWith(model.name);
  const isCountOutput =
    model?.name && outputType.name === `${model.name}CountOutputType`;
  // Get rid of bogus suffixes
  outputType.name = getOutputTypeName(outputType.name);

  if (isAggregateOutput) {
    eventEmitter.emitSync('AggregateOutput', { ...args, outputType });
  }

  const sourceFile = getSourceFile({
    name: outputType.name,
    type: fileType,
  });

  const classStructure: ClassDeclarationStructure = {
    kind: StructureKind.Class,
    isExported: true,
    name: outputType.name,
    decorators: [
      {
        name: 'ObjectType',
        arguments: [],
      },
    ],
    properties: [],
  };

  importDeclarations.add('Field', nestjsGraphql);
  importDeclarations.add('ObjectType', nestjsGraphql);

  for (const field of outputType.fields) {
    const { location, isList, type } = field.outputType;
    const outputTypeName = getOutputTypeName(String(type));
    const settings = isCountOutput
      ? undefined
      : model && fieldSettings.get(model.name)?.get(field.name);
    const propertySettings = settings?.getPropertyType({
      name: outputType.name,
      output: true,
    });
    const isCustomsApplicable =
      outputTypeName === model?.fields.find(f => f.name === field.name)?.type;

    field.outputType.type = outputTypeName;

    const propertyType = castArray(
      propertySettings?.name ||
        getPropertyType({
          location,
          type: outputTypeName,
        }),
    );

    const property = propertyStructure({
      name: field.name,
      isNullable: field.isNullable,
      hasQuestionToken: isCountOutput ? true : undefined,
      propertyType,
      isList,
    });

    classStructure.properties?.push(property);

    if (propertySettings) {
      importDeclarations.create({ ...propertySettings });
    } else if (propertyType.includes('Decimal')) {
      importDeclarations.add('Decimal', '@prisma/client/runtime');
    }

    // Get graphql type
    let graphqlType: string;
    const shouldHideField =
      settings?.shouldHideField({
        name: outputType.name,
        output: true,
      }) ||
      config.decorate.some(
        d =>
          d.name === 'HideField' &&
          d.from === '@nestjs/graphql' &&
          d.isMatchField(field.name) &&
          d.isMatchType(outputTypeName),
      );

    const fieldType = settings?.getFieldType({
      name: outputType.name,
      output: true,
    });

    if (fieldType && isCustomsApplicable && !shouldHideField) {
      graphqlType = fieldType.name;
      importDeclarations.create({ ...fieldType });
    } else {
      const graphqlImport = getGraphqlImport({
        config,
        sourceFile,
        fileType,
        location,
        isId: false,
        typeName: outputTypeName,
        getSourceFile,
      });

      graphqlType = graphqlImport.name;
      let referenceName = propertyType[0];
      if (location === 'enumTypes') {
        referenceName = last(referenceName.split(' ')) as string;
      }

      if (
        graphqlImport.specifier &&
        !importDeclarations.has(graphqlImport.name) &&
        ((graphqlImport.name !== outputType.name && !shouldHideField) ||
          (shouldHideField && referenceName === graphqlImport.name))
      ) {
        importDeclarations.set(graphqlImport.name, {
          namedImports: [{ name: graphqlImport.name }],
          moduleSpecifier: graphqlImport.specifier,
        });
      }
    }

    ok(property.decorators, 'property.decorators is undefined');

    if (shouldHideField) {
      importDeclarations.add('HideField', nestjsGraphql);
      property.decorators.push({ name: 'HideField', arguments: [] });
    } else {
      // Generate `@Field()` decorator
      property.decorators.push({
        name: 'Field',
        arguments: [
          isList ? `() => [${graphqlType}]` : `() => ${graphqlType}`,
          JSON5.stringify({
            ...settings?.fieldArguments(),
            nullable: Boolean(field.isNullable),
          }),
        ],
      });

      if (isCustomsApplicable) {
        for (const options of settings || []) {
          if (
            (options.kind === 'Decorator' &&
              options.output &&
              options.match?.(field.name)) ??
            true
          ) {
            property.decorators.push({
              name: options.name,
              arguments: options.arguments as string[],
            });
            ok(options.from, "Missed 'from' part in configuration or field setting");
            importDeclarations.create(options);
          }
        }
      }
    }

    eventEmitter.emitSync('ClassProperty', property, {
      location,
      isList,
      propertyType,
    });
  }

  sourceFile.set({
    statements: [...importDeclarations.toStatements(), classStructure],
  });
}
Example #26
Source File: CmdbInstanceSelect.tsx    From next-basics with GNU General Public License v3.0 4 votes vote down vote up
export function CmdbInstanceSelectItem(
  props: CmdbInstanceSelectProps,
  ref: any
): React.ReactElement {
  const {
    showKeyField,
    // 默认显示 label 为模型的 name/hostname, value 为 instanceId
    // 当showKeyField时,实例展示是用showKey里的数据展示
    fields = {
      label: [showKeyField ? "#showKey" : getInstanceNameKey(props.objectId)],
      value: "instanceId",
    },

    minimumInputLength = 0,
    extraSearchKey = [],
    extraFields = [],
    mode,
    placeholder,
    allowClear,
    pageSize,
    showSearchTip,
    permission,
    ignoreMissingFieldError,
  } = props;
  const userQuery = formatUserQuery(props.instanceQuery);
  //istanbul ignore else
  if (!fields.value) {
    fields.value = "instanceId";
  }

  const [value, setValue] = React.useState();
  const [options, setOptions] = React.useState<ComplexOption[]>([]);
  const [selectedOptions, setSelectedOptions] = React.useState<ComplexOption[]>(
    []
  );
  const [total, setTotal] = React.useState(0);
  const [loading, setLoading] = React.useState(false);
  const computeFields = () => {
    const result = [
      fields.value,
      ...(Array.isArray(fields.label) ? fields.label : [fields.label]),
      ...extraSearchKey,
      ...extraFields,
    ];

    if (props.objectId === "USER") {
      result.push("user_icon");
    }
    return result;
  };

  const handleChange = (newValue: any): void => {
    let selected: any | any[];
    if (mode === "multiple") {
      const valueSet = new Set(newValue);
      selected = options.filter((item) => valueSet.has(item.value));
      const oldValueSet = new Set(
        difference(
          newValue,
          selected.map((item) => item.value)
        )
      );
      selected = selected.concat(
        selectedOptions.filter((item) => oldValueSet.has(item.value))
      );
    } else {
      selected = options.find((item) => item.value === newValue);
    }
    setValue(newValue);
    setSelectedOptions(selected);
    props.onChange && props.onChange(newValue, selected);
  };
  //istanbul ignore else
  const handleSearch = async (
    q: string,
    extraQuery: any,
    forceSearch = false,
    pageSizeQuery?: number
  ): Promise<ComplexOption[]> => {
    if (forceSearch || q.length >= minimumInputLength) {
      try {
        let list = [];
        if (!props.objectId) {
          return;
        }
        setLoading(true);
        const fieldsQuery = Array.isArray(fields.label)
          ? fields.label.map((label) => ({ [label]: { $like: `%${q}%` } }))
          : [{ [fields.label]: { $like: `%${q}%` } }];
        const data = await InstanceApi_postSearchV3(props.objectId, {
          query: {
            $and: [
              {
                $or: [
                  ...fieldsQuery,
                  ...extraSearchKey.map((key) => ({
                    [key]: { $like: `%${q}%` },
                  })),
                ],
              },

              ...extraQuery,
            ],
          },
          ...(permission ? { permission } : {}),
          fields: computeFields(),
          page_size: pageSizeQuery || pageSize,
          ignore_missing_field_error: ignoreMissingFieldError,
        });

        list = data.list;
        setTotal(data.total);
        // 根据用户设置路径显示特定的 label 和 value
        const option = list.map((item) => ({
          ...item,
          label: Array.isArray(fields.label)
            ? fields.label.map((label) => get(item, label))
            : get(item, fields.label),
          value: get(item, fields.value),
          ...(props.objectId === "USER"
            ? {
                user_icon: get(item, "user_icon", "defaultIcon"),
              }
            : {}),
        }));
        setOptions(option);
        return option;
      } catch (e) {
        handleHttpError(e);
      } finally {
        setLoading(false);
      }
    }
  };
  const fetchInstanceData = async (): Promise<void> => {
    await handleSearch("", userQuery);
  };
  const getLabelOptions = (op: any) => {
    if (props.labelTemplate) {
      return parseTemplate(props.labelTemplate, op);
    } else {
      const label = op.label;
      if (Array.isArray(label)) {
        const firstKey = label[0];
        const resKey = label.slice(1, label.length).join(",");
        if (Array.isArray(firstKey) && props.showKeyField) {
          const subFirstKey = firstKey[0];
          const subResKey = firstKey.slice(1, firstKey.length).join(",");
          return subResKey && props.isMultiLabel
            ? `${subFirstKey}(${subResKey})`
            : subFirstKey ?? "";
        }
        return resKey && props.isMultiLabel
          ? `${firstKey}(${resKey})`
          : firstKey ?? "";
      } else {
        return label;
      }
    }
  };

  React.useEffect(() => {
    // 初始化时通过用户的 value 得出首次 label 的值
    // 由于value的不确定性,可能存在首次查询的值不唯一,初始化时也添加instanceQuery
    (async () => {
      if (!isEqual(props.value, value) && props.value !== undefined) {
        const option = await handleSearch(
          "",
          [
            {
              [fields.value || "instanceId"]: {
                $in: castArray(props.value),
              },
            },

            ...userQuery,
          ],
          true,
          props.value?.length >= pageSize ? props.value.length : pageSize
        );
        setSelectedOptions(option);
      }
      setValue(props.value);
    })();
  }, [props.value]);

  React.useEffect(() => {
    if (!props.firstRender) {
      const resetVal: [] | "" = mode === "multiple" ? [] : "";
      setValue(resetVal);
    }
  }, [props.objectId]);
  //istanbul ignore else
  const debounceSearch = debounce(
    (q: string) => handleSearch(q, userQuery),
    500
  );

  return (
    <Spin spinning={loading}>
      <Select
        ref={ref}
        allowClear={allowClear}
        style={defaults(props.inputBoxStyle, { width: "100%" })}
        showSearch
        filterOption={false}
        value={value}
        mode={mode as ModeOption}
        placeholder={
          placeholder || i18n.t(`${NS_FORMS}:${K.BACKGROUND_SEARCH}`)
        }
        onChange={handleChange}
        onSearch={debounceSearch}
        onFocus={fetchInstanceData}
        optionLabelProp="label"
        disabled={props.disabled}
        dropdownRender={(menu) => {
          return (
            <div>
              {menu}
              {showSearchTip && total > pageSize && (
                <div className={style.moreChoices}>
                  仅显示前{pageSize}项,更多结果请搜索
                </div>
              )}
            </div>
          );
        }}
        {...(props.popoverPositionType === "parent"
          ? { getPopupContainer: (triggerNode) => triggerNode.parentElement }
          : {})}
      >
        {options.map((op, index) => {
          const optionLabel = getLabelOptions(op);
          return (
            <Select.Option key={index} value={op.value} label={optionLabel}>
              {props.showTooltip ? (
                <Tooltip title={optionLabel}>
                  {op.user_icon && (
                    <Avatar
                      src={op.user_icon}
                      size={24}
                      className={classNames(style.avatar, {
                        [style.defaultIcon]: op.user_icon === "defaultIcon",
                      })}
                    >
                      {op.user_icon === "defaultIcon" && op.label?.slice(0, 2)}
                    </Avatar>
                  )}
                  {optionLabel}
                </Tooltip>
              ) : (
                <>
                  {op.user_icon && (
                    <Avatar
                      src={op.user_icon}
                      size={24}
                      className={classNames(style.avatar, {
                        [style.defaultIcon]: op.user_icon === "defaultIcon",
                      })}
                    >
                      {op.user_icon === "defaultIcon" && op.label?.slice(0, 2)}
                    </Avatar>
                  )}
                  {optionLabel}
                </>
              )}
            </Select.Option>
          );
        })}
      </Select>
    </Spin>
  );
}