lodash#first TypeScript Examples

The following examples show how to use lodash#first. 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: index.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
getQuery = (direction: string) => {
    const { query = {}, content, logKey, searchContext } = this.props;
    const { filter } = this.state;
    const { size = 200, requestId, end, start, ...rest } = query;

    if (requestId) {
      return { requestId }; // 查询requestId 则忽略其他查询条件
    }
    const reQuery: IRequery = {};
    if (Direction.forward === direction) {
      if (searchContext && this.searchCount === 1) {
        reQuery.start = start ? start : 0;
      } else {
        const lastItem = last(content);
        reQuery.start = lastItem ? lastItem.timestamp : 0;
        reQuery.end = getCurTimeNs();
      }
      reQuery.count = Number(size);
    } else if (Direction.backward === direction) {
      reQuery.count = -1 * Number(size);
      if (searchContext && this.searchCount === 0) {
        reQuery.end = start;
      } else {
        reQuery.start = filter?.start ?? 0;
        const firstItem = first(content);
        reQuery.end = firstItem ? firstItem.timestamp : filter?.end || Number(end) || getCurTimeNs();
        return { ...filter, ...reQuery, ...rest, logKey };
      }
    }
    return { ...reQuery, ...filter, ...rest, logKey };
  };
Example #2
Source File: jenkins_analyzer.ts    From CIAnalyzer with MIT License 6 votes vote down vote up
detectRepository(build: BuildResponse): string {
    const action = build.actions.find((action) => {
      return action._class === "hudson.plugins.git.util.BuildData" ||
        action._class === "org.jenkinsci.plugins.ghprb.GhprbParametersAction"
    }) as BuildData | GhprbParametersAction | undefined
    if (!action) return ''

    switch (action._class) {
      case "hudson.plugins.git.util.BuildData":
        return first(action.remoteUrls) ?? ''
      case "org.jenkinsci.plugins.ghprb.GhprbParametersAction":
        const repoParam = action.parameters.find((param) => param.name === "ghprbAuthorRepoGitUrl")
        return repoParam?.value ?? ''
    }
  }
Example #3
Source File: jenkins_analyzer.ts    From CIAnalyzer with MIT License 6 votes vote down vote up
detectBranch(build: BuildResponse): string {
    const action = build.actions.find((action) => {
      return action._class === "hudson.plugins.git.util.BuildData" ||
        action._class === "org.jenkinsci.plugins.ghprb.GhprbParametersAction"
    }) as BuildData | GhprbParametersAction | undefined
    if (!action) return ''

    switch (action._class) {
      case "hudson.plugins.git.util.BuildData":
        const branch = first(action.lastBuiltRevision.branch)?.name
        if (!branch) return ''
        // Remove 'refs | remotes | origin' prefix.
        return branch
          .split('/')
          .filter((prefix) => !['refs', 'remotes', 'origin'].includes(prefix))
          .join('/')
      case "org.jenkinsci.plugins.ghprb.GhprbParametersAction":
        const repoParam = action.parameters.find((param) => param.name === "GIT_BRANCH")
        return repoParam?.value ?? ''
    }
  }
Example #4
Source File: outputInterpreter.ts    From nautilus-wallet with MIT License 6 votes vote down vote up
private getMintingToken(): OutputAsset | undefined {
    const firstInputId = first(this._inputs)?.boxId;
    if (!firstInputId) {
      return undefined;
    }

    const token = find(this._box.assets, (b) => b.tokenId === firstInputId);
    if (!token) {
      return undefined;
    }

    if (isEmpty(this._box.additionalRegisters)) {
      return {
        tokenId: token.tokenId,
        amount: toBigNumber(token.amount)!
      };
    }

    const decimals = parseInt(decodeColl(this._box.additionalRegisters["R6"]) ?? "");
    return {
      tokenId: token.tokenId,
      name: decodeColl(this._box.additionalRegisters["R4"]) ?? "",
      decimals,
      amount: decimals
        ? decimalize(toBigNumber(token.amount)!, decimals)
        : toBigNumber(token.amount)!,
      description: decodeColl(this._box.additionalRegisters["R5"]) ?? "",
      minting: true
    };
  }
Example #5
Source File: renderEntryTitle.ts    From strapi-plugin-comments with MIT License 6 votes vote down vote up
renderEntryTitle = (entry: ToBeFixed, config: ToBeFixed = {}) => {
  const { entryLabel } = config;
  const rule =
    entry.uid in entryLabel ? entryLabel[entry.uid] : entryLabel["*"];
  return first(
    Object.keys(entry)
      .filter((_) => rule === _ || rule.includes(_))
      .map((_) => entry[_])
      .filter((_) => _)
  );
}
Example #6
Source File: functions.ts    From strapi-plugin-comments with MIT License 6 votes vote down vote up
convertContentTypeNameToSlug = (str: string): string => {
  const plainConversion = str.replace(
    /[A-Z]/g,
    (letter) => `-${letter.toLowerCase()}`
  );
  return first(plainConversion) === "-"
    ? plainConversion.slice(1, plainConversion.length)
    : plainConversion;
}
Example #7
Source File: data-cell.ts    From S2 with MIT License 6 votes vote down vote up
protected handleHover(cells: CellMeta[]) {
    const currentHoverCell = first(cells) as CellMeta;
    if (currentHoverCell.type !== CellTypes.DATA_CELL) {
      this.hideInteractionShape();
      return;
    }

    if (this.spreadsheet.options.interaction.hoverHighlight) {
      // 如果当前是hover,要绘制出十字交叉的行列样式
      const currentColIndex = this.meta.colIndex;
      const currentRowIndex = this.meta.rowIndex;
      // 当视图内的 cell 行列 index 与 hover 的 cell 一致,绘制hover的十字样式
      if (
        currentColIndex === currentHoverCell?.colIndex ||
        currentRowIndex === currentHoverCell?.rowIndex
      ) {
        this.updateByState(InteractionStateName.HOVER);
      } else {
        // 当视图内的 cell 行列 index 与 hover 的 cell 不一致,隐藏其他样式
        this.hideInteractionShape();
      }
    }

    if (isEqual(currentHoverCell.id, this.getMeta().id)) {
      this.updateByState(InteractionStateName.HOVER_FOCUS);
    }
  }
