lodash#last TypeScript Examples

The following examples show how to use lodash#last. 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: time-series.utils.ts    From aqualink-app with MIT License 7 votes vote down vote up
groupByMetricAndSource = <T extends TimeSeriesGroupable>(
  data: T[],
): TimeSeriesResponse<
  Omit<T, 'metric' | 'source' | 'surveyPointId' | 'surveyPointName'>
> => {
  return _(data)
    .groupBy('metric')
    .mapValues((grouped) => {
      return _(grouped)
        .groupBy('source')
        .mapValues((groupedData) => {
          const { surveyPointId, surveyPointName } = last(groupedData) || {};
          return merge(
            !isNull(surveyPointId)
              ? { surveyPoint: { id: surveyPointId, name: surveyPointName } }
              : {},
            {
              data: groupedData
                .filter((o) =>
                  typeof surveyPointId === 'number'
                    ? surveyPointId === o.surveyPointId
                    : true,
                )
                .map((o) =>
                  omit(
                    o,
                    'metric',
                    'source',
                    'surveyPointId',
                    'surveyPointName',
                  ),
                ),
            },
          );
        })
        .toJSON();
    })
    .toJSON();
}
Example #2
Source File: api-editor.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
processTemp =
  (execOperation: Function) =>
  (temp = []) => {
    return map(temp, (item) => {
      const { render } = item;
      if (render?.type === 'inputSelect') {
        const p = {} as Obj;
        const { operations: rOps, valueConvertType, props: rProps, ...renderRest } = render || {};
        if (rOps?.onSelectOptionParams) {
          p.onLoadData = (_selectOpt: any) => {
            execOperation(rOps.onSelectOptionParams, _selectOpt);
          };
        }
        p.valueConvert = (str: string[]) => {
          let v = str.join('');
          switch (valueConvertType) {
            case 'last':
              v = last(str) as string;
              break;
            default:
              break;
          }
          return v;
        };

        return {
          ...item,
          render: { ...renderRest, props: { ...rProps, ...p } },
        };
      }
      return { ...item };
    });
  }
Example #3
Source File: SearchIndexer.ts    From advocacy-maps with MIT License 6 votes vote down vote up
private async *listCollection() {
    let token: string | undefined = ""
    while (token !== undefined) {
      const result: QuerySnapshot = await this.config.sourceCollection
        .orderBy(this.config.idField)
        .startAfter(token)
        .limit(this.batchSize)
        .get()

      const docs = result.docs.filter(d => d.exists)
      token = last(docs)?.id
      if (docs.length) yield docs
    }
  }
Example #4
Source File: index.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
getValueByOptions = (options: IOption[], value: IOption[]) => {
  const reValue = [] as IOption[];
  map(value, (vItem) => {
    if (reValue.length === 0) {
      reValue.push(find(options, { value: vItem.value }) as IOption);
    } else {
      const lastVal = last(reValue) as IOption;
      lastVal && reValue.push(find(lastVal.children, { value: vItem.value }) as IOption);
    }
  });
  return compact(reValue);
}
Example #5
Source File: addresses.ts    From nautilus-wallet with MIT License 6 votes vote down vote up
export function getChangeAddress(
  outputs: ErgoBoxCandidate[],
  ownAddresses: string[]
): string | undefined {
  const addresses = outputs
    .filter((o) => o.ergoTree.startsWith(P2PK_TREE_PREFIX))
    .map((o) => addressFromPk(o.ergoTree))
    .filter((a) => ownAddresses.includes(a));

  return last(addresses);
}
Example #6
Source File: useSecondsLeft.ts    From fishbowl with MIT License 6 votes vote down vote up
export default function useSecondsLeft(
  activeTurnPlayState: ActiveTurnPlayState
): number {
  const { serverTimeOffset } = useContext(CurrentPlayerContext)
  const currentGame = useContext(CurrentGameContext)
  const activeTurn = last(currentGame.turns)
  const startingSeconds =
    activeTurn?.seconds_per_turn_override || currentGame.seconds_per_turn || 0

  const [secondsLeft, setSecondsLeft] = React.useState(
    calculateSecondsLeft(startingSeconds, serverTimeOffset, activeTurn)
  )
  useEffect(() => {
    setSecondsLeft(
      calculateSecondsLeft(startingSeconds, serverTimeOffset, activeTurn)
    )
  }, [startingSeconds, serverTimeOffset, activeTurn])

  // countdown timer
  useInterval(() => {
    if (
      activeTurnPlayState === ActiveTurnPlayState.Playing &&
      activeTurn?.started_at &&
      secondsLeft >= 1
    ) {
      setSecondsLeft(
        calculateSecondsLeft(startingSeconds, serverTimeOffset, activeTurn)
      )
    }
  }, INTERVAL_DELAY)

  return secondsLeft
}
Example #7
Source File: index.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
render() {
    const { content, query, style = {}, hasLogs = true, ...otherProps } = this.props;
    const { rolling, backwardLoading, downloadLogModalVisible } = this.state;
    const lastItem = last(content);
    const realHaveLog = hasLogs && !!lastItem;

    return (
      <div className="log-viewer" style={style}>
        <PureLogRoller
          ref={(ref) => {
            this.logRoller = ref;
          }}
          content={content}
          onStartRolling={this.startRolling}
          onCancelRolling={this.cancelRolling}
          onGoToTop={this.goToTop}
          onGoToBottom={this.scrollToBottom}
          rolling={rolling}
          backwardLoading={backwardLoading}
          onShowDownloadModal={this.toggleDownloadModal}
          hasLogs={realHaveLog}
          {...otherProps}
        />
        {realHaveLog && (
          <DownloadLogModal
            visible={downloadLogModalVisible}
            start={lastItem ? lastItem.timestamp : 0}
            query={query}
            onCancel={this.toggleDownloadModal}
          />
        )}
      </div>
    );
  }
