Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Azure AD Certificate authentication
This article is the sample showing Gosip custom auth with AAD Certificate Authorization.
1. Create or use existing app registration
2. Make sure that the app is configured for a specific auth scenario:
Certificate
Follow instructions: https://docs.microsoft.com/en-us/sharepoint/dev/solution-guidance/security-apponly-azuread
O365 Admin -> Azure Active Directory
Generate self-signed certificate
or on a Linux or macOS client via openssl
:
Get scripts.
New App Registration
Accounts in this organizational directory only
API Permissions -> SharePoint :: Application :: Sites.FullControl.All -> Grant Admin Consent
Certificates & Secrets -> Upload .cer
file
private.json
sample:
🔐 SharePoint authentication strategies implemented in Gosip
Auth strategy should be selected corresponding to your SharePoint environment and its configuration.
Import path strategy "github.com/koltyakov/gosip/auth/{strategy}"
. Where /{strategy}
stands for a strategy auth package.
/azurecert
✅
❌
/azurecreds
✅
❌
/azureenv
✅
❌
/device
✅
❌
/saml
✅
❌
/addin
✅
❌
/ntlm
❌
✅
/adfs
✅
✅
/fba
❌
✅
/tmg
❌
✅
JSON and struct representations are different in terms of language notations. So credentials parameters names in private.json
files and declared as structs initiators vary.
Gosip supports custom (ad hoc) strategies. Some worthy are boiled in the Sandbox to be added later on to the main package in a case of the demand.
Strategy name
SPO
On-Prem
Credentials sample(s)
On-Demand
✅
✅
Alternative NTLM
❌
✅
When storing credential in local private.json
files, which can be handy in local development scenarios, we strongly recommend to encode secrets such as password
or clientSecret
using cpass. Cpass converts a secret to an encrypted representation which can only be decrypted on the same machine where it was generated. This minimize incidental leaks, i.e. with git commits.
⚡️ SharePoint SDK for Go (Golang)
Unattended authentication using different strategies.
Simplified API consumption (REST, CSOM, SOAP, etc.).
SharePoint-aware embedded features (retries, header presets, error handling).
SharePoint Online (SPO).
On-Premises (2019/2016/2013).
SharePoint Online:
SharePoint On-Premises 2019/2016/2013:
Let's assume it's SharePoint Online and Add-In Only permissions. Then strategy "github.com/koltyakov/gosip/auth/addin"
sub package should be used.
AuthCnfg's from different strategies contains different options relevant for a specified auth type.
Provides a simple way of constructing API endpoint calls with IntelliSense and chainable syntax.
Provides generic GET/POST helpers for REST operations, reducing amount of http.NewRequest
scaffolded code, can be used for custom or not covered with a Fluent API endpoints.
Low-lever SharePoint-aware HTTP client from github.com/koltyakov/gosip
package for custom or not covered with a Fluent API client endpoints with granular control for HTTP request, response, and http.Client
parameters. Used internally but almost never required in a consumer code.
SPClient has Execute
method which is a wrapper function injecting SharePoint authentication and ending up calling http.Client
's Do
method.
, ,
Within experimental in the these deserve mentioning:
syntax for SharePoint object model.
based with user credentials
permissions
ADFS user credentials (automatically detects in strategy)
(NTLM)
(ADFS, WAP -> Basic/NTLM, WAP -> ADFS)
Behind a reverse proxy (, , )
(FBA)
The authentication options can be provided explicitly or can be read from a configuration file (see ).
Many auth flows have been "copied" from library (used as a blueprint), which we intensively use in Node.js ecosystem for years.
Fluent API and wrapper syntax are inspired by which is also the first-class citizen on almost all our Node.js and front-end projects with SharePoint involved.
NTLM handshake authentication
The default NTLM authentication uses go-ntlmssp - the great package by Azure team. However, we found rare environments where it fails for some reason. Fortunately, there is an alternative.
Use the alternative only as a fallback method if go-ntlmssp doesn't work and don't forget to post an issue to help making Open Source tools better.
Azure AD Device Token authentication
This article is the sample showing Gosip custom auth with AAD Device Token Authorization.
If you want users to sign in interactively, the best way is through device token authentication. This authentication flow passes the user a token to paste into a Microsoft sign-in site, where they then authenticate with an Azure Active Directory (AAD) account. This authentication method supports accounts that have multi-factor authentication enabled, unlike standard username/password authentication.
1. Create or use existing app registration
2. Make sure that the app is configured to support device flow
Authentication settings
Public client/native (mobile & desktop)
Suggested Redirect URIs for public clients (mobile, desktop) - https://login.microsoftonline.com/common/oauth2/nativeclient - checked
Default client type - Yes - for Device code flow, learn more
App permissions
Azure Service Management :: user_impersonation
SharePoint :: based on your application requirements
etc. based on application needs
When started the application interacts with user using device login.
After opening the link, providing device code and authenticating in browser the app is ready for communication with your SharePoint site.
The strategy caches auth token in the context of the AAD ClientID. As a result, you won't see the sign in message. If it's not the desired behavior .CleanTokenCache()
method can be called to clean the local cache.
Note, that the technique is mostly applicable when user interaction is assumed. Usage of that auth approach in the headless scenarios is not the best as it can lead "stuck" application if no-one expects sign in interaction.
Browser input interactive auth flow
During the development, it's common to face a situation when production-level auth (AddIn Onli, Azure AD application) can't be configured in the desired timeframes and no auth strategies work. A simple example might be 2FA (multi-factor authentication) or custom ADFS provider. As a quick workaround, the On-Demand auth can help.
On-Demand means that an interactive browser session is started where a user can provide the credentials as if he/she opens the SharePoint site and follows the same flow as reaching the site in a browser.
In that strategy, the application actually opens the browser and communicates via debug protocol for the auth cookies when uses them in the requests.
Check On-Demand auth sources at GitHub.
On-Demand auth is based on Lorca project, however, a vital part of the functionality is not exposed as a public API in Lorca, so the dependency is imported from a fork with only that small change in exposing one additional method.
Lorca masters Chrome Debug Protocol, therefore, the Chrome/Chromium browser must be installed in the system where On-Demand auth is intended to be called.
Chrome is required for the strategy to work
On-Demand configuration assumes only SiteURL to be provided as everything else is dynamically resolved while the transition to the browser page.
The auth technique works for any strategy which is based on the cookies.
The strategy caches the cookies in the context of the SharePoint host. As a result, you won't see the credentials prompt each time. If it's not the desired behavior .CleanCookieCache()
method can be called to clean the local cache.
Note, that the technique is only applicable when user interaction is assumed. Never ever use that auth approach in headless scenarios.
Azure AD environment-based authentication
1. Create or use existing app registration
2. Make sure that the app is configured for a specific auth scenario:
Client credentials (might not work with SharePoint but require a Certificate-based auth)
Certificate
Username/Password (public clients flows must be enabled)
Managed identity
Follow instructions: https://docs.microsoft.com/en-us/sharepoint/dev/solution-guidance/security-apponly-azuread
O365 Admin -> Azure Active Directory
Generate self-signed certificate
or on a Linux or macOS client via openssl
:
Get scripts.
New App Registration
Accounts in this organizational directory only
API Permissions -> SharePoint :: Application :: Sites.FullControl.All -> Grant Admin Consent
Certificates & Secrets -> Upload .cer
file
Use environment variables to provide creds bindings:
AZURE_TENANT_ID
- Directory (tenant) ID in App Registration
AZURE_CLIENT_ID
- Application (client) ID in App Registration
For certificate-base auth:
AZURE_CERTIFICATE_PATH
- path to .pfx
file
AZURE_CERTIFICATE_PASSWORD
- password used for self-signed certificate
For username/password auth:
AZURE_USERNAME
AZURE_PASSWORD
Environment variables can be automatically injected in a runtime for Azure AAD library. To use injection add correcponding environment variables in private.json
into env
JSON property:
Frequently asked questions
Go has many strong parts. It's damn simple, friendly for large team projects, super fast in a compilation, fast in runtime and requires little or no prerequisites. It is friendly for concurrent processes, modern hardware architectures, network applications and cloud-based solutions, DevOps tool.
Go promises backward compatibility and long support period. Many say that Go is "the next enterprise big thing" and one of the default preferred languages. Lots of modern huge and awesome applications are written in Go while staying simple and reliable. Many main vendors use Go in one way or another.
Go has lots of wonderful libraries at disposal. But little or no for SharePoint... until Gosip. So it's our spot to provide such an option.
In the first place, Gosip is for developers who use Go on their daily basis and who got to integrate their apps with SharePoint without diving too deep to learn it from the grouds up.
SharePoint is complicated. We spent years solving different complex and misc problems with the platform. Not only for the end-users and customers but also for other developers and Open Source community.
Recently more and more of our internal tools have been written in Go. Our opinion is that a tool is better if it is used in a variety of different scenarios and by different teams. So we decided to deliver our Go+SP experience to Open Source and improve it together with you.
If you have no idea what is Go probably Gosip is not for you, but if you have no "Why Go?" question Gosip is an option to master SharePoint API with ease.
Anywhere where you considering an application written in Go reasonable.
Use-cases can vary, let me describe just a few we use the library intensively:
Web services and APIs (dedicated web API consumed by many clients)
Microservices applications
Schedule runners, workflow workers, queue listeners
CLI based tools & scripting
Relays and gateways with elements of API communication
Development toolchains
We would love to hear your unique use-case and even help you with our opinion.
Under any circumstances, we're not even suggesting replacing anything with anything. It's just wrong positioning.
Gosip and Go, in particular, is yet another available option that might or might not suit one needs. Even more, we use all the combination of technologies together following common sense, expertise, constraints, and many more factors. Sometimes something in Go helps in a PowerShell script. Sometimes a great .Net library is used together in Go worker. And don't forget we're also sort of ambassadors of Node.js ecosystem for SharePoint.
Together is stronger. For no reason don't search for a holly war aspect use whatever feels better for you for delivering great product or service.
Azure AD authorization with username and password
This article is the sample showing Gosip custom auth with AAD Username/Password Authorization.
Follow the steps
private.json
sample:
AddIn Only authentication
SharePoint Add-Ins will stop working for new tenants as of November 1st, 2024 and they will stop working for existing tenants and will be fully retired as of April 2nd, 2026. See more.
Realm can be left empty or filled in, that will add small performance improvement. The easiest way to find tenant is to open SharePoint Online site collection, click Site Settings
-> Site App Permissions
. Taking any random app, the tenant ID (realm) is the GUID part after the @
.
See more details of AddIn Configuration and Permissions.
private.json
sample:
It's important to know that the legacy AddIn authentication's Client Secrets are issued for a limited time. After expiration, if not managed right way there is a risk to get a service connection aunothorized with the following message:
AADSTS7000222: The provided client secret keys for app '***' are expired. Visit the Azure portal to create new keys for your app: https://aka.ms/NewClientSecret, or consider using certificate credentials for added security: https://aka.ms/certCreds.
To renew an AddIn please follow https://docs.microsoft.com/ru-ru/sharepoint/dev/sp-add-ins/replace-an-expiring-client-secret-in-a-sharepoint-add-in or, long story short:
AddIn Only auth is considered a legacy, in a production Azure Cert is vendor recommended.
In new subscriptions you can face Grant App Permission disabled. You'll be getting the following error:
To enable this feature, connect to SharePoint using Windows PowerShell and then run:
set-spotenant -DisableCustomAppAuthentication $false
.
No-auth mode
It's not an auth strategy but a mode without any authentication flow applied to the SPClient.
Anonymous mode can be handy in a situation when Gosip SharePoint-aware helpers intended to be used however authentication is handled by any other middleware.
Only SiteURL
is required.
private.json
sample:
AddIn Configuration and Permissions
For AddIn Only authentication to work register new addin within your SharePoint Online tenant.
Navigate to app registration page: https://{organization}.sharepoint.com/sites/{site}/_layouts/15/appregnew.aspx
Click "Generate" button next to Client Id and Client Secret, fill in Title, App Domain, Redirect URI (you can type in any values you want).
Copy Client Id and Client Secret and press "Create" button.
Apply permissions for the app on tenant or site collection level.
https://{organization}-admin.sharepoint.com/_layouts/15/appinv.aspx
https://{organization}.sharepoint.com/sites/{site}/_layouts/15/appinv.aspx
Resolve addin by Client Id and paste in App's Permissions Request XML:
Click "Create" and "Trust It".
To check which app principals are assigned for a site collection use:
https://{organization}.sharepoint.com/sites/{site}/_layouts/15/appprincipals.aspx
In new subscriptions you could be needed to enable Grant App Permission. Connect to SharePoint using Windows PowerShell and then run:
set-spotenant -DisableCustomAppAuthentication $false
.
Configuring authentication and API client
Based on authentication provider supported and configured in your SharePoint Farm environment different library authentication strategies might be or not be applicable.
If you have no idea which strategy is used within your farm the question is better be addressed to SharePoint admins.
Let's assume it's SharePoint Online and Add-In Only permissions. Then strategy "github.com/koltyakov/gosip/auth/addin"
sub package should be used.
It could have been SharePoint On-Premise and NTLM and strategy "github.com/koltyakov/gosip/auth/ntlm"
as imported strategy.
Different authentication strategies assumes different credential parameters. Sometimes it can be Username/Password, sometimes CertPath or ClientID/ClientSecret. Please refer a specific strategy documentation for relevalt for the auth type parameters.
Credential can be passed directly in AuthCnfg's struct. Here a sample for addin
:
An anternative is using a configuration file (see more). Which is a JSON containing the same parameters as used with AuthCnfg's struct.
Now when auth is bound it should be passed to client and Fluent API instance:
Most of the samples starts with sp
assuming configuration described above is already in place.
🔨 Provides low-level communication with any SharePoint API
Gosip HTTP client for SharePoint allows consuming any HTTP resource or API, it can even bypass SharePoint site pages (with all assets) within a dev toolchain (proxy, dev server).
Gosip HTTP client is SharePoint nuances-aware, it takes care under the hood of such things as headers, API calls retries, threshholds, error handling, POST API requests Digests, and, of course, authentication and its renewal.
However, dealing at low-level, means you should know SharePoint API rather well and you're ok with the verbosity of Go. If you just starting with SharePoint please consider Fluent API client first and HTTP client for none covered methods and custom stuff.
HTTP Client methods covers those which used in SharePoint all the way.
In a contract to default Go http.Client
's .Do
method, Gosip HTTP Client's methods proceed and read response body to return array of bytes. Which reduce amount of scaffolded code a bit and more handy for the REST API consumption, in our opinion, which is 90% of use cases.
Sends GET
request, embeds "Accept": "application/json;odata=verbose"
header as a default. Use it for REST API GET
calls.
Sends POST
request, embeds "Accept": "application/json;odata=verbose"
and "Content-Type": "application/json;odata=verbose;charset=utf-8"
headers as default. X-RequestDigest is received, cached, and embed automattically. Use it for REST API POST
calls.
Sends POST
request, embeds "Accept": "application/json;odata=verbose"
, "Content-Type": "application/json;odata=verbose;charset=utf-8"
, and "X-Http-Method": "DELETE"
headers as default. X-RequestDigest is received, cached, and embed automattically. Use it for REST API POST
calls with delete resource intention.
Sends POST
request, embeds "Accept": "application/json;odata=verbose"
, "Content-Type": "application/json;odata=verbose;charset=utf-8"
, and "X-Http-Method": "MERGE"
headers as default. X-RequestDigest is received, cached, and embed automattically. Use it for REST API POST
calls with update resource intention.
Sends POST
request to /_vti_bin/client.svc/ProcessQuery
endpoint (CSOM). All required headers for a CSOM request are embed automatically. Method's body should stand for a valid CSOM XML package. The response body is parsed for error handling, yet returned in it's original form.
Sometimes more control is required, e.g. when downloading files or large responses you could prefer precessing a response in chunks. This can be achieved with the low-level usage of Gosip HTTP client.
SPClient has Execute
method which is a wrapper function injecting SharePoint authentication and ending up calling http.Client's Do
method.
So you can dive down to native *http.Request
at this point it's just a standard request from "net/http" package, but authenticated to SharePoint and with some batteries under the hood for a seemles API integration.
Create, read, update and delete
CRUD is the most commonly requested type of API consumption.
This example demonstrate basic operations on a list items sample. Hovewer, SharePoint has a variety of nuances even when it comes to just getting items from a list.
Based on a use case, items can be reveived with alternative methods, as .GetAll
, .GetPaged
, .GetByCAML
, or even lists methods as .RenderListData
.
Payloads can be constructed in a usual Go way using marshalling struct or string maps to JSON. E.g.:
or:
up to your preferences.
Payloads can be constructed in a usual Go way using marshalling struct or string maps to JSON, same as in add operations.
Item can be not only deleted but recycled with a further restore operation, which can be provide more safety.
🏄 Fluent, chainable, IntelliSense powered syntax to master SharePoint API
Provides a simple way of constructing API endpoint calls with IntelliSense and chainable syntax.
Get authenticated
Construct root SP
object using api.NewSP(client)
Construct API calls in a fluent way
Parse responses in the Go way
Embrase strongly typed generic responses
Build awesome apps in Go for SharePoint
Request events handlers
Gosip provides an events system with a set of handlers that can be optionally defined to track different client communication aspects such as request tracking, retries and response error logging, to name just a few.
To define the handlers Hooks
object should be configured and passed to gosip.SPClient
struct.
The following handlers are available at the moment:
All of the handlers are optional.
A handler receives HookEvent
pointer which contains request pointer, response status code, and error (if applicable for an event), and time information to track duration since a request started an event happened.
Hooks sample:
Hooks can be handy for global logging streaming and metrics collection.
It is recommended using asynchronous and only lightweight logic inside hooks.
NTLM handshake authentication
This type of authentication uses HTTP NTLM handshake in order to obtain authentication header.
Gosip uses github.com/Azure/go-ntlmssp
NTLM negotiator, however a custom one also can be provided in case of demand.
private.json
sample:
or
If this strategy doesn't work in your environment yet you know for sure it's NTLM used try this alternative.
User credentials authentication
See more details ADFS user credentials authentication.
Gosip's ADFS also supports a scenario of ADFS or NTML behind WAP (Web Application Proxy) which adds additional auth flow and EdgeAccessCookie
involved into play.
private.json
sample:
private.json
sample:
private.json
sample:
Requests retries on error statuses
Gosip HTTP client is preconfigured with retry policies so different kinds of failures can be back off. Current implementation of policies assumes a specific number of retries for a specific response HTTP Status Code. One would only consider retries for "non-ok" status codes and only those which represents server or networking error which has chances for backing off on a next try.
The default policies are:
StatusCode
Retries
Description
401
5
Unauthorized. A retry might help if apply authentication restores an overdue token or handshake.
429
5
Too many requests throttling error response. In the case of API throttling (relevant for SharePoint Online), retrying is also aware of Retry-After header for delay detection.
500
1
Internal Server Error. Rarely can be restored.
503
10
Service Unavailable. Fixes intermittent issues with the service.
504
5
Gateway Timeout Error.
Retries' delay increases with each attempt: 200ms, 400ms, 800ms, 1.6s, 3.2s, and so on using the following progression formula:
For the responses with Retry-After
header, retry after value is used in preference as threshold is usually require longer wait until next request permitted.
A custom policy can be provided on demand in the following way:
When a specific request assumes no retries, e.g. resolving a folder which potentially doesn't exist but you know that 500 is returned in case of failure and it's a faster algorithm to try optimistically and check or create on an error only, it can be handy disabling any retries but only scoped to a specific request or series of requests.
For that purposes X-Gosip-NoRetry
header is the resolution.
There is no direct way to redefine delays between retries, there was no such demand. However, extending delays is achievable via OnRetry hook. The OnRetry
hook is called after it's known that a retry is needed but before the actual retry. The mechanics of retry checks is implemented in the way that should retry check
actually also waits for the delay. In other words, OnRetry
hook is fired right before the next retry is sent. So, if that hook is locked for a time the delay is increased correspondingly. That might be helpful for adding extra delay, yet won't work for the cases when the delays should be reduced or flatten.
While adding timeouts in hooks, make sure you're using something respecting context cancelation:
Custom authentication mechanisms
Gosip allows providing custom authentication mechanisms. For example, you are considering reusing Fluent API helpers and HTTP Client but existing authentication strategies do not feet your environment specifics. Maybe your tenant configured with custom ADFS provider, maybe it's 2FA and there are no alternatives and you need On-Demand auth, but it missed in Gosip strategies list? Fortunately, this is not any sort of stopper. All included authentication strategies are a sort of a pluging and it's rather affordable to add a new strategy on your own.
Let's take a look at any strategy binding:
What we can see? Some strategy is imported into the strategy
namespace. A strategy has AuthCnfg
struct with some public properties which are obviously taking place in authentication flow. This struct is then passed to &gosip.SPClient{AuthCnfg: authCnfg}
and somehow after following binding the requests are authenticated.
For this construction to work strategy.AuthCnfg
should implement gosip.AuthCnfg
interface which is:
Philosophy of the strategies is to have two initiation modes, the first is a strict declaration of the creds and the second one is reading credentials from the config. That config is not necessarily a file on the file system it can be a request to a key vault or OS credential manager, etc.
As the interface is passed to gosip.SPClient
struct, Gosip knows nothing about the creds and the context, for that reason GetSiteURL
method is vital to target requests to a correct root URL.
GetStrategy
method should return the string alias value of the strategy name if something specific should be happening based on its value.
GetAuth
method is for token and cookie-based authentications, it can be omitted and return just a blank value, or it can be an actual place for authentication flow happening inside, returning a cached string which is when applied somehow to the requests making them authenticated. In case of custom logic, we'd recommend using GetAuth method and don't forget TTL caching to reduce roundtrips. With a robust external auth client, GetAuth can be dummy minimum (NTLM example shows this approach).
And finally, SetAuth
, the method where all the magic happening. SetAuth
method is a middleware, it receives runtime request and should append authentication stuff. Check these as samples: NTML's SetAuth, cookie-based auth, Bearer token-based auth.
By implementing AuthCnfg struct and gosip.AuthCnfg interface any custom authentication can be added to Gosip.
Dealing with items attachments
Our common recommendation with attachments is to use them in moderation with a preference to documents in libraries and linking business objects items to that document using metadata and other logical relationship. But sometimes you need nothing more than just a simple binary addition to an item.
Working with attachments is mostly straightforward as you can only get a list of item's attachment, get a specific attachment by its name, add and delete an attachment.
Attachments API provides little information, actually only FileName and ServerRelativeURL.
To detect which items have attachments the corresponding Attachments
property can be requested within an ordinary get items request:
Adding attachments is almost identical to adding documents to a document library.
With an attachment you can:
Download
Get reader (download in a stream way)
Delete
or Recycle
These actions are rather obvious with the help of the Fluent API.
Resolving a strategy dynamically in runtime
When you deal with multiple SharePoint environments with different strategies and concidering same code automatically resolving a strategy use the dynamic auth helper.
With the dynamic auth you'd need extending a private.json
content with strategy
property, containing a name of a strategy. E.g.:
Available strategy names are: azurecert, azurecreds, device, addin, adfs, fba, ntlm, saml, tmg.
With a custom auth involved it can be extended like this.
Property bags operations
Property bags are a nice way of storing global metadata and settings in SharePoint. Property bags are key-value pairs scoped to the container. In general, any folder can act as a container, also webs have their own property bag storage located in all properties section.
Use-cases can vary depending on how an application uses this key-value storage. For example, PnP Provisioning engine stores applied schema information in property bags. A good thing is that you don't need to provision any additional artifacts to keep some business logic or state variables.
A thing to know that all the props values are strings, dealing with different data types they should be serialized or converted to a string.
To get a limited subset of properties .GetProps
helper method can be used:
Selecting specific properties didn't work in old versions of SharePoint, however, the method has a fallback to getting all and filtering props on the client.
Modern SharePoint sites by default have custom scripting disabled mode. When custom scripting is disabled even an admin account will receive "Access denied. You do not have permission to perform this action or access this resource." error message. This is the expected behavior.
Setting props require "Custom Script" be allowed on a site. See more.
We recommend Office 365 CLI to enable custom scripting.
Property bags are a robust way of storing custom settings and state which requires no additional artifacts. When structuring and consuming correctly they can be a great addition to the application logic.
Download & upload files from/to SharePoint is simple
SharePoint is ECM (Enterprise Content Management) system and it's common to expect files being uploaded, downloaded, migrated, processes, and managed in a variety ways.
Gosip provides an easy way of dealing with SharePoint document listaries, files and folders.
Document library in SharePoint is almost the same as a List, but with intention of being a container for files.
Obviously, file content can be a result of reading a file from disk, e.g.:
For the large files it's better using AddChunked API, hovewer, it was not available in SharePoint 2013.
For a large files it's better getting file reader through:
Using Gosip you can concentrate on business logic and Go language aspects while processing documents actions in SharePoint seemlessly.
With use of IntelliSense and Fluent syntax other supported actions can be consumed based on a specific requirement.
Using Go context with SP client
Gosip client respects native Go Context, you can pass a context on a low level or to a Fluent API to control requests's deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes.
On the low level dealing with the contexts is identical to native approach:
While using HTTPClient, context is defined together with request config.
With Fluent API, context is managed in the same way as in the previous example with the only difference how it is chained to fluent syntax.
Conf
method can be used almost on any hierarchy level. It's inherited with capability to redefine.
Managing roles and objects permissions
Permissions management is an important part of business logic which can be met in worker processes and workflows.
Let's explore how Gosip Fluent API wraps up SharePoint securable object and permissions.
First of all, by securable objects mean any SharePoint artifacts which can be configured with unique permissions: webs, lists, libraries, items, etc.
Fluent API scopes role operations under .Roles()
method.
To assign unique permissions to an object its role inheritence must be broken.
Be aware that abusing breaking role inheritence and having too many unique permissions can have a detrimental effect on SharePoint performance. The architecture of a solution which involves many unique permissions has too be planned carefully. See some valuable recommendations.
Before assigning permissions it's important to know how to get role definitions.
A role definition is a collection of rights bound to a specific object. Role definitions (for example, Full Control, Read, Contribute, Design, or Limited Access) are scoped to the Web site and mean the same thing everywhere within the Web site, but their meanings can differ between sites within the same site collection. Role definitions can also be inherited from the parent Web site, just as permissions can be inherited.
Definitions are csoped as Web level in .RoleDefinitions()
quariable collection.
Definitions can be gotten by Definition ID, Name or Type. We recommend using OOTB definitions and getting them by types. There is an enumerator-like helper for default types api.RoleTypeKinds
.
Principal is site user or group, their IDs are used in roles assignments. Principal ID can be received by requesting UIL (User Information List), getting site user/group by name/email, etc.
It's in preference to operate on groups level and grant permissions to a specific users as little as possible.
The role assignment is the relationship among the role definition, the users and groups, and the scope (for example, one user may be a reader on list 1, while another user is a reader on list 2). The relationship expressed through the role assignment is the key to making SharePoint security management role-based.
At last, now we are ready for roles assigment. 😜 Who told permissions is simple?
Removing role assignments is just the same as adding but in opposite.
To reset permissions inheritance:
After reseting object roles assigments its permissions are again inherited from the parent object.
When dealing with OData collection of objects with unique permissions OData items' role assigment can be requested using RoleAssignments
, also HasUniqueRoleAssignments
can be used in moderation.
Please be aware that
HasUniqueRoleAssignments
is a heavy property that creates workload on a SharePoint server and better be used at minimal.
Try not abusing it by potential requests to large lists getting a bunch of items.
RoleAssigments
if any applied contains an array of objects.
Assigments is a Member and binded RoleDefinitions:
BasePermissions
is permissions representation with Low
and High
pair. Don't panic if API returns only BasePermissions ({ "High": "2147483647", "Low": "4294705151" }
), using HasPermissions
helper it's simple to check if it includes required permissions kind:
Now we know how to treat SharePoint permissions using Gosip and Fluent API. Before wrapping up, we want to stress on importance of careful planning of permissions model, as less unique permissions is better.
Uploading files in chunks
For large documents it's better using files Chunk API which allows splitting upload into separate REST API requests reducing memory consumption and providing a more manageable upload mechanism.
Files Chunk API appeared in SharePoint 2016 and should not be expected to work in the previous version.
Chunk upload can't be used in SharePoint 2013
Fluent API abstracts some aspects of the chunked upload. Internally, /StartUpload
, /ContinueUpload
, /CancelUpload
, and /FinishUpload
endpoints are used. However, these are not exposed for the simplicity. Nonetheless, there is a way for canceling upload with the help of api.AddChunkedOptions
.
There are a few options which are optional. The third method parameter is a configuration structure.
Defaults are: Overwrite
is true and ChunkSize
equals to 10485760 bytes.
Progress
callback allows not only trigger a progress logic, for example upload percentage update, but also to cancel an upload. To cancel an upload the progress callback should return false
.
OData modes headers presets
REST API uses OData modes for controlling response verbosity.
By defining different OData modes (Verbose, Minimalmetadata, Nometadata) within the Accept headers SharePoint REST API returns not only data of different details but also data in different forms payload shape-terms. Which can lead to runtime errors.
When dealing with different versions of SharePoint there are few gotchas to remember. In old SharePoint 2013, only Verbose
mode was allowed by default. This can be amended by installing WCF OData extensions and enabling JSON Light support, however, in our practice, a rare farm admin considering such an update. So it's better stick with a Verbose when it comes to SharePoint 2013.
With SharePoint 2016 and newer, and, obviously, SharePoint Online, we'd recommend Minimalmetadata
as a default. So the payloads could be a bit more smaller in size and effective overall.
Nometadata
mode could be tricky, from one hand it's close to Minimalmetadata
yet doesn't content some vital information such as entity identities and paged collections helpers. We prefer Minimalmetadata
over Nometadata
in general.
Gosip provides some presets for headers which could be handy together with Conf
method.
Along with OData modes, these presets define language header "Accept-Language": "en-US,en;q=0.9"
which forces English messages in responses if English is installed on a site. Which is handy for dev and debugging purposes as sometimes a local non-latin language can be escaped to an unreadable form making it uncomfortable detecting what was wrong in logs.
Operations for features management
You can activate and deactivate features on sites and webs using REST API. However, there are a few nuances to know knowing which makes it simple to manage the features.
You should know specific feature definition ID to add or remove it.
Unfortunately, there is no way of getting features list with names and descriptions programmatically. When getting features list on the web or site you receive a list of activated features definition IDs and nothing else.
Luckily, nowadays one almost always deal only with OOTB features, if not probably something is terribly wrong on a project. 😝
The first argument stands for Feature Definition ID, the second one is force mode.
Arguments are identical.
When a feature was not activated before removing it an error message is expected ("Feature is not activated at this scope").
Site level actions are absolutely identical, the only difference is sp.Site()
entry point.
Getting changes, synchronisation scenarios
When it comes to synchronization or delta processing Change API is for the rescue. There is no need in any sort of logic that used modified date comparison to detect what has been changed since the last processing time. There is no need to wait until the search crawl is done to check changes through web or site collection. And not only these benefits but also tracking permissions changes restores from recycle bin, system updates and much more.
Changes API is what is used together with Webhooks (in SPO), by having a context (scope) and change token(s) you can easily retrieve what has been changed and run the custom logic based on the nature of changes.
Changes can be requested from within different scopes: lists, webs, sites, etc. Change API operates change tokens to establish start and end anchors.
Change tokens received from a specific entity type (e.g. Site) can't be used with another entity type (e.g. List) while sending change query.
Knowing what root entities' token(s) you have the specific changes query can be crafted and sent, e.g.:
Change results are strongly typed with Gosip's API, the changes
variable in the sample is an array of api.ChangeInfo
struct pointers.
Change into struct contains the following properties:
ChangeType
is an important piece of information, it's an enumerator describing a nature of a change. See more.
Change API doesn't return what specifically was changed but only where it was changed. For instance, when an item is changed the API will return its identities information but not the metadata.
In a synchronization scenario or a Webhook after getting information which items are changed the corresponding request(s) should be sent for getting specifics.
When getting the changes the API requires some clarifications about what exactly should be retrieved. This is defined within a change query which is implemented api.ChangeQuery
struct.
Property
Description
ChangeTokenStart
Specifies the start date and start time for changes that are returned through the query
ChangeTokenEnd
Specifies the end date and end time for changes that are returned through the query
Add
Specifies whether add changes are included in the query
Alert
Specifies whether changes to alerts are included in the query
ContentType
Specifies whether changes to content types are included in the query
DeleteObject
Specifies whether deleted objects are included in the query
Field
Specifies whether changes to fields are included in the query
File
Specifies whether changes to files are included in the query
Folder
Specifies whether changes to folders are included in the query
Group
Specifies whether changes to groups are included in the query
GroupMembershipAdd
Specifies whether adding users to groups is included in the query
GroupMembershipDelete
Specifies whether deleting users from the groups is included in the query
Item
Specifies whether general changes to list items are included in the query
List
Specifies whether changes to lists are included in the query
Move
Specifies whether move changes are included in the query
Navigation
Specifies whether changes to the navigation structure of a site collection are included in the query
Rename
Specifies whether renaming changes are included in the query
Restore
Specifies whether restoring items from the recycle bin or from backups is included in the query
RoleAssignmentAdd
Specifies whether adding role assignments is included in the query
RoleAssignmentDelete
Specifies whether adding role assignments is included in the query
RoleDefinitionAdd
Specifies whether adding role assignments is included in the query
RoleDefinitionDelete
Specifies whether adding role assignments is included in the query
RoleDefinitionUpdate
Specifies whether adding role assignments is included in the query
SecurityPolicy
Specifies whether modifications to security policies are included in the query
Site
Specifies whether changes to site collections are included in the query
SystemUpdate
Specifies whether updates made using the item SystemUpdate method are included in the query
Update
Specifies whether update changes are included in the query
User
Specifies whether changes to users are included in the query
View
Specifies whether changes to views are included in the query
Web
Specifies whether changes to Web sites are included in the query
Sometimes it can be lots of changes since a provided token. Gosip implements pagination helper using "jumping" between different change tokens under the hood. When last change item's token is used as a start token to get the "next page". This approach is used in GetNextPage
:
Change API is a powerful mechanism and a robust way for processing delta changes that can and should be used in advanced and optimised synchronizations and business processes withing external workers.
Dealing with user profiles API
Working with UPS API is simple. UPS API is not something sophisticated as Search for example and it has just a few methods which can be in demand in a server-side operation.
Don't get me wrong, there are not-covered social features from within user profiles API in Gosip but we rarely have seen demand and scenarios of their usage in SharePoint solutions. Anyways, if some additional feature coverage is required for you please let us know by posting an issue.
The most in-demand, I would say, feature when it comes to user profiles is getting all profiles. However, UPS API allows only dealing with a single profile, you can't request all of them.
Luckily, this is possible and recommended achieving with the search.
See a bit more advanced sample.
Gosip strongly types properties response using .Data()
helper, here is the resulted struct:
Sometimes you have to be as effective as possible and trim down responses to a minimum. Let's say you only need a single property.
Seriously, I don't know the reason for existing of getting a single property, but not getting specific properties or multiple profiles or updating multiple properties. There are no excuses yet this part of SharePoint API is clunky and really old.
There are two methods for setting user profile property value. Yeah, you heard me the right property in a time.
User profiles API is limited due to its legacy nature. It is what it is. However, many SharePoint solutions, especially intranet portals and workflow processes can be heavily based on UPS. Go worker can be handy for custom synchronization scenarios and also in external workflow workers with UPS as a source for settings for detecting user dynamic roles.
Managing groups, requesting users
Site groups and users can be requested via Web's .SiteGroups()
and .SiteUsers()
queriable collections correspondingly.
You can't add new user via SharePoint API, but a user who exists in AD/AAD can be added to a site by ensuring him/her by a logon name.
We love the "power of defaults". Each web by default has three predefined groups: Owners, Members and Visitors. But their IDs and names are different from web to web. Luckily there is a helper for getting associated groups.
In group's .AddUser
method the argument should be full and valid login name including security provider membership prefix. For instance, while you can ensure Jane using jane.doe@contoso.onmicrosoft.com
the same as an .AddUser
method will fail as Jane's login is actually different i:0#.f|membership|jane.doe@contoso.onmicrosoft.com
.
If you already know UserID but not sure about LoginName .AddUserByID
helper is at the disposal.
Similarly as with adding users:
That's it, most of the common actions with groups and users are covered.
You'd probably will be interested with the connected topics:
💪 Contributing to Gosip client
First of all, thank you for considering contributing to the project! We really appreciate any activity around it. There are no small contributions and any investment can't be underestimated. You can contribute with feedback, finding and posting an issue, docs suggestion, code commit, or a star ⭐️. All of these encourage us supporting this and other our Open Source projects on a high level.
Contributing to the project follows a majority of GitHub and Open Source communities' principles.
Target your pull requests to the dev branch.
Add/update any docs articles related to your changes embeded to the code or as a separate notes to the PR, which we'd love to publish on docs site.
If you are fixing a bug, include a test that would have caught the bug you are fixing.
Keep your PRs as simple as possible and describe the changes to help the reviewer understand your work.
API methods compatibility
When it comes to a code base which should support multiple platform versions, which APIs obviously changes with time and not aligned together, it can be a challenge maintaining-wise.
Luckily, authentication methods are isolated and pretty static and HTTP client is generic for any SharePoint API consumption, no-matter REST, CSOM, or legacy SOAP are intended to be used.
In place record management helpers
In enterprise content management (ECM) scenarios, records are an important element within a document lifecycle and a company's compliance policies. Not only documents but actually items.
When declared as a record a document or item can't be later changed or moved without undeclaring. This guarantees integrity.
REST API in terms of managing classic in-place records is limited, you can only retrieve record status from item metadata. Item's OData__vti_ItemDeclaredRecord
property tells was an item declared as a record and when. The field is a date-time value, empty means item is not a record.
We would not be we if didn't add an extension with allows Gosip to declare, undeclare and declare with a declaration date. To workaround REST's limitation, these functionality is achieved by corresponding CSOM calls. What's great is that Gosip abstracts for you such a nuance providing methods which under-the-hood details are interesting but not necessarily should be known by a library consumer.
Declaration as records is more common for documents (files), yet it's actually a file's item that keeps record status. And therefore holds API actions.
The support of that method is not presented in old SharePoint versions.
Gosip is Open Source project hosted on GitHub. We, at , and community, use the library on our production projects for our customers which makes it easier to guarantee stability and maintenance.
The main project repo is located at .
Include a test for any new functionality and ensure all existing tests are passing by running go test
command(s), see
If you have an idea for a larger change to the library please and let's discuss before you invest many hours - these are very welcome but want to ensure it is something we can merge before you spend the time.
On the other side, the most of the help for a Go developer might come from Fluent API as it self-documented or intuitive usage-wise, covers mostly demanded use-cases, and abstracts most of the API nuances and complexity under the hood so a Go developer should not know lots of SharePoint since start. In most of the cases, when it come to a method which only supported let's say in SharePoint Online we put a special comment spotlighting versions support nuances. But a comment can be easily missed out. We plan to introduce sort of a tool for analysis unsupported methods in a custom code using Gosip Fluent API which verify that only supported methods are used for a targeted platform, this might happen in future in case of a reasonable demand. Until then we recommend manual verification of the methods and API entities support across the platform versions using project.
It's important to notice that in-place record management requires some which is not the topic of this article. So let's observe API methods instead.
There is no methods for declaring and undeclaring items as records in SharePoint REST API (yet) and there won't be ever, IMO, for classic record management, but maybe to the , however, in Gosip, we mimic some CSOM calls and providing corresponding methods.
🔐 Simple secure string password convertor
Cpass is simplified secured password two-ways encryption sub package port for Gosip.
By default, Cpass uses Machine ID as an encryption key so a secret hash can only be decrypted on a machine where it was generated.
Cpass's approach is appropriate in local development scenarios. The main goal is "not to show raw secret while presenting a desktop" or "not to commit raw secret by an incident to code source".
When use result token/hash as a secret in private.json
file(s).
Another option would be installing cpass
from sandbox:
And using cpass
as a CLI, with no parameters the secret can be provided in a masked form without keeping it in console history:
Email notifications utility
There is nothing simpler than sending email notifications using SharePoint REST and Gosip Fluent API. However, when building workflow workers or custom subscription services email functionality is vital. And what can be better than embedded OOTB functionality? No extra knowledge of SMTP server and mail credentials, just a usual API call to _api/SP.Utilities.Utility.SendEmail
utility endpoint.
There are some limitations to this method:
recipients only from the site
impossibility to change the sender
no attachments
but as embedded solution it's OK. And probably sort of workflow notification service should stay in such margins anyways.
Send email utility is not sophisticated but it works and enough for an embedded functionality.
🚦Gosip automated testing
In a clone or fork.
Create auth credentials store files in ./config
folder for corresponding strategies:
See samples.
Not provided auth configs and therefore strategies are ignored and not skipped in tests.
Create auth credentials store files in ./config/integration
folder for corresponding environments:
private.spo.json
private.2013.json // 2013 has its nuances with OData mod and not supported methods
API integration tests are mostly targeted to SharePoint Online and not regularly processed on the legacy versions of the platform so you can face some test exceptions which still should be escaped with t.Skip
and envCode != "spo"
condition:
Environment variables
SPAUTH_ENVCODE=code
environment variable switches target environments. spo
is a default one.
SPAPI_HEAVY_TESTS=true
turns on "heavy" methods, e.g. web creation.
Modify cmd/test/main.go
to include required scenarios and run:
Optionally, you can provide a strategy to use with a corresponding flag:
Configure environment variables:
SPAUTH_SITEURL
SPAUTH_CLIENTID
SPAUTH_CLIENTSECRET
SPAUTH_USERNAME
SPAUTH_PASSWORD
Check Codecov.
Branch
Coverage
Master
Dev
We are targeted to keep code coverage higher than 80% for API and Auth methods altogether.
Searching content via SharePoint API
Search and search API is a huge and complex topic. In this article we're going mostly cover some basics in combination of Gosip plus REST API.
While executing search call there is no difference what web/site is used as a context, search API returns data due to search query object.
The simplest request looks this way:
Search query struct (api.SearchQuery
) contains some, let's say, reasonable amount of options 🙈.
Option
Description
QueryText
A string that contains the text for the search query
QueryTemplate
A string that contains the text that replaces the query text, as part of a query transform
EnableInterleaving
A Boolean value that specifies whether the result tables that are returned for the result block are mixed with the result tables that are returned for the original query
EnableStemming
A Boolean value that specifies whether stemming is enabled
TrimDuplicates
A Boolean value that specifies whether duplicate items are removed from the results
EnableNicknames
A Boolean value that specifies whether the exact terms in the search query are used to find matches, or if nicknames are used also
EnableFQL
A Boolean value that specifies whether the query uses the FAST Query Language (FQL)
EnablePhonetic
A Boolean value that specifies whether the phonetic forms of the query terms are used to find matches
BypassResultTypes
A Boolean value that specifies whether to perform result type processing for the query
ProcessBestBets
A Boolean value that specifies whether to return best bet results for the query. This parameter is used only when EnableQueryRules is set to true, otherwise it is ignored.
EnableQueryRules
A Boolean value that specifies whether to enable query rules for the query
EnableSorting
A Boolean value that specifies whether to sort search results
GenerateBlockRankLog
Specifies whether to return block rank log information in the BlockRankLog property of the interleaved result table. A block rank log contains the textual information on the block score and the documents that were de-duplicated.
SourceID
The result source ID to use for executing the search query
RankingModelID
The ID of the ranking model to use for the query
StartRow
The first row that is included in the search results that are returned. You use this parameter when you want to implement paging for search results.
RowLimit
The maximum number of rows overall that are returned in the search results. Compared to RowsPerPage, RowLimit is the maximum number of rows returned overall.
RowsPerPage
The maximum number of rows to return per page. Compared to RowLimit, RowsPerPage refers to the maximum number of rows to return per page, and is used primarily when you want to implement paging for search results.
SelectProperties
The managed properties to return in the search results
Culture
The locale ID (LCID) for the query
RefinementFilters
The set of refinement filters used when issuing a refinement query (FQL)
Refiners
The set of refiners to return in a search result
HiddenConstraints
The additional query terms to append to the query
Timeout
The amount of time in milliseconds before the query request times out
HitHighlightedProperties
The properties to highlight in the search result summary when the property value matches the search terms entered by the user
ClientType
The type of the client that issued the query
PersonalizationData
The GUID for the user who submitted the search query
ResultsURL
The URL for the search results page
QueryTag
Custom tags that identify the query. You can specify multiple query tags
ProcessPersonalFavorites
A Boolean value that specifies whether to return personal favorites with the search results
QueryTemplatePropertiesURL
The location of the queryparametertemplate.xml file. This file is used to enable anonymous users to make Search REST queries
HitHighlightedMultivaluePropertyLimit
The number of properties to show hit highlighting for in the search results
EnableOrderingHitHighlightedProperty
A Boolean value that specifies whether the hit highlighted properties can be ordered
CollapseSpecification
The managed properties that are used to determine how to collapse individual search results. Results are collapsed into one or a specified number of results if they match any of the individual collapse specifications. In a collapse specification, results are collapsed if their properties match all individual properties in the collapse specification.
UIlanguage
The locale identifier (LCID) of the user interface
DesiredSnippetLength
The preferred number of characters to display in the hit-highlighted summary generated for a search result
MaxSnippetLength
The maximum number of characters to display in the hit-highlighted summary generated for a search result
SummaryLength
The number of characters to display in the result summary for a search result
SortList
The list of properties by which the search results are ordered
Properties
Properties to be used to configure the search query
ReorderingRules
Special rules for reordering search results. These rules can specify that documents matching certain conditions are ranked higher or lower in the results. This property applies only when search results are sorted based on rank.
But don't be afraid you use only a few of them and on demand and when know what you need.
This search request searched only for user profiles (defined by SourceID), return five results per page, skips first ten records ignores trimming duplicates, retrieves specific managed properties and sorts by Title property.
Search response in Fluent API is sligtly bit adjusted with helper methods. Result itself is byte array, so you can process it in a custom way.
For lazy people (as I am) there are .Data()
and .Results()
helpers.
Results helper retrieves PrimaryQueryResult.RelevantResults.Table.Rows
and reduces search response results to the convenient array of string maps. So you can grasp results this way:
Data helper provides more options. It returns the following struct:
So you can have access to refiners and many more search goodies.
Recycling methods and dealing with recycle bin
You can work with recycle bins via REST API similarly as with lists.
Items in recycle bins are queryable collection, OData modifiers can be applied in a usual way.
Response is strongly typed, helps do not care about unmarshalling the structures. Items in recycle bin contains the following metadata:
Once you have Item ID (which is a GUID in case of recycle bin) you can not resore it.
Advanced creating and updating items
In addition to standard OData add and update items operations REST provides such useful methods as AddValidateUsingPath
and ValidateUpdateListItem
. The first is only presented in modern SharePoint, it not only allows adding items right in a sub folder but also operate with form data payloads and control check in process. ValidateUpdateListItem
is handy for operations requiring system-like-update logic via pure REST.
In Gosip, AddValidateUsingPath
and ValidateUpdateListItem
are represented with Items().AddValidate()
and Item.UpdateValidate()
methods correspondingly:
As DecodedPath
option the relative path to folder can be provided. It's optional. The path should be relative to a web without trailing slash in the beginning. Gosip adds web relative URL automatically.
ValidateAddOptions
are also optional, when no new document update or check-in comment or folder path are ever required, a nil
value should be passed.
Using update validate is almost the same:
Form values passed to the methods should stand for an array of { FieldName: "", FieldValue: "" }
objects where field value is a string of specific format depending on field's data type.
Gosip simplifies this payload operating with map of strings. In payload, map key should stand for a valid FieldName
, a value, obviously, is the one mapped to FieldValue
.
The fingerprints for the data types are following:
Field data type
Value sample
Comment
Text (single line and note)
"text"
Number
"123"
as a string
Yes/No
"1"
"1" - Yes, "2" - No
Person or group, single and multiple
`[{ "Key": "LoginName", "IsResolved": true }]`
"LoginName" is a valid login name, including provider prefix
"IsResolved" is optional
Date time
"6/23/2018 10:15 PM"
for different web locales is different
Date only
"6/23/2018'
for different web locales is different
Choice (single)
"Choice 1"
Choice (multi)
"Choice 1;#Choice 2"
";#" separated list
Hyperlink or picture
"https://go.spflow.com, Gosip"
a description can go after URL and ", " delimiter
Lookup (single)
"2"
item ID as string
Lookup (multi)
"1;#;#2;#;#3;#"
";#" separated list, after each ID goes additional ";#"
Managed metadata (single)
"Department 2|220a3627-4cd3-453d-ac54-34e71483bb8a;"
Managed metadata (multi)
"Department 2|220a3627-4cd3-453d-ac54-34e71483bb8a;Department 3|700a1bc3-3ef6-41ba-8a10-d3054f58db4b;"
Advanced scenarios for getting list items
There are so many nuances connected with requesting items in a list. By default, it's recommended using OData operations as the most simple, straightforward and RESTful approach.
But OData doesn't cover all of the needs and you got to switch back but not necessarily backward to CAML methods. There a lot of gaps which can force you doing this: single valued MMD fields, group by requests, getting items from a view, to name just a few. Sometimes it's old known bugs or limitations, sometimes a specific functionality which was in the CAML days.
Of course, CAML query can be slightly bit more complex than just this. 😁
For example, you can request data as in a list view:
However, when working with CAML there is more powerful methods.
RenderListData* methods have completely different response structure. It's good and bad at the same time. The cons are that the results are not compatible and really different from OData methods, not only the shape but also values format. Sometimes it aches. The pros are that render list data methods provide something which is missed in OData responses: group by with collapsed data and only subtotals are possible, recurrent calendar events can be requested (yet it's now so shiny), to name a few.
RenderListData deals with GET request, which is a disadvantage as CAML query length is limited. This limitation is rarely can be met in practice, but the ridiculously complex conditions could fail due to this fact.
This method is super powerful, SharePoint Modern UI list views work using .RenderListDataAsStream
. The method is the continuation of evolving of .RenderListData
enhanced by the vendor for their practical needs of building the modern UI views.
The method deals with POST requests, almost have no length limitation, and operates with many-many options for covering all those aspects and features of the Modern UI view.
The method is in our plans to implement in Gosip Fluent API. But you always can craft that API consumption using AdHoc queries and HTTP Client.
Pagination in SharePoint lists is painful if you misses couple of moments. In a contrast to many databases queries where top
and skip
are straightforward thing to build an pagination, OData's $skiptoken
is not what many think.
First of all, it's not a number of rows to skip before starting returning items on a next paged collection.
The simplest format of skip token is: Paged=TRUE&p_ID=5
The simplest reverse skip token is: Paged=TRUE&PagedPrev=TRUE&p_ID=5
Reverse token returns previous page content obviously.
By looking at p_ID
part you'd think that item's ID is enough to construct a correct skip token, but it's not so. It's only correct for not sorted collections. If any $orderby
modifier is applied, the amount of p_*
parameters changes. Let's assume you sorted the list by Title, it will add something like p_Title=Smth
, where "Smth" is the last row's Title value on current page collection.
Fortunately, skip tokens should not be constructed manually. REST return next page collection URI together with the responses for the current page collection.
In Gosip we have helper methods which makes it simpler working with pagination.
We're planning some improvements with pagination interfaces and scale the approach to all possible paged collections APIs but will try to introduce any backward incompatibilities as little as possible.
Large lists in SharePoint are those which amount of items is larger than view throttling limitation, the default limitation is 5000 items in a view. In On-Prem this value can be tweaked, in SPO this is a hard limit.
REST API can't return more than 5K items at once. Filter conditions based on indexing fields must be applied to trim down items number. It can be hard, though it's common for server-side processing requesting all items even if there are tens of thousands of items in a list. Such operations are not for immidiate actions but long running syncronizations.
In Gosip, .GetAll
method is at disposal.
The method disables any ordering and filtering if applied, as ordering and filtering are not compatible with large lists.
Recommendations for getting all items from a large list:
Always specify only really required fields to retrieve in Select
Use Top
equal to 5000 (as the default Top=100)
Consider event-based synchronizations and partial getting changes
Consider search-based logic
Gosip sandbox area: samples, experiments & suggestions
Have a use case but no idea how to implement? Ask in issues section and we'll reach you with suggestions or sample to start with.
Have a noticeable example to share with the community? Reach us with a contribution suggestion. PRs are welcome!
Have no particular code to share but a description of how you use the library or a blog post? Please let us know, we're happy to post a reference.
Sample
Description
Shows a simple way of importing potentially demanded strategies and selecting one in runtime based on logic, CLI flags in the case of the sample.
Shows how to retrieve auth tokens to use in a PowerShell script (is relevant for Edge auth scenarios, e.g. SharePoint behind WAP perimeter).
Shows how to expose SharePoint API as an anonymous endpoint for a dev server.
Is a syncronization sample which provides a single time assets upload or/and local file system watch mode. Provides in times faster upload when any other known alternative.
Basic unmarhsaling (response parsing) sample.
The sample shows how to get permissions for OData collections.
Accessing SPO tenant scope API basic sample. E.g. for creating classic sites.
The sample shows how to create, delete Modern Sites and how to check a site's provisioning status.
Parsing complex responses
Gosip Fluent API tries providing strongly typed responses objects, but in most situations, it's not possible as there are many factors reshaping a response JSON body.
By defining different OData modes (Verbose, Minimalmetadata, Nometadata) within the Accept headers SharePoint REST API returns not only data of different details but also data in different forms payload shape-terms. Which can lead to runtime errors.
We're are not forcing to use one specific OData mode only (e.g. Minimalmetadata) as this would prevent support of some old SharePoint versions (2013) without additional server-side configurations, we have responses normalisation methods which reshape JSONs from the API to the close or identical form no matter the OData mode is.
Let's take a look at the example:
Almost any API byte array response has extension methods, such as .Data()
and .Normalize()
. The Data method uses predefined generic API entity items unmarshaling, it can be useful for the OOTB entities, e.g. Webs, Groups, Lists, to name just a few. But won't allow getting custom item values for example.
When it comes to custom responses, you have to unmarshal by your own, however, Gosip also helps with this by providing normalization methods.
Let's compare responses in a bit much details:
As can be seen, there is a great difference in Verbose and Minimal/No metadata modes in shape-terms. The difference is mode dramatic when complex data fields come into play.
After normalization, responses shape is reduced to the following:
This makes unmarshalling in times simpler and reduce potential errors after deciding changing OData mode globally.