Example #8
Source File: header-cell.ts    From S2 with MIT License 6 votes vote down vote up
public update() {
    const { interaction } = this.spreadsheet;
    const stateInfo = interaction?.getState();
    const cells = interaction?.getCells();

    if (!first(cells)) return;

    switch (stateInfo?.stateName) {
      case InteractionStateName.SELECTED:
        this.handleSelect(cells, stateInfo?.nodes);
        break;
      case InteractionStateName.HOVER_FOCUS:
      case InteractionStateName.HOVER:
        this.handleHover(cells);
        break;
      case InteractionStateName.SEARCH_RESULT:
        this.handleSearchResult(cells);
        break;
      default:
        this.handleByStateName(cells, stateInfo?.stateName);
        break;
    }
  }
Example #9
Source File: pivot-data-set.ts    From S2 with MIT License 6 votes vote down vote up
public getTotalStatus = (query: DataType) => {
    const { columns, rows } = this.fields;
    const isTotals = (dimensions: string[], isSubTotal?: boolean) => {
      if (isSubTotal) {
        const firstDimension = find(dimensions, (item) => !has(query, item));
        return firstDimension && firstDimension !== first(dimensions);
      }
      return every(dimensions, (item) => !has(query, item));
    };
    const getDimensions = (dimensions: string[], hasExtra: boolean) => {
      return hasExtra
        ? dimensions.filter((item) => item !== EXTRA_FIELD)
        : dimensions;
    };

    return {
      isRowTotal: isTotals(
        getDimensions(rows, !this.spreadsheet.isValueInCols()),
      ),
      isRowSubTotal: isTotals(rows, true),
      isColTotal: isTotals(
        getDimensions(columns, this.spreadsheet.isValueInCols()),
      ),
      isColSubTotal: isTotals(columns, true),
    };
  };
Example #10
Source File: upload-sheet-data.ts    From aqualink-app with MIT License 6 votes vote down vote up
getTimestampFromDateString = (dataObject: any) => {
  const dateKey = Object.keys(dataObject).find(
    (header) =>
      // TODO - these keys should not be hardcoded here.
      headerMatchesKey(header, 'Date Time') ||
      headerMatchesKey(header, 'Date_Time'),
  );

  if (!dateKey) {
    // TODO - Can we raise an error here instead? Is it safe?
    return undefined;
  }

  const timezoneOffset = first(dateKey.match(TIMEZONE_REGEX));

  if (timezoneOffset) {
    return new Date(`${dataObject[dateKey]} GMT ${timezoneOffset}`);
  }

  return new Date(dataObject[dateKey]);
}
Example #11
Source File: get-model-name.ts    From prisma-nestjs-graphql with MIT License 5 votes vote down vote up
function getModelName(args: {
  name: string;
  modelNames: string[];
}): string | undefined {
  const { name, modelNames } = args;
  for (const keyword of splitKeywords) {
    const [test] = name.split(keyword, 1);
    if (modelNames.includes(test)) {
      return test;
    }
  }
  for (const keyword of endsWithKeywords) {
    const [test] = name.split(keyword).slice(-1);
    if (modelNames.includes(test)) {
      return test;
    }
  }
  for (const [start, end] of middleKeywords) {
    let test = name.slice(start.length).slice(0, -end.length);
    if (modelNames.includes(test)) {
      return test;
    }
    test = name.slice(0, -(start + end).length);
    if (modelNames.includes(test)) {
      return test;
    }
  }

  // test for {Model}{UniqueName}CompoundUniqueInput
  if (name.slice(-19) === 'CompoundUniqueInput') {
    const test = name.slice(0, -19);
    const models = modelNames
      .filter(x => test.startsWith(x))
      .sort((a, b) => b.length - a.length);
    return first(models);
  }

  // test for {Model}Count
  if (name.slice(-5) === 'Count') {
    const test = name.slice(0, -5);
    if (modelNames.includes(test)) {
      return test;
    }
  }

  // eslint-disable-next-line consistent-return, unicorn/no-useless-undefined
  return undefined;
}
Example #12
Source File: search.test.ts    From advocacy-maps with MIT License 5 votes vote down vote up
describe("Sync", () => {
  let existing: DocumentSnapshot
  let id: string
  let newBill: any
  let newBillPath: string

  beforeEach(async () => {
    await clearCollections()
    await clearAliases()

    id = `test-bill-${nanoid()}`
    newBillPath = `/generalCourts/192/bills/${id}`
    existing = await testDb.doc(`/generalCourts/192/bills/H1`).get()
    newBill = { ...existing.data()!, id }
  })

  it("Creates documents on create", async () => {
    await testDb.doc(newBillPath).create(newBill)
    await assertDocumentExists()
  })

  it("Updates documents on update", async () => {
    await testDb.doc(newBillPath).create(newBill)
    await assertDocumentExists()

    const updatedTitle = "updated title"
    await testDb.doc(newBillPath).update({ "content.Title": updatedTitle })
    await assertDocumentTitle(updatedTitle)
  })

  it("Deletes documents on delete", async () => {
    await testDb.doc(newBillPath).create(newBill)
    await assertDocumentExists()

    await testDb.doc(newBillPath).delete({ exists: true })
    await assertDocumentDeleted()
  })

  async function assertDocumentDeleted() {
    await waitFor(() => expect(getDoc()).rejects.toThrow(), mediumTimeout)
  }

  async function assertDocumentExists() {
    await waitFor(async () => {
      const doc = await getDoc()
      expect(doc.id).toEqual(id)
    }, mediumTimeout)
  }

  async function assertDocumentTitle(title: string) {
    await waitFor(async () => {
      const doc = await getDoc()
      expect(doc.title).toEqual(title)
    }, mediumTimeout)
  }

  async function getDoc() {
    const collection = first(await client.collections().retrieve())
    expect(collection).toBeTruthy()
    return client.collections(collection!.name).documents(id).retrieve() as any
  }
})
Example #13
Source File: resolveTestimony.ts    From advocacy-maps with MIT License 5 votes vote down vote up
resolveBillTestimony = async (uid: string, billId: string) => {
  const published = await getPublishedTestimony(uid, billId)
  const draft = await getDraftTestimony(uid, billId)
  return {
    draft: first(draft.docs)?.ref,
    publication: first(published.docs)?.ref
  }
}
Example #14
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 #15
Source File: renderInitials.ts    From strapi-plugin-comments with MIT License 5 votes vote down vote up
renderInitials = (value = "") =>
  value
    .split(" ")
    .map((_) => first(_))
    .join("")
    .toUpperCase()
