How to implement rabbit mq or bull js in loopback 4 - loopbackjs

I am trying to implement a background service so that there is less load on the API call. Background task will run to upload files to S3 and send email using nodemailer.

Following the loopback 4's design patterns you can create a service provider and inject it in your service or controller.
Here is a very basic example with Bull.js:
import Bull, { Queue, Job } from "bull";
import {Provider, service} from "#loopback/core";
import {get} from "#loopback/rest";
export function audioProcessor(job: Job) {
console.log(
`Processing audio file: ${job.data.filename}`,
`Audio bitrate: ${job.data.bitrate}`
);
}
export class AudioQueueProvider implements Provider<Queue> {
async value() {
const queue = new Bull("AudioQueue", {
redis: { port: 6379, host: "127.0.0.1" }
});
queue.process(audioProcessor);
return queue;
}
}
export class AudioController {
constructor(
#service(AudioQueueProvider) public queue: Queue;
) {}
#get('/process-audio')
async addToQueue(): Promise<string> {
await this.queue.add(
{
filename: 'process_me.wav',
bitrate: 320,
}
);
return 'Audio file added to the AudioQueue for processing';
}
}
The RabbitMQ implementation should be similar (not tested):
import { Provider, service } from "#loopback/core";
import {get} from "#loopback/rest";
import amqp from "amqplib/callback_api";
export function audioProcessor(msg: any) {
console.log(
`Processing audio file: ${msg.content.filename}`,
`Audio bitrate: ${msg.content.bitrate}`
);
}
export class AudioQueueProvider implements Provider<any> {
async value() {
const CONN_URL = "amqp://localhost";
let ch = null;
const channelName = 'AudioQueue';
amqp.connect(CONN_URL, function (err, conn) {
conn.createChannel(function (err, channel) {
ch = channel;
});
});
ch.assertQueue(channelName, {
durable: false
});
ch.consume(channelName, audioProcessor, {
noAck: true
});
return ch;
}
}
export class AudioController {
constructor(
#service(AudioQueueProvider) public channel: any;
) {}
#get('/process-audio')
async addToQueue(): Promise<string> {
await this.channel.sendToQueue(
'AudioQueue',
{
filename: 'process_me.wav',
bitrate: 320,
}
);
return 'Audio file added to the AudioQueue for processing';
}
}

Related

401 "Unauthorized" error in Django and Angular File Upload

I have created a Django and Angular application to upload files. It was working without errors until I integrated a login page. I have not been able to upload files since integration. I get 401 - "Unauthorized" error. What could have possibly gone wrong?
Auth-interceptor:
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest,HttpErrorResponse } from "#angular/common/http";
import { Injectable } from "#angular/core";
import { catchError, Observable, throwError } from "rxjs";
import { LoginService } from "src/services/login.service";
#Injectable()
export class AuthInterceptorService implements HttpInterceptor {
constructor(private authService: LoginService) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (this.authService.isLoggedIn()) {
const token = this.authService.getAuthToken();
console.log("intercept",token)
// If we have a token, we set it to the header
request = request.clone({
setHeaders: {Authorization: `Token ${token}`}
});
}
return next.handle(request)
}
}
fileupload.component.ts:
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup } from '#angular/forms';
import { LoginService } from 'src/services/login.service';
import { FileUploader, FileLikeObject } from 'ng2-file-upload';
import { concat, Observable } from 'rxjs';
import { HttpEvent, HttpEventType } from '#angular/common/http';
#Component({
selector: 'app-fileupload',
templateUrl: './fileupload.component.html',
styleUrls: ['./fileupload.component.scss']
})
export class FileuploadComponent {
DJANGO_SERVER = 'http://127.0.0.1:8081'
public uploader: FileUploader = new FileUploader({});
public hasBaseDropZoneOver: boolean = false;
constructor(private formBuilder: FormBuilder, private uploadService: LoginService) { }
fileOverBase(event): void {
this.hasBaseDropZoneOver = event;
}
getFiles(): FileLikeObject[] {
return this.uploader.queue.map((fileItem) => {
return fileItem.file;
});
}
upload() {
let files = this.getFiles();
console.log(files);
let requests= [];
files.forEach((file) => {
let formData = new FormData();
formData.append('file' , file.rawFile, file.name);
requests.push(this.uploadService.upload(formData));
console.log(requests,file)
});
concat(...requests).subscribe(
(res) => {
console.log(res);
},
}
);
}}
console.log(err);
}
);
}}
service:
public upload(formData) {
let token= localStorage.getItem('token');
return this.http.post<any>(`${this.DJANGO_SERVER}/upload/`, formData).pipe(map((res) => {
console.log(res)
})
)
}
Thank you
I resolved the issue. It was because I was usign interceptor and I was using third party API for authentication. So instead of Django token, the third party APIs token was sent in header of POST request.
How I resolved it?
I used Httpbackend to process POST requests to Django DB so that the request is not intercepted and then I added custom header (with Django token to the reuest). I used the code snippet on this website: https://levelup.gitconnected.com/the-correct-way-to-make-api-requests-in-an-angular-application-22a079fe8413

