Introduction
Sunbird platform enabled the contributors to create the content & once reviewers review that content and approved it is available for consumption.
This wiki explains the design of the content contribution and review lifecycle.
Background & Problem statement:
We have enabled the content contribution under a project. Any contributor contributes the content only through the project & he/she can’t reuse the same content for multiple projects. Contribution related information currently, we are storing at the content level & this is not a scalable solution.
Currently, We have two levels of reviewers - contribution org reviewers and sourcing org reviewers and this is hardcoded in the code level. If we want to introduce a new review process we need to do again code changes to support & this is not a scalable solution.
Key Design Problems:
Define contribution lifecycle & enable API’s
Enable multi-level review process for contribution.
Design:
As of now, we are storing all the contribution and review information at the content level. Going forward we will split the information into the individual objects as
Content vs Contribution vs ReviewObject metadata:
Content - Properties | Contribution - Properties | Review Object - Properties |
---|---|---|
name | CB:UUID | RO:UUID |
status (at content level) | name | contributionId |
artifactUrl | contentId | reviewerId |
previewUrl | collectionId | status (by this reviewer) |
downloadUrl | unitid | reviewerName |
programId | publishComments | |
many more… | userId | rejectComments |
userName | requestChanges | |
index | ||
many more… |
Lifecycle management:
When we create a content object, at the time the contribution object will also be created. We will not maintain any status at the contribution object level. Review object will be created when the content is submitted for review. From there review lifecycle will be dependent on the reviewer. Below is the lifecycle of the review objects. We can have multiple levels of reviews for a given content/contribution object.
The following image explains the weightage/score of statuses.
Approved is
1
Submitted is
2
Request Changes is
3
Rejected is
4
Ex-1: If the content is being reviewed parallelly it will have two review objects. Let's say one of the review objects is having the status Approved
and another one has Rejected
. Now the Rejected
status outweighs the Approved
status. Hence the content's actual status becomes Rejected
.
Ex-2: Content again having two review status Approved
and RequestChanges
. RequestChanges
will take precedence.
API Spec:
Content Create wrapper API:
Method: POST
Path: https://dock.sunbirded.org/api/program/v1/contribution/create
Request Payload:
{ "request": { "contribution": { "programId": "", "collectionId": "", "unitId": "", "name": "", "userName": "", "userId": "" }, "content": { "name": "", "mimeType": "", "primaryCategory": "", "contentType": "", "creator": "", "createdBy": "" } } }
Response Payload:
{ "id": "api.contribution.create", "ver": "3.0", "ts": "2021-02-24T06:50:03ZZ", "params": { "resmsgid": "64b393de-a9e1-4857-97e4-7caf270b4cfc", "msgid": null, "err": null, "status": "successful", "errmsg": null }, "responseCode": "OK", "result": { "content": { "identifier": "do_1132231119107358721191", "versionKey": "1614150204576" }, "contribution": { "identifier": "CO:1132231119107358721191" } } }
Content Create wrapper API:
Method: POST
Path: https://dock.sunbirded.org/api/program/v1/contribution/update
Request Payload:
{ "request": { "content": { "name": "", "versionKey": "1612319891349" }, "contribution": { "contentId": "", "collectionId": "", "programId": "", "userId": "" }, "review": { "contributionId": "CO:2222222222222", "status": "Approved", // Approved, Rejected, RequestChanges and submited "publishComments": "", "reviewerName": "", "reviewerId": "" } } }
Response Payload:
{ "id": "api.contribution.update", "ver": "3.0", "ts": "2021-02-24T07:03:24ZZ", "params": { "resmsgid": "e7721609-4b75-4358-b0ff-eeac0d8f3f7a", "msgid": null, "err": null, "status": "successful", "errmsg": null }, "responseCode": "OK", "result": { "content": { "identifier": "do_1132231119107358721191", "versionKey": "1614150204576" }, "contribution": { "identifier": "CO:1132231119107358721191" }, "review": { "identifier": "RO:1132231119107358721191" } } }
Content review wrapper API:
Method: POST
Path: https://dock.sunbirded.org/api/program/v1/contribution/review
Request Payload:
{ "request": { "review": { "contentId": "", "collectionId": "", "programId": "" } } }
Response Payload:
{ "id": "api.contribution.review", "ver": "3.0", "ts": "2021-02-24T07:03:24ZZ", "params": { "resmsgid": "e7721609-4b75-4358-b0ff-eeac0d8f3f7a", "msgid": null, "err": null, "status": "successful", "errmsg": null }, "responseCode": "OK", "result": { "content": { "identifier": "do_1132231119107358721191", "versionKey": "1614150204576" }, "review": { "identifier": "RO:1132231119107358721191" } } }
Content publish wrapper API:
Method: POST
Path: https://dock.sunbirded.org/api/program/v1/contribution/publish
Request Payload:
{ "request": { "review": { "contentId": "", "collectionId": "", "programId": "" } } }
Response Payload:
{ "id": "api.contribution.publish", "ver": "3.0", "ts": "2021-02-24T07:03:24ZZ", "params": { "resmsgid": "e7721609-4b75-4358-b0ff-eeac0d8f3f7a", "msgid": null, "err": null, "status": "successful", "errmsg": null }, "responseCode": "OK", "result": { "content": { "identifier": "do_1132231119107358721191", "publishStatus": "Publish Operation for Content Id 'do_1132224396322734081169' Started Successfully!", }, "review": { "identifier": "RO:1132231119107358721191" } } }
Content list wrapper API:
Method: POST
Path: https://dock.sunbirded.org/api/program/v1/contribution/list
Request Payload:
{ "request": { "review": { "collectionId": "", "programId": "" } } }
Response Payload:
{ "id": "api.contribution.list", "ver": "3.0", "ts": "2021-02-24T15:26:10ZZ", "params": { "resmsgid": "962d09e6-91ef-4630-b546-a9529f349921", "msgid": null, "err": null, "status": "successful", "errmsg": null }, "responseCode": "OK", "result": { "count": 2, "contribution": [ { "content": { "name": "", "creator": "", "createdBy": "" }, "contribution": { "name": "", "collectionId": "", "programId": "", "unitId": "" }, "review": [ { "contributionId": "CO:11111", "status": "Approved", "publishComments": "", "reviewerName": "", "reviewerId": "" }, { "contributionId": "CO:2222", "status": "Approved", "publishComments": "", "reviewerName": "", "reviewerId": "" } ] }, { "content": {}, "contribution": {}, "review": [ {}, {} ] } ] } }
Conclusion:
<TODO>
0 Comments