Example #16
Source File: index.tsx    From strapi-plugin-comments with MIT License 5 votes vote down vote up
DetailsEntity = ({
  data = {},
  schema = {},
  config = {},
  filters,
  onFiltersChange,
}) => {
  const { entryLabel = {} } = config;
  const { attributes = {} } = schema;
  const keys = Object.keys(attributes);
  const entityLabelKey = first(entryLabel[data?.uid]);

  const FIELDS_LIMIT = 5;
  const itemKeys = take(
    keys.filter(
      (_) =>
        attributes[_].type === "string" &&
        !isNil(data[_]) &&
        _ !== entityLabelKey
    ),
    FIELDS_LIMIT
  );

  const formatLabel = (label = "") =>
    label
      .split("_")
      .map((_) => capitalize(_))
      .join(" ");

  const entityIsRenderable =
    data && !isEmpty(data) && (!isEmpty(itemKeys) || data[entityLabelKey]);

  return (
    <Box padding={4}>
      {entityIsRenderable && (
        <Box marginBottom={4}>
          <Typography
            variant="sigma"
            textColor="neutral600"
            id="entity-details"
          >
            {getMessage("page.details.panel.entity", "Details")}
          </Typography>
          <Box paddingTop={2} paddingBottom={4}>
            <Divider />
          </Box>
          <Stack size={itemKeys.length}>
            <Flex direction="column" alignItems="flex-start">
              <Typography fontWeight="bold">
                {formatLabel(entityLabelKey)}
              </Typography>
              <Typography>{data[entityLabelKey]}</Typography>
            </Flex>
            {itemKeys.map((_) => (
              <Flex
                key={`prop_${_}`}
                direction="column"
                alignItems="flex-start"
              >
                <Typography fontWeight="bold">{formatLabel(_)}</Typography>
                <Typography>{data[_]}</Typography>
              </Flex>
            ))}
          </Stack>
        </Box>
      )}
      <Box>
        <Typography variant="sigma" textColor="neutral600" id="view-filters">
          {getMessage("page.details.filters.label", "View")}
        </Typography>
        <Box paddingTop={2} paddingBottom={4}>
          <Divider />
        </Box>
        <DetailsFilters data={filters} onChange={onFiltersChange} />
      </Box>
    </Box>
  );
}
Example #17
Source File: jenkins_analyzer.ts    From CIAnalyzer with MIT License 5 votes vote down vote up
detectTrigger(build: BuildResponse): string {
    const causeAction = build.actions.find((action) => {
      return action._class === "hudson.model.CauseAction"
    }) as CauseAction | undefined
    if (!causeAction) return ''

    return first(causeAction.causes)?._class ?? ''
  }
