How to fix error: "Google.Apis.Auth: At least one client secrets (Installed or Web) should be set " for YouTubeAPI - .net-core

I have created an HTTP trigger azure function, which holds the code (below) to upload a video to YouTube automatically. Source: (https://developers.google.com/youtube/v3/docs/videos/insert).
When I try to run the app locally using visual studio, I am getting the following error:
Executed 'Function1' (Failed, Id=d601d64a-2f2c-4f8a-8053-a2f33ca21dbc)
System.Private.CoreLib: Exception while executing function: Function1.
Google.Apis.Auth: At least one client secrets (Installed or Web)
should be set
It looks like a Google Authentication error, but I am unsure as to how to get this fixed and I see that YouTube API does not support Service account? How can this issue be fixed, is there a get around to this? Thanks in advance.
C# Code:
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Upload;
using Google.Apis.YouTube.v3.Data;
using System.Reflection;
using Google.Apis.YouTube.v3;
using Google.Apis.Services;
using System.Threading;
namespace UploadVideo
{
public static class Function1
{
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
log.LogInformation("YouTube Data API: Upload Video");
log.LogInformation("==============================");
try
{
await Run();
}
catch (AggregateException ex)
{
foreach (var e in ex.InnerExceptions)
{
log.LogInformation("Error: " + e.Message);
}
}
return new OkObjectResult($"Video Processed..");
}
private static async Task Run()
{
UserCredential credential;
using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
// This OAuth 2.0 access scope allows an application to upload files to the
// authenticated user's YouTube channel, but doesn't allow other types of access.
new[] { YouTubeService.Scope.YoutubeUpload },
"user",
CancellationToken.None
);
}
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = Assembly.GetExecutingAssembly().GetName().Name
});
var video = new Video();
video.Snippet = new VideoSnippet();
video.Snippet.Title = "Default Video Title";
video.Snippet.Description = "Default Video Description";
video.Snippet.Tags = new string[] { "tag1", "tag2" };
video.Snippet.CategoryId = "22"; // See https://developers.google.com/youtube/v3/docs/videoCategories/list
video.Status = new VideoStatus();
video.Status.PrivacyStatus = "unlisted"; // or "private" or "public"
var filePath = #"C:\Users\Peter\Desktop\audio\test.mp4"; // Replace with path to actual movie file.
using (var fileStream = new FileStream(filePath, FileMode.Open))
{
var videosInsertRequest = youtubeService.Videos.Insert(video, "snippet,status", fileStream, "video/*");
videosInsertRequest.ProgressChanged += videosInsertRequest_ProgressChanged;
videosInsertRequest.ResponseReceived += videosInsertRequest_ResponseReceived;
await videosInsertRequest.UploadAsync();
}
}
private static void videosInsertRequest_ProgressChanged(Google.Apis.Upload.IUploadProgress progress)
{
switch (progress.Status)
{
case UploadStatus.Uploading:
Console.WriteLine("{0} bytes sent.", progress.BytesSent);
break;
case UploadStatus.Failed:
Console.WriteLine("An error prevented the upload from completing.\n{0}", progress.Exception);
break;
}
}
private static void videosInsertRequest_ResponseReceived(Video video)
{
Console.WriteLine("Video id '{0}' was successfully uploaded.", video.Id);
}
}
}

It looks like you were trying to use the service account to do the OAuth2 web server flow, which wont work. The correct code form creating a service account credential is as follows.
GoogleCredential credential;
using (var stream = new FileStream(serviceAccountCredentialFilePath, FileMode.Open, FileAccess.Read))
{
credential = GoogleCredential.FromStream(stream)
.CreateScoped(scopes);
}
Note
as i have mentioned in your other questions the YouTube API does NOT support service account authentication. You must use Oauth2 and i am not convinced this can be done inside of azure functions. As there is no way to spawn the web browser window to request authorization of the user.

Related

unable to store json in single line inside azure blob storage using azure function

