Enable CORS in Angular frontend - javascript

I have a microfrontend running on port 4200, and the other one on 4201. I tried splitting up the translation files and would like to load both of the applications translations, so I wrote a custom loader that is making an http request to port 4201:
export const scope = function(http: HttpClient) {
const loader = ['en', 'de'].reduce((acc: any, lang: string) => {
acc[lang] = () => firstValueFrom(http.get(`http://localhost:4201/assets/i18n/fields/${lang}.json`));
return acc;
}, {});
return {scope: 'fields', loader: loader}
};
#NgModule({
declarations: [RootComponent],
imports: [
CommonModule,
RootRoutingModule,
FormsModule,
SharedModule.forRemote(environment),
TranslocoModule
],
providers: [
{
provide: TRANSLOCO_SCOPE,
deps: [HttpClient],
useFactory: scope
}
]
})
export class RootModule { }
Both applications are javacript frontends, hence CORS blocks it. How can I ignore cores in the frontend?

https://levelup.gitconnected.com/fixing-cors-errors-with-angular-cli-proxy-e5e0ef143f85
This article helped me. It is a workaround to enable CORS in the frontend. But be warned this is just a workaround and in my case it was neccessary because I was loading translation files from one microfrontend to the app shell.

Related

Run a middleware before Nest Js ServeStaticModule

I want to run a middleware before Nest JS serves my React application using ServeStatic Module. I cannot get a nest middleware or even a Global middleware to run on any static routes other than '/'
main.ts
app.use([AuthRedirectMiddleware, VerifyMiddleware]);
// Or even a simple logger
app.use(function (req, res, next) {
console.log("LOG: ", req.originalUrl);
next();
});
// All 3 middlewares only run for / and /api*
// Does not run for /posts , /orders/123 (both are front end routes)
This is only working for API routes and '/'
My static serve module is setup like this:
app.module.ts
#Module({
imports: [
ConfigModule.forRoot(),
ServeStaticModule.forRoot({
rootPath: clientPath,
exclude: ["/api*"],
}),
SharedModule,
...
],
controllers: [],
providers: [],
})
I also have a globalPrefix for api routes in main.js. So all urls except for /api* go to the react application
app.setGlobalPrefix("api");
Try a middleware module:
my-middleware.module.ts
#Module({}) // you can import/provide anything you need in your module here
export class MyMiddlewareModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(MyMiddleware).forRoutes('*');
}
}
#Injectable()
class MyMiddleware implements NestMiddleware {
async use(req: Request, res: Response, next: (error?: any) => void) {
}
}
and then import it in your app.module.ts:
#Module({
imports: [
ConfigModule.forRoot(),
MyMiddleware, // Important for it to be before your static module
ServeStaticModule.forRoot({
rootPath: clientPath,
exclude: ["/api*"],
}),
SharedModule,
...
],
controllers: [],
providers: [],
})
Also these two links maybe useful:
https://github.com/nestjs/docs.nestjs.com/issues/1248
https://docs.nestjs.com/faq/request-lifecycle#request-lifecycle

Jasmine/Karma testing Angular 5 HTTP responses

