/// <summary> /// Synchronize an app from the tenant app catalog with the teams app catalog /// </summary> /// <param name="appMetadata">The app metadata object of the app to remove.</param> /// <returns></returns> public async Task <bool> SyncToTeamsAsync(AppMetadata appMetadata) { if (appMetadata == null || appMetadata.Id == Guid.Empty) { throw new ArgumentException(nameof(appMetadata)); } await new SynchronizationContextRemover(); return(await SyncToTeamsImplementation(appMetadata.Id)); }
#pragma warning restore CA2000 private static async Task <AppMetadata> GetAppMetaData(AppCatalogScope scope, ClientContext context, string accessToken, PnPHttpProvider httpClient, string requestDigest, string id) { AppMetadata returnValue = null; int retryCount = 0; int waitTime = 10; // seconds var metadataRequestUrl = $"{context.Web.Url}/_api/web/{(scope == AppCatalogScope.Tenant ? "tenant" : "sitecollection")}appcatalog/AvailableApps/GetById('{id}')"; using (var metadataRequest = new HttpRequestMessage(HttpMethod.Get, metadataRequestUrl)) { metadataRequest.Headers.Add("accept", "application/json;odata=nometadata"); if (!string.IsNullOrEmpty(accessToken)) { metadataRequest.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken); } if (!string.IsNullOrEmpty(requestDigest)) { metadataRequest.Headers.Add("X-RequestDigest", requestDigest); } while (returnValue == null && retryCount < 5) { // Perform actual post operation HttpResponseMessage metadataResponse = await httpClient.SendAsync(metadataRequest, new System.Threading.CancellationToken()); if (metadataResponse.IsSuccessStatusCode) { // If value empty, URL is taken var metadataResponseString = await metadataResponse.Content.ReadAsStringAsync(); if (metadataResponseString != null) { returnValue = JsonSerializer.Deserialize <AppMetadata>(metadataResponseString); } } else if (metadataResponse.StatusCode != HttpStatusCode.NotFound) { // Something went wrong... throw new Exception(await metadataResponse.Content.ReadAsStringAsync()); } if (returnValue == null) { // try again retryCount++; Thread.Sleep(waitTime * 1000); // wait 10 seconds } } return(returnValue); } }
/// <summary> /// Removes an app from the app catalog /// </summary> /// <param name="appMetadata">The app metadata object of the app to remove.</param> /// <param name="scope">Specifies the app catalog to work with. Defaults to Tenant</param> /// <returns></returns> public async Task <bool> RemoveAsync(AppMetadata appMetadata, AppCatalogScope scope = AppCatalogScope.Tenant) { if (appMetadata == null) { throw new ArgumentException(nameof(appMetadata)); } if (appMetadata.Id == Guid.Empty) { throw new ArgumentException(nameof(appMetadata.Id)); } await new SynchronizationContextRemover(); return(await BaseRequest(appMetadata.Id, AppManagerAction.Remove, true, null, scope)); }
/// <summary> /// Upgrades an app in a site /// </summary> /// <param name="appMetadata">The app metadata object of the app to upgrade.</param> /// <param name="scope">Specifies the app catalog to work with. Defaults to Tenant</param> /// <returns></returns> public async Task <bool> UpgradeAsync(AppMetadata appMetadata, AppCatalogScope scope = AppCatalogScope.Tenant) { if (appMetadata == null) { throw new ArgumentException(nameof(appMetadata)); } if (appMetadata.Id == Guid.Empty) { throw new ArgumentException(nameof(appMetadata.Id)); } await new SynchronizationContextRemover(); return(await UpgradeAsync(appMetadata.Id, scope)); }
private static async Task <AppMetadata> GetAppMetaData(AppCatalogScope scope, ClientContext context, string id) { AppMetadata returnValue = null; int retryCount = 0; int waitTime = 10; // seconds #pragma warning disable CA2000 // Dispose objects before losing scope var httpClient = PnPHttpClient.Instance.GetHttpClient(context); #pragma warning restore CA2000 // Dispose objects before losing scope var metadataRequestUrl = $"{context.Web.Url}/_api/web/{(scope == AppCatalogScope.Tenant ? "tenant" : "sitecollection")}appcatalog/AvailableApps/GetById('{id}')"; using (var metadataRequest = new HttpRequestMessage(HttpMethod.Get, metadataRequestUrl)) { metadataRequest.Headers.Add("accept", "application/json;odata=nometadata"); await PnPHttpClient.AuthenticateRequestAsync(metadataRequest, context).ConfigureAwait(false); while (returnValue == null && retryCount < 5) { // Perform actual post operation HttpResponseMessage metadataResponse = await httpClient.SendAsync(metadataRequest, new System.Threading.CancellationToken()); if (metadataResponse.IsSuccessStatusCode) { // If value empty, URL is taken var metadataResponseString = await metadataResponse.Content.ReadAsStringAsync(); if (metadataResponseString != null) { returnValue = JsonSerializer.Deserialize <AppMetadata>(metadataResponseString); } } else if (metadataResponse.StatusCode != HttpStatusCode.NotFound) { // Something went wrong... throw new Exception(await metadataResponse.Content.ReadAsStringAsync()); } if (returnValue == null) { // try again retryCount++; await Task.Delay(waitTime * 1000); // wait 10 seconds } } return(returnValue); } }
/// <summary> /// Deploys/trusts an app in the app catalog /// </summary> /// <param name="appMetadata">The app metadata object of the app to deploy.</param> /// <param name="skipFeatureDeployment">If set to true will skip the feature deployed for tenant scoped apps.</param> /// <param name="scope">Specifies the app catalog to work with. Defaults to Tenant</param> /// <returns></returns> public async Task <bool> DeployAsync(AppMetadata appMetadata, bool skipFeatureDeployment = true, AppCatalogScope scope = AppCatalogScope.Tenant) { if (appMetadata == null) { throw new ArgumentException(nameof(appMetadata)); } if (appMetadata.Id == Guid.Empty) { throw new ArgumentException(nameof(appMetadata.Id)); } var postObj = new Dictionary <string, object> { { "skipFeatureDeployment", skipFeatureDeployment } }; await new SynchronizationContextRemover(); return(await BaseRequest(appMetadata.Id, AppManagerAction.Deploy, true, postObj, scope)); }
private async Task <AppMetadata> BaseAddRequest(byte[] file, string filename, bool overwrite, int timeoutSeconds, AppCatalogScope scope) { AppMetadata returnValue = null; var isCloned = false; var context = _context; if (scope == AppCatalogScope.Tenant) { // switch context to appcatalog var appcatalogUri = _context.Web.GetAppCatalog(); #pragma warning disable CA2000 // Dispose objects before losing scope context = context.Clone(appcatalogUri); #pragma warning restore CA2000 // Dispose objects before losing scope isCloned = true; } context.Web.EnsureProperty(w => w.Url); #pragma warning disable CA2000 // Dispose objects before losing scope var httpClient = PnPHttpClient.Instance.GetHttpClient(context); #pragma warning restore CA2000 // Dispose objects before losing scope string requestUrl = $"{context.Web.Url}/_api/web/{(scope == AppCatalogScope.Tenant ? "tenant" : "sitecollection")}appcatalog/Add(overwrite={(overwrite.ToString().ToLower())}, url='{filename}')"; var requestDigest = string.Empty; using (var request = new HttpRequestMessage(HttpMethod.Post, requestUrl)) { request.Headers.Add("accept", "application/json;odata=nometadata"); await PnPHttpClient.AuthenticateRequestAsync(request, context).ConfigureAwait(false); request.Headers.Add("binaryStringRequestBody", "true"); request.Content = new ByteArrayContent(file); httpClient.Timeout = new TimeSpan(0, 0, timeoutSeconds); // Perform actual post operation HttpResponseMessage response = await httpClient.SendAsync(request, new CancellationToken()); if (response.IsSuccessStatusCode) { // If value empty, URL is taken var responseString = await response.Content.ReadAsStringAsync(); if (responseString != null) { using (var jsonDocument = JsonDocument.Parse(responseString)) { if (jsonDocument.RootElement.TryGetProperty("UniqueId", out JsonElement uniqueIdElement)) { var id = uniqueIdElement.GetString(); returnValue = await GetAppMetaData(scope, context, id); } } } } else { // Something went wrong... throw new Exception(await response.Content.ReadAsStringAsync()); } } if (isCloned) { context.Dispose(); } return(await Task.Run(() => returnValue)); }
/// <summary> /// Synchronize an app from the tenant app catalog with the teams app catalog /// </summary> /// <param name="appMetadata">The app metadata object of the app to remove.</param> /// <returns></returns> public bool SyncToTeams(AppMetadata appMetadata) { return(Task.Run(() => SyncToTeamsAsync(appMetadata)).GetAwaiter().GetResult()); }
/// <summary> /// Removes an app from the app catalog /// </summary> /// <param name="appMetadata">The app metadata object of the app to remove.</param> /// <param name="scope">Specifies the app catalog to work with. Defaults to Tenant</param> /// <returns></returns> public bool Remove(AppMetadata appMetadata, AppCatalogScope scope = AppCatalogScope.Tenant) { return(Task.Run(() => RemoveAsync(appMetadata, scope)).GetAwaiter().GetResult()); }
/// <summary> /// Deploys/trusts an app in the app catalog /// </summary> /// <param name="appMetadata">The app metadata object of the app to deploy.</param> /// <param name="skipFeatureDeployment">If set to true will skip the feature deployed for tenant scoped apps.</param> /// <param name="scope">Specifies the app catalog to work with. Defaults to Tenant</param> /// <returns></returns> public bool Deploy(AppMetadata appMetadata, bool skipFeatureDeployment = true, AppCatalogScope scope = AppCatalogScope.Tenant) { return(Task.Run(() => DeployAsync(appMetadata, skipFeatureDeployment, scope)).GetAwaiter().GetResult()); }
#pragma warning disable CA2000 private async Task <AppMetadata> BaseAddRequest(byte[] file, string filename, bool overwrite, int timeoutSeconds, AppCatalogScope scope) { AppMetadata returnValue = null; var isCloned = false; var context = _context; if (scope == AppCatalogScope.Tenant) { // switch context to appcatalog var appcatalogUri = _context.Web.GetAppCatalog(); context = context.Clone(appcatalogUri); isCloned = true; } var accessToken = context.GetAccessToken(); using (var handler = new HttpClientHandler()) { context.Web.EnsureProperty(w => w.Url); if (string.IsNullOrEmpty(accessToken)) { context.SetAuthenticationCookiesForHandler(handler); } using (var httpClient = new PnPHttpProvider(handler)) { string requestUrl = $"{context.Web.Url}/_api/web/{(scope == AppCatalogScope.Tenant ? "tenant" : "sitecollection")}appcatalog/Add(overwrite={(overwrite.ToString().ToLower())}, url='{filename}')"; var requestDigest = string.Empty; if (!string.IsNullOrEmpty(accessToken)) { requestDigest = await context.GetRequestDigestAsync().ConfigureAwait(false); } else { requestDigest = await context.GetRequestDigestAsync(handler.CookieContainer).ConfigureAwait(false); } using (var request = new HttpRequestMessage(HttpMethod.Post, requestUrl)) { request.Headers.Add("accept", "application/json;odata=nometadata"); if (!string.IsNullOrEmpty(accessToken)) { request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken); } else { if (context.Credentials is NetworkCredential networkCredential) { handler.Credentials = networkCredential; } } if (!string.IsNullOrEmpty(requestDigest)) { request.Headers.Add("X-RequestDigest", requestDigest); } request.Headers.Add("binaryStringRequestBody", "true"); request.Content = new ByteArrayContent(file); httpClient.Timeout = new TimeSpan(0, 0, timeoutSeconds); // Perform actual post operation HttpResponseMessage response = await httpClient.SendAsync(request, new System.Threading.CancellationToken()); if (response.IsSuccessStatusCode) { // If value empty, URL is taken var responseString = await response.Content.ReadAsStringAsync(); if (responseString != null) { using (var jsonDocument = JsonDocument.Parse(responseString)) { if (jsonDocument.RootElement.TryGetProperty("UniqueId", out JsonElement uniqueIdElement)) { var id = uniqueIdElement.GetString(); returnValue = await GetAppMetaData(scope, context, accessToken, httpClient, requestDigest, id); } } } } else { // Something went wrong... throw new Exception(await response.Content.ReadAsStringAsync()); } } } } if (isCloned) { context.Dispose(); } return(await Task.Run(() => returnValue)); }