diff --git a/lerna.json b/lerna.json index 20f8b786f..3c1a386ec 100644 --- a/lerna.json +++ b/lerna.json @@ -1,4 +1,4 @@ { "$schema": "node_modules/lerna/schemas/lerna-schema.json", "version": "3.1.1" -} \ No newline at end of file +} diff --git a/package.json b/package.json index 500831db0..f0738abfa 100644 --- a/package.json +++ b/package.json @@ -29,12 +29,12 @@ "@mui/lab": "5.0.0-alpha.120", "@mui/material": "5.11.10", "@mui/private-theming": "5.11.9", - "@netcracker/qubership-apihub-api-diff": "dev", - "@netcracker/qubership-apihub-api-doc-viewer": "dev", - "@netcracker/qubership-apihub-api-processor": "dev", - "@netcracker/qubership-apihub-api-unifier": "dev", - "@netcracker/qubership-apihub-api-visitor": "dev", - "@netcracker/qubership-apihub-apispec-view": "dev", + "@netcracker/qubership-apihub-api-diff": "feature-move-prefix-from-server-to-path", + "@netcracker/qubership-apihub-api-doc-viewer": "feature-performance-optimization", + "@netcracker/qubership-apihub-api-processor": "feature-performance-optimization", + "@netcracker/qubership-apihub-api-unifier": "feature-performance-optimization", + "@netcracker/qubership-apihub-api-visitor": "feature-performance-optimization", + "@netcracker/qubership-apihub-apispec-view": "feature-performance-optimization", "@netcracker/qubership-apihub-class-view": "1.0.3", "@netcracker/qubership-apihub-rest-playground": "1.2.0", "@otjs/plaintext": "0.2.2", diff --git a/packages/agents/src/routes/root/NamespacePage/ServicesPage/ServicesPageBody/package-version-builder-worker.ts b/packages/agents/src/routes/root/NamespacePage/ServicesPage/ServicesPageBody/package-version-builder-worker.ts index d8b08e665..b5fae1c0b 100644 --- a/packages/agents/src/routes/root/NamespacePage/ServicesPage/ServicesPageBody/package-version-builder-worker.ts +++ b/packages/agents/src/routes/root/NamespacePage/ServicesPage/ServicesPageBody/package-version-builder-worker.ts @@ -23,7 +23,9 @@ import { COMPLETE_PUBLISH_STATUS, ERROR_PUBLISH_STATUS } from '@apihub/entities/ import { BUILD_TYPE, PackageVersionBuilder } from '@netcracker/qubership-apihub-api-processor' import { packageVersionResolver, + rawDocumentResolver, versionDeprecatedResolver, + versionDocumentsResolver, versionOperationsResolver, versionReferencesResolver, } from '@netcracker/qubership-apihub-ui-shared/utils/builder-resolvers' @@ -77,6 +79,8 @@ const worker: PackageVersionBuilderWorker = { versionReferencesResolver: await versionReferencesResolver(), versionOperationsResolver: await versionOperationsResolver(), versionDeprecatedResolver: await versionDeprecatedResolver(), + versionDocumentsResolver: await versionDocumentsResolver(), + rawDocumentResolver: await rawDocumentResolver(), }, }) diff --git a/packages/portal/server/mocks/packages/document-contents.ts b/packages/portal/server/mocks/packages/document-contents.ts index 39f92ddd2..d44f89138 100644 --- a/packages/portal/server/mocks/packages/document-contents.ts +++ b/packages/portal/server/mocks/packages/document-contents.ts @@ -35,7 +35,6 @@ export const OPENAPI_SPEC: DocumentDto = { deprecated: true, apiKind: 'bwc', apiType: 'rest', - dataHash: 'sdfsdfsf242', metadata: { tags: ['RestControllerV1'], path: '/v1/pets', @@ -48,7 +47,6 @@ export const OPENAPI_SPEC: DocumentDto = { deprecated: false, apiKind: 'no-bwc', apiType: 'rest', - dataHash: 'sdfsdfsf243', metadata: { tags: ['RestControllerV3'], path: '/v3/fruit', @@ -60,7 +58,6 @@ export const OPENAPI_SPEC: DocumentDto = { title: 'Array Cars', apiKind: 'bwc', apiType: 'rest', - dataHash: 'sdfsdfsf244', metadata: { tags: ['RestControllerV5'], path: '/v5/cars/array', @@ -72,7 +69,6 @@ export const OPENAPI_SPEC: DocumentDto = { title: 'Get Cars 2', apiKind: 'bwc', apiType: 'rest', - dataHash: 'sdfsdfsf245', metadata: { tags: ['A', 'B'], path: '/v5/cars/array', diff --git a/packages/portal/server/mocks/packages/operations.ts b/packages/portal/server/mocks/packages/operations.ts index b3550101f..b3143c67c 100644 --- a/packages/portal/server/mocks/packages/operations.ts +++ b/packages/portal/server/mocks/packages/operations.ts @@ -144,7 +144,6 @@ export const OPERATIONS: Writeable = { deprecated: true, apiKind: 'bwc', apiType: 'rest', - dataHash: 'sdfsdfsf242', path: '/quoteManagement/v5/quote', tags: ['RestControllerV5'], method: 'get', @@ -157,7 +156,6 @@ export const OPERATIONS: Writeable = { deprecated: true, apiKind: 'bwc', apiType: 'rest', - dataHash: 'sdfsdfsf242', path: '/quoteManagement/v5/quote', tags: ['RestControllerV5'], method: 'patch', @@ -170,7 +168,6 @@ export const OPERATIONS: Writeable = { deprecated: false, apiKind: 'no-bwc', apiType: 'rest', - dataHash: 'sdfsdfsf243', tags: ['TMF'], path: '/quoteManagement/v3/pets', method: 'get', @@ -182,7 +179,6 @@ export const OPERATIONS: Writeable = { title: 'Array Quote', apiKind: 'bwc', apiType: 'rest', - dataHash: 'sdfsdfsf244', tags: ['RestControllerV5', 'TMF'], path: '/quoteManagement/v5/array', method: 'get', @@ -194,7 +190,6 @@ export const OPERATIONS: Writeable = { title: 'Get Pets2', apiKind: 'bwc', apiType: 'rest', - dataHash: 'sdfsdfsf245', tags: ['TMF', 'RestControllerV5'], path: '/quoteManagement/v5/array', method: 'post', @@ -206,7 +201,6 @@ export const OPERATIONS: Writeable = { data: graphQlOperationData, operationId: 'query-fulltextquotesearch', title: 'fullTextQuoteSearch', - dataHash: '3a2a746b578324e016ddbcdc4c3b462288fabc55', apiKind: 'bwc', apiType: 'graphql', type: 'query', @@ -218,7 +212,6 @@ export const OPERATIONS: Writeable = { data: graphQlOperationData, operationId: 'mutation-createorupdatesessioncontext', title: 'createOrUpdateSessionContext', - dataHash: '045efe99015262f164d740e00e0b110e8a110fbb', apiKind: 'bwc', apiType: 'graphql', type: 'mutation', @@ -230,7 +223,6 @@ export const OPERATIONS: Writeable = { data: graphQlOperationData, operationId: 'subscription-postwaspublished', title: 'postWasPublished', - dataHash: 'f202090d17c30e85b4170e23994746132c711891', apiKind: 'bwc', apiType: 'graphql', type: 'subscription', @@ -282,7 +274,7 @@ export const OPERATIONS: Writeable = { export const DEPRECATED_OPERATIONS: Writeable = { operations: OPERATIONS.operations.map((operation) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { data, dataHash, ...operationBaseProps } = operation + const { data, ...operationBaseProps } = operation return { deprecatedCount: operationBaseProps.deprecated ? '1' : '5', deprecatedInPreviousVersions: operationBaseProps.deprecated ? ['2022.1', '2021.4'] : undefined, diff --git a/packages/portal/server/mocks/packages/types.ts b/packages/portal/server/mocks/packages/types.ts index 01dba1f9f..3dd625dac 100644 --- a/packages/portal/server/mocks/packages/types.ts +++ b/packages/portal/server/mocks/packages/types.ts @@ -200,7 +200,6 @@ type OperationMetadata = Readonly<{ apiType: ApiType data?: object packageRef?: string - dataHash?: string deprecated?: boolean tags?: string[] }> @@ -228,7 +227,7 @@ export type DeprecatedItemDto = Readonly<{ }> export type DeprecatedItemsDto = ReadonlyArray -export type OperationWithDeprecatedDto = Omit & Readonly<{ +export type OperationWithDeprecatedDto = Omit & Readonly<{ deprecatedCount?: string deprecatedInfo?: object deprecatedItems?: DeprecatedItemsDto @@ -347,8 +346,6 @@ export type OperationChangesMetadata = Readonly<{ packageRef?: string previousVersionPackageRef?: string apiKind?: ApiKind - dataHash?: string - previousDataHash?: string tags?: readonly string[] }> diff --git a/packages/portal/src/entities/documents.ts b/packages/portal/src/entities/documents.ts index bca584fe4..df2948f50 100644 --- a/packages/portal/src/entities/documents.ts +++ b/packages/portal/src/entities/documents.ts @@ -15,8 +15,6 @@ */ import type { FileKey, Key } from './keys' -import type { FileFormat } from './file-formats' -import { UNKNOWN_FILE_FORMAT } from './file-formats' import type { OperationData, OperationDto, @@ -28,20 +26,19 @@ import type { import { toPackageRef } from '@netcracker/qubership-apihub-ui-shared/entities/operations' import type { SpecType } from '@netcracker/qubership-apihub-ui-shared/utils/specs' import { UNKNOWN_SPEC_TYPE } from '@netcracker/qubership-apihub-ui-shared/utils/specs' +import type { FileFormat } from '@netcracker/qubership-apihub-ui-shared/utils/files' import { getFileFormat } from '@netcracker/qubership-apihub-ui-shared/utils/files' import type { MethodType } from '@netcracker/qubership-apihub-ui-shared/entities/method-types' import type { GraphQlOperationType } from '@netcracker/qubership-apihub-ui-shared/entities/graphql-operation-types' - -export type DocumentsDto = Readonly<{ - documents: ReadonlyArray - packages: PackagesRefs -}> +import { type DocumentsDto } from '@netcracker/qubership-apihub-ui-shared/entities/documents' +import { UNKNOWN_FILE_FORMAT } from '@netcracker/qubership-apihub-ui-shared/entities/file-formats' export type Documents = ReadonlyArray export type Document = Readonly<{ key: Key slug: Key + filename: string title: string type: SpecType format: FileFormat @@ -57,6 +54,7 @@ export type Document = Readonly<{ export type DocumentDto = Readonly<{ fileId: FileKey slug: Key + filename: string title: string type: SpecType format: FileFormat @@ -74,6 +72,7 @@ export function toDocument(value: DocumentDto, packagesRefs?: PackagesRefs): Doc return { key: value.fileId, slug: value.slug, + filename: value.filename, title: value.title, type: value.type, format: value.format ?? getFileFormat(value.fileId), @@ -100,6 +99,7 @@ export const EMPTY_DOC: Document = { key: '', title: '', slug: '', + filename: '', } export type RestMetaData = { diff --git a/packages/portal/src/entities/project-files.ts b/packages/portal/src/entities/project-files.ts index 581b9396c..910a80feb 100644 --- a/packages/portal/src/entities/project-files.ts +++ b/packages/portal/src/entities/project-files.ts @@ -15,9 +15,9 @@ */ import type { FileKey, Key } from './keys' -import type { FileFormat } from './file-formats' import type { SpecType } from '@netcracker/qubership-apihub-ui-shared/utils/specs' import type { ChangeStatus } from '@netcracker/qubership-apihub-ui-shared/entities/change-statuses' +import type { FileFormat } from '@netcracker/qubership-apihub-ui-shared/entities/file-formats' export type FileData = { content: FileContent diff --git a/packages/portal/src/routes/root/PortalPage/VersionPage/DocumentPreviewPage/DocumentPreviewCard.tsx b/packages/portal/src/routes/root/PortalPage/VersionPage/DocumentPreviewPage/DocumentPreviewCard.tsx index 834039856..aed6713c4 100644 --- a/packages/portal/src/routes/root/PortalPage/VersionPage/DocumentPreviewPage/DocumentPreviewCard.tsx +++ b/packages/portal/src/routes/root/PortalPage/VersionPage/DocumentPreviewPage/DocumentPreviewCard.tsx @@ -19,8 +19,8 @@ import { memo } from 'react' import { useParams } from 'react-router-dom' import { Box } from '@mui/material' import { DocumentPreviewContentBody } from './DocumentPreviewContentBody' -import { usePublishedDocumentRaw } from '../usePublishedDocumentRaw' import { usePackageParamsWithRef } from '../../usePackageParamsWithRef' +import { usePublishedDocumentRaw } from '@netcracker/qubership-apihub-ui-shared/hooks/documents/usePublishedDocumentRaw' export const DocumentPreviewCard: FC = memo(() => { const { documentId } = useParams() diff --git a/packages/portal/src/routes/root/PortalPage/VersionPage/GroupComparePage/GroupCompareContent.tsx b/packages/portal/src/routes/root/PortalPage/VersionPage/GroupComparePage/GroupCompareContent.tsx index a1c87c30b..030594b38 100644 --- a/packages/portal/src/routes/root/PortalPage/VersionPage/GroupComparePage/GroupCompareContent.tsx +++ b/packages/portal/src/routes/root/PortalPage/VersionPage/GroupComparePage/GroupCompareContent.tsx @@ -60,13 +60,11 @@ import { import { isEmpty, isNotEmpty } from '@netcracker/qubership-apihub-ui-shared/utils/arrays' import { LoadingIndicator } from '@netcracker/qubership-apihub-ui-shared/components/LoadingIndicator' import { CONTENT_PLACEHOLDER_AREA, Placeholder } from '@netcracker/qubership-apihub-ui-shared/components/Placeholder' -import { getActionForOperation } from '@apihub/utils/operations' +import { getActionForRestOperation } from '@apihub/utils/operations' import type { ChangeSeverity } from '@netcracker/qubership-apihub-ui-shared/entities/change-severities' import { ACTION_TYPE_COLOR_MAP, - ADD_ACTION_TYPE, CHANGE_SEVERITIES, - REMOVE_ACTION_TYPE, REPLACE_ACTION_TYPE, } from '@netcracker/qubership-apihub-ui-shared/entities/change-severities' import { format } from '@netcracker/qubership-apihub-ui-shared/utils/strings' @@ -178,18 +176,19 @@ export const GroupCompareContent: FC = memo(({ groupCh changeSummary, metadata: metadataObject, previousMetadata: previousMetadataObject, - diffs, } = change const metadata = metadataObject as OperationChangesMetadata & Partial & Partial const previousMetadata = previousMetadataObject as OperationChangesMetadata & Partial & Partial - const { action } = diffs?.[0] ?? {} - const operationAction = getActionForOperation(change, REPLACE_ACTION_TYPE) + const operationAction = getActionForRestOperation(change, REPLACE_ACTION_TYPE) const severity = getMajorSeverity(changeSummary!) const isMetaDataPresent = !!( - metadata?.title && metadata?.path && metadata?.method || + metadata?.title && metadata?.path && metadata?.method + ) + + const isPreviousMetaDataPresent = !!( previousMetadata?.title && previousMetadata?.path && previousMetadata?.method ) @@ -257,13 +256,12 @@ export const GroupCompareContent: FC = memo(({ groupCh }} /> @@ -277,8 +275,7 @@ export const GroupCompareContent: FC = memo(({ groupCh > { }) }, [changesSummary, isPackageFromDashboard, refPackageKey]) - const restGroupingPrefix = packageObj?.restGroupingPrefix + const groupPrefixTemplate = packageObj?.restGroupingPrefix + const currentGroupPrefix = getGroupPrefix(groupPrefixTemplate, group) + const previousGroupPrefix = getGroupPrefix(groupPrefixTemplate, previousGroup) + const { previousOperationKey: operationKeyForOriginOperation, @@ -199,26 +202,41 @@ export const DifferentOperationGroupsComparisonPage: FC = memo(() => { currentOperationKey: operationKey, }, operationAction) - const { data: originOperation, isLoading: isOriginOperationLoading } = useOperation({ + const { data: originOperation, isInitialLoading: isOriginOperationLoading } = useOperation({ packageKey: !isPackageFromDashboard ? originPackageKey : refPackageKey, - versionKey: !isPackageFromDashboard ? changedVersionKey : refComparisonSummary?.previousVersion, - enabled: !!operationAction && actionForOriginalOperation.includes(operationAction) && !!restGroupingPrefix, + versionKey: !isPackageFromDashboard ? originVersionKey : refComparisonSummary?.previousVersion, apiType: apiType as ApiType, - operationKey: operationAction === DiffAction.rename - ? `${getFullGroupForOperation(restGroupingPrefix, previousGroup!)}-${operationKeyForOriginOperation}` - : operationKeyForOriginOperation, + operationKey: operationKeyForOriginOperation, }) - const { data: changedOperation, isLoading: isChangedOperationLoading } = useOperation({ - packageKey: !isPackageFromDashboard ? originPackageKey : refPackageKey, + const { data: changedOperation, isInitialLoading: isChangedOperationLoading } = useOperation({ + packageKey: !isPackageFromDashboard ? changedPackageKey : refPackageKey, versionKey: !isPackageFromDashboard ? changedVersionKey : refComparisonSummary?.version, - enabled: !!operationAction && actionForChangedOperation.includes(operationAction) && !!restGroupingPrefix, apiType: apiType as ApiType, - operationKey: operationAction === DiffAction.rename - ? `${getFullGroupForOperation(restGroupingPrefix, group!)}-${operationKeyForChangedOperation}` - : operationKeyForChangedOperation, + operationKey:operationKeyForChangedOperation, }) + // process operations in the same way they are processed in api-processor for prefix groups comparison + // particularly, remove servers from the operation data + const processedOriginOperation = useMemo( + () => ( + !!originOperation && !!originOperation.data + ? { + ...originOperation, + data: createCopyWithPrefixGroupOperationsOnly(originOperation.data as RestOperationData, previousGroupPrefix) } + : originOperation + ), + [originOperation, previousGroupPrefix], + ) + + const processedChangedOperation = useMemo( + () => ( + !!changedOperation && !!changedOperation.data + ? { ...changedOperation, data: createCopyWithPrefixGroupOperationsOnly(changedOperation.data as RestOperationData, currentGroupPrefix) } + : changedOperation), + [changedOperation, currentGroupPrefix], + ) + const areChangesAndOperationsLoading = isOriginOperationLoading && isChangedOperationLoading const operationPairsGroupedByTags: OperationPairsGroupedByTag = useMemo(() => { const filteredChanges: OperationChanges[] = compareGroups.data?.filter( @@ -353,8 +371,8 @@ export const DifferentOperationGroupsComparisonPage: FC = memo(() => { }, [apiType, areChangesAndOperationsLoading, changedPackageKey, changedVersionKey, filters, firstOperationPair, group, isCurrentOperationGrouped, navigateToFirstGroupOperation, previousGroup, selectedDocumentSlug]) const comparedOperationsPair: OptionalOperationPair = { - previousOperation: originOperation, - currentOperation: changedOperation, + previousOperation: processedOriginOperation, + currentOperation: processedChangedOperation, isLoading: isOriginOperationLoading || isChangedOperationLoading, } @@ -395,8 +413,8 @@ export const DifferentOperationGroupsComparisonPage: FC = memo(() => { } body={ { const actionForOriginalOperation = ['remove', 'replace', 'rename'] const actionForChangedOperation = ['add', 'replace', 'rename'] +const httpMethods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'] -export const getFullGroupForOperation = (restGroupingPrefix: string | undefined, group: string): string => { - if (!restGroupingPrefix) return group - - return convertToSlug(restGroupingPrefix.replace(/{group}/g, group)) +export const getGroupPrefix = (groupPrefixTemplate: string | undefined, group: string | undefined): string => { + if (!groupPrefixTemplate || !group) { + return '' + } + return groupPrefixTemplate.replace(/{group}/g, group) } -const httpMethods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'] - diff --git a/packages/portal/src/routes/root/PortalPage/VersionPage/VersionApiQualitySubPage/VersionApiQualityCard.tsx b/packages/portal/src/routes/root/PortalPage/VersionPage/VersionApiQualitySubPage/VersionApiQualityCard.tsx index 947d53691..87f0c9b0b 100644 --- a/packages/portal/src/routes/root/PortalPage/VersionPage/VersionApiQualitySubPage/VersionApiQualityCard.tsx +++ b/packages/portal/src/routes/root/PortalPage/VersionPage/VersionApiQualitySubPage/VersionApiQualityCard.tsx @@ -1,7 +1,7 @@ import { useValidationDetailsByDocument } from '@apihub/api-hooks/ApiQuality/useValidationDetailsByDocument' import { ValidationRulesettLink } from '@apihub/components/ApiQuality/ValidatationRulesetLink' import type { DocumentValidationSummary } from '@apihub/entities/api-quality/package-version-validation-summary' -import { JSON_FILE_FORMAT, YAML_FILE_FORMAT } from '@apihub/entities/file-formats' +import { JSON_FILE_FORMAT, YAML_FILE_FORMAT } from '@netcracker/qubership-apihub-ui-shared/entities/file-formats' import { transformIssuesToMarkers } from '@apihub/utils/api-quality/issues' import { Box, Typography } from '@mui/material' import { BodyCard } from '@netcracker/qubership-apihub-ui-shared/components/BodyCard' @@ -15,7 +15,7 @@ import type { FC, ReactNode } from 'react' import { memo, useCallback, useMemo, useState } from 'react' import { useParams } from 'react-router' import { ClientValidationStatuses, useApiQualityValidationSummary } from '../ApiQualityValidationSummaryProvider' -import { usePublishedDocumentRaw } from '../usePublishedDocumentRaw' +import { usePublishedDocumentRaw } from '@netcracker/qubership-apihub-ui-shared/hooks/documents/usePublishedDocumentRaw' import type { OriginalDocumentFileFormat } from './types' import { useTransformedRawDocumentByFormat } from './utilities/hooks' import { ValidatedDocumentSelector } from './ValidatedDocumentSelector' diff --git a/packages/portal/src/routes/root/PortalPage/VersionPage/VersionApiQualitySubPage/types.ts b/packages/portal/src/routes/root/PortalPage/VersionPage/VersionApiQualitySubPage/types.ts index 41725a5f3..c336faaad 100644 --- a/packages/portal/src/routes/root/PortalPage/VersionPage/VersionApiQualitySubPage/types.ts +++ b/packages/portal/src/routes/root/PortalPage/VersionPage/VersionApiQualitySubPage/types.ts @@ -1,4 +1,4 @@ -import type { FileFormat, MD_FILE_FORMAT, UNKNOWN_FILE_FORMAT } from '@apihub/entities/file-formats' +import type { FileFormat, MD_FILE_FORMAT, UNKNOWN_FILE_FORMAT } from '@netcracker/qubership-apihub-ui-shared/entities/file-formats' type ProhibitedFileFormat = | typeof MD_FILE_FORMAT | typeof UNKNOWN_FILE_FORMAT diff --git a/packages/portal/src/routes/root/PortalPage/VersionPage/VersionApiQualitySubPage/utilities/transformers.ts b/packages/portal/src/routes/root/PortalPage/VersionPage/VersionApiQualitySubPage/utilities/transformers.ts index a3bef57ab..f670fb93c 100644 --- a/packages/portal/src/routes/root/PortalPage/VersionPage/VersionApiQualitySubPage/utilities/transformers.ts +++ b/packages/portal/src/routes/root/PortalPage/VersionPage/VersionApiQualitySubPage/utilities/transformers.ts @@ -1,4 +1,4 @@ -import { YAML_FILE_FORMAT, JSON_FILE_FORMAT } from '@apihub/entities/file-formats' +import { YAML_FILE_FORMAT, JSON_FILE_FORMAT } from '@netcracker/qubership-apihub-ui-shared/entities/file-formats' import type { SpecItemUri } from '@netcracker/qubership-apihub-ui-shared/utils/specifications' import { encodeKey } from '@netcracker/qubership-apihub-ui-shared/utils/specifications' import { safeParse } from '@stoplight/json' diff --git a/packages/portal/src/routes/root/PortalPage/VersionPage/VersionDocumentsSubPage/FormatViewer.tsx b/packages/portal/src/routes/root/PortalPage/VersionPage/VersionDocumentsSubPage/FormatViewer.tsx index bf520b6db..0bb8851b0 100644 --- a/packages/portal/src/routes/root/PortalPage/VersionPage/VersionDocumentsSubPage/FormatViewer.tsx +++ b/packages/portal/src/routes/root/PortalPage/VersionPage/VersionDocumentsSubPage/FormatViewer.tsx @@ -18,7 +18,6 @@ import type { FC } from 'react' import { memo } from 'react' import { useParams } from 'react-router-dom' import { Box } from '@mui/material' -import { usePublishedDocumentRaw } from '../usePublishedDocumentRaw' import { usePackageParamsWithRef } from '../../usePackageParamsWithRef' import { UnsupportedView } from './UnsupportedView' import { JsonSchemaViewer } from '@netcracker/qubership-apihub-ui-shared/components/JsonSchemaViewer' @@ -27,6 +26,7 @@ import { JSON_FILE_FORMAT, MD_FILE_FORMAT, YAML_FILE_FORMAT } from '@netcracker/ import { LoadingIndicator } from '@netcracker/qubership-apihub-ui-shared/components/LoadingIndicator' import { MarkdownViewer } from '@netcracker/qubership-apihub-ui-shared/components/SpecificationDialog/MarkdownViewer' import { isFastJsonSchema, toJsonSchema } from '@netcracker/qubership-apihub-ui-shared/utils/specifications' +import { usePublishedDocumentRaw } from '@netcracker/qubership-apihub-ui-shared/hooks/documents/usePublishedDocumentRaw' export type FormatViewerProps = { format: FileFormat diff --git a/packages/portal/src/routes/root/PortalPage/VersionPage/useDocuments.tsx b/packages/portal/src/routes/root/PortalPage/VersionPage/useDocuments.tsx index 3e968d925..cf4cb99f6 100644 --- a/packages/portal/src/routes/root/PortalPage/VersionPage/useDocuments.tsx +++ b/packages/portal/src/routes/root/PortalPage/VersionPage/useDocuments.tsx @@ -16,15 +16,13 @@ import { useQuery } from '@tanstack/react-query' import { useVersionWithRevision } from '../../useVersionWithRevision' -import { generatePath } from 'react-router-dom' -import type { Documents, DocumentsDto } from '@apihub/entities/documents' +import type { Documents } from '@apihub/entities/documents' import { toDocuments } from '@apihub/entities/documents' import type { IsLoading } from '@netcracker/qubership-apihub-ui-shared/utils/aliases' import type { Key } from '@netcracker/qubership-apihub-ui-shared/entities/keys' -import { optionalSearchParams } from '@netcracker/qubership-apihub-ui-shared/utils/search-params' -import { portalRequestJson } from '@apihub/utils/requests' -import { getPackageRedirectDetails } from '@netcracker/qubership-apihub-ui-shared/utils/redirects' import type { ApiType } from '@netcracker/qubership-apihub-ui-shared/entities/api-types' +import type { DocumentsDto } from '@netcracker/qubership-apihub-ui-shared/entities/documents' +import { getDocuments } from '@netcracker/qubership-apihub-ui-shared/utils/packages-builder' const DOCUMENTS_QUERY_KEY = 'documents-query-key' @@ -60,24 +58,3 @@ export function useDocuments(options: Partial<{ isInitialLoading: isInitialLoading || isVersionInitialLoading, } } - -async function getDocuments( - packageKey: Key, - versionKey: Key, - apiType?: ApiType, - signal?: AbortSignal, -): Promise { - const packageId = encodeURIComponent(packageKey) - const versionId = encodeURIComponent(versionKey) - - const queryParams = optionalSearchParams({ apiType: { value: apiType } }) - const pathPattern = '/packages/:packageId/versions/:versionId/documents' - return await portalRequestJson( - `${generatePath(pathPattern, { packageId, versionId })}?${queryParams}`, - { method: 'get' }, - { - customRedirectHandler: (response) => getPackageRedirectDetails(response, pathPattern), - }, - signal, - ) -} diff --git a/packages/portal/src/routes/root/PortalPage/VersionPage/useOperations.ts b/packages/portal/src/routes/root/PortalPage/VersionPage/useOperations.ts index ace22fcaf..9ae7b8e53 100644 --- a/packages/portal/src/routes/root/PortalPage/VersionPage/useOperations.ts +++ b/packages/portal/src/routes/root/PortalPage/VersionPage/useOperations.ts @@ -67,7 +67,6 @@ export function usePagedOperations(options?: Partial<{ versionKey: Key refPackageKey: PackageKey deprecated: DeprecatedQueryStatus - hashList: string[] ids: string[] includeData: boolean kind: ApiKind @@ -90,7 +89,6 @@ export function usePagedOperations(options?: Partial<{ tag, ids, includeData, - hashList, label, deprecated, kind, @@ -122,7 +120,6 @@ export function usePagedOperations(options?: Partial<{ versionKey: fullVersion!, refPackageKey: refPackageKey, deprecated: deprecated, - hashList: hashList, ids: ids, includeData: includeData, kind: kind, @@ -161,7 +158,6 @@ export function useOperations(options?: Partial<{ packageKey: Key versionKey: Key deprecated: DeprecatedQueryStatus - hashList: string[] ids: string[] includeData: boolean kind: ApiKind @@ -186,7 +182,6 @@ export function useOperations(options?: Partial<{ tag, ids, includeData, - hashList, label, deprecated, kind, @@ -218,7 +213,6 @@ export function useOperations(options?: Partial<{ packageKey: packageKey!, versionKey: fullVersion!, deprecated: deprecated, - hashList: hashList, ids: ids, includeData: includeData, kind: kind, @@ -260,7 +254,6 @@ export async function getOperations( versionKey: Key refPackageKey?: PackageKey deprecated?: DeprecatedQueryStatus - hashList?: string[] ids?: string[] includeData?: boolean kind?: ApiKind @@ -283,7 +276,6 @@ export async function getOperations( versionKey, refPackageKey, deprecated = ALL_DEPRECATED_QUERY_STATUS, - hashList, ids, includeData = false, kind = ALL_API_KIND, @@ -307,7 +299,6 @@ export async function getOperations( textFilter: { value: textFilter }, documentSlug: { value: documentId }, deprecated: { value: deprecated.toString() }, - hashList: { value: hashList ? encodeURIComponent(JSON.stringify(hashList)).toString() : '' }, ids: { value: ids ? encodeURIComponent(ids.join()).toString() : '' }, includeData: { value: includeData.toString() }, kind: { value: kind }, diff --git a/packages/portal/src/routes/root/PortalPage/package-version-builder-worker.ts b/packages/portal/src/routes/root/PortalPage/package-version-builder-worker.ts index 256e39961..46d1924ab 100644 --- a/packages/portal/src/routes/root/PortalPage/package-version-builder-worker.ts +++ b/packages/portal/src/routes/root/PortalPage/package-version-builder-worker.ts @@ -13,11 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import type { FileId, FileSourceMap, VersionsComparison } from '@netcracker/qubership-apihub-api-processor' + +import type { BuilderResolvers, FileId, FileSourceMap, VersionsComparison } from '@netcracker/qubership-apihub-api-processor' import { BUILD_TYPE, PackageVersionBuilder, VERSION_STATUS } from '@netcracker/qubership-apihub-api-processor' import { packageVersionResolver, + rawDocumentResolver, versionDeprecatedResolver, + versionDocumentsResolver, versionOperationsResolver, versionReferencesResolver, } from '@netcracker/qubership-apihub-ui-shared/utils/builder-resolvers' @@ -60,6 +63,17 @@ export type PackageVersionBuilderWorker = { init: (lastIdentityProviderId: string | null) => Promise } +async function createCommonResolvers(): Promise { + return { + versionResolver: await packageVersionResolver(), + versionReferencesResolver: await versionReferencesResolver(), + versionOperationsResolver: await versionOperationsResolver(), + versionDeprecatedResolver: await versionDeprecatedResolver(), + versionDocumentsResolver: await versionDocumentsResolver(), + rawDocumentResolver: await rawDocumentResolver(), + } +} + const worker: PackageVersionBuilderWorker = { init: async (lastIdentityProviderId) => { if (isInWebWorker(self)) { @@ -68,13 +82,6 @@ const worker: PackageVersionBuilderWorker = { } }, buildChangelogPackage: async ({ packageKey, versionKey, previousPackageKey, previousVersionKey }) => { - const builderResolvers = { - fileResolver: async () => null, - versionResolver: await packageVersionResolver(), - versionReferencesResolver: await versionReferencesResolver(), - versionOperationsResolver: await versionOperationsResolver(), - versionDeprecatedResolver: await versionDeprecatedResolver(), - } const builder = new PackageVersionBuilder( { packageId: packageKey, @@ -85,7 +92,7 @@ const worker: PackageVersionBuilderWorker = { buildType: BUILD_TYPE.CHANGELOG, }, { - resolvers: builderResolvers, + resolvers: await createCommonResolvers(), }, ) @@ -94,13 +101,6 @@ const worker: PackageVersionBuilderWorker = { return [builder.buildResult.comparisons, await builder.createVersionPackage({ type: 'blob' })] }, buildGroupChangelogPackage: async ({ packageKey, versionKey, currentGroup, previousGroup }) => { - const builderResolvers = { - fileResolver: async () => null, - versionResolver: await packageVersionResolver(), - versionReferencesResolver: await versionReferencesResolver(), - versionOperationsResolver: await versionOperationsResolver(), - versionDeprecatedResolver: await versionDeprecatedResolver(), - } const builder = new PackageVersionBuilder( { packageId: packageKey, @@ -111,7 +111,7 @@ const worker: PackageVersionBuilderWorker = { buildType: BUILD_TYPE.PREFIX_GROUPS_CHANGELOG, }, { - resolvers: builderResolvers, + resolvers: await createCommonResolvers(), }, ) @@ -133,10 +133,7 @@ const worker: PackageVersionBuilderWorker = { const builder = new PackageVersionBuilder(buildConfig, { resolvers: { fileResolver: async (fileId: FileId) => fileSources?.[fileId] ?? null, - versionResolver: await packageVersionResolver(), - versionReferencesResolver: await versionReferencesResolver(), - versionOperationsResolver: await versionOperationsResolver(), - versionDeprecatedResolver: await versionDeprecatedResolver(), + ...(await createCommonResolvers()), }, }, fileSources) diff --git a/packages/portal/src/utils/operations.ts b/packages/portal/src/utils/operations.ts index c2aa3f928..f00753062 100644 --- a/packages/portal/src/utils/operations.ts +++ b/packages/portal/src/utils/operations.ts @@ -23,7 +23,7 @@ import type { } from '@netcracker/qubership-apihub-ui-shared/entities/operations' import { DEFAULT_TAG, EMPTY_TAG } from '@netcracker/qubership-apihub-ui-shared/entities/operations' import { isEmpty } from '@netcracker/qubership-apihub-ui-shared/utils/arrays' -import { matchPaths, OPEN_API_PROPERTY_PATHS, PREDICATE_UNCLOSED_END } from '@netcracker/qubership-apihub-api-unifier' +import { matchPaths, OPEN_API_PROPERTY_PATHS, PREDICATE_NOT_OAS_EXTENSION } from '@netcracker/qubership-apihub-api-unifier' import { DiffAction } from '@netcracker/qubership-apihub-api-diff' export function groupOperationsByTags( @@ -70,8 +70,8 @@ export function groupOperationPairsByTags( return operationPairsGroupedByTag } -export const getActionForOperation = (change: OperationChanges, actionType: string): string => { - return !isFullyAddedOrRemovedOperationChange(change) +export const getActionForRestOperation = (change: OperationChanges, actionType: string): string => { + return !isFullyAddedOrRemovedRestOperationChange(change) ? actionType : change?.diffs?.[0]?.action ?? '' } @@ -87,28 +87,28 @@ export function handleOperationTags(tags: readonly string[] | undefined): Set + packages: PackagesRefs +}> + +export type DocumentDto = Readonly<{ + fileId: FileKey + slug: Key + filename: string + title: string + type: SpecType + format: FileFormat + version?: string + labels?: Labels + description?: string + info?: Readonly + externalDocs?: Readonly + operations?: ReadonlyArray + packages?: PackagesRefs // For operations + packageRef?: string // For dashboards +}> + +export type DocumentInfo = { + contact?: Readonly + license?: Readonly + termsOfService?: string +} + +export type DocLink = { + name?: string + url?: string +} + +export type ExternalDocsLink = Omit & { description?: string } + +export type Labels = string[] diff --git a/packages/portal/src/entities/file-formats.ts b/packages/shared/src/entities/file-formats.ts similarity index 100% rename from packages/portal/src/entities/file-formats.ts rename to packages/shared/src/entities/file-formats.ts diff --git a/packages/shared/src/entities/operations.ts b/packages/shared/src/entities/operations.ts index aa1e6ccc9..c59d973ec 100644 --- a/packages/shared/src/entities/operations.ts +++ b/packages/shared/src/entities/operations.ts @@ -24,7 +24,7 @@ import type { FetchNextPageOptions, InfiniteQueryObserverResult } from '@tanstac import type { Key, VersionKey } from './keys' import type { ApiType } from './api-types' import { API_TYPE_REST } from './api-types' -import type { DeprecateItem } from '@netcracker/qubership-apihub-api-processor' +import type { DeprecateItem, ReferencedPackageKind } from '@netcracker/qubership-apihub-api-processor' import type { IsLoading } from '../utils/aliases' export const DEFAULT_API_TYPE: ApiType = API_TYPE_REST @@ -37,13 +37,13 @@ export type OperationDto = RestOperationDto | GraphQlOperationDto export type OperationMetadataDto = Readonly<{ operationId: Key + documentId: Key title: string apiType: ApiType apiKind: ApiKind apiAudience: ApiAudience data?: object packageRef?: string - dataHash: string deprecated?: boolean tags?: Readonly customTags?: CustomTags @@ -69,7 +69,7 @@ export type OperationsWithDeprecationsDto = Readonly<{ operations: ReadonlyArray packages: PackagesRefs }> -export type OperationWithDeprecationsDto = Omit & Readonly<{ +export type OperationWithDeprecationsDto = Omit & Readonly<{ deprecatedCount?: string deprecatedInfo?: object deprecatedItems?: DeprecatedItemsDto @@ -114,7 +114,6 @@ export interface Operation { readonly title: string readonly apiKind: ApiKind readonly apiAudience: ApiAudience - readonly dataHash?: string readonly packageRef?: PackageRef readonly tags?: Readonly readonly customTags?: CustomTags @@ -140,7 +139,7 @@ export type PackagesRefs = { } export type PackageRefDto = { refId: string - kind?: string + kind?: ReferencedPackageKind name?: string version: string notLatestRevision?: boolean diff --git a/packages/shared/src/entities/version-changelog.ts b/packages/shared/src/entities/version-changelog.ts index 877517587..2d50f68d7 100644 --- a/packages/shared/src/entities/version-changelog.ts +++ b/packages/shared/src/entities/version-changelog.ts @@ -57,7 +57,6 @@ export interface OperationInfoDto { readonly apiKind: ApiKind readonly apiAudience: ApiAudience readonly tags: string[] - readonly dataHash: string readonly packageRef?: string } @@ -100,7 +99,6 @@ export interface OperationInfo { readonly title: string readonly apiKind: ApiKind readonly apiAudience: ApiAudience - readonly dataHash: string readonly packageRef?: PackageRef } @@ -166,7 +164,7 @@ export const toOperationChange = ( packageRef: includePackageRefs ? toPackageRef(dto.previousOperation.packageRef, packagesRefs) : undefined, } : undefined, - action: calculateAction(dto.currentOperation?.dataHash, dto.previousOperation?.dataHash), + action: calculateAction(dto.currentOperation?.operationId, dto.previousOperation?.operationId), } } diff --git a/packages/portal/src/routes/root/PortalPage/VersionPage/usePublishedDocumentRaw.ts b/packages/shared/src/hooks/documents/usePublishedDocumentRaw.ts similarity index 78% rename from packages/portal/src/routes/root/PortalPage/VersionPage/usePublishedDocumentRaw.ts rename to packages/shared/src/hooks/documents/usePublishedDocumentRaw.ts index 20d078ac3..a81b6e148 100644 --- a/packages/portal/src/routes/root/PortalPage/VersionPage/usePublishedDocumentRaw.ts +++ b/packages/shared/src/hooks/documents/usePublishedDocumentRaw.ts @@ -16,12 +16,13 @@ import { useQuery } from '@tanstack/react-query' import { generatePath, useParams } from 'react-router-dom' -import { portalRequestText } from '@apihub/utils/requests' -import type { Key } from '@netcracker/qubership-apihub-ui-shared/entities/keys' -import type { FileContent } from '@apihub/entities/project-files' -import type { IsLoading } from '@netcracker/qubership-apihub-ui-shared/utils/aliases' -import { toFormattedJsonString } from '@netcracker/qubership-apihub-ui-shared/utils/strings' -import { getPackageRedirectDetails } from '@netcracker/qubership-apihub-ui-shared/utils/redirects' +import type { IsLoading } from '../../utils/aliases' +import { toFormattedJsonString } from '../../utils/strings' +import { getPackageRedirectDetails } from '../../utils/redirects' +import type { Key } from '../../entities/keys' +import { API_V2, requestBlob } from '../../utils/requests' + +export type FileContent = string const PUBLISHED_DOCUMENT_RAW_QUERY_KEY = 'published-document-raw-query-key' @@ -54,17 +55,27 @@ export async function getPublishedDocumentRaw( versionKey: Key, slug: Key, ): Promise { + const response = await getPublishedDocumentRawBlob(packageKey, versionKey, slug) + return response.text() +} + +export async function getPublishedDocumentRawBlob( + packageKey: Key, + versionKey: Key, + slug: Key, +): Promise { const packageId = encodeURIComponent(packageKey) const versionId = encodeURIComponent(versionKey) const fileId = encodeURIComponent(slug) const pathPattern = '/packages/:packageId/versions/:versionId/files/:fileId/raw' - return await portalRequestText( + return await requestBlob( generatePath(pathPattern, { packageId, versionId, fileId }), { method: 'get', }, { + basePath: API_V2, customRedirectHandler: (response) => getPackageRedirectDetails(response, pathPattern), }, ) diff --git a/packages/shared/src/stories/samples/operations-samples.ts b/packages/shared/src/stories/samples/operations-samples.ts index ebc7cd2d7..8ab661a54 100644 --- a/packages/shared/src/stories/samples/operations-samples.ts +++ b/packages/shared/src/stories/samples/operations-samples.ts @@ -33,7 +33,6 @@ export const restDeprecatedOperations: OperationsWithDeprecations = [ }, 'apiAudience': 'unknown', 'title': '[Deprecated] Get list of changes in branch', - 'dataHash': 'bc2a54ac13185c9adb6c494dc691793fd0fb1322', 'deprecated': true, 'apiKind': 'bwc', 'apiType': 'rest', @@ -61,7 +60,6 @@ export const restDeprecatedOperations: OperationsWithDeprecations = [ }, 'apiAudience': 'unknown', 'title': 'Get project branch files by commit Id', - 'dataHash': 'f8fc078bf2730287acb648c7045139c40f2a045b', 'deprecated': true, 'apiKind': 'bwc', 'apiType': 'rest', @@ -90,7 +88,6 @@ export const restDeprecatedOperations: OperationsWithDeprecations = [ }, 'apiAudience': 'unknown', 'title': 'Publish project branch', - 'dataHash': '3cfe640496a9e342bfd857938481c1c09a212735', 'deprecated': true, 'apiKind': 'bwc', 'apiType': 'rest', @@ -119,7 +116,6 @@ export const restDeprecatedOperations: OperationsWithDeprecations = [ }, 'apiAudience': 'unknown', 'title': '[Draft] Get project version dependents', - 'dataHash': '43d0187d1ed8d7654e3deb35afd243dcd4638f72', 'deprecated': true, 'apiKind': 'bwc', 'apiType': 'rest', diff --git a/packages/shared/src/utils/builder-resolvers.ts b/packages/shared/src/utils/builder-resolvers.ts index 80078716c..47941f2e6 100644 --- a/packages/shared/src/utils/builder-resolvers.ts +++ b/packages/shared/src/utils/builder-resolvers.ts @@ -18,15 +18,20 @@ import { fetchDeprecatedItems, fetchOperations, getPackageVersionContent, + getResolvedVersionDocuments, getVersionReferences, toVersionOperation, } from './packages-builder' import type { + RawDocumentResolver, + ResolvedVersionDocuments, VersionDeprecatedResolver, + VersionDocumentsResolver, VersionOperationsResolver, VersionReferencesResolver, VersionResolver, } from '@netcracker/qubership-apihub-api-processor' +import { getPublishedDocumentRawBlob } from '../hooks/documents/usePublishedDocumentRaw' export async function packageVersionResolver(): Promise { return async (packageId, version, includeOperations = false) => { @@ -90,3 +95,44 @@ export async function versionDeprecatedResolver(): Promise { + return async (version, packageId, apiType) => { + const EMPTY_DOCUMENTS_DTO = { documents: [], packages: {} } + const limit = 100 + const result: ResolvedVersionDocuments = { documents: [], packages: {} } + let page = 0 + let documentsCount = 0 + + try { + while (page === 0 || documentsCount === limit) { + const { documents, packages } = await getResolvedVersionDocuments(packageId, version, apiType) ?? EMPTY_DOCUMENTS_DTO + result.documents = [...result.documents, ...documents] + result.packages = { ...result.packages, ...packages } + + page += 1 + documentsCount = documents.length + } + return result + } catch (error) { + console.error(error) + return EMPTY_DOCUMENTS_DTO + } + } +} + +export async function rawDocumentResolver(): Promise { + return async (version, packageId, slug) => { + try { + const response = await getPublishedDocumentRawBlob(packageId, version, slug) + + const data = await response.blob() + const filename = response.headers.get('content-disposition')!.split('filename=')[1].slice(1, -1) + const contentType = response.headers.get('content-type')! + return new File([data], filename, { type: contentType }) + } catch (error) { + console.error(error) + return null + } + } +} diff --git a/packages/shared/src/utils/packages-builder.ts b/packages/shared/src/utils/packages-builder.ts index 3ab721d3a..3b79bad18 100644 --- a/packages/shared/src/utils/packages-builder.ts +++ b/packages/shared/src/utils/packages-builder.ts @@ -22,6 +22,7 @@ import type { ResolvedGroupDocuments, ResolvedOperation, ResolvedReferences, + ResolvedVersionDocuments, } from '@netcracker/qubership-apihub-api-processor' import { generatePath } from 'react-router-dom' import type { ApiType } from '../entities/api-types' @@ -32,6 +33,7 @@ import { getPackageRedirectDetails } from './redirects' import { API_V1, API_V2, API_V3, requestBlob, requestJson, requestVoid } from './requests' import { optionalSearchParams } from './search-params' import type { Key } from './types' +import type { DocumentsDto } from '../entities/documents' export async function getPackageVersionContent( packageKey: Key, @@ -276,8 +278,8 @@ export function toVersionOperation(value: OperationDto): ResolvedOperation { } return { operationId: value.operationId, + documentId: value.documentId, data: value.data!, - dataHash: value.dataHash, apiKind: value.apiKind as ApiKind, apiAudience: value.apiAudience as ApiAudience, deprecated: value.deprecated ?? false, @@ -317,3 +319,48 @@ export async function fetchExportTemplate( return [await data.text(), getFilename()] } + +export async function getDocuments( + packageKey: Key, + versionKey: Key, + apiType?: ApiType, + signal?: AbortSignal, +): Promise { + const packageId = encodeURIComponent(packageKey) + const versionId = encodeURIComponent(versionKey) + + const queryParams = optionalSearchParams({ apiType: { value: apiType } }) + const pathPattern = '/packages/:packageId/versions/:versionId/documents' + return await requestJson( + `${generatePath(pathPattern, { packageId, versionId })}?${queryParams}`, + { method: 'get' }, + { + basePath: API_V2, + customRedirectHandler: (response) => getPackageRedirectDetails(response, pathPattern), + }, + signal, + ) +} + +// todo fix api-processor and ui types incompatibility. Only one getDocuments should be left +export async function getResolvedVersionDocuments( + packageKey: Key, + versionKey: Key, + apiType?: ApiType, + signal?: AbortSignal, +): Promise { + const packageId = encodeURIComponent(packageKey) + const versionId = encodeURIComponent(versionKey) + + const queryParams = optionalSearchParams({ apiType: { value: apiType } }) + const pathPattern = '/packages/:packageId/versions/:versionId/documents' + return await requestJson( + `${generatePath(pathPattern, { packageId, versionId })}?${queryParams}`, + { method: 'get' }, + { + basePath: API_V2, + customRedirectHandler: (response) => getPackageRedirectDetails(response, pathPattern), + }, + signal, + ) +}