Example #8
Source File: index.tsx    From aqualink-app with MIT License 6 votes vote down vote up
metrics = (data?: OceanSenseData): Record<OceanSenseKeys, Metric> => ({
  PH: {
    label: "ACIDITY",
    value: formatNumber(last(data?.PH)?.value, 2),
    measure: "pH",
    icon: <AcidityIcon />,
  },
  EC: {
    label: "CONDUCTIVITY",
    value: formatNumber(last(data?.EC)?.value, 2),
    measure: "\u00B5S",
    icon: <ConductivityIcon />,
  },
  PRESS: {
    label: "PRESSURE",
    value: formatNumber(last(data?.PRESS)?.value, 2),
    measure: "dbar",
    icon: <PressureIcon />,
  },
  DO: {
    label: "DISSOLVED OXYGEN",
    value: formatNumber(last(data?.DO)?.value, 2),
    measure: "mg/L",
    icon: <DissolvedOxygenIcon />,
  },
  ORP: {
    label: "ORP",
    value: formatNumber(last(data?.ORP)?.value, 2),
    measure: "mV",
    icon: <OrpIcon />,
  },
})
Example #9
Source File: house.tsx    From S2 with MIT License 6 votes vote down vote up
defaultSortParams = [
  {
    sortFieldId: 'name',
    sortMethod: 'ASC',
  },
  {
    sortFieldId: 'unit',
    sortMethod: 'ASC',
  },
  {
    sortFieldId: 'level',
    sortFunc: (params) => {
      const { data } = params;
      return data.sort((a, b) => {
        const aNum = last(a.split(ID_SEPARATOR));
        const bNum = last(b.split(ID_SEPARATOR));
        return aNum - bNum;
      });
    },
  },
]
Example #10
Source File: tab.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
render() {
    const { routes, path } = this.props;
    const lastRouteWithPath = find(routes, 'relativePath');
    if (lastRouteWithPath) {
      const lastPathPart = last(path.split('/')) || '';
      const {
        relativePath,
        tabs,
        ignoreTabQuery,
        keepTabQuery,
        tabKey = lastPathPart,
        alwaysShowTabKey,
        TabRightComp,
      } = lastRouteWithPath;
      // 如果最后一级路径有:号,则不用严格匹配
      if (alwaysShowTabKey || ((relativePath === lastPathPart || relativePath.includes(':')) && tabs)) {
        return (
          <>
            <Menu
              activeKey={alwaysShowTabKey || tabKey}
              menus={tabs}
              TabRightComp={TabRightComp}
              ignoreTabQuery={ignoreTabQuery}
              keepTabQuery={keepTabQuery}
            />
          </>
        );
      }
    }
    return null;
  }
Example #11
Source File: pivot-facet.ts    From S2 with MIT License 6 votes vote down vote up
public getViewCellHeights(layoutResult: LayoutResult) {
    const { rowLeafNodes } = layoutResult;

    const heights = reduce(
      rowLeafNodes,
      (result: number[], node: Node) => {
        result.push(last(result) + node.height);
        return result;
      },
      [0],
    );

    return {
      getTotalHeight: () => {
        return last(heights);
      },

      getCellOffsetY: (index: number) => {
        return heights[index];
      },

      getTotalLength: () => {
        // 多了一个数据 [0]
        return heights.length - 1;
      },

      getIndexRange: (minHeight: number, maxHeight: number) => {
        return getIndexRangeWithOffsets(heights, minHeight, maxHeight);
      },
    };
  }
