# Sharepoint

SharePoint watcher is easy enough to setup and only requires the Graph authorization details.

The Watcher automatically polls for folder changes within a sharepoint library and gives you an SDK as well as a callback any time a new file is added or changed. You will also get the item in the callback if a field has been updated.

This allows for automated processes to be kicked off any time something within the watched SharePoint directory is modified.

## Example Configuration

Here's the fully configured and ready to use snippet of setting this up within Perigee.

```csharp
PerigeeApplication.ApplicationNoInit("Sharepoint Demo", (taskConfig) =>
    {
        
        taskConfig.AddSharepointWatch("Sharepoint",
            taskConfig.GetValue<string>("Sharepoint:tenant"), 
            taskConfig.GetValue<string>("Sharepoint:appID"),
            taskConfig.GetValue<string>("Sharepoint:appSecret"), 
            taskConfig.GetValue<string>("Sharepoint:site"),
            taskConfig.GetValue<string>("Sharepoint:drivePath"), 
            taskConfig.GetValue<string>("Sharepoint:listName"), 
            (ct, log, api, sync, items) =>
            {
                //Process file here
                return true;
                
            }).LinkToConfig("Sharepoint:enabled");

    });

```

All of the above are referencing a node in the <mark style="color:red;">`appsettings.json`</mark> file, you would need to fill this out with your own authorization details.

```json
"Sharepoint": {
    "tenant": "guid",
    "appID": "guid",
    "appSecret": "secret",
    "drivePath": "",
    "site": "https://mydomain.sharepoint.com/sites/demo/",
    "listName": "Documents",
    "enabled": true
  },
```

* **tenant/appID/appSecret** - These will be pulled from the Azure portal app registration.
* The **drivePath** supplied may be blank if you're pulling files from the root of the drive. Otherwise supply the name of the folder to watch.
* The **site** key is the full address of the sharepoint site. It's used to perform a graph lookup of the ID records required.
* The **listName** is the name of the list behind the drive. If using the default document library, this will be "`Documents`". If you've created a custom library, something like "Input", then you will need to set the **listName** to that instead.
* The **enabled** flag is to turn the process on or off and is tied to the `.LinkToConfig()` line above.

## SDK & Callbacks

### Sync Class - Callback Property

The <mark style="color:blue;">**`sync`**</mark> property sent back by the SharePoint watcher contains all of the relevant ID's needed to perform requests within SharePoint. Most importantly:

* ListID
* DriveID
* SiteID
* SiteName

### <mark style="color:blue;">`Items`</mark> callback

When new items arrive to be processed, you'll get a list of those items in that callback. Each item in the list contains all of the properties you'd need to check the item that was changed.&#x20;

```csharp
List<GraphAPIModel.Drive.Children> items;


item.Id;                            // The ID of the item
item.MicrosoftGraphDownloadUrl;     // The URL to download the item
item.Name;                          // The name of the item
item.CreatedBy;                     // The user referencee class of who created it
item.File;                          // If it is a file, this has the file details
item.Folder;                        // If it is a folder, this has the folder details
```

{% hint style="warning" %}
To see the full list and response please check out the [Microsoft documentation ](https://learn.microsoft.com/en-us/graph/api/driveitem-get?view=graph-rest-1.0\&tabs=http#response-1)
{% endhint %}

### Change Notification&#x20;

Want to check if the changed notification is a file?

```csharp
foreach (var item in items)
{
    //Guard change notifications for folders
    if (item.Folder != null || item.File == null) continue;
    
    //Only process file changes
    log?.LogInformation("New file to process from {name} - {item} [{size}]", 
        item.CreatedBy.User.DisplayName, 
        item.Name, 
        item.Size);
}
```

### Get Item + Fields

This call retrieves the item from a drive, and expands the response to include the list detail and fields as well

```csharp
var details = api.GetItemWithList(sync.siteID, sync.driveID, item.Id);
```

The list fields are important as they allow you to get the custom field values. In this example, we have a custom field called "Status" that we can update along the process:

```csharp
var status = details.listItem.fields["Status"];
//Status = "Ready"
```

### Get Item by Path

To get an item if you already have the path.

If expand is true, you'll also receive the list item details along with the response.

```csharp
api.GetItemByPath(sync.siteID, "Folder/File.txt", expandListItem = true)
```

### Get Item by Path In Drive

To get an item if you already have the path for a given DriveID.

If expand is true, you'll also receive the list item details along with the response.

```csharp
api.GetItemByPath(DriveID, "Folder/File.txt", expandListItem = true)
```

### Generate SharePoint Download Link

You can generate a direct download link to a SharePoint item by giving the Site, library, and path to the item.

The resulting string is generated and uses the SharePoint `/Download.aspx` page.

```csharp
api.GenerateSharepointDownloadLink(Site, "Shared Documents", "Folder/File.txt");
```

### Delete an Item

To remove an item by ID:

```csharp
api.DeleteItem(sync.siteID, ItemID);
```

### Patch a Field

After retrieving the [List Details](#get-item-+-fields), you can patch any of the fields with the `PatchField` command:

```csharp
api.PatchField(sync.siteID, sync.listID, details.listItem.id, 
new { 
    Status = "Success",
    Notes = "We are done!"
    });
```

### Get Another Drive

If you need to get a different drive, say a **Processed** drive, you can do so by querying for the drive:

```csharp
var Drives = api.GetSiteDrives(sync.siteID);
var ProcessedDriveID = Drives.Where(f => f.name.Equals("Processed")).FirstOrDefault()?.id ?? "";
```

### Uploading an item

To upload an item to a drive you must supply the `DriveID`, `Name`, `Content`, and `MIME Type`.

We'll upload a document to the [Processed drive as shown here](#get-another-drive).

```csharp
var uploaded = api.UploadFile(ProcessedDriveID, 
    "MyZip.zip", new byte[] {0x0}, "application/zip");

//Get the details from the newly uploaded item
var itemDetails = api.GetItemWithList(sync.siteID, ProcessedDriveID, uploaded.Id);
```

### Download an Item

To download an item from SharePoint we highly recommend first getting the [List Details](#get-item-+-fields) as well as it can provide a backup download link. Then simply call download:

```csharp
if (item.MicrosoftGraphDownloadUrl == null)
{
    //Try resetting it from the details list
    item.MicrosoftGraphDownloadUrl = new Uri(details.DownloadURL);
}

byte[] File = api.DownloadItem(item);
```

### Implementing your own

To call any other method not supplied here use the internal graph call client and supply the right path, the rest will be taken care of automatically.

```csharp
var response = api.RestGraphCall<GraphAPIModel.Generic.Response>(
    $"/sites/{sync.siteID}/lists/{sync.ListName}", Method.Get);
```
