Skip to main content

Migrating from v1 to v2

Version 2 ships with some breaking changes.

New client API#

Version 2.0 ships with an entirely reworked client side API.

Breaking changes suck (we know!) - but this change was important to pave the path forwards for these modules.

Below is a list of all breaking changes:

Upgrading WebViewer#

Version 2.0 requires that WebViewer 8.0+ is installed and used. To learn how to migrate to WebViewer 8.0, please see this guide

Handling timestamps#

In version 1, fields such as createdAt and updatedAt were provided a unix timestamp in MS (for example, 1622647081013). However, this was not flexible enough for many databases.

Now, these fields will be set to the result of your getNow() function.

We recommend setting this function to return a database constant that works with your database.

import CollabServer from '@pdftron/collab-server';
const server = new CollabServer({
getNow: () => 'NOW()',
...otherOptions
});

Now, your resolvers will receive "NOW()" instead of a unix timestamp, meaning this value can be inserted directly.

V1:

import CollabServer from '@pdftron/collab-server'
const server = new CollabServer({
resolvers: {
Mutation: {
addDocument: (doc, context) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
console.log(doc.createdAt) // 1622647081013
const newDoc = await db
.insert('documents')
.values({
author_id: doc.authorId,
is_public: doc.isPublic,
name: doc.name,
// These dates used to have to be transformed into
// a format that works with our database
created_at: new Date(doc.createdAt).toISOString(),
updated_at: new Date(doc.updatedAt).toISOString(),
});
return newDoc;
}
}
}
})

V2:

import CollabServer from '@pdftron/collab-server'
const server = new CollabServer({
getNow: () => 'NOW()', // Add the new `getNow` parameter
resolvers: {
Mutation: {
addDocument: (doc, context) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
console.log(doc.createdAt) // NOW()
const newDoc = await db
.insert('documents')
.values({
author_id: doc.authorId,
is_public: doc.isPublic,
name: doc.name,
// We no longer have to transform these values
// as they are equal to `NOW()`
created_at: doc.createdAt,
updated_at: doc.updatedAt,
});
return newDoc;
}
}
}
})

For more info, view the timestamps guide.

Generating IDs#

In version 1, IDs were generated internally and passed to the resolvers. However, this was not feasible for databases with auto-incrementing IDs.

Now, IDs are not provided to mutation resolvers that write data, and instead you must generate the ID yourself, or rely on your database to do so. This ID must then be returned from your resolver.

V1:

import CollabServer from '@pdftron/collab-server'
const server = new CollabServer({
resolvers: {
Mutation: {
addDocument: (doc, context) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
console.log(doc.id) // 6d99a211-94b4-40ef-b008-c883da393054
const newDoc = await db
.insert('documents')
.values({
id: doc.id, // This ID used to be provided
author_id: doc.authorId,
is_public: doc.isPublic,
name: doc.name,
created_at: new Date(doc.createdAt).toISOString(),
updated_at: new Date(doc.updatedAt).toISOString(),
});
return newDoc;
}
}
}
})

V2:

import CollabServer from '@pdftron/collab-server'
const server = new CollabServer({
resolvers: {
Mutation: {
addDocument: (doc, context) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
console.log(doc.id) // undefined
const newDoc = await db
.insert('documents')
.values({
// An ID is no longer provided - the DB must generate it
author_id: doc.authorId,
is_public: doc.isPublic,
name: doc.name,
created_at: new Date(doc.createdAt).toISOString(),
updated_at: new Date(doc.updatedAt).toISOString(),
});
return newDoc;
}
}
}
})

New annotationId column#

Version 2 adds an annotationId to the annotation entity. This is an optimization that makes handling annotations easier for cross platform use (coming soon).

The annotationId column will be set to the ID given to the annotation by the client. It will always be a string, but might not necessarily be unique.

Annotations are now queried by annotationId and pageNumber rather than by id.

This change only requires you to add an annotation_id column to your annotations table, and to update your annotation resolvers to use the new property.

note

If your database does currently not store this value, you can write a migration script using our utility

V1:

import CollabServer from '@pdftron/collab-server'
const server = new CollabServer({
resolvers: {
Mutation: {
addAnnotation: (annotation, context) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
console.log(annotation.id) // 6d99a211-94b4-40ef-b008-c883da393054
const newAnnotation = await db
.insert('annotations')
.values({
id: annotation.id, // This id is same as the 'name' value in the xfdf
xfdf: annotation.xfdf,
annot_contents: annotation.annotContents,
author_id: annotation.authorId,
document_id: annotation.documentId,
page_number: annotation.pageNumber,
in_reply_to: annotation.inReplyTo,
created_at: new Date(annotation.createdAt).toISOString(),
updated_at: new Date(annotation.updatedAt).toISOString(),
});
return newAnnotation;
}
}
}
})

V2:

import CollabServer from '@pdftron/collab-server'
const server = new CollabServer({
resolvers: {
Mutation: {
addAnnotation: (annotation, context) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
console.log(annotation.id) // undefined
const newAnnotation = await db
.insert('annotations')
.values({
// An ID is no longer provided - the DB must generate it
xfdf: annotation.xfdf,
annot_contents: annotation.annotContents,
author_id: annotation.authorId,
// This annotation id is generated client side
annotation_id: annotation.annotationId,
document_id: annotation.documentId,
page_number: annotation.pageNumber,
in_reply_to: annotation.inReplyTo,
created_at: new Date(annotation.createdAt).toISOString(),
updated_at: new Date(annotation.updatedAt).toISOString(),
});
return newAnnotation;
}
}
}
})

Added isPublic parameter to documents query resolver#

Version two adds a isPublic parameter to the documents resolver.

Update your resolver to apply this additional field:

const server = new CollabServer({
resolvers: {
Query: {
documents: async (query) => {
const {
ids,
userId,
isPublic,
filters = {}
} = query;
// get a reference to our fictional ORM
const db = getDatabaseConnection();
let query = db
.select('*')
.from('documents');
if(ids) {
query = query.where('id', 'IN', ids)
}
if(isPublic) {
query = query.where('is_public', '=', true)
}
if(userId) {
query = query.where(
'id',
'IN',
`
(SELECT document_id FROM document_members WHERE user_id = '${userId}')
`)
}
if(filters.createdBefore) {
query = query.where('created_at', '<', filters.createdBefore)
}
if(filters.createdAfter) {
query = query.where('created_at', '>', filters.createdAfter)
}
if(filters.updatedBefore) {
query = query.where('updated_at', '<', filters.updatedBefore)
}
if(filters.updatedAfter) {
query = query.where('updated_at', '>', filters.updatedAfter)
}
if(filters.orderBy) {
query = query.order(
filters.orderBy === 'updatedAt' ? 'updated_at' : 'created_at',
filters.orderDirection
)
}
if (filters.limit) {
query = query.limit(filters.limit)
}
const docs = await query.get()
return docs;
},
},
},
});

Updated properties on notification events#

In version 1, notification events were sent with a sentBy property that was equal to the users email. In v2, this property has been changed to an object containing both the users email and userName.

This change applies anywhere a notification event is passed.

V1

import { CollabClient } from '@pdftron/collab-client'
const client = new CollabClient({
...otherOptions,
notificationHandler: CollabClient.defaultNotificationHandler({
getText: (event) => {
if(event.type === 'invite') {
return {
title: `New invite recieved from ${event.sentBy}`, // <-- This used to be a string
body: `Click here to view ${event.documentName}`
}
}
},
})
})

V2

import { CollabClient } from '@pdftron/collab-client'
const client = new CollabClient({
...otherOptions,
notificationHandler: CollabClient.defaultNotificationHandler({
getText: (event) => {
if(event.type === 'invite') {
return {
title: `New invite recieved from ${event.sentBy.email}`, // <-- This is now an object
body: `Click here to view ${event.documentName}`
}
}
},
})
})

documentId parameter added to annotationMembers query resolver.#

A documentId was added to the annotationMembers resolver. This allows us to fetch all annotation members for a document in a single query.

This is a simple change to make and is only required if using your own custom resolvers.

const server = new CollabServer({
resolvers: {
Query: {
annotationMembers: async (query) => {
const {
ids,
annotationId,
userId,
filters = {},
documentId
} = query;
// get a reference to our fictional ORM
const db = getDatabaseConnection();
let query = db
.select('*')
.from('annotation_members');
if(ids) {
query = query.where('id', 'IN', ids)
}
if(userId) {
query = query.where('user_id', '=', userId);
}
if(annotationId) {
query = query.where('annotation_id', '=', annotationId);
}
if(documentId) {
query = query.where('document_id', '=', documentId);
}
if(filters.createdBefore) {
query = query.where('created_at', '<', filters.createdBefore)
}
if(filters.createdAfter) {
query = query.where('created_at', '>', filters.createdAfter)
}
if(filters.updatedBefore) {
query = query.where('updated_at', '<', filters.updatedBefore)
}
if(filters.updatedAfter) {
query = query.where('updated_at', '>', filters.updatedAfter)
}
if(filters.orderBy) {
query = query.order(
filters.orderBy === 'updatedAt' ? 'updated_at' : 'created_at',
filters.orderDirection
)
}
if (filters.limit) {
query = query.limit(filters.limit)
}
const members = await query.get()
return members;
},
},
},
});

Create the COLLAB_KEY environment variable#

If you do not have the COLLAB_KEY environment variable set already, you must do so in v2.0.

In version 1, the COLLAB_KEY environment variable was only required in certain situations. In version 2.0, it must now always be set.

Read how to set it here.