Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature(odata-service-inquirer): Adds abap on prem prompts #2096

Merged
merged 53 commits into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
a5f1472
Remove unimplemented message for implemented datasource
IainSAP Jun 21, 2024
399245c
Adds cset
IainSAP Jun 21, 2024
400389f
feat(odata-service-inquirer): Adds Abap on Premise prompting
IainSAP Jun 28, 2024
3ff463e
Merge remote-tracking branch 'origin/main' into 210624
IainSAP Jun 28, 2024
cfa20ee
Updates lock file
IainSAP Jun 28, 2024
f2f1bb7
Removes incorrect cset
IainSAP Jun 28, 2024
bd1850e
fixes based on testing
IainSAP Jul 1, 2024
81044a7
Merge remote-tracking branch 'origin/main' into 210624
IainSAP Jul 1, 2024
f864bc7
Adds cli service selection (Abap On Prem)
IainSAP Jul 2, 2024
c72c563
Merge remote-tracking branch 'origin/main' into 210624
IainSAP Jul 2, 2024
c910987
Updates uname/pword labels
IainSAP Jul 3, 2024
4464e74
Merge remote-tracking branch 'origin/main' into 210624
IainSAP Jul 3, 2024
0da527b
Pass service selection prompt options from new system prompt
IainSAP Jul 3, 2024
bfeb44e
Merge remote-tracking branch 'origin/main' into 210624
IainSAP Jul 9, 2024
4a6e1cd
Adds tests for abap-pn-prem prompts
IainSAP Jul 9, 2024
5aa4b0e
Adding more tests, adds service type discovery
IainSAP Jul 10, 2024
0c78865
Merge remote-tracking branch 'origin/main' into 210624
IainSAP Jul 10, 2024
118cf99
Linting auto fix commit
github-actions[bot] Jul 10, 2024
40817fa
Adds service type handling and tests for 'ServiceTypeForHUBServices'
IainSAP Jul 11, 2024
3e746a9
Merge remote-tracking branch 'origin/feat/2094/adds_abap_on_prem_prom…
IainSAP Jul 11, 2024
b5a9756
Linting auto fix commit
github-actions[bot] Jul 11, 2024
c65e5e0
Add test for autocomplete (cli type-ahead)
IainSAP Jul 11, 2024
d343838
Linting auto fix commit
github-actions[bot] Jul 11, 2024
681ea0b
CLI specific test for service retrieval
IainSAP Jul 11, 2024
f503ce7
Merge branch 'main' into feat/2094/adds_abap_on_prem_prompts
IainSAP Jul 12, 2024
96342a4
Adds remaining tests
IainSAP Jul 12, 2024
eefb9ab
Merge remote-tracking branch 'origin/main' into 210624
IainSAP Jul 12, 2024
e956e9c
Merge branch 'main' into feat/2094/adds_abap_on_prem_prompts
IainSAP Jul 12, 2024
18bc7d6
Covers untested code
IainSAP Jul 15, 2024
57d4eaf
Merge remote-tracking branch 'origin/feat/2094/adds_abap_on_prem_prom…
IainSAP Jul 15, 2024
400492e
Merge remote-tracking branch 'origin/main' into 210624
IainSAP Jul 15, 2024
815b510
Fixes some sonar issues
IainSAP Jul 15, 2024
f8aaeba
Refactors connection validator
IainSAP Jul 15, 2024
10412af
Fixes typo
IainSAP Jul 15, 2024
639383f
Linting auto fix commit
github-actions[bot] Jul 15, 2024
8f2c9a7
Adds ValidationResult type
IainSAP Jul 15, 2024
a8cc75a
Refactor to reduce complexity
IainSAP Jul 15, 2024
11e3b8e
Merge remote-tracking branch
IainSAP Jul 15, 2024
9fe7352
Adds jsdocs
IainSAP Jul 15, 2024
24333dd
Merge branch 'main' into feat/2094/adds_abap_on_prem_prompts
IainSAP Jul 15, 2024
5070b6e
Adds changeset
IainSAP Jul 15, 2024
4b90a4e
Merge branch 'main' into feat/2094/adds_abap_on_prem_prompts
IainSAP Jul 16, 2024
b981fed
Adds new exported function to get Abap on premise system prompting
IainSAP Jul 16, 2024
d4c1466
Merge remote-tracking branch 'origin/feat/2094/adds_abap_on_prem_prom…
IainSAP Jul 16, 2024
80b2c2e
Merge remote-tracking branch 'origin/main' into 210624
IainSAP Jul 16, 2024
e7320ea
Revert export comment
IainSAP Jul 16, 2024
ad8b8cf
Uncomment fixed logger, when default logger is used.
IainSAP Jul 16, 2024
4d1db1a
Fixes lint
IainSAP Jul 17, 2024
7cf7272
Merge branch 'main' into feat/2094/adds_abap_on_prem_prompts
IainSAP Jul 17, 2024
a910cee
Update packages/odata-service-inquirer/src/index.ts
IainSAP Jul 17, 2024
6857676
Removes local only file, fixes typos
IainSAP Jul 17, 2024
987edc8
Merge branch 'main' into feat/2094/adds_abap_on_prem_prompts
IainSAP Jul 18, 2024
3325c31
Merge branch 'main' into feat/2094/adds_abap_on_prem_prompts
IainSAP Jul 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Adding more tests, adds service type discovery
  • Loading branch information
