Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions packages/context/api.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export {
getContext,
isContext,
getCDNBaseUrl,
initializeManifestData,
initializeUI5YamlData,
Expand Down
7 changes: 3 additions & 4 deletions packages/context/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,11 @@
"@sap-ux/edmx-parser": "0.5.13",
"@sap-ux/project-access": "1.1.1",
"@ui5-language-assistant/logic-utils": "4.0.5",
"@ui5-language-assistant/settings": "4.0.5",
"fs-extra": "10.1.0",
"globby": "11.1.0",
"https-proxy-agent": "5.0.1",
"js-yaml": "4.1.0",
"lodash": "4.17.21",
"node-fetch": "3.2.10",
"proxy-from-env": "1.1.0",
"semver": "7.3.7",
"vscode-languageserver": "8.0.2",
"vscode-uri": "2.1.2"
Expand All @@ -41,7 +39,8 @@
"@ui5-language-assistant/test-framework": "4.0.5",
"@ui5-language-assistant/test-utils": "4.0.5",
"rimraf": "3.0.2",
"tmp-promise": "3.0.2"
"tmp-promise": "3.0.2",
"proxyquire": "2.1.3"
},
"scripts": {
"ci": "npm-run-all clean compile lint coverage",
Expand Down
38 changes: 27 additions & 11 deletions packages/context/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,33 @@ export {
export async function getContext(
documentPath: string,
modelCachePath?: string
): Promise<Context> {
const manifestDetails = await getManifestDetails(documentPath);
const yamlDetails = await getYamlDetails(documentPath);
const ui5Model = await getSemanticModel(
modelCachePath,
yamlDetails.framework,
manifestDetails.minUI5Version
);
const services = await getServices(documentPath);
const customViewId = await getCustomViewId(documentPath);
return { manifestDetails, yamlDetails, ui5Model, services, customViewId };
): Promise<Context | Error> {
try {
const manifestDetails = await getManifestDetails(documentPath);
const yamlDetails = await getYamlDetails(documentPath);
const ui5Model = await getSemanticModel(
modelCachePath,
yamlDetails.framework,
manifestDetails.minUI5Version
);
const services = await getServices(documentPath);
const customViewId = await getCustomViewId(documentPath);
return { manifestDetails, yamlDetails, ui5Model, services, customViewId };
} catch (error) {
return error as Error;
}
}

/**
* Checks if data is context or an error
*/
export const isContext = (
data: Context | (Error & { code?: string })
): data is Context => {
if ((data as Context).ui5Model) {
return true;
}
return false;
};

export const DEFAULT_I18N_NAMESPACE = "translation";
6 changes: 1 addition & 5 deletions packages/context/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type {
} from "@ui5-language-assistant/semantic-model-types";
import { ConvertedMetadata } from "@sap-ux/vocabularies-types";
import type { Manifest } from "@sap-ux/project-access";
import { FetchResponse } from "@ui5-language-assistant/logic-utils";

export const DEFAULT_UI5_FRAMEWORK = "SAPUI5";
export const DEFAULT_UI5_VERSION = "1.71.49";
Expand Down Expand Up @@ -111,9 +112,4 @@ export type CAPProjectKind = "Java" | "NodeJS";
export type ProjectKind = CAPProjectKind | "UI5";
export type Project = UI5Project | CAPProject;
export type ProjectType = typeof UI5_PROJECT_TYPE | typeof CAP_PROJECT_TYPE;
export type FetchResponse = {
ok: boolean;
status: number;
json: () => Promise<unknown>;
};
export type Fetcher = (url: string) => Promise<FetchResponse>;
10 changes: 7 additions & 3 deletions packages/context/src/ui5-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
TypeNameFix,
} from "@ui5-language-assistant/semantic-model";
import { Fetcher } from "./types";
import fetch from "./fetch";
import { fetch } from "@ui5-language-assistant/logic-utils";
import {
getLibraryAPIJsonUrl,
getLogger,
Expand Down Expand Up @@ -124,7 +124,11 @@ async function createSemanticModelWithFetcher(
// If the file doesn't exist in the cache (or we couldn't read it), fetch it from the network
if (apiJson === undefined) {
getLogger().info("No cache found for UI5 lib", { libName });
const url = getLibraryAPIJsonUrl(framework, version as string, libName);
const url = await getLibraryAPIJsonUrl(
framework,
version as string,
libName
);
const response = await fetcher(url);
if (response.ok) {
apiJson = await response.json();
Expand Down Expand Up @@ -256,7 +260,7 @@ async function getVersionInfo(
}
let versionInfo = await readFromCache(cacheFilePath);
if (versionInfo === undefined) {
const url = getVersionInfoUrl(framework, version);
const url = await getVersionInfoUrl(framework, version);
const response = await fetcher(url);
if (response.ok) {
versionInfo = await response.json();
Expand Down
37 changes: 29 additions & 8 deletions packages/context/src/utils/ui5.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,31 @@
import { UI5Framework } from "@ui5-language-assistant/semantic-model-types";
import { UI5_FRAMEWORK_CDN_BASE_URL } from "../types";
import { getLogger } from "../utils";
import { tryFetch, getLocalUrl } from "@ui5-language-assistant/logic-utils";

export function getCDNBaseUrl(
/**
* Get CDN URL for UI5 framework. If a URL for `SAPUI5 Web Server` is maintained in settings, it appends UI5 version if available and tries to check if this URL is responding and return it,
* if it fails, it appends UI5 version if available to a public URL and return it
*
* @param framework UI5 framework e.g OpenUI5" | "SAPUI5"
* @param version min ui5 version specified in manifest.json file
*/
export async function getCDNBaseUrl(
framework: UI5Framework,
version: string | undefined
): string {
): Promise<string> {
const localUrl = getLocalUrl(version);
if (localUrl) {
const response = await tryFetch(localUrl);
if (response) {
return localUrl;
}

getLogger().info("Failed to load. Will try over internet.", {
localUrl,
});
}

let url = UI5_FRAMEWORK_CDN_BASE_URL[framework];
if (version) {
url += `${version}/`;
Expand All @@ -16,20 +37,20 @@ export function getVersionJsonUrl(framework: UI5Framework): string {
return `${UI5_FRAMEWORK_CDN_BASE_URL[framework]}version.json`;
}

export function getVersionInfoUrl(
export async function getVersionInfoUrl(
framework: UI5Framework,
version: string
): string {
const cdnBaseUrl = getCDNBaseUrl(framework, version);
): Promise<string> {
const cdnBaseUrl = await getCDNBaseUrl(framework, version);
return `${cdnBaseUrl}resources/sap-ui-version.json`;
}

export function getLibraryAPIJsonUrl(
export async function getLibraryAPIJsonUrl(
framework: UI5Framework,
version: string,
libName: string
): string {
const cdnBaseUrl = getCDNBaseUrl(framework, version);
): Promise<string> {
const cdnBaseUrl = await getCDNBaseUrl(framework, version);
const baseUrl = `${cdnBaseUrl}test-resources/`;
const suffix = "/designtime/api.json";
return baseUrl + libName.replace(/\./g, "/") + suffix;
Expand Down
38 changes: 37 additions & 1 deletion packages/context/test/api-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import * as ui5Yaml from "../src/ui5-yaml";
import * as ui5Model from "../src/ui5-model";
import * as services from "../src/services";
import { UI5SemanticModel } from "@ui5-language-assistant/semantic-model-types";
import { getContext } from "../src/api";
import { getContext, isContext } from "../src/api";
import type { Context } from "../src/types";

describe("context", () => {
afterEach(() => {
Expand Down Expand Up @@ -47,5 +48,40 @@ describe("context", () => {
"ui5Model"
);
});
it("throw connection error", async () => {
const getManifestDetailsStub = stub(
manifest,
"getManifestDetails"
).resolves({
mainServicePath: "/",
customViews: {},
flexEnabled: false,
minUI5Version: undefined,
});
const getYamlDetailsStub = stub(ui5Yaml, "getYamlDetails").resolves({
framework: "OpenUI5",
version: undefined,
});
const getSemanticModelStub = stub(ui5Model, "getSemanticModel").throws({
code: "ENOTFOUND",
});
const result = await getContext("path/to/xml/file");
expect(getManifestDetailsStub).to.have.been.called;
expect(getYamlDetailsStub).to.have.been.called;
expect(getSemanticModelStub).to.have.been.called;
expect(result).to.have.keys("code");
});
});
context("isContext", () => {
it("check true", () => {
const result = isContext({ ui5Model: {} } as Context);
expect(result).to.be.true;
});
it("check false", () => {
const result = isContext({ code: "ENOTFOUND" } as Error & {
code?: string;
});
expect(result).to.be.false;
});
});
});
57 changes: 57 additions & 0 deletions packages/context/test/utils/ui5-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { restore, fake } from "sinon";
import { join } from "path";
import proxyquire from "proxyquire";
import { expect } from "chai";
import { getCDNBaseUrl } from "../../src/utils/ui5";

describe("ui5", () => {
afterEach(() => {
restore();
});
context("getCDNBaseUrl", () => {
it("get CDN without local url [with version]", async () => {
const result = await getCDNBaseUrl("SAPUI5", "1.111.0");
expect(result).to.be.equal("https://ui5.sap.com/1.111.0/");
});
it("get CDN without local url [without version]", async () => {
const result = await getCDNBaseUrl("SAPUI5", undefined);
expect(result).to.be.equal("https://ui5.sap.com/");
});
it("get CDN with local url", async () => {
const filePath = join(__dirname, "..", "..", "src", "utils", "ui5");
const fakeGetLocalUrl = fake.returns("http://localhost:3000/1.111.0/");
const fakeTryFetch = fake.resolves({ ok: true });
const ui5Module = proxyquire
.noPreserveCache()
.noCallThru()
.load(filePath, {
"@ui5-language-assistant/logic-utils": {
getLocalUrl: fakeGetLocalUrl,
tryFetch: fakeTryFetch,
},
});
const result = await ui5Module.getCDNBaseUrl("SAPUI5", "1.111.0");
expect(fakeGetLocalUrl).to.have.been.called;
expect(fakeTryFetch).to.have.been.called;
expect(result).to.be.equal("http://localhost:3000/1.111.0/");
});
it("get CDN with local url [fetch not responding => fall back to public]", async () => {
const filePath = join(__dirname, "..", "..", "src", "utils", "ui5");
const fakeGetLocalUrl = fake.returns("http://localhost:3000/1.111.0/");
const fakeTryFetch = fake.resolves(undefined);
const ui5Module = proxyquire
.noPreserveCache()
.noCallThru()
.load(filePath, {
"@ui5-language-assistant/logic-utils": {
getLocalUrl: fakeGetLocalUrl,
tryFetch: fakeTryFetch,
},
});
const result = await ui5Module.getCDNBaseUrl("SAPUI5", "1.111.0");
expect(fakeGetLocalUrl).to.have.been.called;
expect(fakeTryFetch).to.have.been.called;
expect(result).to.be.equal("https://ui5.sap.com/1.111.0/");
});
});
});
7 changes: 4 additions & 3 deletions packages/fe/test/services/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { AnnotationIssue, getCompletionItems } from "../../src/api";
import { CompletionItem } from "vscode-languageserver-types";
import { TestFramework } from "@ui5-language-assistant/test-framework";
import { getContext, Context } from "@ui5-language-assistant/context";
import { getContext } from "@ui5-language-assistant/context";
import type { Context } from "@ui5-language-assistant/context";
import { validateXMLView } from "@ui5-language-assistant/xml-views-validation";

import { CURSOR_ANCHOR } from "@ui5-language-assistant/test-framework";
Expand Down Expand Up @@ -52,7 +53,7 @@ export const getViewCompletionProvider = (
content,
offset
);
const context = await getContext(documentPath);
const context = (await getContext(documentPath)) as Context;

result = getCompletionItems({
ast,
Expand Down Expand Up @@ -102,7 +103,7 @@ export const getViewValidator = (
insertAfter: "<content>",
});
const { ast } = await framework.readFile(viewFilePathSegments);
const context = await getContext(documentPath);
const context = (await getContext(documentPath)) as Context;
result = validateXMLView({
validators: {
attribute: [validator],
Expand Down
Loading