oeCloud.io Example History Management

is a app which tries to demonstrate History feature provided by oeCloud.

What you’ll build

A oeCloud based app to demonstrate History Management feature, in order to do that we will create a Product model and perform different CURD operation, and learn how history is created for them.

Scenario which will be coverd are :

  • Update a record.
  • Delete a record.

for the above scenarios, we’ll see how history is maintained.

We’ll also learn, How to fetch history of a particular record.

What you’ll need:

  • You should have Node and NPM installed.
  • You should have oeCloud.io based example application running
  • You should have basic knowledge about using swagger explorer running at http://localhost:3000

How to complete this guide

Download and install oeCloud.io example application from here.

Note: We are using swagger* explorer, for exploring model api’s, alternatively user can choose to use any other explorer tool (Ex: Postman).

Once you have started the server, login to application and open a browser link: http://localhost:3000/explorer/.

You should create Product model if it is not available. Please refer to ‘how to create model guide’ here. You should able to see model as below.

Schema of Product Model:

{
    "name": "Product",
    "base": "BaseEntity",
    "strict": false,
    "plural": "Products",
    "idInjection": true,
    "options": {
        "validateUpsert": true
    },
    "properties": {
        "code": {
            "type": "string"
        },
        "name": {
            "type": "string"
        },
        "category": {
            "type": "string"
        },
        "price": {
            "type": "number"
        },
        "offeredSince": {
            "type": "date"
        },
        "active": {
            "type": "boolean"
        },
        "description": {
            "type": "string"
        }
    }
}

Create a record

Using swagger explorer, Post following two records in Product model[POST /api/Products ]

   [
        {
            "code": "HC1001",
            "name": "Caffe Latte",
            "category": "",
            "price": 1.23,
            "offeredSince": "2011-12-31T18:30:00.000Z",
            "description": "Strong Blend Espresso filled up with...",
            "_isDeleted": false,
            "flavourable": true,
            "flavours": []
        },
        {
            "code": "HC1002",
            "name": "Cappuccino",
            "category": "",
            "price": 1.23,
            "offeredSince": "2011-12-31T18:30:00.000Z",
            "description": "Strong Blend Espresso filled up with...",
            "_isDeleted": false,
            "flavourable": true,
            "flavours": []
        }
    ]

You should see output as shown below

[
  {
    "code": "HC1001",
    "name": "Caffe Latte",
    "category": "",
    "price": 1.23,
    "offeredSince": "2011-12-31T18:30:00.000Z",
    "flavourable": true,
    "flavours": [],
    "description": "Strong Blend Espresso filled up with...",
    "id": "59253612500f730c0b4fd487",
    "_type": "Product",
    "_createdBy": "admin",
    "_modifiedBy": "admin",
    "_createdOn": "2017-05-24T07:28:18.719Z",
    "_modifiedOn": "2017-05-24T07:28:18.719Z",
    "_version": "7cb083c4-98bb-4dac-9ebd-d04937cb2598",
    "_isDeleted": false
  },
  {
    "code": "HC1002",
    "name": "Cappuccino",
    "category": "",
    "price": 1.23,
    "offeredSince": "2011-12-31T18:30:00.000Z",
    "flavourable": true,
    "flavours": [],
    "description": "Strong Blend Espresso filled up with...",
    "id": "59253612500f730c0b4fd488",
    "_type": "Product",
    "_createdBy": "admin",
    "_modifiedBy": "admin",
    "_createdOn": "2017-05-24T07:28:18.726Z",
    "_modifiedOn": "2017-05-24T07:28:18.726Z",
    "_version": "18ead731-d775-4cc2-ace7-470cec3130e3",
    "_isDeleted": false
  }
]

Find records, which were inserted

To find records, use api - GET /Products

Output:

[
  {
    "code": "HC1001",
    "name": "Caffe Latte",
    "category": "",
    "price": 1.23,
    "offeredSince": "2011-12-31T18:30:00.000Z",
    "flavourable": true,
    "flavours": [],
    "description": "Strong Blend Espresso filled up with...",
    "id": "59253612500f730c0b4fd487",
    "_type": "Product",
    "_createdBy": "admin",
    "_modifiedBy": "admin",
    "_createdOn": "2017-05-24T07:28:18.719Z",
    "_modifiedOn": "2017-05-24T07:28:18.719Z",
    "_version": "7cb083c4-98bb-4dac-9ebd-d04937cb2598",
    "_isDeleted": false
  },
  {
    "code": "HC1002",
    "name": "Cappuccino",
    "category": "",
    "price": 1.23,
    "offeredSince": "2011-12-31T18:30:00.000Z",
    "flavourable": true,
    "flavours": [],
    "description": "Strong Blend Espresso filled up with...",
    "id": "59253612500f730c0b4fd488",
    "_type": "Product",
    "_createdBy": "admin",
    "_modifiedBy": "admin",
    "_createdOn": "2017-05-24T07:28:18.726Z",
    "_modifiedOn": "2017-05-24T07:28:18.726Z",
    "_version": "18ead731-d775-4cc2-ace7-470cec3130e3",
    "_isDeleted": false
  }
]