IainSAP committed Jul 10, 2024
commit 5aa4b0e335f5197d3533e4603b09229740c93123
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Severity } from '@sap-devx/yeoman-ui-types';
import type { Annotations, AxiosRequestConfig, CatalogService, ServiceProvider } from '@sap-ux/axios-extension';
import { ODataVersion, ServiceType, create } from '@sap-ux/axios-extension';
import type { CatalogService } from '@sap-ux/axios-extension';
import { ODataVersion, ServiceType, V2CatalogService } from '@sap-ux/axios-extension';
import { searchChoices, withCondition, type ListQuestion } from '@sap-ux/inquirer-common';
import { OdataVersion } from '@sap-ux/odata-service-writer';
import { validateClient } from '@sap-ux/project-input-validator';
Expand All @@ -11,9 +11,8 @@ import { hostEnvironment, promptNames, type OdataServiceAnswers } from '../../..
import { PromptState, getHostEnvironment } from '../../../../utils';
import { ConnectionValidator } from '../../../connectionValidator';
import LoggerHelper from '../../../logger-helper';
import { errorHandler } from '../../../prompt-helpers';
import { getNewSystemNameQuestion } from '../new-system/questions';
import { getServiceChoices } from './service-helper';
import { getServiceChoices, getServiceMetadata, getServiceType } from './service-helper';