Example #12
Source File: yml-flow-util.ts    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
getFileName = (name: string) => {
  let useName = name;
  if (name.includes('/')) useName = last(name.split('/')) as string;
  return useName;
}
Example #13
Source File: base-facet.ts    From S2 with MIT License 6 votes vote down vote up
calculateCellWidthHeight = () => {
    const { colLeafNodes } = this.layoutResult;
    const widths = reduce(
      colLeafNodes,
      (result: number[], node: Node) => {
        result.push(last(result) + node.width);
        return result;
      },
      [0],
    );

    this.viewCellWidths = widths;
    this.viewCellHeights = this.getViewCellHeights(this.layoutResult);
  };
Example #14
Source File: file-diff.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
getExpandParams = (path: string, sections: any[], content: string) => {
  let bottom = false;
  let lastLineNo;
  let firstLineNo;
  let offset = 0;
  let sectionIndex = 0;
  if (content === '') {
    bottom = true;
    lastLineNo = (last(sections[sections.length - 2].lines) as any).newLineNo;
    sectionIndex = sections.length - 2;
  } else {
    sectionIndex = sections.findIndex((x) => x.lines[0].content === content);
    const currentSection = sections[sectionIndex];
    firstLineNo = currentSection.lines[1].newLineNo;
  }
  const lastSectionLine = sectionIndex > 0 && !bottom ? last(sections[sectionIndex - 1].lines) : null;
  let unfold = bottom;
  let since = bottom ? lastLineNo + 1 : 1;
  const to = bottom ? lastLineNo + 20 : firstLineNo - 1;
  if (!bottom) {
    if (lastSectionLine && firstLineNo - (lastSectionLine as any).newLineNo <= 20) {
      since = (lastSectionLine as any).newLineNo + 1;
    } else if (firstLineNo <= 21) {
      since = 1;
    } else {
      since = firstLineNo - 21;
      unfold = true;
    }
    if (lastSectionLine) {
      offset = (lastSectionLine as any).newLineNo - (lastSectionLine as any).oldLineNo;
    }
  }
  return { path, since, to, bottom, unfold, offset, sectionIndex };
}
Example #15
Source File: helpers.ts    From js-client with MIT License 6 votes vote down vote up
countEntriesFromModules = (
	msg: RawResponseForSearchStatsMessageReceived | RawResponseForSearchStatsWithinRangeMessageReceived,
): Array<SearchFrequencyStats> => {
	const statsSet = msg.data.Stats.Set;
	return statsSet.map(set => ({
		timestamp: new Date(set.TS),
		count: last(set.Stats)?.OutputCount ?? 0,
	}));
}
Example #16
Source File: index.tsx    From nebula-dashboard with Apache License 2.0 6 votes vote down vote up
updateChart = () => {
    const { data } = this.state;
    if (data.length > 0) {
      const total = last(data)!.count;
      const chartData = data.slice(0, data.length - 1).map(item => ({
        type: item.name,
        value: round(item.count / total, 2),
      }));
      this.chartInstance.data(chartData).render();
    }
  };
