@redocly/reef 0.130.0-next.9 → 0.130.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/CHANGELOG.md +117 -0
  2. package/dist/bin.js +1 -1
  3. package/dist/cli/stats/collectors/openapi.js +1 -1
  4. package/dist/client/ErrorBoundary.js +1 -1
  5. package/dist/client/app/Sidebar/RequestAccessButton.js +2 -2
  6. package/dist/client/app/Sidebar/Sidebar.js +2 -2
  7. package/dist/client/app/hooks/catalog/useCatalogClassic.js +1 -1
  8. package/dist/client/app/hooks/catalog/useCatalogFilter.js +1 -1
  9. package/dist/client/app/hooks/catalog/useCatalogViewMode.js +1 -1
  10. package/dist/client/app/hooks/catalog/useSearchTracker.js +1 -1
  11. package/dist/client/app/hooks/usePageTimeTracker.js +1 -1
  12. package/dist/client/app/hooks/useRouteChangeTracker.js +1 -1
  13. package/dist/client/app/pages/DevLogin/DevLogin.js +1 -1
  14. package/dist/client/app/search/message-handlers.d.ts +29 -0
  15. package/dist/client/app/search/message-handlers.js +1 -0
  16. package/dist/client/app/search/sse-parser.d.ts +10 -0
  17. package/dist/client/app/search/sse-parser.js +2 -0
  18. package/dist/client/app/search/useAiSearch.d.ts +9 -11
  19. package/dist/client/app/search/useAiSearch.js +1 -1
  20. package/dist/client/app/search/useSearch.js +1 -1
  21. package/dist/client/constants/ai-search.d.ts +30 -0
  22. package/dist/client/constants/ai-search.js +1 -0
  23. package/dist/client/constants/index.d.ts +2 -0
  24. package/dist/client/constants/index.js +1 -0
  25. package/dist/client/types/ai-search.d.ts +73 -0
  26. package/dist/client/types/ai-search.js +0 -0
  27. package/dist/client/types/index.d.ts +1 -0
  28. package/dist/constants/l10n/langs/ar.js +1 -1
  29. package/dist/constants/l10n/langs/de.js +1 -1
  30. package/dist/constants/l10n/langs/en.js +1 -1
  31. package/dist/constants/l10n/langs/es.js +1 -1
  32. package/dist/constants/l10n/langs/fr.js +1 -1
  33. package/dist/constants/l10n/langs/hi.js +1 -1
  34. package/dist/constants/l10n/langs/it.js +1 -1
  35. package/dist/constants/l10n/langs/ja.js +1 -1
  36. package/dist/constants/l10n/langs/ko.js +1 -1
  37. package/dist/constants/l10n/langs/pl.js +1 -1
  38. package/dist/constants/l10n/langs/pt-BR.js +1 -1
  39. package/dist/constants/l10n/langs/pt.js +1 -1
  40. package/dist/constants/l10n/langs/ru.js +1 -1
  41. package/dist/constants/l10n/langs/uk.js +1 -1
  42. package/dist/constants/l10n/langs/zh.js +1 -1
  43. package/dist/server/api-routes/run-api-routes-worker.js +1 -1
  44. package/dist/server/constants/common.js +1 -1
  45. package/dist/server/entitlements/entitlements-provider.js +1 -1
  46. package/dist/server/plugins/catalog-entities/database/catalog-entities-service.d.ts +32 -9
  47. package/dist/server/plugins/catalog-entities/database/catalog-entities-service.js +1 -1
  48. package/dist/server/plugins/catalog-entities/database/repositories/local/catalog-entities-bff-repository.d.ts +16 -5
  49. package/dist/server/plugins/catalog-entities/database/repositories/local/catalog-entities-bff-repository.js +21 -17
  50. package/dist/server/plugins/catalog-entities/database/repositories/local/catalog-entities-local-read-repository.d.ts +35 -9
  51. package/dist/server/plugins/catalog-entities/database/repositories/local/catalog-entities-local-read-repository.js +24 -21
  52. package/dist/server/plugins/catalog-entities/database/repositories/local/catalog-entities-local-repository.d.ts +31 -8
  53. package/dist/server/plugins/catalog-entities/database/repositories/local/catalog-entities-local-repository.js +1 -1
  54. package/dist/server/plugins/catalog-entities/database/repositories/local/catalog-entities-local-write-repository.js +1 -1
  55. package/dist/server/plugins/catalog-entities/database/repositories/local/catalog-entities-relations-repository.d.ts +7 -1
  56. package/dist/server/plugins/catalog-entities/database/repositories/local/catalog-entities-relations-repository.js +1 -1
  57. package/dist/server/plugins/catalog-entities/database/repositories/remote/catalog-entities-remote-repository.d.ts +1 -1
  58. package/dist/server/plugins/catalog-entities/database/repositories/remote/catalog-entities-remote-repository.js +1 -1
  59. package/dist/server/plugins/catalog-entities/database/repositories/utils/build-entities-exclusion-filter.d.ts +13 -0
  60. package/dist/server/plugins/catalog-entities/database/repositories/utils/build-entities-exclusion-filter.js +1 -0
  61. package/dist/server/plugins/catalog-entities/database/repositories/utils/build-rbac-filter.d.ts +31 -0
  62. package/dist/server/plugins/catalog-entities/database/repositories/utils/build-rbac-filter.js +9 -0
  63. package/dist/server/plugins/catalog-entities/extensions/extractors/api-description/base.js +1 -1
  64. package/dist/server/plugins/catalog-entities/extensions/extractors/api-description/openapi-entities-extractor.js +1 -1
  65. package/dist/server/plugins/catalog-entities/extensions/extractors/fs-entities-extractor.js +1 -1
  66. package/dist/server/plugins/catalog-entities/get-server-props.js +1 -1
  67. package/dist/server/plugins/catalog-entities/types/openapi.d.ts +11 -0
  68. package/dist/server/plugins/catalog-entities/types/openapi.js +0 -0
  69. package/dist/server/plugins/catalog-entities/types/params.d.ts +6 -0
  70. package/dist/server/plugins/catalog-entities/types/params.js +0 -0
  71. package/dist/server/plugins/catalog-entities/utils/catalog-data-collector.js +1 -1
  72. package/dist/server/plugins/catalog-entities/utils/get-not-accessible-catalog-resources.d.ts +11 -0
  73. package/dist/server/plugins/catalog-entities/utils/get-not-accessible-catalog-resources.js +1 -0
  74. package/dist/server/plugins/config-parser/index.js +1 -1
  75. package/dist/server/plugins/markdown/markdown-static-data-loader.js +1 -1
  76. package/dist/server/plugins/mcp/docs-mcp/tools/docs-mcp-tool.js +1 -1
  77. package/dist/server/plugins/mcp/docs-mcp/tools/get-endpoint-info.js +1 -1
  78. package/dist/server/plugins/mcp/docs-mcp/tools/get-endpoints.js +1 -1
  79. package/dist/server/plugins/mcp/docs-mcp/tools/get-full-api-description.js +1 -1
  80. package/dist/server/plugins/mcp/docs-mcp/tools/get-security-schemes.js +1 -1
  81. package/dist/server/plugins/mcp/handlers/docs-mcp-handler.js +1 -1
  82. package/dist/server/plugins/mcp/handlers/errors.js +1 -1
  83. package/dist/server/plugins/mcp/servers/base-server.js +1 -1
  84. package/dist/server/plugins/openapi-docs/template/helpers.d.ts +1 -1
  85. package/dist/server/plugins/openapi-docs/template/helpers.js +3 -3
  86. package/dist/server/plugins/search/llmstxt/index.js +4 -4
  87. package/dist/server/tools/notifiers/logger.js +1 -1
  88. package/dist/server/tools/notifiers/reporter.js +7 -7
  89. package/dist/server/utils/rbac.d.ts +65 -0
  90. package/dist/server/web-server/auth.js +3 -3
  91. package/dist/server/web-server/middleware/catalogAuthMiddleware.d.ts +4 -6
  92. package/dist/server/web-server/middleware/catalogAuthMiddleware.js +1 -1
  93. package/dist/server/web-server/routes/ask-ai.js +1 -1
  94. package/dist/server/web-server/routes/auth.js +1 -1
  95. package/dist/server/web-server/routes/catalog/bff-catalog-related-entities.js +1 -1
  96. package/dist/server/web-server/routes/catalog/bff-catalog.js +1 -1
  97. package/dist/server/web-server/routes/catalog/catalog.js +1 -1
  98. package/dist/server/web-server/routes/catalog/helpers/has-access-to-entity.d.ts +10 -0
  99. package/dist/server/web-server/routes/catalog/helpers/has-access-to-entity.js +1 -0
  100. package/dist/server/web-server/routes/dynamic-route.js +1 -1
  101. package/dist/server/web-server/routes/helpers/get-current-rbac-teams.d.ts +3 -0
  102. package/dist/server/web-server/routes/helpers/get-current-rbac-teams.js +1 -0
  103. package/dist/server/web-server/routes/helpers/get-rbac-restrictions-data-for-catalog.d.ts +11 -0
  104. package/dist/server/web-server/routes/helpers/get-rbac-restrictions-data-for-catalog.js +1 -0
  105. package/dist/server/web-server/routes/index.js +1 -1
  106. package/dist/server/web-server/routes/mcp-oauth.js +1 -1
  107. package/dist/server/web-server/routes/page-data.js +1 -1
  108. package/dist/server/web-server/utils.d.ts +2 -2
  109. package/package.json +16 -16