export enum abapOnPremInternalPromptNames {
systemUrl = 'systemUrl',
Expand Down Expand Up @@ -64,12 +63,11 @@ export function getAbapOnPremQuestions(
serviceSelectionPromptOptions?: ServiceSelectionPromptOptions
): Question<AbapOnPremAnswers>[] {
PromptState.reset();
errorHandler.resetErrorState();
const connectValidator = new ConnectionValidator();
let serviceChoices: ListChoiceOptions<ServiceAnswer>[];
// Prevent re-requesting services repeatedly by only requesting them once and when the system is changed
let lastSystemUrl: string | undefined;
let lastService: ServiceAnswer | undefined;
let previousSystemUrl: string | undefined;
let previousService: ServiceAnswer | undefined;
const requiredOdataVersion = serviceSelectionPromptOptions?.requiredOdataVersion;

const questions: Question<AbapOnPremAnswers>[] = [
Expand All @@ -91,7 +89,6 @@ export function getAbapOnPremQuestions(
PromptState.odataService.connectedSystem = {
serviceProvider: connectValidator.serviceProvider
};
lastSystemUrl = url;
}
return valResult;
}
Expand Down Expand Up @@ -137,7 +134,6 @@ export function getAbapOnPremQuestions(
PromptState.odataService.connectedSystem = {
serviceProvider: connectValidator.serviceProvider
};
lastSystemUrl = systemUrl;
}
return valResult;
}
Expand All @@ -159,18 +155,20 @@ export function getAbapOnPremQuestions(
message: t('prompts.systemService.message'),
guiOptions: {
breadcrumb: t('prompts.systemService.breadcrumb'),
mandatory: true
mandatory: true,
applyDefaultWhenDirty: true
},
source: (prevAnswers: AbapOnPremAnswers, input: string) =>
searchChoices(input, serviceChoices as ListChoiceOptions[]),
choices: async (answers: AbapOnPremAnswers) => {
if (!serviceChoices || lastSystemUrl !== answers.systemUrl) {
if (!serviceChoices || previousSystemUrl !== answers.systemUrl) {
let catalogs: CatalogService[] = [];
if (requiredOdataVersion) {
catalogs.push(connectValidator.catalogs[requiredOdataVersion]);
} else {
catalogs = Object.values(connectValidator.catalogs);
}
previousSystemUrl = answers.systemUrl;
serviceChoices = await getServiceChoices(catalogs);
}
return serviceChoices;
Expand Down Expand Up @@ -202,17 +200,15 @@ export function getAbapOnPremQuestions(
// Warning: only executes in YUI not cli
validate: async (
service: ServiceAnswer,
answers: Partial<AbapOnPremAnswers>
{ systemUrl }: Partial<AbapOnPremAnswers> = {}
): Promise<string | boolean | ValidationLink> => {
if (!answers.systemUrl) {
if (!systemUrl) {
return false;
}
if (errorHandler.hasError()) {
return errorHandler.getValidationErrorHelp() ?? false;
}
if (service && lastService?.servicePath !== service.servicePath) {
lastService = service;
return getServiceDetails(service, answers.systemUrl, connectValidator);
// Dont keep requesting the same service details
if (service && previousService?.servicePath !== service.servicePath) {
previousService = service;
return getServiceDetails(service, systemUrl, connectValidator);
}
return true;
}
Expand All @@ -239,7 +235,7 @@ export function getAbapOnPremQuestions(
};
}
*/
if (!errorHandler.hasError(true) && answers.serviceSelection && answers.systemUrl) {
if (answers.serviceSelection && answers.systemUrl) {
const result = await getServiceDetails(
answers.serviceSelection,
answers.systemUrl,
Expand Down Expand Up @@ -272,11 +268,14 @@ async function getServiceDetails(
systemUrl: string,
connectionValidator: ConnectionValidator
): Promise<string | boolean> {
// serviceType = await getServiceType(sapSystem, choice.serviceType, service);
const serviceCatalog = connectionValidator.catalogs[service.serviceODataVersion];
if (serviceCatalog instanceof V2CatalogService) {
await getServiceType(service.servicePath, service.serviceType, serviceCatalog);
}
const serviceResult = await getServiceMetadata(
service.servicePath,
connectionValidator.catalogs[service.serviceODataVersion],
connectionValidator.axiosConfig
serviceCatalog,
connectionValidator.serviceProvider
);
if (typeof serviceResult === 'string') {
return serviceResult;
Expand All @@ -289,39 +288,3 @@ async function getServiceDetails(
PromptState.odataService.origin = systemUrl;
return true;
}

/**
* Gets the service metadata and annotations for the specified service path.
*
* @param servicePath service path
* @param catalog the catalog service used to get the annotations
* @param axiosConfig the axios configuration used to create the odata service provider when getting the metadata
* @returns Promise<string | boolean>, string error message or true if successful
*/
async function getServiceMetadata(
servicePath: string,
catalog: CatalogService,
axiosConfig: AxiosRequestConfig
): Promise<{ annotations: Annotations[]; metadata: string; serviceProvider: ServiceProvider } | string> {
let annotations: Annotations[] = [];
try {
try {
annotations = await catalog.getAnnotations({ path: servicePath });
} catch {
LoggerHelper.logger.info(t('prompts.validationMessages.noAnnotations'));
}
// todo: Do we really need to create a new service provider for each service? We already have a catalog service connection
const serviceProvider = create(axiosConfig);
const odataService = serviceProvider.service(servicePath);
LoggerHelper.attachAxiosLogger(serviceProvider.interceptors);
const metadata = await odataService.metadata();
return {
annotations,
metadata,
serviceProvider
};
} catch (error) {
LoggerHelper.logger.error(`An error occurred while getting service metadata for service : ${servicePath}`);
return t('errors.serviceMetadataError', { servicePath });
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import type { CatalogService, ODataServiceInfo } from '@sap-ux/axios-extension';
import type { V2CatalogService } from '@sap-ux/axios-extension';
import {
ServiceType,
type Annotations,
type CatalogService,
type ODataServiceInfo,
type ServiceProvider
} from '@sap-ux/axios-extension';
import type { ListChoiceOptions } from 'inquirer';
import { t } from '../../../../i18n';
import LoggerHelper from '../../../logger-helper';
import type { ServiceAnswer } from './questions';

Expand All @@ -8,12 +16,17 @@ const nonUIServicePaths = ['/IWBEP/COMMON/'];
/**
* Builds and formats the service choices list.
*
* @param serviceInfos service information to build the choices from. Services with a service id contains=ing '/IWBEP/COMMON' are ignored.
* @param serviceInfos service information to build the choices from. Services with a service id containing '/IWBEP/COMMON' are ignored.
* @returns service choices list
*/
const createServiceChoices = (serviceInfos?: ODataServiceInfo[]): ListChoiceOptions<ServiceAnswer>[] => {
const choices: ListChoiceOptions<ServiceAnswer>[] = [];
//const isLogTrace = LoggerHelper.logger. === 'trace';
// Provide additional service information in trace mode (YUI only)
let isLogTrace = false;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Only specific loggers have this method
if (typeof (LoggerHelper.logger as any).getLogLevel === 'function') {
isLogTrace = (LoggerHelper.logger as any).getLogLevel() === 'trace';
}

serviceInfos
// Exclude non-UI compatible services
Expand All @@ -24,21 +37,21 @@ const createServiceChoices = (serviceInfos?: ODataServiceInfo[]): ListChoiceOpti

serviceName = `${serviceName} (${service.serviceVersion}) - OData V${service.odataVersion}`;

/* if (isLogTrace) {
serviceName = `${serviceName} Service Type: ${service.ServiceType}`;
} */
if (isLogTrace) {
serviceName = `${serviceName} Service Type: ${service.serviceType}`;
}

choices?.push({
choices.push({
name: serviceName,
value: {
servicePath,
serviceODataVersion: service.odataVersion,
toString: () => serviceName,
serviceType: (service as any).ServiceType ?? 'Not implemented' // Not implemented yet
serviceType: service.serviceType
}
}) as ListChoiceOptions<ServiceAnswer>;
});
return choices;
return choices.sort((a, b) => a.name!.localeCompare(b.name!));
};

/**
Expand All @@ -64,3 +77,62 @@ export async function getServiceChoices(catalogs: CatalogService[]): Promise<Lis

return createServiceChoices(flatServices);
}

/**
* Gets the service metadata and annotations for the specified service path.
*
* @param servicePath service path
* @param catalog the catalog service used to get the annotations for the specified service path
* @param serviceProvider the service provider for the connected system
* @returns Promise<string | boolean>, string error message or true if successful
*/
export async function getServiceMetadata(
servicePath: string,
catalog: CatalogService,
serviceProvider: ServiceProvider
): Promise<{ annotations: Annotations[]; metadata: string; serviceProvider: ServiceProvider } | string> {
let annotations: Annotations[] = [];
try {
try {
annotations = await catalog.getAnnotations({ path: servicePath });
} catch {
LoggerHelper.logger.info(t('prompts.validationMessages.noAnnotations'));
}

const odataService = serviceProvider.service(servicePath);
const metadata = await odataService.metadata();
return {
annotations,
metadata,
serviceProvider
};
} catch (error) {
LoggerHelper.logger.error(
`An error occurred while getting service metadata for service : ${servicePath}, error: ${error}`
);
return t('errors.serviceMetadataError', { servicePath });
}
}

/**
* Get service type for 'Not Determined' services from `ServiceTypeForHUBServices()`
*
* @param servicePath service path
* @param serviceType service type
* @param catalog the catalog service used to get the service type for the specified service path
* @returns service type
*/
export async function getServiceType(
servicePath: string,
serviceType: string | undefined,
catalog: V2CatalogService
): Promise<string | undefined> {
if (serviceType === ServiceType.NotDetermined) {
try {
serviceType = (await catalog.getServiceType(servicePath)) ?? ServiceType.NotDetermined;
} catch (e) {
LoggerHelper.logger.error(t('errors.serviceTypeRequestError', { error: e.message }));
}
}
return serviceType;
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,8 @@
"urlRedirect": "The service URL is redirecting",
"certValidationRequired": "Certificate validation is required to continue.",
"exitingGeneration": "Exiting generation. {{exitReason}}",
"serviceMetadataError": "An error occurred reading service metadata for service: {{- servicePath}}. See log for more details."
"serviceMetadataError": "An error occurred reading service metadata for service: {{- servicePath}}. See log for more details.",
"serviceTypeRequestError": "Error retrieving service type: {{- error}}"
},
"texts": {
"anExpiredCert": "an expired",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ describe('API tests', () => {
"choices": [Function],
"default": [Function],
"guiOptions": {
"applyDefaultWhenDirty": true,
"breadcrumb": "Service",
"mandatory": true,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,4 +278,8 @@ describe('ConnectionValidator', () => {
});
expect(getODataServiceSpy).toHaveBeenCalledTimes(2);
});

test('should update axios-config with sap-client with calling validateAuth', async () => {
// todo:...
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ describe('getQuestions', () => {
"choices": [Function],
"default": [Function],
"guiOptions": {
"applyDefaultWhenDirty": true,
"breadcrumb": "Service",
"mandatory": true,
},
Expand Down
Loading
Loading