Tutorials

Manipulating Documents

Updated: March 18, 2024

Learn how to manipulate documents (creation, update, state change, deletion...) and launch queries through the REST API.

The steps below leverage the document types included in the addon Getting started with the Nuxeo Platform:

  • The portfolio document type (BCPortfolio) holds the contracts of a customer. Its holds properties about the customer: the company name, industry and size, and the customer's juridical contact information.
  • The contract document type (BCContract) have several properties: an owner (an application user), some dates (signature, start, expiration dates), a type, an amount. It inherits customer information from its portfolio.
  • Some vocabularies are used to populate the portfolio and contract metadata (companySize, contractType and industry)
  • Contracts have a specific lifecycle, so they can evolve though the states draft, approval, running, renegociation, void, deleted and restored
  • Some business logic through automation chains and event handlers make contracts inherit properties from its portfolio, and evolve following its lifecycle

Creating a Document

Goal

Create a new contract in Nuxeo Platform.

Prerequisites

Procedure

  1. Create a file called createContract.js with the following content:

    #!/usr/bin/env node
    const Nuxeo = require('nuxeo');
    const nuxeo = new Nuxeo({
      auth: {
        method: 'basic',
        username: 'Administrator',
        password: 'Administrator'
      }
    });
    
    // Define the portfolio properties first
    const portfolioToCreate = {
      'entity-type': 'document',
      'type': 'BCPortfolio',
      'name': 'awesome-tech',
      'properties': {
        'dc:title': 'Awesome Tech Ltd.',
        'bccstm:companyIndustry': 'it',
        'bccstm:companySize': 'medium',
        // Complex properties are sent as objects
        'bccstm:juridicalContact': {
          'firstName': 'John',
          'lastName': 'McLane',
          'tel': '555 123 456',
          'email': '[email protected]'
        }
     }
    };
    
    // Then define the contract properties
    const contractToCreate = {
    'entity-type': 'document',
    'type': 'BCContract',
    'name': 'skynet-ai-maintenance',
    'properties': {
      'dc:title': 'Skynet AI Maintenance Contract',
      'bccontract:contractOwner': 'afraser',
      'bccontract:signatureDate': '2050-12-24',
      'bccontract:startDate': '2050-12-25',
      'bccontract:expirationDate': '2055-12-31',
      'bccontract:type': 'maintenance'
    }
    };
    
    const whereToCreatePortfolio = '/default-domain/workspaces/North America';
    
    const repository = nuxeo.repository();
    
    // Then create the portfolio and contract
    repository
      .create(whereToCreatePortfolio, portfolioToCreate)
      .then(portfolio => {
        console.log('Portfolio has been created as follows:');
        console.log(portfolio);
        return repository.create(portfolio.path, contractToCreate);
      })
      .then(contract => {
        console.log('Contract has been created as follows:');
        console.log(contract);
      })
      .catch(error => {
        console.log('Apologies, an error occurred.');
        console.log(error);
      });
    
  2. Save and run:

    $ node createContract.js
    

 

Fetching a Document

Documents can be fetched using their id or path.

  1. Create a file called fetchContract.js with the following content.

    #!/usr/bin/env node
    const Nuxeo = require('nuxeo');
    const nuxeo = new Nuxeo({
        auth: {
            method: 'basic',
            username: 'Administrator',
            password: 'Administrator'
        }
    });
    // Further calls will return all schemas when fetching a document
    // Note that it can easily be overridden on a per call basis if needed
    nuxeo.schemas('*');
    nuxeo.repository()
        // These headers allow us to retrieve the associated contract owner in the same call
        .header('depth', 'max')
        .header('fetch.document', 'properties')
        // We'll also retrieve the document hierarchy
        .enricher('document', 'breadcrumb')
        .fetch('/default-domain/workspaces/North America/awesome-tech/skynet-ai-maintenance')
        .then(contract => {
            console.log('Contract has been retrieved:');
            console.log(contract);
            console.log(`\nAnd here is the document's hierarchy to build a breadcrumb navigation:`);
            console.log(contract.contextParameters.breadcrumb);
        })
        .catch(error => {
            console.log(error);
        });
    
  2. Save and run:

    $ node fetchContract.js
    

Notes

  • We obtained detailed information about the contract owner because we added some headers to retrieve all the document's relations.
  • The document hierarchy is provided in context parameters because we used a content enricher. That's only one out of many.
  • The contract inherited the company name and other customer related properties from its portfolio (in the bccustomer schema) thanks to some business logic brought by the addon Getting started with the Nuxeo Platform.

 

Updating a Document

