lodash#omitBy TypeScript Examples

The following examples show how to use lodash#omitBy. 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: utils.ts    From ovineherd with Apache License 2.0 7 votes vote down vote up
getApiQuery = (data: any) => {
  const { type, query, names, ...reset } = data
  const queryObj: any = {}
  const namesObj: any = {}

  const omitInvalidParams = (p: object): any =>
    omitBy(p, (v) => v === '' || typeof v === 'undefined')

  map(reset, (val, key) => {
    if (key.startsWith('q_')) {
      queryObj[key.slice(2)] = val
    } else if (key.startsWith('n_')) {
      namesObj[key.slice(2)] = val
    }
  })

  const queryParams = {
    type,
    ...reset,
    query: getApiConditionStr(
      omitInvalidParams({
        ...query,
        ...queryObj,
      })
    ),
    names: getApiConditionStr(
      omitInvalidParams({
        ...names,
        ...namesObj,
      }),
      true
    ),
  }

  return omitInvalidParams(queryParams)
}
Example #2
Source File: utils.ts    From ovineherd with Apache License 2.0 7 votes vote down vote up
getApiQuery = (data: any) => {
  const { type, query, names, ...reset } = data
  const queryObj: any = {}
  const namesObj: any = {}

  const omitInvalidParams = (p: object): any =>
    omitBy(p, (v) => v === '' || typeof v === 'undefined')

  map(reset, (val, key) => {
    if (key.startsWith('q_')) {
      queryObj[key.slice(2)] = val
    } else if (key.startsWith('n_')) {
      namesObj[key.slice(2)] = val
    }
  })

  const queryParams = {
    type,
    ...reset,
    query: getApiConditionStr(
      omitInvalidParams({
        ...query,
        ...queryObj,
      })
    ),
    names: getApiConditionStr(
      omitInvalidParams({
        ...names,
        ...namesObj,
      }),
      true
    ),
  }

  return omitInvalidParams(queryParams)
}
Example #3
Source File: augment-site-data.ts    From aqualink-app with MIT License 6 votes vote down vote up
async function getAugmentedData(
  site: Site,
  regionRepository: Repository<Region>,
) {
  const [longitude, latitude] = (site.polygon as Point).coordinates;

  const region =
    site.region || (await getRegion(longitude, latitude, regionRepository));

  const MMM = await getMMM(longitude, latitude);
  if (MMM === null) {
    console.warn(
      `Max Monthly Mean appears to be null for Site ${site.name} at (lat, lon): (${latitude}, ${longitude}) `,
    );
  }

  const timezones = geoTz(latitude, longitude);

  return omitBy(
    {
      region,
      timezone: timezones.length > 0 ? timezones[0] : null,
      maxMonthlyMean: MMM,
    },
    isNil,
  );
}
Example #4
Source File: collections.service.ts    From aqualink-app with MIT License 6 votes vote down vote up
async update(collectionId: number, updateCollectionDto: UpdateCollectionDto) {
    const collection = await this.collectionRepository.findOne(collectionId);

    if (!collection) {
      throw new NotFoundException(
        `Collection with ID ${collectionId} not found.`,
      );
    }

    const { name, isPublic, userId, addSiteIds, removeSiteIds } =
      updateCollectionDto;

    const filteredAddSiteIds = addSiteIds?.filter(
      (siteId) => !collection.siteIds.includes(siteId),
    );

    await this.collectionRepository
      .createQueryBuilder('collection')
      .relation('sites')
      .of(collection)
      .addAndRemove(filteredAddSiteIds || [], removeSiteIds || []);

    await this.collectionRepository.update(
      {
        id: collectionId,
      },
      {
        ...omitBy({ name, isPublic }, isUndefined),
        ...(userId !== undefined ? { user: { id: userId } } : {}),
      },
    );

    return this.collectionRepository.findOne(collection!.id);
  }
Example #5
Source File: covertUseResolvesToFormValue.ts    From next-basics with GNU General Public License v3.0 6 votes vote down vote up
export function covertUseResolvesToFormValue(
  resolveData: ResolveConf
): EventFormField {
  // 只转换 useProvider 最新的使用方式,name,fields,ref 的遗留写法会提示用户转到 yaml 编辑
  const providerType = isFlowAPiProvider(
    (resolveData as UseProviderResolveConf).useProvider
  )
    ? "flow"
    : "provider";

  const provider = (resolveData as UseProviderResolveConf).useProvider;

  return {
    handlerType: HandlerType.UseProvider,
    providerType,
    transformMapArray: resolveData.transformMapArray ?? "auto",
    ...(providerType === "flow" ? { flow: provider } : { provider }),
    ...safeDumpFields(
      omitBy(
        {
          if: resolveData.if,
          args: (resolveData as UseProviderResolveConf).args,
          transform: resolveData.transform,
          transformFrom: resolveData.transformFrom,
          onReject: resolveData.onReject,
        },
        isNil
      )
    ),
  };
}
Example #6
Source File: resource.ts    From ue4-remote-control with MIT License 6 votes vote down vote up
async makeRequest<Req, Res>(method: HttpMethodCalls, endpoint: string, body: Req): Promise<Res> {
        const options: rp.Options = {
            uri: `http://localhost:${UE4_SERVER_PORT}${endpoint}`,
            method,
            body: omitBy(body, isUndefined),
            json: true
        };
        d('>> request', options)
        const result = await rp(options) as Res
        d('<< result', result)
        return result
    }
