diff --git a/src/graphql/models/Ydoc.js b/src/graphql/models/Ydoc.js new file mode 100644 index 00000000..b853dea4 --- /dev/null +++ b/src/graphql/models/Ydoc.js @@ -0,0 +1,24 @@ +import { GraphQLObjectType, GraphQLString, GraphQLList } from 'graphql'; + +import YdocVersion from './YdocVersion'; + +const Ydoc = new GraphQLObjectType({ + name: 'Ydoc', + fields: () => ({ + data: { + type: GraphQLString, + // https://www.elastic.co/guide/en/elasticsearch/reference/current/binary.html + description: 'Binary that stores as base64 encoded string', + }, + versions: { + type: new GraphQLList(YdocVersion), + description: + 'Ydoc snapshots which are used to restore to specific version', + resolve: async ({ versions }) => { + return versions || []; + }, + }, + }), +}); + +export default Ydoc; diff --git a/src/graphql/models/YdocVersion.js b/src/graphql/models/YdocVersion.js new file mode 100644 index 00000000..677615ab --- /dev/null +++ b/src/graphql/models/YdocVersion.js @@ -0,0 +1,13 @@ +import { GraphQLObjectType, GraphQLString } from 'graphql'; + +export default new GraphQLObjectType({ + name: 'YdocVersion', + fields: () => ({ + createdAt: { type: GraphQLString }, + snapshot: { + type: GraphQLString, + // https://www.elastic.co/guide/en/elasticsearch/reference/current/binary.html + description: 'Binary that stores as base64 encoded string', + }, + }), +}); diff --git a/src/graphql/queries/GetYdoc.js b/src/graphql/queries/GetYdoc.js new file mode 100644 index 00000000..b2ddad6d --- /dev/null +++ b/src/graphql/queries/GetYdoc.js @@ -0,0 +1,12 @@ +import { GraphQLString, GraphQLNonNull } from 'graphql'; + +import Ydoc from 'graphql/models/Ydoc'; + +export default { + type: Ydoc, + args: { + id: { type: new GraphQLNonNull(GraphQLString) }, + }, + resolve: async (rootValue, { id }, { loaders }) => + loaders.docLoader.load({ index: 'ydocs', id }), +}; diff --git a/src/graphql/queries/__fixtures__/GetYdoc.js b/src/graphql/queries/__fixtures__/GetYdoc.js new file mode 100644 index 00000000..5d440c00 --- /dev/null +++ b/src/graphql/queries/__fixtures__/GetYdoc.js @@ -0,0 +1,26 @@ +export default { + '/ydocs/doc/foo': { + data: 'mock data1', + versions: [ + { + createdAt: '2023-09-07T08:14:14.005Z', + snapshot: 'mock snapshot1', + }, + { + createdAt: '2023-09-07T08:16:45.613Z', + snapshot: 'mock snapshot2', + }, + { + createdAt: '2023-09-07T08:18:32.467Z', + snapshot: 'mock snapshot3', + }, + { + createdAt: '2023-09-07T08:18:49.500Z', + snapshot: 'mock snapshot4', + }, + ], + }, + '/ydocs/doc/foo2': { + data: 'mock data2', + }, +}; diff --git a/src/graphql/queries/__tests__/GetYdoc.js b/src/graphql/queries/__tests__/GetYdoc.js new file mode 100644 index 00000000..6d2eecfe --- /dev/null +++ b/src/graphql/queries/__tests__/GetYdoc.js @@ -0,0 +1,40 @@ +import gql from 'util/GraphQL'; +import { loadFixtures, unloadFixtures } from 'util/fixtures'; +import fixtures from '../__fixtures__/GetYdoc'; + +describe('GetYdoc', () => { + beforeAll(() => loadFixtures(fixtures)); + afterAll(() => unloadFixtures(fixtures)); + + it('should get the specified doc', async () => { + expect( + await gql` + { + GetYdoc(id: "foo") { + data + versions { + createdAt + snapshot + } + } + } + `({}, { user: { id: 'test', appId: 'test' } }) + ).toMatchSnapshot(); + }); + + it('should return empty versions when there is none', async () => { + expect( + await gql` + { + GetYdoc(id: "foo2") { + data + versions { + createdAt + snapshot + } + } + } + `({}, { user: { id: 'test', appId: 'test' } }) + ).toMatchSnapshot(); + }); +}); diff --git a/src/graphql/queries/__tests__/__snapshots__/GetYdoc.js.snap b/src/graphql/queries/__tests__/__snapshots__/GetYdoc.js.snap new file mode 100644 index 00000000..df1d4d72 --- /dev/null +++ b/src/graphql/queries/__tests__/__snapshots__/GetYdoc.js.snap @@ -0,0 +1,40 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`GetYdoc should get the specified doc 1`] = ` +Object { + "data": Object { + "GetYdoc": Object { + "data": "mock data1", + "versions": Array [ + Object { + "createdAt": "2023-09-07T08:14:14.005Z", + "snapshot": "mock snapshot1", + }, + Object { + "createdAt": "2023-09-07T08:16:45.613Z", + "snapshot": "mock snapshot2", + }, + Object { + "createdAt": "2023-09-07T08:18:32.467Z", + "snapshot": "mock snapshot3", + }, + Object { + "createdAt": "2023-09-07T08:18:49.500Z", + "snapshot": "mock snapshot4", + }, + ], + }, + }, +} +`; + +exports[`GetYdoc should return empty versions when there is none 1`] = ` +Object { + "data": Object { + "GetYdoc": Object { + "data": "mock data2", + "versions": Array [], + }, + }, +} +`; diff --git a/src/graphql/schema.js b/src/graphql/schema.js index 6f0543dd..e6e66bd9 100644 --- a/src/graphql/schema.js +++ b/src/graphql/schema.js @@ -5,6 +5,7 @@ import GetArticle from './queries/GetArticle'; import GetReply from './queries/GetReply'; import GetUser from './queries/GetUser'; import GetCategory from './queries/GetCategory'; +import GetYdoc from './queries/GetYdoc'; import ListArticles from './queries/ListArticles'; import ListReplies from './queries/ListReplies'; import ListCategories from './queries/ListCategories'; @@ -41,6 +42,7 @@ export default new GraphQLSchema({ GetReply, GetUser, GetCategory, + GetYdoc, ListArticles, ListReplies, ListCategories,