We will now update the contract to add some custom clauses.

  1. Create a file called updateContract.js with the following content.

    #!/usr/bin/env node
    const Nuxeo = require('nuxeo');
    const nuxeo = new Nuxeo({
        auth: {
            method: 'basic',
            username: 'Administrator',
            password: 'Administrator'
        }
    });
    // Further calls will return all schemas when fetching a document
    // Note that it can easily be overridden on a per call basis if needed
    nuxeo.schemas('*');
    var contractToFetch = '/default-domain/workspaces/North America/awesome-tech/skynet-ai-maintenance';
    // We're updating a complex and multi-valued property here
    // Multi-valued properties are expressed as arrays, complex properties as objects
    // So we're creating an object array here
    var propertiesToUpdate = {
        'bccontract:customClauses': [{
            'label': 'Automatic Subscription Renewal',
            'content': 'In case the user has not cancelled its subscription one month before the contract\'s expiration date, the contract will automatically be renewed for one more year.'
        }, {
            'label': 'Payment Conditions',
            'content': 'When an automatic subscription renewal is triggered, the user will need to pay the annual amount due. This amount will not be refunded if the contract is stopped before its new expiration date.'
        }]
    };
    // And now we launch the actual update
    nuxeo.repository()
        .fetch(contractToFetch)
        .then(function(contract) {
            contract.set(propertiesToUpdate);
            return contract.save();
        })
        .then(function(contract) {
            console.log('Contract has been updated. Custom clauses are now as follows:');
            console.log(contract.properties['bccontract:customClauses']);
        })
        .catch(function(error) {
            console.log('Apologies, an error occurred while updating the contract.');
            console.log(error);
        });
    
    
  2. Save and run:

    $ node updateContract.js
    

 

Changing the State of a Document

This time we will switch the document to the deleted state, then in a second phase restore it to the state it was previously in (draft in this case). This can be used to manage a trash feature, by listing all documents not in the deleted state when launching queries, or only the ones in this state when looking at the trash.

Change document state to 'deleted'

  1. Create a file called trashContract.js with the following content.

    #!/usr/bin/env node
    const Nuxeo = require('nuxeo');
    const nuxeo = new Nuxeo({
        auth: {
            method: 'basic',
            username: 'Administrator',
            password: 'Administrator'
        }
    });
    var contractToFetch = '/default-domain/workspaces/North America/awesome-tech/skynet-ai-maintenance';
    nuxeo.repository()
        .fetch(contractToFetch)
        .then(function(contract) {
            return contract.followTransition('delete');
        })
        .then(function(contract) {
            console.log('Contract state has been changed. Contract is now as follows:');
            console.log(contract);
        })
        .catch(function(error) {
            console.log('Apologies, an error occurred while changing the contract state.');
            console.log(error);
        });
    
  2. Save and run:

    $ node trashContract.js
    

Restore document state to 'draft'

Restore the contract to its previous (draft) state.

  1. Create a file called restoreContract.js with the following content.

    #!/usr/bin/env node
    const Nuxeo = require('nuxeo');
    const nuxeo = new Nuxeo({
        auth: {
            method: 'basic',
            username: 'Administrator',
            password: 'Administrator'
        }
    });
    var contractToFetch = '/default-domain/workspaces/North America/awesome-tech/skynet-ai-maintenance';
    nuxeo.repository()
        .fetch(contractToFetch)
        .then(function(contract) {
            return contract.followTransition('undelete');
        })
        .then(function(contract) {
            console.log('Contract state has been changed. Contract is now as follows:');
            console.log(contract);
        })
        .catch(function(error) {
            console.log('Apologies, an error occurred while changing the contract state.');
            console.log(error);
        });
    
    
  2. Save and run:

    $ node restoreContract.js
    

Querying for Documents

Find a contract that needs to be deleted: it expired before 2016 and has a limitation clause in its content.

  1. Create a file called query.js which launches a query for all documents:

    • of the BCContract type
    • except archived versions and documents that are in the trash
    • that contain the keyword "limitation"
    • having expired before the end of 2015.
    #!/usr/bin/env node
    const Nuxeo = require('nuxeo');
    const nuxeo = new Nuxeo({
      auth: {
        method: 'basic',
        username: 'Administrator',
        password: 'Administrator'
      }
    });
    getContractsQuery = "SELECT * FROM BCContract " +
      " WHERE ecm:isVersion = 0 " +
      " AND ecm:currentLifeCycleState != 'deleted' " +
      " AND ecm:fulltext = 'limit%' " + // % will act as a joker
      " AND bccontract:expirationDate < DATE '2016-01-01' ";
    nuxeo
      .repository()
      .query({
        query: getContractsQuery
      })
      .then(function(contracts) {
        console.log('The following contracts have been retrieved:');
        console.log(contracts);
      })
      .catch(function(error) {
        console.log('Apologies, an error occurred while launching the query.');
        console.log(error);
      });
    
    
  2. Save and run:

    $ node query.js
    

Deleting a Document

The contract to delete has been identified as being the 2015 Annual Conference belonging to the Bon Appétit Caterer portfolio. Delete it.

  1. Create a file called deleteContract.js with the following content.

    #!/usr/bin/env node
    const Nuxeo = require('nuxeo');
    const nuxeo = new Nuxeo({
        auth: {
            method: 'basic',
            username: 'Administrator',
            password: 'Administrator'
        }
    });
    var contractToDelete = '/default-domain/workspaces/North America/Caterer/2015 Annual Conference';
    nuxeo.repository()
        .delete(contractToDelete)
        .then(function(res) {
            console.log('Contract has been deleted permanently. Bye bye contract!')
                // res.status === 204
        })
        .catch(function(error) {
            console.log('Apologies, an error occurred while deleting the contract.');
            console.log(error);
        });
    
    
  2. Save and run:

    $ node deleteContract.js
    

Note: This method does not include a "trash" behavior. The document is permanently erased. You should use the delete state and transitions to get documents to be moved to a trash before being permanently deleted.