From f438b94577093fd43c6013bcde51c680b200752d Mon Sep 17 00:00:00 2001 From: Jonas Carlsen Date: Fri, 13 Mar 2026 09:59:56 +0100 Subject: [PATCH] feat: article revision history --- package.json | 2 +- src/api/articleApi.ts | 12 +++++++++++- src/resolvers/articleResolvers.ts | 11 ++++++++++- src/schema.ts | 5 +++++ src/types/schema.d.ts | 19 +++++++++++++++++++ yarn.lock | 10 +++++----- 6 files changed, 51 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 66a91447..1720bb54 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "@graphql-codegen/typescript-resolvers": "^5.1.7", "@graphql-eslint/eslint-plugin": "^4.4.0", "@graphql-tools/mock": "^9.1.5", - "@ndla/types-backend": "^1.0.106", + "@ndla/types-backend": "^1.0.107", "@ndla/types-embed": "^5.0.21-alpha.0", "@ndla/types-taxonomy": "^1.0.50", "@types/compression": "^1.8.1", diff --git a/src/api/articleApi.ts b/src/api/articleApi.ts index 75561748..ce5f07b5 100644 --- a/src/api/articleApi.ts +++ b/src/api/articleApi.ts @@ -6,7 +6,7 @@ * */ -import { paths, ArticleV2DTO } from "@ndla/types-backend/article-api"; +import { paths, ArticleV2DTO, ArticleRevisionHistoryDTO } from "@ndla/types-backend/article-api"; import { ndlaUrl } from "../config"; import { GQLArticleTransformedContentArgs, @@ -212,3 +212,13 @@ export async function fetchRevisions(articleId: number, _: Context): Promise { + return await client + .GET("/article-api/v2/articles/{article_id}/revision-history", { + params: { + path: { article_id: articleId }, + }, + }) + .then(resolveJsonOATS); +} diff --git a/src/resolvers/articleResolvers.ts b/src/resolvers/articleResolvers.ts index 5e96143a..452a1d17 100644 --- a/src/resolvers/articleResolvers.ts +++ b/src/resolvers/articleResolvers.ts @@ -6,7 +6,7 @@ * */ -import { ArticleV2DTO } from "@ndla/types-backend/article-api"; +import { ArticleV2DTO, ArticleRevisionHistoryDTO } from "@ndla/types-backend/article-api"; import { ConceptSummaryDTO } from "@ndla/types-backend/concept-api"; import { fetchSubjectTopics, searchConcepts } from "../api"; import { @@ -16,6 +16,7 @@ import { fetchVisualElementEmbed, fetchRevisions, fetchArticle, + fetchRevisionHistory, } from "../api/articleApi"; import { coreElements, fetchCrossSubjectTopicsByCode, grepSearch } from "../api/searchApi"; import { ndlaUrl } from "../config"; @@ -26,6 +27,7 @@ import { GQLCrossSubjectElement, GQLImageMetaInformationV3, GQLQueryArticleArgs, + GQLQueryRevisionHistoryArgs, GQLQueryRevisionsArgs, GQLRelatedContent, GQLResourceEmbed, @@ -39,6 +41,13 @@ export const Query = { async revisions(_: any, { articleId }: GQLQueryRevisionsArgs, context: ContextWithLoaders): Promise { return fetchRevisions(articleId, context); }, + async revisionHistory( + _: any, + { id }: GQLQueryRevisionHistoryArgs, + context: ContextWithLoaders, + ): Promise { + return fetchRevisionHistory(id, context); + }, }; export const resolvers = { diff --git a/src/schema.ts b/src/schema.ts index ab419b5f..34ac26eb 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -708,6 +708,10 @@ export const typeDefs = gql` url: String! } + type ArticleRevisionHistory { + revision: [Article!]! + } + type Article { id: Int! revision: Int! @@ -1549,6 +1553,7 @@ export const typeDefs = gql` allMyNdlaResources(size: Int): [MyNdlaResource!]! recentlyFavoritedResources(size: Int): [MyNdlaResource!]! revisions(articleId: Int!): [Int!]! + revisionHistory(id: Int!): ArticleRevisionHistory personalData: MyNdlaPersonalData image(id: String!): ImageMetaInformationV3 examLockStatus: ConfigMetaBoolean! diff --git a/src/types/schema.d.ts b/src/types/schema.d.ts index 4c181a48..95084052 100644 --- a/src/types/schema.d.ts +++ b/src/types/schema.d.ts @@ -105,6 +105,11 @@ export type GQLArticleRequiredLibrary = { url: Scalars['String']['output']; }; +export type GQLArticleRevisionHistory = { + __typename?: 'ArticleRevisionHistory'; + revision: Array; +}; + export type GQLArticleSearchResult = GQLSearchResult & { __typename?: 'ArticleSearchResult'; context?: Maybe; @@ -1449,6 +1454,7 @@ export type GQLQuery = { resourceEmbed: GQLResourceEmbed; resourceEmbeds: GQLResourceEmbed; resourceTypes?: Maybe>; + revisionHistory?: Maybe; revisions: Array; search?: Maybe; searchWithoutPagination?: Maybe; @@ -1674,6 +1680,11 @@ export type GQLQueryResourceEmbedsArgs = { }; +export type GQLQueryRevisionHistoryArgs = { + id: Scalars['Int']['input']; +}; + + export type GQLQueryRevisionsArgs = { articleId: Scalars['Int']['input']; }; @@ -2300,6 +2311,7 @@ export type GQLResolversTypes = { Article: ResolverTypeWrapper; ArticleMetaData: ResolverTypeWrapper; ArticleRequiredLibrary: ResolverTypeWrapper; + ArticleRevisionHistory: ResolverTypeWrapper; ArticleSearchResult: ResolverTypeWrapper; Audio: ResolverTypeWrapper; AudioFile: ResolverTypeWrapper; @@ -2467,6 +2479,7 @@ export type GQLResolversParentTypes = { Article: GQLArticle; ArticleMetaData: GQLArticleMetaData; ArticleRequiredLibrary: GQLArticleRequiredLibrary; + ArticleRevisionHistory: GQLArticleRevisionHistory; ArticleSearchResult: GQLArticleSearchResult; Audio: GQLAudio; AudioFile: GQLAudioFile; @@ -2691,6 +2704,10 @@ export type GQLArticleRequiredLibraryResolvers; }; +export type GQLArticleRevisionHistoryResolvers = { + revision?: Resolver, ParentType, ContextType>; +}; + export type GQLArticleSearchResultResolvers = { context?: Resolver, ParentType, ContextType>; contexts?: Resolver, ParentType, ContextType>; @@ -3644,6 +3661,7 @@ export type GQLQueryResolvers>; resourceEmbeds?: Resolver>; resourceTypes?: Resolver>, ParentType, ContextType>; + revisionHistory?: Resolver, ParentType, ContextType, RequireFields>; revisions?: Resolver, ParentType, ContextType, RequireFields>; search?: Resolver, ParentType, ContextType, Partial>; searchWithoutPagination?: Resolver, ParentType, ContextType, Partial>; @@ -4001,6 +4019,7 @@ export type GQLResolvers = { Article?: GQLArticleResolvers; ArticleMetaData?: GQLArticleMetaDataResolvers; ArticleRequiredLibrary?: GQLArticleRequiredLibraryResolvers; + ArticleRevisionHistory?: GQLArticleRevisionHistoryResolvers; ArticleSearchResult?: GQLArticleSearchResultResolvers; Audio?: GQLAudioResolvers; AudioFile?: GQLAudioFileResolvers; diff --git a/yarn.lock b/yarn.lock index e6d466e2..74e5e03f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1781,10 +1781,10 @@ __metadata: languageName: node linkType: hard -"@ndla/types-backend@npm:^1.0.106": - version: 1.0.106 - resolution: "@ndla/types-backend@npm:1.0.106" - checksum: 10c0/c951aa4743611e148e1a413f475bce1347cdc89585cdb6cc77c2d0fc4bb1f952c96642207039007e8d4d3a9a6c7f7582bddc16670a0ea03dbc4580578cb664f3 +"@ndla/types-backend@npm:^1.0.107": + version: 1.0.107 + resolution: "@ndla/types-backend@npm:1.0.107" + checksum: 10c0/9c24f97311e319eef76d46d18127734228df7b42bffa531d024f1fe6fec72fa38394e1b9f890bcc82fa709b5d4d0ec12b8008ebb909b274935d991d6cfcbdb98 languageName: node linkType: hard @@ -6472,7 +6472,7 @@ __metadata: "@graphql-tools/mock": "npm:^9.1.5" "@graphql-tools/schema": "npm:^10.0.31" "@ndla/licenses": "npm:^9.0.3" - "@ndla/types-backend": "npm:^1.0.106" + "@ndla/types-backend": "npm:^1.0.107" "@ndla/types-embed": "npm:^5.0.21-alpha.0" "@ndla/types-taxonomy": "npm:^1.0.50" "@types/compression": "npm:^1.8.1"