Example #17
Source File: path.ts    From free-swagger with MIT License 5 votes vote down vote up
createDefaultApiName = (url: string, method: Method): string => {
  return `${camelcase(last(url.split('/')) ?? '')}By${capitalize(method)}`
}
Example #18
Source File: edit-list.tsx    From erda-ui with GNU Affero General Public License v3.0 5 votes vote down vote up
CP_EDIT_LIST = (props: CP_EDIT_LIST.Props) => {
  const { props: configProps, state, execOperation, operations, updateState } = props;
  const { visible = true, temp = empty, ...rest } = configProps || {};
  const { list = empty } = state || {};
  const [useTemp, setUseTemp] = React.useState([] as any[]);
  const [value, setValue] = React.useState(list as any[]);
  const [saved, setSaved] = React.useState(true);
  const savedRef = React.useRef(null as any);

  React.useEffect(() => {
    savedRef.current = saved;
  }, [saved]);

  useUpdateEffect(() => {
    setValue(list || []);
  }, [list]);

  React.useEffect(() => {
    setUseTemp(
      map(temp, (item) => {
        const { render } = item;
        if (render?.type === 'inputSelect') {
          const p = {} as Obj;
          const { operations: rOps, valueConvertType, props: rProps, ...renderRest } = render || {};
          if (rOps?.onSelectOption) {
            p.onLoadData = (_selectOpt: any) => {
              execOperation(rOps.onSelectOption, _selectOpt);
            };
          }
          p.valueConvert = (str: string[]) => {
            let v = str.join('');
            switch (valueConvertType) {
              case 'last':
                v = last(str) as string;
                break;
              default:
                break;
            }
            return v;
          };

          return {
            ...item,
            render: { ...renderRest, props: { ...rProps, ...p } },
          };
        }
        return { ...item };
      }),
    );
  }, [execOperation, temp]);

  if (!visible) return null;

  const onSave = (val: Obj[]) => {
    if (!savedRef.current && operations?.save && operations.save?.disabled !== true) {
      execOperation(operations.save, { list: val });
      setSaved(true);
    }
  };

  const onChange = (val: Obj[]) => {
    setValue(val);
    setSaved(false);
    updateState({ list: val });
  };

  return (
    <div className="dice-cp">
      <EditList {...rest} value={value} dataTemp={useTemp} onChange={onChange} onBlurSave={onSave} />
    </div>
  );
}
Example #19
Source File: parse-neo4j-config-port.ts    From relate with GNU General Public License v3.0 5 votes vote down vote up
export function parseNeo4jConfigPort(port: string): number {
    return Number(last(split(port, ':')));
}
Example #20
Source File: roam-vim-panel.ts    From roam-toolkit with MIT License 5 votes vote down vote up
lastBlock(): BlockElement {
        return assumeExists(last(this.blocks()) as BlockElement)
    }
Example #21
Source File: table-facet.ts    From S2 with MIT License 5 votes vote down vote up
public getViewCellHeights() {
    const { dataSet } = this.cfg;

    this.initRowOffsets();

    const defaultCellHeight = this.getDefaultCellHeight();

    return {
      getTotalHeight: () => {
        if (this.rowOffsets) {
          return last(this.rowOffsets);
        }
        return defaultCellHeight * dataSet.getDisplayDataSet().length;
      },

      getCellOffsetY: (offset: number) => {
        if (offset <= 0) return 0;
        if (this.rowOffsets) {
          return this.rowOffsets[offset];
        }
        let totalOffset = 0;
        for (let index = 0; index < offset; index++) {
          totalOffset += defaultCellHeight;
        }
        return totalOffset;
      },

      getTotalLength: () => {
        return dataSet.getDisplayDataSet().length;
      },

      getIndexRange: (minHeight: number, maxHeight: number) => {
        if (this.rowOffsets) {
          return getIndexRangeWithOffsets(
            this.rowOffsets,
            minHeight,
            maxHeight,
          );
        }
        const yMin = Math.floor(minHeight / defaultCellHeight);
        // 防止数组index溢出导致报错
        const yMax =
          maxHeight % defaultCellHeight === 0
            ? maxHeight / defaultCellHeight - 1
            : Math.floor(maxHeight / defaultCellHeight);
        return {
          start: Math.max(0, yMin),
          end: Math.max(0, yMax),
        };
      },
    };
  }
Example #22
Source File: addon-detail.component.ts    From WowUp with GNU General Public License v3.0 5 votes vote down vote up
private getDisplayExternalId(externalId: string): string {
    if (externalId.indexOf("/") !== -1) {
      return `...${last(externalId.split("/")) ?? ""}`;
    }

    return externalId;
  }