Angular app keeps saying message does not exist

I'm building a chat app with Angular and Django using the get stream tutorial. https://getstream.io/blog/realtime-chat-django-angular/
However, I'm trying to run the app to create the chat view but it keeps saying 'messages' does not exist at the point marked 'this point' in the code.
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { MessageResponse, Channel } from 'stream-chat';
import { StreamService } from '../stream.service';
import { StateService } from '../state.service';
declare const feather: any;
#Component({
selector: 'app-chat',
templateUrl: './chat.component.html',
styleUrls: ['./chat.component.scss'],
})
export class ChatComponent implements OnInit {
constructor(
public streamService: StreamService,
private stateService: StateService,
private router: Router
) {}
messages: MessageResponse[] = [];
message = '';
channel!: Channel;
async sendMessage() {
if (this.message) {
try {
await this.channel.sendMessage({
text: this.message,
});
this.message = '';
} catch (err) {
console.log(err);
}
}
}
getClasses(userId: string): { outgoing: boolean; incoming: boolean } {
const userIdMatches = userId === this.streamService.currentUser.messages.id; (this point)
return {
outgoing: userIdMatches,
incoming: !userIdMatches,
};
}
}

can't run apollo-server-express with merged typeDefs and resolvers using #graphql-tools/merge

hey friends this is the structure of my project and files down below:
starting from app.js file:
import { Server } from "./src/Server";
import { config } from "dotenv";
config();
Server.StartServer();
Down below is the Server.ts file that is bootstrapping of the apollo-server-express
import express from "express";
import http from "http";
import { ApolloServer } from "apollo-server-express";
import { GraphQLServerOptions } from "apollo-server-core/src/graphqlOptions";
import { schema } from "./graphql/index";
export class Server {
public static StartServer() {
const app: express.Application = express();
const server = new ApolloServer({
schema,
graphiql: true,
} as unknown as GraphQLServerOptions);
server.applyMiddleware({ app });
const httpServer = http.createServer(app);
server.installSubscriptionHandlers(httpServer);
httpServer.listen(process.env.PORT, function () {
console.log(`server is running on port ${process.env.PORT}`);
});
}
}
this is user.resolvers.ts file that my resolver goes here:
import { IResolvers } from "graphql-tools";
export const resolver: IResolvers = {
Mutation: {
register: function (parent, args, content, info) {
console.log(parent);
},
},
Query: {
getUser: function (parent, args, content, info) {
console.log(parent);
},
},
};
And here we go with the typeDef for in user.schema.ts file:
import { gql } from "apollo-server-express";
export const typeDef = gql`
type User {
username: String
password: String
}
type Mutation {
register(input: {username: String!, passwprd: String!})
}
type Query {
getUSer(username): User
}
`;
And finally over there in ./src/graphql/index.ts file I'm doing the mergig for resolvers and typeDefs there and I'm making the executableSchema for adding it to ApolloServer config object but I face the error below:
Any Ideas and suggestions would be greatly appreciated. Early thanks for the contributors :)

Testing AWS Severless application using Jest

