Trying to use a JS function within an Angular2 service but getting require is not a function in the console. I'm not sure how to include the external JS file. It's at the same directory level as the service using it.
I believe that the declare statement is legit. true?
I'm not sure how to handle the invalid require statement. It produces the
error "Require is not a function"
Service file: analytics.service.ts
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
//how I thought i was supposed to make the JS function available
declare var AlchemyAPI: any;
#Injectable()
export class AnalyticsService {
constructor (private http: Http) {}
getResponse () {
//not sure how to handle this require. I know that its not allowed
though
var AlchemyAPI = require('./alchemyapi');
//instance of external JS file function
var alchemyapi = new AlchemyAPI();
var myText = "Whoa, AlchemyAPI's Node.js SDK is really great, I can't
wait to build my app!";
return this.http.get(alchemyapi.sentiment("text", myText, {}))
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
let body = res.json();
console.log(body);
return body.data || { };
}
private handleError (error: any) {
let errMsg = (error.message) ? error.message :
error.status ? `${error.status} - ${error.statusText}` : 'Server
error';
console.error(errMsg); // log to console instead
return Observable.throw(errMsg);
External JS file thaat makes remote API call: alchemyapi.js (at the same directory level)
var http = require('http');
var fs = require('fs');
//Make the class available
exports = module.exports = AlchemyAPI;
You can remove require and add JS file in script tag of you index.html. declare states typescript compiler that var/function with that name will be available at runtime so won't give compile time errors.
No need to use require. You could just import it like you did with #angular and rxjs:
import { AlchemyAPI } from './alchemyapi';
Now you can use AlchemyAPI inside your code anyway you want.
Related
I have the following code that works in Angular and wish to convert it to node can someone plase help?
import { HttpClient, HttpErrorResponse, HttpHeaders } from '#angular/common/http';
private getDeviceIP(){
this.clientHttp.get("http://api.ipify.org/?format=json").subscribe((res:any)=>{
this.userIpAddress = res.ip;
});
}
Cheers
If you want to use that logic in NodeJS be aware that NodeJS server is the one that will send the request, so you will got his IP address, and not IP address of the client.
If you still want to implement the same logic in NodeJs, you can use axios (Promise based HTTP client), like this:
const axios = require('axios');
const getDeviceIP = (req, res, next) => {
axios({
method: 'get',
url: 'http://api.ipify.org/?format=json'
}).then((response) => {
return res.status(200).json({ success: true, ip_address: response.data.ip })
}).catch((error) => {
return res.status(400).json({ success: false, error })
});
}
Depending on your use-case (see caveat below), this is already converted to Node!
If you use the #angular/common, typescript and xmlhttprequest packages, you can use the following:
import { XMLHttpRequest } from 'xmlhttprequest';
import { HttpClient, HttpXhrBackend } from '#angular/common/http';
// Your unmodified class
class Main {
userIpAddress: any;
constructor (private clientHttp: HttpClient) {}
getDeviceIP () {
this.clientHttp.get('http://api.ipify.org/?format=json').subscribe((res: any) => {
this.userIpAddress = res.ip;
});
}
}
// Manually perform the dependency resolution
const backend = new HttpXhrBackend({
build: (): XMLHttpRequest => new XMLHttpRequest()
});
const client = new HttpClient(backend);
const main = new Main(client);
// Run it!
main.getDeviceIP();
which can be transpiled with:
npx tsc --lib dom,es2015 main.ts
and run with:
node main.js
Caveat
Note the use of dom as a library in transpilation; this is required because the #angular/common/http subpackage isn't properly decoupled from the DOM related parts of #angular/core. For the purposes of making requests with a HttpClient with a Node compatible backend, this is fine, but attempting to convert more DOM related tasks will likely run into issues.
I'm following the tutorials at developers.sap.com for the Javascript:
Get Started with SAP Cloud SDK for JavaScript.
I created my application with:
sap-cloud-sdk init my-sdk-project
Now I'd like to add security to it, specifically, I want to use an approuter to access the app and I want to block any unauthenticated request to the service directly.
Optionally, I want to include scopes for the different endpoints of my app.
I don't have any problem adding an approuter, but when it comes to secure the node app, I can't seem to find the right way.
I can only find examples of securing an app with basic express node apps like these ones:
Hello World Sample using NodeJS
node.js Hello World
But they have a different structure that the one provided by sap-cloud-sdk tool, which uses nestjs.
The Help Portal doesn't point to any examplet either if you are using Nestjs.
Is there any resource, tutorial, or example to help me implement security in an scaffolded app?
Kr,
kepair
There is no resource yet on how to setup Cloud Foundry security with the Cloud SDK for JS, but I tinkered around with it a bit in the past with the following result.
Disclaimer: This is by no means production ready code! Please take this only as a inspiration and verify all behavior on your side via tests as well as adding robust error handling!
Introduce a scopes.decorator.ts file with the following content:
import { SetMetadata } from '#nestjs/common';
export const ScopesMetadataKey = 'scopes';
export const Scopes = (...scopes: string[]) => SetMetadata(ScopesMetadataKey, scopes);
This will create an annotation that you can add to your controller method in a follow up step. The parameters given will be the scopes that an endpoint requires before being called.
Create a Guard scopes.guard.ts like the following:
import { CanActivate, ExecutionContext, Injectable } from '#nestjs/common';
import { Reflector } from '#nestjs/core';
import { retrieveJwt, verifyJwt } from '#sap/cloud-sdk-core';
import { getServices } from '#sap/xsenv';
import { ScopesMetadataKey } from './scopes.decorator';
#Injectable()
export class ScopesGuard implements CanActivate {
private xsappname;
constructor(private readonly reflector: Reflector) {
this.xsappname = getServices({ uaa: { label: 'xsuaa' } }).uaa.xsappname;
}
async canActivate(context: ExecutionContext): Promise<boolean> {
const scopes = this.reflector.get<string[]>(ScopesMetadataKey, context.getHandler());
if (!scopes) {
return true;
}
const request = context.switchToHttp().getRequest();
const encodedJwt = retrieveJwt(request);
if (!encodedJwt) {
return false;
}
const jwt = await verifyJwt(encodedJwt);
return this.matchScopes(scopes, jwt.scope);
}
private matchScopes(expectedScopes: string[], givenScopes: string[]): boolean {
const givenSet = new Set(givenScopes);
return expectedScopes.every(scope => givenSet.has(this.xsappname + '.' + scope));
}
}
This Guard should be called before all endpoints and verifies that all requires scopes are present in the incoming JWT.
Add the guard to your nest application setup:
import { Reflector } from '#nestjs/core';
import { ScopesGuard } from './auth/scopes.guard';
// ...
const app = ...
const reflector = app.get(Reflector)
app.useGlobalGuards(new ScopesGuard(reflector));
// ...
This ensures that all incoming requests are actually "guarded" by your guard above.
Use the annotation created in the first step on your protection worthy endpoints:
import { Controller, Get } from '#nestjs/common';
import { Scopes } from '../auth/scopes.decorator';
#Controller('/api/rest/foo')
export class FooController {
constructor(private readonly fooService: FooService) {}
#Get()
#Scopes('FooViewer')
getFoos(): Promise<Foo[]> {
return this.fooService.getFoos();
}
}
This endpoint is now only callable if a JWT with the required scope is provided.
You can use the standard nodejs authentication implementation in sap-cloud-sdk/nest.js project without creating any middleware.
Since the JWTStrategy which is part of #sap/xssec have the middleware implementation, things are very simplified.
For Authentication change main.ts
import { NestFactory } from '#nestjs/core';
import { AppModule } from './app.module';
import { getServices } from '#sap/xsenv';
const xsuaa = getServices({ xsuaa: { tag: 'xsuaa' } }).xsuaa;
import * as passport from 'passport';
import { JWTStrategy } from '#sap/xssec';
passport.use(new JWTStrategy(xsuaa));
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use(passport.initialize());
app.use(passport.authenticate('JWT', { session: false }));
await app.listen(process.env.PORT || 3000);
}
bootstrap();
This will initialize the middleware.
2. For scope check and authorization
import { Controller, Get, Req, HttpException, HttpStatus } from '#nestjs/common';
import { AppService } from './app.service';
#Controller()
export class AppController {
constructor(private readonly appService: AppService) { }
#Get()
getHello(#Req() req: any): any {
console.log(req.authInfo);
const isAuthorized = req.authInfo.checkLocalScope('YourScope');
if (isAuthorized) {
return req.user;
} else {
return new HttpException('Forbidden', HttpStatus.FORBIDDEN);
}
// return this.appService.getHello();
}
}
For more details please refer to this
I am struggling with Typescript and modifying the definition of existing module.
We are used to put anything we want to output to "res.out" and at the end there is something like this "res.json(res.out)". This allows us to have general control over the app at the moment of sending the response.
So I have function like this
export async function register(req: Request, res: Response, next: Next) {
try {
const user = await userService.registerOrdinaryUser(req.body)
res.status(201);
res.out = user;
return helper.resSend(req, res, next);
} catch (ex) {
return helper.resError(ex, req, res, next);
}
};
We are using restify. And I get compilation error, because "out" is not part of restify.Response.
Now we have workaround that we have our "own" objects, that extends the Restify ones.
import {
Server as iServer,
Request as iRequest,
Response as iResponse,
} from 'restify'
export interface Server extends iServer {
}
export interface Request extends iRequest {
}
export interface Response extends iResponse {
out?: any;
}
export {Next} from 'restify';
We just did this to make project compilable, but looking for better solution. I have tried things like this:
/// <reference types="restify" />
namespace Response {
export interface customResponse;
}
interface customResponse {
out?: any;
}
But it does not work, right now it says "Duplicate identifier 'Response'".
So anyone how to add definition to restify.Response object with some simple code?
You can use interface merging.
import { Response } from "restify";
declare module "restify" {
interface Response {
out?: any
}
}
I'm implementing an Angular 2 service which gets JSON data from play framework server by http request.
http://localhost:9000/read returns JSON data like [{"id":1,"name":"name1"},{"id":2,"name":"name2"}].
And here's my Angular service code (from this tutorial https://angular.io/docs/ts/latest/guide/server-communication.html):
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Hero } from './hero';
import { Observable } from 'rxjs/Observable';
#Injectable()
export class HttpService {
private heroesUrl = "http:localhost:9000/read"; // URL to web API
constructor (private http: Http) {}
getHeroes (): Observable<Hero[]> {
return this.http.get(this.heroesUrl)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
let body = res.json();
return body || { };
}
private handleError (error: Response | any) {
// In a real world app, we might use a remote logging infrastructure
let errMsg: string;
if (error instanceof Response) {
const body = error.json() || '';
const err = body.error || JSON.stringify(body);
errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
} else {
errMsg = error.message ? error.message : error.toString();
}
console.error(errMsg);
return Observable.throw(errMsg);
}
}
But the request in browser looks like this:
GET XHR http://localhost:4200/localhost:9000/read [HTTP/1.1 404 Not Found 5ms]
So Angular creates relative URL, when absolute URL is needed.
So could I...
1)Fix it in code.
2)Or make Angular 2 and Play run on same port.
3)Use JSONP or something else.
The reason to your relative url is simply because you have a typo, which causes this. Instead of
private heroesUrl = "http:localhost:9000/read";
it should be
private heroesUrl = "http://localhost:9000/read";
No other magic should probably not be needed here. You might run into a cors issue. But since this is a playground, and not actual development, in case of CORS you can enable CORS easily in chrome. But this is naturally NOT recommended in a real-life app. But for playing, that would do just fine.
I'm trying to use an Angular 2 HTTP GET request to simply connect with a Node/Express backend that responds with a list of the file names in a certain folder using the fs.readdir method.
I set up the Angular 2 request as a service:
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import './rxjs-operators';
#Injectable()
export class PhotoService {
constructor (private http: Http) {}
private photosUrl = '/api/photos'; // URL to web API
getPhotos() : Observable<string[]> {
return this.http.get(this.photosUrl)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
let body = res.json();
return body.data || { };
}
private handleError (error: any) {
let errMsg = (error.message) ? error.message :
error.status ? `${error.status} - ${error.statusText}` : 'Server error';
console.error(errMsg); // log to console instead
return Observable.throw(errMsg);
}
}
and then called this service from a component:
ngOnInit() {
this.photoService.getPhotos()
.subscribe(
photos => this.fileList = photos,
error => this.errorMessage = <any>error);
}
This is the Node backend (with Express set up as per conventions):
//Photo Service
app.get('/api/photos', function(req, res) {
fs.readdir('./uploads', function(error, files) {
if (error) {
throw error;
}
else {
res.end(files);
}
});
});
As seen, the HTTP request calls a GET method to http://localhost:3000/api/photos and the Node backend is supposed to receive that request and send back an array of strings that have the names of files in the 'uploads' folder.
However it does not seem to be working. I think I'm getting confused with the format in which the Node API sends the response and how that works with the Observable type that Angular uses in the service.
Your Angular 2 code looks good to me. But in your Node backend you should not send data with res.end() (see the documentation). Correct would be res.send(files); or in your case res.json(files); which will also set the right Content-Type header.