Example #18
Source File: custom-data-tooltip.tsx    From S2 with MIT License 4 votes vote down vote up
DataTooltip: React.FC<CustomTooltipProps> = ({ cell }) => {
  const meta = cell.getMeta();

  const currentRow = React.useMemo(
    () =>
      find(meta.spreadsheet.getRowNodes(), {
        rowIndex: meta.rowIndex,
      }),
    [meta],
  );

  const currentLeafCol = React.useMemo(
    () =>
      find(meta.spreadsheet.getColumnNodes(), {
        colIndex: meta.colIndex,
        isLeaf: true,
      }),
    [meta],
  );

  const [, ...derivedLabels] = React.useMemo(() => {
    try {
      return JSON.parse(currentLeafCol.value);
    } catch {
      return [];
    }
  }, [currentLeafCol.value]);

  const rowName = meta.spreadsheet.dataSet.getFieldName(
    currentRow?.valueFiled || currentRow?.value,
  );

  const [value, ...derivedValues] = first(meta.fieldValue?.values) || [
    meta.fieldValue,
  ];

  const { placeholder, style } = meta.spreadsheet.options;
  const emptyPlaceholder = getEmptyPlaceholder(meta, placeholder);
  const valuesCfg = style.cellCfg?.valuesCfg;
  const originalValue = get(meta.fieldValue, valuesCfg?.originalValueField);

  return (
    <div className={cls(styles.strategySheetTooltip, styles.data)}>
      <div className={styles.header}>
        <span className={styles.label}>{rowName}</span>
        <span>{value ?? emptyPlaceholder}</span>
      </div>
      <div className={styles.originalValue}>
        {isNil(originalValue?.[0]?.[0])
          ? emptyPlaceholder
          : originalValue?.[0]?.[0]}
      </div>
      {!isEmpty(derivedValues) && (
        <>
          <div className={styles.divider}></div>
          <ul className={styles.derivedValues}>
            {derivedValues.map((derivedValue, i) => {
              const isNormal = isNil(derivedValue);
              const isUp = isUpDataValue(derivedValue);
              const isDown = !isNormal && !isUp;

              return (
                <li className={styles.value} key={i}>
                  <span className={styles.derivedValueLabel}>
                    {derivedLabels[i]}
                  </span>
                  <span
                    className={cls(styles.derivedValueGroup, {
                      [styles.up]: isUp,
                      [styles.down]: isDown,
                    })}
                  >
                    {!isNormal && <span className={styles.icon}></span>}
                    <span className={styles.value}>
                      {derivedValue ?? emptyPlaceholder}
                    </span>
                  </span>
                </li>
              );
            })}
          </ul>
        </>
      )}
    </div>
  );
}
Example #19
Source File: index.tsx    From strapi-plugin-comments with MIT License 4 votes vote down vote up
Settings = () => {
  useFocusWhenNavigate();

  const { notifyStatus } = useNotifyAT();
  const { trackUsage } = useTracking();
  const trackUsageRef = useRef(trackUsage);
  const toggleNotification = useNotification();
  const { lockApp, unlockApp } = useOverlayBlocker();

  const viewPermissions = useMemo(
    () => ({
      access: pluginPermissions.settings.read,
      change: pluginPermissions.settings.change,
    }),
    []
  );

  const {
    isLoading: isLoadingForPermissions,
    allowedActions: { canAccess, canChange },
  } = useRBAC(viewPermissions);

  const [restoreConfigmationVisible, setRestoreConfigmationVisible] =
    useState(false);
  const [restartRequired, setRestartRequired] = useState(false);
  const [contentTypeExpanded, setContentTypeExpanded] = useState(undefined);

  const { fetch, restartMutation, submitMutation, restoreMutation } =
    useConfig(toggleNotification);
  const {
    data: configData,
    isLoading: isConfigLoading,
    err: configErr,
  }: ToBeFixed = fetch;

  const {
    data: allCollectionsData,
    isLoading: areCollectionsLoading,
    err: collectionsErr,
  }: ToBeFixed = useQuery(["get-all-content-types", canAccess], () =>
    fetchAllContentTypes(toggleNotification)
  );

  const {
    data: allRolesData,
    isLoading: areRolesLoading,
    err: rolesErr,
  }: ToBeFixed = useQuery(["get-all-roles", canAccess], () =>
    fetchRoles(toggleNotification)
  );

  const isLoading =
    isLoadingForPermissions ||
    isConfigLoading ||
    areCollectionsLoading ||
    areRolesLoading;
  const isError = configErr || collectionsErr || rolesErr;

  const preparePayload = ({
    enabledCollections,
    gqlAuthEnabled,
    approvalFlow,
    entryLabel,
    clientUrl,
    clientEmail,
    ...rest
  }: ToBeFixed) => ({
    ...rest,
    enabledCollections,
    approvalFlow: approvalFlow.filter((_) => enabledCollections.includes(_)),
    entryLabel: {
      ...Object.keys(entryLabel).reduce(
        (prev, curr) => ({
          ...prev,
          [curr]: enabledCollections.includes(curr)
            ? entryLabel[curr]
            : undefined,
        }),
        {}
      ),
      "*": entryLabel["*"],
    },
    reportReasons: configData?.reportReasons,
    client: clientEmail || clientUrl ? {
      contactEmail: clientEmail,
      url: clientUrl,
    } : undefined,
    gql: gqlAuthEnabled ? { auth: true } : undefined,
  });

  if (isLoading || isError) {
    return (
      <LoadingIndicatorPage>
        {getMessage("page.settings.loading")}
      </LoadingIndicatorPage>
    );
  }

  const regexUID = !isLoading
    ? new RegExp(
        parseRegExp(fetch.data.regex?.uid).value,
        parseRegExp(fetch.data.regex?.uid).flags
      )
    : null;

  const allRoles = allRolesData?.data || [];
  const allCollections =
    !isLoading &&
    allCollectionsData.filter(
      ({ uid }: ToBeFixed) =>
        first(uid.split(regexUID).filter((s) => s && s.length > 0)) === "api"
    );
  const enabledCollections =
    configData?.enabledCollections
      ?.map((uid: ToBeFixed) =>
        allCollections.find((_) => _.uid === uid) ? uid : undefined
      )
      .filter((_: ToBeFixed) => _) || [];
  const entryLabel = configData?.entryLabel || {};
  const approvalFlow = configData?.approvalFlow || [];
  const badWords = isNil(configData?.badWords) ? true : configData?.badWords;
  const isGQLPluginEnabled = configData?.isGQLPluginEnabled;
  const gqlAuthEnabled = configData?.gql?.auth || undefined;
  const moderatorRoles =
    configData?.moderatorRoles?.filter((code: ToBeFixed) =>
      allRoles.find((_: ToBeFixed) => _.code === code)
    ) || [];
  const clientUrl = configData?.client?.url;
  const clientEmail = configData?.client?.contactEmail;

  const changeApprovalFlowFor = (
    uid: ToBeFixed,
    current: ToBeFixed,
    value: ToBeFixed
  ) => {
    const currentSet = new Set(current);
    if (value) {
      currentSet.add(uid);
    } else {
      currentSet.delete(uid);
    }
    return Array.from(currentSet);
  };

  const changeEntryLabelFor = (
    uid: ToBeFixed,
    current: ToBeFixed,
    value: ToBeFixed
  ) => ({
    ...current,
    [uid]: value && !isEmpty(value) ? [...value] : undefined,
  });

  const handleUpdateConfiguration = async (form: ToBeFixed) => {
    if (canChange) {
      lockApp();
      const payload = preparePayload(form);
      await submitMutation.mutateAsync(payload);
      const enabledCollectionsChanged = !isEqual(
        payload.enabledCollections,
        configData?.enabledCollections
      );
      const gqlAuthChanged = !isEqual(payload.gql?.auth, configData?.gql?.auth);
      if (enabledCollectionsChanged || gqlAuthChanged) {
        setRestartRequired(true);
      }
      unlockApp();
    }
  };

  const handleRestoreConfirmation = () => setRestoreConfigmationVisible(true);
  const handleRestoreConfiguration = async () => {
    if (canChange) {
      lockApp();
      await restoreMutation.mutateAsync();
      unlockApp();
      setRestartRequired(true);
      setRestoreConfigmationVisible(false);
    }
  };
  const handleRestoreCancel = () => setRestoreConfigmationVisible(false);

  const handleRestart = async () => {
    if (canChange) {
      lockApp();
      await restartMutation.mutateAsync();
      setRestartRequired(false);
      unlockApp();
    }
  };
  const handleRestartDiscard = () => setRestartRequired(false);

  const handleSetContentTypeExpanded = (key: ToBeFixed) =>
    setContentTypeExpanded(key === contentTypeExpanded ? undefined : key);

  const boxDefaultProps = {
    background: "neutral0",
    hasRadius: true,
    shadow: "filterShadow",
    padding: 6,
  };

  return (
    <Main>
      <Formik
        initialValues={{
          enabledCollections,
          moderatorRoles,
          badWords,
          approvalFlow,
          entryLabel,
          clientEmail,
          clientUrl,
          gqlAuthEnabled,
        }}
        enableReinitialize={true}
        onSubmit={handleUpdateConfiguration}
      >
        {({ handleSubmit, setFieldValue, values }: ToBeFixed) => (
          <Form noValidate onSubmit={handleSubmit}>
            <HeaderLayout
              title={getMessage("page.settings.header.title")}
              subtitle={getMessage("page.settings.header.description")}
              primaryAction={
                <CheckPermissions
                  permissions={pluginPermissions.settingsChange}
                >
                  <Button
                    type="submit"
                    startIcon={<Check />}
                    disabled={restartRequired}
                  >
                    {getMessage("page.settings.actions.submit")}
                  </Button>
                </CheckPermissions>
              }
            />
            <ContentLayout>
              <Stack size={4}>
                {restartRequired && (
                  <RestartAlert
                    closeLabel={getMessage(
                      "page.settings.actions.restart.alert.cancel"
                    )}
                    title={getMessage(
                      "page.settings.actions.restart.alert.title"
                    )}
                    action={
                      <Box>
                        <Button onClick={handleRestart} startIcon={<Play />}>
                          {getMessage("page.settings.actions.restart")}
                        </Button>
                      </Box>
                    }
                    onClose={handleRestartDiscard}
                  >
                    {getMessage(
                      "page.settings.actions.restart.alert.description"
                    )}
                  </RestartAlert>
                )}

                <Box {...boxDefaultProps}>
                  <Stack size={4}>
                    <Typography variant="delta" as="h2">
                      {getMessage("page.settings.section.general")}
                    </Typography>
                    <Grid gap={4}>
                      <GridItem col={12}>
                        <Select
                          name="enabledCollections"
                          label={getMessage(
                            "page.settings.form.enabledCollections.label"
                          )}
                          placeholder={getMessage(
                            "page.settings.form.enabledCollections.placeholder"
                          )}
                          hint={getMessage(
                            "page.settings.form.enabledCollections.hint"
                          )}
                          onClear={() =>
                            setFieldValue("enabledCollections", [], false)
                          }
                          value={values.enabledCollections}
                          onChange={(value: ToBeFixed) =>
                            setFieldValue("enabledCollections", value, false)
                          }
                          disabled={restartRequired}
                          multi
                          withTags
                        >
                          {allCollections.map(
                            ({ uid, schema: { displayName } }: ToBeFixed) => (
                              <Option key={uid} value={uid}>
                                {displayName}
                              </Option>
                            )
                          )}
                        </Select>
                      </GridItem>
                      {!isEmpty(values.enabledCollections) && (
                        <GridItem col={12}>
                          <AccordionGroup
                            label={getMessage(
                              "page.settings.form.contentTypesSettings.label"
                            )}
                            labelAction={
                              <Tooltip
                                description={getMessage(
                                  "page.settings.form.contentTypesSettings.tooltip"
                                )}
                              >
                                <Information aria-hidden={true} />
                              </Tooltip>
                            }
                          >
                            {orderBy(values.enabledCollections).map((uid) => {
                              const {
                                schema: { displayName, attributes = {} },
                              } = allCollections.find((_) => _.uid === uid);
                              const stringAttributes = Object.keys(
                                attributes
                              ).filter((_) => attributes[_].type === "string");
                              const key = `collectionSettings-${uid}`;
                              return (
                                <Accordion
                                  expanded={contentTypeExpanded === key}
                                  toggle={() =>
                                    handleSetContentTypeExpanded(key)
                                  }
                                  key={key}
                                  id={key}
                                  size="S"
                                >
                                  <AccordionToggle
                                    title={displayName}
                                    togglePosition="left"
                                  />
                                  <AccordionContent>
                                    <Box padding={6}>
                                      <Stack size={4}>
                                        <FormSwitch
                                          name={`collectionSettings-${uid}-approvalFlow`}
                                          label={getMessage(
                                            "page.settings.form.approvalFlow.label"
                                          )}
                                          hint={getMessage({
                                            id: "page.settings.form.approvalFlow.hint",
                                            props: { name: displayName },
                                          })}
                                          selected={values.approvalFlow.includes(
                                            uid
                                          )}
                                          onChange={() =>
                                            setFieldValue(
                                              "approvalFlow",
                                              changeApprovalFlowFor(
                                                uid,
                                                values.approvalFlow,
                                                !values.approvalFlow.includes(
                                                  uid
                                                )
                                              ),
                                              []
                                            )
                                          }
                                          onLabel={getMessage(
                                            "compontents.toogle.enabled"
                                          )}
                                          offLabel={getMessage(
                                            "compontents.toogle.disabled"
                                          )}
                                          disabled={restartRequired}
                                          visibleLabels
                                        />
                                        {!isEmpty(stringAttributes) && (
                                          <Select
                                            name={`collectionSettings-${uid}-entryLabel`}
                                            label={getMessage(
                                              "page.settings.form.entryLabel.label"
                                            )}
                                            placeholder={getMessage(
                                              "page.settings.form.entryLabel.placeholder"
                                            )}
                                            hint={getMessage(
                                              "page.settings.form.entryLabel.hint"
                                            )}
                                            onClear={() =>
                                              setFieldValue(
                                                "entryLabel",
                                                changeEntryLabelFor(
                                                  uid,
                                                  values.entryLabel
                                                )
                                              )
                                            }
                                            value={values.entryLabel[uid] || []}
                                            onChange={(value: ToBeFixed) =>
                                              setFieldValue(
                                                "entryLabel",
                                                changeEntryLabelFor(
                                                  uid,
                                                  values.entryLabel,
                                                  value
                                                )
                                              )
                                            }
                                            multi
                                            withTags
                                            disabled={restartRequired}
                                          >
                                            {stringAttributes.map((key) => (
                                              <Option
                                                key={`collectionSettings-${uid}-entryLabel-${key}`}
                                                value={key}
                                              >
                                                {capitalize(
                                                  key.split("_").join(" ")
                                                )}
                                              </Option>
                                            ))}
                                          </Select>
                                        )}
                                      </Stack>
                                    </Box>
                                  </AccordionContent>
                                </Accordion>
                              );
                            })}
                          </AccordionGroup>
                        </GridItem>
                      )}
                    </Grid>
                  </Stack>
                </Box>

                <Box {...boxDefaultProps}>
                  <Stack size={4}>
                    <Typography variant="delta" as="h2">
                      {getMessage("page.settings.section.additional")}
                    </Typography>
                    <Grid gap={4}>
                      <GridItem col={6} xs={12}>
                        <ToggleInput
                          name="badWords"
                          label={getMessage(
                            "page.settings.form.badWords.label"
                          )}
                          hint={getMessage("page.settings.form.badWords.hint")}
                          checked={values.badWords}
                          onChange={({ target: { checked } }: ToBeFixed) =>
                            setFieldValue("badWords", checked, false)
                          }
                          onLabel={getMessage("compontents.toogle.enabled")}
                          offLabel={getMessage("compontents.toogle.disabled")}
                          disabled={restartRequired}
                        />
                      </GridItem>
                      {isGQLPluginEnabled && (
                        <GridItem col={6} xs={12}>
                          <ToggleInput
                            name="gqlAuthEnabled"
                            label={getMessage(
                              "page.settings.form.gqlAuth.label"
                            )}
                            hint={getMessage("page.settings.form.gqlAuth.hint")}
                            checked={values.gqlAuthEnabled}
                            onChange={({ target: { checked } }: ToBeFixed) =>
                              setFieldValue("gqlAuthEnabled", checked, false)
                            }
                            onLabel={getMessage("compontents.toogle.enabled")}
                            offLabel={getMessage("compontents.toogle.disabled")}
                            disabled={restartRequired}
                          />
                        </GridItem>
                      )}
                    </Grid>
                  </Stack>
                </Box>

                <Box {...boxDefaultProps}>
                  <Stack size={4}>
                    <Typography variant="delta" as="h2">
                      {getMessage("page.settings.section.client")}
                    </Typography>
                    <Grid gap={4}>
                      <GridItem col={3} xs={12}>
                        <TextInput
                          type="url"
                          name="clientUrl"
                          label={getMessage(
                            "page.settings.form.client.url.label"
                          )}
                          hint={getMessage("page.settings.form.client.url.hint")}
                          value={values.clientUrl}
                          onChange={({ target: { value } }: ToBeFixed) =>
                            setFieldValue("clientUrl", value, false)
                          }
                          disabled={restartRequired}
                        />
                      </GridItem>
                      <GridItem col={3} xs={12}>
                        <TextInput
                          type="email"
                          name="clientEmail"
                          label={getMessage(
                            "page.settings.form.client.email.label"
                          )}
                          hint={getMessage("page.settings.form.client.email.hint")}
                          value={values.clientEmail}
                          onChange={({ target: { value } }: ToBeFixed) =>
                            setFieldValue("clientEmail", value, false)
                          }
                          disabled={restartRequired}
                        />
                      </GridItem>
                      <GridItem col={6} xs={12}>
                        <Select
                          name="moderatorRoles"
                          label={getMessage(
                            "page.settings.form.moderatorRoles.label"
                          )}
                          placeholder={getMessage(
                            "page.settings.form.moderatorRoles.placeholder"
                          )}
                          hint={getMessage(
                            "page.settings.form.moderatorRoles.hint"
                          )}
                          onClear={() =>
                            setFieldValue("moderatorRoles", [], false)
                          }
                          value={values.moderatorRoles}
                          onChange={(value: ToBeFixed) =>
                            setFieldValue("moderatorRoles", value, false)
                          }
                          disabled={restartRequired}
                          multi
                          withTags
                        >
                          {allRoles.map(({ code, name }: ToBeFixed) => (
                            <Option key={code} value={code}>
                              {name}
                            </Option>
                          ))}
                        </Select>
                      </GridItem>
                    </Grid>
                  </Stack>
                </Box>

                <CheckPermissions
                  permissions={pluginPermissions.settingsChange}
                >
                  <Box {...boxDefaultProps}>
                    <Stack size={4}>
                      <Stack size={2}>
                        <Typography variant="delta" as="h2">
                          {getMessage("page.settings.section.restore")}
                        </Typography>
                        <Typography variant="pi" as="h4">
                          {getMessage("page.settings.section.restore.subtitle")}
                        </Typography>
                      </Stack>
                      <Grid gap={4}>
                        <GridItem col={6}>
                          <Button
                            variant="danger-light"
                            startIcon={<Refresh />}
                            onClick={handleRestoreConfirmation}
                          >
                            {getMessage("page.settings.actions.restore")}
                          </Button>

                          <ConfirmationDialog
                            isVisible={restoreConfigmationVisible}
                            isActionAsync={restoreMutation.isLoading}
                            header={getMessage(
                              "page.settings.actions.restore.confirmation.header"
                            )}
                            labelConfirm={getMessage(
                              "page.settings.actions.restore.confirmation.button.confirm"
                            )}
                            iconConfirm={<Refresh />}
                            onConfirm={handleRestoreConfiguration}
                            onCancel={handleRestoreCancel}
                          >
                            {getMessage(
                              "page.settings.actions.restore.confirmation.description"
                            )}
                          </ConfirmationDialog>
                        </GridItem>
                      </Grid>
                    </Stack>
                  </Box>
                </CheckPermissions>
              </Stack>
            </ContentLayout>
          </Form>
        )}
      </Formik>
    </Main>
  );
}
Example #20
Source File: txBuilder.ts    From nautilus-wallet with MIT License 4 votes vote down vote up
private async _sign(
    unsigned: UnsignedTransaction,
    unspentBoxes: ErgoBoxes,
    dataInputBoxes: ErgoBoxes,
    context: SignContext
  ) {
    const sigmaRust = wasmModule.SigmaRust;

    if (this._useLedger) {
      let ledgerApp!: ErgoLedgerApp;

      try {
        ledgerApp = new ErgoLedgerApp(await WebUSBTransport.create())
          .useAuthToken()
          .enableDebugMode();
        this.sendCallback({
          connected: true,
          appId: ledgerApp.authToken,
          deviceModel: ledgerApp.transport.deviceModel?.id.toString() ?? LedgerDeviceModelId.nanoX
        });
      } catch (e) {
        this.sendCallback({
          connected: false,
          loading: false,
          state: LedgerState.deviceNotFound
        });

        throw e;
      }

      try {
        const inputs: UnsignedBox[] = [];
        const outputs: BoxCandidate[] = [];

        for (let i = 0; i < unsigned.inputs().len(); i++) {
          const input = unsigned.inputs().get(i);
          const box = findBox(unspentBoxes, input.box_id().to_str());
          if (!box) {
            throw Error(`Input ${input.box_id().to_str()} not found in unspent boxes.`);
          }

          const ergoTree = box.ergo_tree().to_base16_bytes().toString();
          const path =
            find(this._from, (a) => a.script === addressFromErgoTree(ergoTree)) ??
            first(this._from);

          if (!path) {
            throw Error(`Unable to find a sign path for ${input.box_id().to_str()}.`);
          }

          inputs.push({
            txId: box.tx_id().to_str(),
            index: box.index(),
            value: box.value().as_i64().to_str(),
            ergoTree: Buffer.from(box.ergo_tree().sigma_serialize_bytes()),
            creationHeight: box.creation_height(),
            tokens: mapTokens(box.tokens()),
            additionalRegisters: Buffer.from(box.serialized_additional_registers()),
            extension: Buffer.from(input.extension().sigma_serialize_bytes()),
            signPath: `${DERIVATION_PATH}/${path.index}`
          });
        }
        for (let i = 0; i < unsigned.output_candidates().len(); i++) {
          const wasmOutput = unsigned.output_candidates().get(i);

          outputs.push({
            value: wasmOutput.value().as_i64().to_str(),
            ergoTree: Buffer.from(wasmOutput.ergo_tree().sigma_serialize_bytes()),
            creationHeight: wasmOutput.creation_height(),
            tokens: mapTokens(wasmOutput.tokens()),
            registers: this.serializeRegisters(wasmOutput)
          });
        }

        this.sendCallback({
          statusText: "Waiting for device transaction signing confirmation..."
        });
        const proofs = await ledgerApp.signTx(
          {
            inputs,
            dataInputs: [],
            outputs,
            distinctTokenIds: unsigned.distinct_token_ids(),
            changeMap: {
              address: context.bip32.deriveAddress(this._changeIndex ?? 0).script,
              path: `${DERIVATION_PATH}/${this._changeIndex}`
            }
          },
          MAINNET ? Network.Mainnet : Network.Testnet
        );

        this.sendCallback({
          screenText: "Signed",
          statusText: "Sending transaction..."
        });

        return wasmModule.SigmaRust.Transaction.from_unsigned_tx(unsigned, proofs);
      } catch (e) {
        if (e instanceof DeviceError) {
          const resp = {
            loading: false,
            state: LedgerState.error,
            statusText: ""
          };

          switch (e.code) {
            case RETURN_CODE.DENIED:
              resp.statusText = "Transaction signing denied.";
              break;
            case RETURN_CODE.INTERNAL_CRYPTO_ERROR:
              resp.statusText =
                "It looks like your device is locked. Make sure it is unlocked before proceeding.";
              break;
            default:
              resp.statusText = `[Device error] ${e.message}`;
          }

          this.sendCallback(resp);
        }

        throw e;
      } finally {
        ledgerApp.transport.close();
      }
    }

    const wallet = this.buildWallet(this._from, context.bip32);
    const blockHeaders = sigmaRust.BlockHeaders.from_json(context.blockHeaders);
    const preHeader = sigmaRust.PreHeader.from_block_header(blockHeaders.get(0));
    const signContext = new sigmaRust.ErgoStateContext(preHeader, blockHeaders);
    const signed = wallet.sign_transaction(signContext, unsigned, unspentBoxes, dataInputBoxes);
    return signed;
  }
