@geode/opengeodeweb-front 10.11.0 → 10.12.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.oxlintrc.json CHANGED
@@ -122,7 +122,8 @@
122
122
  "max-statements": "off",
123
123
  "vitest/prefer-to-be-truthy": "off",
124
124
  "vitest/prefer-to-be-falsy": "off",
125
- "vitest/require-test-timeout": "warn"
125
+ "vitest/require-test-timeout": "warn",
126
+ "vitest/prefer-importing-vitest-globals": "off"
126
127
  }
127
128
  },
128
129
  {
@@ -14,8 +14,8 @@ const toggle_loading = useToggle(loading);
14
14
  async function get_inspection_results() {
15
15
  toggle_loading();
16
16
  const params = {
17
- geode_object_type: geode_object_type,
18
- filename: filename,
17
+ geode_object_type,
18
+ filename,
19
19
  };
20
20
  const geodeStore = useGeodeStore();
21
21
 
@@ -2,6 +2,7 @@
2
2
  import Loading from "@ogw_front/components/Loading";
3
3
  import Recaptcha from "@ogw_front/components/Recaptcha";
4
4
  import { Status } from "@ogw_front/utils/status";
5
+ import { appMode } from "@ogw_front/utils/local/app_mode";
5
6
  import { useInfraStore } from "@ogw_front/stores/infra";
6
7
 
7
8
  const { logo } = defineProps({
@@ -12,22 +13,16 @@ const { logo } = defineProps({
12
13
  });
13
14
 
14
15
  const infraStore = useInfraStore();
15
-
16
- watch(
17
- () => infraStore.is_captcha_validated,
18
- (value, oldValue) => {
19
- if (value && !oldValue && import.meta.client) {
20
- infraStore.create_backend();
21
- }
22
- },
23
- );
16
+ if (infraStore.app_mode !== appMode.CLOUD) {
17
+ infraStore.create_backend();
18
+ }
24
19
  </script>
25
20
 
26
21
  <template>
27
22
  <v-container class="justify">
28
23
  <v-row align-content="center" align="center">
29
24
  <v-col
30
- v-if="!infraStore.is_captcha_validated"
25
+ v-if="!infraStore.status == Status.NOT_CREATED"
31
26
  class="align"
32
27
  cols="12"
33
28
  align-self="center"
@@ -1,9 +1,6 @@
1
1
  <script setup>
2
- import { appMode } from "@ogw_front/utils/local/app_mode";
3
2
  import { useInfraStore } from "@ogw_front/stores/infra";
4
3
 
5
- const RESPONSE_STATUS_OK = 200;
6
-
7
4
  const { button_label, button_color, color } = defineProps({
8
5
  button_label: {
9
6
  type: String,
@@ -20,7 +17,7 @@ const { button_label, button_color, color } = defineProps({
20
17
  required: false,
21
18
  },
22
19
  });
23
- const infraStore = useInfraStore();
20
+
24
21
  const name = ref("");
25
22
  const email = ref("");
26
23
  const launch = ref(false);
@@ -40,26 +37,9 @@ const emailRules = [
40
37
  },
41
38
  ];
42
39
 
43
- onMounted(() => {
44
- if (
45
- import.meta.client &&
46
- (process.env.NODE_ENV !== "production" || infraStore.app_mode !== appMode.CLOUD)
47
- ) {
48
- infraStore.$patch({ is_captcha_validated: true });
49
- }
50
- });
51
- async function submit_recaptcha() {
52
- const response = await $fetch.raw(`/.netlify/functions/recaptcha`, {
53
- method: "POST",
54
- body: {
55
- name: name.value,
56
- email: email.value,
57
- launch: launch.value,
58
- },
59
- });
60
- infraStore.$patch({
61
- is_captcha_validated: response.status === RESPONSE_STATUS_OK,
62
- });
40
+ function submit() {
41
+ const infraStore = useInfraStore();
42
+ return infraStore.create_backend(name.value, email.value, launch.value);
63
43
  }
64
44
  </script>
65
45
 
@@ -89,7 +69,7 @@ async function submit_recaptcha() {
89
69
  </VRow>
90
70
  <VRow align="center" justify="center">
91
71
  <VCol cols="4" class="d-flex justify-center align-center">
92
- <VBtn :text="button_label" :color="color || button_color" @click="submit_recaptcha" />
72
+ <VBtn :text="button_label" :color="color || button_color" @click="submit" />
93
73
  </VCol>
94
74
  </VRow>
95
75
  </template>
@@ -27,7 +27,7 @@ onMounted(() => {
27
27
  function getTextureCoordinates() {
28
28
  geodeStore.request(
29
29
  back_schemas.opengeodeweb_back.texture_coordinates,
30
- { id: id },
30
+ { id },
31
31
  {
32
32
  response_function: (response) => {
33
33
  texture_coordinates.value = response.texture_coordinates;
@@ -0,0 +1,63 @@
1
+ import { Status } from "@ogw_front/utils/status";
2
+ import { useAppStore } from "./app";
3
+ import { useFeedbackStore } from "./feedback";
4
+ import { useInfraStore } from "./infra";
5
+
6
+ export const useCloudStore = defineStore("cloud", {
7
+ state: () => ({
8
+ status: Status.NOT_CONNECTED,
9
+ }),
10
+ actions: {
11
+ launch(name, email, launch) {
12
+ this.status = Status.CONNECTING;
13
+ console.log("[CLOUD] Launching cloud backend...");
14
+ const schema = {
15
+ $id: "/api/app/run_cloud",
16
+ methods: ["POST"],
17
+ type: "object",
18
+ properties: {
19
+ name: { type: "string" },
20
+ email: { type: "string" },
21
+ launch: { type: "boolean" },
22
+ },
23
+ required: ["name", "email", "launch"],
24
+ additionalProperties: true,
25
+ };
26
+ const params = {
27
+ name,
28
+ email,
29
+ launch,
30
+ };
31
+ console.log("[CLOUD] params", params);
32
+ const appStore = useAppStore();
33
+ const feedbackStore = useFeedbackStore();
34
+ return appStore.request(schema, params, {
35
+ request_error_function: () => {
36
+ feedbackStore.$patch({ server_error: true });
37
+ this.status = Status.NOT_CONNECTED;
38
+ },
39
+ response_function: (response) => {
40
+ feedbackStore.$patch({ server_error: false });
41
+ console.log(`[CLOUD] Cloud launched on ${response.url}`);
42
+ this.status = Status.CONNECTED;
43
+ const infraStore = useInfraStore();
44
+ infraStore.$patch({
45
+ domain_name: response.url,
46
+ });
47
+ },
48
+ response_error_function: () => {
49
+ feedbackStore.$patch({ server_error: true });
50
+ this.status = Status.NOT_CONNECTED;
51
+ },
52
+ });
53
+ },
54
+ connect() {
55
+ console.log("[CLOUD] Cloud connected");
56
+ this.status = Status.CONNECTED;
57
+ return Promise.resolve();
58
+ },
59
+ },
60
+ share: {
61
+ omit: ["status"],
62
+ },
63
+ });
@@ -33,10 +33,7 @@ export const useGeodeStore = defineStore("geode", {
33
33
  const infraStore = useInfraStore();
34
34
  let geode_url = `${this.protocol}://${infraStore.domain_name}:${this.port}`;
35
35
  if (infraStore.app_mode === appMode.CLOUD) {
36
- if (infraStore.ID === "") {
37
- throw new Error("ID must not be empty in cloud mode");
38
- }
39
- geode_url += `/${infraStore.ID}/geode`;
36
+ geode_url += `/geode`;
40
37
  }
41
38
  return geode_url;
42
39
  },
@@ -2,23 +2,16 @@ import { Status } from "@ogw_front/utils/status";
2
2
  import { appMode } from "@ogw_front/utils/local/app_mode";
3
3
  import { registerRunningExtensions } from "@ogw_front/utils/extension";
4
4
  import { useAppStore } from "@ogw_front/stores/app";
5
- import { useLambdaStore } from "@ogw_front/stores/lambda";
5
+ import { useCloudStore } from "@ogw_front/stores/cloud";
6
6
 
7
7
  export const useInfraStore = defineStore("infra", {
8
8
  state: () => ({
9
9
  app_mode: useRuntimeConfig().public.MODE,
10
- ID: "",
11
- is_captcha_validated: false,
12
10
  status: Status.NOT_CREATED,
13
11
  microservices: [],
12
+ domain_name: "localhost",
14
13
  }),
15
14
  getters: {
16
- domain_name() {
17
- if (this.app_mode === appMode.CLOUD) {
18
- return useRuntimeConfig().public.API_URL;
19
- }
20
- return "localhost";
21
- },
22
15
  microservices_connected() {
23
16
  console.log("microservices", this.microservices);
24
17
  return this.microservices.every((store) => store.status === Status.CONNECTED);
@@ -37,7 +30,7 @@ export const useInfraStore = defineStore("infra", {
37
30
  console.log("[INFRA] Microservice registered:", store_name);
38
31
  }
39
32
  },
40
- create_backend() {
33
+ create_backend(name, email, launch) {
41
34
  console.log("[INFRA] Starting create_backend - Mode:", this.app_mode);
42
35
  console.log(
43
36
  "[INFRA] Registered microservices:",
@@ -47,18 +40,15 @@ export const useInfraStore = defineStore("infra", {
47
40
  return;
48
41
  }
49
42
  return navigator.locks.request("infra.create_backend", async () => {
50
- this.status = Status.CREATING;
51
43
  if (this.status === Status.CREATED) {
52
44
  return;
53
45
  }
46
+ this.status = Status.CREATING;
54
47
  console.log("[INFRA] Lock granted for create_backend");
55
48
  if (this.app_mode === appMode.CLOUD) {
56
- console.log("[INFRA] CLOUD mode - Launching lambda...");
57
- const lambdaStore = useLambdaStore();
58
- this.ID = await lambdaStore.launch();
59
- console.log("[INFRA] Lambda launched successfully");
49
+ const cloudStore = useCloudStore();
50
+ await cloudStore.launch(name, email, launch);
60
51
  } else {
61
- console.log(`[INFRA] ${this.app_mode} mode - Launching microservices...`);
62
52
  const appStore = useAppStore();
63
53
  await appStore.createProjectFolder();
64
54
  if (this.app_mode === appMode.DESKTOP) {
@@ -49,10 +49,7 @@ export const useViewerStore = defineStore(
49
49
  const base_url = computed(() => {
50
50
  let viewer_url = `${protocol.value}://${infraStore.domain_name}:${port.value}`;
51
51
  if (infraStore.app_mode === appMode.CLOUD) {
52
- if (infraStore.ID === "") {
53
- throw new Error("ID must not be empty in cloud mode");
54
- }
55
- viewer_url += `/${infraStore.ID}/viewer`;
52
+ viewer_url += `/viewer`;
56
53
  }
57
54
  viewer_url += "/ws";
58
55
  return viewer_url;
@@ -16,7 +16,7 @@ async function upload_file(
16
16
 
17
17
  const request_options = {
18
18
  method: "PUT",
19
- body: body,
19
+ body,
20
20
  };
21
21
  microservice.start_request();
22
22
  return await $fetch(route, {
package/nuxt.config.js CHANGED
@@ -1,22 +1,19 @@
1
1
  // Node imports
2
- import path, { dirname } from "node:path";
3
- import { fileURLToPath } from "node:url";
2
+ import path from "node:path";
4
3
 
5
4
  // Local imports
6
5
  import package_json from "./package.json";
7
6
 
8
- const __dirname = dirname(fileURLToPath(import.meta.url));
7
+ const __dirname = import.meta.dirname;
9
8
 
10
9
  export default defineNuxtConfig({
11
10
  runtimeConfig: {
12
11
  public: {
13
- API_URL: "api.geode-solutions.com",
14
12
  COMMAND_BACK: "opengeodeweb-back",
15
13
  COMMAND_VIEWER: "opengeodeweb-viewer",
16
14
  NUXT_ROOT_PATH: __dirname,
17
15
  MODE: process.env.MODE || "CLOUD",
18
16
  PROJECT: package_json.name,
19
- SITE_BRANCH: process.env.NODE_ENV === "production" ? process.env.SITE_BRANCH : "",
20
17
  },
21
18
  },
22
19
 
@@ -28,6 +25,7 @@ export default defineNuxtConfig({
28
25
  alias: {
29
26
  "@ogw_front": path.resolve(__dirname, "app"),
30
27
  "@ogw_internal": path.resolve(__dirname, "internal"),
28
+ "@ogw_server": path.resolve(__dirname, "server"),
31
29
  "@ogw_tests": path.resolve(__dirname, "tests"),
32
30
  },
33
31
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geode/opengeodeweb-front",
3
- "version": "10.11.0",
3
+ "version": "10.12.0-rc.1",
4
4
  "description": "OpenSource Vue/Nuxt/Pinia/Vuetify framework for web applications",
5
5
  "homepage": "https://github.com/Geode-solutions/OpenGeodeWeb-Front",
6
6
  "bugs": {
@@ -34,8 +34,9 @@
34
34
  "build": ""
35
35
  },
36
36
  "dependencies": {
37
- "@geode/opengeodeweb-back": "latest",
38
- "@geode/opengeodeweb-viewer": "latest",
37
+ "@geode/opengeodeweb-back": "next",
38
+ "@geode/opengeodeweb-viewer": "next",
39
+ "@google-cloud/run": "3.2.0",
39
40
  "@kitware/vtk.js": "33.3.0",
40
41
  "@mdi/font": "7.4.47",
41
42
  "@pinia/nuxt": "0.11.3",
@@ -48,6 +49,8 @@
48
49
  "conf": "15.1.0",
49
50
  "dexie": "4.2.1",
50
51
  "get-port-please": "3.2.0",
52
+ "google-auth-library": "10.6.1",
53
+ "googleapis": "171.4.0",
51
54
  "js-file-download": "0.4.12",
52
55
  "jszip": "3.10.1",
53
56
  "node-stream-zip": "1.15.0",
@@ -70,17 +73,9 @@
70
73
  "@pinia/testing": "1.0.3",
71
74
  "@vitejs/plugin-vue": "6.0.4",
72
75
  "@vue/test-utils": "2.4.6",
73
- "eslint": "9.26.0",
74
- "eslint-plugin-import": "2.31.0",
75
- "eslint-plugin-nuxt": "4.0.0",
76
- "eslint-plugin-prettier": "5.4.0",
77
- "eslint-plugin-prettier-vue": "5.0.0",
78
- "eslint-plugin-vue": "10.1.0",
79
- "eslint-plugin-vuetify": "2.5.2",
80
76
  "happy-dom": "20.0.11",
81
77
  "msw": "2.11.1",
82
78
  "playwright-core": "1.52.0",
83
- "prettier": "3.3.3",
84
79
  "resize-observer-polyfill": "1.5.1",
85
80
  "unplugin-auto-import": "20.0.0",
86
81
  "vite": "7.3.1",
@@ -0,0 +1,46 @@
1
+ // Node imports
2
+
3
+ // Third party imports
4
+ import { createError, defineEventHandler, readBody } from "h3";
5
+ import { GoogleAuth } from "google-auth-library";
6
+ import { ServicesClient } from "@google-cloud/run";
7
+
8
+ // Local imports
9
+ import { artifactImages, checkRecaptchaParams, requestConfig } from "@ogw_server/utils/cloud";
10
+
11
+ export default defineEventHandler(async (event) => {
12
+ try {
13
+ const { name, email, launch } = await readBody(event);
14
+ if (!checkRecaptchaParams(name, email, launch)) {
15
+ return {
16
+ statusCode: 500,
17
+ body: JSON.stringify({ message: "INTERNAL_ERROR" }),
18
+ };
19
+ }
20
+ const credentials = JSON.parse(process.env.GOOGLE_CLOUD_KEY);
21
+ const location = "europe-west9";
22
+ const projectId = process.env.GOOGLE_CLOUD_PROJECT;
23
+ const parent = `projects/${projectId}/locations/${location}`;
24
+ const auth = new GoogleAuth({
25
+ credentials,
26
+ scopes: ["https://www.googleapis.com/auth/cloud-platform"],
27
+ });
28
+ const authClient = await auth.getClient();
29
+ const [routerImage, backImage, viewerImage] = await artifactImages(parent, authClient);
30
+ const request = requestConfig(parent, routerImage, backImage, viewerImage);
31
+ const runClient = new ServicesClient({ authClient });
32
+ const [operation] = await runClient.createService(request);
33
+ const [response] = await operation.promise();
34
+ console.log("Service URL created:", response.uri);
35
+ return {
36
+ statusCode: 200,
37
+ url: response.uri.replace(/^https?:\/\//i, ""),
38
+ };
39
+ } catch (error) {
40
+ console.log(error);
41
+ throw createError({
42
+ statusCode: 500,
43
+ statusMessage: error.message,
44
+ });
45
+ }
46
+ });
@@ -0,0 +1,96 @@
1
+ // Node imports
2
+
3
+ // Third party imports
4
+ import { google } from "googleapis";
5
+
6
+ // Local imports
7
+
8
+ function checkRecaptchaParams(name, email, launch) {
9
+ console.log("check_recaptcha_params", { name, email, launch });
10
+ return name === "" && email === "" && launch === false;
11
+ }
12
+
13
+ async function artifactImage(registry, parent, repo) {
14
+ const branch = process.env.BRANCH;
15
+ const [_, projectId] = parent.split("/");
16
+ const repository = `${parent}/repositories/ghcr/packages/geode-solutions%2F`;
17
+ const artifactRegistry = `europe-west9-docker.pkg.dev/${projectId}/ghcr/geode-solutions`;
18
+ const response = await registry.projects.locations.repositories.packages.tags.get({
19
+ name: `${repository}${repo}/tags/${branch}`,
20
+ });
21
+ const digest = response.data.version.split("/").pop();
22
+ const image = `${artifactRegistry}/${repo}@${digest}`;
23
+ console.log("Found image for", repo, image);
24
+ return image;
25
+ }
26
+
27
+ function artifactImages(parent, authClient) {
28
+ const projectName = process.env.PROJECT;
29
+ const registry = google.artifactregistry({
30
+ version: "v1",
31
+ auth: authClient,
32
+ });
33
+ return Promise.all([
34
+ artifactImage(registry, parent, "opengeodeweb-router"),
35
+ artifactImage(registry, parent, `${projectName}-back`),
36
+ artifactImage(registry, parent, `${projectName}-viewer`),
37
+ ]);
38
+ }
39
+
40
+ // oxlint-disable-next-line max-lines-per-function
41
+ function requestConfig(parent, routerImage, backImage, viewerImage) {
42
+ const resources = {
43
+ limits: {
44
+ cpu: "1000m",
45
+ memory: "512Mi",
46
+ },
47
+ };
48
+ const volumeMounts = {
49
+ name: "data",
50
+ mountPath: "/data",
51
+ };
52
+ return {
53
+ parent,
54
+ service: {
55
+ ingress: "INGRESS_TRAFFIC_ALL",
56
+ invokerIamDisabled: true,
57
+ scaling: {
58
+ scalingMode: "MANUAL",
59
+ manualInstanceCount: 1,
60
+ },
61
+ template: {
62
+ volumes: [
63
+ {
64
+ name: "data",
65
+ emptyDir: {
66
+ medium: "MEMORY",
67
+ },
68
+ },
69
+ ],
70
+ containers: [
71
+ {
72
+ image: routerImage,
73
+ ports: [
74
+ {
75
+ containerPort: 80,
76
+ },
77
+ ],
78
+ resources,
79
+ },
80
+ {
81
+ image: backImage,
82
+ resources,
83
+ volumeMounts: [volumeMounts],
84
+ },
85
+ {
86
+ image: viewerImage,
87
+ resources,
88
+ volumeMounts: [volumeMounts],
89
+ },
90
+ ],
91
+ },
92
+ },
93
+ };
94
+ }
95
+
96
+ export { checkRecaptchaParams, artifactImages, requestConfig };
@@ -85,7 +85,7 @@ describe(FileSelector, () => {
85
85
  },
86
86
  props: {
87
87
  multiple: false,
88
- files: files,
88
+ files,
89
89
  auto_upload: true,
90
90
  },
91
91
  });
@@ -107,7 +107,7 @@ describe(FileSelector, () => {
107
107
  },
108
108
  props: {
109
109
  multiple: false,
110
- files: files,
110
+ files,
111
111
  auto_upload: false,
112
112
  },
113
113
  });
@@ -86,7 +86,6 @@ const geodeStoreMock = {
86
86
  };
87
87
  const infraStoreMock = {
88
88
  app_mode: appMode.BROWSER,
89
- ID: "1234",
90
89
  };
91
90
  const viewerStoreMock = {
92
91
  ws_connect: vi.fn().mockResolvedValue(),
@@ -0,0 +1,89 @@
1
+ // Third party imports
2
+ import { beforeEach, describe, expect, expectTypeOf, test, vi } from "vitest";
3
+ import { registerEndpoint } from "@nuxt/test-utils/runtime";
4
+
5
+ // Local imports
6
+ import { Status } from "@ogw_front/utils/status";
7
+ import { setupActivePinia } from "@ogw_tests/utils";
8
+ import { useCloudStore } from "@ogw_front/stores/cloud";
9
+ import { useFeedbackStore } from "@ogw_front/stores/feedback";
10
+
11
+ // CONSTANTS
12
+ const PROJECT = "project";
13
+ const STATUS_500 = 500;
14
+
15
+ beforeEach(() => {
16
+ setupActivePinia();
17
+ });
18
+
19
+ function setupConfig() {
20
+ const config = useRuntimeConfig();
21
+ config.public.PROJECT = PROJECT;
22
+ }
23
+
24
+ describe("Cloud Store", () => {
25
+ describe("state", () => {
26
+ test("initial state", () => {
27
+ const cloudStore = useCloudStore();
28
+ expectTypeOf(cloudStore.status).toBeString();
29
+ expect(cloudStore.status).toBe(Status.NOT_CONNECTED);
30
+ });
31
+ });
32
+
33
+ describe("actions", () => {
34
+ describe("launch", () => {
35
+ const postFakeCall = vi.fn();
36
+
37
+ test("successful launch", async () => {
38
+ setupConfig();
39
+ const cloudStore = useCloudStore();
40
+ const feedbackStore = useFeedbackStore();
41
+
42
+ registerEndpoint("/api/app/run_cloud", {
43
+ method: "POST",
44
+ handler: postFakeCall,
45
+ });
46
+
47
+ postFakeCall.mockImplementation(() => ({
48
+ url: "http://test.com",
49
+ }));
50
+
51
+ await cloudStore.launch("", "", false);
52
+
53
+ expect(cloudStore.status).toBe(Status.CONNECTED);
54
+ expect(feedbackStore.server_error).toBe(false);
55
+ });
56
+
57
+ test("failed launch - error response", async () => {
58
+ setupConfig();
59
+ const cloudStore = useCloudStore();
60
+ const feedbackStore = useFeedbackStore();
61
+
62
+ registerEndpoint("/api/app/run_cloud", {
63
+ method: "POST",
64
+ handler: postFakeCall,
65
+ });
66
+
67
+ postFakeCall.mockImplementation(() => {
68
+ throw createError({
69
+ status: STATUS_500,
70
+ statusMessage: "Internal Server Error",
71
+ });
72
+ });
73
+
74
+ await expect(cloudStore.launch("", "", false)).rejects.toThrow("500 Internal Server Error");
75
+
76
+ expect(cloudStore.status).toBe(Status.NOT_CONNECTED);
77
+ expect(feedbackStore.server_error).toBe(true);
78
+ });
79
+ });
80
+
81
+ describe("connect", () => {
82
+ test("successful connect", async () => {
83
+ const cloudStore = useCloudStore();
84
+ await cloudStore.connect();
85
+ expect(cloudStore.status).toBe(Status.CONNECTED);
86
+ });
87
+ });
88
+ });
89
+ });
@@ -14,7 +14,6 @@ import { useInfraStore } from "@ogw_front/stores/infra";
14
14
  const PORT_443 = "443";
15
15
  const PORT_12 = "12";
16
16
  const PORT_5000 = "5000";
17
- const CLOUD_ID = "123456";
18
17
  const STATUS_500 = 500;
19
18
  const EXPECTED_ONE_REQUEST = 1;
20
19
  const EXPECTED_NO_REQUEST = 0;
@@ -88,18 +87,8 @@ describe("geode store", () => {
88
87
  const infraStore = useInfraStore();
89
88
  const geodeStore = useGeodeStore();
90
89
  infraStore.app_mode = appMode.CLOUD;
91
- infraStore.ID = CLOUD_ID;
92
90
  infraStore.domain_name = "example.com";
93
- expect(geodeStore.base_url).toBe(`https://example.com:${PORT_443}/${CLOUD_ID}/geode`);
94
- });
95
-
96
- test("app_mode CLOUD, ID empty", () => {
97
- const infraStore = useInfraStore();
98
- const geodeStore = useGeodeStore();
99
- infraStore.app_mode = appMode.CLOUD;
100
- infraStore.ID = "";
101
- infraStore.domain_name = "example.com";
102
- expect(() => geodeStore.base_url).toThrow("ID must not be empty in cloud mode");
91
+ expect(geodeStore.base_url).toBe(`https://example.com:${PORT_443}/geode`);
103
92
  });
104
93
  });
105
94
 
@@ -6,9 +6,9 @@ import { registerEndpoint } from "@nuxt/test-utils/runtime";
6
6
  import { Status } from "@ogw_front/utils/status";
7
7
  import { appMode } from "@ogw_front/utils/local/app_mode";
8
8
  import { setupActivePinia } from "@ogw_tests/utils";
9
+ import { useCloudStore } from "@ogw_front/stores/cloud";
9
10
  import { useGeodeStore } from "@ogw_front/stores/geode";
10
11
  import { useInfraStore } from "@ogw_front/stores/infra";
11
- import { useLambdaStore } from "@ogw_front/stores/lambda";
12
12
  import { useViewerStore } from "@ogw_front/stores/viewer";
13
13
 
14
14
  // Mock navigator.locks API
@@ -31,8 +31,7 @@ describe("Infra Store", () => {
31
31
  describe("state", () => {
32
32
  test("initial state", () => {
33
33
  const infraStore = useInfraStore();
34
- expectTypeOf(infraStore.ID).toBeString();
35
- expectTypeOf(infraStore.is_captcha_validated).toBeBoolean();
34
+ expectTypeOf(infraStore.domain_name).toBeString();
36
35
  expectTypeOf(infraStore.status).toBeString();
37
36
  });
38
37
  });
@@ -58,7 +57,7 @@ describe("Infra Store", () => {
58
57
  test("test app_mode CLOUD", () => {
59
58
  const infraStore = useInfraStore();
60
59
  infraStore.app_mode = appMode.CLOUD;
61
- expect(infraStore.domain_name).toBe("api.geode-solutions.com");
60
+ expect(infraStore.domain_name).toBe("localhost");
62
61
  });
63
62
  });
64
63
 
@@ -273,28 +272,27 @@ describe("Infra Store", () => {
273
272
  expect(infraStore.microservices.length).toBe(2);
274
273
  });
275
274
  });
275
+ });
276
276
 
277
- describe("create_backend", () => {
278
- // Test without microservices
279
- test("test with end-point", async () => {
280
- const infraStore = useInfraStore();
281
- const geodeStore = useGeodeStore();
282
- const viewerStore = useViewerStore();
283
- const lambdaStore = useLambdaStore();
284
-
285
- infraStore.app_mode = appMode.CLOUD;
286
- const ID = "123456";
287
- registerEndpoint(lambdaStore.base_url, {
288
- method: "POST",
289
- handler: () => ({ ID }),
290
- });
291
- await infraStore.create_backend();
292
- expect(infraStore.status).toBe(Status.CREATED);
293
- expect(infraStore.ID).toBe(ID);
294
-
295
- expect(geodeStore.status).toBe(Status.NOT_CONNECTED);
296
- expect(viewerStore.status).toBe(Status.NOT_CONNECTED);
277
+ describe("create_backend", () => {
278
+ // Test without microservices
279
+ test("test with end-point", async () => {
280
+ const infraStore = useInfraStore();
281
+ const geodeStore = useGeodeStore();
282
+ const viewerStore = useViewerStore();
283
+
284
+ infraStore.app_mode = appMode.CLOUD;
285
+ const url = "test.com";
286
+ registerEndpoint("/api/app/run_cloud", {
287
+ method: "POST",
288
+ handler: () => ({ url }),
297
289
  });
290
+ await infraStore.create_backend("", "", false);
291
+ expect(infraStore.status).toBe(Status.CREATED);
292
+ expect(infraStore.domain_name).toBe(url);
293
+
294
+ expect(geodeStore.status).toBe(Status.NOT_CONNECTED);
295
+ expect(viewerStore.status).toBe(Status.NOT_CONNECTED);
298
296
  });
299
297
  });
300
298
  });
@@ -111,18 +111,8 @@ describe("Viewer Store", () => {
111
111
  const infraStore = useInfraStore();
112
112
  const viewerStore = useViewerStore();
113
113
  infraStore.app_mode = appMode.CLOUD;
114
- infraStore.ID = "123456";
115
114
  infraStore.domain_name = "example.com";
116
- expect(viewerStore.base_url).toBe("wss://example.com:443/123456/viewer/ws");
117
- });
118
-
119
- test("test app_mode CLOUD, ID empty", () => {
120
- const infraStore = useInfraStore();
121
- const viewerStore = useViewerStore();
122
- infraStore.app_mode = appMode.CLOUD;
123
- infraStore.ID = "";
124
- infraStore.domain_name = "example.com";
125
- expect(() => viewerStore.base_url).toThrowError("ID must not be empty in cloud mode");
115
+ expect(viewerStore.base_url).toBe("wss://example.com:443/viewer/ws");
126
116
  });
127
117
  });
128
118
  describe("is_busy", () => {
@@ -1,9 +1,8 @@
1
1
  import { defineConfig } from "vitest/config";
2
2
  import { defineVitestProject } from "@nuxt/test-utils/config";
3
- import { fileURLToPath } from "node:url";
4
3
  import path from "node:path";
5
4
 
6
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
5
+ const __dirname = import.meta.dirname;
7
6
 
8
7
  const RETRIES = 3;
9
8
  const DEFAULT_RETRY = 0;
@@ -22,14 +21,13 @@ export default defineConfig({
22
21
  },
23
22
  },
24
23
  test: {
25
- globals: false,
26
24
  setupFiles: [path.resolve(__dirname, "./setup_indexeddb.js")],
27
25
  projects: [
28
26
  await defineVitestProject({
29
27
  test: {
30
28
  name: "unit",
31
- globals: false,
32
29
  include: ["tests/unit/**/*.test.js"],
30
+ globals: true,
33
31
  environment: "nuxt",
34
32
  testTimeout: TIMEOUTS.unit,
35
33
  setupFiles: [path.resolve(__dirname, "./setup_indexeddb.js")],
@@ -44,8 +42,8 @@ export default defineConfig({
44
42
  await defineVitestProject({
45
43
  test: {
46
44
  name: "integration",
47
- globals: false,
48
45
  include: ["tests/integration/**/*.test.js"],
46
+ globals: true,
49
47
  environment: "nuxt",
50
48
  fileParallelism: false,
51
49
  testTimeout: TIMEOUTS.integration,
package/.eslintrc.cjs DELETED
@@ -1,15 +0,0 @@
1
- // oxlint-disable-next-line import/no-commonjs
2
- module.exports = {
3
- root: true,
4
- env: {
5
- node: true,
6
- browser: true,
7
- es2021: true,
8
- },
9
- extends: [
10
- "eslint:recommended",
11
- "plugin:vue/recommended",
12
- "plugin:vuetify/recommended",
13
- "plugin:nuxt/recommended",
14
- ],
15
- };
@@ -1,57 +0,0 @@
1
- import { Status } from "@ogw_front/utils/status";
2
- import { useFeedbackStore } from "@ogw_front/stores/feedback";
3
-
4
- export const useLambdaStore = defineStore("lambda", {
5
- state: () => ({
6
- status: Status.NOT_CONNECTED,
7
- }),
8
- getters: {
9
- protocol() {
10
- return "https";
11
- },
12
- port() {
13
- return "443";
14
- },
15
- base_url() {
16
- const public_runtime_config = useRuntimeConfig().public;
17
- const domain_name = public_runtime_config.API_URL;
18
- const projectPath = `/${public_runtime_config.PROJECT}`;
19
- const url = `${this.protocol}://${domain_name}:${this.port}${public_runtime_config.SITE_BRANCH}${projectPath}/createbackend`;
20
- return url;
21
- },
22
- is_busy() {
23
- return false;
24
- },
25
- },
26
- actions: {
27
- async launch() {
28
- console.log("[LAMBDA] Launching lambda backend...");
29
- const feedbackStore = useFeedbackStore();
30
-
31
- const { data, error } = await useFetch(this.base_url, {
32
- method: "POST",
33
- });
34
-
35
- if (error.value || !data.value) {
36
- this.status = Status.NOT_CONNECTED;
37
- feedbackStore.server_error = true;
38
- console.error("[LAMBDA] Failed to launch lambda backend", error.value);
39
- throw new Error("Failed to launch lambda backend");
40
- }
41
-
42
- this.status = Status.CONNECTED;
43
- const id = data.value.ID;
44
-
45
- console.log("[LAMBDA] Lambda launched, ID:", id);
46
- return id;
47
- },
48
- connect() {
49
- console.log("[LAMBDA] Lambda connected");
50
- this.status = Status.CONNECTED;
51
- return Promise.resolve();
52
- },
53
- },
54
- share: {
55
- omit: ["status"],
56
- },
57
- });
@@ -1,24 +0,0 @@
1
- function check_recaptcha_params(name, email, launch) {
2
- console.log("check_recaptcha_params", { name, email, launch });
3
- if (name !== "") {
4
- return {
5
- statusCode: 500,
6
- body: JSON.stringify({ message: "INTERNAL_ERROR" }),
7
- };
8
- }
9
- if (email !== "") {
10
- return {
11
- statusCode: 500,
12
- body: JSON.stringify({ message: "INTERNAL_ERROR" }),
13
- };
14
- }
15
- if (launch !== false) {
16
- return {
17
- statusCode: 500,
18
- body: JSON.stringify({ message: "INTERNAL_ERROR" }),
19
- };
20
- }
21
- return { statusCode: 200, body: JSON.stringify({ message: "OK" }) };
22
- }
23
-
24
- export { check_recaptcha_params };
package/eslint.config.js DELETED
@@ -1,28 +0,0 @@
1
- import nuxt from "eslint-plugin-nuxt";
2
- import vue from "eslint-plugin-vue";
3
- import vuetify from "eslint-plugin-vuetify";
4
-
5
- // oxlint-disable-next-line import/no-anonymous-default-export
6
- export default [
7
- {
8
- files: ["**/*.{js,ts,vue}"],
9
- languageOptions: {
10
- ecmaVersion: 2021,
11
- sourceType: "module",
12
- globals: {
13
- window: "readonly",
14
- document: "readonly",
15
- },
16
- },
17
- plugins: {
18
- vue,
19
- vuetify,
20
- nuxt,
21
- },
22
- rules: {
23
- ...vue.configs.recommended.rules,
24
- ...vuetify.configs.recommended.rules,
25
- ...nuxt.configs.recommended.rules,
26
- },
27
- },
28
- ];
@@ -1,37 +0,0 @@
1
- import { describe, expect, test, vi } from "vitest";
2
-
3
- import Launcher from "@ogw_front/components/Launcher";
4
- import ResizeObserver from "resize-observer-polyfill";
5
- import { flushPromises } from "@vue/test-utils";
6
- import { mountSuspended } from "@nuxt/test-utils/runtime";
7
-
8
- import { setupActivePinia, vuetify } from "@ogw_tests/utils";
9
- import { useInfraStore } from "@ogw_front/stores/infra";
10
-
11
- // Mock navigator.locks API
12
- const mockLockRequest = vi.fn().mockImplementation(async (name, task) => await task({ name }));
13
-
14
- vi.stubGlobal("navigator", {
15
- ...navigator,
16
- locks: {
17
- request: mockLockRequest,
18
- },
19
- });
20
-
21
- globalThis.ResizeObserver = ResizeObserver;
22
-
23
- describe(Launcher, () => {
24
- test(`Mount`, async () => {
25
- const pinia = setupActivePinia();
26
- const infraStore = useInfraStore();
27
- const wrapper = await mountSuspended(Launcher, {
28
- global: {
29
- plugins: [vuetify, pinia],
30
- },
31
- });
32
- expect(wrapper.exists()).toBe(true);
33
- await infraStore.$patch({ is_captcha_validated: true });
34
- await flushPromises();
35
- expect(infraStore.create_backend).toHaveBeenCalled();
36
- });
37
- });
@@ -1,130 +0,0 @@
1
- // Third party imports
2
- import { beforeEach, describe, expect, expectTypeOf, test, vi } from "vitest";
3
- import { registerEndpoint } from "@nuxt/test-utils/runtime";
4
-
5
- // Local imports
6
- import { Status } from "@ogw_front/utils/status";
7
- import { setupActivePinia } from "@ogw_tests/utils";
8
- import { useFeedbackStore } from "@ogw_front/stores/feedback";
9
- import { useLambdaStore } from "@ogw_front/stores/lambda";
10
-
11
- // CONSTANTS
12
- const PORT_443 = "443";
13
- const API_URL = "api.example.com";
14
- const SITE_BRANCH = "/test";
15
- const PROJECT = "project";
16
- const TEST_ID = "test-id-123456";
17
- const STATUS_500 = 500;
18
-
19
- beforeEach(() => {
20
- setupActivePinia();
21
- });
22
-
23
- function setupConfig() {
24
- const config = useRuntimeConfig();
25
- config.public.API_URL = API_URL;
26
- config.public.SITE_BRANCH = SITE_BRANCH;
27
- config.public.PROJECT = PROJECT;
28
- }
29
-
30
- describe("Lambda Store", () => {
31
- describe("state", () => {
32
- test("initial state", () => {
33
- const lambdaStore = useLambdaStore();
34
- expectTypeOf(lambdaStore.status).toBeString();
35
- expect(lambdaStore.status).toBe(Status.NOT_CONNECTED);
36
- });
37
- });
38
-
39
- describe("getters", () => {
40
- describe("protocol", () => {
41
- test("test protocol is always https", () => {
42
- const lambdaStore = useLambdaStore();
43
- expect(lambdaStore.protocol).toBe("https");
44
- });
45
- });
46
-
47
- describe("port", () => {
48
- test("test port is always 443", () => {
49
- const lambdaStore = useLambdaStore();
50
- expect(lambdaStore.port).toBe(PORT_443);
51
- });
52
- });
53
-
54
- describe("base_url", () => {
55
- test("test base_url construction", () => {
56
- setupConfig();
57
- const lambdaStore = useLambdaStore();
58
- expect(lambdaStore.base_url).toBe(
59
- `https://${API_URL}:${PORT_443}${SITE_BRANCH}/${PROJECT}/createbackend`,
60
- );
61
- });
62
- });
63
-
64
- describe("is_busy", () => {
65
- test("test is_busy is always false", () => {
66
- const lambdaStore = useLambdaStore();
67
- expect(lambdaStore.is_busy).toBe(false);
68
- });
69
- });
70
- });
71
-
72
- describe("actions", () => {
73
- describe("launch", () => {
74
- const postFakeCall = vi.fn();
75
-
76
- test("successful launch", async () => {
77
- setupConfig();
78
- const lambdaStore = useLambdaStore();
79
- const feedbackStore = useFeedbackStore();
80
-
81
- lambdaStore.base_url = "test-base-url";
82
- registerEndpoint(lambdaStore.base_url, {
83
- method: "POST",
84
- handler: postFakeCall,
85
- });
86
-
87
- postFakeCall.mockImplementation(() => ({
88
- ID: TEST_ID,
89
- }));
90
-
91
- const id = await lambdaStore.launch();
92
-
93
- expect(lambdaStore.status).toBe(Status.CONNECTED);
94
- expect(id).toBe(TEST_ID);
95
- expect(feedbackStore.server_error).toBe(false);
96
- });
97
-
98
- test("failed launch - error response", async () => {
99
- setupConfig();
100
- const lambdaStore = useLambdaStore();
101
- const feedbackStore = useFeedbackStore();
102
-
103
- registerEndpoint(lambdaStore.base_url, {
104
- method: "POST",
105
- handler: postFakeCall,
106
- });
107
-
108
- postFakeCall.mockImplementation(() => {
109
- throw createError({
110
- status: STATUS_500,
111
- statusMessage: "Internal Server Error",
112
- });
113
- });
114
-
115
- await expect(lambdaStore.launch()).rejects.toThrow("Failed to launch lambda backend");
116
-
117
- expect(lambdaStore.status).toBe(Status.NOT_CONNECTED);
118
- expect(feedbackStore.server_error).toBe(true);
119
- });
120
- });
121
-
122
- describe("connect", () => {
123
- test("successful connect", async () => {
124
- const lambdaStore = useLambdaStore();
125
- await lambdaStore.connect();
126
- expect(lambdaStore.status).toBe(Status.CONNECTED);
127
- });
128
- });
129
- });
130
- });
@@ -1,40 +0,0 @@
1
- // Third party imports
2
- import { describe, expect, test } from "vitest";
3
-
4
- // Local imports
5
- import { check_recaptcha_params } from "@ogw_front/utils/recaptcha";
6
-
7
- describe("recaptcha", () => {
8
- const default_name = "";
9
- const default_email = "";
10
- const default_launch = false;
11
- const internal_error = 500;
12
- const success = 200;
13
-
14
- describe("wrong params", () => {
15
- test("name", () => {
16
- const name = "test";
17
- const result = check_recaptcha_params(name, default_email, default_launch);
18
- expect(result.statusCode).toBe(internal_error);
19
- });
20
-
21
- test("email", () => {
22
- const email = "test";
23
- const result = check_recaptcha_params(default_name, email, default_launch);
24
- expect(result.statusCode).toBe(internal_error);
25
- });
26
-
27
- test("launch", () => {
28
- const launch = true;
29
- const result = check_recaptcha_params(default_name, default_email, launch);
30
- expect(result.statusCode).toBe(internal_error);
31
- });
32
- });
33
-
34
- describe("right params", () => {
35
- test("name", () => {
36
- const result = check_recaptcha_params(default_name, default_email, default_launch);
37
- expect(result.statusCode).toBe(success);
38
- });
39
- });
40
- });