Update a record.

To update a record, use api - PUT /Products/{id}

Note: Set id as 59253612500f730c0b4fd487.

Sample data:

{
  "code": "xxx",
  "name": "yyy",
  "category": "zzz",
  "_version": "7cb083c4-98bb-4dac-9ebd-d04937cb2598"
}

You should see response like below.

{
  "code": "xxx",
  "name": "yyy",
  "category": "zzz",
  "price": 1.23,
  "offeredSince": "2011-12-31T18:30:00.000Z",
  "flavourable": true,
  "flavours": [],
  "description": "Strong Blend Espresso filled up with...",
  "id": "59253612500f730c0b4fd487",
  "_type": "Product",
  "_createdBy": "admin",
  "_modifiedBy": "admin",
  "_createdOn": "2017-05-24T07:28:18.719Z",
  "_modifiedOn": "2017-05-24T07:30:26.719Z",
  "_oldVersion": "7cb083c4-98bb-4dac-9ebd-d04937cb2598",
  "_version": "0c8dcd68-36cd-47c5-92e5-fdca4d49b085",
  "_isDeleted": false
}

Note: You should see the _version is changed again. _version field is mandatory.

Check history

  • Go to commondb database in mongodb. You can use Robomongo tool to do that.
  • You should see ProductHistory collection which is newly created.
  • You should query it and see the record. You should able to find record similar to below
{
    "_id" : ObjectId("5925250fd6a8857c25ffd920"),
    "_modelId" : 59253612500f730c0b4fd487,
   "code": "HC1001",
    "name": "Caffe Latte",
    "category" : "",
    "price" : 1.23,
    "offeredSince" : ISODate("2011-12-31T18:30:00.000Z"),
    "flavourable" : true,
    "flavours" : [],
    "description" : "Strong Blend Espresso filled up with...",
    "_type" : "Product",
    "_createdBy" : "admin",
    "_modifiedBy" : "admin",
    "_createdOn" : ISODate("2017-05-24T05:42:34.845Z"),
    "_modifiedOn" : ISODate("2017-05-24T05:42:34.845Z"),
    "_scope" : [ 
        "tenantId:default"
    ],
    "_autoScope" : {
        "tenantId" : "default"
    },
    "_version" : "c1f907ea-30ab-49ac-b10e-9c1e0c44f50c",
    "_isDeleted" : false,
}

Observation: result will be the state of record before it was updated.

Delete a record.

To Delete a record, use api - Delete /Products/{id}/{version}

> **Note:** Set id as 59253612500f730c0b4fd487 and version as 0c8dcd68-36cd-47c5-92e5-fdca4d49b085. 

Output:

{
    "count" : 1
}

Now login to mongodb and observe the another record created in ProductHistory collection similar to below.

{
    "_id" : ObjectId("5925272cd6a8857c25ffd922"),
    "_modelId" : 59253612500f730c0b4fd487,
    "code" : "xxx",
    "name" : "yyy",
    "category" : "zzz",
    "price" : 1.23,
    "offeredSince" : ISODate("2011-12-31T18:30:00.000Z"),
    "flavourable" : true,
    "flavours" : [],
    "description" : "Strong Blend Espresso filled up with...",
    "_type" : "Product",
    "_createdBy" : "admin",
    "_modifiedBy" : "admin",
    "_createdOn" : ISODate("2017-05-24T05:42:34.845Z"),
    "_modifiedOn" : ISODate("2017-05-24T06:15:43.239Z"),
    "_scope" : [ 
        "tenantId:default"
    ],
    "_autoScope" : {
        "tenantId" : "default"
    },
    "_oldVersion" : "c1f907ea-30ab-49ac-b10e-9c1e0c44f50c",
    "_version" : "9456e149-7a5b-48e5-8e7a-43eb25846878",
    "_isDeleted" : false
}
**Observation:** result will be array of two record first record which was created second record which was updated in last step.

Observation made:

Sr.No Scenario Observation
1 Upate a record When a record is updated, its previous state is stored as history and current record is updated with new data.
2 Delete a record When a record is deleted, its previous state is stored as history and current record is deleted from the database.
3 Fetch history for a record when a filter on _modelId is applied it return the history of that record only. This should be done directly in database

Internals

  • Model History functionality is available to Model when model is extended from BaseEntity. By default it is enabled. You can disabled this functionality by disabling mixin as shown below.
 "mixins": {
        "HistoryMixin": false
    }
  • History collection is created for every model. Original id of record is stored as _modelId and thus you can track life cycle of a record using the _modelId

Summary

  • History functionality is used to maintain model’s record history.
  • This functionality will be useful as entire life cycle of a model’s record can be traced and tracked