koltyakov / gosip

⚡️ SharePoint SDK for Go
https://go.spflow.com
MIT License
140 stars 32 forks source link

Modern In place record management #50

Closed Ballzer0 closed 1 year ago

Ballzer0 commented 2 years ago

Record management documentation: https://go.spflow.com/samples/record-management

In the summary it says that you are mimicing the CSOM calls. This means you are doing "Classic IN PLACE Records Management"

Is there any plans to impliment "Modern IN PLACE Records Management"? As there is at least an api method for it now?

example of modern api call to declare record.

POST:  https://domain.sharepoint.com/sites/{nameOfSite}/_api/web/lists/GetByTitle/{nameOfList}/items({itemID})/SetComplianceTag()

BODY:
{
    "complianceTag": "{nameOfComplianceTag}",
    "isTagPolicyHold": "False",
    "isTagPolicyRecord": "True",
    "isEventBasedTag": "False",
    "isTagSuperLock": "False"
}

example of modern api call to undeclare record.

POST:  https://domain.sharepoint.com/sites/{nameOfSite}/_api/web/lists/GetByTitle/{nameOfList}/items({itemID})/SetComplianceTag()

BODY:
{
    "complianceTag": "{nameOfComplianceTag}",
    "isTagPolicyHold": "False",
    "isTagPolicyRecord": "False",
    "isEventBasedTag": "False",
    "isTagSuperLock": "False"
}
koltyakov commented 2 years ago

Thanks for the suggestion! This is something relatively easy to add.

Meanwhile this (as well as any API request) also can be done in a custom way: https://go.spflow.com/api/http-client

Ballzer0 commented 2 years ago

Yeah, ive been testing this http call against and comparing it to this part of the Modern record management documentation: https://docs.microsoft.com/en-us/microsoft-365/compliance/record-versioning?view=o365-worldwide

For some reason its not adding the record to the "Preservation Hold Library" when you use SetComplianceTag()

specifically this doesnt happen when we use that method. We are testing to see if its possible to automate this section aswell when we unlock record image

Ballzer0 commented 2 years ago

Using sharepoint rest explorer. https://s-kainet.github.io/sp-rest-explorer/#/

We have found 2 methods that seem to work even better. They even add the files as expected to the "Preservation Hold Library"

to Unlock a a record:

POST:  https://domain.sharepoint.com/sites/{nameOfSite}/_api/SP.CompliancePolicy.SPPolicyStoreProxy.UnlockRecordItem()

BODY:
{
     listUrl: "/sites/{nameOfSite}/{nameOfList}", 
     itemId: "{itemID}"
}

to Lock a a record:

POST:  https://domain.sharepoint.com/sites/{nameOfSite}/_api/SP.CompliancePolicy.SPPolicyStoreProxy.LockRecordItem()

BODY:
{
     listUrl: "/sites/{nameOfSite}/{nameOfList}", 
     itemId: "{itemID}"
}

You still need to use SetComplianceTag to actually set the tag

koltyakov commented 2 years ago

Hey @Ballzer0,

I'm not sure I know well how the Modern In Place Records management works.

Configured and Published Retention Labels, but nothing appear on a site yet. So can't verify the methods:

package api

import (
    "bytes"
    "encoding/json"
    "fmt"
)

// https://support.microsoft.com/en-us/office/apply-retention-labels-to-files-in-sharepoint-or-onedrive-11a6835b-ec9f-40db-8aca-6f5ef18132df

// LockRecordItem locks a record via modern API method SP.CompliancePolicy.SPPolicyStoreProxy.UnlockRecordItem()
func (records *Records) LockRecordItem() error {
    itemID, listURL, err := records.getItemContext()
    if err != nil {
        return err
    }

    client := NewHTTPClient(records.item.client)
    endpoint := fmt.Sprintf("%s/_api/SP.CompliancePolicy.SPPolicyStoreProxy.LockRecordItem()", getPriorEndpoint(records.item.endpoint, "/_api"))

    prop := map[string]interface{}{}
    prop["listUrl"] = listURL
    prop["itemId"] = itemID
    body, _ := json.Marshal(prop)

    _, err = client.Post(endpoint, bytes.NewBuffer(body), records.item.config)
    return err
}

// UnlockRecordItem unlocks a record via modern API method SP.CompliancePolicy.SPPolicyStoreProxy.UnlockRecordItem()
func (records *Records) UnlockRecordItem() error {
    itemID, listURL, err := records.getItemContext()
    if err != nil {
        return err
    }

    client := NewHTTPClient(records.item.client)
    endpoint := fmt.Sprintf("%s/_api/SP.CompliancePolicy.SPPolicyStoreProxy.UnlockRecordItem()", getPriorEndpoint(records.item.endpoint, "/_api"))

    prop := map[string]interface{}{}
    prop["listUrl"] = listURL
    prop["itemId"] = itemID
    body, _ := json.Marshal(prop)

    _, err = client.Post(endpoint, bytes.NewBuffer(body), records.item.config)
    return err
}

func (records *Records) getItemContext() (int, string, error) {
    // Get item's context
    data, err := records.item.Select("Id,ParentList/RootFolder/ServerRelativeURL").Expand("ParentList/RootFolder").Get()
    if err != nil {
        return 0, "", err
    }

    item := &struct {
        ID         int `json:"Id"`
        ParentList struct {
            RootFolder struct {
                ServerRelativeURL string `json:"ServerRelativeUrl"`
            } `json:"RootFolder"`
        } `json:"ParentList"`
    }{}

    if err := json.Unmarshal(data.Normalized(), &item); err != nil {
        return 0, "", err
    }

    return item.ID, item.ParentList.RootFolder.ServerRelativeURL, nil
}

Could try these ad-hoc methods in your code base & environment?

P.S. I haven't pushed these changes to dev as I'm not sure, you can find the changes in https://github.com/koltyakov/gosip/tree/dev-modern-records and try referencing branch-tagged version.