guideMigrating to RTC in Node.js

This article presents an example of an application that allows for migrating users and documents created in non Real-time Collaboration to Real-time Collaboration.

# Dependencies

This example uses the following dependencies:

  • axios
  • body-parser
  • cors
  • express

It also uses core dependencies from Node.js: path and fs.

# Example

The following example allows you to upload an editor bundle, create a user and import a document to the Real-time Collaboration.

The following example will cover:

  • Uploading an editor bundle to CKEditor Cloud Services
  • Creating a Real-time Collaboration user
  • Mapping the document and all its data from the non Real-time Collaboration and importing to the CKEditor Cloud Services

Presented below is an example of a simple Express.js application.

Remember to provide a correct API secret and generate a proper request signature.

// mapDocumentData.js

/**
 * Maps all data for the document to the structure and assigns data missing in non-RTC 
 * according to the CKEditor Cloud Services' requirements.
 * 
 * @param {Object} [documentData] The whole data of the document with all its resources saved from the non Real-time Collaboration.
 * @param {String} [documentData.documentId] The ID of the document to import
 * @param {String} [documentData.bundleVersion] The version of the bundled editor to use for the document
 * @param {String} [documentData.editorData] The content of the document in HTML or Markdown
 * @param {Array.<Object>} [documentData.commentThreadsData]
 * @param {Array.<Object>} [documentData.suggestionsData]
 * @param {Array.<Object>} [documentData.revisionsData]
 * @param {String} [suggestionAuthorID] the ID of the user to assign for suggestions that don't have an author assigned to them
 * @returns {Object}
 */
function mapDocumentData( documentData = {}, suggestionAuthorID ) {
    const threads = [];
    const comments = [];

    const {
        commentThreadsData = [],
        suggestionsData = [],
        revisionsData = [],
        documentId,
        bundleVersion,
        editorData
    } = documentData;

    commentThreadsData.forEach( commentThread => {
        const hasMatchingSuggestion = suggestionsData.some( suggestion => suggestion.id === commentThread.id );

        threads.push( {
            id: commentThread.threadId,
            context: commentThread.context,
            resolved_at: commentThread.resolvedAt,
            resolved_by: commentThread.resolvedBy,
            attributes: commentThread.attributes,
            author_id: commentThread.authorId
        } );

        comments.push(
            ...commentThread.comments.map( comment => ( {
                id: comment.commentId,
                thread_id: commentThread.threadId,
                content: comment.content,
                attributes: comment.attributes,
                user: { id: comment.authorId },
                created_at: comment.created_at,
                type: hasMatchingSuggestion ? 2 : 1
            } ) )
        );
    } );

    const revisions = [];
    let version = 1;

    revisionsData.forEach( ( revision, index ) => {
        if ( revision.toVersion > version ) {
            version = revision.toVersion;
        }

        revisions.push( {
            revision_id: revision.id,
            request_id: index,
            name: revision.name,
            creator_id: revision.authorId,
            authors_ids: revision.authors_ids,
            diff_data: JSON.stringify( revision.diffData ),
            created_at: revision.createdAt,
            attributes: revision.attributes,
            from_version: revision.fromVersion,
            to_version: revision.toVersion
        } )
    } );

    const suggestions = suggestionsData.map( suggestion => ( {
        id: suggestion.id,
        type: suggestion.type,
        author_id: suggestion.authorId ?? suggestionAuthorID,
        created_at: suggestion.createdAt,
        has_comments: suggestion.hasComments,
        data: suggestion.data,
        attributes: suggestion.attributes,
    } ) )

    const importBody = {
        id: documentId,
        content: {
            bundle_version: bundleVersion,
            data: editorData,
            version: version ?? undefined
        },
        comments,
        suggestions,
        revisions,
        threads
    };

    return importBody;
}
// index.js
const path = require( 'path' );
const fs = require( 'fs' );
const express = require( 'express' );
const axios = require( 'axios' );
const cors = require( 'cors' );
const bodyParser = require( 'body-parser' );
const generateSignature = require( './utils/generateSignature' ); // See: https://ckeditor.com/docs/cs/latest/examples/security/request-signature-nodejs.html.
const mapDocumentData = require( './utils/mapDocumentData' );
const editorBundle = fs.readFileSync( path.resolve( '../client/build/ckeditor.js' ) ); // It should be your bundled editor.

const app = express();
const port = 8000; // The default application port.
const apiSecret = 'SECRET'; // Do not forget to hide this value in a safe place e.g. a .env file!
const organizationId = 'organizationId'; // Type your organization ID here.
const environmentId = 'environmentId'; // Type your environment ID here.
// If you use On-Premises application you can adjust baseApiUrl accordingly with your application URL.
const baseApiUrl = `https://${ organizationId }.cke-cs.com/api/v5/${ environmentId }`;

app.use( bodyParser.urlencoded( { extended: true } ) );
app.use( bodyParser.json() );
app.use( cors() );