@@ -1,15 +1,15 @@
1
- import{and as c,count as v,eq as u,isNotNull as D,notExists as y,or as $,sql as e}from"drizzle-orm";import{unionAll as f}from"drizzle-orm/sqlite-core";import{logger as F}from"../../../../../tools/notifiers/logger.js";import{VERSION_NOT_SPECIFIED as A}from"@redocly/theme/core/constants";import{applyPagination as S}from"../../../../../providers/database/pagination/index.js";import{applyFilter as B}from"../../../../../providers/database/pagination/filter.js";import{FIELDS_TO_SELECT_FOR_ENTITY as _,FIELDS_TO_SELECT_FOR_ENTITY_RELATION as m}from"../utils.js";import{createEntityReadModel as k}from"../../mappers/create-entity-read-model.js";import{createEntityRelation as C}from"../../mappers/create-entity-relation.js";import{entitiesTable as i}from"../../../../../providers/database/databases/catalog-sqlite/schemas/entities-table.js";import{entitiesRelationsTable as E}from"../../../../../providers/database/databases/catalog-sqlite/schemas/entities-relations-table.js";import{CatalogEntitiesRelationsRepository as I}from"./catalog-entities-relations-repository.js";import{CatalogEntitiesBffRepository as g}from"./catalog-entities-bff-repository.js";import{createMergedEntityFieldsForSelect as b}from"../utils/create-merged-entity-fields-for-select.js";import{buildSemanticVersionSortExpr as L}from"../utils/semantic-version-sort.js";import{normalizeRevisionFlags as M}from"../utils/normalize-revision-flags.js";class te{#e;#t=void 0;#i;#r;constructor(t){this.#e=t,this.#i=new I(this.#e,this.#t||""),this.#r=new g(this.#e,this.#t||"")}async attachDatabase(t){this.#t!==t&&(this.#t=t,await this.#e.client.run(e`ATTACH DATABASE ${t} AS remote`),this.#i=new I(this.#e,t),this.#r=new g(this.#e,t))}async getEntities(t={}){const r=this.#t?f(this.#e.client.select(_).from(e`remote.entities`).where(y(this.#e.client.select({id:_.id}).from(i).where(c(e`${i.key} = remote.entities.key`,e`${i.version} = remote.entities.version`,e`${i.revision} = remote.entities.revision`)))),this.#e.client.select(_).from(i).where(y(this.#e.client.select({id:e.raw("id")}).from(e`remote.entities as r`).where(c(e`r.key = ${i.key}`,e`r.version = ${i.version}`,e`r.revision = ${i.revision}`)))),this.#e.client.select(b("r","l")).from(e`remote.entities as r`).innerJoin(e`entities as l`,e`l.key = r.key AND l.version = r.version AND l.revision = r.revision`)):this.#e.client.select(_).from(i),s=this.#e.client.select(_).from(r.as("combined_entities")),n=this.#e.client.select(_).from(r.as("combined_entities")).$dynamic(),d=S(n,{...t,limit:void 0,skip:void 0,after:void 0,before:void 0}),l=this.#e.client.$count(d),a=s.$dynamic(),o=t.limit||10,h=S(a,{...t,limit:o+1}),[N,p]=await Promise.all([h.run(),l]),w=N.rows,T=w.length>o;return{items:w.slice(0,o).map(R=>k(R)).filter(R=>R!==null),hasMore:T,total:p}}async getEntityKeysAndVersionsBySourceFile(t){const r=this.#t?f(this.#e.client.select({keyVersion:e`key || ':' || COALESCE(version, ${A})`.as("keyVersion")}).from(e`remote.entities`).where(c(e`source_file = ${t}`,e`source = 'file'`,e`key IS NOT NULL`)),this.#e.client.select({keyVersion:e`${i.key} || ':' || COALESCE(${i.version}, ${A})`.as("keyVersion")}).from(i).where(c(u(i.sourceFile,t),u(i.source,"file"),e`${i.key} IS NOT NULL`,y(this.#e.client.select({key:e.raw("key")}).from(e`remote.entities as remote`).where(e`remote.key = ${i.key}`))))):this.#e.client.select({keyVersion:e`${i.key} || ':' || COALESCE(${i.version}, ${A})`.as("keyVersion")}).from(i).where(c(u(i.sourceFile,t),u(i.source,"file"),D(i.key))),s=await this.#e.client.selectDistinct({keyVersion:e`combined_keys_versions.keyVersion`}).from(r.as("combined_keys_versions")).run();return new Set(s.rows.map(n=>n.keyVersion))}async listEntityRevisions(t,r){const s=[u(i.key,t),u(i.isDeleted,!1),...r?[u(i.version,r)]:[]],n={version:e.raw("version"),revision:e.raw("revision"),isCurrent:e.raw("is_current"),createdAt:e.raw("created_at"),updatedAt:e.raw("updated_at"),isDefaultVersion:e.raw("is_default_version")},d=this.#t?f(this.#e.client.select(n).from(e`remote.entities`).where(c(e`key = ${t}`,e`is_deleted = 0`,r?e`version = ${r}`:void 0,y(this.#e.client.select({id:e.raw("id")}).from(i).where(c(e`${i.key} = remote.entities.key`,e`${i.version} = remote.entities.version`,e`${i.revision} = remote.entities.revision`))))),this.#e.client.select({version:i.version,revision:i.revision,isCurrent:i.isCurrent,createdAt:i.createdAt,updatedAt:i.updatedAt,isDefaultVersion:i.isDefaultVersion}).from(i).where(c(...s,y(this.#e.client.select({id:e.raw("id")}).from(e`remote.entities as r`).where(c(e`r.key = ${i.key}`,e`r.version = ${i.version}`,e`r.revision = ${i.revision}`))))),this.#e.client.select({version:e.raw("r.version AS version"),revision:e.raw("r.revision AS revision"),isCurrent:e.raw("MAX(r.is_current, l.is_current) AS is_current"),createdAt:e.raw("MIN(r.created_at, l.created_at) AS created_at"),updatedAt:e.raw("MAX(r.updated_at, l.updated_at) AS updated_at"),isDefaultVersion:e.raw("MAX(r.is_default_version, l.is_default_version) AS is_default_version")}).from(e`remote.entities as r`).innerJoin(e`entities as l`,e`l.key = r.key AND l.version = r.version AND l.revision = r.revision`).where(c(e`r.key = ${t}`,e`r.is_deleted = 0`,e`l.is_deleted = 0`,r?e`r.version = ${r}`:void 0))):this.#e.client.select({version:i.version,revision:i.revision,isCurrent:i.isCurrent,createdAt:i.createdAt,updatedAt:i.updatedAt,isDefaultVersion:i.isDefaultVersion}).from(i).where(c(...s)),a=(await this.#e.client.select({version:e.raw("version"),revision:e.raw("revision"),isCurrent:e.raw("is_current"),createdAt:e.raw("created_at"),updatedAt:e.raw("updated_at"),isDefaultVersion:e.raw("is_default_version")}).from(d.as("combined_revisions")).orderBy(e`${L("version")} DESC`,e.raw("revision DESC")).run()).rows.map(o=>({version:o.version||null,revision:o.revision??"",isCurrent:o.is_current!==null?!!o.is_current:!1,createdAt:o.created_at||null,updatedAt:o.updated_at||null,isDefaultVersion:o.is_default_version!==null?!!o.is_default_version:!1}));return M(a),a}async getEntitiesCountByTypes(){if(this.#t){const t=e`
1
+ import{and as u,count as F,eq as _,isNotNull as X,notExists as f,or as J,sql as e}from"drizzle-orm";import{unionAll as p}from"drizzle-orm/sqlite-core";import{logger as q}from"../../../../../tools/notifiers/logger.js";import{VERSION_NOT_SPECIFIED as v}from"@redocly/theme/core/constants";import{applyPagination as R}from"../../../../../providers/database/pagination/index.js";import{applyFilter as P}from"../../../../../providers/database/pagination/filter.js";import{createEntityFieldsForSelect as S,FIELDS_TO_SELECT_FOR_ENTITY as h,FIELDS_TO_SELECT_FOR_ENTITY_RELATION as m}from"../utils.js";import{createEntityReadModel as g}from"../../mappers/create-entity-read-model.js";import{createEntityRelation as b}from"../../mappers/create-entity-relation.js";import{entitiesTable as t}from"../../../../../providers/database/databases/catalog-sqlite/schemas/entities-table.js";import{entitiesRelationsTable as y}from"../../../../../providers/database/databases/catalog-sqlite/schemas/entities-relations-table.js";import{entitiesAttributesTable as d}from"../../../../../providers/database/databases/catalog-sqlite/schemas/entities-attributes-table.js";import{CatalogEntitiesRelationsRepository as $}from"./catalog-entities-relations-repository.js";import{CatalogEntitiesBffRepository as B}from"./catalog-entities-bff-repository.js";import{createMergedEntityFieldsForSelect as j}from"../utils/create-merged-entity-fields-for-select.js";import{buildSemanticVersionSortExpr as M}from"../utils/semantic-version-sort.js";import{normalizeRevisionFlags as x}from"../utils/normalize-revision-flags.js";import{buildEntitiesExclusionFilter as A}from"../utils/build-entities-exclusion-filter.js";import{buildRbacFilter as T,buildRbacFilterRaw as Y}from"../utils/build-rbac-filter.js";class Ee{#e;#t=void 0;#i;#s;constructor(i){this.#e=i,this.#i=new $(this.#e,this.#t||""),this.#s=new B(this.#e,this.#t||"")}async attachDatabase(i){this.#t!==i&&(this.#t=i,await this.#e.client.run(e`ATTACH DATABASE ${i} AS remote`),this.#i=new $(this.#e,i),this.#s=new B(this.#e,i))}async getEntities({paginationParams:i,rbacTeams:s,excludedTypes:r,excludedEntities:n}){const o=A(r,n,"remote.entities"),c=A(r,n,"entities"),a=A(r,n,"r"),l=this.#t?p(this.#e.client.select(S("remote.entities")).from(e`remote.entities`).leftJoin(e`remote.entities_attributes`,_(t.key,d.entityKey)).where(u(T(s,"remote.entities_attributes"),o,f(this.#e.client.select({id:h.id}).from(t).where(u(e`${t.key} = remote.entities.key`,e`${t.version} = remote.entities.version`,e`${t.revision} = remote.entities.revision`))))),this.#e.client.select(S("entities")).from(t).leftJoin(d,_(t.key,d.entityKey)).where(u(T(s,"entities_attributes"),c,f(this.#e.client.select({id:e.raw("id")}).from(e`remote.entities as r`).where(u(e`r.key = ${t.key}`,e`r.version = ${t.version}`,e`r.revision = ${t.revision}`))))),this.#e.client.select(j("r","l")).from(e`remote.entities as r`).innerJoin(e`entities as l`,e`l.key = r.key AND l.version = r.version AND l.revision = r.revision`).leftJoin(e`entities_attributes as ea`,e`l.key = ea.entity_key`).where(u(T(s,"ea"),a))):this.#e.client.select(S("entities")).from(t).leftJoin(d,_(t.key,d.entityKey)).where(u(T(s,"entities_attributes"),c)),N=this.#e.client.select(h).from(l.as("combined_entities")),E=this.#e.client.select(h).from(l.as("combined_entities")).$dynamic(),k=R(E,{...i,limit:void 0,skip:void 0,after:void 0,before:void 0}),O=this.#e.client.$count(k),w=N.$dynamic(),I=i.limit||10,U=R(w,{...i,limit:I+1}),[W,V]=await Promise.all([U.run(),O]),D=W.rows,H=D.length>I;return{items:D.slice(0,I).map(C=>g(C)).filter(C=>C!==null),hasMore:H,total:V}}async getEntityKeysAndVersionsBySourceFile(i){const s=this.#t?p(this.#e.client.select({keyVersion:e`key || ':' || COALESCE(version, ${v})`.as("keyVersion")}).from(e`remote.entities`).where(u(e`source_file = ${i}`,e`source = 'file'`,e`key IS NOT NULL`)),this.#e.client.select({keyVersion:e`${t.key} || ':' || COALESCE(${t.version}, ${v})`.as("keyVersion")}).from(t).where(u(_(t.sourceFile,i),_(t.source,"file"),e`${t.key} IS NOT NULL`,f(this.#e.client.select({key:e.raw("key")}).from(e`remote.entities as remote`).where(e`remote.key = ${t.key}`))))):this.#e.client.select({keyVersion:e`${t.key} || ':' || COALESCE(${t.version}, ${v})`.as("keyVersion")}).from(t).where(u(_(t.sourceFile,i),_(t.source,"file"),X(t.key))),r=await this.#e.client.selectDistinct({keyVersion:e`combined_keys_versions.keyVersion`}).from(s.as("combined_keys_versions")).run();return new Set(r.rows.map(n=>n.keyVersion))}async listEntityRevisions(i,s){const r=[_(t.key,i),_(t.isDeleted,!1),...s?[_(t.version,s)]:[]],n={version:e.raw("version"),revision:e.raw("revision"),isCurrent:e.raw("is_current"),createdAt:e.raw("created_at"),updatedAt:e.raw("updated_at"),isDefaultVersion:e.raw("is_default_version")},o=this.#t?p(this.#e.client.select(n).from(e`remote.entities`).where(u(e`key = ${i}`,e`is_deleted = 0`,s?e`version = ${s}`:void 0,f(this.#e.client.select({id:e.raw("id")}).from(t).where(u(e`${t.key} = remote.entities.key`,e`${t.version} = remote.entities.version`,e`${t.revision} = remote.entities.revision`))))),this.#e.client.select({version:t.version,revision:t.revision,isCurrent:t.isCurrent,createdAt:t.createdAt,updatedAt:t.updatedAt,isDefaultVersion:t.isDefaultVersion}).from(t).where(u(...r,f(this.#e.client.select({id:e.raw("id")}).from(e`remote.entities as r`).where(u(e`r.key = ${t.key}`,e`r.version = ${t.version}`,e`r.revision = ${t.revision}`))))),this.#e.client.select({version:e.raw("r.version AS version"),revision:e.raw("r.revision AS revision"),isCurrent:e.raw("MAX(r.is_current, l.is_current) AS is_current"),createdAt:e.raw("MIN(r.created_at, l.created_at) AS created_at"),updatedAt:e.raw("MAX(r.updated_at, l.updated_at) AS updated_at"),isDefaultVersion:e.raw("MAX(r.is_default_version, l.is_default_version) AS is_default_version")}).from(e`remote.entities as r`).innerJoin(e`entities as l`,e`l.key = r.key AND l.version = r.version AND l.revision = r.revision`).where(u(e`r.key = ${i}`,e`r.is_deleted = 0`,e`l.is_deleted = 0`,s?e`r.version = ${s}`:void 0))):this.#e.client.select({version:t.version,revision:t.revision,isCurrent:t.isCurrent,createdAt:t.createdAt,updatedAt:t.updatedAt,isDefaultVersion:t.isDefaultVersion}).from(t).where(u(...r)),a=(await this.#e.client.select({version:e.raw("version"),revision:e.raw("revision"),isCurrent:e.raw("is_current"),createdAt:e.raw("created_at"),updatedAt:e.raw("updated_at"),isDefaultVersion:e.raw("is_default_version")}).from(o.as("combined_revisions")).orderBy(e`${M("version")} DESC`,e.raw("revision DESC")).run()).rows.map(l=>({version:l.version||null,revision:l.revision??"",isCurrent:l.is_current!==null?!!l.is_current:!1,createdAt:l.created_at||null,updatedAt:l.updated_at||null,isDefaultVersion:l.is_default_version!==null?!!l.is_default_version:!1}));return x(a),a}async getEntitiesCountByTypes(){if(this.#t){const i=e`
2
2
  SELECT key, type, version,
3
3
  ROW_NUMBER() OVER (
4
4
  PARTITION BY key
5
- ORDER BY ${L("version")} DESC
5
+ ORDER BY ${M("version")} DESC
6
6
  ) as rn
7
7
  FROM (
8
8
  SELECT key, type, version FROM remote.entities WHERE is_current = 1 AND is_deleted = 0
9
9
  UNION ALL
10
10
  SELECT key, type, version FROM entities WHERE is_current = 1 AND is_deleted = 0
11
11
  )
12
- `;return this.#e.client.select({type:e`type`,count:v()}).from(e`(SELECT * FROM (${t}) WHERE rn = 1) as highest_version_entities`).groupBy(e`type`)}return this.#e.client.select({type:i.type,count:v()}).from(i).where(c(u(i.isCurrent,!0),u(i.isDeleted,!1))).groupBy(i.type)}async getEntityById(t){const n=(await(this.#t?f(this.#e.client.select(_).from(e`remote.entities`).where(e`id = ${t}`),this.#e.client.select(_).from(i).where(u(i.id,t))):this.#e.client.select(_).from(i).where(u(i.id,t))).run()).rows[0];return n?k(n):null}async getOutdatedEntities(t){const r=this.#t?f(this.#e.client.select(_).from(e`remote.entities`),this.#e.client.select(_).from(i).where(y(this.#e.client.select({id:_.id}).from(e`remote.entities as remote`).where(e`remote.key = ${i.key}`)))):this.#e.client.select(_).from(i),s=this.#e.client.select(_).from(r.as("combined_entities")).$dynamic(),{whereCondition:n}=B(s,t),d=$(e`combined_entities.scorecards_status = 'OUTDATED'`,e`combined_entities.scorecards_status IS NULL`),l=n?c(n,d):d;return(await s.where(l).run()).rows.map(o=>k(o)).filter(o=>o!==null)}async getEntitiesRelations(t={}){const r=this.#t?f(this.#e.client.select(m).from(e`remote.entities_relations`),this.#e.client.select(m).from(E).where(y(this.#e.client.select({id:m.id}).from(e`remote.entities_relations as remote`).where(e`remote.source_key = ${E.sourceKey}`)))):this.#e.client.select(m).from(E),s=this.#e.client.select(m).from(r.as("combined_entities_relations")).$dynamic(),n=this.#e.client.select(m).from(r.as("combined_entities_relations")).$dynamic(),d=S(n,{...t,limit:void 0,skip:void 0,after:void 0,before:void 0}),l=this.#e.client.$count(d),a=t.limit||10,o=S(s,{...t,limit:a+1}),[h,N]=await Promise.all([o.run(),l]),p=h.rows,w=p.length>a;return{items:p.slice(0,a).map(T=>C(T)).filter(T=>T!==null),hasMore:w,total:N}}async getEntityRelationById(t){const n=(await(this.#t?f(this.#e.client.select(m).from(e`remote.entities_relations`).where(u(E.id,t)),this.#e.client.select(m).from(E).where(c(u(E.id,t),y(this.#e.client.select({id:m.id}).from(e`remote.entities_relations as remote`).where(e`remote.id = ${E.id}`))))):this.#e.client.select(m).from(E).where(u(E.id,t))).run()).rows[0];return n?C(n):null}async getEntitiesWithRelations(t={},r){return this.#r.getEntitiesWithRelations(t,r)}async getEntityWithRelationsByKey(t,r,s){return this.#r.getEntityWithRelationsByKey(t,r,s)}async getRelationsForEntity(t,r,s){return this.#i.getRelationsForEntity(t,r,s)}async getRelatedEntities(t,r={}){return this.#i.getRelatedEntities(t,r)}async getCatalogFilters({entitiesTypes:t=[],emptyFilters:r=[]}){if(!r.length)return{};try{return await this.#s(t),(r.includes("domains")||r.includes("owners"))&&await this.#n(),await this.#o(r)}catch(s){return console.error("Error fetching catalog filters:",s),{}}finally{await this.#l()}}async#s(t){if(this.#t?await this.#e.client.run(e`
12
+ `;return this.#e.client.select({type:e`type`,count:F()}).from(e`(SELECT * FROM (${i}) WHERE rn = 1) as highest_version_entities`).groupBy(e`type`)}return this.#e.client.select({type:t.type,count:F()}).from(t).where(u(_(t.isCurrent,!0),_(t.isDeleted,!1))).groupBy(t.type)}async getEntityById(i,s){const{rbacTeams:r,excludedTypes:n,excludedEntities:o}=s||{},c=A(n,o,"remote.entities"),a=A(n,o,"entities"),E=(await(this.#t?p(this.#e.client.select(S("remote.entities")).from(e`remote.entities`).leftJoin(e`remote.entities_attributes`,_(t.key,d.entityKey)).where(u(e`remote.entities.id = ${i}`,T(r,"remote.entities_attributes"),c)),this.#e.client.select(S("entities")).from(t).leftJoin(d,_(t.key,d.entityKey)).where(u(_(t.id,i),T(r,"entities_attributes"),a))):this.#e.client.select(S("entities")).from(t).leftJoin(d,_(t.key,d.entityKey)).where(u(_(t.id,i),T(r,"entities_attributes"),a))).run()).rows[0];return E?g(E):null}async getOutdatedEntities(i){const s=this.#t?p(this.#e.client.select(h).from(e`remote.entities`),this.#e.client.select(h).from(t).where(f(this.#e.client.select({id:h.id}).from(e`remote.entities as remote`).where(e`remote.key = ${t.key}`)))):this.#e.client.select(h).from(t),r=this.#e.client.select(h).from(s.as("combined_entities")).$dynamic(),{whereCondition:n}=P(r,i),o=J(e`combined_entities.scorecards_status = 'OUTDATED'`,e`combined_entities.scorecards_status IS NULL`),c=n?u(n,o):o;return(await r.where(c).run()).rows.map(l=>g(l)).filter(l=>l!==null)}async getEntitiesRelations(i={}){const s=this.#t?p(this.#e.client.select(m).from(e`remote.entities_relations`),this.#e.client.select(m).from(y).where(f(this.#e.client.select({id:m.id}).from(e`remote.entities_relations as remote`).where(e`remote.source_key = ${y.sourceKey}`)))):this.#e.client.select(m).from(y),r=this.#e.client.select(m).from(s.as("combined_entities_relations")).$dynamic(),n=this.#e.client.select(m).from(s.as("combined_entities_relations")).$dynamic(),o=R(n,{...i,limit:void 0,skip:void 0,after:void 0,before:void 0}),c=this.#e.client.$count(o),a=i.limit||10,l=R(r,{...i,limit:a+1}),[N,E]=await Promise.all([l.run(),c]),k=N.rows,O=k.length>a;return{items:k.slice(0,a).map(w=>b(w)).filter(w=>w!==null),hasMore:O,total:E}}async getEntityRelationById(i){const n=(await(this.#t?p(this.#e.client.select(m).from(e`remote.entities_relations`).where(_(y.id,i)),this.#e.client.select(m).from(y).where(u(_(y.id,i),f(this.#e.client.select({id:m.id}).from(e`remote.entities_relations as remote`).where(e`remote.id = ${y.id}`))))):this.#e.client.select(m).from(y).where(_(y.id,i))).run()).rows[0];return n?b(n):null}async getEntitiesWithRelations({paginationParams:i,rbacTeams:s,excludedEntities:r,excludedTypes:n}){return this.#s.getEntitiesWithRelations({paginationParams:i,rbacTeams:s,excludedEntities:r,excludedTypes:n})}async getEntityWithRelationsByKey({entityKey:i,filter:s,rbacTeams:r,excludedTypes:n,excludedEntities:o}){return this.#s.getEntityWithRelationsByKey({entityKey:i,filter:s,rbacTeams:r,excludedTypes:n,excludedEntities:o})}async getRelationsForEntity(i,s,r){return this.#i.getRelationsForEntity(i,s,r)}async getRelatedEntities({key:i,paginationParams:s,rbacTeams:r,excludedTypes:n,excludedEntities:o}){return this.#i.getRelatedEntities({key:i,paginationParams:s,rbacTeams:r,excludedTypes:n,excludedEntities:o})}async getCatalogFilters({entitiesTypes:i=[],emptyFilters:s=[],rbacTeams:r,excludedTypes:n,excludedEntities:o}){if(!s.length)return{};try{return await this.#r({entitiesTypes:i,rbacTeams:r,excludedTypes:n,excludedEntities:o}),(s.includes("domains")||s.includes("owners"))&&await this.#o(),await this.#a(s)}catch(c){return console.error("Error fetching catalog filters:",c),{}}finally{await this.#c()}}async#r({entitiesTypes:i,rbacTeams:s,excludedTypes:r,excludedEntities:n}){const o=Y(s,"ea"),c=this.#n(r,n);if(this.#t){const a=["e.is_current = 1","e.is_deleted = 0"];o&&a.push(o),c&&a.push(c.replace(/ENTITY_ALIAS/g,"e"));const l=["r.key IS NULL","e.is_current = 1","e.is_deleted = 0"];o&&l.push(o),c&&l.push(c.replace(/ENTITY_ALIAS/g,"e")),await this.#e.client.run(e.raw(`
13
13
  CREATE TEMP TABLE IF NOT EXISTS temp_combined_entities AS
14
14
  SELECT
15
15
  e.key,
@@ -17,7 +17,8 @@ import{and as c,count as v,eq as u,isNotNull as D,notExists as y,or as $,sql as
17
17
  e.tags,
18
18
  e.metadata
19
19
  FROM remote.entities e
20
- WHERE e.is_current = 1
20
+ LEFT JOIN remote.entities_attributes ea ON ea.entity_key = e.key
21
+ WHERE ${a.join(" AND ")}
21
22
  UNION ALL
22
23
  SELECT
23
24
  e.key,
@@ -26,26 +27,28 @@ import{and as c,count as v,eq as u,isNotNull as D,notExists as y,or as $,sql as
26
27
  e.metadata
27
28
  FROM entities e
28
29
  LEFT JOIN remote.entities r ON r.key = e.key AND r.is_current = 1
29
- WHERE r.key IS NULL AND e.is_current = 1
30
- `):await this.#e.client.run(e`
30
+ LEFT JOIN entities_attributes ea ON ea.entity_key = e.key
31
+ WHERE ${l.join(" AND ")}
32
+ `))}else{const a=["e.is_current = 1","e.is_deleted = 0"];o&&a.push(o),c&&a.push(c.replace(/ENTITY_ALIAS/g,"e")),await this.#e.client.run(e.raw(`
31
33
  CREATE TEMP TABLE IF NOT EXISTS temp_combined_entities AS
32
34
  SELECT
33
- key,
34
- type,
35
- tags,
36
- metadata
37
- FROM entities
38
- WHERE is_current = 1
39
- `),t.length){const r=t.map(s=>`'${s.replace(/'/g,"''")}'`).join(",");await this.#e.client.run(e.raw(`
35
+ e.key,
36
+ e.type,
37
+ e.tags,
38
+ e.metadata
39
+ FROM entities e
40
+ LEFT JOIN entities_attributes ea ON ea.entity_key = e.key
41
+ WHERE ${a.join(" AND ")}
42
+ `))}if(i.length){const a=i.map(l=>`'${l.replace(/'/g,"''")}'`).join(",");await this.#e.client.run(e.raw(`
40
43
  CREATE TEMP TABLE IF NOT EXISTS temp_filtered_entities AS
41
44
  SELECT * FROM temp_combined_entities
42
- WHERE type IN (${r})
45
+ WHERE type IN (${a})
43
46
  `))}else await this.#e.client.run(e`
44
47
  CREATE TEMP TABLE IF NOT EXISTS temp_filtered_entities AS
45
48
  SELECT * FROM temp_combined_entities
46
49
  `);await this.#e.client.run(e`
47
50
  CREATE INDEX IF NOT EXISTS idx_temp_filtered_type ON temp_filtered_entities(type)