I have a requirement to store json in a single line(without any formatting) inside a blob storage file. I am using azure function with Newtonsoft.JSon properties for some manipulation purpose and then writing to a blob . But when I try to using JToken.Parse I am getting exception or internal server error.Below is the code I am using:
#r "Newtonsoft.Json"
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public static async Task<IActionResult> Run(HttpRequest req,TextWriter outputBlob,ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
log.LogInformation($"Response is {requestBody}");
dynamic jObject = JsonConvert.DeserializeObject(requestBody);
JToken jCategory = jObject;
var clus = jCategory["clusters"];
foreach(JObject item in clus)
{
var custom_tag=item["custom_tags"];
var app_logical_name = item.SelectToken("custom_tags.app_name");
item.SelectToken("init_scripts_safe_mode").Parent.AddAfterSelf(new JProperty("app_logical_name",app_logical_name));
}
var clus2 = JsonConvert.SerializeObject(jCategory,Formatting.None);
//var clus_new=JArray.Parse(clus).toString(Newtonsoft.Json.Formatting.None);
outputBlob.Write(clus2);
// outputBlob.Write(clus_new);
return new OkObjectResult(requestBody);
}
I have tried both ways but both are giving runtime errors. I just need to put the json in a single line(without any formatting) and write to blob.Can you please help me in this ?
This is the structure on my side:
{
"clusters":[
{
"custom_tags":{
"app_name": "appname1"
},
"init_scripts_safe_mode":{
"xxx": "yyy"
}
},
{
"custom_tags":{
"app_name": "appname2"
},
"init_scripts_safe_mode":{
"xxx2": "yyy2"
}
}
],
"test":"333"
}
And this is my code:
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace FunctionApp3
{
public static class Function1
{
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
log.LogInformation($"Response is {requestBody}");
dynamic jObject = JsonConvert.DeserializeObject(requestBody);
JToken jCategory = jObject;
var clus = jCategory["clusters"];s2 = JsonConvert.SerializeObject(jCategory, Formatting.None);
foreach (JObject item in clus)
{
var custom_tag = item["custom_tags"];
var app_logical_name = item.SelectToken("custom_tags.app_name");
var xxx = item.SelectToken("init_scripts_safe_mode");
xxx.Parent.AddAfterSelf(new JProperty("app_logical_name", app_logical_name));
log.LogInformation(JsonConvert.SerializeObject(custom_tag, Formatting.None));
log.LogInformation(JsonConvert.SerializeObject(app_logical_name, Formatting.None));
}
return new OkObjectResult(clus);
}
}
}
It seems no problem:
If you get the server side error, please check the details log to get where is the error comes from.
The 500 error is not helpful to solve this problem, you need to check the specific error of the azure function. You can use application insights to get the details error. The function must configure the corresponding application insights before you can view the log on the portal.
So you need to configure an application insights to your function app like this:
Then your function app will restart.
Of course, you can also go to kudu to view:
First, go to advanced tools, then click 'GO',
Then After you go to kudu, click Debug Console -> CMD -> LogFiles -> Application -> Functions -> yourtriggername. You will find log file there.
If you are based on linux OS, after go to kudu, just click 'log stream'(this is not supportted to consumption plan for linux.).

Unauthorized when accessing Azure cognitive services from netcore3.1 console app