Example #23
Source File: NavigationTabsProvider.tsx    From amplication with Apache License 2.0 5 votes vote down vote up
function NavigationTabsProvider({ children }: Props) {
  const [currentApplication, setCurrentApplication] = useState<string>("");

  const [navigationTabItems, setNavigationTabItems] = useState<
    NavigationTabItem[]
  >([]);

  const registerNavigationTabItem = useCallback(
    (addItem: NavigationTabItem) => {
      if (addItem.applicationId !== currentApplication) {
        setCurrentApplication(addItem.applicationId);
        setNavigationTabItems([]);
      }

      setNavigationTabItems((items) => {
        let exist = false;
        const next = items.map((item) => {
          if (item.key === addItem.key) {
            exist = true;
            return addItem;
          }
          return {
            ...item,
            active: false,
          };
        });

        if (!exist) {
          next.push({
            ...addItem,
            active: true,
          });
        }
        return next;
      });
    },
    [setNavigationTabItems, setCurrentApplication, currentApplication]
  );

  const unregisterNavigationTabItem = useCallback(
    (key: string) => {
      const nextItems = navigationTabItems.filter((item) => item.key !== key);

      const nextUrl = last(nextItems)?.url;

      setNavigationTabItems(nextItems);
      return nextUrl;
    },
    [setNavigationTabItems, navigationTabItems]
  );

  const NavigationTabsContextValue = useMemo(
    () => ({
      items: navigationTabItems,
      registerItem: registerNavigationTabItem,
      unregisterItem: unregisterNavigationTabItem,
    }),
    [navigationTabItems, registerNavigationTabItem, unregisterNavigationTabItem]
  );

  return (
    <NavigationTabsContext.Provider value={NavigationTabsContextValue}>
      {children}
    </NavigationTabsContext.Provider>
  );
}
Example #24
Source File: BoundaryDropdown.tsx    From prism-frontend with MIT License 5 votes vote down vote up
/**
 * Converts the boundary layer data into a list of options for the dropdown
 * grouped by admin level 2, with individual sections under admin level 3.
 */
function getCategories(
  data: LayerData<BoundaryLayerProps>['data'],
  search: string,
  i18nLocale: typeof i18n,
) {
  const locationLevelNames = isEnglishLanguageSelected(i18nLocale)
    ? boundaryLayer.adminLevelNames
    : boundaryLayer.adminLevelLocalNames;
  if (!boundaryLayer.adminLevelNames.length) {
    console.error(
      'Boundary layer has no admin level names. Cannot generate categories.',
    );
    return [];
  }

  // Make categories based off the level of all boundaries
  return sortBy(
    data.features
      .reduce<
        Array<{
          title: string;
          children: { value: string; label: string }[];
        }>
      >((ret, feature) => {
        const parentCategory = feature.properties?.[locationLevelNames[0]];
        const label = feature.properties?.[last(locationLevelNames)!];
        const code = feature.properties?.[boundaryLayer.adminCode];
        if (!label || !code || !parentCategory) {
          return ret;
        }
        // filter via search
        const searchIncludes = (field: string) =>
          field.toLowerCase().includes(search.toLowerCase());
        if (
          search &&
          !searchIncludes(label) &&
          !searchIncludes(code) &&
          !searchIncludes(parentCategory)
        ) {
          return ret;
        }
        // add to categories if exists
        const category = ret.find(c => c.title === parentCategory);
        if (category) {
          // eslint-disable-next-line fp/no-mutating-methods
          category.children.push({ value: code, label });
        } else {
          return [
            ...ret,
            {
              title: parentCategory,
              children: [{ value: code, label }],
            },
          ];
        }
        return ret;
      }, [])
      .map(category => ({
        ...category,
        // sort children by label
        children: sortBy(category.children, 'label'),
      })),
    // then finally sort categories.
    'title',
  );
}
Example #25
Source File: corner-cell.ts    From S2 with MIT License 5 votes vote down vote up
private isLastRowCornerCell() {
    const { cornerType, field } = this.meta;
    const { rows } = this.headerConfig;
    return (
      cornerType === CornerNodeType.Row &&
      (this.spreadsheet.isHierarchyTreeType() || last(rows) === field)
    );
  }