Example #21
Source File: circleci_analyzer_v2.ts    From CIAnalyzer with MIT License 4 votes vote down vote up
createWorkflowReport( pipeline: Pipeline, workflow: Workflow): WorkflowReport {
    const repository = workflow.project_slug.split('/').slice(1).join('/')
    const { workflowName, workflowId, buildNumber, workflowRunId }
      = this.createWorkflowParams(workflow.name, repository, pipeline.number)

    const jobReports: JobReport[] = workflow.jobs.map((job) => {
      const stepReports: StepReport[] = job.steps
        .filter((step) => {
          const action = first(step.actions)!
          // NOTE: Ignore background step (ex. Setup service container image step)
          return action.background === false
        })
        .map((step) => {
          const action = first(step.actions)!
          const startedAt = new Date(action.start_time)
          // NOTE: Sometimes action.end_time will be broken, so it should be replaced when it's value is invalid.
          const validatedEndTime = action.end_time ?? action.start_time
          const completedAt = new Date(validatedEndTime)
          // step
          return {
            name: action.name,
            status: this.normalizeStatus(action.status),
            number: action.step,
            startedAt,
            completedAt,
            stepDurationSec: diffSec(startedAt, completedAt)
          }
        })

      const startedAt = new Date(job.started_at)
      const completedAt = new Date(job.stopped_at ?? job.started_at)
      const url = new URL(`pipelines/${workflow.project_slug}/${workflow.pipeline_number}/workflows/${workflow.id}`, this.htmlBaseUrl)
      // job
      return {
        workflowRunId,
        buildNumber: job.job_number,
        jobId: job.id,
        jobName: job.name,
        status: this.normalizeStatus(job.status as CircleciStatus),
        startedAt,
        completedAt,
        jobDurationSec: diffSec(startedAt, completedAt),
        sumStepsDurationSec: secRound(sumBy(stepReports, 'stepDurationSec')),
        steps: stepReports,
        url: url.toString(),
        executorClass: job.detail.executor.resource_class,
        executorType: job.detail.executor.type,
        executorName: '',
      }
    })

    const sortedJobs = sortBy(workflow.jobs, 'job_number')
    const firstJob = first(sortedJobs)
    const createdAt = new Date(workflow.created_at) 
    // Sometimes worklfow has invalid timestamps, so remediate it.
    const startedAt = new Date(firstJob?.started_at ?? workflow.created_at)
    const completedAt = new Date(workflow.stopped_at ?? startedAt)
    const status = this.normalizeWorkflowStatus(workflow.status)
    // workflow
    return {
      service: 'circleci',
      workflowId,
      buildNumber,
      workflowRunId,
      workflowName,
      createdAt,
      trigger: pipeline.trigger.type,
      status,
      repository,
      headSha: pipeline.vcs.revision,
      branch: pipeline.vcs.branch ?? '',
      tag: pipeline.vcs.tag ?? '',
      jobs: jobReports,
      startedAt,
      completedAt,
      workflowDurationSec: diffSec(startedAt, completedAt),
      sumJobsDurationSec: secRound(sumBy(jobReports, 'sumStepsDurationSec')),
      successCount: (status === 'SUCCESS') ? 1 : 0,
      parameters: [],
      queuedDurationSec: diffSec(createdAt, startedAt), // firstJob.started_at - workflow.created_at
      commitMessage: pipeline.vcs.commit?.subject ?? '',
      actor: pipeline.trigger.actor.login,
      url: '',
    }
  }