Example #7
Source File: liveData.ts    From aqualink-app with MIT License 5 votes vote down vote up
getLiveData = async (
  site: Site,
  isDeployed: boolean,
): Promise<SofarLiveData> => {
  console.time(`getLiveData for site ${site.id}`);
  const { polygon, sensorId, maxMonthlyMean } = site;
  // TODO - Accept Polygon option
  const [longitude, latitude] = (polygon as Point).coordinates;

  const now = new Date();

  const [spotterRawData, degreeHeatingDays, satelliteTemperature] =
    await Promise.all([
      sensorId && isDeployed ? getSpotterData(sensorId) : undefined,
      getDegreeHeatingDays(latitude, longitude, now, maxMonthlyMean),
      getSofarHindcastData(
        SofarModels.NOAACoralReefWatch,
        sofarVariableIDs[SofarModels.NOAACoralReefWatch]
          .analysedSeaSurfaceTemperature,
        latitude,
        longitude,
        now,
        96,
      ),
    ]);

  const spotterData = spotterRawData
    ? {
        topTemperature: getLatestData(spotterRawData.topTemperature),
        bottomTemperature: getLatestData(spotterRawData.bottomTemperature),
        longitude:
          spotterRawData.longitude && getLatestData(spotterRawData.longitude),
        latitude:
          spotterRawData.latitude && getLatestData(spotterRawData.latitude),
      }
    : {};

  const filteredValues = omitBy(
    {
      degreeHeatingDays,
      satelliteTemperature:
        satelliteTemperature && getLatestData(satelliteTemperature),
      // Override all possible values with spotter data.
      ...spotterData,
    },
    (data) => isNil(data?.value) || data?.value === 9999,
  );

  const dailyAlertLevel = calculateAlertLevel(
    maxMonthlyMean,
    filteredValues?.satelliteTemperature?.value,
    degreeHeatingDays?.value,
  );

  console.timeEnd(`getLiveData for site ${site.id}`);

  return {
    site: { id: site.id },
    ...filteredValues,
    ...(spotterData.longitude &&
      spotterData.latitude && {
        spotterPosition: {
          longitude: spotterData.longitude,
          latitude: spotterData.latitude,
        },
      }),
    dailyAlertLevel,
  };
}
Example #8
Source File: requests.ts    From aqualink-app with MIT License 5 votes vote down vote up
generateUrlQueryParams = (params: Record<string, any>) => {
  const stringifiedParams = new URLSearchParams({
    ...omitBy(params, isUndefined),
  }).toString();

  return stringifiedParams.length ? `?${stringifiedParams}` : "";
}
Example #9
Source File: omitNilCssRules.ts    From excalideck with MIT License 5 votes vote down vote up
export default function omitNilCssRules(
    cssRules: Record<string, string | undefined | null>
): Record<string, string> {
    return omitBy<any>(cssRules, isNil);
}
Example #10
Source File: omit-undefined-shallow.ts    From js-client with MIT License 5 votes vote down vote up
omitUndefinedShallow = <O>(o: O): { [P in keyof O]: Exclude<O[P], undefined> } =>
	omitBy(o, isUndefined) as any