48
- `)}async#n(){this.#t?await this.#e.client.run(e`
51
+ `)}#n(i,s){const r=[];if(i&&i.length>0){const n=i.map(o=>`'${o.replace(/'/g,"''")}'`).join(", ");r.push(`ENTITY_ALIAS.type NOT IN (${n})`)}if(s&&s.length>0){const n=s.map(o=>`'${o.replace(/'/g,"''")}'`).join(", ");r.push(`ENTITY_ALIAS.key NOT IN (${n})`)}return r.length>0?`(${r.join(" AND ")})`:null}async#o(){this.#t?await this.#e.client.run(e`
49
52
  CREATE TEMP TABLE IF NOT EXISTS temp_combined_relations AS
50
53
  SELECT
51
54
  source_key,
@@ -75,12 +78,12 @@ import{and as c,count as v,eq as u,isNotNull as D,notExists as y,or as $,sql as
75
78
  CREATE INDEX IF NOT EXISTS idx_temp_rel_target ON temp_combined_relations(target_key)
76
79
  `),await this.#e.client.run(e`
77
80
  CREATE INDEX IF NOT EXISTS idx_temp_rel_relation ON temp_combined_relations(source_to_target_relation)
78
- `)}async#o(t){const r={},s=[],n=t.filter(a=>a.startsWith("metadata."));t.includes("type")&&s.push(`
81
+ `)}async#a(i){const s={},r=[],n=i.filter(a=>a.startsWith("metadata."));i.includes("type")&&r.push(`
79
82
  SELECT 'type' as filter_name, type as value, COUNT(*) as count
80
83
  FROM temp_filtered_entities
81
84
  WHERE type IS NOT NULL
82
85
  GROUP BY type
83
- `),t.includes("tags")&&s.push(`
86
+ `),i.includes("tags")&&r.push(`
84
87
  SELECT 'tags' as filter_name, tag.value as value, COUNT(DISTINCT tfe.key) as count
85
88
  FROM temp_filtered_entities tfe,
86
89
  json_each(COALESCE(tfe.tags, json_array())) as tag
@@ -89,7 +92,7 @@ import{and as c,count as v,eq as u,isNotNull as D,notExists as y,or as $,sql as
89
92
  AND tag.value IS NOT NULL
90
93
  AND tag.value != ''
91
94
  GROUP BY tag.value
92
- `),t.includes("domains")&&s.push(`
95
+ `),i.includes("domains")&&r.push(`
93
96
  SELECT 'domains' as filter_name, tcr.source_key as value, COUNT(DISTINCT tfe.key) as count
94
97
  FROM temp_combined_relations tcr
95
98
  INNER JOIN temp_filtered_entities tfe ON tfe.key = tcr.target_key
@@ -97,7 +100,7 @@ import{and as c,count as v,eq as u,isNotNull as D,notExists as y,or as $,sql as
97
100
  WHERE tce.type = 'domain'
98
101
  AND tcr.source_to_target_relation = 'hasParts'
99
102
  GROUP BY tcr.source_key
100
- `),t.includes("owners")&&s.push(`
103
+ `),i.includes("owners")&&r.push(`
101
104
  SELECT 'owners' as filter_name, owner_key as value, COUNT(DISTINCT entity_key) as count
102
105
  FROM (
103
106
  SELECT
@@ -117,7 +120,7 @@ import{and as c,count as v,eq as u,isNotNull as D,notExists as y,or as $,sql as
117
120
  INNER JOIN temp_combined_entities tce ON tce.key = owner_key
118
121
  WHERE owner_key IS NOT NULL
119
122
  GROUP BY owner_key
120
- `);for(const a of n)await this.#a(a,r);if(s.length===0)return r;const d=s.join(" UNION ALL "),l=await this.#e.client.run(e.raw(d));if(l?.rows)for(const a of l.rows){const o=a.filter_name,h=a.value,N=Number(a.count)||0;r[o]||(r[o]=[]),h&&r[o].push({value:h,count:N})}return r}async#a(t,r){const s=t.substring(9),n=`$.${U(s)}`,d=await this.#e.client.run(e.raw(`
123
+ `);for(const a of n)await this.#l(a,s);if(r.length===0)return s;const o=r.join(" UNION ALL "),c=await this.#e.client.run(e.raw(o));if(c?.rows)for(const a of c.rows){const l=a.filter_name,N=a.value,E=Number(a.count)||0;s[l]||(s[l]=[]),N&&s[l].push({value:N,count:E})}return s}async#l(i,s){const r=i.substring(9),n=`$.${Q(r)}`,o=await this.#e.client.run(e.raw(`
121
124
  SELECT json_extract(metadata, '${n}') as value, COUNT(*) as count
122
125
  FROM temp_filtered_entities
123
126
  WHERE metadata IS NOT NULL
@@ -125,4 +128,4 @@ import{and as c,count as v,eq as u,isNotNull as D,notExists as y,or as $,sql as
125
128
  AND json_extract(metadata, '${n}') IS NOT NULL
126
129
  AND json_extract(metadata, '${n}') != ''
127
130
  GROUP BY value
128
- `));d?.rows&&(r[t]=d.rows.map(l=>({value:l.value,count:Number(l.count)||0})).filter(l=>l.value))}async#l(){try{await this.#e.client.run(e`DROP TABLE IF EXISTS temp_combined_entities`),await this.#e.client.run(e`DROP TABLE IF EXISTS temp_filtered_entities`),await this.#e.client.run(e`DROP TABLE IF EXISTS temp_combined_relations`)}catch(t){F.error("Error cleaning up temp tables:",t)}}}function U(O){return O.replace(/[^a-zA-Z0-9._-]/g,"")}export{te as CatalogEntitiesLocalReadRepository};
131
+ `));o?.rows&&(s[i]=o.rows.map(c=>({value:c.value,count:Number(c.count)||0})).filter(c=>c.value))}async#c(){try{await this.#e.client.run(e`DROP TABLE IF EXISTS temp_combined_entities`),await this.#e.client.run(e`DROP TABLE IF EXISTS temp_filtered_entities`),await this.#e.client.run(e`DROP TABLE IF EXISTS temp_combined_relations`)}catch(i){q.error("Error cleaning up temp tables:",i)}}}function Q(L){return L.replace(/[^a-zA-Z0-9._-]/g,"")}export{Ee as CatalogEntitiesLocalReadRepository};
@@ -6,6 +6,7 @@ import type { DatabaseConnection, RepositoryInstanceOptions } from '../../../../
6
6
  import type { CatalogFiltersParams } from './catalog-entities-local-read-repository.js';
7
7
  import type { SidebarConnectedEntity } from '@redocly/theme/core/types';
8
8
  import type { EntityReadModelSchema } from '../../../../../plugins/catalog-entities/schemas/read-model-schemas.js';
9
+ import type { GetEntityByIdParams } from '../../../types/params.js';
9
10
  import { BaseRepository } from '../../../../../providers/database/base-repository.js';
10
11
  import { type CreateEntityParams, type CreateEntityResult } from './catalog-entities-local-write-repository.js';
11
12
  export declare class CatalogEntitiesLocalRepository extends BaseRepository {
@@ -15,8 +16,13 @@ export declare class CatalogEntitiesLocalRepository extends BaseRepository {
15
16
  getEntitySources(): Record<string, SidebarConnectedEntity>;
16
17
  static getInstance(options: RepositoryInstanceOptions): Promise<CatalogEntitiesLocalRepository>;
17
18
  attachDatabase(databasePath: string): Promise<void>;
18
- getEntities(paginationParams?: PaginationParams): Promise<import("./catalog-entities-local-read-repository.js").ListResult<EntityReadModelSchema>>;
19
- getEntityById(id: string): Promise<EntityReadModelSchema | null>;
19
+ getEntities({ paginationParams, rbacTeams, excludedTypes, excludedEntities, }: {
20
+ paginationParams: PaginationParams;
21
+ rbacTeams?: string[];
22
+ excludedTypes?: string[];
23
+ excludedEntities?: string[];
24
+ }): Promise<import("./catalog-entities-local-read-repository.js").ListResult<EntityReadModelSchema>>;
25
+ getEntityById(id: string, params?: GetEntityByIdParams): Promise<EntityReadModelSchema | null>;
20
26
  getEntityKeysAndVersionsBySourceFile(sourceFile: string): Promise<Set<string>>;
21
27
  getEntitiesCountByTypes(): Promise<{
22
28
  type: string;
@@ -50,12 +56,29 @@ export declare class CatalogEntitiesLocalRepository extends BaseRepository {
50
56
  sourceKey: string;
51
57
  targetKey: string;
52
58
  } | null>;
53
- getEntitiesWithRelations(paginationParams?: PaginationParams, rbacTeams?: string[]): Promise<import("./catalog-entities-local-read-repository.js").ListResult<import("@redocly/theme").BffCatalogEntity>>;
54
- getEntityWithRelationsByKey(entityKey: string, filter?: {
55
- revision?: string | null;
56
- version?: string | null;
57
- }, rbacTeams?: string[]): Promise<import("@redocly/theme").BffCatalogEntity | null>;
58
- getRelatedEntities(entityKey: string, paginationParams?: PaginationParams): Promise<import("./catalog-entities-local-read-repository.js").ListResult<import("@redocly/theme").BffCatalogRelatedEntity>>;
59
+ getEntitiesWithRelations({ paginationParams, rbacTeams, excludedTypes, excludedEntities, }: {
60
+ paginationParams: PaginationParams;
61
+ rbacTeams?: string[];
62
+ excludedTypes?: string[];
63
+ excludedEntities?: string[];
64
+ }): Promise<import("./catalog-entities-local-read-repository.js").ListResult<import("@redocly/theme").BffCatalogEntity>>;
65
+ getEntityWithRelationsByKey({ entityKey, filter, rbacTeams, excludedTypes, excludedEntities, }: {
66
+ entityKey: string;
67
+ filter?: {
68
+ revision?: string | null;
69
+ version?: string | null;
70
+ };
71
+ rbacTeams?: string[];
72
+ excludedTypes?: string[];
73
+ excludedEntities?: string[];
74
+ }): Promise<import("@redocly/theme").BffCatalogEntity | null>;
75
+ getRelatedEntities({ key, paginationParams, rbacTeams, excludedTypes, excludedEntities, }: {
76
+ key: string;
77
+ paginationParams: PaginationParams;
78
+ rbacTeams?: string[];
79
+ excludedTypes?: string[];
80
+ excludedEntities?: string[];
81
+ }): Promise<import("./catalog-entities-local-read-repository.js").ListResult<import("@redocly/theme").BffCatalogRelatedEntity>>;
59
82
  createEntity(createEntityParams: CreateEntityParams): Promise<CreateEntityResult>;
60
83
  createEntities(createEntitiesParams: CreateEntityParams[]): Promise<void>;
61
84
  createEntityRelation(entityRelation: EntityRelationDtoSchema): Promise<{
@@ -1 +1 @@
1
- import{promiseMapLimit as o}from"../../../../../utils/async/promise-map-limit.js";import{logger as R}from"../../../../../tools/notifiers/logger.js";import{DatabaseConnectionFactory as f}from"../../../../../providers/database/database-connection-factory.js";import{BaseRepository as p}from"../../../../../providers/database/base-repository.js";import{CatalogEntitiesLocalReadRepository as m}from"./catalog-entities-local-read-repository.js";import{CatalogEntitiesLocalWriteRepository as w}from"./catalog-entities-local-write-repository.js";import{createEntityRelationDbRecordFromDto as g}from"../../mappers/create-entity-relation-db-record-from-dto.js";import{hasOptionsChanged as I}from"../../../utils/has-options-changed.js";const l=50;class s extends p{static#i;static#n;#t;#e;#s={};constructor(t){super(t),this.#t=new m(this.databaseClient),this.#e=new w(this.databaseClient,this.organizationId,this.projectId)}get transactionsManager(){return this.databaseClient.transactionsManager}getEntitySources(){return this.#s}static async getInstance(t){const e=I(s.#n,t);if(!s.#i||e){const n=await f.create("catalog-local",t);if(!n)throw new Error("Failed to create db connection for catalog entities local repository");s.#i=new s(n),s.#n=t}return s.#i}async attachDatabase(t){await this.#t.attachDatabase(t)}getEntities(t={}){return this.#t.getEntities(t)}getEntityById(t){return this.#t.getEntityById(t)}getEntityKeysAndVersionsBySourceFile(t){return this.#t.getEntityKeysAndVersionsBySourceFile(t)}getEntitiesCountByTypes(){return this.#t.getEntitiesCountByTypes()}getEntitiesRelations(t={}){return this.#t.getEntitiesRelations(t)}getEntityRelationById(t){return this.#t.getEntityRelationById(t)}getEntitiesWithRelations(t={},e){return this.#t.getEntitiesWithRelations(t,e)}getEntityWithRelationsByKey(t,e,n){return this.#t.getEntityWithRelationsByKey(t,e,n)}getRelatedEntities(t,e={}){return this.#t.getRelatedEntities(t,e)}createEntity(t){return t.isRootEntity&&(this.#s[t.sourceFile]={key:t.entity.key,version:t.entity.version??void 0}),this.#e.createEntity(t)}async createEntities(t){await o(t,l,async e=>this.createEntity(e))}createEntityRelation(t){const e=g(t,this.organizationId,this.projectId);return this.#e.upsertEntityRelation(e)}async createEntityRelations(t){await o(t,l,async e=>this.createEntityRelation(e))}deleteEntity(t){return this.#e.deleteEntity(t)}async softDeleteEntities({filter:t,revision:e,fileHash:n}){const c=await this.getEntities({filter:t}),i=await this.#e.softDeleteEntities(c.items,e,n);await this.softDeleteEntitiesRelations(i,e)}async softDeleteEntitiesRelations(t,e){try{const n=t.filter(i=>i.result==="created");if(n.length===0)return!0;const c=await o(n,l,async i=>(await this.#t.getRelationsForEntity(i.entityKey,i.entityVersion,e)).map(r=>{if(!r)return null;const y=r.direction,a=r.sourceToTargetRelation,u=r.targetKey,d=y==="outgoing"?i.entityKey:u,h=y==="outgoing"?u:i.entityKey,E=y==="outgoing"?a:a.startsWith("reverse:")?a.slice(8):a;return!E||!d||!h?null:g({type:E,sourceKey:d,targetKey:h,sourceVersion:i.entityVersion,targetVersion:i.entityVersion,sourceRevision:e,targetRevision:e,isDeleted:!0},this.organizationId,this.projectId)}).filter(r=>r!==null));return await o(c.flat(),l,async i=>this.#e.upsertEntityRelation(i)),!0}catch(n){return R.error("Error soft deleting entity relations",n),!1}}deleteEntities(t){return this.#e.deleteEntities(t)}deleteEntityRelation(t){return this.#e.deleteEntityRelation(t)}deleteEntityRelations(t){return this.#e.deleteEntityRelations(t)}getCatalogFilters(t){return this.#t.getCatalogFilters(t)}listEntityRevisions(t,e){return this.#t.listEntityRevisions(t,e)}updateEntityScorecardsStatus(t,e){return this.#e.updateEntityScorecardsStatus(t,e)}updateEntityScorecardsStatusIfCalculating(t,e){return this.#e.updateEntityScorecardsStatusIfCalculating(t,e)}getOutdatedEntities(t){return this.#t.getOutdatedEntities(t)}async setEntitiesAsOutdated(t){await this.#e.setEntitiesAsOutdated(t)}}export{s as CatalogEntitiesLocalRepository};
1
+ import{promiseMapLimit as l}from"../../../../../utils/async/promise-map-limit.js";import{logger as R}from"../../../../../tools/notifiers/logger.js";import{DatabaseConnectionFactory as p}from"../../../../../providers/database/database-connection-factory.js";import{BaseRepository as f}from"../../../../../providers/database/base-repository.js";import{CatalogEntitiesLocalReadRepository as m}from"./catalog-entities-local-read-repository.js";import{CatalogEntitiesLocalWriteRepository as w}from"./catalog-entities-local-write-repository.js";import{createEntityRelationDbRecordFromDto as g}from"../../mappers/create-entity-relation-db-record-from-dto.js";import{hasOptionsChanged as I}from"../../../utils/has-options-changed.js";const c=50;class r extends f{static#i;static#n;#t;#e;#s={};constructor(t){super(t),this.#t=new m(this.databaseClient),this.#e=new w(this.databaseClient,this.organizationId,this.projectId)}get transactionsManager(){return this.databaseClient.transactionsManager}getEntitySources(){return this.#s}static async getInstance(t){const e=I(r.#n,t);if(!r.#i||e){const i=await p.create("catalog-local",t);if(!i)throw new Error("Failed to create db connection for catalog entities local repository");r.#i=new r(i),r.#n=t}return r.#i}async attachDatabase(t){await this.#t.attachDatabase(t)}getEntities({paginationParams:t,rbacTeams:e,excludedTypes:i,excludedEntities:s}){return this.#t.getEntities({paginationParams:t,rbacTeams:e,excludedTypes:i,excludedEntities:s})}getEntityById(t,e){return this.#t.getEntityById(t,e)}getEntityKeysAndVersionsBySourceFile(t){return this.#t.getEntityKeysAndVersionsBySourceFile(t)}getEntitiesCountByTypes(){return this.#t.getEntitiesCountByTypes()}getEntitiesRelations(t={}){return this.#t.getEntitiesRelations(t)}getEntityRelationById(t){return this.#t.getEntityRelationById(t)}getEntitiesWithRelations({paginationParams:t,rbacTeams:e,excludedTypes:i,excludedEntities:s}){return this.#t.getEntitiesWithRelations({paginationParams:t,rbacTeams:e,excludedTypes:i,excludedEntities:s})}getEntityWithRelationsByKey({entityKey:t,filter:e,rbacTeams:i,excludedTypes:s,excludedEntities:n}){return this.#t.getEntityWithRelationsByKey({entityKey:t,filter:e,rbacTeams:i,excludedTypes:s,excludedEntities:n})}getRelatedEntities({key:t,paginationParams:e,rbacTeams:i,excludedTypes:s,excludedEntities:n}){return this.#t.getRelatedEntities({key:t,paginationParams:e,rbacTeams:i,excludedTypes:s,excludedEntities:n})}createEntity(t){return t.isRootEntity&&(this.#s[t.sourceFile]={key:t.entity.key,version:t.entity.version??void 0}),this.#e.createEntity(t)}async createEntities(t){await l(t,c,async e=>this.createEntity(e))}createEntityRelation(t){const e=g(t,this.organizationId,this.projectId);return this.#e.upsertEntityRelation(e)}async createEntityRelations(t){await l(t,c,async e=>this.createEntityRelation(e))}deleteEntity(t){return this.#e.deleteEntity(t)}async softDeleteEntities({filter:t,revision:e,fileHash:i}){const s=await this.getEntities({paginationParams:{filter:t}}),n=await this.#e.softDeleteEntities(s.items,e,i);await this.softDeleteEntitiesRelations(n,e)}async softDeleteEntitiesRelations(t,e){try{const i=t.filter(n=>n.result==="created");if(i.length===0)return!0;const s=await l(i,c,async n=>(await this.#t.getRelationsForEntity(n.entityKey,n.entityVersion,e)).map(a=>{if(!a)return null;const y=a.direction,o=a.sourceToTargetRelation,u=a.targetKey,d=y==="outgoing"?n.entityKey:u,h=y==="outgoing"?u:n.entityKey,E=y==="outgoing"?o:o.startsWith("reverse:")?o.slice(8):o;return!E||!d||!h?null:g({type:E,sourceKey:d,targetKey:h,sourceVersion:n.entityVersion,targetVersion:n.entityVersion,sourceRevision:e,targetRevision:e,isDeleted:!0},this.organizationId,this.projectId)}).filter(a=>a!==null));return await l(s.flat(),c,async n=>this.#e.upsertEntityRelation(n)),!0}catch(i){return R.error("Error soft deleting entity relations",i),!1}}deleteEntities(t){return this.#e.deleteEntities(t)}deleteEntityRelation(t){return this.#e.deleteEntityRelation(t)}deleteEntityRelations(t){return this.#e.deleteEntityRelations(t)}getCatalogFilters(t){return this.#t.getCatalogFilters(t)}listEntityRevisions(t,e){return this.#t.listEntityRevisions(t,e)}updateEntityScorecardsStatus(t,e){return this.#e.updateEntityScorecardsStatus(t,e)}updateEntityScorecardsStatusIfCalculating(t,e){return this.#e.updateEntityScorecardsStatusIfCalculating(t,e)}getOutdatedEntities(t){return this.#t.getOutdatedEntities(t)}async setEntitiesAsOutdated(t){await this.#e.setEntitiesAsOutdated(t)}}export{r as CatalogEntitiesLocalRepository};
@@ -1 +1 @@
1
- import{and as m,eq as c,isNull as v,or as A,sql as N}from"drizzle-orm";import{logger as u}from"../../../../../tools/notifiers/logger.js";import{promiseMapLimit as E}from"../../../../../utils/async/promise-map-limit.js";import{sha1 as F}from"../../../../../utils/crypto/sha1.js";import{isWebView as b}from"../../../../../../utils/env/is-web-view.js";import{VERSION_NOT_SPECIFIED as O}from"@redocly/theme/core/constants";import{entitiesAttributesTable as D}from"../../../../../providers/database/databases/catalog-sqlite/schemas/entities-attributes-table.js";import{createEntityDbRecord as U}from"../../mappers/create-entity-db-record.js";import{createEntityRelationDbRecordFromFileSchema as I}from"../../mappers/create-entity-relation-db-record-from-file-schema.js";import{entitiesTable as r}from"../../../../../providers/database/databases/catalog-sqlite/schemas/entities-table.js";import{entitiesRelationsTable as i}from"../../../../../providers/database/databases/catalog-sqlite/schemas/entities-relations-table.js";import{convertFilterToWhereCondition as C}from"../../../../../providers/database/pagination/filter.js";import{createEntityAttributesDbRecord as z}from"../../mappers/create-entity-attributes-db-record.js";import{RevisionRepository as P}from"../common/revision-repository.js";import{VersionRepository as j}from"../common/version-repository.js";const w=15;class re{#e;#t;#r;#i;#s;constructor(e,t,s){this.#e=e,this.#t=t,this.#r=s,this.#i=new P(e),this.#s=new j(e)}async createEntity({entity:e,fileHash:t,sourceFile:s,revision:a=new Date().toISOString(),isRootEntity:o=!1,isDeleted:n=!1,rbacTeams:y=[]}){try{const{relations:l=[],...f}=e,p=F(JSON.stringify(f)),d=e.version??O,g=await this.#i.shouldSkipRevisionCreation(e.key,d,p,o,n);if(await this.#o({entityKey:e.key,rbacTeams:y}),g)return{result:"skipped",entityKey:e.key};const h=await this.#i.shouldSetNewCurrentRevision({key:e.key,version:d,revision:a}),R=U({entity:{...e,revision:a,hash:p,isCurrent:h,isDefaultVersion:h,isDeleted:n,version:d},sourceFile:s,organizationId:this.#t,projectId:this.#r,source:"file",fileHash:t}),{key:V,source:L,...K}=R;if(h&&(await this.#i.markAllRevisionsAsNotCurrent(V),await this.#s.markAllVersionsAsNotDefault(V)),b())return await this.#n(R,l),{result:"created",entityKey:e.key,entityRevision:a,entityVersion:d};const S=this.#e.client.insert(r).values(R).onConflictDoUpdate({target:[r.key,r.source,r.revision,r.version],set:K}),T=l?.length&&l.length>0?this.#e.client.insert(i).values(l.map(k=>I({relation:k,sourceFile:s,fileHash:t,sourceKey:e.key,sourceVersion:d,sourceRevision:a??null,organizationId:this.#t,projectId:this.#r}))).onConflictDoNothing({target:[i.sourceKey,i.targetKey,i.sourceVersion,i.targetVersion,i.sourceRevision,i.targetRevision,i.sourceToTargetRelation]}).run():Promise.resolve();return await E([S,T],w,async k=>k),{result:"created",entityKey:e.key,entityRevision:a,entityVersion:d}}catch(l){return u.error("Error adding entity",l),{result:"error",entityKey:e.key}}}async deleteEntity(e){try{return await this.#e.client.delete(r).where(c(r.key,e)),e}catch(t){return u.error("Error deleting entity",t),null}}async deleteEntities(e){try{const t=C(e);if(!t)return!1;const s=await this.#e.client.delete(r).where(t).returning({key:r.key,source:r.source,isCurrent:r.isCurrent,isDefaultVersion:r.isDefaultVersion,version:r.version});if(s.length===0)return!0;const a=s.reduce((o,n)=>((n.isCurrent||n.isDefaultVersion)&&o.add(n.key),o),new Set);if(a.size===0)return!0;await E(Array.from(a),w,async o=>this.#i.ensureDefaultAndCurrentRevisionForKey(o));for(const o of s)await this.#e.client.delete(i).where(A(m(c(i.sourceKey,o.key),...o.version?[c(i.sourceVersion,o.version)]:[v(i.sourceVersion)]),m(c(i.targetKey,o.key),...o.version?[c(i.targetVersion,o.version)]:[v(i.targetVersion)])));return!0}catch(t){return u.error("Error deleting entities",t),!1}}async deleteEntityRelation(e){try{return await this.#e.client.delete(i).where(c(i.id,e)),e}catch{return null}}async softDeleteEntities(e,t,s){try{const a=e.map(n=>{const y={type:n.type,key:n.key,title:n.title,summary:n.summary??void 0,tags:n.tags??void 0,metadata:n.metadata??void 0,git:n.git??void 0,contact:n.contact??void 0,links:n.links??void 0,version:n.version??void 0};return this.createEntity({entity:y,revision:t,sourceFile:n.sourceFile??"",fileHash:s,isDeleted:!0})});return await E(a,w,async n=>n)}catch(a){return u.error("Error soft deleting entities",a),[]}}async deleteEntityRelations(e){try{const t=C(e);return t?(await this.#e.client.delete(i).where(t),!0):!1}catch(t){return u.error("Error deleting entity relations",t),!1}}async upsertEntityRelation(e){if(!e)return null;try{const{sourceKey:t,targetKey:s,sourceVersion:a,targetVersion:o,sourceRevision:n,targetRevision:y,...l}=e,f=await this.#e.client.insert(i).values(e).onConflictDoUpdate({target:[i.sourceKey,i.targetKey,i.sourceVersion,i.targetVersion,i.sourceRevision,i.targetRevision,i.sourceToTargetRelation],set:l}).returning();return f?.length?f[0]:null}catch(t){return u.error("Error creating entity relation",t),null}}async#n(e,t){const{key:s,source:a,version:o,isDefaultVersion:n,...y}=e,p=(await this.#e.client.select({id:r.id}).from(r).where(m(c(r.key,s),c(r.source,a??"file"),o?c(r.version,o):v(r.version))).limit(1).run()).rows.length>0?this.#e.client.update(r).set(y).where(m(c(r.key,s),c(r.source,a??"file"),o?c(r.version,o):v(r.version))).run():this.#e.client.insert(r).values(e).onConflictDoUpdate({target:[r.key,r.source,r.revision,r.version],set:y}).run(),d=t?.map(g=>{const h=I({relation:g,sourceFile:e.sourceFile??"",fileHash:e.fileHash??"",sourceKey:e.key,sourceVersion:e.version??null,sourceRevision:e.revision??null,organizationId:this.#t,projectId:this.#r});return this.#e.client.insert(i).values(h).onConflictDoUpdate({target:[i.sourceKey,i.targetKey,i.sourceVersion,i.targetVersion,i.sourceRevision,i.targetRevision,i.sourceToTargetRelation],set:h}).run()})??[];await E([p,...d],w,async g=>g)}async updateEntityScorecardsStatus(e,t){try{return(await this.#e.client.update(r).set({scorecardsStatus:t}).where(c(r.id,e)).returning()).length>0}catch(s){return u.error("Error updating entity scorecards status",s),!1}}async updateEntityScorecardsStatusIfCalculating(e,t){try{return(await this.#e.client.update(r).set({scorecardsStatus:t}).where(N`${r.id} = ${e} AND ${r.scorecardsStatus} = 'CALCULATING'`).returning()).length>0}catch(s){return u.error("Error updating entity scorecards status if calculating",s),!1}}async#o({entityKey:e,rbacTeams:t}){try{await this.#e.client.insert(D).values(z({rbacTeams:t,entityKey:e,organizationId:this.#t,projectId:this.#r})).onConflictDoUpdate({target:[D.entityKey],set:{rbacTeams:JSON.stringify(t)}}).run()}catch(s){u.error("Error saving entity attributes",s)}}async setEntitiesAsOutdated(e){try{const t=C(e);await this.#e.client.update(r).set({scorecardsStatus:"OUTDATED"}).where(t)}catch(t){u.error("Error updating entities as outdated",t)}}}export{re as CatalogEntitiesLocalWriteRepository};
1
+ import{and as m,eq as c,isNull as v,or as N,sql as T}from"drizzle-orm";import{logger as u}from"../../../../../tools/notifiers/logger.js";import{promiseMapLimit as E}from"../../../../../utils/async/promise-map-limit.js";import{sha1 as F}from"../../../../../utils/crypto/sha1.js";import{isWebView as b}from"../../../../../../utils/env/is-web-view.js";import{VERSION_NOT_SPECIFIED as O}from"@redocly/theme/core/constants";import{entitiesAttributesTable as D}from"../../../../../providers/database/databases/catalog-sqlite/schemas/entities-attributes-table.js";import{createEntityDbRecord as U}from"../../mappers/create-entity-db-record.js";import{createEntityRelationDbRecordFromFileSchema as I}from"../../mappers/create-entity-relation-db-record-from-file-schema.js";import{entitiesTable as r}from"../../../../../providers/database/databases/catalog-sqlite/schemas/entities-table.js";import{entitiesRelationsTable as i}from"../../../../../providers/database/databases/catalog-sqlite/schemas/entities-relations-table.js";import{convertFilterToWhereCondition as C}from"../../../../../providers/database/pagination/filter.js";import{createEntityAttributesDbRecord as z}from"../../mappers/create-entity-attributes-db-record.js";import{RevisionRepository as P}from"../common/revision-repository.js";import{VersionRepository as j}from"../common/version-repository.js";const w=15;class re{#e;#t;#r;#i;#s;constructor(e,t,s){this.#e=e,this.#t=t,this.#r=s,this.#i=new P(e),this.#s=new j(e)}async createEntity({entity:e,fileHash:t,sourceFile:s,revision:a=new Date().toISOString(),isRootEntity:o=!1,isDeleted:n=!1,rbacTeams:d}){try{const{relations:l=[],...f}=e,p=F(JSON.stringify(f)),y=e.version??O,g=await this.#i.shouldSkipRevisionCreation(e.key,y,p,o,n);if(Array.isArray(d)&&await this.#o({entityKey:e.key,rbacTeams:d}),g)return{result:"skipped",entityKey:e.key};const h=await this.#i.shouldSetNewCurrentRevision({key:e.key,version:y,revision:a}),R=U({entity:{...e,revision:a,hash:p,isCurrent:h,isDefaultVersion:h,isDeleted:n,version:y},sourceFile:s,organizationId:this.#t,projectId:this.#r,source:"file",fileHash:t}),{key:V,source:L,...K}=R;if(h&&(await this.#i.markAllRevisionsAsNotCurrent(V),await this.#s.markAllVersionsAsNotDefault(V)),b())return await this.#n(R,l),{result:"created",entityKey:e.key,entityRevision:a,entityVersion:y};const S=this.#e.client.insert(r).values(R).onConflictDoUpdate({target:[r.key,r.source,r.revision,r.version],set:K}),A=l?.length&&l.length>0?this.#e.client.insert(i).values(l.map(k=>I({relation:k,sourceFile:s,fileHash:t,sourceKey:e.key,sourceVersion:y,sourceRevision:a??null,organizationId:this.#t,projectId:this.#r}))).onConflictDoNothing({target:[i.sourceKey,i.targetKey,i.sourceVersion,i.targetVersion,i.sourceRevision,i.targetRevision,i.sourceToTargetRelation]}).run():Promise.resolve();return await E([S,A],w,async k=>k),{result:"created",entityKey:e.key,entityRevision:a,entityVersion:y}}catch(l){return u.error("Error adding entity",l),{result:"error",entityKey:e.key}}}async deleteEntity(e){try{return await this.#e.client.delete(r).where(c(r.key,e)),e}catch(t){return u.error("Error deleting entity",t),null}}async deleteEntities(e){try{const t=C(e);if(!t)return!1;const s=await this.#e.client.delete(r).where(t).returning({key:r.key,source:r.source,isCurrent:r.isCurrent,isDefaultVersion:r.isDefaultVersion,version:r.version});if(s.length===0)return!0;const a=s.reduce((o,n)=>((n.isCurrent||n.isDefaultVersion)&&o.add(n.key),o),new Set);if(a.size===0)return!0;await E(Array.from(a),w,async o=>this.#i.ensureDefaultAndCurrentRevisionForKey(o));for(const o of s)await this.#e.client.delete(i).where(N(m(c(i.sourceKey,o.key),...o.version?[c(i.sourceVersion,o.version)]:[v(i.sourceVersion)]),m(c(i.targetKey,o.key),...o.version?[c(i.targetVersion,o.version)]:[v(i.targetVersion)])));return!0}catch(t){return u.error("Error deleting entities",t),!1}}async deleteEntityRelation(e){try{return await this.#e.client.delete(i).where(c(i.id,e)),e}catch{return null}}async softDeleteEntities(e,t,s){try{const a=e.map(n=>{const d={type:n.type,key:n.key,title:n.title,summary:n.summary??void 0,tags:n.tags??void 0,metadata:n.metadata??void 0,git:n.git??void 0,contact:n.contact??void 0,links:n.links??void 0,version:n.version??void 0};return this.createEntity({entity:d,revision:t,sourceFile:n.sourceFile??"",fileHash:s,isDeleted:!0})});return await E(a,w,async n=>n)}catch(a){return u.error("Error soft deleting entities",a),[]}}async deleteEntityRelations(e){try{const t=C(e);return t?(await this.#e.client.delete(i).where(t),!0):!1}catch(t){return u.error("Error deleting entity relations",t),!1}}async upsertEntityRelation(e){if(!e)return null;try{const{sourceKey:t,targetKey:s,sourceVersion:a,targetVersion:o,sourceRevision:n,targetRevision:d,...l}=e,f=await this.#e.client.insert(i).values(e).onConflictDoUpdate({target:[i.sourceKey,i.targetKey,i.sourceVersion,i.targetVersion,i.sourceRevision,i.targetRevision,i.sourceToTargetRelation],set:l}).returning();return f?.length?f[0]:null}catch(t){return u.error("Error creating entity relation",t),null}}async#n(e,t){const{key:s,source:a,version:o,isDefaultVersion:n,...d}=e,p=(await this.#e.client.select({id:r.id}).from(r).where(m(c(r.key,s),c(r.source,a??"file"),o?c(r.version,o):v(r.version))).limit(1).run()).rows.length>0?this.#e.client.update(r).set(d).where(m(c(r.key,s),c(r.source,a??"file"),o?c(r.version,o):v(r.version))).run():this.#e.client.insert(r).values(e).onConflictDoUpdate({target:[r.key,r.source,r.revision,r.version],set:d}).run(),y=t?.map(g=>{const h=I({relation:g,sourceFile:e.sourceFile??"",fileHash:e.fileHash??"",sourceKey:e.key,sourceVersion:e.version??null,sourceRevision:e.revision??null,organizationId:this.#t,projectId:this.#r});return this.#e.client.insert(i).values(h).onConflictDoUpdate({target:[i.sourceKey,i.targetKey,i.sourceVersion,i.targetVersion,i.sourceRevision,i.targetRevision,i.sourceToTargetRelation],set:h}).run()})??[];await E([p,...y],w,async g=>g)}async updateEntityScorecardsStatus(e,t){try{return(await this.#e.client.update(r).set({scorecardsStatus:t}).where(c(r.id,e)).returning()).length>0}catch(s){return u.error("Error updating entity scorecards status",s),!1}}async updateEntityScorecardsStatusIfCalculating(e,t){try{return(await this.#e.client.update(r).set({scorecardsStatus:t}).where(T`${r.id} = ${e} AND ${r.scorecardsStatus} = 'CALCULATING'`).returning()).length>0}catch(s){return u.error("Error updating entity scorecards status if calculating",s),!1}}async#o({entityKey:e,rbacTeams:t}){try{await this.#e.client.insert(D).values(z({rbacTeams:t,entityKey:e,organizationId:this.#t,projectId:this.#r})).onConflictDoUpdate({target:[D.entityKey],set:{rbacTeams:JSON.stringify(t)}}).run()}catch(s){u.error("Error saving entity attributes",s)}}async setEntitiesAsOutdated(e){try{const t=C(e);await this.#e.client.update(r).set({scorecardsStatus:"OUTDATED"}).where(t)}catch(t){u.error("Error updating entities as outdated",t)}}}export{re as CatalogEntitiesLocalWriteRepository};
@@ -12,6 +12,12 @@ export declare class CatalogEntitiesRelationsRepository {
12
12
  direction: "outgoing" | "incoming";
13
13
  priority: number;
14
14
  }[]>;
15
- getRelatedEntities(key: string, paginationParams?: PaginationParams): Promise<ListResult<BffCatalogRelatedEntity>>;
15
+ getRelatedEntities({ key, paginationParams, rbacTeams, excludedTypes, excludedEntities, }: {
16
+ key: string;
17
+ paginationParams: PaginationParams;
18
+ rbacTeams?: string[];
19
+ excludedTypes?: string[];
20
+ excludedEntities?: string[];
21
+ }): Promise<ListResult<BffCatalogRelatedEntity>>;
16
22
  }
17
23
  //# sourceMappingURL=catalog-entities-relations-repository.d.ts.map
@@ -1 +1 @@
1
- import{unionAll as w}from"drizzle-orm/sqlite-core";import{and as u,desc as p,eq as y,notExists as C,sql as i}from"drizzle-orm";import{VERSION_NOT_SPECIFIED as D}from"@redocly/theme/core/constants";import{entitiesTable as e}from"../../../../../providers/database/databases/catalog-sqlite/schemas/entities-table.js";import{entitiesRelationsTable as t}from"../../../../../providers/database/databases/catalog-sqlite/schemas/entities-relations-table.js";import{excludeFieldsFromFilter as B,getFirstFilterFieldValue as N}from"../../../../../providers/database/pagination/filter.js";import{applyPagination as O}from"../../../../../providers/database/pagination/index.js";import{createBffRelatedEntityFromQueryRow as K}from"../../mappers/create-bff-related-entity.js";import{mapEntityRelationRow as Q}from"../../mappers/map-entity-relation-row.js";class M{#e;#i;constructor(o,n){this.#e=o,this.#i=n}async getRelationsForEntity(o,n,d){const a=await this.#t(o,n,d);return a?(await this.#e.client.select().from(a).run()).rows.map(s=>Q(s)):[]}async getRelatedEntities(o,n={}){const d=N(n.filter,"version"),a=N(n.filter,"revision"),r=await this.#t(o,d,a);if(!r)return{items:[],total:0};const s=this.#e.client.select({relationType:r.relationName,direction:r.direction,key:e.key,revision:e.revision,id:e.id,type:e.type,title:e.title,summary:e.summary,tags:e.tags,metadata:e.metadata,createdAt:e.createdAt,updatedAt:e.updatedAt,source:e.source,sourceFile:e.sourceFile,version:e.version,isDeleted:e.isDeleted,isCurrent:e.isCurrent,dedupRn:i`ROW_NUMBER() OVER (PARTITION BY ${r.entityKey} ORDER BY ${r.priority} ASC, CASE WHEN ${r.entityRevision} = ${e.revision} THEN 1 ELSE 0 END DESC, CASE WHEN ${r.entityVersion} = ${e.version} THEN 1 ELSE 0 END DESC, ${e.isCurrent} DESC, ${e.revision} DESC)`.as("dedupRn")}).from(r).innerJoin(e,i`${r.entityKey} = ${e.key} AND (${r.entityRevision} = ${e.revision} OR ${r.entityRevision} = '' OR ${e.isCurrent} = 1) AND (${r.entityVersion} = ${e.version} OR ${r.entityVersion} = '' OR ${e.version} = ${D})`).as("unique_relations"),m=i`${s.dedupRn} = 1 AND COALESCE(${s.isDeleted}, 0) = 0`,E=this.#e.client.select().from(s).$dynamic(),$=this.#e.client.select().from(s).$dynamic(),f=B(n.filter,["version","revision"]),R={...n,filter:f,baseWhereCondition:m},l=O($,{...R,limit:void 0,skip:void 0,after:void 0,before:void 0}),c=this.#e.client.$count(l),h=n.limit||10,S=O(E,{...R,limit:h+1}),[V,A]=await Promise.all([S.run(),c]),g=V.rows.filter(v=>v.id!=null).map(v=>K(v)).filter(v=>v!==null),T=g.slice(0,h),F=g.length>h;return{items:T,hasMore:F,total:A}}async#r(o,n){if(n){const s=await(this.#i?w(this.#e.client.select({version:i.raw("version"),revision:i.raw("revision")}).from(i`remote.entities`).where(u(i`key = ${o}`,i`version = ${n}`)).orderBy(i.raw("revision DESC")),this.#e.client.select({version:e.version,revision:e.revision}).from(e).where(u(y(e.key,o),y(e.version,n),C(this.#e.client.select({id:i.raw("id")}).from(i`remote.entities as remote`).where(u(i`remote.key = ${e.key}`,i`remote.version = ${n}`))))).orderBy(p(e.revision))):this.#e.client.select({version:e.version,revision:e.revision}).from(e).where(u(y(e.key,o),y(e.version,n))).orderBy(p(e.revision))).limit(1).run();if(s.rows.length>0){const m=s.rows[0];return{version:m.version||null,revision:m.revision||null}}return null}const a=await(this.#i?w(this.#e.client.select({version:i.raw("version"),revision:i.raw("revision")}).from(i`remote.entities`).where(u(i`key = ${o}`,i`is_current = 1`)),this.#e.client.select({version:e.version,revision:e.revision}).from(e).where(u(y(e.key,o),y(e.isCurrent,!0),C(this.#e.client.select({id:i.raw("id")}).from(i`remote.entities as remote`).where(u(i`remote.key = ${e.key}`,i`remote.is_current = 1`)))))):this.#e.client.select({version:e.version,revision:e.revision}).from(e).where(u(y(e.key,o),y(e.isCurrent,!0)))).limit(1).run();if(a.rows.length>0){const r=a.rows[0];return{version:r.version||null,revision:r.revision||null}}return null}async#t(o,n,d){const a=d?{version:n||null,revision:d}:await this.#r(o,n||null),r=n||a?.version||null,s=d||a?.revision||null,m=!r||r===D,E=m?i`1 = 1`:i`(${t.sourceVersion} = ${r} OR ${t.sourceVersion} = '')`,$=m?i`1 = 1`:i`(${t.targetVersion} = ${r} OR ${t.targetVersion} = '')`,f=s?i`(${t.sourceRevision} <= ${s} OR ${t.sourceRevision} = '')`:i`1 = 0`,R=s?i`(${t.targetRevision} <= ${s} OR ${t.targetRevision} = '')`:i`1 = 0`,l=this.#e.client.select({entityKey:t.targetKey,entityRevision:t.targetRevision,entityVersion:t.targetVersion,relationName:t.sourceToTargetRelation,direction:i`'outgoing'`.as("direction"),priority:i`1`.as("priority"),isDeleted:t.isDeleted,rn:i`ROW_NUMBER() OVER (PARTITION BY ${t.targetKey}, ${t.sourceToTargetRelation} ORDER BY CASE WHEN ${t.sourceVersion} != '' THEN 1 ELSE 0 END DESC, ${t.sourceRevision} DESC)`.as("rn")}).from(t).where(u(y(t.sourceKey,o),E,f)).as("outgoing_filtered"),c=this.#e.client.select({entityKey:t.sourceKey,entityRevision:t.sourceRevision,entityVersion:t.sourceVersion,relationName:t.targetToSourceRelation,direction:i`'incoming'`.as("direction"),priority:i`2`.as("priority"),isDeleted:t.isDeleted,rn:i`ROW_NUMBER() OVER (PARTITION BY ${t.sourceKey}, ${t.targetToSourceRelation} ORDER BY CASE WHEN ${t.targetVersion} != '' THEN 1 ELSE 0 END DESC, ${t.targetRevision} DESC)`.as("rn")}).from(t).where(u(y(t.targetKey,o),$,R)).as("incoming_filtered");return this.#e.client.select({entityKey:l.entityKey,entityRevision:l.entityRevision,entityVersion:l.entityVersion,relationName:l.relationName,direction:l.direction,priority:l.priority}).from(l).where(i`${l.rn} = 1 AND COALESCE(${l.isDeleted}, 0) = 0`).unionAll(this.#e.client.select({entityKey:c.entityKey,entityRevision:c.entityRevision,entityVersion:c.entityVersion,relationName:c.relationName,direction:c.direction,priority:c.priority}).from(c).where(i`${c.rn} = 1 AND COALESCE(${c.isDeleted}, 0) = 0`)).as("all_relations")}}export{M as CatalogEntitiesRelationsRepository};
1
+ import{unionAll as C}from"drizzle-orm/sqlite-core";import{and as d,desc as D,eq as u,notExists as N,sql as i}from"drizzle-orm";import{VERSION_NOT_SPECIFIED as A}from"@redocly/theme/core/constants";import{entitiesTable as e}from"../../../../../providers/database/databases/catalog-sqlite/schemas/entities-table.js";import{entitiesRelationsTable as t}from"../../../../../providers/database/databases/catalog-sqlite/schemas/entities-relations-table.js";import{entitiesAttributesTable as p}from"../../../../../providers/database/databases/catalog-sqlite/schemas/entities-attributes-table.js";import{excludeFieldsFromFilter as W,getFirstFilterFieldValue as O}from"../../../../../providers/database/pagination/filter.js";import{applyPagination as S}from"../../../../../providers/database/pagination/index.js";import{createBffRelatedEntityFromQueryRow as k}from"../../mappers/create-bff-related-entity.js";import{mapEntityRelationRow as H}from"../../mappers/map-entity-relation-row.js";import{buildEntitiesExclusionFilter as L}from"../utils/build-entities-exclusion-filter.js";import{buildRbacFilter as q}from"../utils/build-rbac-filter.js";class te{#e;#i;constructor(n,r){this.#e=n,this.#i=r}async getRelationsForEntity(n,r,m){const y=await this.#t(n,r,m);return y?(await this.#e.client.select().from(y).run()).rows.map(a=>H(a)):[]}async getRelatedEntities({key:n,paginationParams:r,rbacTeams:m,excludedTypes:y,excludedEntities:l}){const a=O(r.filter,"version"),v=O(r.filter,"revision"),o=await this.#t(n,a,v);if(!o)return{items:[],total:0};const R=this.#e.client.select({relationType:o.relationName,direction:o.direction,key:e.key,revision:e.revision,id:e.id,type:e.type,title:e.title,summary:e.summary,tags:e.tags,metadata:e.metadata,createdAt:e.createdAt,updatedAt:e.updatedAt,source:e.source,sourceFile:e.sourceFile,version:e.version,isDeleted:e.isDeleted,isCurrent:e.isCurrent,rbacTeams:p.rbacTeams,dedupRn:i`ROW_NUMBER() OVER (PARTITION BY ${o.entityKey} ORDER BY ${o.priority} ASC, CASE WHEN ${o.entityRevision} = ${e.revision} THEN 1 ELSE 0 END DESC, CASE WHEN ${o.entityVersion} = ${e.version} THEN 1 ELSE 0 END DESC, ${e.isCurrent} DESC, ${e.revision} DESC)`.as("dedupRn")}).from(o).innerJoin(e,i`${o.entityKey} = ${e.key} AND (${o.entityRevision} = ${e.revision} OR ${o.entityRevision} = '' OR ${e.isCurrent} = 1) AND (${o.entityVersion} = ${e.version} OR ${o.entityVersion} = '' OR ${e.version} = ${A})`).leftJoin(p,u(e.key,p.entityKey)).as("unique_relations"),$=q(m,"unique_relations"),f=L(y,l,"unique_relations");let s=i`${R.dedupRn} = 1 AND COALESCE(${R.isDeleted}, 0) = 0`;$&&(s=i`${s} AND ${$}`),f&&(s=i`${s} AND ${f}`);const c=this.#e.client.select().from(R).$dynamic(),V=this.#e.client.select().from(R).$dynamic(),T=W(r.filter,["version","revision"]),g={...r,filter:T,baseWhereCondition:s},F=S(V,{...g,limit:void 0,skip:void 0,after:void 0,before:void 0}),B=this.#e.client.$count(F),h=r.limit||10,K=S(c,{...g,limit:h+1}),[b,_]=await Promise.all([K.run(),B]),w=b.rows.filter(E=>E.id!=null).map(E=>k(E)).filter(E=>E!==null),Q=w.slice(0,h),I=w.length>h;return{items:Q,hasMore:I,total:_}}async#r(n,r){if(r){const a=await(this.#i?C(this.#e.client.select({version:i.raw("version"),revision:i.raw("revision")}).from(i`remote.entities`).where(d(i`key = ${n}`,i`version = ${r}`)).orderBy(i.raw("revision DESC")),this.#e.client.select({version:e.version,revision:e.revision}).from(e).where(d(u(e.key,n),u(e.version,r),N(this.#e.client.select({id:i.raw("id")}).from(i`remote.entities as remote`).where(d(i`remote.key = ${e.key}`,i`remote.version = ${r}`))))).orderBy(D(e.revision))):this.#e.client.select({version:e.version,revision:e.revision}).from(e).where(d(u(e.key,n),u(e.version,r))).orderBy(D(e.revision))).limit(1).run();if(a.rows.length>0){const v=a.rows[0];return{version:v.version||null,revision:v.revision||null}}return null}const y=await(this.#i?C(this.#e.client.select({version:i.raw("version"),revision:i.raw("revision")}).from(i`remote.entities`).where(d(i`key = ${n}`,i`is_current = 1`)),this.#e.client.select({version:e.version,revision:e.revision}).from(e).where(d(u(e.key,n),u(e.isCurrent,!0),N(this.#e.client.select({id:i.raw("id")}).from(i`remote.entities as remote`).where(d(i`remote.key = ${e.key}`,i`remote.is_current = 1`)))))):this.#e.client.select({version:e.version,revision:e.revision}).from(e).where(d(u(e.key,n),u(e.isCurrent,!0)))).limit(1).run();if(y.rows.length>0){const l=y.rows[0];return{version:l.version||null,revision:l.revision||null}}return null}async#t(n,r,m){const y=m?{version:r||null,revision:m}:await this.#r(n,r||null),l=r||y?.version||null,a=m||y?.revision||null,v=!l||l===A,o=v?i`1 = 1`:i`(${t.sourceVersion} = ${l} OR ${t.sourceVersion} = '')`,R=v?i`1 = 1`:i`(${t.targetVersion} = ${l} OR ${t.targetVersion} = '')`,$=a?i`(${t.sourceRevision} <= ${a} OR ${t.sourceRevision} = '')`:i`1 = 0`,f=a?i`(${t.targetRevision} <= ${a} OR ${t.targetRevision} = '')`:i`1 = 0`,s=this.#e.client.select({entityKey:t.targetKey,entityRevision:t.targetRevision,entityVersion:t.targetVersion,relationName:t.sourceToTargetRelation,direction:i`'outgoing'`.as("direction"),priority:i`1`.as("priority"),isDeleted:t.isDeleted,rn:i`ROW_NUMBER() OVER (PARTITION BY ${t.targetKey}, ${t.sourceToTargetRelation} ORDER BY CASE WHEN ${t.sourceVersion} != '' THEN 1 ELSE 0 END DESC, ${t.sourceRevision} DESC)`.as("rn")}).from(t).where(d(u(t.sourceKey,n),o,$)).as("outgoing_filtered"),c=this.#e.client.select({entityKey:t.sourceKey,entityRevision:t.sourceRevision,entityVersion:t.sourceVersion,relationName:t.targetToSourceRelation,direction:i`'incoming'`.as("direction"),priority:i`2`.as("priority"),isDeleted:t.isDeleted,rn:i`ROW_NUMBER() OVER (PARTITION BY ${t.sourceKey}, ${t.targetToSourceRelation} ORDER BY CASE WHEN ${t.targetVersion} != '' THEN 1 ELSE 0 END DESC, ${t.targetRevision} DESC)`.as("rn")}).from(t).where(d(u(t.targetKey,n),R,f)).as("incoming_filtered");return this.#e.client.select({entityKey:s.entityKey,entityRevision:s.entityRevision,entityVersion:s.entityVersion,relationName:s.relationName,direction:s.direction,priority:s.priority}).from(s).where(i`${s.rn} = 1 AND COALESCE(${s.isDeleted}, 0) = 0`).unionAll(this.#e.client.select({entityKey:c.entityKey,entityRevision:c.entityRevision,entityVersion:c.entityVersion,relationName:c.relationName,direction:c.direction,priority:c.priority}).from(c).where(i`${c.rn} = 1 AND COALESCE(${c.isDeleted}, 0) = 0`)).as("all_relations")}}export{te as CatalogEntitiesRelationsRepository};
@@ -12,7 +12,7 @@ export declare class CatalogEntitiesRemoteRepository extends BaseRepository {
12
12
  static getInstance(options: RepositoryInstanceOptions): Promise<CatalogEntitiesRemoteRepository | null>;
13
13
  createEntity(entity: EntityDtoSchema): Promise<EntityReadModelSchema | null>;
14
14
  updateEntity(incomingEntity: Partial<EntityDtoSchema>, entityToBeUpdated: EntityReadModelSchema): Promise<EntityReadModelSchema | null>;
15
- deleteEntity(id: string): Promise<string | null>;
15
+ deleteEntity(entityToBeRemoved: EntityReadModelSchema): Promise<string | null>;
16
16
  createEntityRelations(relations: EntityRelationDtoSchema[]): Promise<(DatabaseEntityRelation | null)[]>;
17
17
  createEntityRelation(entityRelation: EntityRelationDtoSchema): Promise<DatabaseEntityRelation | null>;
18
18
  deleteEntityRelation(id: string): Promise<string | null>;
@@ -1 +1 @@
1
- import{and as f,eq as n,ne as C,or as w,sql as p}from"drizzle-orm";import{convertFilterToWhereCondition as D}from"../../../../../providers/database/pagination/filter.js";import{promiseMapLimit as V}from"../../../../../utils/async/promise-map-limit.js";import{sha1 as E}from"../../../../../utils/crypto/sha1.js";import{logger as a}from"../../../../../tools/notifiers/logger.js";import{entitiesTable as i}from"../../../../../providers/database/databases/catalog-sqlite/schemas/entities-table.js";import{telemetryTraceStep as c}from"../../../../../telemetry/helpers/trace-step.js";import{BaseRepository as A}from"../../../../../providers/database/base-repository.js";import{DatabaseConnectionFactory as I}from"../../../../../providers/database/database-connection-factory.js";import{entitiesRelationsTable as r}from"../../../../../providers/database/databases/catalog-sqlite/schemas/entities-relations-table.js";import{VERSION_NOT_SPECIFIED as b}from"@redocly/theme/core/constants";import{createEntityDbRecord as _}from"../../mappers/create-entity-db-record.js";import{createEntityReadModel as k}from"../../mappers/create-entity-read-model.js";import{createEntityRelationDbRecordFromDto as O}from"../../mappers/create-entity-relation-db-record-from-dto.js";import{RevisionRepository as K}from"../common/revision-repository.js";import{VersionRepository as F}from"../common/version-repository.js";const S=15;class h extends A{static#r;#t;#i;#n=!1;get transactionsManager(){return this.databaseClient.transactionsManager}constructor(t){super(t),this.#t=new K(t.client),this.#i=new F(t.client)}async sync(){if(!this.#n&&this.isNonRemoteDatabaseMode()){a.warn("Catalog entities database is currently operating in local mode: not connected to the remote database. All changes and data will only persist locally and will not be synced remotely."),this.#n=!0;return}return c("catalog_entities.remote_repository.sync",async()=>{await this.#e(),await this.databaseClient.sync()})}static async getInstance(t){return await c("catalog_entities.remote_repository.get_instance",async e=>{if(!h.#r)try{const o=await I.create("sqld-remote",t);if(!o)return a.error("Failed to create db connection for catalog entities remote repository"),e?.error(new Error("Failed to create db connection for catalog entities remote repository")),h.#r=null,null;h.#r=new h(o)}catch(o){return a.error("Error creating db connection for catalog entities remote repository",o),e?.error(o),h.#r=null,null}return h.#r})}async createEntity(t){return c("catalog_entities.remote_repository.create_entity",async()=>{await this.#e();try{a.info(`Adding entity ${t.key} to remote database`);const{relations:e=[],...o}=t,l=E(JSON.stringify(o)),s=t.version??b,y=t.revision??new Date().toISOString();if(await this.#t.shouldSkipRevisionCreation(t.key,s,l))throw new Error("Entity validation failed: entity already exists");const d=await this.#t.shouldSetNewCurrentRevision({key:t.key,version:s,revision:y}),u=_({entity:{...t,revision:y,hash:l,isCurrent:d,isDefaultVersion:d,version:s},organizationId:this.organizationId,projectId:this.projectId,source:"remote",sourceFile:null,fileHash:null}),{key:m,source:L,...N}=u;d&&(await this.#t.markAllRevisionsAsNotCurrent(m),await this.#i.markAllVersionsAsNotDefault(m));const v=await this.databaseClient.client.insert(i).values(u).onConflictDoUpdate({target:[i.key,i.source,i.revision,i.version],set:N}).returning();return v.length?(e&&await this.createEntityRelations(e.map(R=>({...R,sourceKey:t.key,targetKey:R.key}))),k(v[0])):null}catch(e){throw a.error("Error adding entity",e),e}})}async updateEntity(t,e){return c("catalog_entities.remote_repository.update_entity",async()=>{await this.#e();try{a.info(`Updating entity ${e.key} in remote database`);const o=await this.#t.shouldSetNewCurrentRevision({key:e.key,version:t.version??e.version??b,revision:e.revision});o&&(await this.#t.markAllRevisionsAsNotCurrent(e.key),await this.#i.markAllVersionsAsNotDefault(e.key));const l=_({entity:{...e,...t,hash:E(JSON.stringify({...e,...t})),isCurrent:o,isDefaultVersion:o,createdAt:e.createdAt??void 0},organizationId:this.organizationId,projectId:this.projectId,source:"remote",sourceFile:null,fileHash:null}),{key:s,source:y,scorecardsStatus:g,...d}=l,u=await this.databaseClient.client.insert(i).values(l).onConflictDoUpdate({target:[i.key,i.source,i.revision,i.version],set:{...d,scorecardsStatus:p`CASE WHEN ${i.scorecardsStatus} = 'CALCULATING' THEN 'CANCELLED' ELSE 'OUTDATED' END`}}).returning();return u.length?k(u[0]):null}catch(o){return a.error("Error updating entity",o),null}})}async deleteEntity(t){return c("catalog_entities.remote_repository.delete_entity",async()=>{await this.#e();try{const e=await this.databaseClient.client.select().from(i).where(n(i.id,t)).get();return e?(await this.#o(e),await this.databaseClient.client.delete(i).where(n(i.id,t)),e&&this.#t.ensureDefaultAndCurrentRevisionForKey(e.key),t):null}catch(e){return a.error("Error deleting entity",e),null}})}async createEntityRelations(t){return c("catalog_entities.remote_repository.create_entity_relations",async()=>(await this.#e(),await V(t,S,async e=>this.createEntityRelation(e))))}async createEntityRelation(t){return c("catalog_entities.remote_repository.create_entity_relation",async()=>{if(await this.#e(),!t)return null;try{const e=O(t,this.organizationId,this.projectId),{sourceKey:o,targetKey:l,sourceVersion:s,targetVersion:y,sourceRevision:g,targetRevision:d,...u}=e,m=await this.databaseClient.client.insert(r).values(e).onConflictDoUpdate({target:[r.sourceKey,r.targetKey,r.sourceVersion,r.targetVersion,r.sourceRevision,r.targetRevision,r.sourceToTargetRelation],set:u}).returning();return m.length?m[0]:null}catch(e){throw a.error("Error creating entity relation",e),e}})}async deleteEntityRelation(t){return c("catalog_entities.remote_repository.delete_entity_relation",async()=>{await this.#e();try{return await this.databaseClient.client.delete(r).where(n(r.id,t)),t}catch(e){return a.error("Error deleting entity relation",e),null}})}async deleteEntitiesRelations(t){return c("catalog_entities.remote_repository.delete_entities_relations",async()=>{await this.#e();try{const e=D(t);return e?(await this.databaseClient.client.delete(r).where(e),!0):!1}catch(e){return a.error("Error deleting entities relations",e),!1}})}async#o(t){const{key:e,version:o,revision:l}=t,s=o??"",y=l??"";if(((await this.databaseClient.client.select({count:p`COUNT(*)`}).from(i).where(f(n(i.key,e),C(i.id,t.id))).get())?.count??0)===0){await this.databaseClient.client.delete(r).where(w(n(r.sourceKey,e),n(r.targetKey,e)));return}if(((await this.databaseClient.client.select({count:p`COUNT(*)`}).from(i).where(f(n(i.key,e),n(i.version,s),C(i.id,t.id))).get())?.count??0)===0){await this.databaseClient.client.delete(r).where(w(f(n(r.sourceKey,e),n(r.sourceVersion,s)),f(n(r.targetKey,e),n(r.targetVersion,s))));return}await this.databaseClient.client.delete(r).where(w(f(n(r.sourceKey,e),n(r.sourceVersion,s),n(r.sourceRevision,y)),f(n(r.targetKey,e),n(r.targetVersion,s),n(r.targetRevision,y))))}#e(){return c("catalog_entities.remote_repository.db_health",async t=>{if(this.databaseClient.dbClient.$client.closed){const e=new Error("The remote database connection is closed!");throw t?.error(e),e}})}}export{h as CatalogEntitiesRemoteRepository};
1
+ import{and as g,eq as n,ne as R,or as f,sql as p}from"drizzle-orm";import{convertFilterToWhereCondition as D}from"../../../../../providers/database/pagination/filter.js";import{promiseMapLimit as V}from"../../../../../utils/async/promise-map-limit.js";import{sha1 as E}from"../../../../../utils/crypto/sha1.js";import{logger as a}from"../../../../../tools/notifiers/logger.js";import{entitiesTable as i}from"../../../../../providers/database/databases/catalog-sqlite/schemas/entities-table.js";import{telemetryTraceStep as c}from"../../../../../telemetry/helpers/trace-step.js";import{BaseRepository as A}from"../../../../../providers/database/base-repository.js";import{DatabaseConnectionFactory as I}from"../../../../../providers/database/database-connection-factory.js";import{entitiesRelationsTable as r}from"../../../../../providers/database/databases/catalog-sqlite/schemas/entities-relations-table.js";import{VERSION_NOT_SPECIFIED as b}from"@redocly/theme/core/constants";import{createEntityDbRecord as _}from"../../mappers/create-entity-db-record.js";import{createEntityReadModel as k}from"../../mappers/create-entity-read-model.js";import{createEntityRelationDbRecordFromDto as O}from"../../mappers/create-entity-relation-db-record-from-dto.js";import{RevisionRepository as K}from"../common/revision-repository.js";import{VersionRepository as F}from"../common/version-repository.js";const S=15;class h extends A{static#r;#t;#i;#n=!1;get transactionsManager(){return this.databaseClient.transactionsManager}constructor(t){super(t),this.#t=new K(t.client),this.#i=new F(t.client)}async sync(){if(!this.#n&&this.isNonRemoteDatabaseMode()){a.warn("Catalog entities database is currently operating in local mode: not connected to the remote database. All changes and data will only persist locally and will not be synced remotely."),this.#n=!0;return}return c("catalog_entities.remote_repository.sync",async()=>{await this.#e(),await this.databaseClient.sync()})}static async getInstance(t){return await c("catalog_entities.remote_repository.get_instance",async e=>{if(!h.#r)try{const o=await I.create("sqld-remote",t);if(!o)return a.error("Failed to create db connection for catalog entities remote repository"),e?.error(new Error("Failed to create db connection for catalog entities remote repository")),h.#r=null,null;h.#r=new h(o)}catch(o){return a.error("Error creating db connection for catalog entities remote repository",o),e?.error(o),h.#r=null,null}return h.#r})}async createEntity(t){return c("catalog_entities.remote_repository.create_entity",async()=>{await this.#e();try{a.info(`Adding entity ${t.key} to remote database`);const{relations:e=[],...o}=t,l=E(JSON.stringify(o)),s=t.version??b,y=t.revision??new Date().toISOString();if(await this.#t.shouldSkipRevisionCreation(t.key,s,l))throw new Error("Entity validation failed: entity already exists");const d=await this.#t.shouldSetNewCurrentRevision({key:t.key,version:s,revision:y}),u=_({entity:{...t,revision:y,hash:l,isCurrent:d,isDefaultVersion:d,version:s},organizationId:this.organizationId,projectId:this.projectId,source:"remote",sourceFile:null,fileHash:null}),{key:m,source:L,...N}=u;d&&(await this.#t.markAllRevisionsAsNotCurrent(m),await this.#i.markAllVersionsAsNotDefault(m));const v=await this.databaseClient.client.insert(i).values(u).onConflictDoUpdate({target:[i.key,i.source,i.revision,i.version],set:N}).returning();return v.length?(e&&await this.createEntityRelations(e.map(C=>({...C,sourceKey:t.key,targetKey:C.key}))),k(v[0])):null}catch(e){throw a.error("Error adding entity",e),e}})}async updateEntity(t,e){return c("catalog_entities.remote_repository.update_entity",async()=>{await this.#e();try{a.info(`Updating entity ${e.key} in remote database`);const o=await this.#t.shouldSetNewCurrentRevision({key:e.key,version:t.version??e.version??b,revision:e.revision});o&&(await this.#t.markAllRevisionsAsNotCurrent(e.key),await this.#i.markAllVersionsAsNotDefault(e.key));const l=_({entity:{...e,...t,hash:E(JSON.stringify({...e,...t})),isCurrent:o,isDefaultVersion:o,createdAt:e.createdAt??void 0},organizationId:this.organizationId,projectId:this.projectId,source:"remote",sourceFile:null,fileHash:null}),{key:s,source:y,scorecardsStatus:w,...d}=l,u=await this.databaseClient.client.insert(i).values(l).onConflictDoUpdate({target:[i.key,i.source,i.revision,i.version],set:{...d,scorecardsStatus:p`CASE WHEN ${i.scorecardsStatus} = 'CALCULATING' THEN 'CANCELLED' ELSE 'OUTDATED' END`}}).returning();return u.length?k(u[0]):null}catch(o){return a.error("Error updating entity",o),null}})}async deleteEntity(t){return c("catalog_entities.remote_repository.delete_entity",async()=>{await this.#e();try{return await this.#o(t),await this.databaseClient.client.delete(i).where(n(i.id,t.id)),await this.#t.ensureDefaultAndCurrentRevisionForKey(t.key),t.id}catch(e){return a.error("Error deleting entity",e),null}})}async createEntityRelations(t){return c("catalog_entities.remote_repository.create_entity_relations",async()=>(await this.#e(),await V(t,S,async e=>this.createEntityRelation(e))))}async createEntityRelation(t){return c("catalog_entities.remote_repository.create_entity_relation",async()=>{if(await this.#e(),!t)return null;try{const e=O(t,this.organizationId,this.projectId),{sourceKey:o,targetKey:l,sourceVersion:s,targetVersion:y,sourceRevision:w,targetRevision:d,...u}=e,m=await this.databaseClient.client.insert(r).values(e).onConflictDoUpdate({target:[r.sourceKey,r.targetKey,r.sourceVersion,r.targetVersion,r.sourceRevision,r.targetRevision,r.sourceToTargetRelation],set:u}).returning();return m.length?m[0]:null}catch(e){throw a.error("Error creating entity relation",e),e}})}async deleteEntityRelation(t){return c("catalog_entities.remote_repository.delete_entity_relation",async()=>{await this.#e();try{return await this.databaseClient.client.delete(r).where(n(r.id,t)),t}catch(e){return a.error("Error deleting entity relation",e),null}})}async deleteEntitiesRelations(t){return c("catalog_entities.remote_repository.delete_entities_relations",async()=>{await this.#e();try{const e=D(t);return e?(await this.databaseClient.client.delete(r).where(e),!0):!1}catch(e){return a.error("Error deleting entities relations",e),!1}})}async#o(t){const{key:e,version:o,revision:l}=t,s=o??"",y=l??"";if(((await this.databaseClient.client.select({count:p`COUNT(*)`}).from(i).where(g(n(i.key,e),R(i.id,t.id))).get())?.count??0)===0){await this.databaseClient.client.delete(r).where(f(n(r.sourceKey,e),n(r.targetKey,e)));return}if(((await this.databaseClient.client.select({count:p`COUNT(*)`}).from(i).where(g(n(i.key,e),n(i.version,s),R(i.id,t.id))).get())?.count??0)===0){await this.databaseClient.client.delete(r).where(f(g(n(r.sourceKey,e),n(r.sourceVersion,s)),g(n(r.targetKey,e),n(r.targetVersion,s))));return}await this.databaseClient.client.delete(r).where(f(g(n(r.sourceKey,e),n(r.sourceVersion,s),n(r.sourceRevision,y)),g(n(r.targetKey,e),n(r.targetVersion,s),n(r.targetRevision,y))))}#e(){return c("catalog_entities.remote_repository.db_health",async t=>{if(this.databaseClient.dbClient.$client.closed){const e=new Error("The remote database connection is closed!");throw t?.error(e),e}})}}export{h as CatalogEntitiesRemoteRepository};
@@ -0,0 +1,13 @@
1
+ import { type SQL } from 'drizzle-orm';
2
+ /**
3
+ * Builds an exclusion filter condition as a drizzle SQL object.
4
+ * Use this with drizzle query builder to exclude specific entity types or keys.
5
+ *
6
+ * Exclusion filter logic:
7
+ * 1. If excludedEntities is provided -> exclude entities matching those keys
8
+ * 2. If excludedTypes is provided -> exclude entities matching those types
9
+ * 3. Multiple conditions are combined with AND
10
+ * 4. Returns undefined if no exclusions are specified
11
+ */
12
+ export declare function buildEntitiesExclusionFilter(excludedTypes?: string[], excludedEntities?: string[], tableAlias?: string): SQL | undefined;
13
+ //# sourceMappingURL=build-entities-exclusion-filter.d.ts.map
@@ -0,0 +1 @@
1
+ import{sql as n}from"drizzle-orm";function u(r,i,t){const o=[],f=t?n.raw(`${t}.type`):n.raw("type"),p=t?n.raw(`${t}.key`):n.raw("key");if(i?.length&&o.push(n`${p} NOT IN (${n.join(i.map(e=>n`${e}`),n`, `)})`),r?.length)for(const e of r)o.push(n`${f} != ${e}`);if(o.length!==0)return o.length===1?o[0]:n`(${n.join(o,n` AND `)})`}export{u as buildEntitiesExclusionFilter};
@@ -0,0 +1,31 @@
1
+ import { type SQL } from 'drizzle-orm';
2
+ /**
3
+ * Builds an RBAC filter condition as a drizzle SQL object.
4
+ * Use this with drizzle query builder.
5
+ *
6
+ * RBAC filter logic:
7
+ * 1. If rbac_teams is NULL -> allow access (no restrictions)
8
+ * 2. If rbac_teams is empty array -> allow access (no restrictions)
9
+ * 3. Otherwise, check if any of user's teams match using json_each
10
+ */
11
+ export declare function buildRbacFilter(rbacTeams?: string[], tableAlias?: string): SQL | undefined;
12
+ /**
13
+ * Builds an RBAC filter condition as a raw SQL string.
14
+ * Use this when constructing raw SQL queries with sql.raw().
15
+ *
16
+ * Same RBAC filter logic as buildRbacFilter but returns a string for raw SQL usage.
17
+ */
18
+ export declare function buildRbacFilterRaw(rbacTeams?: string[], tableAlias?: string): string | null;
19
+ /**
20
+ * Builds RBAC JOIN and filter for subqueries that need to join with combined_target_attributes CTE.
21
+ * Returns both the LEFT JOIN clause and the AND filter condition for use in raw SQL subqueries.
22
+ *
23
+ * @param rbacTeams - Array of team names the user belongs to
24
+ * @param entityKeyAlias - The alias for the entity key column (e.g., 'd.key', 'o.key')
25
+ * @returns Object with `join` and `filter` SQL fragments (both empty if no RBAC teams)
26
+ */
27
+ export declare function buildSubqueryRbacJoinAndFilter(rbacTeams?: string[], entityKeyAlias?: string): {
28
+ join: SQL;
29
+ filter: SQL;
30
+ };
31
+ //# sourceMappingURL=build-rbac-filter.d.ts.map
@@ -0,0 +1,9 @@
1
+ import{sql as t}from"drizzle-orm";function E(e,r){if(!e||e.length===0)return;const n=r?`${r}.rbac_teams`:"rbac_teams",a=t.join(e.map(o=>t`${o}`),t`, `);return t`(${t.raw(n)} IS NULL OR json_array_length(COALESCE(${t.raw(n)}, '[]')) = 0 OR EXISTS (SELECT 1 FROM json_each(COALESCE(${t.raw(n)}, '[]')) AS t WHERE t.value IN (${a})))`}function s(e,r){if(!e||e.length===0)return null;const n=r?`${r}.rbac_teams`:"rbac_teams",a=e.map(o=>`'${o.replace(/'/g,"''")}'`).join(", ");return`(${n} IS NULL OR json_array_length(COALESCE(${n}, '[]')) = 0 OR EXISTS (SELECT 1 FROM json_each(COALESCE(${n}, '[]')) AS t WHERE t.value IN (${a})))`}function u(e,r){if(!(e&&e.length>0))return{join:t``,filter:t``};const a=t`LEFT JOIN combined_target_attributes attr ON attr.entity_key = ${t.raw(r??"key")}`,o=t`
2
+ AND (
3
+ attr.rbac_teams IS NULL
4
+ OR json_array_length(attr.rbac_teams) = 0
5
+ OR EXISTS (
6
+ SELECT 1 FROM json_each(attr.rbac_teams) as t
7
+ WHERE t.value IN (${t.join(e.map(i=>t`${i}`),t`, `)})
8
+ )
9
+ )`;return{join:a,filter:o}}export{E as buildRbacFilter,s as buildRbacFilterRaw,u as buildSubqueryRbacJoinAndFilter};
@@ -1 +1 @@
1
- import{FileHashStatus as c}from"../../../../../persistence/file-hashes/types.js";import{promiseMapLimit as E}from"../../../../../utils/async/promise-map-limit.js";import{OPERATORS as u}from"../../../../../providers/database/pagination/constants.js";import{VERSION_NOT_SPECIFIED as f}from"@redocly/theme/core/constants";import{getRbacTeamsListForResource as g}from"../../../../../utils/rbac.js";import{resolveEntityVersion as v}from"../../../utils/resolve-entity-version.js";import{catalogDataCollector as h}from"../../../utils/catalog-data-collector.js";const y=3;class _{type="api-description";specType;fileType;actions;context;catalogEntitiesService;fileHashManager;entitySources={};#e;constructor(t,e){this.specType=t,this.fileType=e.fileType,this.actions=e.actions,this.context=e.context,this.catalogEntitiesService=e.catalogEntitiesService,this.fileHashManager=e.fileHashManager,this.#e=e.shouldCalculateEntities??!1}async extract(){const t=await this.loadApiDescriptions();await this.fileHashManager.markAllAsOutdated(this.fileType),t.length&&h.addExtractor(this.specType),await E(t,y,async e=>{try{if(e.isVirtual||!e.hash)return;if(!((await this.fileHashManager.getByPath(e.realRelativePath))?.hash!==e.hash||this.#e||process.env.FORCE_CATALOG_CACHE_REVALIDATE==="true")){h.increaseSkippedFilesCount(),await this.fileHashManager.upsert(this.fileType,e.realRelativePath,e.hash,c.UP_TO_DATE);return}const a=await this.catalogEntitiesService.getEntityKeysAndVersionsBySourceFile(e.realRelativePath);process.env.FORCE_CATALOG_CACHE_REVALIDATE==="true"&&(await this.catalogEntitiesService.deleteEntitiesInLocalDatabase({field:"key",operator:"in",value:Array.from(a).map(s=>s.split(":")[0])}),a.clear());const o=new Date().toISOString(),n=this.#a(e);await this.processApiDescription(e,o,n,a),await this.#i(a,e.realRelativePath,o,e.hash),h.increaseProcessedFilesCount(),await this.fileHashManager.upsert(this.fileType,e.realRelativePath,e.hash,c.UP_TO_DATE)}catch(i){this.context.logger.warn(`Error extracting entities from ${this.specType} description: ${e.realRelativePath}`),this.context.logger.warn(i)}}),await this.#t()}#t=async()=>{const t=await this.fileHashManager.getAllOutdated(this.fileType);if(!t||t.length===0)return;const e=await this.catalogEntitiesService.getEntities({limit:1e3,filter:{op:"AND",conditions:[{field:"source_file",operator:"in",value:t.map(({filePath:i})=>i)},{field:"is_current",operator:"equal",value:!0}]}});e&&e.items.length>0&&await this.catalogEntitiesService.deleteEntitiesInLocalDatabase({field:"key",operator:"in",value:e.items.map(({key:i})=>i)}),await this.fileHashManager.deleteFileHashes({op:"AND",conditions:[{field:"file_type",operator:"equal",value:this.fileType},{field:"status",operator:"equal",value:c.OUTDATED}]})};#i=async(t,e,i,r)=>{if(t.size===0||process.env.FORCE_CATALOG_CACHE_REVALIDATE==="true")return;const a=Array.from(t).map(s=>{const[l,p]=s.split(":");return{key:l,version:p}}),o=Array.from(new Set(a.map(({key:s})=>s))),n=a.map(({key:s,version:l})=>({op:u.AND,conditions:[{field:"key",operator:"equal",value:s},{field:"version",operator:"equal",value:l}]}));await this.catalogEntitiesService.softDeleteEntitiesInLocalDatabase({revision:i,fileHash:r,filter:{op:"AND",conditions:[{field:"key",operator:"in",value:o},{op:u.OR,conditions:n},{field:"source",operator:"equal",value:"file"},{field:"source_file",operator:"equal",value:e}]}})};#a(t){const e=t.document?.info?.version??t.definition?.info?.version??null,i=v(e,t.realRelativePath);return i.success?i.version??f:f}getRbacTeamsForDefinition(t){const e=this.actions.getConfig().rbac,i=this.actions.getRouteByFsPath(t);return g(i||{fsPath:t},e||{})}}export{_ as BaseApiEntitiesExtractor};
1
+ import{FileHashStatus as c}from"../../../../../persistence/file-hashes/types.js";import{promiseMapLimit as E}from"../../../../../utils/async/promise-map-limit.js";import{OPERATORS as u}from"../../../../../providers/database/pagination/constants.js";import{VERSION_NOT_SPECIFIED as f}from"@redocly/theme/core/constants";import{getRbacTeamsListForResource as g}from"../../../../../utils/rbac.js";import{resolveEntityVersion as v}from"../../../utils/resolve-entity-version.js";import{catalogDataCollector as h}from"../../../utils/catalog-data-collector.js";const m=3;class _{type="api-description";specType;fileType;actions;context;catalogEntitiesService;fileHashManager;entitySources={};#e;constructor(t,e){this.specType=t,this.fileType=e.fileType,this.actions=e.actions,this.context=e.context,this.catalogEntitiesService=e.catalogEntitiesService,this.fileHashManager=e.fileHashManager,this.#e=e.shouldCalculateEntities??!1}async extract(){const t=await this.loadApiDescriptions();await this.fileHashManager.markAllAsOutdated(this.fileType),t.length&&h.addExtractor(this.specType),await E(t,m,async e=>{try{if(e.isVirtual||!e.hash)return;if(!((await this.fileHashManager.getByPath(e.realRelativePath))?.hash!==e.hash||this.#e||process.env.FORCE_CATALOG_CACHE_REVALIDATE==="true")){h.increaseSkippedFilesCount(),await this.fileHashManager.upsert(this.fileType,e.realRelativePath,e.hash,c.UP_TO_DATE);return}const a=await this.catalogEntitiesService.getEntityKeysAndVersionsBySourceFile(e.realRelativePath);process.env.FORCE_CATALOG_CACHE_REVALIDATE==="true"&&(await this.catalogEntitiesService.deleteEntitiesInLocalDatabase({field:"key",operator:"in",value:Array.from(a).map(s=>s.split(":")[0])}),a.clear());const o=new Date().toISOString(),n=this.#a(e);await this.processApiDescription(e,o,n,a),await this.#i(a,e.realRelativePath,o,e.hash),h.increaseProcessedFilesCount(),await this.fileHashManager.upsert(this.fileType,e.realRelativePath,e.hash,c.UP_TO_DATE)}catch(i){this.context.logger.warn(`Error extracting entities from ${this.specType} description: ${e.realRelativePath}`),this.context.logger.warn(i)}}),await this.#t()}#t=async()=>{const t=await this.fileHashManager.getAllOutdated(this.fileType);if(!t||t.length===0)return;const e=await this.catalogEntitiesService.getEntities({paginationParams:{limit:1e3,filter:{op:"AND",conditions:[{field:"source_file",operator:"in",value:t.map(({filePath:i})=>i)},{field:"is_current",operator:"equal",value:!0}]}}});e&&e.items.length>0&&await this.catalogEntitiesService.deleteEntitiesInLocalDatabase({field:"key",operator:"in",value:e.items.map(({key:i})=>i)}),await this.fileHashManager.deleteFileHashes({op:"AND",conditions:[{field:"file_type",operator:"equal",value:this.fileType},{field:"status",operator:"equal",value:c.OUTDATED}]})};#i=async(t,e,i,r)=>{if(t.size===0||process.env.FORCE_CATALOG_CACHE_REVALIDATE==="true")return;const a=Array.from(t).map(s=>{const[l,p]=s.split(":");return{key:l,version:p}}),o=Array.from(new Set(a.map(({key:s})=>s))),n=a.map(({key:s,version:l})=>({op:u.AND,conditions:[{field:"key",operator:"equal",value:s},{field:"version",operator:"equal",value:l}]}));await this.catalogEntitiesService.softDeleteEntitiesInLocalDatabase({revision:i,fileHash:r,filter:{op:"AND",conditions:[{field:"key",operator:"in",value:o},{op:u.OR,conditions:n},{field:"source",operator:"equal",value:"file"},{field:"source_file",operator:"equal",value:e}]}})};#a(t){const e=t.document?.info?.version??t.definition?.info?.version??null,i=v(e,t.realRelativePath);return i.success?i.version??f:f}getRbacTeamsForDefinition(t){const e=this.actions.getConfig().rbac,i=this.actions.getRouteByFsPath(t);return g(i||{fsPath:t},e||{})}}export{_ as BaseApiEntitiesExtractor};
@@ -1 +1 @@
1
- import{REDOCLY_TEAMS_RBAC as g}from"@redocly/config";import{removeLeadingSlash as b}from"@redocly/theme/core/utils";import{toKebabCase as f}from"../../../../../../utils/string/to-kebab-case.js";import{promiseMapLimit as $}from"../../../../../utils/async/promise-map-limit.js";import{promiseMapLimitWithStatus as N}from"../../../../../utils/async/promise-map-limit-with-status.js";import{extractTeamsFromScopeItems as E}from"../../../../../utils/rbac.js";import{removeMarkdocTags as R}from"../../../../markdown/markdoc/helpers/remove-markdoc-tags.js";import{BaseApiEntitiesExtractor as T}from"./base.js";import{OpenapiSchemaResolver as w}from"../../../utils/openapi-schema-resolver.js";import{extractPartsFromComponentsRef as v}from"../../../utils/extract-parts-from-components-ref.js";import{validateEntityRelation as x}from"../../../entities/validate-entity.js";import{isValidTagName as S}from"../../../utils/is-valid-tag-name.js";const A=["get","post","put","delete","patch"],O=15,d=15;class V extends T{#e;constructor(e){super("openapi",e),this.#e=e.context.logger}async loadApiDescriptions(){return await this.actions.loadOpenApiDefinitions(this.context)}mapApiDescriptionToEntity(e,o){const s=b(e.realRelativePath),n=e.definition.info.title,t=s.replace(/\.[^.]+$/,""),a=f(t.replace(/[\\/]/g,"-")),r=e.definition["x-redocly-catalog-key"],i=typeof r=="string"&&r.trim()?f(r.trim()):a;return{type:this.type,key:i,title:n,summary:R(e.definition.info.description),tags:e.definition.tags?.filter(c=>c.name&&S(c.name)).map(c=>c.name),metadata:{specType:this.specType,descriptionFile:s},version:o}}async processApiDescription(e,o,s,n){if(w.clearSchemaCache(),!e?.definition?.paths)return;const t=this.getRbacTeamsForDefinition(e.relativePath),a=this.mapApiDescriptionToEntity(e,s);await this.catalogEntitiesService.createEntityInLocalDatabase({entity:a,sourceFile:e.realRelativePath,fileHash:e.hash,isRootEntity:!0,revision:o,rbacTeams:t}),n.delete(`${a.key}:${s}`),await this.#n(e,a.key,a.version,s,o,n,t);const r=this.#s(e.definition.paths);if(r.length!==0){const c=(await N(r,O,async({operation:h,path:m,method:l})=>{if(!h.operationId)return;const y=await this.#r(h,m,l,e,a.key,a.version,o,t);y&&n.delete(`${y}:${s}`)},this.#e)).count.failed;c>0&&this.#e.warn(`Extraction completed with ${c} failures out of ${r.length} operations for ${a.key}`)}}#s(e){return Object.entries(e).flatMap(([o,s])=>A.filter(n=>s[n]).map(n=>{const t=s[n];if(!t)throw new Error(`Operation not found for method ${n} on path ${o}`);return{operation:t,path:o,method:n.toUpperCase()}}))}async#n(e,o,s,n,t,a,r){const i=e.definition.components?.schemas;if(!i)return;const c=Object.entries(i);c.length!==0&&await $(c,d,async([h,m])=>{const l=await this.#o({schemaName:h,schema:m,description:e,parentKey:o,parentVersion:s,parentRevision:t,rbacTeams:r});a.delete(`${l.entityKey}:${n}`),l.result==="created"&&await this.catalogEntitiesService.createEntityRelationInLocalDatabase({sourceKey:o,sourceVersion:s,sourceRevision:t,type:"uses",targetKey:l.entityKey,targetVersion:s,targetRevision:t,fileHash:e.hash})})}async#o({schemaName:e,schema:o,description:s,parentKey:n,parentVersion:t,parentRevision:a,rbacTeams:r}){const i=w.resolveSchemaRefs(o,s),c=JSON.stringify(i),h=i[g]?E(i[g]):r,m={type:"data-schema",key:`${n}-${f(e)}`,title:e,summary:i.description??i.title??null,tags:[],metadata:{specType:this.specType,schema:c},version:t};return await this.catalogEntitiesService.createEntityInLocalDatabase({entity:m,sourceFile:s.realRelativePath,fileHash:s.hash,revision:a,rbacTeams:h})}async#r(e,o,s,n,t,a,r,i){const c=await Promise.all([this.#m(e.requestBody??null,n),this.#h(e.responses??{},n)]),h=c[0].schemaNames,m=c[1].schemaNames;c[0].errors.length>0&&this.#e.warn(`Schema extraction errors for operation ${e.operationId}: ${c[0].errors.join(", ")}`),c[1].errors.length>0&&this.#e.warn(`Response schema extraction errors for operation ${e.operationId}: ${c[1].errors.join(", ")}`);const l=h.map(p=>`${t}-${f(p)}`),y=m.map(p=>`${t}-${f(p)}`),u=await this.#p({path:o,method:s,operation:e,requestBodySchemasKeys:l,responseSchemasKeys:y,description:n,parentKey:t,parentVersion:a,parentRevision:r,rbacTeams:i});return u.result==="created"&&await Promise.all([this.#i(h,e.operationId??"",n.hash,t,a,r),this.#c(m,e.operationId??"",n.hash,t,a,r),this.#f({apiOperationKey:u.entityKey,operationRelations:e["x-catalog-relations"],descriptionUniqueKey:t,description:n,parentVersion:a,parentRevision:r})]),u.entityKey??null}async#i(e,o,s,n,t,a){return e.length===0||!o?[]:await this.#a(e,o,s,n,t,a)}async#c(e,o,s,n,t,a){return e.length===0||!o?[]:await this.#a(e,o,s,n,t,a)}#m(e,o){const s=[],n=[];try{if(e&&"content"in e&&e.content)for(const t of Object.values(e.content)){if(!t.schema?.$ref)continue;const a=this.#t(t.schema.$ref);a?.componentType==="schemas"&&a.componentName&&s.push(a.componentName)}if(e&&e.$ref){const t=this.#t(e.$ref);if(t?.componentType==="requestBodies"&&t.componentName){const a=o.definition.components?.requestBodies?.[t.componentName];if(a&&"content"in a)for(const r of Object.values(a.content??{})){if(!r.schema?.$ref)continue;const i=this.#t(r.schema.$ref);i?.componentType==="schemas"&&i.componentName&&s.push(i.componentName)}}}}catch(t){n.push(`Failed to extract request body schemas: ${t instanceof Error?t.message:"Unknown error"}`)}return{schemaNames:[...new Set(s)],errors:n}}#h(e,o){const s=[],n=[];try{for(const t of Object.values(e)){if(t&&"content"in t&&t.content)for(const a of Object.values(t.content)){if(!a.schema?.$ref)continue;const r=this.#t(a.schema.$ref);r?.componentType==="schemas"&&r.componentName&&s.push(r.componentName)}if(t&&t.$ref){const a=this.#t(t.$ref);if(a?.componentType==="responses"&&a.componentName){const r=o.definition.components?.responses?.[a.componentName];if(r&&"content"in r)for(const i of Object.values(r.content??{})){if(!i.schema?.$ref)continue;const c=this.#t(i.schema.$ref);c?.componentType==="schemas"&&c.componentName&&s.push(c.componentName)}}}}}catch(t){n.push(`Failed to extract response schemas: ${t instanceof Error?t.message:"Unknown error"}`)}return{schemaNames:[...new Set(s)],errors:n}}#t(e){try{return v(e)}catch{return{componentType:"",componentName:null}}}async#a(e,o,s,n,t,a){return e.length===0?[]:await $(e,d,async r=>await this.#l({operationId:o,schemaName:r,descriptionHash:s,parentKey:n,parentVersion:t,parentRevision:a}))}async#l({operationId:e,schemaName:o,descriptionHash:s,parentKey:n,parentVersion:t,parentRevision:a}){const r=`${n}-${f(o)}`;try{return await this.catalogEntitiesService.createEntityRelationInLocalDatabase({sourceKey:`${n}-${f(e)}`,type:"uses",targetKey:r,fileHash:s,sourceVersion:t,targetVersion:t,sourceRevision:a,targetRevision:a}),r}catch(i){throw this.#e.error(`Failed to create relation between operation ${e} and schema ${o}: ${i instanceof Error?i.message:"Unknown error"}`),i}}async#p({path:e,method:o,operation:s,requestBodySchemasKeys:n,responseSchemasKeys:t,description:a,parentKey:r,parentVersion:i,parentRevision:c,rbacTeams:h}){const m=`${s.operationId}`,l=`${r}-${f(m)}`,y=s[g]?E(s[g]):h,u={type:"api-operation",key:l,title:m,summary:R(s.summary),tags:s.tags?.filter(p=>S(p)),metadata:{path:e,method:o,payload:n,responses:t},version:i};try{return await this.catalogEntitiesService.createEntityInLocalDatabase({entity:u,sourceFile:a.realRelativePath,fileHash:a.hash,revision:c,rbacTeams:y})}catch(p){throw this.#e.error(`Failed to create API operation entity for ${m}: ${p instanceof Error?p.message:"Unknown error"}`),p}}async#f({apiOperationKey:e,operationRelations:o,descriptionUniqueKey:s,description:n,parentVersion:t,parentRevision:a}){try{await this.catalogEntitiesService.createEntityRelationInLocalDatabase({sourceKey:e,type:"partOf",targetKey:s,fileHash:n.hash,sourceVersion:t,targetVersion:t,sourceRevision:a,targetRevision:a}),await this.#y(e,o,t,a)}catch(r){this.#e.error(`Failed to create API operation relations for ${e}: ${r instanceof Error?r.message:"Unknown error"}`)}}async#y(e,o,s,n){o?.length&&await $(o,O,async t=>{try{x(t),await this.catalogEntitiesService.createEntityRelationInLocalDatabase({sourceKey:e,type:t.type,targetKey:t.key,sourceVersion:s,targetVersion:"",sourceRevision:n,targetRevision:""})}catch(a){this.context.logger.warn(`Error creating entity relation for operation ${e} based on custom property: ${a instanceof Error?a.message:"Unknown error"}`)}})}}export{V as OpenApiEntitiesExtractor};
1
+ import{REDOCLY_TEAMS_RBAC as $}from"@redocly/config";import{removeLeadingSlash as x}from"@redocly/theme/core/utils";import{toKebabCase as p}from"../../../../../../utils/string/to-kebab-case.js";import{promiseMapLimit as E}from"../../../../../utils/async/promise-map-limit.js";import{promiseMapLimitWithStatus as T}from"../../../../../utils/async/promise-map-limit-with-status.js";import{extractTeamsFromScopeItems as R}from"../../../../../utils/rbac.js";import{removeMarkdocTags as w}from"../../../../markdown/markdoc/helpers/remove-markdoc-tags.js";import{BaseApiEntitiesExtractor as N}from"./base.js";import{OpenapiSchemaResolver as S}from"../../../utils/openapi-schema-resolver.js";import{extractPartsFromComponentsRef as v}from"../../../utils/extract-parts-from-components-ref.js";import{validateEntityRelation as A}from"../../../entities/validate-entity.js";import{isValidTagName as O}from"../../../utils/is-valid-tag-name.js";const D=["get","post","put","delete","patch"],b=15,d=15;class U extends N{#e;constructor(e){super("openapi",e),this.#e=e.context.logger}async loadApiDescriptions(){return await this.actions.loadOpenApiDefinitions(this.context)}mapApiDescriptionToEntity(e,o){const s=x(e.realRelativePath),n=e.definition.info.title,t=s.replace(/\.[^.]+$/,""),a=p(t.replace(/[\\/]/g,"-")),r=e.definition["x-redocly-catalog-key"],i=typeof r=="string"&&r.trim()?p(r.trim()):a;return{type:this.type,key:i,title:n,summary:w(e.definition.info.description),tags:e.definition.tags?.filter(c=>c.name&&O(c.name)).map(c=>c.name),metadata:{specType:this.specType,descriptionFile:s},version:o}}async processApiDescription(e,o,s,n){if(S.clearSchemaCache(),!e?.definition?.paths)return;const t=this.getRbacTeamsForDefinition(e.relativePath),a=this.mapApiDescriptionToEntity(e,s);await this.catalogEntitiesService.createEntityInLocalDatabase({entity:a,sourceFile:e.realRelativePath,fileHash:e.hash,isRootEntity:!0,revision:o,rbacTeams:t}),n.delete(`${a.key}:${s}`),await this.#n(e,a.key,a.version,s,o,n,t);const r=this.#s(e.definition.paths);if(r.length!==0){const c=(await T(r,b,async({operation:h,path:m,method:l})=>{if(!h.operationId)return;const f=await this.#r(h,m,l,e,a.key,a.version,o,t);f&&n.delete(`${f}:${s}`)},this.#e)).count.failed;c>0&&this.#e.warn(`Extraction completed with ${c} failures out of ${r.length} operations for ${a.key}`)}}#s(e){return Object.entries(e).flatMap(([o,s])=>D.filter(n=>s[n]).map(n=>{const t=s[n];if(!t)throw new Error(`Operation not found for method ${n} on path ${o}`);return{operation:t,path:o,method:n.toUpperCase()}}))}async#n(e,o,s,n,t,a,r){const i=e.definition.components?.schemas;if(!i)return;const c=Object.entries(i);c.length!==0&&await E(c,d,async([h,m])=>{const l=await this.#o({schemaName:h,schema:m,description:e,parentKey:o,parentVersion:s,parentRevision:t,rbacTeams:r});a.delete(`${l.entityKey}:${n}`),l.result==="created"&&await this.catalogEntitiesService.createEntityRelationInLocalDatabase({sourceKey:o,sourceVersion:s,sourceRevision:t,type:"uses",targetKey:l.entityKey,targetVersion:s,targetRevision:t,fileHash:e.hash})})}async#o({schemaName:e,schema:o,description:s,parentKey:n,parentVersion:t,parentRevision:a,rbacTeams:r}){const i=S.resolveSchemaRefs(o,s),c=JSON.stringify(i),h=i[$]?R(i[$]):r,m=R(i["x-rbac"]),l={type:"data-schema",key:`${n}-${p(e)}`,title:e,summary:i.description??i.title??null,tags:[],metadata:{specType:this.specType,schema:c},version:t};return await this.catalogEntitiesService.createEntityInLocalDatabase({entity:l,sourceFile:s.realRelativePath,fileHash:s.hash,revision:a,rbacTeams:m.length?m:h})}async#r(e,o,s,n,t,a,r,i){const c=await Promise.all([this.#m(e.requestBody??null,n),this.#h(e.responses??{},n)]),h=c[0].schemaNames,m=c[1].schemaNames;c[0].errors.length>0&&this.#e.warn(`Schema extraction errors for operation ${e.operationId}: ${c[0].errors.join(", ")}`),c[1].errors.length>0&&this.#e.warn(`Response schema extraction errors for operation ${e.operationId}: ${c[1].errors.join(", ")}`);const l=h.map(g=>`${t}-${p(g)}`),f=m.map(g=>`${t}-${p(g)}`),y=await this.#p({path:o,method:s,operation:e,requestBodySchemasKeys:l,responseSchemasKeys:f,description:n,parentKey:t,parentVersion:a,parentRevision:r,rbacTeams:i});return y.result==="created"&&await Promise.all([this.#i(h,e.operationId??"",n.hash,t,a,r),this.#c(m,e.operationId??"",n.hash,t,a,r),this.#f({apiOperationKey:y.entityKey,operationRelations:e["x-catalog-relations"],descriptionUniqueKey:t,description:n,parentVersion:a,parentRevision:r})]),y.entityKey??null}async#i(e,o,s,n,t,a){return e.length===0||!o?[]:await this.#a(e,o,s,n,t,a)}async#c(e,o,s,n,t,a){return e.length===0||!o?[]:await this.#a(e,o,s,n,t,a)}#m(e,o){const s=[],n=[];try{if(e&&"content"in e&&e.content)for(const t of Object.values(e.content)){if(!t.schema?.$ref)continue;const a=this.#t(t.schema.$ref);a?.componentType==="schemas"&&a.componentName&&s.push(a.componentName)}if(e&&e.$ref){const t=this.#t(e.$ref);if(t?.componentType==="requestBodies"&&t.componentName){const a=o.definition.components?.requestBodies?.[t.componentName];if(a&&"content"in a)for(const r of Object.values(a.content??{})){if(!r.schema?.$ref)continue;const i=this.#t(r.schema.$ref);i?.componentType==="schemas"&&i.componentName&&s.push(i.componentName)}}}}catch(t){n.push(`Failed to extract request body schemas: ${t instanceof Error?t.message:"Unknown error"}`)}return{schemaNames:[...new Set(s)],errors:n}}#h(e,o){const s=[],n=[];try{for(const t of Object.values(e)){if(t&&"content"in t&&t.content)for(const a of Object.values(t.content)){if(!a.schema?.$ref)continue;const r=this.#t(a.schema.$ref);r?.componentType==="schemas"&&r.componentName&&s.push(r.componentName)}if(t&&t.$ref){const a=this.#t(t.$ref);if(a?.componentType==="responses"&&a.componentName){const r=o.definition.components?.responses?.[a.componentName];if(r&&"content"in r)for(const i of Object.values(r.content??{})){if(!i.schema?.$ref)continue;const c=this.#t(i.schema.$ref);c?.componentType==="schemas"&&c.componentName&&s.push(c.componentName)}}}}}catch(t){n.push(`Failed to extract response schemas: ${t instanceof Error?t.message:"Unknown error"}`)}return{schemaNames:[...new Set(s)],errors:n}}#t(e){try{return v(e)}catch{return{componentType:"",componentName:null}}}async#a(e,o,s,n,t,a){return e.length===0?[]:await E(e,d,async r=>await this.#l({operationId:o,schemaName:r,descriptionHash:s,parentKey:n,parentVersion:t,parentRevision:a}))}async#l({operationId:e,schemaName:o,descriptionHash:s,parentKey:n,parentVersion:t,parentRevision:a}){const r=`${n}-${p(o)}`;try{return await this.catalogEntitiesService.createEntityRelationInLocalDatabase({sourceKey:`${n}-${p(e)}`,type:"uses",targetKey:r,fileHash:s,sourceVersion:t,targetVersion:t,sourceRevision:a,targetRevision:a}),r}catch(i){throw this.#e.error(`Failed to create relation between operation ${e} and schema ${o}: ${i instanceof Error?i.message:"Unknown error"}`),i}}async#p({path:e,method:o,operation:s,requestBodySchemasKeys:n,responseSchemasKeys:t,description:a,parentKey:r,parentVersion:i,parentRevision:c,rbacTeams:h}){const m=`${s.operationId}`,l=`${r}-${p(m)}`,f=s[$]?R(s[$]):h,y=R(s["x-rbac"]),g={type:"api-operation",key:l,title:m,summary:w(s.summary),tags:s.tags?.filter(u=>O(u)),metadata:{path:e,method:o,payload:n,responses:t},version:i};try{return await this.catalogEntitiesService.createEntityInLocalDatabase({entity:g,sourceFile:a.realRelativePath,fileHash:a.hash,revision:c,rbacTeams:y.length>0?y:f})}catch(u){throw this.#e.error(`Failed to create API operation entity for ${m}: ${u instanceof Error?u.message:"Unknown error"}`),u}}async#f({apiOperationKey:e,operationRelations:o,descriptionUniqueKey:s,description:n,parentVersion:t,parentRevision:a}){try{await this.catalogEntitiesService.createEntityRelationInLocalDatabase({sourceKey:e,type:"partOf",targetKey:s,fileHash:n.hash,sourceVersion:t,targetVersion:t,sourceRevision:a,targetRevision:a}),await this.#y(e,o,t,a)}catch(r){this.#e.error(`Failed to create API operation relations for ${e}: ${r instanceof Error?r.message:"Unknown error"}`)}}async#y(e,o,s,n){o?.length&&await E(o,b,async t=>{try{A(t),await this.catalogEntitiesService.createEntityRelationInLocalDatabase({sourceKey:e,type:t.type,targetKey:t.key,sourceVersion:s,targetVersion:"",sourceRevision:n,targetRevision:""})}catch(a){this.context.logger.warn(`Error creating entity relation for operation ${e} based on custom property: ${a instanceof Error?a.message:"Unknown error"}`)}})}}export{U as OpenApiEntitiesExtractor};
@@ -1 +1 @@
1
- import{CATALOG_ENTITIES_FILES_REGEX as E,ENTITY_SCHEMA_EXCLUDED_FOLDERS as d}from"../../../../constants/plugins/catalog-entities.js";import{FileHashStatus as l,FileType as a}from"../../../../persistence/file-hashes/types.js";import{OPERATORS as u}from"../../../../providers/database/pagination/constants.js";import{promiseMapLimit as h}from"../../../../utils/async/promise-map-limit.js";import{VERSION_NOT_SPECIFIED as m}from"@redocly/theme/core/constants";import{extractFileContent as p}from"../../entities/extract-file-content.js";import{resolveEntityVersion as g}from"../../utils/resolve-entity-version.js";import{parseAndValidateEntities as y}from"../../entities/validate-entity.js";import{catalogDataCollector as f}from"../../utils/catalog-data-collector.js";const I=15;class v{#t;#e;#i;#r;#s;constructor({fileHashManager:e,context:t,catalogEntitiesService:i,catalogConfig:o,shouldCalculateEntities:s}){this.#t=e,this.#e=t,this.#i=i,this.#r=o,this.#s=s}async extract(e){try{if(e&&this.#o(e)){await this.#n(e);return}await this.#t.markAllAsOutdated(a.ENTITY_DEFINITION);const i=this.#e.fs.scan(E).filter(({relativePath:o})=>this.#o(o));i.length&&f.addExtractor("fs"),await h(i,I,async({relativePath:o})=>{await this.#n(o)}),await this.#f()}catch(t){this.#e.logger.error("Error extracting entities.",t)}}#o=e=>!!(e.match(E)&&!d.some(t=>e.includes(t)));#n=async e=>{try{const t=await p(e,this.#e);if(!t){this.#e.logger.warn(`Error extracting content from ${e}.`);return}const i=await this.#t.computeFileHash(t);if(!((await this.#t.getByPath(e))?.hash!==i||this.#s||process.env.FORCE_CATALOG_CACHE_REVALIDATE==="true")){f.increaseSkippedFilesCount(),await this.#t.upsert(a.ENTITY_DEFINITION,e,i,l.UP_TO_DATE);return}const r=this.#a(t.entities,e);if(!r)return;const n=await this.#i.getEntityKeysAndVersionsBySourceFile(e);await this.#c(r,e,i,n),await this.#l(n,e),f.increaseProcessedFilesCount(),await this.#t.upsert(a.ENTITY_DEFINITION,e,i,l.UP_TO_DATE)}catch(t){this.#e.logger.warn(`Error processing file "${e}". ${t instanceof Error?t.message:String(t)}.`)}};#a=(e,t)=>{try{return y(e,this.#r)}catch(i){return this.#e.logger.warn(`Error validating entities in "${t}". ${i instanceof Error?i.message:String(i)}.`),null}};#c=async(e,t,i,o)=>{for(const s of e)try{const r=g(s.version,t);if(!r.success){this.#e.logger.warn(`Entity "${s.key}" in file "${t}" has conflicting versions: file version "${r.fileVersion}" differs from folder version "${r.folderVersion}". Entity will not be created.`);continue}const n={...s,version:r.version};if(await this.#i.createEntityInLocalDatabase({entity:n,sourceFile:t,fileHash:i}),s.key){const c=r.version??m;o.delete(`${s.key}:${c}`)}}catch(r){const n=s.key??"unknown";this.#e.logger.warn(`Error processing entity "${n}" from "${t}". ${r instanceof Error?r.message:String(r)}.`)}};#l=async(e,t)=>{if(e.size===0)return;const i=Array.from(e).map(r=>{const[n,c]=r.split(":");return{key:n,version:c}}),o=Array.from(new Set(i.map(({key:r})=>r))),s=i.map(({key:r,version:n})=>({op:u.AND,conditions:[{field:"key",operator:"equal",value:r},{field:"version",operator:"equal",value:n}]}));await this.#i.deleteEntitiesInLocalDatabase({op:"AND",conditions:[{field:"key",operator:"in",value:o},{op:u.OR,conditions:s},{field:"source",operator:"equal",value:"file"},{field:"source_file",operator:"equal",value:t}]})};#f=async()=>{const e=await this.#t.getAllOutdated(a.ENTITY_DEFINITION);if(!e||e.length===0)return;const t=await this.#i.getEntities({limit:1e3,filter:{op:"AND",conditions:[{field:"source_file",operator:"in",value:e.map(({filePath:i})=>i)},{field:"is_current",operator:"equal",value:!0}]}});!t||t.items.length===0||(await this.#i.deleteEntitiesInLocalDatabase({field:"key",operator:"in",value:t.items.map(({key:i})=>i)}),await this.#t.deleteFileHashes({op:"AND",conditions:[{field:"file_type",operator:"equal",value:a.ENTITY_DEFINITION},{field:"status",operator:"equal",value:l.OUTDATED}]}))}}export{v as FsEntitiesExtractor};
1
+ import{CATALOG_ENTITIES_FILES_REGEX as E,ENTITY_SCHEMA_EXCLUDED_FOLDERS as d}from"../../../../constants/plugins/catalog-entities.js";import{FileHashStatus as l,FileType as a}from"../../../../persistence/file-hashes/types.js";import{OPERATORS as u}from"../../../../providers/database/pagination/constants.js";import{promiseMapLimit as m}from"../../../../utils/async/promise-map-limit.js";import{VERSION_NOT_SPECIFIED as p}from"@redocly/theme/core/constants";import{extractFileContent as h}from"../../entities/extract-file-content.js";import{resolveEntityVersion as g}from"../../utils/resolve-entity-version.js";import{parseAndValidateEntities as y}from"../../entities/validate-entity.js";import{catalogDataCollector as f}from"../../utils/catalog-data-collector.js";const I=15;class v{#t;#e;#i;#r;#s;constructor({fileHashManager:e,context:t,catalogEntitiesService:i,catalogConfig:o,shouldCalculateEntities:s}){this.#t=e,this.#e=t,this.#i=i,this.#r=o,this.#s=s}async extract(e){try{if(e&&this.#o(e)){await this.#n(e);return}await this.#t.markAllAsOutdated(a.ENTITY_DEFINITION);const i=this.#e.fs.scan(E).filter(({relativePath:o})=>this.#o(o));i.length&&f.addExtractor("fs"),await m(i,I,async({relativePath:o})=>{await this.#n(o)}),await this.#f()}catch(t){this.#e.logger.error("Error extracting entities.",t)}}#o=e=>!!(e.match(E)&&!d.some(t=>e.includes(t)));#n=async e=>{try{const t=await h(e,this.#e);if(!t){this.#e.logger.warn(`Error extracting content from ${e}.`);return}const i=await this.#t.computeFileHash(t);if(!((await this.#t.getByPath(e))?.hash!==i||this.#s||process.env.FORCE_CATALOG_CACHE_REVALIDATE==="true")){f.increaseSkippedFilesCount(),await this.#t.upsert(a.ENTITY_DEFINITION,e,i,l.UP_TO_DATE);return}const r=this.#a(t.entities,e);if(!r)return;const n=await this.#i.getEntityKeysAndVersionsBySourceFile(e);await this.#c(r,e,i,n),await this.#l(n,e),f.increaseProcessedFilesCount(),await this.#t.upsert(a.ENTITY_DEFINITION,e,i,l.UP_TO_DATE)}catch(t){this.#e.logger.warn(`Error processing file "${e}". ${t instanceof Error?t.message:String(t)}.`)}};#a=(e,t)=>{try{return y(e,this.#r)}catch(i){return this.#e.logger.warn(`Error validating entities in "${t}". ${i instanceof Error?i.message:String(i)}.`),null}};#c=async(e,t,i,o)=>{for(const s of e)try{const r=g(s.version,t);if(!r.success){this.#e.logger.warn(`Entity "${s.key}" in file "${t}" has conflicting versions: file version "${r.fileVersion}" differs from folder version "${r.folderVersion}". Entity will not be created.`);continue}const n={...s,version:r.version};if(await this.#i.createEntityInLocalDatabase({entity:n,sourceFile:t,fileHash:i}),s.key){const c=r.version??p;o.delete(`${s.key}:${c}`)}}catch(r){const n=s.key??"unknown";this.#e.logger.warn(`Error processing entity "${n}" from "${t}". ${r instanceof Error?r.message:String(r)}.`)}};#l=async(e,t)=>{if(e.size===0)return;const i=Array.from(e).map(r=>{const[n,c]=r.split(":");return{key:n,version:c}}),o=Array.from(new Set(i.map(({key:r})=>r))),s=i.map(({key:r,version:n})=>({op:u.AND,conditions:[{field:"key",operator:"equal",value:r},{field:"version",operator:"equal",value:n}]}));await this.#i.deleteEntitiesInLocalDatabase({op:"AND",conditions:[{field:"key",operator:"in",value:o},{op:u.OR,conditions:s},{field:"source",operator:"equal",value:"file"},{field:"source_file",operator:"equal",value:t}]})};#f=async()=>{const e=await this.#t.getAllOutdated(a.ENTITY_DEFINITION);if(!e||e.length===0)return;const t=await this.#i.getEntities({paginationParams:{limit:1e3,filter:{op:"AND",conditions:[{field:"source_file",operator:"in",value:e.map(({filePath:i})=>i)},{field:"is_current",operator:"equal",value:!0}]}}});!t||t.items.length===0||(await this.#i.deleteEntitiesInLocalDatabase({field:"key",operator:"in",value:t.items.map(({key:i})=>i)}),await this.#t.deleteFileHashes({op:"AND",conditions:[{field:"file_type",operator:"equal",value:a.ENTITY_DEFINITION},{field:"status",operator:"equal",value:l.OUTDATED}]}))}}export{v as FsEntitiesExtractor};