mediasoup-client TypeError: Cannot read property '_createTransport' of null at createRecvTransport - mediasoup

I've got an error in mediasoup-client while executing "device.createRecvTransport" in the Redux SAGA (I know this is not the best practice, but no choice).
"device.createRecvTransport" is actually an abstraction which holds a webRTC RTCPeerConnection instance. When I call it, it throws this error.
TypeError: Cannot read property '_createTransport' of null at createRecvTransport
the code:
function* createRecvTransport(device: Device) {
const {
id,
iceParameters,
iceCandidates,
dtlsParameters,
sctpParameters
} = yield sendRequest({
action: 'createWebRtcTransport',
data: {
forceTcp: false,
producing: false,
consuming: true
}
});
const recvTransport: Transport = yield call(device.createRecvTransport, {
id,
iceParameters,
iceCandidates,
dtlsParameters,
sctpParameters,
iceServers: []
});
}
the Device is declared with function
function* initDevice() {
const handlerName = detectDevice();
const device = new Device({
handlerName
});
const routerRtpCapabilities = yield sendRequest({
action: 'getRouterRtpCapabilities'
});
device.load({ routerRtpCapabilities });
}
"device.createSendTransport" method also works, the only issue is with creating Receive Transport. The method "router.createWebRtcTransport" get executed in the mediasoup router and it returns data before executing "createRecvTransport" in client side.

Seems the problem is with the "yield call" expression.
As the "device.createRecvTransport" isn't an async (handled with promise) function, it shouldn't be executed using "yield call".
When I removed "yield call", it fixed.

Related

How can I invoke and use Google Cloud Functions in a Flutter app?