Example #26
Source File: index.tsx    From aqualink-app with MIT License 5 votes vote down vote up
OceanSenseMetrics = ({ classes }: OceanSenseMetricsProps) => {
  const theme = useTheme();
  const isMobile = useMediaQuery(theme.breakpoints.down("xs"));
  const isTablet = useMediaQuery(theme.breakpoints.up("md"));
  const data = useSelector(siteLatestOceanSenseDataSelector);
  const loading = useSelector(siteLatestOceanSenseDataLoadingSelector);

  const lastTimestamp = last(data?.PH)?.timestamp;
  const relativeTime = lastTimestamp
    ? toRelativeTime(lastTimestamp)
    : undefined;

  return (
    <>
      <Box className={classes.root}>
        <Grid container justify="space-between" alignItems="center" spacing={2}>
          {Object.values(metrics(data)).map((item) => (
            <Grid
              item
              xs={isMobile ? 12 : undefined}
              sm={isTablet ? undefined : 5}
              key={item.label}
            >
              <Grid
                className={classes.cardItem}
                container
                alignItems="center"
                spacing={2}
              >
                <Grid item>{item.icon}</Grid>
                <Grid item>
                  <Typography display="block" variant="caption">
                    {item.label}
                  </Typography>
                  {loading ? (
                    <Box py={0.5}>
                      <CircularProgress size={22} thickness={2} />
                    </Box>
                  ) : (
                    <Typography className={classes.blueText} variant="h3">
                      {item.value}
                    </Typography>
                  )}
                  <Chip className={classes.chip} label={item.measure} />
                </Grid>
              </Grid>
            </Grid>
          ))}
        </Grid>
      </Box>
      <Grid container>
        <Grid item xs={12} sm={6} md={3}>
          <UpdateInfo
            timeText="Last data received"
            relativeTime={relativeTime}
            imageText="VIEWBLUE"
            live={false}
            frequency="hourly"
          />
        </Grid>
        {!isMobile && <Grid item className={classes.triangle} />}
      </Grid>
    </>
  );
}
Example #27
Source File: eosBancor.ts    From webapp with MIT License 5 votes vote down vote up
past24HourTrades = async (
  lastBlockTarget?: number,
  highBlockNum = -1,
  tradesFetched: DFuseTrade[] = []
): Promise<DFuseTrade[]> => {
  const response = await dfuseClient.graphql(
    searchTransactionsWithHigherBlock,
    {
      variables: {
        limit: 1000,
        highBlockNum
      }
    }
  );

  if (response.errors && response.errors.length > 0) {
    throw new Error(response.errors[0].message);
  }

  const results: DFuseTrade[] =
    response.data.searchTransactionsBackward.results || [];

  const firstBlock = first(results)!.block.num;
  const lastBlock = last(results)!.block.num;

  if (!lastBlockTarget) {
    const secondsInADay = 86400;
    const blocksPerSecond = 2;
    const searchBlockAmount = secondsInADay * blocksPerSecond;
    lastBlockTarget = firstBlock - searchBlockAmount;
  }

  const blockTargetReached = lastBlock <= lastBlockTarget;
  const latestTrades = [...tradesFetched, ...results];
  if (blockTargetReached) {
    return latestTrades.map(trade => ({
      ...trade,
      trace: {
        ...trade.trace,
        executedActions: trade.trace.executedActions.filter(x => x.json)
      }
    }));
  } else {
    return past24HourTrades(lastBlockTarget, lastBlock - 1, latestTrades);
  }
}
Example #28
Source File: scraper.test.ts    From advocacy-maps with MIT License 5 votes vote down vote up
describe("chooseBatches", () => {
  it("splits into batches", () => {
    const rawIds = listOfNumbers(2000)
    const ids = chooseBatches({
      ids: rawIds,
      docsPerBatch: 100,
      numBatches: 10,
      startAfterId: ""
    })

    // Correct number of batches
    expect(ids).toHaveLength(10)
    // Batches are the correct size
    ids.forEach(batch => expect(batch).toHaveLength(100))

    const ids2 = chooseBatches({
      ids: rawIds,
      docsPerBatch: 100,
      numBatches: 10,
      startAfterId: last(last(ids))!
    })

    const allIds = flattenDeep([ids, ids2]),
      sortedIds = [...allIds].sort()

    // Batches are sorted
    expect(sortedIds).toEqual(allIds)

    // Batches are exhaustive
    expect(new Set(allIds)).toEqual(new Set(rawIds))
  })

  it("starts after identified", () => {
    const rawIds = ["a", "b", "c", "d", "e"]
    const ids = chooseBatches({
      ids: rawIds,
      docsPerBatch: 1,
      numBatches: 2,
      startAfterId: "b"
    })
    expect(ids).toEqual([["c"], ["d"]])
  })

  it("wraps", () => {
    const rawIds = ["a", "b", "c", "d", "e"]
    const ids = chooseBatches({
      ids: rawIds,
      docsPerBatch: 1,
      numBatches: 2,
      startAfterId: "d"
    })
    expect(ids).toEqual([["e"], ["a"]])
  })

  it("does not return duplicates", () => {
    const rawIds = ["a", "b", "c", "d", "e"]
    let ids = chooseBatches({
      ids: rawIds,
      docsPerBatch: 1,
      numBatches: 10,
      startAfterId: ""
    })
    expect(ids).toEqual([["a"], ["b"], ["c"], ["d"], ["e"]])

    ids = chooseBatches({
      ids: rawIds,
      docsPerBatch: 10,
      numBatches: 10,
      startAfterId: "b"
    })
    expect(ids).toEqual([["c", "d", "e", "a", "b"]])
  })
})
Example #29
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],
  });
}