I am working in an application : Java Backend and Angular frontend. I am using angular Fromly, data is coming to service, but from the service it is not going to server.
lets share the code snipts:
Service Code:
export class RecommendationRequestService {
readonly ROOT_URL = environment.apiUrl + '/am/v1/recommendation-requests';
constructor(private http: HttpClient, private configService: RecommenderConfigService) {
}
updateData(interviewStatus: InterviewStatusRecommendation): Observable<any> {
console.log(interviewStatus);
return this.http.put<any>(this.ROOT_URL, interviewStatus);
}
}
This line is printing intended data set : console.log(interviewStatus);
The server is running.
The code from where the service is being called :
onSubmit() {
this.model.recommendationRequest.agentInitiationId = this.agentInitiationId;
const subs = this.service.updateData(this.model).subscribe(response => {
console.log('------' + response);
if (response === 'OK') {
this.notify.success('Request Recommendation Update success.');
} else {
this.notify.error('Request Recommendation Update fail.');
}
},
err => {
if (err.error.hasOwnProperty('code') && err.error.code === 1000) {
this.notify.error(CommonEnum.VALIDATION_ERROR);
}
});
subs.unsubscribe();
}
console.log('------' + response); this line should print at least -----, But nothing.
I have checked the network monitor from the browser, no call is going.
What might be the possible issue, any thing from fromly?
You are doing it incorrect as Aldin Bradaric also updated in the comment, as soon as you make the call on the very next moment you are unsubscribing it. This is what you should do :
public subs: [] = [];
onSubmit() {
this.model.recommendationRequest.agentInitiationId = this.agentInitiationId;
const subs = this.service.updateData(this.model).subscribe(response => {
console.log('------' + response);
if (response === 'OK') {
this.notify.success('Request Recommendation Update success.');
} else {
this.notify.error('Request Recommendation Update fail.');
}
},
err => {
if (err.error.hasOwnProperty('code') && err.error.code === 1000) {
this.notify.error(CommonEnum.VALIDATION_ERROR);
}
});
//subs.unsubscribe(); // remove it and add it to the lifecycle hooks
this.subs.push(subs);
}
ngOnDestroy() {
// create an array of subscription
this.subs.forEach(sub => sub.unsubscribe() )
}
Related
I am looking to queue requests to ExpressJs so that only one request is processed on an endpoint at a time. I have found some examples of this: ExpressJS backend put requests into a queue
It seems though they require a separate function for each endpoint. I am trying to create one function that allows me to pass a queue name, and then stack items in the specified queues.
It is an API listening for requests, and upon receiving one will then execute a request to another online API before relaying the result back to the user through the original express endpoint. Ultimately I will then look to add some very basic caching for each endpoint, just to store a short JSON string for 3 seconds before expiring. That way it returns the cached string within the 3 second limit rather than fetch the data again from online.
Here is as far as I got, I would be curious to hear if there are better ways:
//UI request -> check cache -> return response || call request then return response
// Queue items on endpoint
class QueueUnique {
func;
q;
requestCache = [];
constructor(func) {
this.q = Promise.resolve();
this.func = func;
}
add(request) {
// Fetch all cached items related to the current endpoint queue
const cachedItem = this.requestCache.find(
(itm) => itm.queueName === request.queueName
);
// If the current request is within X seconds of the last successful requesst, return the cache
// otherwise make a new request
if (cachedItem && new Date().getTime() - cachedItem.runtime > 3000) {
console.log(
"Cache is over 3 seconds old. Doing new request. Queue name: " +
request.queueName
// no cahe, forward request:
//seperate this in to function
//res.sendResponse = res.send
// res.send = (body) => {
// request.body = body
// this.updateCache(request);
// res.sendResponse(body)
//}
//next()
);
this.updateCache(request);
} else if (cachedItem) {
console.log("Valid cache, return cache");
// res.send(request.body)
this.updateCache(request);
} else {
console.log("no cache");
//continue as normal as if no cache
// no cahe, forward request: Same as first run
this.addToCache(request);
}
// Do I need to use await before setting datetime?
//then cache
// Set the current time as a value in the item Array
request.runtime = new Date().getTime();
const queuedFunc = this.queue(request);
queuedFunc();
}
addToCache(request) {
// Add the new item to the permanent cache
this.requestCache.push(request);
}
updateCache(request) {
// Update the permanent request cache entry
const arrayIndex = this.requestCache.findIndex(
(itm) => itm.queueName === request.queueName
);
this.requestCache[arrayIndex] = request;
}
queue(item) {
return () => {
this.q = this.q
.then(() => this.func(item))
.catch((err) => {
console.log(err);
});
return this.q;
};
}
}
const response = (item) => {
return new Promise((resolve) => {
setTimeout(() => {
console.log("say", item.payload);
resolve();
}, item.delay);
});
};
const queue = [];
function test(bar, payload) {
if (!queue[bar]) {
queue[bar] = new QueueUnique(response);
}
queue[bar].add(payload);
console.log(queue);
return queue;
}
test("te", {
queueName: "ping",
payload: "one",
delay: 3000,
});
test("te", {
queueName: "ping",
payload: "one",
delay: 3000,
});
test("te", {
queueName: "ping",
payload: "one",
delay: 3000,
});
test("te2", {
queueName: "ping",
payload: "two",
delay: 1000,
});
test("te2", {
queueName: "ping",
payload: "two",
delay: 1000,
});
Here is what I have for now. Would be great to hear about improvements or issues. It is converted to Typescript as it is what I ultimately will be using.
import queueCache from './middleware/queueCache'
...
app.locals.cacheTimeout = 3000
app.get('/test', queueCache, (_req, res) => {
res.json({ test: 'test' })
})
app.get('/test1', queueCache, (_req, res) => {
res.json({ test1: 'test1' })
})
queueCache.ts:
import { Request, Response, NextFunction } from 'express'
interface requestData {
queueName?: string
cachedData?: string
runtime?: number
}
const cachedItemList = {} as Array<requestData>
const queue = [] as Array<QueueUnique>
const requestCache = [] as Array<requestData>
// Class to queue items and cache results
class QueueUnique {
func
q: Promise<unknown>
constructor(
func: (req: Request, res: Response, next: NextFunction) => Promise<unknown>
) {
this.func = func
this.q = Promise.resolve()
}
add(req: Request, res: Response, next: NextFunction) {
// Check if the item is already cached
if (checkCache(req, res)) {
return
}
// If not cached, add to queue
const queuedFunc = this.queue(req, res, next)
queuedFunc()
}
queue(req: Request, res: Response, next: NextFunction) {
return () => {
this.q = this.q
.then(() => this.func(req, res, next))
.catch((err) => {
console.log(err)
})
return this.q
}
}
}
const response = (req: Request, res: Response, next: NextFunction) => {
// Do another check to see if item just finished in queue created a useful cache
return new Promise((resolve) => {
if (checkCache(req, res)) {
resolve(true)
return
}
setTimeout(() => {
if (cachedItemList[0].queueName) {
// Got this far, and cache exists so it must be older than set time, starting new request.
// Return response to user
res.sendResponse = res.json
res.json = (body) => {
res.sendResponse(body)
// Find the current item in the request cache
const arrayIndex = requestCache?.findIndex(
(itm) => itm.queueName === req.url
)
// Set the time that the request was stored
requestCache[arrayIndex].runtime = new Date().getTime()
// Store the body of the response in the cache
requestCache[arrayIndex].cachedData = body
return res
}
} else {
// There was no cache
// Return the response to the caller
res.sendResponse = res.json
res.json = (body) => {
res.sendResponse(body)
// Only use cache on GET requests. When not GET, this middleware only acts as a queue.
if (req.method === 'GET') {
// Check if it is already in cache to avoid duplicates
// Overcomes an error: https://github.com/expressjs/express/issues/4826
const arrayIndex = requestCache?.findIndex(
(itm) => itm.queueName === req.url
)
if (arrayIndex === -1) {
requestCache.push({
cachedData: body, // Add the new item to the permanent cache
queueName: req.url, // Add the request URL to the item for later reference
runtime: new Date().getTime() // Add the time the request was made
})
}
}
return res
}
}
next()
resolve(true)
}, 4000)
})
}
function checkCache(req: Request, res: Response) {
// Fetch all cached items related to the current endpoint queue, which is named after the endpoint url
cachedItemList[0] =
requestCache.find((itm) => itm.queueName === req.url) || {}
// If the current request is within X seconds of the last successful requesst, return the cached version
if (
cachedItemList[0].runtime &&
new Date().getTime() - cachedItemList[0].runtime <
req.app.locals.cacheTimeout
) {
// Return the cached item to the user
res.json(cachedItemList[0].cachedData)
return true
} else {
return false
}
}
// Create multipule queues, one for each endpoint
function sortQueues(req: Request, res: Response, next: NextFunction) {
// Use the endpoint name to create a queue within the queues array
if (!queue[req.route.path]) {
queue[req.route.path] = new QueueUnique(response)
}
queue[req.route.path].add(req, res, next)
}
export default sortQueues
I want to send broadcast messages from server (using laravel) to clients (using java).
What I'm using
Pusher as boradcast driver.
laravel passport for api authentication.
What I've done in server side
I've configured my Pusher credentials in .env file.
Uncommented App\Providers\BroadcastServiceProvider::class line in config/app.php file.
In config/auth.php file I've added the following:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'devices' => [
'driver' => 'session',
'provider' => 'devices',
],
'api' => [
'driver' => 'passport',
'provider' => 'devices',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
// using devices table to authenticate over api guard
'devices' => [
'driver' => 'eloquent',
'model' => App\Device::class,
],
],
In App\Providers\BroadcastServiceProvider class I added the following to boot() function:
Broadcast::routes(['prefix' => 'api', 'middleware' => 'auth:api']);
In routes/channels.php I added the following:
Broadcast::channel('device.{device_id}', function ($device, $device_id) {
return $device->id === $device_id;
});
Created an event AdvertisementAdded by running php artisan make:event AdvertisementAdded, added implements ShouldBroadcast then added the following to its broadcastOn() method:
return new PrivateChannel('device.'.$this->device_id);
What I've done in client side
Because I'm just testing now, I got my access_token and device_id by sending a login request from postman
I copied that accessToken to my java client and stored it in accessToken variable as String, here's the code:
String accessToken = "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImY3ZTVlMTAzZWE3MzJjMTI5NzY1YTliMmMzOTM0N2ZhOGE4OTU5MjRjNDA5ZjgyOTA4ZDg5NTFjZTBkOGZlNTA2M2M1YTI1MDBlOTdhZDdiIn0.eyJhdWQiOiIxIiwianRpIjoiZjdlNWUxMDNlYTczMmMxMjk3NjVhOWIyYzM5MzQ3ZmE4YTg5NTkyNGM0MDlmODI5MDhkODk1MWNlMGQ4ZmU1MDYzYzVhMjUwMGU5N2FkN2IiLCJpYXQiOjE1NTkwOTYyNDgsIm5iZiI6MTU1OTA5NjI0OCwiZXhwIjoxNTkwNzE4NjQ3LCJzdWIiOiI3Iiwic2NvcGVzIjpbXX0.FKeE9Z-wv2yUNQPl-qsbu9baYGTdbQ6DuzaI1R8azR6l1CIP9uRI4hCaoWvgx0GXWWLPRNhfQl-YD3KP2YOraW16-h4ie_95B9VQrpFxXnlqKojsfh1xSrSNSl5HncslMWQPVjoesBpM5y_cpG19PGgu-SWo0W6s9Fiz_Nm70oyyZB9mSqU8PVQvAOSNr6TMR0aC3iMLFfkyZkTSwj8EoRyD2LGW6v4PFriqx8JLbZASCOiUYBlYnunWrTFDOAenZcoa5Sw7u7kbSvYehjDKRwKjQM6zmPfi0A3Mp0CHjHE599OXb-NG2IMH-wmlT0vEZjP2U97hxmsNW1RtHNXWaRKFL9T-WVmZbJf3fH5hXqTv495L3MQfq_m5YFHyc5NuIqK4K4xMJB956a33ICnH8DmvPmJgderNAhqEX1JHUAsR63K7xbZxRBDS8OlQYcEf-_v75X0kT1s067enSvI8Vs212AVnI6k0FmgQNM8DfJUq6YduD0m2F2ZWpKPrwdd6PdW5ZlZTEv-D8dYIEQ_CwOWohNoENATmTqxDpPBxK5c723MEt8S7Sa9MEGAo56HW3-9pbazbEdY1GqPWKVkov7K_6eBFcWsV67AgJpoKFt6RiBfRvokgiH96WG89qBB_Ucpm8uBahX93FaOXhVLW0VjJH2LQKrGw0bb5LS8Ql5o";
String deviceId = "7";
Map<String, String> authHeaders = new HashMap();
authHeaders.put("Authorization", accessToken);
HttpAuthorizer authorizer = new HttpAuthorizer("http://localhost:8000/api/broadcasting/auth");
authorizer.setHeaders(authHeaders);
PusherOptions options = new PusherOptions();
options.setAuthorizer(authorizer).setCluster(PUSHER_CLUSTER);
Pusher pusher = new Pusher(PUSHER_APP_KEY, options);
pusher.subscribePrivate("private-device." + deviceId, new PrivateChannelEventListener() {
#Override
public void onEvent(String channelName, String eventName, final String data) {
System.out.println(String.format("Received event on channel [%s]", channelName));
}
#Override
public void onSubscriptionSucceeded(String string) {
System.out.println(String.format("Subscribed to channel [%s]", string));
}
#Override
public void onAuthenticationFailure(String string, Exception excptn) {
System.out.println(string);
}
});
pusher.connect(new ConnectionEventListener() {
#Override
public void onConnectionStateChange(ConnectionStateChange change) {
System.out.println("State changed to " + change.getCurrentState() +
" from " + change.getPreviousState());
}
#Override
public void onError(String message, String code, Exception e) {
System.out.println("There was a problem connecting!");
}
});
// Keeping main thread alive
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
When running the code above, it outputs the following on console:
State changed to CONNECTING from DISCONNECTED
State changed to CONNECTED from CONNECTING
java.io.IOException: Server returned HTTP response code: 403 for URL: http://localhost:8000/api/broadcasting/auth
I'm sure that auth:api middleware is working as I expect on other requests.
Here's a snippet from my routes/api.php:
Route::middleware('auth:api')->group(function () {
Route::prefix('advertisements')->group(function () {
Route::get('/request', 'AdvertisementsController#getDeviceAdvertisements')
->name('advertisements.getDeviceAdvertisements');
});
});
And here's a test to that route from postman (with the same access token as above):
And here's a test to api/broadcasting/auth route from postman (with the same access token as above):
What's the problem? Why all api routes under auth:api middleware working properly but not api/broadcasting/auth route??
Note
I tried working with public channels with no problems.
After a whole day of searching, finally It's solved.
The error happens when authorizing the channel, not when authenticating the request using auth:api middleware.
My private channel authorizing function in routes/channels.php always returns false meaning it will reject all subscribing requests to private-device.{device_id} channel:
Broadcast::channel('device.{device_id}', function ($device, $device_id) {
// this always return false, because of inequality of types
return $device->id === $device_id;
});
Authorizing function above always return false, because of inequality of types between $device->id (which is of type int) and $device_id (which is of type string).
So, in order to solve the problem, I cast both of them to int and then checked for equality.
Here's the code I used to solve the problem:
Broadcast::channel('device.{device_id}', function ($device, $device_id) {
return (int) $device->id === (int) $device_id;
});
I have created a simple app using REST in java which return string value successfully with REST client. Now I want to get string value using Http rest client in Angular2. I have created service for getting data from rest client in angular2 which saying successfully access rest but when I am printing data like {{serverData}} it's print nothing.
service.ts
#Injectable()
export class HttpSiftgridService {
private url:string = "http://localhost:8080/app-rest/rest /get/getData";
constructor(private _http: Http) {}
getSiftgridData() {
alert(this._http.get(this.url).map(res => res.json));
alert("hh");
return this._http.get(this.url).map(res => res.json);
}
private handleError(error : Response) {
console.error(error);
return Observable.throw(error.json().error || ' error');
}
}
app.component.ts
export class AppComponent implements OnInit{
serverData: string;
constructor(private _httpService:HttpSiftgridService) {}
ngOnInit() {
this._httpService.getSiftgridData()
.subscribe(
data => this.serverData = JSON.stringify(data),
error => console.log("Error in getting Data"),
() => console.log("Successfully")
);
}
}
my rest app running on tomcat.
Change:
return this._http.get(this.url).map(res => res.json);
To:
return this._http.get(this.url).map(res => res.json());
I know it's not a real answer but that wouldn't work as comment:
If you change it to
ngOnInit() {
this._httpService.getSiftgridData()
.subscribe(
data => {
this.serverData = JSON.stringify(data);
console.log(data);
},
error => console.log("Error in getting Data"),
() => console.log("Successfully")
);
}
is the data printed?
Is Successfully printed?
I have an angular webapp.
It has one service that will query a simple node api on the same server.
That api takes the name of an environment variable as parameter and return the path to the java api that matches that environment variable.
servicesModule.factory('EnvVarApi', function($http){
var url = 'http://localhost:8080/api/variable_env/';
return {
getUrlApi : function(var_env, callbackSuccess) {
$http.get(url+ var_env).success(callbackSuccess);
}
}
});
This works fine. It returns an object of the form:
{"cle" : API_KEY, "valeur" : API_PATH}
Now i want to create another service, that will query that java api (I will have 3 of them at the end)..
How would i go about retrieving the path to that API only ONCE? And once it loaded return the correct resource. Atm it's breaking because the resource returned has a null path before the callback.
servicesModule.factory('ApiRef', function($resource, EnvVarApi){
var url = null;
EnvVarApi.getUrlApi('APIREF_KEY', function(data) {
if (data) {
url = data.valeur;
}
});
return $resource(url, {'cle' : '#cle'}, {
put : {method : 'PUT'}
});
});
The first call is made fine and return the path, however as expected it breaks because i'm return a $resource with a null url at first.
And there is maybe a better way to do that than to inject my first service in my second service.
EDIT : FIXED thanks to answer marked bellow.
Here the final code:
servicesModule.factory('EnvVarApi', function($http){
var url = 'http://localhost:8080/api/variable_env/';
return {
getUrlApi : function(var_env) {
return $http.get(url+ var_env);
}
}
});
And
servicesModule.factory('ApiRef',['$q','$http', 'EnvVarApi',function ($q,$http, EnvVarApi) {
var defer = $q.defer();
EnvVarApi.getUrlApi('APIREF_KEY').then(function (resp) {
defer.resolve($http.get(resp.data.valeur));
});
return defer.promise;
}]);
You can use $q service, check code below
app.factory('call2',['$q','$http',function ($q,$http) {
var url = 'whatever url';
var defer = $q.defer();
$http.get(url).then(function (resp) {
defer.resolve($http.get(resp.data));
//resp.data contains the returned by $http request URL object
});
return defer.promise;
}]);
At Controller o Directive side (remember to inject service into controller)
call2.then(function (resp) {
$scope.response = resp.data;
//resp.data will contain the response of '$http.get(resp.data)'
})
you can use $resource instead of $http.get, see below
app.factory('call2', ['$q', '$resource', function ($q, $resource) {
var url = 'whatever url';
var defer = $q.defer();
$resource(url).get(function (resp) {
defer.resolve($resource(resp.url).get().$promise);
});
return defer.promise;
}]);
$http.get returns a promise, the simple thing to do is to resolve the promise in your controller... not in a factory. Meaning no .success() in your factory... only in the controller
If you need this in the service... it would be like so:
servicesModule.factory('ApiRef', function($resource, EnvVarApi){
var url = null;
// this is ASYNC, meaning it will got to the return statement below and wait until the promise is resolved to assign the url
EnvVarApi.getUrlApi('APIREF_KEY', function(data) {
if (data) {
url = data.valeur;
$resource(url, {'cle' : '#cle'}, { // url defined here, gets put correctly
put : {method : 'PUT'}
});
}
});
return $resource(url, {'cle' : '#cle'}, {
put : {method : 'PUT'}
});
});
I am trying to convert json object to xml document by using the following java script code:
function createXML(oObjTree) {
function loadObjTree(oParentEl, oParentObj) {
var vValue,
oChild;
if (oParentObj instanceof String || oParentObj instanceof Number || oParentObj instanceof Boolean) {
oParentEl.appendChild(oNewDoc.createTextNode(oParentObj.toString()));
} else if (oParentObj.constructor === Date) {
oParentEl.appendChild(oNewDoc.createTextNode(oParentObj.toGMTString()));
}
for (var sName in oParentObj) {
if (isFinite(sName)) {
continue;
}
vValue = oParentObj[sName];
if (sName === "keyValue") {
if (vValue !== null && vValue !== true) {
oParentEl.appendChild(oNewDoc.createTextNode(vValue.constructor === Date ? vValue.toGMTString() : String(vValue)));
}
} else if (sName === "keyAttributes") {
for (var sAttrib in vValue) {
oParentEl.setAttribute(sAttrib, vValue[sAttrib]);
}
} else if (sName.charAt(0) === "#") {
oParentEl.setAttribute(sName.slice(1), vValue);
} else if (vValue.constructor === Array) {
for (var nItem = 0; nItem < vValue.length; nItem++) {
oChild = oNewDoc.createElement(sName);
loadObjTree(oChild, vValue[nItem]);
oParentEl.appendChild(oChild);
}
} else {
oChild = oNewDoc.createElement(sName);
if (vValue instanceof Object) {
loadObjTree(oChild, vValue);
} else if (vValue !== null && vValue !== true) {
oChild.appendChild(oNewDoc.createTextNode(vValue.toString()));
}
oParentEl.appendChild(oChild);
}
}
}
const oNewDoc = document.implementation.createDocument("", "", null);
loadObjTree(oNewDoc, oObjTree);
return oNewDoc;
};
Now, I want to send the converted xml document to a restful web service using Ajax call from java script
My ajax call is as follows:
var lookupReq = (window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"));
var xmlDoc= createXML(lookupJson);
//make the ajax call to hit the web service
lookupReq.send(xmlDoc);
My web service is as below:
#Path("/ImageServlet")
#Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
public class ImageViewerServlet {
#POST
#Path("/lookup")
#Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML})
public Response getObjectId(GAIssuesLookupArg gaArg)
{
System.out.println("gaArgs : "+gaArg);
return null;
}
}
At the web service end, I am using JAXB for marshaling and unmarshaling.
But, I am getting Bad Request Error when I try to hit the web service by using the above mentioned ajax call. Can any one please tell me where I am doing the mistake and correct my code?
Please let me know if my question is not clear. Thanks in advance.