I have created a url scraper function, working and tested on Google Cloud, but I am really drawing a blank on how to invoke it. I have tried two methods, one using the cloud_functions package, and the other using a standard HTTPS get. I've tried looking online, but none of the solutions/guides involve functions with an input from the Flutter app, and an output back to the app.
Here's the structure of the function (which is working alright). I've named this function Parse in Google Cloud Platform.
<PYTHON PACKAGE IMPORTS>
def Parser(url):
<URL PARSE FUNCTIONS>
return source, datetime, imageurl, keyword
def invoke_parse(request):
request_json = request.get_json(silent=True)
file = Parser(request_json['url'])
return jsonify({
"source": file[0],
"datetime": file[1],
"imageurl": file[2],
"keyword": file[3],
})
The first method I tried was using an HTTP CALL to get the function. But that isn't working, even though there are no errors - I suspect it's just returning nothing.
parser(String url) async{ // Here I honestly don't know where to use the url input within the function
var uri = Uri.parse(<Function URL String>);
HttpClient client;
try {
var request = await client.getUrl(uri);
var response = await request.close();
if (response.statusCode == HttpStatus.ok) {
var json = await response.transform(utf8.decoder).join();
Map data = jsonDecode(json) as Map;
source = data['source']; // These are the variables used in the main Flutter app
postedAt = data['datetime'];
_imageUrl = data['image'];
keyword = data['keyword'];
} else {
print('Error running parse:\nHttp status ${response.statusCode}');
}
} catch (exception) {
print('Failed invoking the parse function.');
}
}
That didn't work, so I thought I might alternatively use the cloud_functions package as follows (in lieu of the previous):
parser(String url) async {
var functionUrl = <FUNCTION URL>;
HttpsCallable callable = CloudFunctions.instance.getHttpsCallable(functionName: 'Parse')
..timeout = const Duration(seconds: 30);
try {
final HttpsCallableResult result = await callable.call(
<String, dynamic>{
'url': url,
}
);
setState(() {
source = result.data['source']; //THESE ARE VARIABLES USED IN THE FLUTTER APP
postedAt = result.data['datetime'];
_imageUrl = result.data['image'];
keyword = result.data['keyword'];
});
}
on CloudFunctionsException catch (e) {
print('caught firebase functions exception');
print(e.code);
print(e.message);
print(e.details);
} catch (e) {
print('caught generic exception');
print(e);
}
}
In the latter case, the code ran without errors but doesn't work. My flutter log states the following error:
I/flutter ( 2821): caught generic exception
I/flutter ( 2821): PlatformException(functionsError, Cloud function failed with exception., {code: NOT_FOUND, details: null, message: NOT_FOUND})
which I'm assuming is also an error at not being able to read the function.
Any help on how I should go about processing my function would be appreciated. Apologies if something is a really obvious solution, but I am not familiar as much with HTTP requests and cloud platforms.
Thanks and cheers.
Node Js Backend Function
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
exports.test = functions.https.onCall(async (data, context) => {
functions.logger.info("Hello logs: ", {structuredData: true});
functions.logger.info( data.token, {structuredData: true});
}
Flutter frontend
1- pubspec.yaml
cloud_functions: ^1.1.2
2 - Code
HttpsCallable callable = FirebaseFunctions.instance.httpsCallable('test');
final HttpsCallableResult results = await callable.call<Map>( {
'token': token,
});

unable to test a method which uses Observable in Jasmine

My Angular application has a service QuestionManagementService which response on a BackendService to send REST messages. The BackendService in turn uses HttpClient. I am trying to test the QuestionManagementService in isolation.
The method I am testing is addQuestion
addQuestion(question:PracticeQuestion):any{
console.log("In QuestionManagementService: addQuestion: ",question);
this.bs.createQuestion(question).subscribe((res)=>{
console.log("add practice question - response is ",res);//I EXPECT THESE PRINTS TO SHOW BUT THEY DON'T
let ev = <HttpEvent<any>>(res);
if(ev.type === HttpEventType.Response) {
console.log('response from server: returning body '+ev.body);
let isResponseStructureOK: boolean = this.helper.validateServerResponseStructure(ev.body);
if (isResponseStructureOK) {
let response:ServerResponseAPI = ev.body;
console.log("received response from server: " + response.result);
this.addQuestionSubject.next(new Result(response.result,response['additional-info']));
} else {
console.log("received incorrect response structure from server: ", ev.body);
this.addQuestionSubject.next(new Result('error','Invalid response structure from server'));
}
}
else {
console.log("not response. ignoring");
}
},
(error:ServerResponseAPI)=>{
console.log("got error from the Observable: ",error);
let errorMessage:string = this.helper.userFriendlyErrorMessage(error);
this.addQuestionSubject.next(new Result('error',errorMessage));//TODOM - need to standardise errors
},
()=>{ //observable complete
console.log("observable completed")
});
}
As I am doing unit testing of addQuestion, I thought that I can mock the createQuestion method of the BackendService. The spec I have written so far is the following but I don't think it is correct as I don't see any prints on the console when the mocked response of createQuestion is received.
fit('should add a question',()=>{
let backendService = TestBed.get(WebToBackendInterfaceService);
let questionService = TestBed.get(QuestionManagementService);
let question = new PracticeQuestion(...);
const responseData = { result: 'success', ['additional-info']: 'question added successfully' };
let httpResponseEvent:HttpResponse<any> = new HttpResponse<any>({body:responseData});
//mock response of WebToBackendInterfaceService
spyOn(backendService,'createQuestion').and.returnValue(new Observable(()=>{
httpResponseEvent;
}));
questionService.addQuestion$.subscribe((res:Result)=>{
console.log('received response from Question Services',res);
expect(res).toBeTruthy();
let validResponse:boolean = ((res.result === 'success') || (res.result === 'initial')) ;
expect(validResponse).toEqual(true);
});
questionService.addQuestion(question);
expect(backendService.createQuestion).toHaveBeenCalled();
});
The issue was that I was creating an Observable but I didn't push the value to an Observer using next. The correct implementation is (code snippet)
spyOn(backendService,'createQuestion').and.returnValue(new Observable((subscriber)=>{ //subscriber or observer
subscriber.next(httpResponseEvent)}
));
When creating an Observable, the Observable's constructor in Rxjs takes a subscribe function as argument. The definition of subscribe function is
(observer)=>{
/*logic of calculating values which Observer should produce and emit then using observer.next(value)*/
}
reference - http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html
With the above code, the this.bs.createQuestion(question) returns a mock Observable whose subscribe function is
(subscriber)=>{ //subscribe function
subscriber.next(httpResponseEvent)
}
The above subscribe function will be called whenever any Observer subscribes to the Observable. So in code .subscribe((res)=>{...}, I subscribe to the mock Observable and now the subscribe function will emit the dummy value, httpResponseEvent to my code. For those new to Observables, the subscribe function takes an Observer as argument. An Observer is an object which has 3 methods next, error and complete. Note that the subscribe function takes such an object
(res)=>{... },
(error:ServerResponseAPI)=>{
...
},
()=>{ //observable complete
...
})

Angular 6 HttpClient.get Observable does not assign value

I suppose that the answer will be very obvious, but still it evades me. I'm new on working with observables, and now I'm facing issues assigning a value from one. I had success if I define it (this._apps) as an Observable and asking from the view to the service using subscribe (But for my taste is was way convoluted (three levels inside a map just to return another observable with the array and then another function to subscribe the previous to assign the variable and another subscription in the view to finally show the information), inefficient and on top of that I could not get it "right" again). The task is very simple. Given the class Application
export class Application {
name: string;
baseUrl: string;
deprecated: boolean;
}
And the service (just the relevant code)
private _apps: Application[] = [];
constructor(private _http: HttpClient) {
this.getAllApplications().subscribe(apps => {
console.log('Apps subscriber');
this._apps = apps;
console.log('Apps subscriber Ends ' + apps);
},
err => {
console.log(err.status); // 401
console.log(err.error.error); // undefined
console.log(JSON.parse(err.error).error); // unauthorized
});
}
private getAllApplications() {
return this._http.get<Application[]>('http://development:4300/api/v1/apps');
}
From the constructor the function which gets the information from WebAPI is triggered, and the remote call is successful, but the variable this._apps is an empty array if I try to call it from anywhere in the code. I could not determine the type of the parameter "apps" in the subscribe function, but for some reason it cannot be assigned and the best answer given is that it is a function (See my first update) in one of my tries. Currently it returns in the console "[object Object]", but apps[0] gives undefined, so it is an empty Array.
This is the console output, just starting the application:
Angular is running in the development mode. Call enableProdMode() to enable the production mode.
Refreshing apps cache calling http://development:4300/api/v1/atbc-apps
Apps subscriber
Apps subscriber Ends [object Object]
I was trying this solution among many others that I forget (to use the more modern HttpClient instead the Http I used before), so what I'm doing wrong?
Update 1
I changed the constructor to this:
constructor(private _http: HttpClient) {
this.getAllApplications().subscribe(apps => {
console.log('apps length ' + apps.length);
this._apps = apps; // Remember private _apps: Application[] = [];
console.log('Apps subscriber Ends ' + apps.toString);
},
err => {
console.log(err.status); // 401
console.log(err.error.error); // undefined
console.log(JSON.parse(err.error).error); // unauthorized
});
}
and the declaration of the function called into this:
private getAllApplications(): Observable<Application[]> {
// the exactly the same as before
}
And now I got from the console this:
apps length undefined
Apps subscriber Ends
function () {
if (this instanceof Promise) {
return PROMISE_OBJECT_TO_STRING;
}
return originalObjectToString.apply(this, arguments);
}
That is the function I was talking about. Any ideas about why even though there is no errors (nor at compile time, neither at runtime), the returning object is not a real Application array?
Change this line:
private _apps: Application[] = [];
to:
_apps: Application[] = [];
Which will default to making it public. Then this line will see it:
this._apps = apps;
At the end I suppose is a mindset to work with Observables, and I tried to build a kind of cache, so the only way I could do it (let me know if there is a better way) was using the view to fill-out the cache. I could not do it from the service itself because the calling the function from the view is synchronous and to fill out the array is async. So I had to create a public setApplicationCache procedure which is filled out after calling the service from the view, it call the setApplicationCache( Application[] ) function and the rest works because it takes just the cache to do filtering and other operations or use it from other pages w/o calling the database again and again.
This is the code from the first view called (main page)
ngOnInit() {
this._myService.getAllApplications().subscribe(arrObjApps => {
this._myService.setApplicationsCache(arrObjApps)
this.listApps = this._myService.getApplications(true);
});
And the service has this functions:
private _apps: Application[] = [];
getAllApplications(): Observable<Application[]> {
return this._http.get('http://development:4300/api/v1/atbc-apps').pipe(
map( (response: Response) => {
let results = response.json().data.map( app => {
return new Application(app.name, app.baseUrl, app.deprecated);
});
return results;
})
);
}
getApplication(appName: string): Application {
return this._apps.find(app => app.name == appName);
}
getApplications(onlyActives: boolean): Application[] {
if (onlyActives) {
return this._apps.filter(app => app.deprecated == false);
} else {
return this._apps;
}
}
And as I stated the solution should be obvious. Just again the async mindset required to work with observables.

Login Service implementation in angularjs not working

This is my controller which is calling the login service
mod.controller("loginCtrl",function($scope,loginService,$http)
{
$scope.Userlogin = function()
{
var User = {
userid :$scope.uname,
pass:$scope.pass
};
var res = UserloginService(User);
console.log(res);
alert("login_succ");
}
});
And this is the login service code which takes the User variable and checks for username & password
mod.service("loginService",function($http,$q) {
UserloginService = function(User) {
var deffered = $q.defer();
$http({
method:'POST',
url:'http://localhost:8080/WebApplication4_1/login.htm',
data:User
}).then(function(data) {
deffered.resolve(data);
}).error(function(status) {
deffered.reject({
status:status
});
});
return deffered.promise;
// var response = $http({
//
// method:"post",
// url:"http://localhost:8080/WebApplication4_1/login.htm",
// data:JSON.stringify(User),
// dataType:"json"
// });
// return "Name";
}
});
I have created a rest api using springs which upon passing json return back the username and password in json like this
Console shows me this error for angular
You need to enable CORS for your application for guidance see this link
https://htet101.wordpress.com/2014/01/22/cors-with-angularjs-and-spring-rest/
I prefer to use Factory to do what you're trying to do, which would be something like this:
MyApp.factory('MyService', ["$http", function($http) {
var urlBase = "http://localhost:3000";
return {
getRecent: function(numberOfItems) {
return $http.get(urlBase+"/things/recent?limit="+numberOfItems);
},
getSomethingElse: function(url) {
return $http.get(urlBase+"/other/things")
},
search: function (searchTerms) {
return $http.get(urlBase+"/search?q="+searchTerms);
}
}
}]);
And then in your controller you can import MyService and then use it in this way:
MyService.getRecent(10).then(function(res) {
$scope.things = res.data;
});
This is a great way to handle it, because you're putting the .then in your controller and you are able to control the state of the UI during a loading state if you'd like, like this:
// initialize the loading var, set to false
$scope.loading = false;
// create a reuseable update function, and inside use a promise for the ajax call,
// which is running inside the `Factory`
$scope.updateList = function() {
$scope.loading = true;
MyService.getRecent(10).then(function(res) {
$scope.loading = false;
$scope.things = res.data;
});
};
$scope.updateList();
The error in the console shows two issues with your code:
CORS is not enabled in your api. To fix this you need to enable CORS using Access-Control-Allow-Origin header to your rest api.
Unhandled rejection error, as the way you are handling errors with '.error()' method is deprecated.
'Promise.error()' method is deprecated according to this and this commit in Angular js github repo.
Hence you need to change the way you are handling errors as shown below :
$http().then(successCallback, errorCallback);
function successCallback (res) {
return res;
}
function errorCallback (err) {
return err;
}
One more thing in your code which can be avoided is you have defined a new promise and resolving it using $q methods, which is not required. $http itself returns a promise by default, which you need not define again inside it to use it as a Promise. You can directly use $http.then().

Calling a JSON function from a module in Dojo

The contents of my dataHelper.js file:
define(["dojo/_base/declare", "dojo/dom", "dojo/_base/xhr", "dojo/json"],
function(declare, dom, xhr, json){
return {
getJSON: function(){
xhr.get({
url: "../../cpuusage.json",
handleAs: "json",
load: function(jsonData){
return jsonData;
},
error: function() {
}
});
}
};
});
I'm trying to run this from my index.html as follows:
var chartData = dataHelper.getJSON();
I think I have several issues. First of all, I'm not sure my module and the getJSON function is defined correctly. Secondly I get errors on my console:
TypeError: this.source is undefined
[Break On This Error]
= [],
dojo.js (line 362)
SyntaxError: missing : after property id
},
dojo.js (line 330)
SyntaxError: missing : after property id
},
dojo.js (line 330)
SyntaxError: missing : after property id
},
All I want to achieve first is load the json data into the chartData variable. Many thanks.
The first issue I'm seeing is you are treating an asynchronous process as if it was a synchronous one. The xhr.get returns immediately after the request to the server is sent, it does not block until a response is received.
First, I would add a console.log to your module definition to ensure that your dataHelper module is being loaded correctly.
define(["dojo/_base/xhr"],
function(xhr){
console.log('dataHelper.js loaded');
return {
//
};
});
Also note that above you aren't using any of the base dojo modules except dojo/_base/xhr, so it is unnecessary to include them (unless they are used outside this snippet).
You need to update your code to handle this call asynchronously. To do this, you could take advantage of the fact that the xhr.get method returns a Deferred object. This class makes dealing with asynchronous in a consistent manner quite easy.
To do this, update the dataHelper module to return the result of the xhr call:
define(["dojo/_base/xhr"], function(xhr){
return {
getJSON: function(){
//this returns a Deferred object, what to do on load and error is then handled by the invoker
return xhr.get({
url: "../../cpuusage.json",
handleAs: "json"
});
}
};
});
Then, when utilizing this module:
//replace dataHelper with whatever it's path is
require(['dataHelper'],function(dataHelper){
var deferred = dataHelper.getJSON();
deferred.then(function(data){
//this function is invoked once the data has been fully loaded
}, function(error){
//this function is invoked if an error occurs while loading the data (in case of a server error response or if the response isn't in the format you specified)
});
});
This is my proposal:
Your dataHelper.js file:
define("dataHelper", ["dojo/_base/declare", "dojo/dom", "dojo/_base/xhr"],
function(declare, dom, xhr){
return declare("dataHelper", [], {
getJSON: function(){
return xhr.get({
url: "../../cpuusage.json",
handleAs: "json"
});
});
};
});
your invocation:
require(["dataHelper"], function(dataHelper) {
var chartData;
dataHelper.getJSON().then(function(jsonData) {
chartData = jsonData;
//Continue doing stuff with chartData in here, not outside
});
});