# 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);
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.perigee.software/core-modules/event-sources/watchers/sharepoint.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
