Inventory API blocked by CORS policy on Dev Server

I have created an Angular service to fetch devices from my Cumulocity tenant using Cumulocity’s FetchClient and InventoryService API

import { Injectable } from '@angular/core';
import { 
  BasicAuth,
  FetchClient,
  InventoryService} from '@c8y/client';
import { environment } from 'src/app/environment/environment';

@Injectable({
  providedIn: 'root'
})
export class DeviceFetchService {

  private client!: FetchClient;
  private inventory!: InventoryService;

  constructor() { 
    const auth = new BasicAuth({
      username: environment.c8y.username,
      password: environment.c8y.password
    })

    this.client = new FetchClient(auth, environment.c8y.tenantUrl); 
    this.inventory = new InventoryService(this.client);
  }

  async getDevicesByType(deviceType: string){
    const { data } = await this.inventory.list({
      pageSize: 100,
      query: `type eq '{deviceType}'`
    });

    return data;
  }
}

When I first tried to implement the service into my component, I got the standard CORS policy implementation error.

Access to fetch at 'http://my-tenant.cumulocity.com/inventory/managedObjects?pageSize=100&query=type%20eq%20%27%7BdeviceType%7D%27' from origin 'http://127.0.0.1:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.

I followed the suggestion by Bing CoPilot and tried to configure CORS policy locally using a proxy file, then linking that config file into my angular.json

proxy.conf.json

{
    "/api": {
      "target": "http://my-tenant.cumulocity.com",
      "secure": false,
      "changeOrigin": true,
      "pathRewrite": {
        "^/api": ""
      }
    }
}

angular.json

"architect": {
  "serve": {
    "options": {
      "proxyConfig": "./proxy.conf.json"
    }
  }
}

However, this has only caused me to get an 404 error when I try to rerun the application on my local server.

 GET http://127.0.0.1:4200/tenant/currentTenant 404 (Not Found)
Promise.then		
bootstrap	@	bootstrap.ts:14
(anonymous)	@	main.ts:18
Promise.then		
_applicationSetup	@	main.ts:19
applicationSetup	@	main.ts:9
30195	@	main.ts:7
(anonymous)	@	main.js:944
(anonymous)	@	main.js:945
(anonymous)	@	main.js:947
Show 120 more frames
bootstrap.ts:14 
 
 POST http://127.0.0.1:4200/user/logout 404 (Not Found)
Promise.then		
bootstrap	@	bootstrap.ts:14
(anonymous)	@	main.ts:18
Promise.then		
_applicationSetup	@	main.ts:19
applicationSetup	@	main.ts:9
30195	@	main.ts:7
(anonymous)	@	main.js:944
(anonymous)	@	main.js:945
(anonymous)	@	main.js:947
Show 506 more frames

image

Hi @luclib,

is your angular application based on Cumulocity’s Web SDK (using e.g. @c8y/ngx-components and further packages), or are you building your app with only the @c8y/client package?
It seems to be the latter…

Is there any specific reason why you’ve decided against using the Web SDK and instead to do it the hard way and implement this on your own?

Your proxy config looks like you are proxying requests going to /api/... to http://novanta.us.cumulocity.com. So in case you would like to keep this config for what ever reason, you would need to send your request to http://127.0.0.1:4200/api/tenant/currentTenant to reach http://novanta.us.cumulocity.com/api/tenant/currentTenant.
But this config does not really work once you deploy your app into a Cumulocity tenant as there is no /api path there.

Regards,
Tristan

Hi Tristan,

I am using Cumulocity’s Web SDK.
image

The application is written in Angular 16, and I have added C8Y’s Web SDK version 1019.2.9.

Then you should be able to just dependency inject the inventory service like this:

import { Injectable } from '@angular/core';
import { 
  BasicAuth,
  FetchClient,
  InventoryService} from '@c8y/client';
import { environment } from 'src/app/environment/environment';

@Injectable({
  providedIn: 'root'
})
export class DeviceFetchService {

  constructor(private inventory: InventoryService) {}

  async getDevicesByType(deviceType: string){
    const { data } = await this.inventory.list({
      pageSize: 100,
      query: `type eq '{deviceType}'`
    });

    return data;
  }
}

Regards,
Tristan

Great, that is a lot easier to do.

Thanks!