// This function will be responsible for sending requests to CKEditor Cloud Services API.
async function sendRequest( method, url, body ) {
    const CSTimestamp = Date.now();
    const payload = {
        method,
        url,
        mode: 'no-cors',
        headers: {
            'Content-Type': 'application/json',
            'X-CS-Signature': generateSignature( apiSecret, method, url, CSTimestamp, body ),
            'X-CS-Timestamp': CSTimestamp
        }
    };

    if ( method.toUpperCase() !== 'GET' ) {
        payload.data = body;
    }

    try {
        const { status, data } = await axios( payload );

        return { status, data };
    } catch ( { response } ) {
        const { status, data } = response;

        return { status, data };
    }
}

// Upload the editor bundle. Note that you will need to upload your editor again if you change the bundle.
app.post( '/upload-editor', async ( req, res ) => {
    const { bundleVersion } = req.body;

    const { status, data } = await sendRequest( 'POST', `${ baseApiUrl }/editors`, {
        bundle: editorBundle.toString(),
        config: {
            cloudServices: {
                bundleVersion // This value should be unique per environment.
            }
        }
    } );

    return res.json( { status, data } );
} );

// Create a user for Real-time Collaboration.
app.post( '/create-user', async ( req, res ) => {
    const { email, id, name } = req.body;

    const { status, data } = await sendRequest( 'POST', `${ baseApiUrl }/users`, {
        id,
        email,
        name
    } );

    return res.json( { status, data } );
} );

// Import a document to the CKEditor Cloud Services.
app.post( '/import-document', async ( req, res ) => {
    const { 
        documentData,
        suggestionAuthorId
    } = req.body;

    const importBody = mapDocumentData( documentData, suggestionAuthorId );

    const { status, data } = await sendRequest( 'POST', `${ baseApiUrl }/documents`, importBody );

    return res.json( { status, data } );
} );

app.listen( port, () => console.log( `The application is listening on port ${ port }!` ) );

# Usage

Run:

node index.js

By sending HTTP requests to this application you can now perform actions and communicate with CKEditor Cloud Services. See the following example:

  1. Uploading editor bundle.
try {
    const response = await fetch( 'http://localhost:8000/upload-editor', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify( { bundleVersion: '1.0.0' } )
    } );

    const data = await response.json();

    console.log( 'Result of uploading editor:', data );
} catch ( error ) {
    console.log( 'Error occurred when uploading editor:', error );
}
  1. Creating a user.
try {
    const response = await fetch( 'http://localhost:8000/create-user', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify( { 
            id: 'user-1',
            email: 'example@example.com',
            name: 'Example user'
        } )
    } );

    const data = await response.json();

    console.log( 'Result of uploading user:', data );
} catch ( error ) {
    console.log( 'Error occurred when uploading user:', error );
}
  1. Importing a document to the CKEditor Cloud Services. For documentData you can either use the data taken from your server or you can use the example below.
