Skip to content

Commit

Permalink
feat: Extend axios-extension services (#1958)
Browse files Browse the repository at this point in the history
* feat: extend axios-extension services

* refactor: change function name

* refactor: enhance logging in error handling

* test: add tests

* test: add tests

* test: add tests

* refactor: remove method

* fix: fix lint errors

* fix: fix build

* feat: export types and refactor

* refactor: change log

* refactor: refactor methods

* fix: fix sonar issue

* refactor: update service schema
  • Loading branch information
GDamyanov authored Jul 25, 2024
1 parent faa48da commit ad9b56d
Show file tree
Hide file tree
Showing 10 changed files with 256 additions and 5 deletions.
5 changes: 5 additions & 0 deletions .changeset/brave-lamps-jam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sap-ux/axios-extension': patch
---

Extend axios-extension services
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ export { ListPackageService } from './list-package-service';
export { FileStoreService } from './filestore-service';
export { GeneratorService } from './generator-service';
export { BusinessObjectsService } from './businessobjects-service';
export { UI5RtVersionService } from './ui5-rt-version-service';
export { AbapCDSViewService } from './abapcdsview-service';
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import type { AdtCategory } from '../../types';
import { AdtService } from './adt-service';

/**
* UI5RtVersionService implements ADT requests to get UI5 Version
* of ABAP system.
*/
export class UI5RtVersionService extends AdtService {
/**
* @see AdtService.getAdtCatagory()
*/
private static adtCategory = {
scheme: 'http://www.sap.com/adt/categories/filestore',
term: 'ui5-rt-version'
};

/**
* Get ADT scheme ID.
*
* @returns AdtCategory
*/
public static getAdtCatagory(): AdtCategory {
return UI5RtVersionService.adtCategory;
}

/**
* Get UI5 Version of ABAP system.
*
* @returns UI5 Version on the connected ABAP System.
*/
public async getUI5Version(): Promise<string> {
try {
const result = await this.get('');
return result.data as string;
} catch (error) {
this.log.error('Could not fetch UI5 Version.');
this.log.debug(error);
throw error;
}
}
}
24 changes: 22 additions & 2 deletions packages/axios-extension/src/abap/app-index-service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { Service } from '../base/service-provider';
import { Axios } from 'axios';
import { Axios, isAxiosError } from 'axios';
import type { Logger } from '@sap-ux/logger';
import { isAxiosError } from '../base/odata-request-error';

export interface App extends Record<string, unknown> {
'sap.app/id': string;
Expand Down Expand Up @@ -55,6 +54,27 @@ export abstract class AppIndexService extends Axios implements Service {
return JSON.parse(response.data).results as AppIndex;
}

/**
* Check for a given app id whether the manifest first is supported.
*
* @param {string} appId - The id of the app.
* @returns {Promise<boolean>} - "true" for apps supporting manifest first which are apps with minUI5Version at least 1.30 and not scaffolding-based (i.e. dependency to sap.ca.scfld.md library), otherwise it returns "false".
*/
public async getIsManiFirstSupported(appId: string): Promise<boolean> {
try {
const params = {
'id': appId
};
const response = await this.get('/ui5_app_mani_first_supported', { params });
const isManiFirstSupported = JSON.parse(response.data);

return isManiFirstSupported;
} catch (error) {
this.log.error(`Fail fetching ui5_app_mani_first_supported for app with id: ${appId}.`);
this.log.debug(error);
throw error;
}
}
/**
* Gets the app info for the specified id.
*
Expand Down
8 changes: 7 additions & 1 deletion packages/axios-extension/src/abap/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ export {
Ui5AbapRepositoryService,
UndeployConfig
} from './ui5-abap-repository-service';
export { LayeredRepositoryService, AdaptationConfig, MergedAppDescriptor } from './lrep-service';
export {
LayeredRepositoryService,
AdaptationConfig,
MergedAppDescriptor,
SystemInfo,
AdaptationProjectType
} from './lrep-service';
export { AbapServiceProvider } from './abap-service-provider';
export { AppIndex, AppIndexService, Ui5AppInfo } from './app-index-service';
export * from './message';
Expand Down
51 changes: 51 additions & 0 deletions packages/axios-extension/src/abap/lrep-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,30 @@ export interface Message {
variables?: string[];
}

/**
* All available adaptation project types from system.
*/
export enum AdaptationProjectType {
ON_PREMISE = 'onPremise',
CLOUD_READY = 'cloudReady'
}

/**
* Structure of the system info reponse data.
*/
export interface SystemInfo {
/**
* Supported adaptation project types from system.
*/
adaptationProjectTypes: AdaptationProjectType[];
activeLanguages: Language[];
}

interface Language {
sap: string;
description: string;
i18n: string;
}
/**
* Technically supported layers, however, in practice only `CUSTOMER_BASE` is used
*/
Expand Down Expand Up @@ -256,6 +280,33 @@ export class LayeredRepositoryService extends Axios implements Service {
}
}

/**
* Get system info.
*
* @param language
* @param cloudPackage name
* @returns the system info object
*/
public async getSystemInfo(language: string = 'EN', cloudPackage?: string): Promise<SystemInfo> {
try {
const params = {
'sap-language': language
};
if (cloudPackage) {
params['package'] = cloudPackage;
}

const response = await this.get(`${DTA_PATH_SUFFIX}system_info`, { params });
this.tryLogResponse(response, 'Successful getting system info.');
return JSON.parse(response.data) as SystemInfo;
} catch (error) {
this.log.error('Getting system data failed.');
this.log.debug(error);

throw error;
}
}

/**
* Try parsing the response and log the result. If the parsing fails and an alternative is provided, log it instead.
*
Expand Down
56 changes: 55 additions & 1 deletion packages/axios-extension/test/abap/abap-adt-service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import {
FileStoreService,
BusinessObjectsService,
GeneratorService,
UI5RtVersionService,
AbapCDSViewService
} from '../../src';
import type { AxiosError } from '../../src';
import * as auth from '../../src/auth';
import type { ArchiveFileNode } from '../../src/abap/types';
import fs from 'fs';
Expand Down Expand Up @@ -45,7 +47,8 @@ enum AdtServices {
FILE_STORE = '/sap/bc/adt/filestore/ui5-bsp/objects',
//BUSINESS_OBJECTS = '/sap/bc/adt/repository/informationsystem/search',
GENERATOR = '/sap/bc/adt/repository/generators',
PUBLISH = '/sap/bc/adt/businessservices/odatav4'
PUBLISH = '/sap/bc/adt/businessservices/odatav4',
UI5_RT_VERSION = '/sap/bc/adt/filestore/ui5-bsp/ui5-rt-version'
}

const server = 'https://server.example';
Expand Down Expand Up @@ -971,3 +974,54 @@ describe('Generator Service', () => {
).rejects.toThrowError();
});
});

describe('UI5 RT Version service', () => {
const ui5VersionMock = '1.21.1';
beforeAll(() => {
nock.disableNetConnect();
});

afterAll(() => {
nock.cleanAll();
nock.enableNetConnect();
});

const provider = createForAbap(config);

test('Get UI5 Version', async () => {
nock(server)
.get(AdtServices.DISCOVERY)
.replyWithFile(200, join(__dirname, 'mockResponses/discovery-1.xml'))
.get(AdtServices.UI5_RT_VERSION)
.reply(200, ui5VersionMock);

const ui5RtVersionService = await provider.getAdtService<UI5RtVersionService>(UI5RtVersionService);
const ui5Version = await ui5RtVersionService?.getUI5Version();
expect(ui5Version).toBe(ui5VersionMock);
});

test('Throws error when request fails', async () => {
const mockAxiosError = {
response: {
status: 404,
data: 'Not found'
},
message: 'Request failed with status code 404'
} as AxiosError;
nock(server)
.get(AdtServices.DISCOVERY)
.replyWithFile(200, join(__dirname, 'mockResponses/discovery-1.xml'))
.get(AdtServices.UI5_RT_VERSION)
.replyWithError(mockAxiosError);

const ui5RtVersionService = await provider.getAdtService<UI5RtVersionService>(UI5RtVersionService);

try {
await ui5RtVersionService?.getUI5Version();
fail('The function should have thrown an error.');
} catch (error) {
expect(error).toBeDefined();
expect(error.message).toBe('Request failed with status code 404');
}
});
});
42 changes: 42 additions & 0 deletions packages/axios-extension/test/abap/app-index-service.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { AppIndexService, createForAbap } from '../../src';
import nock from 'nock';
import appIndexMock from './mockResponses/appIndex.json';
import type { AxiosError } from '../../src';
nock.disableNetConnect();
import appInfoJsonMock from './mockResponses/ui5AppInfo.json';
import type { ToolsLogger } from '@sap-ux/logger';
import * as Logger from '@sap-ux/logger';
Expand Down Expand Up @@ -78,6 +80,46 @@ describe('AppIndexService', () => {
});
});

describe('getIsManiFirstSupported', () => {
const provider = createForAbap(config);
const service: AppIndexService = provider.getAppIndex();

test('get is manifest first supported', async () => {
nock.cleanAll();
nock(server)
.get((path) => path.startsWith(`${AppIndexService.PATH}/ui5_app_mani_first_supported`))
.reply(200, (_path) => {
return appIndexMock['ui5_app_mani_first_supported'];
})
.persist();

const result = await service.getIsManiFirstSupported('appId');
expect(result).toBe(true);
});

test('request fails and throw error', async () => {
const mockAxiosError = {
response: {
status: 404,
data: 'Not found'
},
message: 'Request failed with status code 404'
} as AxiosError;
nock.cleanAll();
nock(server)
.get((path) => path.startsWith(`${AppIndexService.PATH}/ui5_app_mani_first_supported`))
.replyWithError(mockAxiosError)
.persist();

try {
await service.getIsManiFirstSupported('appId');
fail('The function should have thrown an error.');
} catch (error) {
expect(error).toBeDefined();
expect(error.message).toBe('Request failed with status code 404');
}
});
});
describe('getAppInfo', () => {
const provider = createForAbap(config);
const service: AppIndexService = provider.getAppIndex();
Expand Down
30 changes: 30 additions & 0 deletions packages/axios-extension/test/abap/lrep-service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { LayeredRepositoryService, createForAbap } from '../../src';
import type { AxiosError } from '../../src';
import type { ToolsLogger } from '@sap-ux/logger';
import * as Logger from '@sap-ux/logger';
import { describe } from 'node:test';

const loggerMock: ToolsLogger = {
debug: jest.fn(),
Expand Down Expand Up @@ -281,4 +282,33 @@ describe('LayeredRepositoryService', () => {
}
});
});

describe('getSystemInfo', () => {
const mockResult = {
adaptationProjectTypes: ['onPremise', 'cloudReady'],
activeLanguages: [{ sap: 'EN', description: 'EN Language', i18n: 'EN-en' }]
};

test('successful call with provided package and without provided language', async () => {
nock(server)
.get((path) => path.startsWith(`${LayeredRepositoryService.PATH}/dta_folder/system_info`))
.reply(200, (_path) => {
return mockResult;
});
const systemInfo = await service.getSystemInfo('Z_TEST_PACKAGE');
expect(systemInfo).toEqual(mockResult);
});

test('throws error when request fails', async () => {
nock(server)
.get((path) => path.startsWith(`${LayeredRepositoryService.PATH}/dta_folder/system_info`))
.reply(500);
try {
await service.getSystemInfo('Z_TEST_PACKAGE');
fail('The function should have thrown an error.');
} catch (error) {
expect(error).toBeDefined();
}
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@
"sap.app/type": "card",
"url": "/sap/bc/ui5_ui5/sap/example_app_2/cards/mycard"
}
]
],
"ui5_app_mani_first_supported": true
}

0 comments on commit ad9b56d

Please sign in to comment.