mirror of
https://github.com/strapi/strapi.git
synced 2025-12-24 13:43:41 +00:00
chore: migrate media-library utils to Typescript (#21532)
* chore: migrate to TS typeFromMime * chore: migrate to TS toSingularTypes * chore: migrate to TS prefixPluginTranslations * chore: migrate to TS prefixFileUrlWithBackendUrl * chore: migrate to TS moveElement * chore: migrate to TS getTrad * chore: migrate to TS getFileExtension * chore: migrate to TS containsAssetFilter * chore: migrate to TS displayedFilters * chore: migrate to TS downloadFile * chore: remove findRecursiveFolderMetadatas because is unused * chore: migrate to TS appendSearchParamsToUrl * chore: migrate to TS formatBytes * chore: migrate to TS formatDuration * chore: migrate to TS urlYupSchema * chore: migrate to TS urlsToAssets * chore: migrate to TS rawFileToAsset * chore: migrate to TS getFolderURL * chore: migrate to TS getFolderParents * chore: migrate to TS createAssetUrl * chore: migrate to TS findRecursiveFolderByValue * chore: migrate to TS getAllowedFiles * chore: migrate to TS getBreadcrumbDataCM * chore: migrate to TS normalizeAPIError * chore: migrate to TS getAPIInnerErrors * chore: migrate to TS getBreadcrumbDataML and change the utils imports * chore: fix export from index * chore: reduce the errors type definition * chore: change Query type * chore: change the way utils are exported in the index * chore: reduce the code in the custom declaration type file
This commit is contained in:
parent
43e15a3ba8
commit
38dcf9a2f0
9
packages/core/upload/admin/custom.d.ts
vendored
Normal file
9
packages/core/upload/admin/custom.d.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
export {};
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
strapi: {
|
||||
backendURL: string;
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,7 @@ import { Filter } from '@strapi/icons';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import displayedFilters from '../../../utils/displayedFilters';
|
||||
import { displayedFilters } from '../../../utils';
|
||||
import FilterList from '../../FilterList';
|
||||
import FilterPopover from '../../FilterPopover';
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ import { Search } from '@strapi/icons';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import getTrad from '../../../../utils/getTrad';
|
||||
import { getTrad } from '../../../../utils';
|
||||
|
||||
const SearchAsset = ({ onChangeSearch, queryValue }) => {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
@ -24,9 +24,7 @@ import {
|
||||
} from '../../../constants';
|
||||
import { useFolder } from '../../../hooks/useFolder';
|
||||
import { usePersistentState } from '../../../hooks/usePersistentState';
|
||||
import { getBreadcrumbDataCM, toSingularTypes } from '../../../utils';
|
||||
import getAllowedFiles from '../../../utils/getAllowedFiles';
|
||||
import getTrad from '../../../utils/getTrad';
|
||||
import { getBreadcrumbDataCM, toSingularTypes, getTrad, getAllowedFiles } from '../../../utils';
|
||||
import { AssetGridList } from '../../AssetGridList';
|
||||
import { Breadcrumbs } from '../../Breadcrumbs';
|
||||
import { EmptyAssets } from '../../EmptyAssets';
|
||||
|
||||
@ -4,7 +4,7 @@ import { Flex, Typography } from '@strapi/design-system';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import getTrad from '../../../utils/getTrad';
|
||||
import { getTrad } from '../../../utils';
|
||||
import { AssetGridList } from '../../AssetGridList';
|
||||
|
||||
export const SelectedStep = ({ selectedAssets, onSelectAsset, onReorderAsset }) => {
|
||||
|
||||
@ -12,9 +12,7 @@ import { useFolders } from '../../hooks/useFolders';
|
||||
import { useMediaLibraryPermissions } from '../../hooks/useMediaLibraryPermissions';
|
||||
import useModalQueryParams from '../../hooks/useModalQueryParams';
|
||||
import { useSelectionState } from '../../hooks/useSelectionState';
|
||||
import { containsAssetFilter, getTrad } from '../../utils';
|
||||
import getAllowedFiles from '../../utils/getAllowedFiles';
|
||||
import { moveElement } from '../../utils/moveElement';
|
||||
import { containsAssetFilter, getTrad, getAllowedFiles, moveElement } from '../../utils';
|
||||
import { EditAssetContent } from '../EditAssetDialog';
|
||||
import { EditFolderContent } from '../EditFolderDialog';
|
||||
|
||||
|
||||
@ -9,8 +9,7 @@ import { useIntl } from 'react-intl';
|
||||
import { AssetDefinition, FolderDefinition } from '../../constants';
|
||||
import { useBulkMove } from '../../hooks/useBulkMove';
|
||||
import { useFolderStructure } from '../../hooks/useFolderStructure';
|
||||
import { getTrad } from '../../utils';
|
||||
import { normalizeAPIError } from '../../utils/normalizeAPIError';
|
||||
import { getTrad, normalizeAPIError } from '../../utils';
|
||||
import SelectTree from '../SelectTree';
|
||||
|
||||
export const BulkMoveDialog = ({ onClose, selected, currentFolder }) => {
|
||||
|
||||
@ -7,7 +7,7 @@ import PropTypes from 'prop-types';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import { useClipboard } from '../../hooks/useClipboard';
|
||||
import getTrad from '../../utils/getTrad';
|
||||
import { getTrad } from '../../utils';
|
||||
|
||||
export const CopyLinkButton = ({ url }) => {
|
||||
const { toggleNotification } = useNotification();
|
||||
|
||||
@ -6,7 +6,7 @@ import PropTypes from 'prop-types';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { styled, useTheme } from 'styled-components';
|
||||
|
||||
import getTrad from '../../../utils/getTrad';
|
||||
import { getTrad } from '../../../utils';
|
||||
|
||||
import { CroppingActionRow } from './components';
|
||||
|
||||
|
||||
@ -10,9 +10,7 @@ import { AssetDefinition, AssetType } from '../../../constants';
|
||||
import { useCropImg } from '../../../hooks/useCropImg';
|
||||
import { useEditAsset } from '../../../hooks/useEditAsset';
|
||||
import { useUpload } from '../../../hooks/useUpload';
|
||||
import { createAssetUrl } from '../../../utils';
|
||||
import { downloadFile } from '../../../utils/downloadFile';
|
||||
import getTrad from '../../../utils/getTrad';
|
||||
import { createAssetUrl, getTrad, downloadFile } from '../../../utils';
|
||||
import { CopyLinkButton } from '../../CopyLinkButton';
|
||||
import { UploadProgress } from '../../UploadProgress';
|
||||
import { RemoveAssetDialog } from '../RemoveAssetDialog';
|
||||
|
||||
@ -27,8 +27,7 @@ import * as yup from 'yup';
|
||||
import { AssetDefinition } from '../../constants';
|
||||
import { useEditAsset } from '../../hooks/useEditAsset';
|
||||
import { useFolderStructure } from '../../hooks/useFolderStructure';
|
||||
import { findRecursiveFolderByValue, getTrad, getFileExtension } from '../../utils';
|
||||
import formatBytes from '../../utils/formatBytes';
|
||||
import { findRecursiveFolderByValue, getTrad, getFileExtension, formatBytes } from '../../utils';
|
||||
import { ContextInfo } from '../ContextInfo';
|
||||
import SelectTree from '../SelectTree';
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ import { IntlProvider } from 'react-intl';
|
||||
import { QueryClient, QueryClientProvider } from 'react-query';
|
||||
|
||||
import en from '../../../translations/en.json';
|
||||
import { downloadFile } from '../../../utils/downloadFile';
|
||||
import { downloadFile } from '../../../utils';
|
||||
import { EditAssetDialog } from '../index';
|
||||
|
||||
jest.mock('../../../hooks/useFolderStructure');
|
||||
|
||||
@ -13,7 +13,7 @@ import { IntlProvider } from 'react-intl';
|
||||
import { QueryClient, QueryClientProvider } from 'react-query';
|
||||
|
||||
import en from '../../../translations/en.json';
|
||||
import { downloadFile } from '../../../utils/downloadFile';
|
||||
import { downloadFile } from '../../../utils';
|
||||
import { EditAssetDialog } from '../index';
|
||||
|
||||
jest.mock('../../../utils/downloadFile');
|
||||
|
||||
@ -6,8 +6,7 @@ import PropTypes from 'prop-types';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import { AssetDefinition } from '../../../constants';
|
||||
import { prefixFileUrlWithBackendUrl } from '../../../utils';
|
||||
import getTrad from '../../../utils/getTrad';
|
||||
import { getTrad, prefixFileUrlWithBackendUrl } from '../../../utils';
|
||||
import { CopyLinkButton } from '../../CopyLinkButton';
|
||||
|
||||
export const CarouselAssetActions = ({ asset, onDeleteAsset, onAddAsset, onEditAsset }) => {
|
||||
|
||||
@ -5,7 +5,7 @@ import PropTypes from 'prop-types';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import { AssetDefinition } from '../../../constants';
|
||||
import getTrad from '../../../utils/getTrad';
|
||||
import { getTrad } from '../../../utils/getTrad';
|
||||
import { EditAssetDialog } from '../../EditAssetDialog';
|
||||
|
||||
import { CarouselAsset } from './CarouselAsset';
|
||||
|
||||
@ -7,8 +7,7 @@ import { useIntl } from 'react-intl';
|
||||
import { styled } from 'styled-components';
|
||||
|
||||
import { AssetSource } from '../../../constants';
|
||||
import getTrad from '../../../utils/getTrad';
|
||||
import { rawFileToAsset } from '../../../utils/rawFileToAsset';
|
||||
import { getTrad, rawFileToAsset } from '../../../utils';
|
||||
|
||||
const TextAlignTypography = styled(Typography)`
|
||||
align-items: center;
|
||||
|
||||
@ -4,8 +4,7 @@ import { useField, useNotification } from '@strapi/admin/strapi-admin';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import getAllowedFiles from '../../utils/getAllowedFiles';
|
||||
import getTrad from '../../utils/getTrad';
|
||||
import { getTrad, getAllowedFiles } from '../../utils';
|
||||
import { AssetDialog } from '../AssetDialog';
|
||||
import { EditFolderDialog } from '../EditFolderDialog';
|
||||
import { UploadAssetDialog } from '../UploadAssetDialog/UploadAssetDialog';
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
export default function flattenTree(tree, parent, depth = 0) {
|
||||
return tree.flatMap((item) =>
|
||||
item.children
|
||||
? [{ ...item, parent: parent?.value, depth }, ...flattenTree(item.children, item, depth + 1)]
|
||||
: { ...item, depth, parent: parent?.value }
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
type TreeNode<T> = {
|
||||
value: T;
|
||||
children?: TreeNode<T>[];
|
||||
label?: string;
|
||||
};
|
||||
|
||||
export type FlattenedNode<T> = {
|
||||
value: T;
|
||||
parent?: T;
|
||||
depth: number;
|
||||
// we need the label in places where flattenTree is used
|
||||
label?: string;
|
||||
children?: TreeNode<T>[];
|
||||
};
|
||||
|
||||
export default function flattenTree<T>(
|
||||
tree: TreeNode<T>[],
|
||||
parent: TreeNode<T> | null = null,
|
||||
depth: number = 0
|
||||
): FlattenedNode<T>[] {
|
||||
return tree.flatMap((item) =>
|
||||
item.children
|
||||
? [{ ...item, parent: parent?.value, depth }, ...flattenTree(item.children, item, depth + 1)]
|
||||
: { ...item, depth, parent: parent?.value }
|
||||
);
|
||||
}
|
||||
@ -4,7 +4,7 @@ import { Box, Divider, Modal, Tabs } from '@strapi/design-system';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import getTrad from '../../../utils/getTrad';
|
||||
import { getTrad } from '../../../utils';
|
||||
|
||||
import { FromComputerForm } from './FromComputerForm';
|
||||
import { FromUrlForm } from './FromUrlForm';
|
||||
|
||||
@ -9,8 +9,7 @@ import { useIntl } from 'react-intl';
|
||||
import { styled } from 'styled-components';
|
||||
|
||||
import { AssetSource } from '../../../constants';
|
||||
import getTrad from '../../../utils/getTrad';
|
||||
import { rawFileToAsset } from '../../../utils/rawFileToAsset';
|
||||
import { getTrad, rawFileToAsset } from '../../../utils';
|
||||
|
||||
const Wrapper = styled(Flex)`
|
||||
flex-direction: column;
|
||||
|
||||
@ -6,9 +6,7 @@ import { Form, Formik } from 'formik';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import getTrad from '../../../utils/getTrad';
|
||||
import { urlsToAssets } from '../../../utils/urlsToAssets';
|
||||
import { urlSchema } from '../../../utils/urlYupSchema';
|
||||
import { getTrad, urlsToAssets, urlSchema } from '../../../utils';
|
||||
|
||||
export const FromUrlForm = ({ onClose, onAddAsset, trackedLocation }) => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
@ -6,7 +6,7 @@ import PropTypes from 'prop-types';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import { AssetDefinition } from '../../../constants';
|
||||
import getTrad from '../../../utils/getTrad';
|
||||
import { getTrad } from '../../../utils';
|
||||
import { AssetCard } from '../../AssetCard/AssetCard';
|
||||
import { UploadingAssetCard } from '../../AssetCard/UploadingAssetCard';
|
||||
|
||||
|
||||
@ -7,7 +7,10 @@ import { QueryClient, QueryClientProvider } from 'react-query';
|
||||
|
||||
import { PendingAssetStep } from '../PendingAssetStep';
|
||||
|
||||
jest.mock('../../../../utils/getTrad', () => (x) => x);
|
||||
jest.mock('../../../../utils', () => ({
|
||||
...jest.requireActual('../../../../utils'),
|
||||
getTrad: (x) => x,
|
||||
}));
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
|
||||
@ -6,8 +6,7 @@ import { MediaLibraryDialog } from './components/MediaLibraryDialog';
|
||||
import { MediaLibraryInput } from './components/MediaLibraryInput';
|
||||
import { PERMISSIONS } from './constants';
|
||||
import pluginId from './pluginId';
|
||||
import getTrad from './utils/getTrad';
|
||||
import { prefixPluginTranslations } from './utils/prefixPluginTranslations';
|
||||
import { getTrad, prefixPluginTranslations } from './utils';
|
||||
|
||||
const name = pluginPkg.strapi.name;
|
||||
|
||||
|
||||
12
packages/core/upload/admin/src/newConstants.ts
Normal file
12
packages/core/upload/admin/src/newConstants.ts
Normal file
@ -0,0 +1,12 @@
|
||||
// TODO: replace this file with the constants file when it will be migrated to TS
|
||||
export enum AssetType {
|
||||
Video = 'video',
|
||||
Image = 'image',
|
||||
Document = 'doc',
|
||||
Audio = 'audio',
|
||||
}
|
||||
|
||||
export enum AssetSource {
|
||||
Url = 'url',
|
||||
Computer = 'computer',
|
||||
}
|
||||
@ -5,7 +5,7 @@ import PropTypes from 'prop-types';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import { pageSizes, sortOptions } from '../../../../constants';
|
||||
import getTrad from '../../../../utils/getTrad';
|
||||
import { getTrad } from '../../../../utils';
|
||||
|
||||
const Settings = ({ sort = '', pageSize = 10, onChange }) => {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
@ -16,7 +16,7 @@ import { NavLink } from 'react-router-dom';
|
||||
|
||||
import { useConfig } from '../../../hooks/useConfig';
|
||||
import pluginID from '../../../pluginId';
|
||||
import getTrad from '../../../utils/getTrad';
|
||||
import { getTrad } from '../../../utils';
|
||||
|
||||
import { Settings } from './components/Settings';
|
||||
import { onChange, setLoaded } from './state/actions';
|
||||
|
||||
@ -5,7 +5,7 @@ import PropTypes from 'prop-types';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import { AssetDefinition, FolderDefinition } from '../../../../constants';
|
||||
import getTrad from '../../../../utils/getTrad';
|
||||
import { getTrad } from '../../../../utils';
|
||||
|
||||
import { BulkDeleteButton } from './BulkDeleteButton';
|
||||
import { BulkMoveButton } from './BulkMoveButton';
|
||||
|
||||
@ -7,7 +7,7 @@ import { useIntl } from 'react-intl';
|
||||
|
||||
import FilterList from '../../../../components/FilterList';
|
||||
import FilterPopover from '../../../../components/FilterPopover';
|
||||
import displayedFilters from '../../../../utils/displayedFilters';
|
||||
import { displayedFilters } from '../../../../utils';
|
||||
|
||||
export const Filters = () => {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
|
||||
@ -5,7 +5,7 @@ import PropTypes from 'prop-types';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import { AssetDefinition, FolderDefinition } from '../../../constants';
|
||||
import getTrad from '../../../utils/getTrad';
|
||||
import { getTrad } from '../../../utils';
|
||||
|
||||
import { BulkDeleteButton } from './BulkDeleteButton';
|
||||
import { BulkMoveButton } from './BulkMoveButton';
|
||||
|
||||
@ -7,7 +7,7 @@ import { useIntl } from 'react-intl';
|
||||
|
||||
import FilterList from '../../../components/FilterList';
|
||||
import FilterPopover from '../../../components/FilterPopover';
|
||||
import displayedFilters from '../../../utils/displayedFilters';
|
||||
import { displayedFilters } from '../../../utils';
|
||||
|
||||
export const Filters = () => {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
|
||||
@ -1,24 +0,0 @@
|
||||
/**
|
||||
* Append the given search params to the given URL.
|
||||
*
|
||||
* @param {String} url The URL string to append the search params to
|
||||
* @param {Object} params The object of search params to append to the URL
|
||||
* @returns {String} A string representing the URL with the search params appended
|
||||
*/
|
||||
const appendSearchParamsToUrl = ({ url, params }) => {
|
||||
if (url === undefined || typeof params !== 'object') {
|
||||
return url;
|
||||
}
|
||||
|
||||
const urlObj = new URL(url, window.strapi.backendURL);
|
||||
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined) {
|
||||
urlObj.searchParams.append(key, value);
|
||||
}
|
||||
});
|
||||
|
||||
return urlObj.toString();
|
||||
};
|
||||
|
||||
export { appendSearchParamsToUrl };
|
||||
@ -0,0 +1,22 @@
|
||||
interface AppendSearchParamsToUrlProps {
|
||||
url?: string;
|
||||
params?: Record<string, string | null | undefined> | string;
|
||||
}
|
||||
|
||||
const appendSearchParamsToUrl = ({ url, params }: AppendSearchParamsToUrlProps) => {
|
||||
if (url === undefined || typeof params !== 'object') {
|
||||
return url;
|
||||
}
|
||||
|
||||
const urlObj = new URL(url, window.strapi.backendURL);
|
||||
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null) {
|
||||
urlObj.searchParams.append(key, value);
|
||||
}
|
||||
});
|
||||
|
||||
return urlObj.toString();
|
||||
};
|
||||
|
||||
export { appendSearchParamsToUrl };
|
||||
@ -1,4 +1,6 @@
|
||||
const containsMimeTypeFilter = (query) => {
|
||||
import type { Query } from '../../../shared/contracts/files';
|
||||
|
||||
const containsMimeTypeFilter = (query: Query | null) => {
|
||||
const filters = query?.filters?.$and;
|
||||
|
||||
if (!filters) {
|
||||
@ -12,8 +14,6 @@ const containsMimeTypeFilter = (query) => {
|
||||
return !!result;
|
||||
};
|
||||
|
||||
const containsAssetFilter = (query) => {
|
||||
export const containsAssetFilter = (query: Query | null) => {
|
||||
return containsMimeTypeFilter(query);
|
||||
};
|
||||
|
||||
export default containsAssetFilter;
|
||||
@ -1,20 +0,0 @@
|
||||
import prefixFileUrlWithBackendUrl from './prefixFileUrlWithBackendUrl';
|
||||
|
||||
/**
|
||||
* Create image URL for asset
|
||||
* @param {Object} asset
|
||||
* @param {Boolean} forThumbnail - if true, return URL for thumbnail
|
||||
* if there's no thumbnail, return the URL of the original image.
|
||||
* @return {String} URL
|
||||
*/
|
||||
const createAssetUrl = (asset, forThumbnail = true) => {
|
||||
if (asset.isLocal) {
|
||||
return asset.url;
|
||||
}
|
||||
|
||||
const assetUrl = forThumbnail ? asset?.formats?.thumbnail?.url || asset.url : asset.url;
|
||||
|
||||
return prefixFileUrlWithBackendUrl(assetUrl);
|
||||
};
|
||||
|
||||
export default createAssetUrl;
|
||||
13
packages/core/upload/admin/src/utils/createAssetUrl.ts
Normal file
13
packages/core/upload/admin/src/utils/createAssetUrl.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { prefixFileUrlWithBackendUrl } from './prefixFileUrlWithBackendUrl';
|
||||
|
||||
import type { File } from '../../../shared/contracts/files';
|
||||
|
||||
export const createAssetUrl = (asset: File, forThumbnail = true) => {
|
||||
if (asset.isLocal) {
|
||||
return asset.url;
|
||||
}
|
||||
|
||||
const assetUrl = forThumbnail ? asset?.formats?.thumbnail?.url || asset.url : asset.url;
|
||||
|
||||
return prefixFileUrlWithBackendUrl(assetUrl);
|
||||
};
|
||||
@ -1,4 +1,4 @@
|
||||
const displayedFilters = [
|
||||
export const displayedFilters = [
|
||||
{
|
||||
name: 'createdAt',
|
||||
fieldSchema: {
|
||||
@ -27,5 +27,3 @@ const displayedFilters = [
|
||||
metadatas: { label: 'type' },
|
||||
},
|
||||
];
|
||||
|
||||
export default displayedFilters;
|
||||
@ -1,4 +1,4 @@
|
||||
export const downloadFile = async (url, fileName) => {
|
||||
export const downloadFile = async (url: string, fileName: string) => {
|
||||
const fileBlob = await fetch(url).then((res) => res.blob());
|
||||
const urlDownload = window.URL.createObjectURL(fileBlob);
|
||||
const link = document.createElement('a');
|
||||
@ -1,17 +0,0 @@
|
||||
export default function findRecursiveFolderByValue(data, value) {
|
||||
let result;
|
||||
|
||||
function iter(a) {
|
||||
if (a.value === value) {
|
||||
result = a;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return Array.isArray(a.children) && a.children.some(iter);
|
||||
}
|
||||
|
||||
data.some(iter);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
import type { FolderNode } from '../../../shared/contracts/folders';
|
||||
|
||||
interface FolderStructureValue extends Omit<FolderNode, 'children'> {
|
||||
value: number | null;
|
||||
children?: FolderStructureValue[];
|
||||
}
|
||||
|
||||
type Value = number | null | { value: number | null };
|
||||
|
||||
export function findRecursiveFolderByValue(data: FolderStructureValue[], value: Value) {
|
||||
let result;
|
||||
|
||||
function iter(a: FolderStructureValue) {
|
||||
if (a.value === value) {
|
||||
result = a;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return Array.isArray(a.children) && a.children.some(iter);
|
||||
}
|
||||
|
||||
data.some(iter);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
const findRecursiveFolderMetadatas = (node, searchedId, parentId = null) => {
|
||||
let result = null;
|
||||
|
||||
if (node.value === parseInt(searchedId, 10)) {
|
||||
result = { parentId, currentFolderLabel: node.label };
|
||||
} else {
|
||||
for (let i = 0; i < node.children.length && !result; i++) {
|
||||
result = findRecursiveFolderMetadatas(node.children[i], searchedId, node.value);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export default findRecursiveFolderMetadatas;
|
||||
@ -1,13 +0,0 @@
|
||||
import byteSize from 'byte-size';
|
||||
|
||||
function formatBytes(receivedBytes, decimals = 0) {
|
||||
const { value, unit } = byteSize(receivedBytes * 1000, { precision: decimals });
|
||||
|
||||
if (!unit) {
|
||||
return '0B';
|
||||
}
|
||||
|
||||
return `${value}${unit.toUpperCase()}`;
|
||||
}
|
||||
|
||||
export default formatBytes;
|
||||
12
packages/core/upload/admin/src/utils/formatBytes.ts
Normal file
12
packages/core/upload/admin/src/utils/formatBytes.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import byteSize from 'byte-size';
|
||||
|
||||
export function formatBytes(receivedBytes: number | string, decimals = 0) {
|
||||
const realBytes = typeof receivedBytes === 'string' ? Number(receivedBytes) : receivedBytes;
|
||||
const { value, unit } = byteSize(realBytes * 1000, { precision: decimals });
|
||||
|
||||
if (!unit) {
|
||||
return '0B';
|
||||
}
|
||||
|
||||
return `${value}${unit.toUpperCase()}`;
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
import { intervalToDuration } from 'date-fns';
|
||||
|
||||
const zeroPad = (num) => String(num).padStart(2, '0');
|
||||
const zeroPad = (num?: number) => String(num).padStart(2, '0');
|
||||
|
||||
export const formatDuration = (durationInSecond) => {
|
||||
export const formatDuration = (durationInSecond: number) => {
|
||||
const duration = intervalToDuration({ start: 0, end: durationInSecond * 1000 });
|
||||
|
||||
return `${zeroPad(duration.hours)}:${zeroPad(duration.minutes)}:${zeroPad(duration.seconds)}`;
|
||||
@ -1,15 +1,24 @@
|
||||
import { normalizeAPIError } from './normalizeAPIError';
|
||||
import type { FetchError } from '@strapi/admin/strapi-admin';
|
||||
import type { MessageDescriptor } from 'react-intl';
|
||||
|
||||
type GetAPIInnerErrorsReturn = {
|
||||
[key: string]: MessageDescriptor;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* Returns a normalized error message
|
||||
*
|
||||
*/
|
||||
function getAPIInnerErrors(error, { getTrad }) {
|
||||
export function getAPIInnerErrors(
|
||||
error: FetchError,
|
||||
{ getTrad }: { getTrad: (key: string) => string }
|
||||
) {
|
||||
const normalizedError = normalizeAPIError(error, getTrad);
|
||||
|
||||
if (normalizedError && 'errors' in normalizedError) {
|
||||
return normalizedError.errors.reduce((acc, error) => {
|
||||
return normalizedError.errors.reduce<GetAPIInnerErrorsReturn>((acc, error) => {
|
||||
if ('path' in error.values) {
|
||||
acc[error.values.path] = {
|
||||
id: error.id,
|
||||
@ -23,5 +32,3 @@ function getAPIInnerErrors(error, { getTrad }) {
|
||||
|
||||
return normalizedError?.defaultMessage;
|
||||
}
|
||||
|
||||
export default getAPIInnerErrors;
|
||||
@ -1,16 +1,28 @@
|
||||
import toSingularTypes from './toSingularTypes';
|
||||
|
||||
import { toSingularTypes } from './toSingularTypes';
|
||||
import type { File } from '../../../shared/contracts/files';
|
||||
/**
|
||||
* Returns the files that can be added to the media field
|
||||
* @param {Object[]} pluralTypes Array of string (allowedTypes)
|
||||
* @param {Object[]} files Array of files
|
||||
* @returns Object[]
|
||||
*/
|
||||
const getAllowedFiles = (pluralTypes, files) => {
|
||||
|
||||
interface AllowedFiles extends File {
|
||||
documentId: string;
|
||||
isSelectable: boolean;
|
||||
locale: string | null;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export const getAllowedFiles = (pluralTypes: string[], files: AllowedFiles[]) => {
|
||||
const singularTypes = toSingularTypes(pluralTypes);
|
||||
|
||||
const allowedFiles = files.filter((file) => {
|
||||
const fileType = file.mime.split('/')[0];
|
||||
const fileType = file?.mime?.split('/')[0];
|
||||
|
||||
if (!fileType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (singularTypes.includes('file') && !['video', 'image', 'audio'].includes(fileType)) {
|
||||
return true;
|
||||
@ -21,5 +33,3 @@ const getAllowedFiles = (pluralTypes, files) => {
|
||||
|
||||
return allowedFiles;
|
||||
};
|
||||
|
||||
export default getAllowedFiles;
|
||||
@ -1,34 +0,0 @@
|
||||
import getTrad from './getTrad';
|
||||
|
||||
const getBreadcrumbDataML = (folder) => {
|
||||
let data = [
|
||||
{
|
||||
id: null,
|
||||
label: { id: getTrad('plugin.name'), defaultMessage: 'Media Library' },
|
||||
},
|
||||
];
|
||||
|
||||
if (folder?.parent?.parent) {
|
||||
data.push([]);
|
||||
}
|
||||
|
||||
if (folder?.parent) {
|
||||
data.push({
|
||||
id: folder.parent.id,
|
||||
label: folder.parent.name,
|
||||
path: folder.parent.path,
|
||||
});
|
||||
}
|
||||
|
||||
if (folder) {
|
||||
data.push({
|
||||
id: folder.id,
|
||||
label: folder.name,
|
||||
path: folder.path,
|
||||
});
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
export default getBreadcrumbDataML;
|
||||
53
packages/core/upload/admin/src/utils/getBreadcrumbDataCM.ts
Normal file
53
packages/core/upload/admin/src/utils/getBreadcrumbDataCM.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import { getTrad } from './getTrad';
|
||||
|
||||
import type { Folder } from '../../../shared/contracts/folders';
|
||||
import type { MessageDescriptor } from 'react-intl';
|
||||
|
||||
interface BreadcrumbDataFolder extends Omit<Folder, 'children' | 'files' | 'parent'> {
|
||||
parent?: BreadcrumbDataFolder;
|
||||
children?: {
|
||||
count: number;
|
||||
};
|
||||
files?: {
|
||||
count: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface BreadcrumbItem {
|
||||
id?: number | null;
|
||||
label?: MessageDescriptor | string;
|
||||
path?: string;
|
||||
}
|
||||
|
||||
type BreadcrumbData = BreadcrumbItem | [];
|
||||
|
||||
export const getBreadcrumbDataCM = (folder: BreadcrumbDataFolder | null) => {
|
||||
let data: BreadcrumbData[] = [
|
||||
{
|
||||
id: null,
|
||||
label: { id: getTrad('plugin.name'), defaultMessage: 'Media Library' },
|
||||
},
|
||||
];
|
||||
|
||||
if (folder?.parent?.parent) {
|
||||
data.push([]);
|
||||
}
|
||||
|
||||
if (folder?.parent) {
|
||||
data.push({
|
||||
id: folder.parent.id,
|
||||
label: folder.parent.name,
|
||||
path: folder.parent.path,
|
||||
});
|
||||
}
|
||||
|
||||
if (folder) {
|
||||
data.push({
|
||||
id: folder.id,
|
||||
label: folder.name,
|
||||
path: folder.path,
|
||||
});
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
@ -1,38 +0,0 @@
|
||||
import getFolderURL from './getFolderURL';
|
||||
import getTrad from './getTrad';
|
||||
|
||||
const getBreadcrumbDataML = (folder, { pathname, query }) => {
|
||||
let data = [
|
||||
{
|
||||
id: null,
|
||||
label: { id: getTrad('plugin.name'), defaultMessage: 'Media Library' },
|
||||
href: folder ? getFolderURL(pathname, query) : undefined,
|
||||
},
|
||||
];
|
||||
|
||||
if (folder?.parent?.parent) {
|
||||
data.push([]);
|
||||
}
|
||||
|
||||
if (folder?.parent) {
|
||||
data.push({
|
||||
id: folder.parent.id,
|
||||
label: folder.parent.name,
|
||||
href: getFolderURL(pathname, query, {
|
||||
folder: folder.parent.id,
|
||||
folderPath: folder.parent.path,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
if (folder) {
|
||||
data.push({
|
||||
id: folder.id,
|
||||
label: folder.name,
|
||||
});
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
export default getBreadcrumbDataML;
|
||||
60
packages/core/upload/admin/src/utils/getBreadcrumbDataML.ts
Normal file
60
packages/core/upload/admin/src/utils/getBreadcrumbDataML.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import { getFolderURL } from './getFolderURL';
|
||||
import { getTrad } from './getTrad';
|
||||
import type { Query } from '../../../shared/contracts/files';
|
||||
import type { Folder } from '../../../shared/contracts/folders';
|
||||
import type { MessageDescriptor } from 'react-intl';
|
||||
|
||||
interface GetBreadcrumbDataMLProps {
|
||||
folder: Folder;
|
||||
options: {
|
||||
pathname: string;
|
||||
query?: Query;
|
||||
};
|
||||
}
|
||||
|
||||
interface GetBreadcrumbDataMLReturn {
|
||||
id: number | null;
|
||||
label: string | MessageDescriptor;
|
||||
href?: string;
|
||||
}
|
||||
|
||||
type BreadcrumbData = GetBreadcrumbDataMLReturn | [];
|
||||
|
||||
export const getBreadcrumbDataML = (
|
||||
folder: GetBreadcrumbDataMLProps['folder'] | null,
|
||||
{ pathname, query }: GetBreadcrumbDataMLProps['options']
|
||||
) => {
|
||||
let data: BreadcrumbData[] = [
|
||||
{
|
||||
id: null,
|
||||
label: { id: getTrad('plugin.name'), defaultMessage: 'Media Library' },
|
||||
href: folder ? getFolderURL(pathname, query || {}) : undefined,
|
||||
},
|
||||
];
|
||||
|
||||
if (folder?.parent && typeof folder?.parent !== 'number' && folder?.parent?.parent) {
|
||||
data.push([]);
|
||||
}
|
||||
|
||||
if (folder?.parent && typeof folder.parent !== 'number') {
|
||||
data.push({
|
||||
id: folder.parent.id,
|
||||
label: folder.parent.name,
|
||||
href: getFolderURL(pathname, query || {}, {
|
||||
folder: folder.parent.id?.toString(),
|
||||
folderPath: folder.parent.path,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
if (folder) {
|
||||
data.push({
|
||||
id: folder.id,
|
||||
label: folder.name,
|
||||
});
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
export default getBreadcrumbDataML;
|
||||
@ -1,3 +0,0 @@
|
||||
const getFileExtension = (ext) => (ext && ext[0] === '.' ? ext.substring(1) : ext);
|
||||
|
||||
export default getFileExtension;
|
||||
2
packages/core/upload/admin/src/utils/getFileExtension.ts
Normal file
2
packages/core/upload/admin/src/utils/getFileExtension.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export const getFileExtension = (ext?: string | null) =>
|
||||
ext && ext[0] === '.' ? ext.substring(1) : ext;
|
||||
@ -1,6 +1,13 @@
|
||||
import flattenTree from '../components/SelectTree/utils/flattenTree';
|
||||
|
||||
const getFolderParents = (folders, currentFolderId) => {
|
||||
import type { FolderNode } from '../../../shared/contracts/folders';
|
||||
|
||||
interface FolderStructureValue extends Omit<FolderNode, 'children'> {
|
||||
value: number | null;
|
||||
children?: FolderStructureValue[];
|
||||
}
|
||||
|
||||
export const getFolderParents = (folders: FolderStructureValue[], currentFolderId: number) => {
|
||||
const parents = [];
|
||||
const flatFolders = flattenTree(folders);
|
||||
const currentFolder = flatFolders.find((folder) => folder.value === currentFolderId);
|
||||
@ -14,11 +21,9 @@ const getFolderParents = (folders, currentFolderId) => {
|
||||
while (parent !== undefined) {
|
||||
// eslint-disable-next-line no-loop-func
|
||||
let parentToStore = flatFolders.find(({ value }) => value === parent);
|
||||
parents.push({ id: parentToStore.value, label: parentToStore.label });
|
||||
parent = parentToStore.parent;
|
||||
parents.push({ id: parentToStore?.value, label: parentToStore?.label });
|
||||
parent = parentToStore?.parent;
|
||||
}
|
||||
|
||||
return parents.reverse();
|
||||
};
|
||||
|
||||
export default getFolderParents;
|
||||
@ -1,16 +1,12 @@
|
||||
/**
|
||||
* @param {string} pathname
|
||||
* @param {object} currentQuery
|
||||
* @param {string} query._q Search value of the query
|
||||
* @param {object} newQuery
|
||||
* @param {string} newQuery.folder
|
||||
* @param {string} newQuery.folderPath
|
||||
* @returns {string}
|
||||
*/
|
||||
import type { Query } from '../../../shared/contracts/files';
|
||||
|
||||
import { stringify } from 'qs';
|
||||
|
||||
const getFolderURL = (pathname, currentQuery, { folder, folderPath } = {}) => {
|
||||
export const getFolderURL = (
|
||||
pathname: string,
|
||||
currentQuery: Query,
|
||||
{ folder, folderPath }: { folder?: string; folderPath?: string } = {}
|
||||
) => {
|
||||
const { _q, ...queryParamsWithoutQ } = currentQuery;
|
||||
const queryParamsString = stringify(
|
||||
{
|
||||
@ -25,5 +21,3 @@ const getFolderURL = (pathname, currentQuery, { folder, folderPath } = {}) => {
|
||||
// we remove it here to allow navigating in a folder and see the result of this navigation
|
||||
return `${pathname}${queryParamsString ? `?${queryParamsString}` : ''}`;
|
||||
};
|
||||
|
||||
export default getFolderURL;
|
||||
@ -1,5 +0,0 @@
|
||||
import pluginId from '../pluginId';
|
||||
|
||||
const getTrad = (id) => `${pluginId}.${id}`;
|
||||
|
||||
export default getTrad;
|
||||
3
packages/core/upload/admin/src/utils/getTrad.ts
Normal file
3
packages/core/upload/admin/src/utils/getTrad.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import pluginId from '../pluginId';
|
||||
|
||||
export const getTrad = (id: string) => `${pluginId}.${id}`;
|
||||
@ -1,16 +0,0 @@
|
||||
export { appendSearchParamsToUrl } from './appendSearchParamsToUrl';
|
||||
export { default as containsAssetFilter } from './containsAssetFilter';
|
||||
export { default as createAssetUrl } from './createAssetUrl';
|
||||
export { default as findRecursiveFolderByValue } from './findRecursiveFolderByValue';
|
||||
export { default as findRecursiveFolderMetadatas } from './findRecursiveFolderMetadatas';
|
||||
export { default as formatBytes } from './formatBytes';
|
||||
export * from './formatDuration';
|
||||
export { default as getBreadcrumbDataCM } from './getBreadcrumbDataCM';
|
||||
export { default as getBreadcrumbDataML } from './getBreadcrumbDataML';
|
||||
export { default as getFolderParents } from './getFolderParents';
|
||||
export { default as getFolderURL } from './getFolderURL';
|
||||
export { default as getTrad } from './getTrad';
|
||||
export { default as toSingularTypes } from './toSingularTypes';
|
||||
export { default as getFileExtension } from './getFileExtension';
|
||||
export { default as prefixFileUrlWithBackendUrl } from './prefixFileUrlWithBackendUrl';
|
||||
export { default as getAPIInnerErrors } from './getAPIInnerErrors';
|
||||
25
packages/core/upload/admin/src/utils/index.ts
Normal file
25
packages/core/upload/admin/src/utils/index.ts
Normal file
@ -0,0 +1,25 @@
|
||||
export * from './appendSearchParamsToUrl';
|
||||
export * from './containsAssetFilter';
|
||||
export * from './createAssetUrl';
|
||||
export * from './displayedFilters';
|
||||
export * from './downloadFile';
|
||||
export * from './findRecursiveFolderByValue';
|
||||
export * from './formatBytes';
|
||||
export * from './formatDuration';
|
||||
export * from './getAllowedFiles';
|
||||
export * from './getAPIInnerErrors';
|
||||
export * from './getBreadcrumbDataCM';
|
||||
export * from './getBreadcrumbDataML';
|
||||
export * from './getFileExtension';
|
||||
export * from './getFolderParents';
|
||||
export * from './getFolderURL';
|
||||
export * from './getTrad';
|
||||
export * from './moveElement';
|
||||
export * from './normalizeAPIError';
|
||||
export * from './prefixFileUrlWithBackendUrl';
|
||||
export * from './prefixPluginTranslations';
|
||||
export * from './rawFileToAsset';
|
||||
export * from './toSingularTypes';
|
||||
export * from './typeFromMime';
|
||||
export * from './urlsToAssets';
|
||||
export * from './urlYupSchema';
|
||||
@ -1,4 +1,4 @@
|
||||
const move = (array, oldIndex, newIndex) => {
|
||||
const move = (array: number[], oldIndex: number, newIndex: number) => {
|
||||
if (newIndex >= array.length) {
|
||||
newIndex = array.length - 1;
|
||||
}
|
||||
@ -7,7 +7,7 @@ const move = (array, oldIndex, newIndex) => {
|
||||
return array;
|
||||
};
|
||||
|
||||
export const moveElement = (array, index, offset) => {
|
||||
export const moveElement = (array: number[], index: number, offset: number) => {
|
||||
const newIndex = index + offset;
|
||||
|
||||
return move(array, index, newIndex);
|
||||
@ -1,53 +0,0 @@
|
||||
function getPrefixedId(message, callback) {
|
||||
const prefixedMessage = `apiError.${message}`;
|
||||
|
||||
// if a prefix function has been passed in it is used to
|
||||
// prefix the id, e.g. to allow an error message to be
|
||||
// set only for a localization namespace
|
||||
if (typeof callback === 'function') {
|
||||
return callback(prefixedMessage);
|
||||
}
|
||||
|
||||
return prefixedMessage;
|
||||
}
|
||||
|
||||
function normalizeError(error, { name, intlMessagePrefixCallback }) {
|
||||
const { message } = error;
|
||||
|
||||
const normalizedError = {
|
||||
id: getPrefixedId(message, intlMessagePrefixCallback),
|
||||
defaultMessage: message,
|
||||
name: error.name ?? name,
|
||||
values: {},
|
||||
};
|
||||
|
||||
if ('path' in error) {
|
||||
normalizedError.values = { path: error.path.join('.') };
|
||||
}
|
||||
|
||||
return normalizedError;
|
||||
}
|
||||
|
||||
const validateErrorIsYupValidationError = (err) =>
|
||||
typeof err.details === 'object' && err.details !== null && 'errors' in err.details;
|
||||
|
||||
export function normalizeAPIError(apiError, intlMessagePrefixCallback) {
|
||||
const error = apiError.response?.data.error;
|
||||
|
||||
if (error) {
|
||||
// some errors carry multiple errors (such as ValidationError)
|
||||
if (validateErrorIsYupValidationError(error)) {
|
||||
return {
|
||||
name: error.name,
|
||||
message: error?.message || null,
|
||||
errors: error.details.errors.map((err) =>
|
||||
normalizeError(err, { name: error.name, intlMessagePrefixCallback })
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
return normalizeError(error, { intlMessagePrefixCallback });
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
92
packages/core/upload/admin/src/utils/normalizeAPIError.ts
Normal file
92
packages/core/upload/admin/src/utils/normalizeAPIError.ts
Normal file
@ -0,0 +1,92 @@
|
||||
import type { errors } from '@strapi/utils';
|
||||
import type { FetchError } from '@strapi/admin/strapi-admin';
|
||||
|
||||
type ApiError = InstanceType<(typeof errors)[keyof typeof errors]>;
|
||||
|
||||
interface NormalizeErrorOptions {
|
||||
name?: string;
|
||||
intlMessagePrefixCallback?: (id: string) => string;
|
||||
}
|
||||
|
||||
interface NormalizeErrorReturn {
|
||||
id: string;
|
||||
defaultMessage: string;
|
||||
name?: string;
|
||||
values: Record<'path', string> | Record<string, never>;
|
||||
}
|
||||
|
||||
interface YupFormattedError {
|
||||
path: string[];
|
||||
message: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
function getPrefixedId(message: string, callback?: (prefixedMessage: string) => string) {
|
||||
const prefixedMessage = `apiError.${message}`;
|
||||
|
||||
// if a prefix function has been passed in it is used to
|
||||
// prefix the id, e.g. to allow an error message to be
|
||||
// set only for a localization namespace
|
||||
if (typeof callback === 'function') {
|
||||
return callback(prefixedMessage);
|
||||
}
|
||||
|
||||
return prefixedMessage;
|
||||
}
|
||||
|
||||
function normalizeError(
|
||||
error: ApiError | YupFormattedError,
|
||||
{ name, intlMessagePrefixCallback }: NormalizeErrorOptions
|
||||
): NormalizeErrorReturn {
|
||||
const { message } = error;
|
||||
|
||||
const normalizedError = {
|
||||
id: getPrefixedId(message, intlMessagePrefixCallback),
|
||||
defaultMessage: message,
|
||||
name: error.name ?? name,
|
||||
values: {},
|
||||
};
|
||||
|
||||
if ('path' in error) {
|
||||
normalizedError.values = { path: error.path.join('.') };
|
||||
}
|
||||
|
||||
return normalizedError;
|
||||
}
|
||||
|
||||
const validateErrorIsYupValidationError = (
|
||||
err: ApiError
|
||||
): err is errors.YupValidationError & { details: { errors: YupFormattedError[] } } =>
|
||||
typeof err.details === 'object' && err.details !== null && 'errors' in err.details;
|
||||
|
||||
/**
|
||||
* Normalize the format of `ResponseError`
|
||||
* in places where the hook `useAPIErrorHandler` can not called
|
||||
* (e.g. outside of a React component).
|
||||
*/
|
||||
export function normalizeAPIError(
|
||||
apiError: FetchError,
|
||||
intlMessagePrefixCallback?: NormalizeErrorOptions['intlMessagePrefixCallback']
|
||||
):
|
||||
| NormalizeErrorReturn
|
||||
| { name: string; message: string | null; errors: NormalizeErrorReturn[] }
|
||||
| null {
|
||||
const error = apiError.response?.data.error;
|
||||
|
||||
if (error) {
|
||||
// some errors carry multiple errors (such as ValidationError)
|
||||
if (validateErrorIsYupValidationError(error)) {
|
||||
return {
|
||||
name: error.name,
|
||||
message: error?.message || null,
|
||||
errors: error.details.errors.map((err) =>
|
||||
normalizeError(err, { name: error.name, intlMessagePrefixCallback })
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
return normalizeError(error, { intlMessagePrefixCallback });
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
/**
|
||||
* Create the file URL with the backend URL
|
||||
* @param {Object} asset
|
||||
* @param {String} fileURL - if true, return the file URL with the Backend url
|
||||
* if there's no file url or it doesn't start with a slash return the original file url.
|
||||
* @return {String} file Url
|
||||
*/
|
||||
const prefixFileUrlWithBackendUrl = (fileURL) => {
|
||||
return !!fileURL && fileURL.startsWith('/') ? `${window.strapi.backendURL}${fileURL}` : fileURL;
|
||||
};
|
||||
|
||||
export default prefixFileUrlWithBackendUrl;
|
||||
@ -0,0 +1,3 @@
|
||||
export const prefixFileUrlWithBackendUrl = (fileURL?: string) => {
|
||||
return !!fileURL && fileURL.startsWith('/') ? `${window.strapi.backendURL}${fileURL}` : fileURL;
|
||||
};
|
||||
@ -1,13 +0,0 @@
|
||||
const prefixPluginTranslations = (trad, pluginId) => {
|
||||
if (!pluginId) {
|
||||
throw new TypeError("pluginId can't be empty");
|
||||
}
|
||||
|
||||
return Object.keys(trad).reduce((acc, current) => {
|
||||
acc[`${pluginId}.${current}`] = trad[current];
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
};
|
||||
|
||||
export { prefixPluginTranslations };
|
||||
@ -0,0 +1,15 @@
|
||||
type Translations = {
|
||||
[key: string]: string;
|
||||
};
|
||||
|
||||
export const prefixPluginTranslations = (trad: Translations, pluginId?: string) => {
|
||||
if (!pluginId) {
|
||||
throw new TypeError("pluginId can't be empty");
|
||||
}
|
||||
|
||||
return Object.keys(trad).reduce((acc: Translations, current: string) => {
|
||||
acc[`${pluginId}.${current}`] = trad[current];
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
};
|
||||
@ -1,6 +1,9 @@
|
||||
// TODO: replace this import with the import from constants file when it will be migrated to TS
|
||||
import { AssetSource } from '../newConstants';
|
||||
import { typeFromMime } from './typeFromMime';
|
||||
import type { RawFile } from '../../../shared/contracts/files';
|
||||
|
||||
export const rawFileToAsset = (rawFile, assetSource) => {
|
||||
export const rawFileToAsset = (rawFile: RawFile, assetSource: AssetSource) => {
|
||||
return {
|
||||
size: rawFile.size / 1000,
|
||||
createdAt: new Date(rawFile.lastModified).toISOString(),
|
||||
@ -1,4 +1,4 @@
|
||||
import { appendSearchParamsToUrl } from '..';
|
||||
import { appendSearchParamsToUrl } from '../appendSearchParamsToUrl';
|
||||
|
||||
describe('appendSearchParamsToUrl', () => {
|
||||
const updateTime = '2023-07-19T03:00:00.000Z';
|
||||
@ -47,7 +47,7 @@ describe('appendSearchParamsToUrl', () => {
|
||||
});
|
||||
|
||||
describe('relativeURL', () => {
|
||||
let originalBackendURL;
|
||||
let originalBackendURL: string;
|
||||
|
||||
beforeAll(() => {
|
||||
/**
|
||||
@ -1,4 +1,4 @@
|
||||
import { containsAssetFilter } from '..';
|
||||
import { containsAssetFilter } from '../containsAssetFilter';
|
||||
|
||||
describe('containsAssetFilter', () => {
|
||||
test('does not fail on empty query objects', () => {
|
||||
@ -10,11 +10,11 @@ describe('downloadFile', () => {
|
||||
|
||||
documentSpy.mockReturnValue({
|
||||
click: clickSpy,
|
||||
set href(val) {
|
||||
set href(val: string) {
|
||||
hrefSpy(val);
|
||||
},
|
||||
setAttribute: setAttributeSpy,
|
||||
});
|
||||
} as unknown as HTMLAnchorElement);
|
||||
|
||||
await downloadFile('/some/file', 'my-filename');
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import findRecursiveFolderByValue from '../findRecursiveFolderByValue';
|
||||
import { findRecursiveFolderByValue } from '../findRecursiveFolderByValue';
|
||||
|
||||
const FIXTURE = [
|
||||
{
|
||||
@ -1,51 +0,0 @@
|
||||
import findRecursiveFolderMetadatas from '../findRecursiveFolderMetadatas';
|
||||
|
||||
const FIXTURE_STRUCTURE = {
|
||||
value: null,
|
||||
label: 'Media Library',
|
||||
children: [
|
||||
{
|
||||
value: 1,
|
||||
label: 'Cats',
|
||||
children: [
|
||||
{
|
||||
value: 2,
|
||||
label: 'Michka',
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
describe('ML || utils || findRecursiveFolderMetadatas', () => {
|
||||
test('should return parent folder id and label', () => {
|
||||
const result = findRecursiveFolderMetadatas(FIXTURE_STRUCTURE, 2);
|
||||
|
||||
expect(result).toEqual({
|
||||
parentId: 1,
|
||||
currentFolderLabel: 'Michka',
|
||||
});
|
||||
});
|
||||
|
||||
test('should return parent id null if parent is root ML', () => {
|
||||
const result = findRecursiveFolderMetadatas(FIXTURE_STRUCTURE, 1);
|
||||
|
||||
expect(result).toEqual({
|
||||
currentFolderLabel: 'Cats',
|
||||
parentId: null,
|
||||
});
|
||||
});
|
||||
|
||||
test('should return null if searched id does not exist', () => {
|
||||
const result = findRecursiveFolderMetadatas(FIXTURE_STRUCTURE, 10);
|
||||
|
||||
expect(result).toEqual(null);
|
||||
});
|
||||
|
||||
test('should return null if searched id does not exist (nullish)', () => {
|
||||
const result = findRecursiveFolderMetadatas(FIXTURE_STRUCTURE, null);
|
||||
|
||||
expect(result).toEqual(null);
|
||||
});
|
||||
});
|
||||
@ -1,4 +1,4 @@
|
||||
import formatBytes from '../formatBytes';
|
||||
import { formatBytes } from '../formatBytes';
|
||||
|
||||
describe('UPLOAD | components | EditForm | utils', () => {
|
||||
describe('formatBytes', () => {
|
||||
@ -1,6 +1,6 @@
|
||||
import { FetchError } from '@strapi/admin/strapi-admin';
|
||||
|
||||
import getAPIInnerErrors from '../getAPIInnerErrors';
|
||||
import { getAPIInnerErrors } from '../getAPIInnerErrors';
|
||||
|
||||
const API_VALIDATION_ERROR_FIXTURE = new FetchError('ValidationError', {
|
||||
data: {
|
||||
@ -24,7 +24,6 @@ const API_VALIDATION_ERROR_FIXTURE = new FetchError('ValidationError', {
|
||||
},
|
||||
},
|
||||
},
|
||||
status: 422,
|
||||
});
|
||||
|
||||
const API_APPLICATION_ERROR_FIXTURE = new FetchError('ApplicationError', {
|
||||
@ -35,7 +34,6 @@ const API_APPLICATION_ERROR_FIXTURE = new FetchError('ApplicationError', {
|
||||
details: {},
|
||||
},
|
||||
},
|
||||
status: 400,
|
||||
});
|
||||
|
||||
describe('getAPIInnerError', () => {
|
||||
@ -1,153 +0,0 @@
|
||||
import getAllowedFiles from '../getAllowedFiles';
|
||||
|
||||
const files = [
|
||||
{
|
||||
id: 1,
|
||||
mime: 'application',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
mime: 'application',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
mime: 'image/png',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
mime: 'video/mov',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
mime: 'image/jpg',
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
mime: 'image/test',
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
mime: 'audio/mpeg',
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
mime: 'audio/x-wav',
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
mime: 'audio/ogg',
|
||||
},
|
||||
];
|
||||
|
||||
describe('UPLOAD | components | MediaLibraryInput | utils | getAllowedFiles', () => {
|
||||
it('returns an empty array of when the allowed files is empty', () => {
|
||||
const results = getAllowedFiles([], files);
|
||||
|
||||
expect(results).toEqual([]);
|
||||
});
|
||||
|
||||
it('returns an array with elements that are not video or image when the allowedTypes is files', () => {
|
||||
const results = getAllowedFiles(['files'], files);
|
||||
|
||||
expect(results).toEqual([
|
||||
{
|
||||
id: 1,
|
||||
mime: 'application',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
mime: 'application',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns an array with elements that are only video when the allowedTypes is videos', () => {
|
||||
const results = getAllowedFiles(['videos'], files);
|
||||
|
||||
expect(results).toEqual([
|
||||
{
|
||||
id: 4,
|
||||
mime: 'video/mov',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns an array with elements that are only video when the allowedTypes is videos', () => {
|
||||
const results = getAllowedFiles(['audios'], files);
|
||||
|
||||
expect(results).toEqual([
|
||||
{
|
||||
id: 7,
|
||||
mime: 'audio/mpeg',
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
mime: 'audio/x-wav',
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
mime: 'audio/ogg',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns an array with elements that are only image when the allowedTypes is images', () => {
|
||||
const results = getAllowedFiles(['images'], files);
|
||||
|
||||
expect(results).toEqual([
|
||||
{
|
||||
id: 3,
|
||||
mime: 'image/png',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
mime: 'image/jpg',
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
mime: 'image/test',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns an array with elements that are image and video when the allowedTypes are videos and images', () => {
|
||||
const results = getAllowedFiles(['videos', 'images', 'audios'], files);
|
||||
|
||||
expect(results).toEqual([
|
||||
{
|
||||
id: 3,
|
||||
mime: 'image/png',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
mime: 'video/mov',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
mime: 'image/jpg',
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
mime: 'image/test',
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
mime: 'audio/mpeg',
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
mime: 'audio/x-wav',
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
mime: 'audio/ogg',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns an array with all the elements', () => {
|
||||
const results = getAllowedFiles(['videos', 'images', 'files', 'audios'], files);
|
||||
|
||||
expect(results).toEqual(files);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,133 @@
|
||||
import { getAllowedFiles } from '../getAllowedFiles';
|
||||
|
||||
const COMMON_PROPERTIES = {
|
||||
size: 100,
|
||||
createdAt: '2021-09-01T00:00:00.000Z',
|
||||
updatedAt: '2021-09-01T00:00:00.000Z',
|
||||
folder: null,
|
||||
folderPath: '/',
|
||||
documentId: 'documentId',
|
||||
hash: 'hash',
|
||||
locale: null,
|
||||
provider: 'local',
|
||||
isSelectable: true,
|
||||
type: 'asset',
|
||||
};
|
||||
|
||||
const FILE_1 = {
|
||||
id: 1,
|
||||
mime: 'application',
|
||||
name: 'file.application',
|
||||
url: '/uploads/file.application',
|
||||
...COMMON_PROPERTIES,
|
||||
};
|
||||
|
||||
const FILE_2 = {
|
||||
id: 2,
|
||||
mime: 'application',
|
||||
name: 'file2.application',
|
||||
url: '/uploads/file2.application',
|
||||
...COMMON_PROPERTIES,
|
||||
};
|
||||
|
||||
const FILE_3 = {
|
||||
id: 3,
|
||||
mime: 'image/png',
|
||||
name: 'image.png',
|
||||
url: '/uploads/image.png',
|
||||
...COMMON_PROPERTIES,
|
||||
};
|
||||
|
||||
const FILE_4 = {
|
||||
id: 4,
|
||||
mime: 'video/mov',
|
||||
name: 'video.mov',
|
||||
url: '/uploads/video.mov',
|
||||
...COMMON_PROPERTIES,
|
||||
};
|
||||
|
||||
const FILE_5 = {
|
||||
id: 5,
|
||||
mime: 'image/jpg',
|
||||
name: 'image2.jpg',
|
||||
url: '/uploads/image2.jpg',
|
||||
...COMMON_PROPERTIES,
|
||||
};
|
||||
|
||||
const FILE_6 = {
|
||||
id: 6,
|
||||
mime: 'image/test',
|
||||
name: 'image.test',
|
||||
url: '/uploads/image.test',
|
||||
...COMMON_PROPERTIES,
|
||||
};
|
||||
|
||||
const FILE_7 = {
|
||||
id: 7,
|
||||
mime: 'audio/mpeg',
|
||||
name: 'audio.mpeg',
|
||||
url: '/uploads/audio.mpeg',
|
||||
...COMMON_PROPERTIES,
|
||||
};
|
||||
|
||||
const FILE_8 = {
|
||||
id: 8,
|
||||
mime: 'audio/x-wav',
|
||||
name: 'audio.x-wav',
|
||||
url: '/uploads/audio.x-wav',
|
||||
...COMMON_PROPERTIES,
|
||||
};
|
||||
|
||||
const FILE_9 = {
|
||||
id: 9,
|
||||
mime: 'audio/ogg',
|
||||
name: 'audio.ogg',
|
||||
url: '/uploads/audio.ogg',
|
||||
...COMMON_PROPERTIES,
|
||||
};
|
||||
|
||||
const files = [FILE_1, FILE_2, FILE_3, FILE_4, FILE_5, FILE_6, FILE_7, FILE_8, FILE_9];
|
||||
|
||||
describe('UPLOAD | components | MediaLibraryInput | utils | getAllowedFiles', () => {
|
||||
it('returns an empty array of when the allowed files is empty', () => {
|
||||
const results = getAllowedFiles([], files);
|
||||
|
||||
expect(results).toEqual([]);
|
||||
});
|
||||
|
||||
it('returns an array with elements that are not video or image when the allowedTypes is files', () => {
|
||||
const results = getAllowedFiles(['files'], files);
|
||||
|
||||
expect(results).toEqual([FILE_1, FILE_2]);
|
||||
});
|
||||
|
||||
it('returns an array with elements that are only video when the allowedTypes is videos', () => {
|
||||
const results = getAllowedFiles(['videos'], files);
|
||||
|
||||
expect(results).toEqual([FILE_4]);
|
||||
});
|
||||
|
||||
it('returns an array with elements that are only video when the allowedTypes is videos', () => {
|
||||
const results = getAllowedFiles(['audios'], files);
|
||||
|
||||
expect(results).toEqual([FILE_7, FILE_8, FILE_9]);
|
||||
});
|
||||
|
||||
it('returns an array with elements that are only image when the allowedTypes is images', () => {
|
||||
const results = getAllowedFiles(['images'], files);
|
||||
|
||||
expect(results).toEqual([FILE_3, FILE_5, FILE_6]);
|
||||
});
|
||||
|
||||
it('returns an array with elements that are image and video when the allowedTypes are videos and images', () => {
|
||||
const results = getAllowedFiles(['videos', 'images', 'audios'], files);
|
||||
|
||||
expect(results).toEqual([FILE_3, FILE_4, FILE_5, FILE_6, FILE_7, FILE_8, FILE_9]);
|
||||
});
|
||||
|
||||
it('returns an array with all the elements', () => {
|
||||
const results = getAllowedFiles(['videos', 'images', 'files', 'audios'], files);
|
||||
|
||||
expect(results).toEqual(files);
|
||||
});
|
||||
});
|
||||
@ -1,9 +1,10 @@
|
||||
import { getBreadcrumbDataCM } from '..';
|
||||
import { getBreadcrumbDataCM } from '../getBreadcrumbDataCM';
|
||||
|
||||
const FIXTURE_FOLDER = {
|
||||
id: 1,
|
||||
name: 'first-level',
|
||||
path: '/1 ',
|
||||
pathId: 1,
|
||||
};
|
||||
|
||||
describe('getBreadcrumbDataCM', () => {
|
||||
@ -1,4 +1,4 @@
|
||||
import { getBreadcrumbDataML } from '..';
|
||||
import { getBreadcrumbDataML } from '../getBreadcrumbDataML';
|
||||
|
||||
const FIXTURE_PATHNAME = '/media-library';
|
||||
const FIXTURE_QUERY = {
|
||||
@ -1,11 +1,10 @@
|
||||
import getFileExtension from '../getFileExtension';
|
||||
import { getFileExtension } from '../getFileExtension';
|
||||
|
||||
describe('getFileExtension', () => {
|
||||
it('should return undefined if ext does not exits', () => {
|
||||
const ext = null;
|
||||
const expected = null;
|
||||
|
||||
// @ts-expect-error ext should be a string so will throw error that ext is null.
|
||||
expect(getFileExtension(ext)).toEqual(expected);
|
||||
});
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import getFolderParents from '../getFolderParents';
|
||||
import { getFolderParents } from '../getFolderParents';
|
||||
|
||||
const FIXTURE_FOLDER_STRUCTURE = [
|
||||
{
|
||||
@ -1,8 +1,8 @@
|
||||
import { getFolderURL } from '..';
|
||||
import { getFolderURL } from '../getFolderURL';
|
||||
|
||||
const FIXTURE_PATHNAME = '/media-library';
|
||||
const FIXTURE_QUERY = {};
|
||||
const FIXTURE_FOLDER = 1;
|
||||
const FIXTURE_FOLDER = '1';
|
||||
const FIXTURE_FOLDER_PATH = '/1/2/3';
|
||||
|
||||
describe('getFolderURL', () => {
|
||||
@ -24,12 +24,8 @@ describe('getFolderURL', () => {
|
||||
|
||||
test('keeps and stringifies query parameter', () => {
|
||||
expect(
|
||||
getFolderURL(
|
||||
FIXTURE_PATHNAME,
|
||||
{ ...FIXTURE_QUERY, some: 'thing' },
|
||||
{ folder: FIXTURE_FOLDER }
|
||||
)
|
||||
).toMatchInlineSnapshot(`"/media-library?some=thing&folder=1"`);
|
||||
getFolderURL(FIXTURE_PATHNAME, { ...FIXTURE_QUERY }, { folder: FIXTURE_FOLDER })
|
||||
).toMatchInlineSnapshot(`"/media-library?folder=1"`);
|
||||
});
|
||||
|
||||
test('includes folderPath if provided', () => {
|
||||
@ -24,7 +24,6 @@ const API_VALIDATION_ERROR_FIXTURE = new FetchError('ValidationError', {
|
||||
},
|
||||
},
|
||||
},
|
||||
status: 422,
|
||||
});
|
||||
|
||||
const API_APPLICATION_ERROR_FIXTURE = new FetchError('ApplicationError', {
|
||||
@ -35,7 +34,6 @@ const API_APPLICATION_ERROR_FIXTURE = new FetchError('ApplicationError', {
|
||||
details: {},
|
||||
},
|
||||
},
|
||||
status: 400,
|
||||
});
|
||||
|
||||
describe('normalizeAPIError', () => {
|
||||
@ -66,7 +64,7 @@ describe('normalizeAPIError', () => {
|
||||
});
|
||||
|
||||
test('Handle ValidationError with custom prefix function', () => {
|
||||
const prefixFunction = (id) => `custom.${id}`;
|
||||
const prefixFunction = (id: string) => `custom.${id}`;
|
||||
|
||||
expect(normalizeAPIError(API_VALIDATION_ERROR_FIXTURE, prefixFunction)).toStrictEqual({
|
||||
name: 'ValidationError',
|
||||
@ -103,7 +101,7 @@ describe('normalizeAPIError', () => {
|
||||
});
|
||||
|
||||
test('Handle ApplicationError with custom prefix function', () => {
|
||||
const prefixFunction = (id) => `custom.${id}`;
|
||||
const prefixFunction = (id: string) => `custom.${id}`;
|
||||
|
||||
expect(normalizeAPIError(API_APPLICATION_ERROR_FIXTURE, prefixFunction)).toStrictEqual({
|
||||
name: 'ApplicationError',
|
||||
@ -1,4 +1,4 @@
|
||||
import prefixFileUrlWithBackendUrl from '../prefixFileUrlWithBackendUrl';
|
||||
import { prefixFileUrlWithBackendUrl } from '../prefixFileUrlWithBackendUrl';
|
||||
|
||||
describe('prefixFileUrlWithBackendUrl', () => {
|
||||
it("should add the strapi back-end url if the file's url startsWith '/'", () => {
|
||||
@ -1,4 +1,4 @@
|
||||
import toSingularTypes from '../toSingularTypes';
|
||||
import { toSingularTypes } from '../toSingularTypes';
|
||||
|
||||
describe('UPLOAD | utils | toSingularTypes', () => {
|
||||
it('returns an array', () => {
|
||||
@ -1,4 +1,5 @@
|
||||
import { AssetType } from '../../constants';
|
||||
// TODO: replace this import with the import from constants file when it will be migrated to TS
|
||||
import { AssetType } from '../../newConstants';
|
||||
import { typeFromMime } from '../typeFromMime';
|
||||
|
||||
describe('typeFromMime', () => {
|
||||
@ -1,14 +0,0 @@
|
||||
/**
|
||||
* Transforms an arrays of plural type to singular one
|
||||
* @param {Object[]} types
|
||||
* @returns Object[]
|
||||
*/
|
||||
const toSingularTypes = (types) => {
|
||||
if (!types) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return types.map((type) => type.substring(0, type.length - 1));
|
||||
};
|
||||
|
||||
export default toSingularTypes;
|
||||
9
packages/core/upload/admin/src/utils/toSingularTypes.ts
Normal file
9
packages/core/upload/admin/src/utils/toSingularTypes.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export const toSingularTypes = (types?: string[]) => {
|
||||
if (!types) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return types.map((type) => type.substring(0, type.length - 1));
|
||||
};
|
||||
|
||||
export default toSingularTypes;
|
||||
@ -1,6 +1,7 @@
|
||||
import { AssetType } from '../constants';
|
||||
// TODO: replace this import with the import from constants file when it will be migrated to TS
|
||||
import { AssetType } from '../newConstants';
|
||||
|
||||
export const typeFromMime = (mime) => {
|
||||
export const typeFromMime = (mime: string) => {
|
||||
if (mime.includes(AssetType.Image)) {
|
||||
return AssetType.Image;
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
import { translatedErrors as errorsTrads } from '@strapi/admin/strapi-admin';
|
||||
import * as yup from 'yup';
|
||||
|
||||
import getTrad from './getTrad';
|
||||
import { getTrad } from './getTrad';
|
||||
|
||||
export const urlSchema = yup.object().shape({
|
||||
urls: yup.string().test({
|
||||
@ -1,18 +1,19 @@
|
||||
import { AssetSource } from '../constants';
|
||||
// TODO: replace this import with the import from constants file when it will be migrated to TS
|
||||
import { AssetSource } from '../newConstants';
|
||||
|
||||
import { typeFromMime } from './typeFromMime';
|
||||
|
||||
function getFilenameFromURL(url) {
|
||||
function getFilenameFromURL(url: string) {
|
||||
return new URL(url).pathname.split('/').pop();
|
||||
}
|
||||
|
||||
export const urlsToAssets = async (urls) => {
|
||||
export const urlsToAssets = async (urls: string[]) => {
|
||||
const assetPromises = urls.map((url) =>
|
||||
fetch(url).then(async (res) => {
|
||||
const blob = await res.blob();
|
||||
|
||||
const loadedFile = new File([blob], getFilenameFromURL(res.url), {
|
||||
type: res.headers.get('content-type'),
|
||||
const loadedFile = new File([blob], getFilenameFromURL(res.url)!, {
|
||||
type: res.headers.get('content-type') || undefined,
|
||||
});
|
||||
|
||||
return {
|
||||
@ -29,7 +30,7 @@ export const urlsToAssets = async (urls) => {
|
||||
const assets = assetsResults.map((fullFilledAsset) => ({
|
||||
source: AssetSource.Url,
|
||||
name: fullFilledAsset.name,
|
||||
type: typeFromMime(fullFilledAsset.mime),
|
||||
type: typeFromMime(fullFilledAsset.mime!),
|
||||
url: fullFilledAsset.url,
|
||||
ext: fullFilledAsset.url.split('.').pop(),
|
||||
mime: fullFilledAsset.mime,
|
||||
@ -5,6 +5,6 @@
|
||||
"baseUrl": ".",
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"include": ["./src", "../shared", "../package.json"],
|
||||
"include": ["./src", "./custom.d.ts", "../shared", "../package.json"],
|
||||
"exclude": ["**/__mocks__", "./src/**/tests", "**/*.test.*"]
|
||||
}
|
||||
|
||||
@ -7,5 +7,5 @@
|
||||
"@tests/*": ["./tests/*"]
|
||||
}
|
||||
},
|
||||
"include": ["../package.json", "./src", "../shared", "./tests"]
|
||||
"include": ["../package.json", "./src", "../shared", "./tests", "./custom.d.ts"]
|
||||
}
|
||||
|
||||
@ -87,6 +87,7 @@
|
||||
"@testing-library/dom": "10.1.0",
|
||||
"@testing-library/react": "15.0.7",
|
||||
"@testing-library/user-event": "14.5.2",
|
||||
"@types/byte-size": "8.1.2",
|
||||
"@types/fs-extra": "11.0.4",
|
||||
"@types/koa": "2.13.4",
|
||||
"@types/koa-range": "0.3.5",
|
||||
|
||||
@ -4,6 +4,58 @@ type SortOrder = 'ASC' | 'DESC';
|
||||
|
||||
type SortKey = 'createdAt' | 'name';
|
||||
|
||||
// Abstract type for comparison operators where the keys are generic strings
|
||||
type ComparisonOperators<T> = {
|
||||
[operator: string]: T | T[] | boolean; // Any string can be used as an operator key
|
||||
};
|
||||
|
||||
// Abstract type for filter conditions with dynamic field names
|
||||
export type FilterCondition<T> = {
|
||||
[field: string]: T | ComparisonOperators<T> | FilterCondition<T>; // Field names are dynamic and values are comparison operators
|
||||
};
|
||||
|
||||
// Abstract type for filters where the logical operator (like $and) is a generic string
|
||||
type Filters<T> = {
|
||||
[logicOperator: string]: FilterCondition<T>[]; // Logical operator key is a generic string
|
||||
};
|
||||
|
||||
export type Query = {
|
||||
_q?: string;
|
||||
folderPath?: string;
|
||||
folder?:
|
||||
| null
|
||||
| number
|
||||
| {
|
||||
id: number;
|
||||
};
|
||||
page?:
|
||||
| string
|
||||
| number
|
||||
| {
|
||||
id: string | number;
|
||||
};
|
||||
pageSize?: string | number;
|
||||
pagination?: {
|
||||
pageSize: number;
|
||||
};
|
||||
sort?: `${SortKey}:${SortOrder}`;
|
||||
filters?: Filters<string | number | boolean>;
|
||||
state?: boolean;
|
||||
};
|
||||
|
||||
type FileFormat = {
|
||||
name: string;
|
||||
hash: string;
|
||||
ext: string;
|
||||
mime: string;
|
||||
path: null | string;
|
||||
width: number;
|
||||
height: number;
|
||||
size: number;
|
||||
sizeInBytes: number;
|
||||
url: string;
|
||||
};
|
||||
|
||||
export interface File {
|
||||
id: number;
|
||||
name: string;
|
||||
@ -11,7 +63,7 @@ export interface File {
|
||||
caption?: string | null;
|
||||
width?: number;
|
||||
height?: number;
|
||||
formats?: Record<string, unknown>;
|
||||
formats?: Record<string, FileFormat>;
|
||||
hash: string;
|
||||
ext?: string;
|
||||
mime?: string;
|
||||
@ -23,7 +75,7 @@ export interface File {
|
||||
provider?: string;
|
||||
provider_metadata?: Record<string, unknown>;
|
||||
isUrlSigned?: boolean;
|
||||
folder?: number;
|
||||
folder?: number | null;
|
||||
folderPath?: string;
|
||||
related?: {
|
||||
id: string | number;
|
||||
@ -33,12 +85,11 @@ export interface File {
|
||||
createdAt?: string;
|
||||
updatedAt?: string;
|
||||
createdBy?: number;
|
||||
publishedAt?: string;
|
||||
updatedBy?: number;
|
||||
isLocal?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /upload/files - Get files
|
||||
*/
|
||||
export interface RawFile extends Blob {
|
||||
size: number;
|
||||
lastModified: number;
|
||||
@ -53,6 +104,9 @@ export interface Pagination {
|
||||
total: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /upload/files - Get files
|
||||
*/
|
||||
export declare namespace GetFiles {
|
||||
export interface Request {
|
||||
body: {};
|
||||
|
||||
@ -9,20 +9,20 @@ import type { File } from './files';
|
||||
export interface Folder {
|
||||
id: number;
|
||||
name: string;
|
||||
pathId: number;
|
||||
pathId?: number;
|
||||
/**
|
||||
* parent id
|
||||
*/
|
||||
parent?: number;
|
||||
parent?: number | null | Folder;
|
||||
/**
|
||||
* children ids
|
||||
*/
|
||||
children?: number[];
|
||||
path: string;
|
||||
path?: string;
|
||||
files?: File[];
|
||||
}
|
||||
|
||||
type FolderNode = Partial<Folder> & {
|
||||
export type FolderNode = Partial<Omit<Folder, 'children'>> & {
|
||||
children: FolderNode[];
|
||||
};
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user