diff --git a/apify-api/openapi/openapi.yaml b/apify-api/openapi/openapi.yaml index ce45b17220..3a12794776 100644 --- a/apify-api/openapi/openapi.yaml +++ b/apify-api/openapi/openapi.yaml @@ -45,8 +45,7 @@ info: To use your token in a request, either: - - Add the token to your request's `Authorization` header as `Bearer - `. + - Add the token to your request's `Authorization` header as `Bearer `. E.g., `Authorization: Bearer xxxxxxx`. [More info](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization). (Recommended). diff --git a/apify-api/openapi/paths/actors/acts@{actorId}@run-sync.yaml b/apify-api/openapi/paths/actors/acts@{actorId}@run-sync.yaml index 47fb777f6b..946f402356 100644 --- a/apify-api/openapi/paths/actors/acts@{actorId}@run-sync.yaml +++ b/apify-api/openapi/paths/actors/acts@{actorId}@run-sync.yaml @@ -5,29 +5,22 @@ post: description: | Runs a specific Actor and returns its output. - The POST payload including its `Content-Type` header is passed as `INPUT` to the Actor (usually application/json). - The HTTP response contains Actors `OUTPUT` record from its default key-value store. - The Actor is started with the default options; you can override them using various URL query parameters. - If the Actor run exceeds 300 seconds, the HTTP response will have status 408 (Request Timeout). - Beware that it might be impossible to maintain an idle HTTP connection for a long period of time, due to client timeout or network conditions. Make sure your HTTP client is configured to have a long enough connection timeout. - If the connection breaks, you will not receive any information about the run and its status. - To run the Actor asynchronously, use the [Run Actor](#/reference/actors/run-collection/run-actor) API endpoint instead. operationId: act_runSync_post @@ -42,9 +35,9 @@ post: example: janedoe~my-actor - name: outputRecordKey in: query - description: |- + description: | Key of the record from run's default key-value store to be returned - in the response. By default, it is `OUTPUT`. + in the response. By default, it is `OUTPUT`. style: form explode: true schema: @@ -103,12 +96,9 @@ post: description: | Specifies optional webhooks associated with the Actor run, which can be used to receive a notification - e.g. when the Actor finished or failed. The value is a Base64-encoded JSON array of objects defining the webhooks. For more information, see - - [Webhooks - documentation](https://docs.apify.com/platform/integrations/webhooks). + [Webhooks documentation](https://docs.apify.com/platform/integrations/webhooks). style: form explode: true schema: @@ -143,9 +133,8 @@ post: example: error: type: run-failed - message: >- - Actor run did not succeed (run ID: 55uatRrZib4xbZs, status: - FAILED) + message: | + Actor run did not succeed (run ID: 55uatRrZib4xbZs, status: FAILED) '408': description: '' headers: {} @@ -168,23 +157,17 @@ get: summary: Without input description: | Runs a specific Actor and returns its output. - The run must finish in 300 seconds otherwise the API endpoint returns a timeout error. - The Actor is not passed any input. - Beware that it might be impossible to maintain an idle HTTP connection for a long period of time, - due to client timeout or network conditions. Make sure your HTTP client is configured to have a long enough connection timeout. - If the connection breaks, you will not receive any information about the run and its status. - To run the Actor asynchronously, use the [Run Actor](#/reference/actors/run-collection/run-actor) API endpoint instead. operationId: act_runSync_get @@ -199,9 +182,9 @@ get: example: janedoe~my-actor - name: outputRecordKey in: query - description: |- + description: | Key of the record from run's default key-value store to be returned - in the response. By default, it is `OUTPUT`. + in the response. By default, it is `OUTPUT`. style: form explode: true schema: @@ -260,12 +243,9 @@ get: description: | Specifies optional webhooks associated with the Actor run, which can be used to receive a notification - e.g. when the Actor finished or failed. The value is a Base64-encoded JSON array of objects defining the webhooks. For more information, see - - [Webhooks - documentation](https://docs.apify.com/platform/integrations/webhooks). + [Webhooks documentation](https://docs.apify.com/platform/integrations/webhooks). style: form explode: true schema: diff --git a/apify-docs-theme/src/theme/NotFound.jsx b/apify-docs-theme/src/theme/NotFound.jsx index 7139852582..0ab64cb339 100644 --- a/apify-docs-theme/src/theme/NotFound.jsx +++ b/apify-docs-theme/src/theme/NotFound.jsx @@ -1,6 +1,6 @@ -import React from 'react'; import { PageMetadata } from '@docusaurus/theme-common'; import Layout from '@theme/Layout'; +import React from 'react'; export default function NotFound() { return ( diff --git a/apify-docs-theme/src/theme/SearchPage/index.js b/apify-docs-theme/src/theme/SearchPage/index.js index fa97eff27a..9e0c4d05d5 100644 --- a/apify-docs-theme/src/theme/SearchPage/index.js +++ b/apify-docs-theme/src/theme/SearchPage/index.js @@ -17,6 +17,7 @@ import { } from '@docusaurus/theme-common/internal'; import Translate, { translate } from '@docusaurus/Translate'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import { useLocation } from '@docusaurus/router'; import { useAlgoliaThemeConfig, useSearchResultUrlProcessor, @@ -120,6 +121,8 @@ function SearchPageContent() { const documentsFoundPlural = useDocumentsFoundPlural(); const docsSearchVersionsHelpers = useDocsSearchVersionsHelpers(); const [searchQuery, setSearchQuery] = useSearchQueryString(); + const location = useLocation(); + const notFound = new URLSearchParams(location.search).get('not-found'); const initialSearchResultState = { items: [], query: null, @@ -302,7 +305,14 @@ function SearchPageContent() {
-

{getTitle()}

+ {notFound ? ( + <> +

Page Not Found

+

{getTitle()}

+ + ) : ( +

{getTitle()}

+ )}
e.preventDefault()}>
div > .menu__link { +aside li.section-header > div > .menu__link, +aside li.section-header > ul > li > div > .menu__link { text-transform: uppercase; opacity: 0.8; font-size: 16px; @@ -998,6 +999,10 @@ aside li.section-header > div > .menu__link { margin: 0; } +aside li.section-header > div > .menu__link { + font-size: 20px; +} + aside li.section-header.menu__list-item { margin-top: 15px; margin-bottom: 5px; @@ -1011,6 +1016,11 @@ aside li.section-header > .menu__list { padding-left: 0; } +.theme-doc-sidebar-menu li.section-header > ul > li .menu__list-item-collapsible:hover, +.theme-doc-sidebar-menu li.section-header > ul > li .menu__list-item-collapsible--active { + background: inherit !important; +} + .beta-chip { display: inline-block; border: 1px solid #ccc; diff --git a/apify-docs-theme/static/js/custom.js b/apify-docs-theme/static/js/custom.js index 0a51cc0d57..7632c29c65 100644 --- a/apify-docs-theme/static/js/custom.js +++ b/apify-docs-theme/static/js/custom.js @@ -63,7 +63,7 @@ function scrollSidebarItemIntoView() { // handles automatic scrolling of the API reference sidebar (openapi-docs) function scrollOpenApiSidebarItemIntoView() { - const $li = document.querySelector(`li > a.menu__link--active[href]`); + const $li = document.querySelector(`ul.theme-doc-sidebar-menu a.menu__link--active[href]`); if (!$li) { return; @@ -76,27 +76,31 @@ function scrollOpenApiSidebarItemIntoView() { } function redirectOpenApiDocs() { - const { hash, pathname } = new URL(window.location.href); + const { hash, pathname, origin } = new URL(window.location.href); // TODO change to '/api/v2' if (pathname.replace(/\/$/, '') !== '/api/v2-new') { return; } - if (hash.startsWith('#/reference/')) { - const sidebarItems = document.querySelectorAll('[data-altids]'); + const sidebarItems = document.querySelectorAll('[data-altids]'); + + if (hash.startsWith('#/reference/') || hash.startsWith('#tag/')) { + let matched = false; for (const item of sidebarItems) { const ids = item.getAttribute('data-altids').split(','); + if (ids.find((variant) => variant === hash)) { + matched = true; item.click(); + setTimeout(() => scrollOpenApiSidebarItemIntoView(), 200); } } - } - if (hash.startsWith('#tag/')) { - const id = hash.substring('#tag/'.length); - console.log('redirect', { id, hash }); + if (!matched) { + window.location.href = `${origin}/search?q=${hash.slice(1)}¬-found=1`; + } } } @@ -121,7 +125,7 @@ window.addEventListener('load', () => { setTimeout(() => scrollSidebarItemIntoView(), 1000); // docusaurus-openapi-docs plugin: scroll sidebar into viewport, no need for a large timeout here - setTimeout(() => scrollOpenApiSidebarItemIntoView(), 100); + setTimeout(() => scrollOpenApiSidebarItemIntoView(), 200); }); window.addEventListener('popstate', () => { diff --git a/docusaurus.config.js b/docusaurus.config.js index 7051e5b417..1ad9457188 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -1,6 +1,7 @@ const { join } = require('node:path'); const clsx = require('clsx'); +const { createApiPageMD } = require('docusaurus-plugin-openapi-docs/lib/markdown'); const { config } = require('./apify-docs-theme'); const { collectSlugs } = require('./tools/utils/collectSlugs'); @@ -195,15 +196,28 @@ module.exports = { v2: { specPath: 'apify-api.yaml', outputDir: './sources/api', + markdownGenerators: { + createApiPageMD: (pageData) => { + let md = createApiPageMD(pageData); + + // HTML comments are wrongly escaped, we need to undo that + if (md.includes('<!--')) { + md = md.replace('<!--', ''); + } + + return md; + }, + }, sidebarOptions: { - groupPathsBy: 'tag', + groupPathsBy: 'tagGroup', categoryLinkSource: 'tag', sidebarCollapsed: false, sidebarCollapsible: false, sidebarGenerators: { createDocItem: (item, context) => { const legacyUrls = item.api?.['x-legacy-doc-urls'] ?? []; - const altIds = legacyUrls.map((url) => { + const altids = legacyUrls.map((url) => { const { hash } = new URL(url); return hash; }); @@ -223,7 +237,7 @@ module.exports = { type: 'doc', id: context.basePath === '' ? `${id}` : `${context.basePath}/${id}`, label: sidebarLabel ?? title ?? id, - customProps: { altIds }, + customProps: { altids }, className, }; }, diff --git a/patches/docusaurus-plugin-openapi-docs+0.0.0-961.patch b/patches/docusaurus-plugin-openapi-docs+0.0.0-961.patch new file mode 100644 index 0000000000..8066ca25b3 --- /dev/null +++ b/patches/docusaurus-plugin-openapi-docs+0.0.0-961.patch @@ -0,0 +1,36 @@ +diff --git a/node_modules/docusaurus-plugin-openapi-docs/lib/sidebars/index.js b/node_modules/docusaurus-plugin-openapi-docs/lib/sidebars/index.js +index 5802904..576c3ed 100644 +--- a/node_modules/docusaurus-plugin-openapi-docs/lib/sidebars/index.js ++++ b/node_modules/docusaurus-plugin-openapi-docs/lib/sidebars/index.js +@@ -139,9 +139,16 @@ function groupByTags(items, sidebarOptions, options, tags, docPath) { + } + const taggedApiItems = apiItems.filter((item) => { var _a; return !!((_a = item.api.tags) === null || _a === void 0 ? void 0 : _a.includes(tag)); }); + const taggedSchemaItems = schemaItems.filter((item) => { var _a; return !!((_a = item.schema["x-tags"]) === null || _a === void 0 ? void 0 : _a.includes(tag)); }); ++ const altids = []; ++ ++ if (tagObject?.['x-legacy-doc-urls']) { ++ altids.push(...tagObject['x-legacy-doc-urls']); ++ } ++ + return { + type: "category", + label: (_a = tagObject === null || tagObject === void 0 ? void 0 : tagObject["x-displayName"]) !== null && _a !== void 0 ? _a : tag, ++ customProps: { altids }, + link: linkConfig, + collapsible: sidebarCollapsible, + collapsed: sidebarCollapsed, +@@ -201,11 +208,12 @@ function generateSidebarSlice(sidebarOptions, options, api, tags, docPath, tagGr + filteredTags.push(tag); + } + }); ++ const { sidebarCollapsed, sidebarCollapsible } = sidebarOptions; + const groupCategory = { + type: "category", + label: tagGroup.name, +- collapsible: true, +- collapsed: true, ++ collapsible: sidebarCollapsible, ++ collapsed: sidebarCollapsed, + items: groupByTags(api, sidebarOptions, options, [filteredTags], docPath), + }; + if (options.showSchemas) { diff --git a/sources/api/sidebars.js b/sources/api/sidebars.js index 620938b987..72e4ece330 100644 --- a/sources/api/sidebars.js +++ b/sources/api/sidebars.js @@ -1,3 +1,13 @@ +// eslint-disable-next-line global-require +const items = require('./sidebar.ts'); + +for (const item of items) { + // this is wrongly rendered in each category (openapi group tag) + if (item.items[0].id === 'apify-api') { + item.items.shift(); + } +} + module.exports = { api: [ { @@ -5,8 +15,8 @@ module.exports = { label: 'Apify API', collapsible: false, className: 'section-header', - // eslint-disable-next-line global-require - items: require('./sidebar.ts'), + link: { type: 'doc', id: 'apify-api' }, + items, }, ], }; diff --git a/src/theme/DocSidebarItem/Category/index.js b/src/theme/DocSidebarItem/Category/index.js new file mode 100644 index 0000000000..025ab30beb --- /dev/null +++ b/src/theme/DocSidebarItem/Category/index.js @@ -0,0 +1,212 @@ +import Link from '@docusaurus/Link'; +import { + isActiveSidebarItem, + findFirstSidebarItemLink, + useDocSidebarItemsExpandedState, +} from '@docusaurus/plugin-content-docs/client'; +import { + ThemeClassNames, + useThemeConfig, + usePrevious, + Collapsible, + useCollapsible, +} from '@docusaurus/theme-common'; +import { isSamePath } from '@docusaurus/theme-common/internal'; +import { translate } from '@docusaurus/Translate'; +import useIsBrowser from '@docusaurus/useIsBrowser'; +import DocSidebarItems from '@theme/DocSidebarItems'; +import clsx from 'clsx'; +import React, { useEffect, useMemo } from 'react'; + +// If we navigate to a category and it becomes active, it should automatically +// expand itself +function useAutoExpandActiveCategory({ isActive, collapsed, updateCollapsed }) { + const wasActive = usePrevious(isActive); + useEffect(() => { + const justBecameActive = isActive && !wasActive; + if (justBecameActive && collapsed) { + updateCollapsed(false); + } + }, [isActive, wasActive, collapsed, updateCollapsed]); +} + +/** + * When a collapsible category has no link, we still link it to its first child + * during SSR as a temporary fallback. This allows to be able to navigate inside + * the category even when JS fails to load, is delayed or simply disabled + * React hydration becomes an optional progressive enhancement + * see https://github.com/facebookincubator/infima/issues/36#issuecomment-772543188 + * see https://github.com/facebook/docusaurus/issues/3030 + */ +function useCategoryHrefWithSSRFallback(item) { + const isBrowser = useIsBrowser(); + return useMemo(() => { + if (item.href && !item.linkUnlisted) { + return item.href; + } + // In these cases, it's not necessary to render a fallback + // We skip the "findFirstCategoryLink" computation + if (isBrowser || !item.collapsible) { + return undefined; + } + return findFirstSidebarItemLink(item); + }, [item, isBrowser]); +} + +function CollapseButton({ collapsed, categoryLabel, onClick }) { + return ( +