I have a netcore console app which is accessing the Azure's Text analysis API's using the Client library from the Microsoft.Azure.CognitiveServices.Language.TextAnalytics Nuget package.
When trying to access the API, I receive the following HttpException:
Unauthorized. Access token is missing, invalid, audience is incorrect (https://cognitiveservices.azure.com), or have expired.
Unhandled exception. System.AggregateException: One or more errors occurred. (Operation returned an invalid status code 'Unauthorized')
When accessing the same API using exactly the same code which is hosted on Azure Functions - everything works as expected. I was unable to find any info in the docs or anywhere else.
Try the .net core console app code below to use TextAnalytics SDK :
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.CognitiveServices.Language.TextAnalytics;
using Microsoft.Azure.CognitiveServices.Language.TextAnalytics.Models;
using Microsoft.Rest;
namespace TextAnalysis
{
class Program
{
private static readonly string key = "<your text analyisis service key>";
private static readonly string endpoint = "<your text analyisis service endpoint>";
static void Main(string[] args)
{
var client = authenticateClient();
sentimentAnalysisExample(client);
languageDetectionExample(client);
entityRecognitionExample(client);
keyPhraseExtractionExample(client);
Console.Write("Press any key to exit.");
Console.ReadKey();
}
static TextAnalyticsClient authenticateClient()
{
ApiKeyServiceClientCredentials credentials = new ApiKeyServiceClientCredentials(key);
TextAnalyticsClient client = new TextAnalyticsClient(credentials)
{
Endpoint = endpoint
};
return client;
}
class ApiKeyServiceClientCredentials : ServiceClientCredentials
{
private readonly string apiKey;
public ApiKeyServiceClientCredentials(string apiKey)
{
this.apiKey = apiKey;
}
public override Task ProcessHttpRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request == null)
{
throw new ArgumentNullException("request");
}
request.Headers.Add("Ocp-Apim-Subscription-Key", this.apiKey);
return base.ProcessHttpRequestAsync(request, cancellationToken);
}
}
static void sentimentAnalysisExample(ITextAnalyticsClient client)
{
var result = client.Sentiment("I had the best day of my life.", "en");
Console.WriteLine($"Sentiment Score: {result.Score:0.00}");
}
static void languageDetectionExample(ITextAnalyticsClient client)
{
var result = client.DetectLanguage("This is a document written in English.","us");
Console.WriteLine($"Language: {result.DetectedLanguages[0].Name}");
}
static void entityRecognitionExample(ITextAnalyticsClient client)
{
var result = client.Entities("Microsoft was founded by Bill Gates and Paul Allen on April 4, 1975, to develop and sell BASIC interpreters for the Altair 8800.");
Console.WriteLine("Entities:");
foreach (var entity in result.Entities)
{
Console.WriteLine($"\tName: {entity.Name},\tType: {entity.Type ?? "N/A"},\tSub-Type: {entity.SubType ?? "N/A"}");
foreach (var match in entity.Matches)
{
Console.WriteLine($"\t\tOffset: {match.Offset},\tLength: {match.Length},\tScore: {match.EntityTypeScore:F3}");
}
}
}
static void keyPhraseExtractionExample(TextAnalyticsClient client)
{
var result = client.KeyPhrases("My cat might need to see a veterinarian.");
// Printing key phrases
Console.WriteLine("Key phrases:");
foreach (string keyphrase in result.KeyPhrases)
{
Console.WriteLine($"\t{keyphrase}");
}
}
}
}
You can find your key and endpoint here on Azure portal :
Result :

How Publish SNS message in C# Lambda Core