Click to see the example data.
{
    "documentId": "document-1",
    "bundleVersion": "1.0.0",
    "editorData": "<h2 style=\"text-align:center;\">NON-DISCLOSURE AGREEMENT (NDA)</h2><p>&nbsp;</p><p>This Nondisclosure Agreement or (\"Agreement\") has been entered into on the <suggestion-start name=\"deletion:ea6c5028e3c0efaf0b9a23d227d89696c:user-1\"></suggestion-start>the <suggestion-end name=\"deletion:ea6c5028e3c0efaf0b9a23d227d89696c:user-1\"></suggestion-end>date of 10.05.2021 and is by and between:</p><p><strong>Party Disclosing Information:</strong> James Lahey with a mailing address of <comment-start name=\"eca0c3e71883a578ee37bdc0e510a41a8:49cf1\"></comment-start>3 Maple Rd. Spryfield B3P 1H8<comment-end name=\"eca0c3e71883a578ee37bdc0e510a41a8:49cf1\"></comment-end> &nbsp;(\"Disclosing Party\").</p><p><strong>Party Receiving Information:</strong> &nbsp;Richard Lafleur with a mailing address of &nbsp;1 Bonnyville Dr. Spryfield B3P 1H8 (\"Receiving Party\"). For the purpose of preventing the unauthorized disclosure of Confidential Information as defined below. The parties agree to enter into a confidential relationship concerning the disclosure of certain <suggestion-start name=\"insertion:e64910f0cd32232aac1cce1c6b5b565b6:user-1\"></suggestion-start>proprietary<suggestion-end name=\"insertion:e64910f0cd32232aac1cce1c6b5b565b6:user-1\"></suggestion-end><suggestion-start name=\"deletion:e513f8e2e4eca79825823ff00294f3716:user-1\"></suggestion-start>business<suggestion-end name=\"deletion:e513f8e2e4eca79825823ff00294f3716:user-1\"></suggestion-end> property and confidential information (\"Confidential Information\").</p><p>&nbsp;</p><h2>1. Definition of Confidential Information</h2><p>For purposes of this Agreement, \"Confidential Information\" shall include all information or material that has or could have commercial value or other utility in the business in which the Disclosing Party is engaged. If Confidential Information is in written form, the Disclosing Party shall label or stamp the materials with the word \"Confidential\" or some similar warning. If Confidential Information is transmitted orally, the Disclosing Party shall promptly provide a writing indicating that such oral communication constituted Confidential Information.</p><h2>2. Exclusions from Confidential Information</h2><p>Receiving Party's obligations under this Agreement do not extend to information that is:(a) publicly known at the time of disclosure or subsequently becomes publicly known through no fault of the Receiving Party; (b) discovered or created by the Receiving Party before disclosure by Disclosing Party; (c) learned by the Receiving Party through legitimate means other than from the Disclosing Party or Disclosing Party's representatives; or (d) is disclosed by Receiving Party with Disclosing Party's prior written approval.</p><h2>3. Obligations of Receiving Party</h2><p>Receiving Party shall hold and maintain the Confidential Information in strictest confidence for the sole and exclusive benefit of the Disclosing Party. Receiving Party shall carefully restrict access to Confidential Information to employees, contractors and third parties as is reasonably required and shall require those persons to sign nondisclosure restrictions at least as protective as those in this Agreement. Receiving Party shall not, without the prior written approval of Disclosing Party, use for Receiving Party's benefit, publish, copy, or otherwise disclose to others, or permit the use by others for their benefit or to the detriment of Disclosing Party, any Confidential Information. Receiving Party shall return to Disclosing Party any and all records, notes, and other written, printed, or tangible materials in its possession pertaining to Confidential Information immediately if Disclosing Party requests it in writing.</p><h2>4. Time Periods</h2><p>The nondisclosure provisions of this Agreement shall survive the termination of this Agreement and Receiving Party's duty to hold Confidential Information in confidence shall remain in effect until the Confidential Information no longer qualifies as a trade secret or until Disclosing Party sends Receiving Party written notice releasing Receiving Party from this Agreement, whichever occurs first.</p><h2>5. Relationships</h2><p>Nothing contained in this Agreement shall be deemed to constitute either party a partner, joint venture or employee of the other party for any purpose.</p><h2>6. Severability</h2><p>If a court finds any provision of this Agreement invalid or unenforceable, the remainder of this Agreement shall be interpreted so as best to affect the intent of the parties.</p><h2>7. Integration</h2><p>This Agreement expresses the complete understanding of the parties with respect to the subject matter and supersedes all prior proposals, agreements, representations, and understandings. This Agreement may not be amended except in writing signed by both parties.</p><h2>8. Waiver</h2><p>The failure to exercise any right provided in this Agreement shall not be a waiver of prior or subsequent rights.</p><h2>9. Notice of Immunity</h2><p>Employee is provided notice that an individual shall not be held criminally or civilly liable under any federal or state trade secret law for the disclosure of a trade secret that is made <comment-start name=\"e5964fb81e95e0cd712ecf95fafad425f:57cf0\"></comment-start>(i)<comment-end name=\"e5964fb81e95e0cd712ecf95fafad425f:57cf0\"></comment-end> in confidence to a federal, state, or local government official, either directly or indirectly, or to an attorney; and (ii) solely for the purpose of reporting or investigating a suspected violation of law; or is made in a complaint or other document filed in a lawsuit or other proceeding, if such filing is made under seal. An individual who files a lawsuit for retaliation by an employer for reporting a suspected violation of law may disclose the trade secret to the attorney of the individual and use the trade secret information in the court proceeding, if the individual (i) files any document containing the trade secret under seal; and (ii) does not disclose the trade secret, except pursuant to court order. This Agreement and each party's obligations shall be binding on the representatives, assigns and successors of such party. Each party has signed this Agreement through its authorized representative.</p><p>&nbsp;</p><p>&nbsp;</p><p><strong>DISCLOSING PARTY Signature:</strong> _____________________________________________________</p><p><strong>Typed or Printed Name:</strong> James Lahey</p><p><strong>Date:</strong> 10.05.2021</p><p>&nbsp;</p><p><strong>RECEIVING PARTY Signature:</strong> _____________________________________________________</p><p><strong>Typed or Printed Name:</strong> Richard Lafleur</p><p><strong>Date:</strong> 10.05.2021</p>",
    "commentThreadsData": [
        {
            "threadId": "eca0c3e71883a578ee37bdc0e510a41a8",
            "context": {
            "type": "text",
            "value": "3 Maple Rd. Spryfield B3P 1H8"
            },
            "authorId": "user-1",
            "resolvedAt": null,
            "resolvedBy": null,
            "comments": [
                {
                    "commentId": "e949f2f153f07ddc9b5f161062d4a1ad9",
                    "content": "<p>This might be Mr. Lafleur's address. Can we double-check the addresses?</p>",
                    "createdAt": "2023-08-24T09:35:17.338Z",
                    "authorId": "user-1",
                    "attributes": {}
                }
            ],
            "attributes": {}
        },
        {
            "threadId": "e5964fb81e95e0cd712ecf95fafad425f",
            "context": {
            "type": "text",
            "value": "(i)"
            },
            "authorId": "user-1",
            "resolvedAt": null,
            "resolvedBy": null,
            "comments": [
                {
                    "commentId": "edadb356033be84bb93085092e4d8b15b",
                    "content": "<p>Maybe we should change this listing to be consistent with the one from section 2</p>",
                    "createdAt": "2023-08-24T09:37:11.805Z",
                    "authorId": "user-1",
                    "attributes": {}
                }
            ],
            "attributes": {}
        }
    ],
    "suggestionsData": [
        {
            "id": "ea6c5028e3c0efaf0b9a23d227d89696c",
            "type": "deletion",
            "createdAt": "2023-08-24T09:31:40.515Z",
            "hasComments": false,
            "data": null,
            "attributes": {}
        },
        {
            "id": "e513f8e2e4eca79825823ff00294f3716",
            "type": "deletion",
            "createdAt": "2023-08-24T09:32:02.186Z",
            "hasComments": false,
            "data": null,
            "attributes": {}
        },
        {
            "id": "e64910f0cd32232aac1cce1c6b5b565b6",
            "type": "insertion",
            "createdAt": "2023-08-24T09:32:02.186Z",
            "hasComments": false,
            "data": null,
            "attributes": {}
        }
    ],
    "revisionsData": [
        {
            "id": "initial",
            "name": "Empty document",
            "creatorId": "user-1",
            "authorsIds": [],
            "diffData": {
                "main": {
                    "insertions": "[{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[]}]",
                    "deletions": "[{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[]}]",
                    "attachChange": null,
                    "attributesBefore": {},
                    "attributesAfter": {}
                }
            },
            "createdAt": "2023-08-24T09:28:43.499Z",
            "attributes": {},
            "fromVersion": 2,
            "toVersion": 2
        },
        {
            "id": "ed433b9857b18e2ece0ca0b19a0288ba5",
            "name": "revision-1",
            "creatorId": "user-1",
            "authorsIds": [
            "user-1"
            ],
            "diffData": {
                "main": {
                    "insertions": "[{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[[\"data-revision-start-before\",\"insertion:user-1:0\"],[\"style\",\"text-align:center;\"]],\"children\":[\"NON-DISCLOSURE AGREEMENT (NDA)\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"This Nondisclosure Agreement or (\\\"Agreement\\\") has been entered into on the \",{\"type\":\"u\",\"name\":\"suggestion-start\",\"attributes\":[[\"name\",\"deletion:ea6c5028e3c0efaf0b9a23d227d89696c:user-1\"]],\"children\":[]},\"the \",{\"type\":\"u\",\"name\":\"suggestion-end\",\"attributes\":[[\"name\",\"deletion:ea6c5028e3c0efaf0b9a23d227d89696c:user-1\"]],\"children\":[]},\"date of 10.05.2021 and is by and between:\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[{\"type\":\"a\",\"name\":\"strong\",\"attributes\":[],\"children\":[\"Party Disclosing Information:\"]},\" James Lahey with a mailing address of 3 Maple Rd. Spryfield B3P 1H8    (\\\"Disclosing Party\\\").\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[{\"type\":\"a\",\"name\":\"strong\",\"attributes\":[],\"children\":[\"Party Receiving Information:\"]},\"    Richard Lafleur with a mailing address of    1 Bonnyville Dr. Spryfield B3P 1H8 (\\\"Receiving Party\\\"). For the purpose of preventing the unauthorized disclosure of Confidential Information as defined below. The parties agree to enter into a confidential relationship concerning the disclosure of certain \",{\"type\":\"u\",\"name\":\"suggestion-start\",\"attributes\":[[\"name\",\"insertion:e64910f0cd32232aac1cce1c6b5b565b6:user-1\"]],\"children\":[]},\"proprietary\",{\"type\":\"u\",\"name\":\"suggestion-end\",\"attributes\":[[\"name\",\"insertion:e64910f0cd32232aac1cce1c6b5b565b6:user-1\"]],\"children\":[]},{\"type\":\"u\",\"name\":\"suggestion-start\",\"attributes\":[[\"name\",\"deletion:e513f8e2e4eca79825823ff00294f3716:user-1\"]],\"children\":[]},\"business\",{\"type\":\"u\",\"name\":\"suggestion-end\",\"attributes\":[[\"name\",\"deletion:e513f8e2e4eca79825823ff00294f3716:user-1\"]],\"children\":[]},\" property and confidential information (\\\"Confidential Information\\\").\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[]},{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[],\"children\":[\"1. Definition of Confidential Information\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"For purposes of this Agreement, \\\"Confidential Information\\\" shall include all information or material that has or could have commercial value or other utility in the business in which the Disclosing Party is engaged. If Confidential Information is in written form, the Disclosing Party shall label or stamp the materials with the word \\\"Confidential\\\" or some similar warning. If Confidential Information is transmitted orally, the Disclosing Party shall promptly provide a writing indicating that such oral communication constituted Confidential Information.\"]},{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[],\"children\":[\"2. Exclusions from Confidential Information\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"Receiving Party's obligations under this Agreement do not extend to information that is:(a) publicly known at the time of disclosure or subsequently becomes publicly known through no fault of the Receiving Party; (b) discovered or created by the Receiving Party before disclosure by Disclosing Party; (c) learned by the Receiving Party through legitimate means other than from the Disclosing Party or Disclosing Party's representatives; or (d) is disclosed by Receiving Party with Disclosing Party's prior written approval.\"]},{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[],\"children\":[\"3. Obligations of Receiving Party\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"Receiving Party shall hold and maintain the Confidential Information in strictest confidence for the sole and exclusive benefit of the Disclosing Party. Receiving Party shall carefully restrict access to Confidential Information to employees, contractors and third parties as is reasonably required and shall require those persons to sign nondisclosure restrictions at least as protective as those in this Agreement. Receiving Party shall not, without the prior written approval of Disclosing Party, use for Receiving Party's benefit, publish, copy, or otherwise disclose to others, or permit the use by others for their benefit or to the detriment of Disclosing Party, any Confidential Information. Receiving Party shall return to Disclosing Party any and all records, notes, and other written, printed, or tangible materials in its possession pertaining to Confidential Information immediately if Disclosing Party requests it in writing.\"]},{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[],\"children\":[\"4. Time Periods\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"The nondisclosure provisions of this Agreement shall survive the termination of this Agreement and Receiving Party's duty to hold Confidential Information in confidence shall remain in effect until the Confidential Information no longer qualifies as a trade secret or until Disclosing Party sends Receiving Party written notice releasing Receiving Party from this Agreement, whichever occurs first.\"]},{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[],\"children\":[\"5. Relationships\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"Nothing contained in this Agreement shall be deemed to constitute either party a partner, joint venture or employee of the other party for any purpose.\"]},{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[],\"children\":[\"6. Severability\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"If a court finds any provision of this Agreement invalid or unenforceable, the remainder of this Agreement shall be interpreted so as best to affect the intent of the parties.\"]},{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[],\"children\":[\"7. Integration\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"This Agreement expresses the complete understanding of the parties with respect to the subject matter and supersedes all prior proposals, agreements, representations, and understandings. This Agreement may not be amended except in writing signed by both parties.\"]},{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[],\"children\":[\"8. Waiver\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"The failure to exercise any right provided in this Agreement shall not be a waiver of prior or subsequent rights.\"]},{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[],\"children\":[\"9. Notice of Immunity\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"Employee is provided notice that an individual shall not be held criminally or civilly liable under any federal or state trade secret law for the disclosure of a trade secret that is made (i) in confidence to a federal, state, or local government official, either directly or indirectly, or to an attorney; and (ii) solely for the purpose of reporting or investigating a suspected violation of law; or is made in a complaint or other document filed in a lawsuit or other proceeding, if such filing is made under seal. An individual who files a lawsuit for retaliation by an employer for reporting a suspected violation of law may disclose the trade secret to the attorney of the individual and use the trade secret information in the court proceeding, if the individual (i) files any document containing the trade secret under seal; and (ii) does not disclose the trade secret, except pursuant to court order. This Agreement and each party's obligations shall be binding on the representatives, assigns and successors of such party. Each party has signed this Agreement through its authorized representative.\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[{\"type\":\"a\",\"name\":\"strong\",\"attributes\":[],\"children\":[\"DISCLOSING PARTY Signature:\"]},\" _____________________________________________________\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[{\"type\":\"a\",\"name\":\"strong\",\"attributes\":[],\"children\":[\"Typed or Printed Name:\"]},\" James Lahey\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[{\"type\":\"a\",\"name\":\"strong\",\"attributes\":[],\"children\":[\"Date:\"]},\" 10.05.2021\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[{\"type\":\"a\",\"name\":\"strong\",\"attributes\":[],\"children\":[\"RECEIVING PARTY Signature:\"]},\" _____________________________________________________\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[{\"type\":\"a\",\"name\":\"strong\",\"attributes\":[],\"children\":[\"Typed or Printed Name:\"]},\" Richard Lafleur\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[[\"data-revision-end-after\",\"insertion:user-1:0\"]],\"children\":[{\"type\":\"a\",\"name\":\"strong\",\"attributes\":[],\"children\":[\"Date:\"]},\" 10.05.2021\"]}]",
                    "deletions": "[{\"type\":\"c\",\"name\":\"p\",\"attributes\":[[\"data-revision-end-after\",\"deletion:user-1:0\"],[\"data-revision-start-before\",\"deletion:user-1:0\"]],\"children\":[]}]",
                    "attachChange": null,
                    "attributesBefore": {},
                    "attributesAfter": {}
                }
            },
            "createdAt": "2023-08-24T09:33:51.568Z",
            "attributes": {},
            "fromVersion": 2,
            "toVersion": 14
        },
        {
            "id": "e6a32e19aceae7a67bb9861a0b887611b",
            "name": "",
            "creatorId": null,
            "authorsIds": [
            "user-1"
            ],
            "diffData": {
                "main": {
                    "insertions": "[{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[[\"style\",\"text-align:center;\"]],\"children\":[\"NON-DISCLOSURE AGREEMENT (NDA)\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"This Nondisclosure Agreement or (\\\"Agreement\\\") has been entered into on the \",{\"type\":\"u\",\"name\":\"suggestion-start\",\"attributes\":[[\"name\",\"deletion:ea6c5028e3c0efaf0b9a23d227d89696c:user-1\"]],\"children\":[]},\"the \",{\"type\":\"u\",\"name\":\"suggestion-end\",\"attributes\":[[\"name\",\"deletion:ea6c5028e3c0efaf0b9a23d227d89696c:user-1\"]],\"children\":[]},\"date of 10.05.2021 and is by and between:\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[{\"type\":\"a\",\"name\":\"strong\",\"attributes\":[],\"children\":[\"Party Disclosing Information:\"]},\" James Lahey with a mailing address of \",{\"type\":\"u\",\"name\":\"comment-start\",\"attributes\":[[\"name\",\"eca0c3e71883a578ee37bdc0e510a41a8:49cf1\"]],\"children\":[]},\"3 Maple Rd. Spryfield B3P 1H8\",{\"type\":\"u\",\"name\":\"comment-end\",\"attributes\":[[\"name\",\"eca0c3e71883a578ee37bdc0e510a41a8:49cf1\"]],\"children\":[]},\"    (\\\"Disclosing Party\\\").\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[{\"type\":\"a\",\"name\":\"strong\",\"attributes\":[],\"children\":[\"Party Receiving Information:\"]},\"    Richard Lafleur with a mailing address of    1 Bonnyville Dr. Spryfield B3P 1H8 (\\\"Receiving Party\\\"). For the purpose of preventing the unauthorized disclosure of Confidential Information as defined below. The parties agree to enter into a confidential relationship concerning the disclosure of certain \",{\"type\":\"u\",\"name\":\"suggestion-start\",\"attributes\":[[\"name\",\"insertion:e64910f0cd32232aac1cce1c6b5b565b6:user-1\"]],\"children\":[]},\"proprietary\",{\"type\":\"u\",\"name\":\"suggestion-end\",\"attributes\":[[\"name\",\"insertion:e64910f0cd32232aac1cce1c6b5b565b6:user-1\"]],\"children\":[]},{\"type\":\"u\",\"name\":\"suggestion-start\",\"attributes\":[[\"name\",\"deletion:e513f8e2e4eca79825823ff00294f3716:user-1\"]],\"children\":[]},\"business\",{\"type\":\"u\",\"name\":\"suggestion-end\",\"attributes\":[[\"name\",\"deletion:e513f8e2e4eca79825823ff00294f3716:user-1\"]],\"children\":[]},\" property and confidential information (\\\"Confidential Information\\\").\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[]},{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[],\"children\":[\"1. Definition of Confidential Information\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"For purposes of this Agreement, \\\"Confidential Information\\\" shall include all information or material that has or could have commercial value or other utility in the business in which the Disclosing Party is engaged. If Confidential Information is in written form, the Disclosing Party shall label or stamp the materials with the word \\\"Confidential\\\" or some similar warning. If Confidential Information is transmitted orally, the Disclosing Party shall promptly provide a writing indicating that such oral communication constituted Confidential Information.\"]},{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[],\"children\":[\"2. Exclusions from Confidential Information\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"Receiving Party's obligations under this Agreement do not extend to information that is:(a) publicly known at the time of disclosure or subsequently becomes publicly known through no fault of the Receiving Party; (b) discovered or created by the Receiving Party before disclosure by Disclosing Party; (c) learned by the Receiving Party through legitimate means other than from the Disclosing Party or Disclosing Party's representatives; or (d) is disclosed by Receiving Party with Disclosing Party's prior written approval.\"]},{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[],\"children\":[\"3. Obligations of Receiving Party\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"Receiving Party shall hold and maintain the Confidential Information in strictest confidence for the sole and exclusive benefit of the Disclosing Party. Receiving Party shall carefully restrict access to Confidential Information to employees, contractors and third parties as is reasonably required and shall require those persons to sign nondisclosure restrictions at least as protective as those in this Agreement. Receiving Party shall not, without the prior written approval of Disclosing Party, use for Receiving Party's benefit, publish, copy, or otherwise disclose to others, or permit the use by others for their benefit or to the detriment of Disclosing Party, any Confidential Information. Receiving Party shall return to Disclosing Party any and all records, notes, and other written, printed, or tangible materials in its possession pertaining to Confidential Information immediately if Disclosing Party requests it in writing.\"]},{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[],\"children\":[\"4. Time Periods\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"The nondisclosure provisions of this Agreement shall survive the termination of this Agreement and Receiving Party's duty to hold Confidential Information in confidence shall remain in effect until the Confidential Information no longer qualifies as a trade secret or until Disclosing Party sends Receiving Party written notice releasing Receiving Party from this Agreement, whichever occurs first.\"]},{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[],\"children\":[\"5. Relationships\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"Nothing contained in this Agreement shall be deemed to constitute either party a partner, joint venture or employee of the other party for any purpose.\"]},{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[],\"children\":[\"6. Severability\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"If a court finds any provision of this Agreement invalid or unenforceable, the remainder of this Agreement shall be interpreted so as best to affect the intent of the parties.\"]},{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[],\"children\":[\"7. Integration\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"This Agreement expresses the complete understanding of the parties with respect to the subject matter and supersedes all prior proposals, agreements, representations, and understandings. This Agreement may not be amended except in writing signed by both parties.\"]},{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[],\"children\":[\"8. Waiver\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"The failure to exercise any right provided in this Agreement shall not be a waiver of prior or subsequent rights.\"]},{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[],\"children\":[\"9. Notice of Immunity\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"Employee is provided notice that an individual shall not be held criminally or civilly liable under any federal or state trade secret law for the disclosure of a trade secret that is made \",{\"type\":\"u\",\"name\":\"comment-start\",\"attributes\":[[\"name\",\"e5964fb81e95e0cd712ecf95fafad425f:57cf0\"]],\"children\":[]},\"(i)\",{\"type\":\"u\",\"name\":\"comment-end\",\"attributes\":[[\"name\",\"e5964fb81e95e0cd712ecf95fafad425f:57cf0\"]],\"children\":[]},\" in confidence to a federal, state, or local government official, either directly or indirectly, or to an attorney; and (ii) solely for the purpose of reporting or investigating a suspected violation of law; or is made in a complaint or other document filed in a lawsuit or other proceeding, if such filing is made under seal. An individual who files a lawsuit for retaliation by an employer for reporting a suspected violation of law may disclose the trade secret to the attorney of the individual and use the trade secret information in the court proceeding, if the individual (i) files any document containing the trade secret under seal; and (ii) does not disclose the trade secret, except pursuant to court order. This Agreement and each party's obligations shall be binding on the representatives, assigns and successors of such party. Each party has signed this Agreement through its authorized representative.\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[{\"type\":\"a\",\"name\":\"strong\",\"attributes\":[],\"children\":[\"DISCLOSING PARTY Signature:\"]},\" _____________________________________________________\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[{\"type\":\"a\",\"name\":\"strong\",\"attributes\":[],\"children\":[\"Typed or Printed Name:\"]},\" James Lahey\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[{\"type\":\"a\",\"name\":\"strong\",\"attributes\":[],\"children\":[\"Date:\"]},\" 10.05.2021\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[{\"type\":\"a\",\"name\":\"strong\",\"attributes\":[],\"children\":[\"RECEIVING PARTY Signature:\"]},\" _____________________________________________________\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[{\"type\":\"a\",\"name\":\"strong\",\"attributes\":[],\"children\":[\"Typed or Printed Name:\"]},\" Richard Lafleur\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[{\"type\":\"a\",\"name\":\"strong\",\"attributes\":[],\"children\":[\"Date:\"]},\" 10.05.2021\"]}]",
                    "deletions": "[{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[[\"style\",\"text-align:center;\"]],\"children\":[\"NON-DISCLOSURE AGREEMENT (NDA)\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"This Nondisclosure Agreement or (\\\"Agreement\\\") has been entered into on the \",{\"type\":\"u\",\"name\":\"suggestion-start\",\"attributes\":[[\"name\",\"deletion:ea6c5028e3c0efaf0b9a23d227d89696c:user-1\"]],\"children\":[]},\"the \",{\"type\":\"u\",\"name\":\"suggestion-end\",\"attributes\":[[\"name\",\"deletion:ea6c5028e3c0efaf0b9a23d227d89696c:user-1\"]],\"children\":[]},\"date of 10.05.2021 and is by and between:\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[{\"type\":\"a\",\"name\":\"strong\",\"attributes\":[],\"children\":[\"Party Disclosing Information:\"]},\" James Lahey with a mailing address of 3 Maple Rd. Spryfield B3P 1H8    (\\\"Disclosing Party\\\").\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[{\"type\":\"a\",\"name\":\"strong\",\"attributes\":[],\"children\":[\"Party Receiving Information:\"]},\"    Richard Lafleur with a mailing address of    1 Bonnyville Dr. Spryfield B3P 1H8 (\\\"Receiving Party\\\"). For the purpose of preventing the unauthorized disclosure of Confidential Information as defined below. The parties agree to enter into a confidential relationship concerning the disclosure of certain \",{\"type\":\"u\",\"name\":\"suggestion-start\",\"attributes\":[[\"name\",\"insertion:e64910f0cd32232aac1cce1c6b5b565b6:user-1\"]],\"children\":[]},\"proprietary\",{\"type\":\"u\",\"name\":\"suggestion-end\",\"attributes\":[[\"name\",\"insertion:e64910f0cd32232aac1cce1c6b5b565b6:user-1\"]],\"children\":[]},{\"type\":\"u\",\"name\":\"suggestion-start\",\"attributes\":[[\"name\",\"deletion:e513f8e2e4eca79825823ff00294f3716:user-1\"]],\"children\":[]},\"business\",{\"type\":\"u\",\"name\":\"suggestion-end\",\"attributes\":[[\"name\",\"deletion:e513f8e2e4eca79825823ff00294f3716:user-1\"]],\"children\":[]},\" property and confidential information (\\\"Confidential Information\\\").\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[]},{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[],\"children\":[\"1. Definition of Confidential Information\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"For purposes of this Agreement, \\\"Confidential Information\\\" shall include all information or material that has or could have commercial value or other utility in the business in which the Disclosing Party is engaged. If Confidential Information is in written form, the Disclosing Party shall label or stamp the materials with the word \\\"Confidential\\\" or some similar warning. If Confidential Information is transmitted orally, the Disclosing Party shall promptly provide a writing indicating that such oral communication constituted Confidential Information.\"]},{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[],\"children\":[\"2. Exclusions from Confidential Information\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"Receiving Party's obligations under this Agreement do not extend to information that is:(a) publicly known at the time of disclosure or subsequently becomes publicly known through no fault of the Receiving Party; (b) discovered or created by the Receiving Party before disclosure by Disclosing Party; (c) learned by the Receiving Party through legitimate means other than from the Disclosing Party or Disclosing Party's representatives; or (d) is disclosed by Receiving Party with Disclosing Party's prior written approval.\"]},{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[],\"children\":[\"3. Obligations of Receiving Party\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"Receiving Party shall hold and maintain the Confidential Information in strictest confidence for the sole and exclusive benefit of the Disclosing Party. Receiving Party shall carefully restrict access to Confidential Information to employees, contractors and third parties as is reasonably required and shall require those persons to sign nondisclosure restrictions at least as protective as those in this Agreement. Receiving Party shall not, without the prior written approval of Disclosing Party, use for Receiving Party's benefit, publish, copy, or otherwise disclose to others, or permit the use by others for their benefit or to the detriment of Disclosing Party, any Confidential Information. Receiving Party shall return to Disclosing Party any and all records, notes, and other written, printed, or tangible materials in its possession pertaining to Confidential Information immediately if Disclosing Party requests it in writing.\"]},{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[],\"children\":[\"4. Time Periods\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"The nondisclosure provisions of this Agreement shall survive the termination of this Agreement and Receiving Party's duty to hold Confidential Information in confidence shall remain in effect until the Confidential Information no longer qualifies as a trade secret or until Disclosing Party sends Receiving Party written notice releasing Receiving Party from this Agreement, whichever occurs first.\"]},{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[],\"children\":[\"5. Relationships\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"Nothing contained in this Agreement shall be deemed to constitute either party a partner, joint venture or employee of the other party for any purpose.\"]},{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[],\"children\":[\"6. Severability\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"If a court finds any provision of this Agreement invalid or unenforceable, the remainder of this Agreement shall be interpreted so as best to affect the intent of the parties.\"]},{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[],\"children\":[\"7. Integration\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"This Agreement expresses the complete understanding of the parties with respect to the subject matter and supersedes all prior proposals, agreements, representations, and understandings. This Agreement may not be amended except in writing signed by both parties.\"]},{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[],\"children\":[\"8. Waiver\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"The failure to exercise any right provided in this Agreement shall not be a waiver of prior or subsequent rights.\"]},{\"type\":\"c\",\"name\":\"h2\",\"attributes\":[],\"children\":[\"9. Notice of Immunity\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[\"Employee is provided notice that an individual shall not be held criminally or civilly liable under any federal or state trade secret law for the disclosure of a trade secret that is made (i) in confidence to a federal, state, or local government official, either directly or indirectly, or to an attorney; and (ii) solely for the purpose of reporting or investigating a suspected violation of law; or is made in a complaint or other document filed in a lawsuit or other proceeding, if such filing is made under seal. An individual who files a lawsuit for retaliation by an employer for reporting a suspected violation of law may disclose the trade secret to the attorney of the individual and use the trade secret information in the court proceeding, if the individual (i) files any document containing the trade secret under seal; and (ii) does not disclose the trade secret, except pursuant to court order. This Agreement and each party's obligations shall be binding on the representatives, assigns and successors of such party. Each party has signed this Agreement through its authorized representative.\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[{\"type\":\"a\",\"name\":\"strong\",\"attributes\":[],\"children\":[\"DISCLOSING PARTY Signature:\"]},\" _____________________________________________________\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[{\"type\":\"a\",\"name\":\"strong\",\"attributes\":[],\"children\":[\"Typed or Printed Name:\"]},\" James Lahey\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[{\"type\":\"a\",\"name\":\"strong\",\"attributes\":[],\"children\":[\"Date:\"]},\" 10.05.2021\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[{\"type\":\"a\",\"name\":\"strong\",\"attributes\":[],\"children\":[\"RECEIVING PARTY Signature:\"]},\" _____________________________________________________\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[{\"type\":\"a\",\"name\":\"strong\",\"attributes\":[],\"children\":[\"Typed or Printed Name:\"]},\" Richard Lafleur\"]},{\"type\":\"c\",\"name\":\"p\",\"attributes\":[],\"children\":[{\"type\":\"a\",\"name\":\"strong\",\"attributes\":[],\"children\":[\"Date:\"]},\" 10.05.2021\"]}]",
                    "attachChange": null,
                    "attributesBefore": {},
                    "attributesAfter": {}
                }
            },
            "createdAt": "2023-08-24T09:37:39.710Z",
            "attributes": {},
            "fromVersion": 14,
            "toVersion": 16
        }
    ]
}

try {
    const response = await fetch( 'http://localhost:8000/import-document', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify( {
            documentData,
            suggestionAuthorId: 'user-1'
        } )
    } );

    const data = await response.json();

    console.log( 'Result of importing the document:', data );
} catch ( error ) {
    console.log( 'Error occurred during importing the document:', error );
}