I'm totally new to TDD and I'm trying to debug a big Angular 5 application we're working in, at the company.
The app is working well, but now I have to implement tests, and I'm learning this stuff while I create the most basic and starter ones. I wrote this stuff already for the main module, just for trying this tooling:
describe('AppComponent', () => {
let httpClientSpy: { get: jasmine.Spy }
let dataReq: DataRequester;
let queryBuilder: PanelQueryBuilder;
let query: DataQuery;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
MainComponent,
Menu,
MessageViewer
],
imports: [
BrowserModule,
BrowserAnimationsModule,
routing,
AngularFontAwesomeModule,
FormsModule,
HttpModule,
ChartModule,
Ng4LoadingSpinnerModule.forRoot(),
NgbModule.forRoot()
],
providers: [
ReactiveService,
DataRequester,
{ provide: APP_BASE_HREF, useValue : '/' }
]
}).compileComponents();
}));
it('should create the app', async(() => {
const fixture = TestBed.createComponent(MainComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
}));
it('userType should be defined', async(()=>{
expect(MainComponent.userType).toBeDefined();
}))
it('DataRequester exists and retrieves info', async(()=>{
beforeEach(() => {
// TODO: spy on other methods too
httpClientSpy = jasmine.createSpyObj('HttpClient', ['get']);
dataReq = new DataRequester(<any> httpClientSpy);
queryBuilder = new PanelQueryBuilder();
});
expect(MainComponent).toBeDefined();
}))
it('1. Build query and check integrity', async()=>{
query = queryBuilder.buildInitialQuery("panel", "conversions", 144);
expect(query).toBeDefined();
})
it('2. Send query and check result', async()=>{
dataReq.requestData(query, 'conversions').then( res => {
expect(res).toContain("panel");
})
})
});
I want you to focus in one part: the DataRequester service. It's a service that has a method which returns a promise, and calls a specific part of our backend, returning the data. I just want here to check if this response object contains the property "panel", and the test...
...¡actually says it exists! But if I try to change the name of the property, to some non-existing property... it validates as true too. So maybe, the HTTP request is NOT working properly here, doing something wrong here.
Am I doing something bad in this code? Why doesn't the DataRequester "requestData" method execute properly, so Jasmine is able to properly test the conditions I want, in the response object?
Yes, you"re doign something bad in your code. But don't worry, I did the same thing when I started.
First, you must understand the basics of unit testing : unit tests are made to prevent side effects in a unit.
Side effects are changes in a wanted behavior : for instance, you want to color a div in blue, and after some code editing, it colors red : this is a side effect.
A unit is the feature you're testing. In Angular, you can see which one it is with this :
describe('AppComponent'
Here, you're testing AppComponent.
Now that we settled this, let's go over what is wrong in your tests : you use real instances of your services. This means that you don't don't a single unit anymore : you're testing several units.
To correct that
You have to mock your services. You will check if your component is actually calling the service, and not if the service is calling your API (this will be checked by the unit test of the service itself).
In your test bed :
TestBed.configureTestingModule({
declarations: [
MainComponent,
Menu,
MessageViewer
],
imports: [
BrowserModule,
BrowserAnimationsModule,
routing,
AngularFontAwesomeModule,
FormsModule,
HttpModule,
ChartModule,
Ng4LoadingSpinnerModule.forRoot(),
NgbModule.forRoot()
],
providers: [
{
provide: ReactiveService,
useValue : {}
},
{
provide: DataRequester,
useValue: {}
},
{ provide: APP_BASE_HREF, useValue : '/' }
]
Usually, components handle only the view : you don't really to mock them (although you should).
This allows you to remove the HttpModule, which isn't required in any test.
You can also remove your routing module, because Angular provides a mock of it already : RouterTestingModule.
Your test bed becomes this
TestBed.configureTestingModule({
declarations: [
MainComponent,
Menu,
MessageViewer
],
imports: [
BrowserModule,
BrowserAnimationsModule,
RouterTestingModule,
AngularFontAwesomeModule,
FormsModule,
ChartModule,
Ng4LoadingSpinnerModule.forRoot(),
NgbModule.forRoot()
],
providers: [
{
provide: ReactiveService,
useValue : {}
},
{
provide: DataRequester,
useValue: {}
},
{ provide: APP_BASE_HREF, useValue : '/' }
]
Now you have a proper test bed.
All you have to do left, is add in the useValue of your mocks, every service property used by your component with the correct signature.
For instance, imagine your app component has this :
ngOnInit() {
this.dataRequester.requestWidth('URL').subscribe(res => this.reactiveService.width = res);
}
Then your test bed becomes this :
TestBed.configureTestingModule({
declarations: [
MainComponent,
Menu,
MessageViewer
],
imports: [
BrowserModule,
BrowserAnimationsModule,
RouterTestingModule,
AngularFontAwesomeModule,
FormsModule,
ChartModule,
Ng4LoadingSpinnerModule.forRoot(),
NgbModule.forRoot()
],
providers: [
{
provide: ReactiveService,
useValue : {
width: 0
}
},
{
provide: DataRequester,
useValue: {
requestWidth: () => of(100)
}
},
{ provide: APP_BASE_HREF, useValue : '/' }
]
(Values of the mock aren't important, you'll change them on demand)
As you can see, because your requester service returns an Observable, you are forced to return one too. And because your reactive service stores the width, you have to declare a variable of type number.
Now, in your test, using the previous example, you will do this :
it(`should get width from requester and store it in reactive service`, fakeAsync(() => {
spyOn(component['dataRequester'], 'requestWidth').and.callThrough();
component.ngOnInit();
tick();
expect(component['dataRequester'].requestWidth).toHaveBeenCalledWith('URL');
expect(component['reactiveService'].width).toEqual(100);
}));
You declare what you do (test driven), you spy on your service (to see if it has been called), then you call through (because our mock is already an Observable and returns 100).
Then, you call the method to test, and flush the async calls out (fakeAsync and tick are part of Angular, you can find them in the docs about testing).
Finally, you make your expectations.
And with that, you successfully tested your first method !

Angular 2 Set APP_BASE_HREF with a value from a Promise / Observable

I try to set the APP_BASE_HREF in the "CoreModule" with a value from a async rest call.
I can't see how this is done, because the provide method needs to return a string.
for example:
#NgModule({
imports: [
...
HttpModule
],
...
providers: [
...
...
BackendRequestClass,
{ provide: APP_BASE_HREF, useFactory: () => () => return '/some/path', deps: [], multi: true }
],
});
but when I need the value from a webservice, I can't return the string.
Any ideas how this could be done?
thx
I tried your solution.
The problem is, that at the time
{ provide: APP_BASE_HREF, useFactory: (config) => config.appBaseHref, deps: [ConfigService] }
the config.appBaseHref is not set yet.
When I debug the code I see, that the APP_INITIALIZER is executed after the provider from APP_BASE_HREF
That causes, that the BASE_HREF is not set.
We had the same challenge: Set the APP_BASE_HREF dynamically, based on a result from an async API call to allow routing of an app which can be accessed through different URLs.
While the solution presented by Günter looks sweet, it unfortunately did not work, at least with Angular 7. In our case, APP_BASE_HREF always took precedence over APP_INITIALIZER, so we couldn’t initialize the APP_BASE_HREF based on the resolved promise value returned from the initializer.
Instead, I implemented the API call to happen before the Angular bootstrapping and then injected the provider so that it’s available within the Angular context.
Adding to main.ts:
fetchConfig().then(config => {
platformBrowserDynamic([ { provide: ConfigData, useValue: config } ])
.bootstrapModule(AppModule)
.catch(err => console.log(err));
});
We can then configure the APP_BASE_HREF in the providers within app.module.ts:
providers: [
// …
{
provide: APP_BASE_HREF,
useFactory: (config: ConfigData) => config.base,
deps: [ ConfigData ]
}
]
[edit 2019-08-21] Clarification per the comments: fetchConfig() is the API request which gets the configuration and returns a Promise. And ConfigData is not an Angular service, but just a simple class which gets instantiated in fetchConfig() and which gives me type safety when accessing it later in my application:
function fetchConfig (): Promise<ConfigData> {
return fetch('/api/config')
.then(response => response.json())
.then(response => new ConfigData(response));
}
You can use APP_INITIALIZER to get the path in advance and then use a dependency as shown in Angularjs2 - preload server configuration before the application starts
export function loadConfig(config: ConfigService) => () => config.load()
#NgModule({
declarations: [AppComponent],
imports: [BrowserModule,
routes,
FormsModule,
HttpModule],
providers: [
ConfigService,
BackendRequestClass,
{ provide: APP_INITIALIZER,
useFactory: loadConfig,
deps: [ConfigService],
multi: true },
{ provide: APP_BASE_HREF, useFactory: (config) => config.appBaseHref, deps: [ConfigService] }
],
bootstrap: [AppComponent]
})
export class AppModule { }
The ConfigService can inject BackendRequestClass or Http and fetch the data and then make it available using its appBaseHref property (just an example how it could be done).

Conditionally load a different set of factories in Angular based on environment

I'm working on an Angular application which has two different types of backend; one which is used when the application is loaded via a normal web page and one which is used inside a Cordova application. The purpose of this is so that the rest of the application just knows it gets the services and only the services are specific to storing data on the client or on the server via AJAX.
Obviously I could do this using a symlink or something that I change before loading, but I'd really like to just set it up so I can use a different environment config and have it load the other set of services.
Each service will have two versions, one for "http backend" and one for "mobile backend". Both versions will either extend the same abstract base class or implement the same interface.
Perhaps most importantly, the application should not load the code for the unused set of services, so just having the code decide which service to return won't suffice.
Is there a good way to do this?
Something like this should work:
// service used in components/other services
export class DataService<T> {
constructor(
private connectionService: ConnectionService,
private crudService: CrudService,
private storageService: StorageService,
private store: Store<AppState>) {}
}
then you can provide different implementations on module level:
const FirebaseProviders = [
{ provide: AuthService, useClass: FirebaseAuthService },
{ provide: ConnectionService, useClass: FirebaseConnectionService },
{ provide: CrudService, useClass: FirebaseCrudService },
{ provide: ErrorHandler, useClass: FirebaseErrorHandler, deps: [DataService, Store] },
{ provide: StorageService, useClass: FirebaseStorageService },
];
const MockProviders = [
{ provide: AuthService, useClass: MockAuthService },
{ provide: ConnectionService, useClass: MockConnectionService },
{ provide: CrudService, useClass: MockCrudService },
{ provide: ErrorHandler, useClass: AppErrorHandler, deps: [Store] },
{ provide: StorageService, useClass: StorageService },
];
const CoreProviders = environment.production ? FirebaseProviders : MockProviders;
#NgModule({
...
providers: [
...CoreProviders,
DataService
]
})
export class CoreModule {}
Make sure to list all dependencies in
{ provide: <ServiceA>, useClass: <ServiceB>, deps: [<IMPORTANT for AoT>] }
or you will get nasty (hard to debug) AoT error.

When/how does #NgModule get evaluated?

I've 3 applications which share a common framework so there's currently a bunch of duplicated code (basic ui layout, common code such as loggers etc, security/keycloak auth wrapper). I've extracted all the duplicate module code but also want to simplify the app module declaration itself. I've created an "Application" class containing a static method which takes a couple of params and builds an NgModule definition based on common modules, concatenating the passed in ones...
public static Define({modules, states, defaultState}) {
return {
imports: [
BrowserModule,
FormsModule,
HttpModule,
TreeModule,
NgbModule.forRoot(),
ToastModule.forRoot(<ToastOptions>{animate: 'flyRight', positionClass: 'toast-bottom-right'}),
UIRouterModule.forRoot(<RootModule>{ states: states.concat({name: 'app', url: '/app', component: ShellComponent}), useHash: true, otherwise: defaultState }),
CommonModule,
DialogsModule,
LayoutModule,
].concat(modules),
providers: [
{
provide: Http,
useFactory: (backend: XHRBackend, defaultOptions: RequestOptions) => new AuthHttpService(backend, defaultOptions),
deps: [XHRBackend, RequestOptions]
}
],
bootstrap: [ UIView ]
};
}
Each application can then simply call this and just list its specific modules, states and an initial/default state like this...
#NgModule(
Application.Define({
modules: [
PatientIdentityModule,
RecordViewerModule,
ResourcesModule,
],
states: [
{name: 'app.resourceList', url: '/resourceList', component: ResourcesComponent },
{name: 'app.resourceEdit', url: '/resourceEdit/:itemAction/:itemUuid', component: ResourcesComponent },
{name: 'app.patientIdentity', url: '/patientIdentity', component : PatientIdentityComponent},
{name : 'app.recordViewer', url: '/recordViewer', component : RecordViewerComponent }
],
defaultState : { state: 'app.recordViewer', params: {} }
})
)
export class AppModule {}
This works great when all the code is together but when I try extracting the Application class and building a node module library, I then get
Error: No NgModule metadata found for 'AppModule'
Any ideas or am I going about this completely the wrong way!?
Angular 2.1.0, typescript 2.0.6, webpack 1.13.2

Categories