I found the code below at https://forums.aws.amazon.com/thread.jspa?threadID=53629:
AmazonSimpleNotificationService sns = AWSClientFactory.CreateAmazonSNSClient(key, secret);
PublishRequest req = new PublishRequest();
req.WithMessage("This is my test message");
req.WithSubject("The Test Subject");
req.WithTopicArn(topicArn);
PublishResponse result = sns.Publish(req);
But does it work in .NET Core? If so how, and what using statements?
I used this Nuget install:
Install-Package AWSSDK.SimpleNotificationService -Version 3.3.0.23
Are the methods totally different? Just poking around using Intellisense, I have found:
var req = new AmazonSimpleNotificationServiceRequest();
var client = new AmazonSimpleNotificationServiceClient();
but req. doesn't show any properties.
I've tried searching here: https://docs.aws.amazon.com/sdkfornet/v3/apidocs/Index.html
but it is saying "The service is currently unavailable. Please try again after some time." (so yes, I will try later, but not sure it will have what I want anyhow).
--- Update 10/30 - This is the the only publish method of the
AmazonSimpleNotificationServiceRequest() class
--- Update 2 on 10/30 - Found this post:
Send SMS using AWS SNS - .Net Core
Created new question for code that I'm trying, but it's not working:
How to call SNS PublishAsync from Lambda Function?
The .NET Core version of the SDK only support async operations because that is what the underlying HTTP Client in .NET Core supports. Your example with the WithXXX operations is from the older V2 version of the SDK not the current V3 modularized version.
The only difference you should need to do for V3 when using .NET Core is use async operations. For example here is a very simple console
using System;
using Amazon.SimpleNotificationService;
using Amazon.SimpleNotificationService.Model;
using System.Threading.Tasks;
namespace ConsoleApp4
{
class Program
{
static void Main(string[] args)
{
var client = new AmazonSimpleNotificationServiceClient(Amazon.RegionEndpoint.USEast2);
SendMessage(client).Wait();
}
static async Task SendMessage(IAmazonSimpleNotificationService snsClient)
{
var request = new PublishRequest
{
TopicArn = "INSERT TOPIC ARN",
Message = "Test Message"
};
await snsClient.PublishAsync(request);
}
}
}
Here is a longer example. Let me know if this works and what other types of examples you would like. I'd like to improve the .NET developer guide, https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/welcome.html.
using System;
using Amazon.SimpleNotificationService;
using Amazon.SimpleNotificationService.Model;
namespace SNSExample
{
class Program
{
static async System.Threading.Tasks.Task SNSAsync()
{
try
{
AmazonSimpleNotificationServiceClient client = new AmazonSimpleNotificationServiceClient(Amazon.RegionEndpoint.USWest2);
// Create a topic
CreateTopicRequest createTopicReq = new CreateTopicRequest("New-Topic-Name");
CreateTopicResponse createTopicRes = await client.CreateTopicAsync(createTopicReq);
Console.WriteLine("Topic ARN: {0}", createTopicRes.TopicArn);
//subscribe to an SNS topic
SubscribeRequest subscribeRequest = new SubscribeRequest(createTopicRes.TopicArn, "email", "your#email.com");
SubscribeResponse subscribeResponse = await client.SubscribeAsync(subscribeRequest);
Console.WriteLine("Subscribe RequestId: {0}", subscribeResponse.ResponseMetadata.RequestId);
Console.WriteLine("Check your email and confirm subscription.");
//publish to an SNS topic
PublishRequest publishRequest = new PublishRequest(createTopicRes.TopicArn, "My text published to SNS topic with email endpoint");
PublishResponse publishResponse = await client.PublishAsync(publishRequest);
Console.WriteLine("Publish MessageId: {0}", publishResponse.MessageId);
//delete an SNS topic
DeleteTopicRequest deleteTopicRequest = new DeleteTopicRequest(createTopicRes.TopicArn);
DeleteTopicResponse deleteTopicResponse = await client.DeleteTopicAsync(deleteTopicRequest);
Console.WriteLine("DeleteTopic RequestId: {0}", deleteTopicResponse.ResponseMetadata.RequestId);
}
catch (Exception ex)
{
Console.WriteLine("\n\n{0}", ex.Message);
}
}
static void Main(string[] args)
{
SNSAsync().Wait();
}
}
}

How to send an XML to an asp.net Web api call?