Example #22
Source File: PeptideContext.tsx    From nextclade with MIT License 4 votes vote down vote up
export function PeptideContext({ group }: PeptideContextProps) {
  const { t } = useTranslationSafe()

  const { changes, contextNucRange, codonAaRange, refContext, queryContext } = group
  const refCodons = refContext.match(/.{1,3}/g)
  const queryCodons = queryContext.match(/.{1,3}/g)

  const firstChange = first(queryCodons)
  const lastChange = last(refCodons)

  const firstRefCodon = first(refCodons)
  const lastRefCodon = last(refCodons)

  const firstQryCodon = first(queryCodons)
  const lastQryCodon = last(queryCodons)

  if (
    !refCodons ||
    !queryCodons ||
    !firstChange ||
    !lastChange ||
    !firstRefCodon ||
    !firstQryCodon ||
    !lastRefCodon ||
    !lastQryCodon
  ) {
    return null
  }

  const changesAndCodons = safeZip3(changes, refCodons.slice(1, -1), queryCodons.slice(1, -1))

  let itemsBegin = changesAndCodons
  let itemsEnd: typeof changesAndCodons = []
  if (changesAndCodons.length > 6) {
    itemsBegin = changesAndCodons.slice(0, 3)
    itemsEnd = changesAndCodons.slice(-3)
  }

  const width = (itemsBegin.length + itemsEnd.length + 2) * 80 + 80

  return (
    <Table borderless className="mb-1 mx-2" $width={width}>
      <tbody>
        <tr>
          <td>
            <TableNuc>
              <tbody>
                <TrName>
                  <TdName>{t('Codon')}</TdName>
                </TrName>
                <TrName>
                  <TdName>{t('Ref. AA')}</TdName>
                </TrName>
                <TrName>
                  <TdName>{t('Ref.')}</TdName>
                </TrName>
                <TrName>
                  <TdName>{t('Query')}</TdName>
                </TrName>
                <TrName>
                  <TdName>{t('Query AA')}</TdName>
                </TrName>
                <TrName>
                  <TdName>{t('1st nuc.')}</TdName>
                </TrName>
              </tbody>
            </TableNuc>
          </td>

          <PeptideContextCodon
            refCodon={firstRefCodon}
            queryCodon={firstQryCodon}
            codon={codonAaRange.begin - 1}
            nucBegin={contextNucRange.begin}
          />

          {itemsBegin.map(renderCodons)}
          {itemsEnd.length > 0 && <PeptideContextEllipsis />}
          {itemsEnd.length > 0 && itemsEnd.map(renderCodons)}

          <PeptideContextCodon
            refCodon={lastRefCodon}
            queryCodon={lastQryCodon}
            codon={codonAaRange.end}
            nucBegin={contextNucRange.end - 3}
          />
        </tr>
      </tbody>
    </Table>
  )
}
Example #23
Source File: circleci_analyzer.ts    From CIAnalyzer with MIT License 4 votes vote down vote up
createWorkflowReport( workflowRun: WorkflowRun, jobs: SingleBuildResponse[], tagMap: RepositoryTagMap): WorkflowReport {
    const sortedJobs = sortBy(jobs, 'build_num')
    const firstJob = first(sortedJobs)!
    const lastJob = last(sortedJobs)!
    const repository = `${firstJob.username}/${firstJob.reponame}`
    const { workflowName, workflowId, buildNumber, workflowRunId }
      = this.createWorkflowParams(workflowRun.workflow_name, repository, lastJob.build_num)

    const jobReports: JobReport[] = sortedJobs.map((job) => {
      const stepReports: StepReport[] = job.steps
        .filter((step) => {
          const action = first(step.actions)!
          // NOTE: Ignore background step (ex. Setup service container image step)
          return action.background === false
        })
        .map((step) => {
          const action = first(step.actions)!
          const startedAt = new Date(action.start_time)
          // NOTE: Sometimes action.end_time will be broken, so it should be replaced when it's value is invalid.
          const validatedEndTime = action.end_time ?? action.start_time
          const completedAt = new Date(validatedEndTime)
          // step
          return {
            name: action.name,
            status: this.normalizeStatus(action.status),
            number: action.step,
            startedAt,
            completedAt,
            stepDurationSec: diffSec(startedAt, completedAt)
          }
        })

      const startedAt = new Date(job.start_time)
      const completedAt = new Date(job.stop_time)
      // job
      return {
        workflowRunId,
        buildNumber: job.build_num,
        jobId: job.workflows.job_id,
        jobName: job.workflows.job_name,
        status: this.normalizeStatus(job.status),
        startedAt,
        completedAt,
        jobDurationSec: diffSec(startedAt, completedAt),
        sumStepsDurationSec: secRound(sumBy(stepReports, 'stepDurationSec')),
        steps: stepReports,
        url: '',
        executorClass: '',
        executorType: '',
        executorName: '',
      }
    })

    const startedAt = min(jobReports.map((job) => job.startedAt ))!
    const completedAt = max(jobReports.map((job) => job.completedAt ))!
    const status = this.estimateWorkflowStatus(jobReports)
    const createdAt = min(jobs.map((job) => new Date(job.queued_at)))!
    // workflow
    return {
      service: 'circleci',
      workflowId,
      buildNumber,
      workflowRunId,
      workflowName,
      createdAt,
      trigger: firstJob.why,
      status,
      repository,
      headSha: firstJob.vcs_revision,
      branch: firstJob.branch,
      tag: tagMap.get(firstJob.vcs_revision) ?? '',
      jobs: jobReports,
      startedAt,
      completedAt,
      workflowDurationSec: diffSec(startedAt, completedAt),
      sumJobsDurationSec: secRound(sumBy(jobReports, 'sumStepsDurationSec')),
      successCount: (status === 'SUCCESS') ? 1 : 0,
      parameters: [],
      queuedDurationSec: diffSec(createdAt, min(jobs.map((job) => new Date(job.start_time)))!),
      commitMessage: '',
      actor: '',
      url: '',
    }
  }