I'm quite new to ml5 and p5 libraries and during implementation to my Angular project I'm receiving this error:
TypeError: this.objectDetector.detect is not a function
After logging objectDetector object console shows this:
ZoneAwarePromise {__zone_symbol__state: null, __zone_symbol__value: Array(0)}
p5 drawing working good but combined with ml5 is not working.
Here's my component code:
import { Component, OnInit } from '#angular/core';
import * as p5 from 'p5';
declare let ml5: any;
#Component({
selector: 'app-new-found',
templateUrl: './new-found.component.html',
styleUrls: ['./new-found.component.scss']
})
export class NewFoundComponent implements OnInit {
objectDetector;
img;
constructor(
) { }
ngOnInit(): void {
const sketch = (s) => {
s.preload = () => {
this.objectDetector = ml5.objectDetector('cocossd');
console.log('detector object is loaded', this.objectDetector);
this.img = s.loadImage('https://i.imgur.com/Mzh4cHR.jpg');
}
s.setup = () => {
s.createCanvas(700, 700).parent('test-canvas');
this.objectDetector.detect(this.img, this.gotResult);
s.image(this.img, 0, 0);
};
s.draw = () => {
};
}
let canvas = new p5(sketch);
}
gotResult(error, results) {
if (error) {
console.error(error);
} else {
console.log(results);
//drawResults(results);
}
}
}
ml5 library is imported in <HEAD> of my index.html file.
Does someone know how to get rid of this error?
Thank you.
Finally I figured it out. The ml5.objectDetector('cocossd'); function must be marked as await because it takes quite long time to execute. Below is working code:
import { Component, OnInit } from '#angular/core';
import * as p5 from 'p5';
declare let ml5: any;
#Component({
selector: 'app-new-found',
templateUrl: './new-found.component.html',
styleUrls: ['./new-found.component.scss']
})
export class NewFoundComponent implements OnInit {
objectDetector;
img;
constructor(
) { }
async ngOnInit(): Promise<void> {
this.objectDetector = await ml5.objectDetector('cocossd');
const sketch = (s) => {
s.preload = () => {
console.log(ml5);
console.log('detector object is loaded', this.objectDetector);
this.img = s.loadImage('https://i.imgur.com/Mzh4cHR.jpg');
}
s.setup = () => {
s.createCanvas(700, 700).parent('test-canvas');
this.objectDetector.detect(this.img, this.gotResult);
s.image(this.img, 0, 0);
};
s.draw = () => {
};
}
let canvas = new p5(sketch);
}
gotResult(error, results) {
if (error) {
console.error(error);
} else {
console.log(results);
//drawResults(results);
}
}
}
It is possible that the library has not fully loaded yet. I would create a polling technique here where you keep checking if the value has been initialized and only then proceed.
This is the code I use for polling that xola script has loaded:
let subscription = interval(1000)
.pipe(timeout(3 * 60 * 1000))
.subscribe({
next: () => {
if (this.window.xola) {
const xola = this.window.xola();
subscription.unsubscribe();
this.xolaSubject.next(xola);
}
},
error: (error) => {
if (error instanceof TimeoutError) {
console.error('Xola took too long to load, check your connection.');
}
subscription.unsubscribe();
},
});
Related
I am not getting any clue how to mock a method. I have to write a unit test for this function:
index.ts
export async function getTenantExemptionNotes(platform: string) {
return Promise.all([(await getCosmosDbInstance()).getNotes(platform)])
.then(([notes]) => {
return notes;
})
.catch((error) => {
return Promise.reject(error);
});
}
api/CosmosDBAccess.ts
import { Container, CosmosClient, SqlQuerySpec } from "#azure/cosmos";
import { cosmosdbConfig } from "config/Config";
import { Workload } from "config/PlatformConfig";
import { fetchSecret } from "./FetchSecrets";
export class CosmoDbAccess {
private static instance: CosmoDbAccess;
private container: Container;
private constructor(client: CosmosClient) {
this.container = client
.database(cosmosdbConfig.database)
.container(cosmosdbConfig.container);
}
static async getInstance() {
if (!CosmoDbAccess.instance) {
try {
const connectionString = await fetchSecret(
"CosmosDbConnectionString"
);
const client: CosmosClient = new CosmosClient(connectionString);
// Deleting to avoid error: Refused to set unsafe header "user-agent"
delete client["clientContext"].globalEndpointManager.options
.defaultHeaders["User-Agent"];
CosmoDbAccess.instance = new CosmoDbAccess(client);
return CosmoDbAccess.instance;
} catch (error) {
// todo - send to app insights
}
}
return CosmoDbAccess.instance;
}
public async getAllNotesForLastSixMonths() {
const querySpec: SqlQuerySpec = {
// Getting data from past 6 months
query: `SELECT * FROM c
WHERE (udf.convertToDate(c["Date"]) > DateTimeAdd("MM", -6, GetCurrentDateTime()))
AND c.IsArchived != true
ORDER BY c.Date DESC`,
parameters: [],
};
const query = this.container.items.query(querySpec);
const response = await query.fetchAll();
return response.resources;
}
}
export const getCosmosDbInstance = async () => {
const cosmosdb = await CosmoDbAccess.getInstance();
return cosmosdb;
};
index.test.ts
describe("getExemptionNotes()", () => {
beforeEach(() => {
jest.resetAllMocks();
});
it("makes a network call to getKustoResponse which posts to axios and returns what axios returns", async () => {
const mockNotes = [
{
},
];
const cosmosDBInstance = jest
.spyOn(CosmoDbAccess, "getInstance")
.mockReturnValue(Promise.resolve(CosmoDbAccess.instance));
const kustoResponseSpy = jest
.spyOn(CosmoDbAccess.prototype, "getAllNotesForLastSixMonths")
.mockReturnValue(Promise.resolve([mockNotes]));
const actual = await getExemptionNotes();
expect(kustoResponseSpy).toHaveBeenCalledTimes(1);
expect(actual).toEqual(mockNotes);
});
});
I am not able to get instance of CosmosDB or spyOn just the getAllNotesForLastSixMonths method. Please help me code it or give hints. The complexity is because the class is singleton or the methods are static and private
I am using socket.io in my angular and node application. A user joins the room the user can see his username in the user list. When user2 joins, user1 can see both user1 and user2 in the user list. However, user2 can only see user2. If user 3 joins. user1 can see user 1, user2, and user3. User2 can see user2 and user3. However, user3 only sees user3.
chat.service.ts
import { Injectable } from '#angular/core';
import * as io from 'socket.io-client';
import { Observable, onErrorResumeNext, observable } from 'rxjs';
//import { Observable } from 'rxjs/Observable';
#Injectable()
export class ChatService {
private socket = io('http://localhost:8080');
joinRoom(data) {
this.socket.emit('join', data);
}
newUserJoined() {
let observable = new Observable<{user: String, message:String}>(observer => {
this.socket.on('new user joined ', (data) => {
observer.next(data);
});
return () => {
this.socket.disconnect();
};
});
return observable;
}
leaveRoom(data) {
this.socket.emit('leave', data);
}
userLeftRoom() {
let observable = new Observable<{user: String, message:String}>(observer => {
this.socket.on('left room', (data) => {
observer.next(data);
});
return () => {
this.socket.disconnect();
};
});
return observable;
}
sendMessage(data) {
this.socket.emit('message', data);
}
newMessageRecieved() {
let observable = new Observable<{user: String, message:String, time: any}>(observer => {
this.socket.on('new message', (data) => {
observer.next(data);
});
return () => {
this.socket.disconnect();
};
});
return observable;
}
getRoomUsers() {
let observable = new Observable<{user: String, message:String}>(observer => {
this.socket.on('roomUsers', (data) => {
observer.next(data);
});
return () => {
this.socket.disconnect();
};
});
return observable;
}
}
chat.component.ts
import { Component, OnInit } from '#angular/core';
import { ChatService } from '../../services/chat.service';
import { AuthService } from '../../services/auth.service';
import { Observable } from 'rxjs';
#Component({
selector: 'app-chat',
templateUrl: './chat.component.html',
styleUrls: ['./chat.component.css'],
providers: [ChatService]
})
export class ChatComponent implements OnInit {
room: any;
user: any;
username: any;
roomName: any;
messageArray: Array<{user: String, message: String, time: any}> = [];
userArray: Array<{user: String, message: String}> = [];
messageText: String;
time: any;
constructor( private chatService: ChatService, private authService: AuthService) {
// this.chatService.newUserJoined()
// .subscribe(data => this.userArray.push(data));
// this.chatService.userLeftRoom()
// .subscribe(data => this.userArray.splice(this.userArray.indexOf(data)));
this.chatService.newMessageRecieved()
.subscribe(data => this.messageArray.push(data));
this.chatService.getRoomUsers()
.subscribe(data => this.userArray.push(data));
}
ngOnInit() {
this.getUser();
}
getUser() {
this.user = localStorage.getItem('user');
this.username = JSON.parse(this.user).username;
this.getRoom();
}
getRoom() {
this.room = localStorage.getItem('room');
this.roomName = JSON.parse(this.room).name;
this.join();
}
join() {
console.log(this.roomName);
console.log(this.username);
this.chatService.joinRoom({user: this.username, room: this.roomName});
}
leave() {
console.log(this.roomName);
console.log(this.username);
let userIndex = this.userArray.indexOf(this.username);
delete this.userArray[userIndex];
localStorage.removeItem('room');
this.chatService.leaveRoom({user: this.username, room: this.roomName});
}
sendMessage() {
console.log(this.roomName);
console.log(this.username);
this.chatService.sendMessage({user: this.username, room: this.roomName, message: this.messageText, time: this.time});
this.messageText = '';
}
}
chat.component.html
<ul *ngFor="let item of userArray" id="usersList">
<li >{{item.user}}</li>
</ul>
What comes through the “roomUsers” socket event? All the current users?
To me it looks like that event is only dispatching new arrivals, so every user that shows up only sees themselves and users that come after them.
Without too much knowledge of your code, it seems like one solution is to make the “roomUsers” event contain all users and not just newly added users. Another possibility is to define that Observable outside of the function and then provide the same observable to each newcomer, so everyone has the same data. Switch that function to a stream and expose it something like:
roomUsers$ = from(this.socket.on(“roomUsers”).pipe(
scan((a,c) => [...a, c], []),
shareReplay(1));
The shareReplay(1) operator means even late subscribers get the most recent value, and the scan builds things up as an array. So on your component you wouldn’t do the .push method into the array.
Again, I could be missing context, but hopefully this points you in the right directions.
This is rather a stylistic question. I'm using Pino in some of my Javascript/Typescript microservices. As they're running on AWS I'd like to propagate the RequestId.
When one of my functions is invoked, I'm creating a new child logger like this:
const parentLogger = pino(pinoDefaultConfig)
function createLogger(context) {
return parentLogger.child({
...context,
})
}
function createLoggerForAwsLambda(awsContext) {
const context = {
requestId: awsContext.awsRequestId,
}
return createLogger(context)
}
I'm then passing down the logger instance to all methods. That said, (... , logger) is in almost every method signature which is not too nice. Moreover, I need to provide a logger in my tests.
How do you do it? Is there a better way?
you should implement some sort of Dependency Injection and include your logger there.
if your using microservices and maybe write lambdas in a functional approach, you can handle it by separating the initialization responsibility in a fashion like this:
import { SomeAwsEvent } from 'aws-lambda';
import pino from 'pino';
const createLogger = (event: SomeAwsEvent) => {
return pino().child({
requestId: event.requestContext.requestId
})
}
const SomeUtil = (logger: pinno.Logger) => () => {
logger.info('SomeUtil: said "hi"');
}
const init(event: SomeAwsEvent) => {
const logger = createLogger(event);
someUtil = SomeUtil(logger);
return {
logger,
someUtil
}
}
export const handler = (event: SomeAwsEvent) => {
const { someUtil } = init(event);
someUtil();
...
}
The simplest way is to use some DI library helper to tackle this
import { createContainer } from "iti"
interface Logger {
info: (msg: string) => void
}
class ConsoleLogger implements Logger {
info(msg: string): void {
console.log("[Console]:", msg)
}
}
class PinoLogger implements Logger {
info(msg: string): void {
console.log("[Pino]:", msg)
}
}
interface UserData {
name: string
}
class AuthService {
async getUserData(): Promise<UserData> {
return { name: "Big Lebowski" }
}
}
class User {
constructor(private data: UserData) {}
name = () => this.data.name
}
class PaymentService {
constructor(private readonly logger: Logger, private readonly user: User) {}
sendMoney() {
this.logger.info(`Sending monery to the: ${this.user.name()} `)
return true
}
}
export async function runMyApp() {
const root = createContainer()
.add({
logger: () =>
process.env.NODE_ENV === "production"
? new PinoLogger()
: new ConsoleLogger(),
})
.add({ auth: new AuthService() })
.add((ctx) => ({
user: async () => new User(await ctx.auth.getUserData()),
}))
.add((ctx) => ({
paymentService: async () =>
new PaymentService(ctx.logger, await ctx.user),
}))
const ps = await root.items.paymentService
ps.sendMoney()
}
console.log(" ---- My App START \n\n")
runMyApp().then(() => {
console.log("\n\n ---- My App END")
})
it is easy to write tests too:
import { instance, mock, reset, resetCalls, verify, when } from "ts-mockito"
import { PaymentService } from "./payment-service"
import type { Logger } from "./logger"
const mockedLogger = mock<Logger>()
when(mockedLogger.info).thenReturn(() => null)
describe("Payment service: ", () => {
beforeEach(() => {
resetCalls(mockedLogger)
// reset(mockedLogger)
})
it("should call logger info when sending money", () => {
const paymentService = new PaymentService(instance(mockedLogger))
expect(paymentService.sendMoney()).toBe(true)
})
})
I would not use the requestId as part of the context of the logger, but use it as the payload of the logger, like logger.info({ requestId }, myLogMessage). This was you can have a simple function create a child logger that you can use for the entire module.
I'm trying to use latitude and longitude from the geolocation service in my list service. Unfortunately this keeps returning as undefined. Not really sure what the issue could be.
list.service.ts
import { Injectable } from '#angular/core';
import { Http, Headers } from '#angular/http';
import { Observable, Subject, asapScheduler, pipe, of, from, interval, merge, fromEvent, SubscriptionLike, PartialObserver } from 'rxjs';
import { List } from '../models/list.model';
import { map } from 'rxjs/operators';
import { GeolocationService } from '../services/geolocation.service';
#Injectable()
export class ListService {
constructor(private http: Http, private geoServ: GeolocationService) { }
getLongitude() {
this.geoServ.getLongitude().subscribe((longitude) => {
console.log(longitude)
});
}
private serverApi = 'http://localhost:3000';
public getAllLists(): Observable<List[]> {
const URI = `${this.serverApi}/yelp/${longitude}/${latitude}`;
return this.http.get(URI)
.pipe(map(res => res.json()))
.pipe(map(res => <List[]>res.businesses));
}
}
geolocation.service.ts
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class GeolocationService {
constructor() { }
getGeoLocation() {
console.log('Geolocation working!');
const options = {
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0
};
const success = (pos) => {
const crd = pos.coords;
console.log(`Latitude : ${crd.latitude}`);
console.log(`Longitude : ${crd.longitude}`);
};
const error = (err) => {
console.warn(`ERROR(${err.code}): ${err.message}`);
};
navigator.geolocation.getCurrentPosition(success, error, options);
}
getLongitude() {
console.log('Geolocation working!');
const options = {
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0
};
const success = (pos) => {
const crd = pos.coords;
console.log(`Longitude : ${crd.longitude}`);
const longitude = crd.longitude;
return longitude;
};
const error = (err) => {
console.warn(`ERROR(${err.code}): ${err.message}`);
};
navigator.geolocation.getCurrentPosition(success, error, options);
}
}
thank you for taking the time to look at this. And I appreciate the further help on my additional question below. text text text text won't let me post until I add more details text text text text.
You are not returning anything from your methods. You should use a return statement. You also need to use either a callback function a promise or a observable. I'll give you a observable example:
getLongitude() {
this.geoServ.getLongitude().subscribe((longitude) => {
console.log(longitude)
});
}
And change your getLongitude:
getLongitude() {
return new Observable((observer) => {
console.log('Geolocation working!');
const options = {
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0
};
const success = (pos) => {
const crd = pos.coords;
console.log(`Longitude : ${crd.longitude}`);
const longitude = crd.longitude;
observer.next(longitude);
observer.complete();
};
const error = (err) => {
console.warn(`ERROR(${err.code}): ${err.message}`);
observer.error(err);
observer.complete();
};
navigator.geolocation.getCurrentPosition(success, error, options);
});
}
If you want to get the longitude and latitude in your getAllList method you can use the mergeMap operator:
public getAllLists(): Observable<List[]> {
return this.geoServ.getGeoLocation().pipe(
mergeMap(({ latitude, longitude}) =>
this.http.get(`${this.serverApi}/yelp/${longitude}/${latitude}`)
),
map(res => res.businesses)
);
}
I'm using the HttpClient here. This is the 'new' http module from angular. It's advised to use this one instead of the old one. It also automatically does the json() call for you.
So I tried sending an object through my websocket by translating it to json and then back when it returns. Unfortunately it gives me the below error. The console.log shows me that it is valid JSON, but somehow it gives me an error at JSON.parse in the service document. Can anyone see what I did wrong?
The error
core.js?223c:1440 ERROR SyntaxError: Unexpected token c in JSON at position 0
at JSON.parse (<anonymous>)
at WebSocket._this.ws.onmessage [as __zone_symbol__ON_PROPERTYmessage] (movie-chat.service.ts?6086:22)
at WebSocket.wrapFn (zone.js?fad3:1166)
console.log result of event.data (valid json)
{"message":"good boy","extra":"extra"}
movie-chat.service.ts
import {Injectable} from "#angular/core";
import 'rxjs/rx';
import {HttpClient} from "#angular/common/http";
import {Observable} from "rxjs/Observable";
// We need #injectable if we want to use http
#Injectable()
export class MovieChatService {
ws;
constructor(private http: HttpClient) {
}
// receive events
createObservableSocket(url:string){
this.ws = new WebSocket(url);
return new Observable(observer => {
this.ws.onmessage = (e) => {
console.log(e.data);
var object = JSON.parse(e.data);
observer.next(object);
}
this.ws.onerror = (event) => observer.error(event);
this.ws.onclose = (event) => observer.complete();
}
);
}
// send events
sendMessage(message) {
message = JSON.stringify(message);
console.log(message);
this.ws.send(message);
}
}
Back-end handling of messages
var wss = new Websocket.Server({port:3185});
var CLIENTS = [];
wss.on('connection',
function(websocket) {
CLIENTS.push(websocket);
websocket.send('connected to socket');
websocket.on('message', function (message) {
console.log('Server received:', message);
sendAll(message)
});
websocket.on('close', function(client) {
CLIENTS.splice(CLIENTS.indexOf(client), 1);
});
websocket.on('error', function(client) {
CLIENTS.splice(CLIENTS.indexOf(client), 1);
});
});
movie-chat.component.ts
import {Component, OnInit} from "#angular/core";
import { MovieChatService} from "./movie-chat.service";
#Component({
selector: 'app-movie-chat',
templateUrl: './movie-chat.component.html',
styleUrls: ['./movie-chat.component.css']
})
export class MovieChatComponent implements OnInit{
fullName;
messageFromServer;
title = 'Websocket Demo';
url;
ws;
messages = [];
constructor(private movieChatService: MovieChatService){
}
ngOnInit(){
this.fullName = localStorage.getItem('fullName');
this.url = 'ws://localhost:3185';
this.movieChatService.createObservableSocket(this.url)
.subscribe(data => {
this.messageFromServer = data;
},
err => console.log(err),
() => console.log('The observable stream, is complete'));
}
sendMessageToServer(){
console.log('Client sending message to websocket server');
this.movieChatService.sendMessage({
message: 'good boy',
extra: 'extra'
});
}
}
It seems that you are trying to parse a Json Object,
{"message":"good boy","extra":"extra"}
JSON.parse expect string parameter and you are passing an Json Object for that the exception is rised.
We try to surround the Parse with try and catch
import {Injectable} from "#angular/core";
import 'rxjs/rx';
import {HttpClient} from "#angular/common/http";
import {Observable} from "rxjs/Observable";
// We need #injectable if we want to use http
#Injectable()
export class MovieChatService {
ws;
constructor(private http: HttpClient) {
}
// receive events
createObservableSocket(url:string){
this.ws = new WebSocket(url);
return new Observable(observer => {
this.ws.onmessage = (e) => {
console.log(e.data);
try {
var object = JSON.parse(e.data);
observer.next(object);
} catch (e) {
console.log("Cannot parse data : " + e);
}
}
this.ws.onerror = (event) => observer.error(event);
this.ws.onclose = (event) => observer.complete();
}
);
}
// send events
sendMessage(message) {
message = JSON.stringify(message);
console.log(message);
this.ws.send(message);
}
}
So, I didn't actually solve it the way I wanted it, but I found out that it is possible to send arrays through a websocket. I did this and on the receiving end I transferred it into an object in the service file. It does the job for now. If anyone knows a better solution, let me know :)
createObservableSocket(url:string){
this.ws = new WebSocket(url);
return new Observable(observer => {
this.ws.onmessage = (e) => {
var obj = e.data.split(',');
console.log(obj);
obj = {
name: obj[0],
msg: obj[1]
};
console.log(obj);
observer.next(obj);
}
this.ws.onerror = (event) => observer.error(event);
this.ws.onclose = (event) => observer.complete();
}
);
}