Example #11
Source File: dailyData.ts    From aqualink-app with MIT License 4 votes vote down vote up
/* eslint-disable no-console */
export async function getSitesDailyData(
  connection: Connection,
  endOfDate: Date,
  siteIds?: number[],
) {
  const siteRepository = connection.getRepository(Site);
  const dailyDataRepository = connection.getRepository(DailyData);
  const exclusionDatesRepository = connection.getRepository(ExclusionDates);
  const allSites = await siteRepository.find(
    siteIds && siteIds.length > 0
      ? {
          where: {
            id: In(siteIds),
          },
        }
      : {},
  );
  const start = new Date();
  console.log(
    `Updating ${allSites.length} sites for ${endOfDate.toDateString()}.`,
  );
  await Bluebird.map(
    allSites,
    async (site) => {
      const excludedDates = await getExclusionDates(
        exclusionDatesRepository,
        site.sensorId,
      );

      const dailyDataInput = await getDailyData(site, endOfDate, excludedDates);

      // If no data returned from the update function, skip
      if (hasNoData(dailyDataInput)) {
        console.log('No data has been fetched. Skipping...');
        return;
      }

      // Calculate weekly alert level
      const weeklyAlertLevel = await getWeeklyAlertLevel(
        dailyDataRepository,
        endOfDate,
        site,
      );

      const entity = dailyDataRepository.create({
        ...dailyDataInput,
        weeklyAlertLevel: getMaxAlert(
          dailyDataInput.dailyAlertLevel,
          weeklyAlertLevel,
        ),
      });
      try {
        // Try to save daily data entity
        await dailyDataRepository.save(entity);
      } catch (err) {
        // Update instead of insert
        if (get(err, 'constraint') === 'no_duplicated_date') {
          const filteredData = omitBy(entity, isNil);
          await dailyDataRepository
            .createQueryBuilder('dailyData')
            .update()
            .where('site_id = :site_id', { site_id: site.id })
            .andWhere('Date(date) = Date(:date)', { date: entity.date })
            .set(filteredData)
            .execute();
        } else {
          console.error(
            `Error updating data for Site ${
              site.id
            } & ${endOfDate.toDateString()}: ${err}.`,
          );
        }
      }
    },
    { concurrency: 8 },
  );
  console.log(
    `Updated ${allSites.length} sites in ${
      (new Date().valueOf() - start.valueOf()) / 1000
    } seconds`,
  );
}
Example #12
Source File: covertEventToFormValue.ts    From next-basics with GNU General Public License v3.0 4 votes vote down vote up
export function covertEventToFormValue(
  handler: BrickEventHandler
): EventFormField {
  const handlerType = getHandlerType(handler);

  if (handlerType === HandlerType.BuiltinAction) {
    //特殊处理 history.push / segueId.push
    if (
      ["segue.push", "history.push"].includes(
        (handler as BuiltinBrickEventHandler).action
      )
    ) {
      const args = (handler as BuiltinBrickEventHandler).args ?? [];
      return {
        handlerType,
        action: (handler as BuiltinBrickEventHandler).action,
        [(handler as BuiltinBrickEventHandler).action === "segue.push"
          ? "segueId"
          : "path"]: args[0] as string,
        ...safeDumpFields(
          omitBy(
            {
              if: handler.if,
              args: args.length > 1 ? args.slice(1) : undefined,
              callback: (handler as BuiltinBrickEventHandler).callback,
            },
            isNil
          )
        ),
      };
    }

    return {
      handlerType,
      action: (handler as BuiltinBrickEventHandler).action,
      ...safeDumpFields(
        omitBy(
          {
            if: handler.if,
            args: (handler as BuiltinBrickEventHandler).args,
            callback: (handler as BuiltinBrickEventHandler).callback,
          },
          isNil
        )
      ),
    };
  } else if (handlerType === HandlerType.UseProvider) {
    const providerType = isFlowAPiProvider(
      (handler as UseProviderEventHandler).useProvider
    )
      ? "flow"
      : "provider";

    const poll = (handler as UseProviderEventHandler).poll;
    const provider = (handler as UseProviderEventHandler).useProvider;
    return {
      handlerType,
      providerType,
      useProviderMethod:
        (handler as UseProviderEventHandler).method || "resolve",
      pollEnabled: poll?.enabled,
      ...(providerType === "flow" ? { flow: provider } : { provider }),
      ...safeDumpFields(
        omitBy(
          {
            if: handler.if,
            args: (handler as UseProviderEventHandler).args,
            callback: (handler as UseProviderEventHandler).callback,
            poll: poll ? omit(poll, "enabled") : undefined,
          },
          isNil
        )
      ),
    };
  } else if (handlerType === CustomBrickEventType.SetProps) {
    const selectorType = "targetRef" in handler ? "targetRef" : "target";
    return {
      handlerType: HandlerType.CustomBrick,
      brickEventType: CustomBrickEventType.SetProps,
      selectorType,
      brickSelector: (handler as SetPropsCustomBrickEventHandler)[
        selectorType
      ] as string,
      ...safeDumpFields(
        omitBy(
          {
            if: handler.if,
            properties: (handler as SetPropsCustomBrickEventHandler).properties,
          },
          isNil
        )
      ),
    };
  } else if (handlerType === CustomBrickEventType.ExecuteMethod) {
    const selectorType = "targetRef" in handler ? "targetRef" : "target";
    return {
      handlerType: HandlerType.CustomBrick,
      brickEventType: CustomBrickEventType.ExecuteMethod,
      selectorType,
      brickSelector: (handler as ExecuteCustomBrickEventHandler)[
        selectorType
      ] as string,
      method: (handler as ExecuteCustomBrickEventHandler).method,
      ...safeDumpFields(
        omitBy(
          {
            if: handler.if,
            args: (handler as ExecuteCustomBrickEventHandler).args,
            callback: (handler as ExecuteCustomBrickEventHandler).callback,
          },
          isNil
        )
      ),
    };
  } else {
    return {} as EventFormField;
  }
}
Example #13
Source File: index.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
BatchProcessing = ({ afterDelete }: IProps) => {
  const [visible, setVisible] = useState(false);
  const [caseTotal, choosenInfo] = testCaseStore.useStore((s) => [s.caseTotal, s.choosenInfo]);
  const { isAll, primaryKeys } = choosenInfo;
  const [params, query] = routeInfoStore.useStore((s) => [s.params, s.query]);
  const { openRemarkModal } = testPlanStore.reducers;
  const { planUserCaseBatch, deleteRelations, exportFiles } = testPlanStore.effects;
  const checked = isAll || !!size(primaryKeys);
  const afterDeleteRef = React.useRef(afterDelete);
  const onClick = useCallback(
    ({ key }: any) => {
      if (key !== 'excel' && (!caseTotal || !checked)) {
        message.error(i18n.t('dop:After the use case is selected, the batch operation can be performed.'));
        return;
      }
      let selectProjectId;
      let searchQuery = {};
      switch (key) {
        case 'delete':
          Modal.confirm({
            title: i18n.t('Remove'),
            content: i18n.t('dop:plan-remove-case-confirm'),
            onOk: () => deleteRelations({ type: 'multi', relationIDs: [] }).then(afterDeleteRef.current),
          });
          break;
        case 'actor':
          setVisible(true);
          break;
        case 'remark':
          openRemarkModal({ type: 'multi', remark: '' });
          break;
        case 'excel':
          selectProjectId = params.projectId;
          searchQuery = omit(formatQuery(query), ['selectProjectId', 'testPlanId', 'testSetId', 'eventKey']);

          // eslint-disable-next-line no-case-declarations
          let queryParam = qs.stringify(omitBy({ selectProjectId, ...params }, isNull), { arrayFormat: 'none' });
          queryParam += qs.stringify(searchQuery, { arrayFormat: 'none' });
          queryParam += `&${qs.stringify({ relationID: primaryKeys }, { arrayFormat: 'none' })}`;
          exportFiles(`${queryParam}&fileType=excel`);
          break;
        default:
          break;
      }
    },
    [caseTotal, checked, deleteRelations, exportFiles, openRemarkModal, params, query, primaryKeys],
  );

  const menus = useMemo(() => {
    return (
      <Menu onClick={onClick}>
        <Menu.Item key="delete">
          <span>{i18n.t('Delete')}</span>
        </Menu.Item>
        <Menu.Item key="actor">
          <span>{i18n.t('dop:Change Executor')}</span>
        </Menu.Item>
        {/* <Menu.Item key="remark">
          <span>添加备注</span>
        </Menu.Item> */}
        <Menu.Item key="excel">
          <span>{i18n.t('dop:Export Excel')}</span>
        </Menu.Item>
      </Menu>
    );
  }, [onClick]);

  const onCancel = () => {
    setVisible(false);
  };

  const handleOk = (data: Pick<TEST_PLAN.PlanBatch, 'executorID'>) => {
    planUserCaseBatch(data).then(() => {
      onCancel();
    });
  };

  const fieldsList = [
    {
      label: i18n.t('dop:Executor'),
      name: 'executorID',
      getComp: () => <MemberSelector scopeType="project" scopeId={params.projectId} />,
    },
  ];
  return (
    <>
      <DropdownSelect overlay={menus} buttonText={i18n.t('dop:Batch Operations')} />
      <FormModal
        title={i18n.t('dop:Change Executor')}
        visible={visible}
        onOk={handleOk}
        onCancel={onCancel}
        fieldsList={fieldsList}
      />
    </>
  );
}
Example #14
Source File: index.tsx    From erda-ui with GNU Affero General Public License v3.0 4 votes vote down vote up
CaseDrawer = ({ visible, scope, onClose, afterClose, afterSave, caseList }: IProps) => {
  const params = routeInfoStore.useStore((s) => s.params);
  const caseDetail = testCaseStore.useStore((s) => s.caseDetail);
  const envList = testEnvStore.useStore((s) => s.envList);
  const dirName = testSetStore.useStore((s) => s.breadcrumbInfo.pathName);
  const { clearCaseDetail } = testCaseStore.reducers;
  const { editPartial, create: addTestCase, attemptTestApi, getCaseDetail } = testCaseStore.effects;
  const [isExecuting, fetchingDetail] = useLoading(testCaseStore, ['attemptTestApi', 'getCaseDetail']);
  const [{ fullData, titleIsEmpty }, updater] = useUpdate<IState>(initState);
  const drawer = React.useRef<{ saved: boolean }>({ saved: false });
  const activeElementRef = React.useRef<{ target: Element | null }>({ target: null });
  const editMode = !!caseDetail.id;
  React.useEffect(() => {
    if (caseDetail.id) {
      let apis = [];
      if (caseDetail.apis) {
        try {
          // 其他不用透传的放在rest里
          apis = caseDetail.apis.map(({ apiInfo, apiResponse, apiRequest, assertResult, status, ...rest }: any) => ({
            rest,
            ...JSON.parse(apiInfo),
            apiResponse,
            apiRequest,
            assertResult,
            status,
          }));
        } catch (error) {
          // do nothing
        }
      }
      updater.fullData({ ...caseDetail, apisFormat: apis });
    }
  }, [caseDetail, params.projectId, updater]);
  const shareLink = `${location.href.split('?')[0]}?${mergeSearch({ caseId: fullData.id }, true)}`;
  const updateDate = fromNow(caseDetail.updatedAt, { edgeNow: true });
  const handleClose = () => {
    onClose();
    clearCaseDetail();
    updater.fullData({ ...defaultData });
    updateSearch({ caseId: undefined });
  };
  const updateFullData = (key: string, value: any, autoSave = false) => {
    const newData = {
      ...fullData,
      [key]: value,
    };
    updater.fullData(newData);
    if (editMode && autoSave) {
      handleSave(false, newData);
    }
  };
  const handleAddInTitle = (type: string) => {
    switch (type) {
      case 'stepAndResults':
        updateFullData('stepAndResults', (fullData.stepAndResults || []).concat(getEmptyStep()));
        break;
      case 'apisFormat':
        updateFullData('apisFormat', [...fullData.apisFormat, getEmptyApi()]);
        break;
      default:
    }
  };
  const executeApi = (data: any, index: number, extra = { envId: 0 }) => {
    const newData = cloneDeep(data);
    if (newData.body.type) {
      newData.body.type = bodyTypeMap[newData.body.type] || newData.body.type;
    }
    const { url } = qs.parseUrl(newData.url);
    const api = {
      id: String(index),
      ...pick(newData, pickKeys),
      url,
    };
    // 这里的usecaseTestEnvID是testCaseID
    return attemptTestApi({ apis: [api], projectTestEnvID: extra.envId, usecaseTestEnvID: fullData.testCaseID });
  };
  const executeAllApi = (apiList: IApi[], extra = { envId: 0 }) => {
    const apis = apiList.map((item: any, index: number) => {
      const { url } = qs.parseUrl(item.url);
      const api = {
        id: String(index),
        ...pick(item, pickKeys),
        url,
      };
      return api;
    });
    // 这里的usecaseTestEnvID是testCaseID
    return attemptTestApi({ apis, projectTestEnvID: extra.envId, usecaseTestEnvID: fullData.testCaseID }).then(
      (result) => {
        updater.fullData({
          ...fullData,
          apisFormat: fullData.apisFormat.map((a: any, i: number) => ({ ...a, attemptTest: result[i] })),
        });
      },
    );
  };
  const checkName = (e: React.FocusEvent<HTMLInputElement>) => {
    const name = e.target.value;
    if (name === caseDetail.name) {
      return;
    }
    updater.titleIsEmpty(!name);
    if (editMode && name) {
      handleSave(false);
    }
  };
  const handleSave = async (close: boolean, data?: any) => {
    const newData = data || fullData;
    const errorInfo = doCheck(newData);
    if (errorInfo) {
      message.warning(errorInfo);
      return;
    }
    const { apisFormat, ...rests } = newData;
    const saveData = {
      ...rests,
      apis: apisFormat.map((item: IApi) => {
        const { apiResponse, assertResult, status, rest: restParams, attemptTest, ...info } = item;
        const saveApi = {
          ...restParams,
          apiResponse,
          assertResult,
          status,
          apiInfo: JSON.stringify(info),
        };
        return omitBy(saveApi, isUndefined);
      }),
    };
    const payload = editMode ? { ...saveData, id: caseDetail.testCaseID } : saveData;
    const request = editMode ? editPartial : addTestCase;
    const res = await request(payload);
    newData.id && getCaseDetail({ id: newData.id, scope });
    drawer.current.saved = true;
    if (afterSave) {
      await afterSave(saveData, editMode, res);
    }
    if (close) {
      handleClose();
    }
    if (!editMode) {
      updater.fullData({ ...defaultData });
    }
  };

  const handleAnyBlur = (e: React.FocusEvent) => {
    if (activeElementRef.current.target?.className.includes('ant-input')) {
      return;
    }

    if (!editMode) {
      return;
    }
    const { nodeName, classList } = e.target;
    const isMarkdownArea = classList.value.includes('section-container');
    // markdown编辑器需要blur后自行save
    if (['INPUT', 'TEXTAREA'].includes(nodeName) && !isMarkdownArea) {
      handleSave(false);
    }
  };

  const handleAnyMouseDown = React.useCallback((e: MouseEvent) => {
    activeElementRef.current.target = e.target as Element;
  }, []);

  React.useEffect(() => {
    if (visible) {
      window.addEventListener('mousedown', handleAnyMouseDown);
    } else {
      window.removeEventListener('mousedown', handleAnyMouseDown);
    }

    return () => {
      window.removeEventListener('mousedown', handleAnyMouseDown);
    };
  }, [visible, handleAnyMouseDown]);

  const handleVisibleChange = (v: boolean) => {
    if (!v) {
      afterClose && afterClose(drawer.current.saved);
      drawer.current.saved = false;
    }
  };

  const caseMetaData = React.useMemo(() => {
    return {
      priority: fullData.priority,
      createdAt: caseDetail.createdAt,
      creatorID: caseDetail.creatorID,
    };
  }, [fullData.priority, caseDetail.createdAt, caseDetail.creatorID]);

  const append =
    (scope === 'testPlan' && editMode) || !fullData.apisFormat.length ? null : (
      <span className="text-desc hover-active" onClick={() => executeAllApi(fullData.apisFormat, { envId: 0 })}>
        <SelectEnv
          envList={envList}
          onClick={(env: any) => {
            executeAllApi(fullData.apisFormat, { envId: env.id });
          }}
        >
          <>
            <ErdaIcon fill="black-4" type="play" size="16" />
            {i18n.t('Execute') + ' '}
            <span className="text-xs">({i18n.t('dop:Execute without environment')})</span>
          </>
        </SelectEnv>
      </span>
    );
  return (
    <Drawer
      className="case-drawer"
      width="908"
      placement="right"
      closable={false}
      maskClosable={editMode}
      visible={visible}
      destroyOnClose
      onClose={handleClose}
      afterVisibleChange={handleVisibleChange}
    >
      <Spin spinning={fetchingDetail}>
        <div className="case-drawer-header px-5 py-5">
          <div className="flex justify-between items-center">
            <div className="flex-1">
              <Input
                className={classnames('case-name text-lg font-medium text-normal', titleIsEmpty && 'error')}
                size="large"
                autoFocus
                placeholder={i18n.t('dop:Use case title (required)')}
                autoComplete="off"
                value={fullData.name}
                onChange={(e) => {
                  updateFullData('name', e.target.value);
                }}
                onBlur={checkName}
              />
            </div>
            <div className="case-drawer-header-op">
              {editMode ? (
                <>
                  <Copy selector=".copy-share-link" tipName={i18n.t('dop:share link')} />
                  <ErdaIcon
                    type="share-one"
                    className="cursor-copy copy-share-link ml-3 mt-1"
                    size="16"
                    data-clipboard-text={shareLink}
                  />
                </>
              ) : null}
              <ErdaIcon type="close" onClick={handleClose} className="ml-3 mt-1 cursor-pointer" size="16" />
            </div>
          </div>
          <div className="flex justify-between items-center mt-4">
            <Tooltip title={dirName && dirName.length < 40 ? null : dirName}>
              <div className="flex text-base nowrap mr-5 color-text-desc case-drawer-header-desc">
                <ErdaIcon type="wjj1" size="16" className="mr-1" fill="yellow" />
                <span className="truncate">{dirName}</span>
              </div>
            </Tooltip>
            {editMode && (
              <div className="inline-flex justify-between items-center">
                <UserInfo.RenderWithAvatar id={caseDetail.updaterID} />
                &nbsp;{i18n.t('dop:updated on')}&nbsp;{updateDate}
              </div>
            )}
          </div>
        </div>
        <div className="case-drawer-body flex justify-between">
          <div className="case-drawer-body-left flex-1 px-5 py-4">
            <div onBlurCapture={handleAnyBlur}>
              <ContentPanel title={i18n.t('dop:Preconditions')}>
                <MarkdownEditor
                  value={fullData.preCondition}
                  onBlur={(v: string) => {
                    editMode && handleSave(false, { ...fullData, preCondition: v });
                    updateFullData('preCondition', v);
                  }}
                  placeholder={i18n.t('dop:No content')}
                />
              </ContentPanel>
              <ContentPanel
                title={i18n.t('dop:Steps and results')}
                mode="add"
                onClick={() => {
                  handleAddInTitle('stepAndResults');
                }}
              >
                <CaseStep
                  value={fullData.stepAndResults}
                  onChange={(stepsData, autoSave) => updateFullData('stepAndResults', stepsData, autoSave)}
                />
              </ContentPanel>
              <ContentPanel
                title={i18n.t('dop:Interface')}
                mode="add"
                onClick={() => {
                  handleAddInTitle('apisFormat');
                }}
                loading={isExecuting}
                append={append}
              >
                <CaseAPI
                  value={fullData.apisFormat}
                  onChange={(apis, autoSave) => updateFullData('apisFormat', apis, autoSave)}
                  mode={scope === 'testPlan' && editMode ? 'plan' : ''}
                  executeApi={executeApi}
                />
              </ContentPanel>
              <ContentPanel title={i18n.t('Description')}>
                <MarkdownEditor
                  value={fullData.desc}
                  onBlur={(v: string) => {
                    editMode && handleSave(false, { ...fullData, desc: v });
                    updateFullData('desc', v);
                  }}
                  placeholder={i18n.t('dop:Additional description')}
                />
              </ContentPanel>
            </div>
            <div className="mt-8">
              {visible && scope === 'testPlan' && editMode ? <RelatedBugs relationID={caseDetail.id} /> : null}
            </div>
          </div>
          <div className="case-drawer-body-right px-5 py-4">
            <CaseMeta onBlurCapture={handleAnyBlur} onChange={updateFullData} dataSource={caseMetaData} />
          </div>
        </div>
      </Spin>
      <div className="case-drawer-footer">
        <Spin spinning={false}>
          <CaseFooter
            scope={scope}
            onClose={handleClose}
            onOk={handleSave}
            editMode={editMode}
            caseList={caseList || []}
          />
        </Spin>
      </div>
    </Drawer>
  );
}
Example #15
Source File: EventsList.tsx    From jitsu with MIT License 4 votes vote down vote up
EventsList: React.FC<{
  type: EventType
  filterOptions: FilterOption[]
}> = ({ type, filterOptions }) => {
  const statusOptions = [
    { label: "All", value: null },
    { label: "Error", value: "error" },
  ]

  const listInnerRef = useRef()
  const location = useLocation()
  const params = new URLSearchParams(location.search)
  const [autoReload, setAutoReload] = useState(true)
  const [selectedEvent, setSelectedEvent] = useState(null)
  const [events, setEvents] = useState<Event[]>([])
  const [filteredEvents, setFilteredEvents] = useState<Event[]>([])
  const [term, setTerm] = useState(params.get("q"))
  const [idFilter, setIdFilter] = useState(
    filterOptions.find(f => f.value === params.get("id"))?.value ?? filterOptions[0]?.value
  )
  const [statusFilter, setStatusFilter] = useState(
    statusOptions.find(f => f.value === params.get("status"))?.value ?? statusOptions[0]?.value
  )
  const [reloadCount, setReloadCount] = useState(0)
  const services = useServices()
  const history = useHistory()

  const destinationsMap: Record<string, DestinationData> = destinationsStore.listIncludeHidden.reduce((index, dst) => {
    index[dst._uid] = dst
    return index
  }, {})

  useEffect(() => {
    if (!idFilter) {
      history.push({ search: null })
      return
    }
    let queryParams = omitBy({ type, id: idFilter, status: statusFilter }, isNull)
    if (term) {
      queryParams["q"] = term
    }
    history.push({ search: new URLSearchParams(queryParams).toString() })
  }, [idFilter, statusFilter, term])

  const { data, error } = useLoaderAsObject(() => {
    if (!idFilter) {
      return null
    }

    const ids = type === EventType.Destination ? `${services.activeProject.id}.${idFilter}` : idFilter
    setSelectedEvent(null)
    return services.backendApiClient
      .get(
        `/events/cache?project_id=${services.activeProject.id}&limit=500&namespace=${type}&ids=${ids}&status=${
          statusFilter ?? ""
        }`,
        { proxy: true }
      )
      .then(events => {
        return { events, id: idFilter }
      })
  }, [idFilter, statusFilter, reloadCount])

  useEffect(() => {
    const interval = setInterval(() => {
      if (!autoReload || selectedEvent) {
        return
      }
      setReloadCount(reloadCount + 1)
    }, 15000)
    return () => clearInterval(interval)
  }, [autoReload, selectedEvent, reloadCount])

  const filterByTerm = (events, term) => {
    return term ? events.filter(i => JSON.stringify(i.rawJson).indexOf(term) !== -1) : events
  }

  const search = term => {
    setTerm(term)
    setFilteredEvents(filterByTerm(events, term))
  }

  useEffect(() => {
    const initialEvents = error || !data ? [] : processEvents(type, data)
    setEvents(initialEvents)
    setFilteredEvents(filterByTerm(initialEvents, term))
  }, [error, data])

  if (!filterOptions.length) {
    return <NoDataFlowing showHint={true} />
  }

  const filters = (
    <>
      <div className={`mb-6 flex ${styles.filters}`}>
        <SelectFilter
          className="mr-5"
          label={type === EventType.Token ? "API Key" : "Destination"}
          initialValue={idFilter}
          options={filterOptions}
          onChange={option => {
            setIdFilter(option.value)
          }}
        />
        <SelectFilter
          className="mr-5"
          label="Status"
          initialValue={statusFilter}
          options={statusOptions}
          onChange={option => {
            setStatusFilter(option.value)
          }}
        />
        <Button
          size="large"
          type="primary"
          className={styles.reloadBtn}
          onClick={() => {
            setReloadCount(count => count + 1)
          }}
        >
          <ReloadOutlined /> Reload
        </Button>
      </div>
      <Input className="w-full" placeholder="Filter" value={term} onChange={e => search(e.target.value)} />
    </>
  )

  const eventStatusMessage = event => {
    const error = event.status === EventStatus.Error
    const skip = event.status === EventStatus.Skip

    if (type === EventType.Token) {
      if (skip) {
        return `Skip`
      }
      return error ? event.rawJson.error ?? "Error" : "Success"
    }

    return error
      ? "Failed - at least one destination load is failed"
      : skip
      ? "Skipped - event was not sent to destination"
      : "Success - successfully sent to destination"
  }

  if (error) {
    return (
      <div className="w-full">
        {filters}
        <CenteredError error={error} />
      </div>
    )
  } else if (!data) {
    return (
      <div className="w-full">
        {filters}
        <CenteredSpin />
      </div>
    )
  }

  const { last_minute_limited, cache_capacity_per_interval, interval_seconds } = data?.events
  const alert =
    last_minute_limited > 0 ? (
      <div className="mt-4">
        <Alert
          message={`This isn't a full list of all events. Jitsu doesn't cache all events, but the only ${cache_capacity_per_interval} event per ${interval_seconds} seconds. Other ${last_minute_limited} events from the last minute have been being processed and stored to the destinations but haven't been saved into the cache.`}
          type="warning"
        />
      </div>
    ) : null

  const onScroll = () => {
    if (!listInnerRef.current) {
      return
    }
    const { scrollTop } = listInnerRef.current
    const startAutoReload = scrollTop === 0
    if (startAutoReload === autoReload) {
      return
    }
    setAutoReload(startAutoReload)
  }

  return (
    <>
      {filters}
      {alert}
      <div
        className={`mt-3 transition-all duration-300 ${styles.autoReloadInfo} ${
          autoReload && !selectedEvent ? "" : "opacity-0"
        }`}
      >
        <ReloadOutlined spin={true} /> Auto reload is enabled. <a onClick={() => setAutoReload(false)}>Disable</a>
      </div>
      <div className={styles.eventsList} ref={listInnerRef} onScroll={onScroll}>
        {!filteredEvents.length ? <NoDataFlowing showHint={false} /> : null}
        {filteredEvents.map(event => {
          const active = event.eventId === selectedEvent
          return (
            <div key={event.eventId}>
              <div
                className={`overflow-hidden w-full flex flex-row border-b border-secondaryText border-opacity-50 items-center cursor-pointer h-12 ${
                  selectedEvent === event.eventId ? "bg-bgSecondary" : "hover:bg-bgComponent"
                }`}
                key="header"
                onClick={() => setSelectedEvent(active ? null : event.eventId)}
              >
                <div className="w-6 flex items-center justify-center px-3 text-lg" key="icon">
                  <Tooltip title={eventStatusMessage(event)}>
                    {event.status === EventStatus.Error ? (
                      <ExclamationCircleOutlined className="text-error" />
                    ) : event.status === EventStatus.Pending || event.status === EventStatus.Skip ? (
                      <MinusCircleOutlined className="text-warning" />
                    ) : (
                      <CheckCircleOutlined className="text-success" />
                    )}
                  </Tooltip>
                </div>
                <div
                  className={`text-xxs whitespace-nowrap text-secondaryText px-1 ${styles.timestampColumn}`}
                  key="time"
                >
                  <div>{event.timestamp.format("YYYY-MM-DD HH:mm:ss")} UTC</div>
                  <div className="text-xxs">{event.timestamp.fromNow()}</div>
                </div>
                <div
                  className="pl-4 text-3xs text-secondaryText font-monospace overflow-hidden overflow-ellipsis h-12 leading-4 flex-shrink"
                  key="json"
                >
                  {event.rawJson.malformed ? event.rawJson.malformed : JSON.stringify(event.rawJson, null, 2)}
                </div>
                <div
                  className={cn(
                    "w-12 text-testPale flex items-center justify-center px-2 text-xl transition-transform duration-500",
                    styles.expandBtn,
                    active && "transform rotate-90"
                  )}
                  key="expand"
                >
                  <RightCircleOutlined />
                </div>
              </div>
              <div key="details">
                {active && <EventsView event={event} allDestinations={destinationsMap} className="pb-6" />}
              </div>
            </div>
          )
        })}
      </div>
    </>
  )
}