I have a basic serverless application below, I want to test using Jest if getUserById method is called. I am also using inversifyjs. Now when I run my test I am getting an error TypeError: Reflect.hasOwnMetadata is not a function. Another thing how can I mock a response here?
handler.spec.ts
import { IUsersHandler } from './../src/IUsersHandler';
import { UsersHandler } from './../src/UsersHandler';
import { APIGatewayProxyResult } from 'aws-lambda';
let handler: IUsersHandler;
let mockResponse: APIGatewayProxyResult;
describe("UsersHandler", () => {
beforeEach(() => {
handler = new UsersHandler();
});
it("should call getUserById method", () => {
const spy = jest.spyOn(handler, 'getUserById').mockImplementation(async () => mockResponse);
expect(spy).toBeCalledTimes(1);
});
});
UsersHandler Class
import { IUsersHandler } from './IUsersHandler';
import { injectable } from "inversify";
import { APIGatewayProxyHandler, APIGatewayProxyResult, APIGatewayProxyEvent } from "aws-lambda";
#injectable()
export class UsersHandler implements IUsersHandler {
constructor() { }
public getUserById: APIGatewayProxyHandler = async (
event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> => {
try {
return {
statusCode: 200,
body: JSON.stringify(event)
};
} catch (err) {
return {
statusCode: 500,
body: JSON.stringify(err)
};
}
};
}
User Interface
import { APIGatewayProxyHandler } from 'aws-lambda';
export interface IUsersHandler {
getUserById: APIGatewayProxyHandler;
}
export const TUsersHandler = Symbol.for('IUsersHandler');
Handler.ts
import { IUsersHandler, TUsersHandler } from './src/IUsersHandler';
import { container } from "./src/inversify.config";
import 'source-map-support/register';
export const getUserById = async function (event, context, callback) {
const handler: IUsersHandler = container.get<IUsersHandler>(TUsersHandler);
return handler.getUserById(event, context, callback);
};
Final handler.spec.ts
import "reflect-metadata";
import { IUsersHandler } from "./../src/IUsersHandler";
import { UsersHandler } from "./../src/UsersHandler";
import { APIGatewayProxyEvent, APIGatewayProxyResult } from "aws-lambda";
let handler: IUsersHandler;
let mockEvent: APIGatewayProxyEvent;
let mockResponse: APIGatewayProxyResult;
describe("UsersHandler", () => {
beforeEach(() => {
mockResponse = {
statusCode: 200,
body: "This is a test",
};
handler = new UsersHandler();
});
it("should call getUserById method", async () => {
const spy = jest
.spyOn(handler, "getUserById")
.mockImplementation(async () => mockResponse);
const response: any = await handler.getUserById(mockEvent, null, null);
expect(spy).toBeCalledTimes(1);
expect(response.body).toBe("This is a test");
expect(response.statusCode).toBe(200);
});
});

Angular 2: BehaviorSubject/Subject does not work outside constructor

Hi I need pass the data from one component to another, for this I am using the class BehavorSubject(I tried too with Subject class, but doesnt works for me). This is my code:
Home page has a filter, and when is selected a filter, it called the service and it service should change a variable of homePage
HomePage.ts
#Component({
providers: [PatientService],
})
export class HomePage {
subscription: Subscription;
constructor( public auth: AuthService,
public patientService: PatientService) {
this.subscription = this.patientService.nameGiven.subscribe(
nameGiven => {
this.patientsByElement = nameGiven.toString();
});
------ more code---
}
Filtro.ts
export class FiltroPage {
showFilter(filter : FiltroT): void{
... code ...
clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
this.PatientService.getPatientsByTags(this.token,this.filterSelected);
} , 1000);
}
}
patient-service.ts
import { Subject } from 'rxjs/Subject';
import { Observable ,BehaviorSubject } from 'rxjs/Rx';
#Injectable()
export class PatientService {
nameSource = new BehaviorSubject("asd");
nameGiven = this.nameSource.asObservable();
this.nameSource.next('hi!!!'); //**it works but only in the constructor**
this.nameGiven.subscribe(()=>{
console.log("it changed");
});
getPatientsByTags(token: String, tags: Array<string>){
return new Promise(resolve => {
this.http.get(ConnectionParams.DevEnv + ProceduresNames.TagsByPatient + ProceduresNames.TagIdEtiqueta + tags, options)
.map(res => res.json())
.subscribe(data => {
if(data.data.poAnicuRegistros){
console.log("here")
this.nameSource.next('hi TON :/'); // <-- ***here is the problem. It doesnt work***
}
else
console.log("XX");
resolve( this.data);
});
});
}
}
Finally i didn't use the BehaviorSubject/Subject, i pass the data from the filter to Homepage of this way :
HomePage.ts
public popoverCtrl: PopoverController
//code ...
showFilter(myEvent) {
let popover = this.popoverCtrl.create(FiltroPage, {
showConfirm: (x) => {
//do something with the data received from the filter
}
});
popover.present({
ev: myEvent
});
}
Filter.ts
//code...
params: NavParams;
showConfirm() {// function that return the data to homePage
this.params.get('showConfirm')(this.patientsBytag);
}