I'm trying to create a live broadcast using the nodejs client library but I'm getting the following error:
{ Error: Title is required
at Request._callback code: 400,
.
.
.
errors:
[ { domain: 'youtube.liveBroadcast',
reason: 'titleRequired',
message: 'Title is required',
extendedHelp: 'https://developers.google.com/youtube/v3/live/docs/liveBroadcasts/insert#request_body' } ] }
It's working on the API Explorer and that's getting me lost with this one. Here is code:
var google = require('googleapis');
var OAuth2 = google.auth.OAuth2;
var youtube = google.youtube('v3');
var oauth2Client = new OAuth2(
'xxxx', //CLIENT_ID
'xxxx', //MY_CLIENT_SECRET,
'http://localhost:3000/api/integrations/youtube'//YOUR_REDIRECT_URL
);
oauth2Client.setCredentials({
access_token: "xxxx",
refresh_token: "xxxx"
});
broadcastParams = {
"auth": oauth2Client,
"part": "snippet,status,contentDetails",
"snippet": {
"title": "Testing NodeJS",
"scheduledStartTime": "2017-02-20T14:00:00.000Z",
"scheduledEndTime": "2017-02-20T15:00:00.000Z",
},
"status": {
"privacyStatus": "private",
},
"contentDetails": {
"monitorStream": {
"enableMonitorStream": true,
}
}
};
youtube.liveBroadcasts.insert(broadcastParams,
function(err,broadcast) {
if (err) {
return console.log('Error creating broadcast: ', err);
}
console.log('Broadcast = ' + JSON.stringify(broadcast));
});
Thanks for the help!
Got it solved.
My broadcast parameters wasn't correct. I was missing the "resource". Here is the code that is working now:
var google = require('googleapis');
var OAuth2 = google.auth.OAuth2;
var youtube = google.youtube('v3');
var oauth2Client = new OAuth2(
'xxxx', //CLIENT_ID
'xxxx', //MY_CLIENT_SECRET,
'http://localhost:3000/api/integrations/youtube'//YOUR_REDIRECT_URL
);
oauth2Client.setCredentials({
access_token: "xxxx",
refresh_token: "xxxx"
});
broadcastParams = {
"auth": oauth2Client,
"part": "snippet,status,contentDetails",
"resource": {
"snippet": {
"title": "Tesing NodeJS 123",
"scheduledStartTime": "2017-02-20T14:00:00.000Z",
"scheduledEndTime": "2017-02-20T15:00:00.000Z",
},
"status": {
"privacyStatus": "private",
},
"contentDetails": {
"monitorStream": {
"enableMonitorStream": true,
}
}
}
};
youtube.liveBroadcasts.insert(broadcastParams, function(err,broadcast) {
if (err) {
return console.log('Error creating broadcast: ', err);
}
console.log('Broadcast = ' + JSON.stringify(broadcast));
});
Related
I'm trying to Use Youtube api v3 to comment on video and getting this error but my request data is correct according to documentation.
Here is my code.
Using oauth the code setting access_token like this
oauth.setCredentials(tokens);
var channelId = "UCq-Fj5jknLsUf-MWSy4_brA";
var request = Youtube.commentThreads.insert({
"part": [
"snippet"
],
"resource": {
"snippet": {
"videoId": "qfuFeUnAm8E",
"topLevelComment": {
"snippet": {
"textOriginal": "best video"
}
},
"channelId": channelId
}
}
}, (err, data) => {
if (err) {
console.log(err, 'errerrerr')
}
if (data) {
console.log(data, 'datadata');
}
});
This is the error i'm getting in return
errors: [
{
message: "The API server failed to successfully process the request. While this can be a transient error, it usually indicates that the request's input is invalid. Check the structure of the <code>commentThread</code> resource in the request body to ensure that it is valid.",
domain: 'youtube.commentThread',
reason: 'processingFailure',
location: 'body',
locationType: 'other'
}
]
This is the authentication or authorization code generating everytime
"tokens": {
"access_token": "[redacted]",
"scope": "https://www.googleapis.com/auth/youtube https://www.googleapis.com/auth/youtubepartner https://www.googleapis.com/auth/youtube.force-ssl https://www.googleapis.com/auth/youtube.upload",
"token_type": "Bearer",
"expiry_date": 1655195240477
}
I think you should consider testing out your insert in the try me The object you have created doesnt look right at all.
You should consult comments resource for the proper format of the body.
<script src="https://apis.google.com/js/api.js"></script>
<script>
/**
* Sample JavaScript code for youtube.comments.insert
* See instructions for running APIs Explorer code samples locally:
* https://developers.google.com/explorer-help/code-samples#javascript
*/
function authenticate() {
return gapi.auth2.getAuthInstance()
.signIn({scope: "https://www.googleapis.com/auth/youtube.force-ssl"})
.then(function() { console.log("Sign-in successful"); },
function(err) { console.error("Error signing in", err); });
}
function loadClient() {
gapi.client.setApiKey("YOUR_API_KEY");
return gapi.client.load("https://www.googleapis.com/discovery/v1/apis/youtube/v3/rest")
.then(function() { console.log("GAPI client loaded for API"); },
function(err) { console.error("Error loading GAPI client for API", err); });
}
// Make sure the client is loaded and sign-in is complete before calling this method.
function execute() {
return gapi.client.youtube.comments.insert({
"resource": {
"snippet": {
"videoId": "qfuFeUnAm8E",
"channelId": "UCq-Fj5jknLsUf-MWSy4_brA"
}
}
})
.then(function(response) {
// Handle the results here (response.result has the parsed body).
console.log("Response", response);
},
function(err) { console.error("Execute error", err); });
}
gapi.load("client:auth2", function() {
gapi.auth2.init({client_id: "YOUR_CLIENT_ID"});
});
</script>
<button onclick="authenticate().then(loadClient)">authorize and load</button>
<button onclick="execute()">execute</button>
Trying to get this to work and GET / Patch work just fine, but POST gives me HTTP STATUS 400 and 403. Must be something with scopes. In Azure AD I have set the following scopes:
Mail.ReadWrite (Delegated)
Mail.ReadWrite (Application)
Mail.Send Delegated)
Mail.Send (Application)
So, signing in works just fine, getting / patching messages as well. Only POST doesnt seem to work.
See code for exact error messages.
Angular10
App.module
export function MSALInstanceFactory(): IPublicClientApplication {
return new PublicClientApplication({
auth: {
clientId: 'xxxx',
authority: 'https://login.microsoftonline.com/common/',
redirectUri: '/',
postLogoutRedirectUri: '/#/login'
},
cache: {
cacheLocation: BrowserCacheLocation.LocalStorage,
storeAuthStateInCookie: isIE, // set to true for IE 11
},
system: {
loggerOptions: {
loggerCallback,
logLevel: LogLevel.Info,
piiLoggingEnabled: false
}
}
});
}
export function MSALInterceptorConfigFactory(): MsalInterceptorConfiguration {
const protectedResourceMap = new Map<string, Array<string>>();
protectedResourceMap.set('https://graph.microsoft.com/v1.0/me', ['user.read', 'mail.readWrite', 'email']);
// also tried these scopes ..
// protectedResourceMap.set('https://graph.microsoft.com/v1.0', ['user.read', 'mail.readWrite', 'email']);
// protectedResourceMap.set('https://graph.microsoft.com/v1.0/query', ['user.read', 'mail.readWrite', 'email']);
// protectedResourceMap.set('https://graph.microsoft.com/v1.0/search/query', ['user.read', 'mail.readWrite', 'email']);
return {
interactionType: InteractionType.Redirect,
protectedResourceMap
};
}
export function MSALGuardConfigFactory(): MsalGuardConfiguration {
return { interactionType: InteractionType.Redirect };
}
#NgModule({
imports: [
BrowserModule,
// etc..
],
declarations: [AppComponent],
providers: [
NgEventBus,
ChhServices,
SynclogService,
AppService,
AuthService,
GapiServices,
{
provide: ErrorHandler,
useClass: ErrorService,
},
{
provide: HTTP_INTERCEPTORS,
useClass: MsalInterceptor,
multi: true
},
{
provide: MSAL_INSTANCE,
useFactory: MSALInstanceFactory
},
{
provide: MSAL_GUARD_CONFIG,
useFactory: MSALGuardConfigFactory
},
{
provide: MSAL_INTERCEPTOR_CONFIG,
useFactory: MSALInterceptorConfigFactory
},
MsalService,
MsalGuard,
MsalBroadcastService
],
bootstrap: [AppComponent],
})
export class AppModule { }
Auth.service
signIn() {
console.log('AuthService::signIn');
this.msalService.loginPopup().subscribe((result) => {
this.accessToken = result['accessToken'];
console.log('authority', result, this.accessToken);
});
}
testGraphApi() {
// 200 OK
const apiGet = this.httpClient.get(`https://graph.microsoft.com/v1.0/me/messages/`).subscribe((data) => {
console.log('get', '/me/messages', data);
});
const categories: any[] = ['custom'];
const body = {
subject: '2320, with tags',
flag: { flagStatus: 'flagged' }, // notFlagged
categories,
body: {
contentType: 'html',
content: 'lalala'
},
inferenceClassification: 'other'
};
const id = 'AQMkADAwATM3ZmYAZS0zOTkANy02MTAwAC0wMAItMDAKAEYAAAM_TfJTK-tISYhjZdaCkkbgBwCPpkVcscQ9QJF-EDzB8h_oAAACAQwAAACPpkVcscQ9QJF-EDzB8h_oAAACHbIAAAA=';
// 200 OK
const apiPatch = this.httpClient.patch(`https://graph.microsoft.com/v1.0/me/messages/${id}`, body).subscribe((data) => {
console.log('patch', '/me/messages', data);
});
const bodySendMail = {
'message': {
'subject': 'Meet for lunch?',
'body': {
'contentType': 'Text',
'content': 'The new cafeteria is open.'
},
// etc..
}
}
const headers = new HttpHeaders({ 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.accessToken}` });
// 403 Forbidden
// "code": "ErrorAccessDenied",
// "message": "Access is denied. Check credentials and try again.",
const apiSendMail = this.httpClient.post(`https://graph.microsoft.com/v1.0/me/sendMail`, bodySendMail, { headers }).subscribe((data) => {
console.log('post', '/me/sendMail', data);
});
const bodySearch = {
'requests': [
{
'entityTypes': [
'message'
],
'query': {
'queryString': 'ref:6019d6bf1ce3425fb833559e'
},
'from': 0,
'size': 5
}
]
}
// 400 Bad Request
// "code": "AuthenticationError",
// "message": "Error authenticating with resource",
const apiSearch = this.httpClient.post(`https://graph.microsoft.com/v1.0/search/query`, bodySearch, { headers }).subscribe((data) => {
console.log('post', '/search/query', data);
});
}
// 403 Forbidden
// "code": "ErrorAccessDenied",
// "message": "Access is denied. Check credentials and try again."
Send mail API needs Mail.Send permission. When requesting /me endpoint which bases the current signed-in user, it should have the delegated permission.
So you need to add Mail.Send of delegated permission in the portal and add it in your code.
// 400 Bad Request
// "code": "AuthenticationError",
// "message": "Error authenticating with resource"
searchEntity: query API needs the Mail.ReadWrite delegated permission. This api only supports "work or school account". A work account typically uses an organization’s custom domain name or company name, such as "jon#contoso.com" or "xxx#yourTenantName.onmicrosoft.com".
You could test to request the api in Graph Explorer.
Starting up NestJS & GraphQL using yarn start:dev using await app.listen(3200);. When trying to connect to my Neo4J Desktop, I get this error trying to get my queries at localhost:3200/graphQL:
"errors": [
{
"message": "Client network socket disconnected before secure TLS connection was established",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"getMovies"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"code": "ServiceUnavailable",
"name": "Neo4jError"
}
}
}
],
"data": null
}
So I figured my local Neo4J desktop graph is not running correctly, but I can't seem to find any answer how to solve it.. Currently I have a config.ts file which has:
export const HOSTNAME = 'localhost';
export const NEO4J_USER = 'neo4j';
export const NEO4J_PASSWORD = '123';
and a file neogql.resolver.ts:
import {
Resolver,
Query,
Args,
ResolveProperty,
Parent,
} from '#nestjs/graphql';
import { HOSTNAME, NEO4J_USER, NEO4J_PASSWORD } from '../config';
import { Movie } from '../graphql';
import { Connection, relation, node } from 'cypher-query-builder';
import { NotFoundException } from '#nestjs/common';
const db = new Connection(`bolt://${HOSTNAME}`, {
username: NEO4J_USER,
password: NEO4J_PASSWORD,
});
#Resolver('Movie')
export class NeogqlResolver {
#Query()
async getMovies(): Promise<Movie> {
const movies = (await db
.matchNode('movies', 'Movie')
.return([
{
movies: [{ id: 'id', title: 'title', year: 'year' }],
},
])
.run()) as any;
return movies;
}
#Query('movie')
async getMovieById(
#Args('id')
id: string,
): Promise<any> {
const movie = (await db
.matchNode('movie', 'Movie')
.where({ 'movie.id': id })
.return([
{
movie: [{ id: 'id', title: 'title', year: 'year' }],
},
])
.run<any>()) as any;
if (movie.length === 0) {
throw new NotFoundException(
`Movie id '${id}' does not exist in database `,
);
}
return movie[0];
}
#ResolveProperty()
async actors(#Parent() movie: any) {
const { id } = movie;
return (await db
.match([node('actors', 'Actor'), relation('in'), node('movie', 'Movie')])
.where({ 'movie.id': id })
.return([
{
actors: [
{
id: 'id',
name: 'name',
born: 'born',
},
],
},
])
.run()) as any;
}
}
Be sure to pass the Config object like this:
var hostname = this.configService.get<string>('NEO4J_URL');
var username = this.configService.get<string>('NEO4J_USERNAME');
var password = this.configService.get<string>('NEO4J_PASSWORD');
db = new Connection(`${hostname}`, {
username: username,
password: password,
}, {
driverConfig: { encrypted: "ENCRYPTION_OFF" }
});
I had the same problem with grandSTACK when running against a neo4j version 4 server. According to Will Lyon this is due to mismatched encryption defaults between driver and database: https://community.neo4j.com/t/migrating-an-old-grandstack-project-to-neo4j-4/16911/2
So passing a config object with
{ encrypted: "ENCRYPTION_OFF"}
to the Connection constructor should do the trick.
I wanted to create ECS task that takes all json as its environment input. But my cdk code won't deploy because of following error message, the error message is so vague and it is difficult for me to figure out why my code is wrong.
Failed to call Step Functions for request: 'com.amazonaws.services.stepfunctions.model.CreateStateMachineRequest'. (Service: null; Status Code: 500; Error Code: null; Request ID: null)
new StateMachine (/local/home/miae/Explanation/src/ForecastingDeepLearningExplanationInfrastructure/node_modules/#aws-cdk/aws-stepfunctions/lib/state-machine.ts:101:26)
My cdk code
...
const ecsFargateTask = new sfn.Task(this, 'myEcs', {
inputPath: "$",
resultPath: "$.ecs",
task: new class implements sfn.IStepFunctionsTask {
bind(): sfn.StepFunctionsTaskConfig {
return {
resourceArn: "arn:aws:states:::ecs:runTask.sync",
parameters: {
"LaunchType": "FARGATE",
"Cluster": props.cluster.clusterArn,
"TaskDefinition": taskDefinition.taskDefinitionArn,
"Overrides": {
"ContainerOverrides": [{
"Name": "myContainer",
"Environment.$": "$.envs"
}]
}
}
};
}
}
});
}
const chain = sfn.Chain.start(ecsFargateTask);
new sfn.StateMachine(this, `StateMachineCopy${props.stage}`, {
definition: chain,
timeout: cdk.Duration.seconds(3000)
});
This is the Step function I want, and I could manually create this without problem.
{
"StartAt": "ExplanationEcs",
"States": {
"ExplanationEcs": {
"End": true,
"InputPath": "$",
"Parameters": {
"LaunchType": "FARGATE",
"Cluster": "arn:aws:ecs:us-west-2:123456789:cluster/myCluster482E02CC-1VWQ5XRG4II88",
"TaskDefinition": "arn:aws:ecs:us-west-2:123456789:task-definition/myTaskDefinitionE3E6548C:3",
"Overrides": {
"ContainerOverrides": [
{
"Name": "myContainer",
"Environment.$": "$.envs"
}
]
}
},
"Type": "Task",
"Resource": "arn:aws:states:::ecs:runTask.sync",
"ResultPath": "$.ecs"
}
},
"TimeoutSeconds": 3000
}
I have the following in the application template:
<vaadin-grid id="directory">
<vaadin-grid-tree-column path="name" header="Name"></vaadin-grid-tree-column>
</vaadin-grid>
The iron-ajax calls the following on a successful response:
getlist(request) {
var myResponse = request.detail.response;
console.log(myResponse);
this.$.directory.items = myResponse;
}
The data that is returned is:
[
{
"name": "apps",
"fullpath": "/db/system/xqdoc/apps",
"children": [
{
"name": "xqDoc",
"fullpath": "/db/system/xqdoc/apps/xqDoc",
"children": [
{
"name": "modules",
"fullpath": "/db/system/xqdoc/apps/xqDoc/modules",
"children": [
{
"name": "config.xqm.xml",
"fullpath": "/db/system/xqdoc/apps/xqDoc/modules/config.xqm.xml"
},
{
"name": "xqdoc-lib.xqy.xml",
"fullpath": "/db/system/xqdoc/apps/xqDoc/modules/xqdoc-lib.xqy.xml"
}
]
}
]
}
]
}
]
The apps shows up, but when I expand the apps node, then xqDoc node doees not show up.
Do I need additional data in the dataset?
Am I missing some coding that is needed?
I have the solution.
<vaadin-grid id="directory" selected-items="{{selected}}">
<vaadin-grid-tree-column path="name" header="Name"item-has-children-path="hasChildren"></vaadin-grid-tree-column>
</vaadin-grid>
I setup the provider using the connectedCallback and not to use an iron-ajax for talking with the server.
connectedCallback() {
super.connectedCallback();
const grid = this.$.directory;
this.$.directory.dataProvider = function(params, callback) {
let url = "/exist/restxq/xqdoc/level" +
'?page=' + params.page + // the requested page index
'&per_page=' + params.pageSize; // number of items on the page
if (params.parentItem) {
url += '&path=' + params.parentItem.fullpath;
}
var xhr = new XMLHttpRequest();
xhr.onload = function() {
var response = JSON.parse(xhr.responseText);
callback(
response.data, // requested page of items
response.totalSize // total number of items
);
};
xhr.open('GET', url, true);
xhr.send();
};
this.$.directory.addEventListener('active-item-changed', function(event) {
const item = event.detail.value;
if (item && item.hasChildren == false) {
grid.selectedItems = [item];
} else {
grid.selectedItems = [];
}
});
}
The web service returns a level of the tree:
{
"totalSize": 2,
"data": [
{
"name": "apps",
"fullpath": "/apps",
"hasChildren": true
},
{
"name": "lib",
"fullpath": "/lib",
"hasChildren": true
}
]
}
The codebase is here: https://github.com/lcahlander/xqDoc-eXist-db