I am trying to make a web API Post method call as follows but it not working as expected,xmlcontent seems OK but somehow the formatting seems messed up when the request is being sent and the response throws an error ,I double checked the XML from python and it works,is there a better way to create and send the XML?what am I doing wrong?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Http;
namespace WebApiXML
{
public class Program
{
static void Main(string[] args)
{
testWCF2(); //Or whatever
Console.ReadLine();
}
public static async Task testWCF2()
{
string xmlcontent = #"<SoftwareProductBuild>
<BuildSource>DATASOURCE</BuildSource>
<BuiltBy>username1</BuiltBy>
<CreatedBy>username1</CreatedBy>
<Name>username1_1959965_1969310_524f7fef-5b37-11e7-b4ee-f0921c133f10_UL.AB.1.2_test2</Name>
<Status>Approved</Status>
<BuiltOn>2017-06-27T06:20:30.275690</BuiltOn>
<Tag>username1_1959965_1969310_524f7fef-5b37-11e7-b4ee-f0921c133f10_test2</Tag>
<Keywords>
<KeywordInfo>
<Name>subystem</Name>
</KeywordInfo>
</Keywords>
<SoftwareImageBuilds>
<SoftwareImageBuild>
<Type>LA</Type>
<Name>username1_1959965_1969310_524f7fef-5b37-11e7-b4ee-f0921c133f10_UL.AB.1.2_test2</Name>
<Location>\\location1\data1\PRECOMMIT_OS_DEF</Location>
<Variant>PRECOMMIT_OS_DEF</Variant>
<LoadType>Direct</LoadType>
<Target>msm8998</Target>
<SoftwareImages>
<SoftwareImage>
<Name>UL.AB.1.2</Name>
</SoftwareImage>
</SoftwareImages>
</SoftwareImageBuild>
</SoftwareImageBuilds>
</SoftwareProductBuild>";
#region using
using (var client = new System.Net.Http.HttpClient())
{
var response = await client.PostAsXmlAsync("http://server:8100/api/SoftwareProductBuild", xmlcontent);
if (!response.IsSuccessStatusCode)
{
//throw new InvalidUriException("Some error with details.");
Console.WriteLine(response);
}
Console.WriteLine("Printing DEV Pool Response\n");
}
#endregion
//return null;
}
}
}
PostAsXmlAsync will try to serialize the object passed to it. So you have a string that contains XML and then try to post the string as XML(Double serialization).
Use StringContent, giving it the XML string value and set the content type to appropriate media type, then post it. i.e. client.PostAsync(url, content)
using (var client = new System.Net.Http.HttpClient()) {
var url = "http://server:8100/api/SoftwareProductBuild";
var content = new StringContent(xmlcontent, Encoding.UTF8, "application/xml");
var response = await client.PostAsync(url, content);
if (response.IsSuccessStatusCode) {
var responseBody = await response.Content.ReadAsStringAsync();
Console.WriteLine("Printing DEV Pool Response\n");
Console.WriteLine(responseBody);
} else {
Console.WriteLine(string.Format("Bad Response {0} \n", response.StatusCode.ToString()));
}
}

Uploading a file from .NET application to a Web API POST endpoint

I'd like to use Web API to build some endpoints for applications to consume. The first job I'd like it to do is allow the client to upload a file to the server.
The client will run a .NET app of some kind, maybe a console app or maybe something else. It won't be a webpage using a form element or file input.
I think the Web API would look something like this:
public class FileController : ApiController
{
public bool Post(File newFile)
{
return true;
}
}
Using this as a model class:
public class File
{
public string name { get; set; }
public Stream uploadStream { get; set; }
}
I'm sure that's horribly wrong but it's my first Web API.
I'm trying to test this in a console application:
namespace TestFileUpload
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Starting the test...");
using (FileStream readstream = new FileStream(#"C:\\Test\Test2.txt", FileMode.Open, FileAccess.Read))
{
WebAPI.Classes.File newFile = new WebAPI.Classes.File()
{
name = "Test.txt",
uploadStream = readstream
};
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://localhost:50326");
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response;
response = client.PostAsJsonAsync("http://localhost:50326/api/file", newFile).Result;
Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
};
Console.ReadLine();
}
}
}
Now I'm getting a timeout error when I try to get the response:
"Error getting value from 'ReadTimeout' on 'System.IO.FileStream'."
Help?
There are many ways for a client to consume a Web API service, but the most straight forward would be to use the web api client library. Perhaps you should consider building a simple get method that returns an object before jumping into file uploads.
Web API from .NET Client
You can not add a service reference as Web API does not expose a wsdl.

Resources