Skip to content

Commit

Permalink
feat: enable offline mode (#576)
Browse files Browse the repository at this point in the history
* feat: enable offline mode

* fix: include all schema

* fix: review comments
  • Loading branch information
marufrasully authored Mar 22, 2023
1 parent cdbbd0b commit 5a28f9f
Show file tree
Hide file tree
Showing 32 changed files with 11,173 additions and 108 deletions.
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

0 comments on commit 5a28f9f

Please sign in to comment.