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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
/* 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 |
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 |
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 |
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} />
{i18n.t('dop:updated on')} {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 |
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>
</>
)
}