retold-facto 0.1.1 → 1.0.0
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/.github/workflows/publish-image.yml +66 -0
- package/BUILDING-AND-PUBLISHING.md +140 -0
- package/Dockerfile +5 -2
- package/package.json +22 -11
- package/source/services/Retold-Facto-StoreConnectionManager.js +19 -0
- package/source/services/web-app/pict-app-full/Pict-Application-Facto-Full.js +15 -0
- package/source/services/web-app/pict-app-full/views/PictView-Facto-Full-Connections.js +57 -90
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Publish a container image to GitHub Container Registry on every
|
|
2
|
+
# version tag push (e.g. `v1.2.3`). Generated by `quack docker-init`.
|
|
3
|
+
#
|
|
4
|
+
# Image lands at:
|
|
5
|
+
# ghcr.io/stevenvelozo/retold-facto:<version>
|
|
6
|
+
# ghcr.io/stevenvelozo/retold-facto:latest (only on stable tags)
|
|
7
|
+
|
|
8
|
+
name: Publish container image
|
|
9
|
+
|
|
10
|
+
on:
|
|
11
|
+
push:
|
|
12
|
+
tags:
|
|
13
|
+
- 'v*.*.*'
|
|
14
|
+
workflow_dispatch:
|
|
15
|
+
inputs:
|
|
16
|
+
tag:
|
|
17
|
+
description: 'Tag to apply (e.g. dev or 1.2.3-test). `latest` is reserved for stable tag pushes.'
|
|
18
|
+
required: true
|
|
19
|
+
default: 'dev'
|
|
20
|
+
|
|
21
|
+
permissions:
|
|
22
|
+
contents: read
|
|
23
|
+
packages: write
|
|
24
|
+
|
|
25
|
+
jobs:
|
|
26
|
+
build-and-push:
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
steps:
|
|
29
|
+
- name: Checkout
|
|
30
|
+
uses: actions/checkout@v4
|
|
31
|
+
|
|
32
|
+
- name: Set up QEMU (multi-arch support)
|
|
33
|
+
uses: docker/setup-qemu-action@v3
|
|
34
|
+
|
|
35
|
+
- name: Set up Docker Buildx
|
|
36
|
+
uses: docker/setup-buildx-action@v3
|
|
37
|
+
|
|
38
|
+
- name: Log in to GHCR
|
|
39
|
+
uses: docker/login-action@v3
|
|
40
|
+
with:
|
|
41
|
+
registry: ghcr.io
|
|
42
|
+
username: ${{ github.actor }}
|
|
43
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
44
|
+
|
|
45
|
+
- name: Compute image tags
|
|
46
|
+
id: meta
|
|
47
|
+
uses: docker/metadata-action@v5
|
|
48
|
+
with:
|
|
49
|
+
images: ghcr.io/${{ github.repository_owner }}/retold-facto
|
|
50
|
+
tags: |
|
|
51
|
+
type=semver,pattern={{version}}
|
|
52
|
+
type=semver,pattern={{major}}.{{minor}}
|
|
53
|
+
type=semver,pattern={{major}}
|
|
54
|
+
type=raw,value=${{ github.event.inputs.tag }},enable=${{ github.event_name == 'workflow_dispatch' }}
|
|
55
|
+
|
|
56
|
+
- name: Build and push
|
|
57
|
+
uses: docker/build-push-action@v5
|
|
58
|
+
with:
|
|
59
|
+
context: .
|
|
60
|
+
file: ./Dockerfile
|
|
61
|
+
platforms: linux/amd64,linux/arm64
|
|
62
|
+
push: true
|
|
63
|
+
tags: ${{ steps.meta.outputs.tags }}
|
|
64
|
+
labels: ${{ steps.meta.outputs.labels }}
|
|
65
|
+
cache-from: type=gha
|
|
66
|
+
cache-to: type=gha,mode=max
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Building and Publishing
|
|
2
|
+
|
|
3
|
+
How to ship `retold-facto` to npm and to GitHub Container Registry
|
|
4
|
+
(GHCR). Generated by `quack docker-init`; the structure matches the
|
|
5
|
+
shared template across all dockerized retold tools.
|
|
6
|
+
|
|
7
|
+
`retold-facto` is a **long-running service**. Restart policy in compose / k8s should be `unless-stopped` (or equivalent). The Dockerfile should declare a HEALTHCHECK against the service's health endpoint.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## TL;DR
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# npm-only release (the default — most common case)
|
|
15
|
+
npm run release:patch
|
|
16
|
+
|
|
17
|
+
# npm release that ALSO rebuilds the GHCR image
|
|
18
|
+
npm run release:patch:image
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The default release is npm-only. Docker images are deliberate, opt-in
|
|
22
|
+
artifacts because each multi-arch build burns several minutes of CI
|
|
23
|
+
time. Use `:image` (or set `BUILD_DOCKER=1`) when runtime code,
|
|
24
|
+
dependencies, env-var contract, or the Dockerfile changed.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Prerequisites (one-time setup)
|
|
29
|
+
|
|
30
|
+
- **npm login** — `npm whoami` should print your username.
|
|
31
|
+
- **Git remote configured** — `git remote get-url origin` should print
|
|
32
|
+
`git@github.com:stevenvelozo/retold-facto.git` (or the
|
|
33
|
+
HTTPS equivalent).
|
|
34
|
+
- **Push access to the repo** — required so `postversion` /
|
|
35
|
+
`postpublish` hooks can push commits and tags.
|
|
36
|
+
- **Docker** (only if you want to test the image locally before tag).
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Ecosystem convention: lockfiles are gitignored
|
|
41
|
+
|
|
42
|
+
`package-lock.json` is in this repo's `.gitignore` (Quackage convention
|
|
43
|
+
shared across the retold ecosystem). The Dockerfile must use `npm install`,
|
|
44
|
+
not `npm ci` — `npm ci` requires the lockfile to be in the build context
|
|
45
|
+
and CI runners only check out what's in git. If you see EUSAGE errors in
|
|
46
|
+
GHCR build logs, change `RUN npm ci` to `RUN npm install` in the
|
|
47
|
+
Dockerfile.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Releasing
|
|
52
|
+
|
|
53
|
+
| Command | npm registry | GHCR image rebuild |
|
|
54
|
+
|--------------------------------------|--------------|--------------------|
|
|
55
|
+
| `npm run release:patch` | yes | no |
|
|
56
|
+
| `npm run release:patch:image` | yes | yes |
|
|
57
|
+
| `npm run release:minor` | yes | no |
|
|
58
|
+
| `npm run release:minor:image` | yes | yes |
|
|
59
|
+
| `npm run release:major` | yes | no |
|
|
60
|
+
| `npm run release:major:image` | yes | yes |
|
|
61
|
+
|
|
62
|
+
The non-`:image` variants are the default because most patch releases
|
|
63
|
+
don't change runtime behavior. The `:image` variants tell the pipeline
|
|
64
|
+
"this release does change runtime — build me a new image."
|
|
65
|
+
|
|
66
|
+
### Direct CLI (also works)
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm publish # npm only
|
|
70
|
+
npm run publish:docker # npm + docker (sets BUILD_DOCKER=1)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### From `retold-manager` TUI
|
|
74
|
+
|
|
75
|
+
- `[!]` Publish — npm only
|
|
76
|
+
- `[D]` Publish with docker image — npm + GHCR build
|
|
77
|
+
|
|
78
|
+
### Promoting a previous npm release to docker later
|
|
79
|
+
|
|
80
|
+
If you released `v<x>` to npm only, then later decide you do want a
|
|
81
|
+
docker image:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
git push origin v<x> # pushes the local tag → GHCR fires
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The local tag is still sitting there from the original `npm version`
|
|
88
|
+
step. Pushing it triggers the workflow without touching npm.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## The chain
|
|
93
|
+
|
|
94
|
+
The lifecycle hooks all live in `package.json` and delegate to
|
|
95
|
+
`npx quack release …`. Default path (`BUILD_DOCKER` unset):
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
npm publish
|
|
99
|
+
→ prepublishOnly: npm test ← test gate
|
|
100
|
+
→ publish to npm
|
|
101
|
+
→ postpublish: BUILD_DOCKER unset → no-op ← image NOT triggered
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Docker-included path (`BUILD_DOCKER=1`):
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
BUILD_DOCKER=1 npm publish (or: npm run publish:docker)
|
|
108
|
+
→ prepublishOnly: npm test
|
|
109
|
+
→ publish to npm
|
|
110
|
+
→ postpublish: BUILD_DOCKER=1 → tag + push ← image trigger
|
|
111
|
+
→ .github/workflows/publish-image.yml fires
|
|
112
|
+
→ docker buildx build linux/amd64,linux/arm64
|
|
113
|
+
→ docker push ghcr.io/stevenvelozo/retold-facto:<version>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Verifying a release
|
|
119
|
+
|
|
120
|
+
1. **npm**: `npm view retold-facto version`
|
|
121
|
+
2. **Workflow**: `https://github.com/stevenvelozo/retold-facto/actions`
|
|
122
|
+
3. **Image**: `docker pull ghcr.io/stevenvelozo/retold-facto:latest`
|
|
123
|
+
|
|
124
|
+
If the first `docker pull` returns `denied`, the package is private by
|
|
125
|
+
default — flip visibility to public via Package Settings → Danger Zone
|
|
126
|
+
on the package page.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Image consumption
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
docker pull ghcr.io/stevenvelozo/retold-facto:latest
|
|
134
|
+
docker run --rm ghcr.io/stevenvelozo/retold-facto:latest
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Configuration via env vars: see this module's README for the supported
|
|
138
|
+
`<MODULE>_*` variables. Any secret-bearing var also accepts `<NAME>_FILE`
|
|
139
|
+
pointing at a file whose contents become the value (mysql/postgres
|
|
140
|
+
convention; works with docker secrets and k8s Secrets).
|
package/Dockerfile
CHANGED
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
FROM node:20-slim AS builder
|
|
3
3
|
WORKDIR /app
|
|
4
4
|
COPY package*.json ./
|
|
5
|
-
|
|
5
|
+
# `npm install`, not `npm ci` — package-lock.json is gitignored per
|
|
6
|
+
# the Quackage convention shared across the retold ecosystem, so the
|
|
7
|
+
# build context never has it. See BUILDING-AND-PUBLISHING.md.
|
|
8
|
+
RUN npm install
|
|
6
9
|
COPY .quackage.json ./
|
|
7
10
|
COPY source/ source/
|
|
8
11
|
COPY bin/ bin/
|
|
@@ -16,7 +19,7 @@ RUN cp node_modules/pict/dist/pict.min.js source/services/web-app/web/pict.min.j
|
|
|
16
19
|
FROM node:20-slim
|
|
17
20
|
WORKDIR /app
|
|
18
21
|
COPY package*.json ./
|
|
19
|
-
RUN npm
|
|
22
|
+
RUN npm install --omit=dev
|
|
20
23
|
COPY --from=builder /app/source/ source/
|
|
21
24
|
COPY --from=builder /app/bin/ bin/
|
|
22
25
|
COPY --from=builder /app/test/model/ test/model/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "retold-facto",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Data warehouse and knowledge graph storage for the Retold ecosystem.",
|
|
5
5
|
"main": "source/Retold-Facto.js",
|
|
6
6
|
"bin": {
|
|
@@ -15,7 +15,17 @@
|
|
|
15
15
|
"build-codemirror": "npx esbuild source/services/web-app/codemirror-entry.js --bundle --outfile=source/services/web-app/web/codemirror-bundle.js --format=iife --global-name=CodeMirrorModules --platform=browser --target=es2018",
|
|
16
16
|
"build-test-model": "cd test && npx stricture -i model/ddl/Facto.ddl",
|
|
17
17
|
"docker-build": "docker build -t retold-facto .",
|
|
18
|
-
"docker-run": "docker run -p 8386:8386 retold-facto"
|
|
18
|
+
"docker-run": "docker run -p 8386:8386 retold-facto",
|
|
19
|
+
"prepublishOnly": "npm test",
|
|
20
|
+
"postversion": "npx quack release postversion",
|
|
21
|
+
"postpublish": "npx quack release postpublish",
|
|
22
|
+
"publish:docker": "npx quack release publish --image",
|
|
23
|
+
"release:patch": "npx quack release patch",
|
|
24
|
+
"release:minor": "npx quack release minor",
|
|
25
|
+
"release:major": "npx quack release major",
|
|
26
|
+
"release:patch:image": "npx quack release patch --image",
|
|
27
|
+
"release:minor:image": "npx quack release minor --image",
|
|
28
|
+
"release:major:image": "npx quack release major --image"
|
|
19
29
|
},
|
|
20
30
|
"mocha": {
|
|
21
31
|
"spec": "test/RetoldFacto_tests.js",
|
|
@@ -96,30 +106,31 @@
|
|
|
96
106
|
"chai": "^6.2.2",
|
|
97
107
|
"pict-docuserve": "^0.1.5",
|
|
98
108
|
"puppeteer": "^24.40.0",
|
|
99
|
-
"quackage": "^1.
|
|
109
|
+
"quackage": "^1.2.0",
|
|
100
110
|
"stricture": "^4.0.2",
|
|
101
111
|
"supertest": "^7.2.2"
|
|
102
112
|
},
|
|
103
113
|
"dependencies": {
|
|
104
114
|
"@codemirror/lang-markdown": "^6.5.0",
|
|
105
115
|
"@codemirror/state": "^6.6.0",
|
|
106
|
-
"meadow-connection-sqlite": "^1.0.
|
|
116
|
+
"meadow-connection-sqlite": "^1.0.19",
|
|
107
117
|
"@codemirror/view": "^6.41.0",
|
|
108
|
-
"bibliograph": "^0.1.
|
|
118
|
+
"bibliograph": "^0.1.6",
|
|
109
119
|
"codemirror": "^6.0.2",
|
|
110
120
|
"fable": "^3.1.71",
|
|
111
121
|
"fable-serviceproviderbase": "^3.0.19",
|
|
112
122
|
"fast-xml-parser": "^5.5.10",
|
|
113
123
|
"meadow": "^2.0.37",
|
|
114
|
-
"meadow-connection-manager": "^1.0
|
|
115
|
-
"meadow-connection-mysql": "^1.0.
|
|
124
|
+
"meadow-connection-manager": "^1.1.0",
|
|
125
|
+
"meadow-connection-mysql": "^1.0.18",
|
|
116
126
|
"meadow-endpoints": "^4.0.17",
|
|
117
|
-
"meadow-integration": "^1.0.
|
|
118
|
-
"orator": "^6.
|
|
127
|
+
"meadow-integration": "^1.0.38",
|
|
128
|
+
"orator": "^6.1.1",
|
|
119
129
|
"orator-serviceserver-restify": "^2.0.10",
|
|
120
|
-
"orator-static-server": "^2.
|
|
121
|
-
"pict": "^1.0.
|
|
130
|
+
"orator-static-server": "^2.1.3",
|
|
131
|
+
"pict": "^1.0.365",
|
|
122
132
|
"pict-router": "^1.0.9",
|
|
133
|
+
"pict-section-connection-form": "^0.0.2",
|
|
123
134
|
"pict-section-flow": "^0.0.17",
|
|
124
135
|
"pict-section-histogram": "^1.0.0",
|
|
125
136
|
"pict-section-markdowneditor": "^1.0.10",
|
|
@@ -406,6 +406,25 @@ class RetoldFactoStoreConnectionManager extends libFableServiceProviderBase
|
|
|
406
406
|
return fNext();
|
|
407
407
|
});
|
|
408
408
|
|
|
409
|
+
// GET /facto/connection/schemas -- aggregated form schemas, drives
|
|
410
|
+
// the schema-driven Connections form (pict-section-connection-form).
|
|
411
|
+
// MCM 1.1.0+ exposes getAllProviderFormSchemas(); older versions are
|
|
412
|
+
// handled defensively so the UI surfaces a friendly empty state.
|
|
413
|
+
pOratorServiceServer.doGet(`${tmpRoutePrefix}/connection/schemas`,
|
|
414
|
+
(pRequest, pResponse, fNext) =>
|
|
415
|
+
{
|
|
416
|
+
let tmpMCM = this.fable.MeadowConnectionManager;
|
|
417
|
+
let tmpSchemas = (tmpMCM && typeof(tmpMCM.getAllProviderFormSchemas) === 'function')
|
|
418
|
+
? tmpMCM.getAllProviderFormSchemas()
|
|
419
|
+
: [];
|
|
420
|
+
if (tmpSchemas.length === 0)
|
|
421
|
+
{
|
|
422
|
+
this.fable.log.warn('Facto: meadow-connection-manager getAllProviderFormSchemas() returned no schemas; UI will show "no providers detected".');
|
|
423
|
+
}
|
|
424
|
+
pResponse.send({ Schemas: tmpSchemas });
|
|
425
|
+
return fNext();
|
|
426
|
+
});
|
|
427
|
+
|
|
409
428
|
this.fable.log.info(`StoreConnectionManager routes connected at ${tmpRoutePrefix}/connection*`);
|
|
410
429
|
}
|
|
411
430
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const libPictApplication = require('pict-application');
|
|
2
2
|
const libPictRouter = require('pict-router');
|
|
3
3
|
const libPictSectionModal = require('pict-section-modal');
|
|
4
|
+
const libPictSectionConnectionForm = require('pict-section-connection-form');
|
|
4
5
|
|
|
5
6
|
const THEME_LIST =
|
|
6
7
|
[
|
|
@@ -88,6 +89,20 @@ class FactoFullApplication extends libPictApplication
|
|
|
88
89
|
this.pict.addView('Facto-Full-SourceEditor', libViewSourceEditor.default_configuration, libViewSourceEditor);
|
|
89
90
|
this.pict.addView('Facto-Full-Scanner', libViewScanner.default_configuration, libViewScanner);
|
|
90
91
|
this.pict.addView('Facto-Full-Connections', libViewConnections.default_configuration, libViewConnections);
|
|
92
|
+
|
|
93
|
+
// Shared schema-driven connection form. Renders the type
|
|
94
|
+
// select + per-provider field block into the slot owned by
|
|
95
|
+
// the Facto-Full-Connections view. Schemas come from
|
|
96
|
+
// /facto/connection/schemas (Retold-Facto-StoreConnectionManager).
|
|
97
|
+
this.pict.addView('PictSection-ConnectionForm',
|
|
98
|
+
Object.assign({}, libPictSectionConnectionForm.default_configuration,
|
|
99
|
+
{
|
|
100
|
+
ContainerSelector: '#Facto-Conn-Form-FieldsSlot',
|
|
101
|
+
DefaultDestinationAddress: '#Facto-Conn-Form-FieldsSlot',
|
|
102
|
+
SchemasAddress: 'AppData.Facto.ConnectionFormSchemas',
|
|
103
|
+
ActiveAddress: 'AppData.Facto.ConnectionFormActiveProvider',
|
|
104
|
+
FieldIDPrefix: 'facto-conn'
|
|
105
|
+
}), libPictSectionConnectionForm);
|
|
91
106
|
this.pict.addView('Facto-Full-ProjectionDetail', libViewProjectionDetail.default_configuration, libViewProjectionDetail);
|
|
92
107
|
this.pict.addView('Facto-Full-Throughput', libViewThroughput.default_configuration, libViewThroughput);
|
|
93
108
|
this.pict.addView('Facto-Full-SchemaResearch', libViewSchemaResearch.default_configuration, libViewSchemaResearch);
|
|
@@ -70,49 +70,18 @@ const _ViewConfiguration =
|
|
|
70
70
|
|
|
71
71
|
<div id="Facto-Conn-Form" class="facto-conn-form">
|
|
72
72
|
<div class="facto-conn-form-grid">
|
|
73
|
-
<div>
|
|
73
|
+
<div style="grid-column: span 2;">
|
|
74
74
|
<label>Connection Name</label>
|
|
75
75
|
<input type="text" id="Facto-Conn-Name" placeholder="e.g. Production MySQL">
|
|
76
76
|
</div>
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
<option value="RocksDB">RocksDB</option>
|
|
86
|
-
</select>
|
|
87
|
-
</div>
|
|
88
|
-
<div id="Facto-Conn-FilePath-Wrap" style="display:none;">
|
|
89
|
-
<label>File Path</label>
|
|
90
|
-
<input type="text" id="Facto-Conn-FilePath" placeholder="/path/to/database.sqlite">
|
|
91
|
-
</div>
|
|
92
|
-
<div id="Facto-Conn-Host-Wrap">
|
|
93
|
-
<label>Host</label>
|
|
94
|
-
<input type="text" id="Facto-Conn-Host" placeholder="localhost">
|
|
95
|
-
</div>
|
|
96
|
-
<div id="Facto-Conn-Port-Wrap">
|
|
97
|
-
<label>Port</label>
|
|
98
|
-
<input type="number" id="Facto-Conn-Port" placeholder="3306">
|
|
99
|
-
</div>
|
|
100
|
-
<div id="Facto-Conn-User-Wrap">
|
|
101
|
-
<label>User</label>
|
|
102
|
-
<input type="text" id="Facto-Conn-User" placeholder="root">
|
|
103
|
-
</div>
|
|
104
|
-
<div id="Facto-Conn-Password-Wrap">
|
|
105
|
-
<label>Password</label>
|
|
106
|
-
<input type="password" id="Facto-Conn-Password" placeholder="password">
|
|
107
|
-
</div>
|
|
108
|
-
<div id="Facto-Conn-Database-Wrap">
|
|
109
|
-
<label>Database</label>
|
|
110
|
-
<input type="text" id="Facto-Conn-Database" placeholder="my_database">
|
|
111
|
-
</div>
|
|
112
|
-
<div class="facto-conn-form-actions">
|
|
113
|
-
<button class="facto-btn facto-btn-primary facto-btn-small" onclick="{~P~}.views['Facto-Full-Connections'].addConnection()">Save Connection</button>
|
|
114
|
-
<button class="facto-btn facto-btn-secondary facto-btn-small" onclick="{~P~}.views['Facto-Full-Connections'].toggleConnectionForm()">Cancel</button>
|
|
115
|
-
</div>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<!-- pict-section-connection-form renders the type select + per-provider field block here -->
|
|
80
|
+
<div id="Facto-Conn-Form-FieldsSlot" style="margin-top:0.6em"></div>
|
|
81
|
+
|
|
82
|
+
<div class="facto-conn-form-actions" style="margin-top:0.6em">
|
|
83
|
+
<button class="facto-btn facto-btn-primary facto-btn-small" onclick="{~P~}.views['Facto-Full-Connections'].addConnection()">Save Connection</button>
|
|
84
|
+
<button class="facto-btn facto-btn-secondary facto-btn-small" onclick="{~P~}.views['Facto-Full-Connections'].toggleConnectionForm()">Cancel</button>
|
|
116
85
|
</div>
|
|
117
86
|
</div>
|
|
118
87
|
|
|
@@ -155,6 +124,28 @@ class FactoFullConnectionsView extends libPictView
|
|
|
155
124
|
this.pict.views['Pict-Section-Modal'].toast('Error loading connections: ' + pError.message, {type: 'error'});
|
|
156
125
|
});
|
|
157
126
|
|
|
127
|
+
// Fetch the form schemas and hand them to the shared
|
|
128
|
+
// pict-section-connection-form view (which renders into
|
|
129
|
+
// #Facto-Conn-Form-FieldsSlot once the user opens the form).
|
|
130
|
+
this.pict.providers.Facto.api('GET', '/facto/connection/schemas').then(
|
|
131
|
+
(pData) =>
|
|
132
|
+
{
|
|
133
|
+
let tmpSchemas = (pData && Array.isArray(pData.Schemas)) ? pData.Schemas : [];
|
|
134
|
+
let tmpFormView = this.pict.views['PictSection-ConnectionForm'];
|
|
135
|
+
if (tmpFormView && typeof(tmpFormView.setSchemas) === 'function')
|
|
136
|
+
{
|
|
137
|
+
tmpFormView.setSchemas(tmpSchemas);
|
|
138
|
+
}
|
|
139
|
+
this.pict.AppData.Facto.ConnectionFormSchemas = tmpSchemas;
|
|
140
|
+
}).catch(
|
|
141
|
+
(pError) =>
|
|
142
|
+
{
|
|
143
|
+
if (this.pict.log && this.pict.log.warn)
|
|
144
|
+
{
|
|
145
|
+
this.pict.log.warn(`Facto: failed to fetch /facto/connection/schemas: ${pError && pError.message}`);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
|
|
158
149
|
return super.onAfterRender();
|
|
159
150
|
}
|
|
160
151
|
|
|
@@ -166,31 +157,16 @@ class FactoFullConnectionsView extends libPictView
|
|
|
166
157
|
tmpForm.classList.toggle('active');
|
|
167
158
|
if (tmpForm.classList.contains('active'))
|
|
168
159
|
{
|
|
169
|
-
|
|
160
|
+
// Make sure the schema-driven form renders into the slot
|
|
161
|
+
// when the user opens the panel. AutoRender is false on
|
|
162
|
+
// the shared view so we trigger it here. Schemas were
|
|
163
|
+
// fetched earlier by loadAvailableConnectionTypes().
|
|
164
|
+
let tmpFormView = this.pict.views['PictSection-ConnectionForm'];
|
|
165
|
+
if (tmpFormView) { tmpFormView.render(); }
|
|
170
166
|
}
|
|
171
167
|
}
|
|
172
168
|
}
|
|
173
169
|
|
|
174
|
-
updateConnectionFormFields()
|
|
175
|
-
{
|
|
176
|
-
let tmpType = this.pict.providers.FactoUI.getVal('Facto-Conn-Type');
|
|
177
|
-
let tmpIsFileBased = (tmpType === 'SQLite' || tmpType === 'RocksDB');
|
|
178
|
-
|
|
179
|
-
let tmpFilePathWrap = document.getElementById('Facto-Conn-FilePath-Wrap');
|
|
180
|
-
let tmpHostWrap = document.getElementById('Facto-Conn-Host-Wrap');
|
|
181
|
-
let tmpPortWrap = document.getElementById('Facto-Conn-Port-Wrap');
|
|
182
|
-
let tmpUserWrap = document.getElementById('Facto-Conn-User-Wrap');
|
|
183
|
-
let tmpPasswordWrap = document.getElementById('Facto-Conn-Password-Wrap');
|
|
184
|
-
let tmpDatabaseWrap = document.getElementById('Facto-Conn-Database-Wrap');
|
|
185
|
-
|
|
186
|
-
if (tmpFilePathWrap) tmpFilePathWrap.style.display = tmpIsFileBased ? '' : 'none';
|
|
187
|
-
if (tmpHostWrap) tmpHostWrap.style.display = tmpIsFileBased ? 'none' : '';
|
|
188
|
-
if (tmpPortWrap) tmpPortWrap.style.display = tmpIsFileBased ? 'none' : '';
|
|
189
|
-
if (tmpUserWrap) tmpUserWrap.style.display = tmpIsFileBased ? 'none' : '';
|
|
190
|
-
if (tmpPasswordWrap) tmpPasswordWrap.style.display = tmpIsFileBased ? 'none' : '';
|
|
191
|
-
if (tmpDatabaseWrap) tmpDatabaseWrap.style.display = tmpIsFileBased ? 'none' : '';
|
|
192
|
-
}
|
|
193
|
-
|
|
194
170
|
refreshConnectionList()
|
|
195
171
|
{
|
|
196
172
|
let tmpContainer = document.getElementById('Facto-Conn-List');
|
|
@@ -229,41 +205,31 @@ class FactoFullConnectionsView extends libPictView
|
|
|
229
205
|
addConnection()
|
|
230
206
|
{
|
|
231
207
|
let tmpName = this.pict.providers.FactoUI.getVal('Facto-Conn-Name');
|
|
232
|
-
let tmpType = this.pict.providers.FactoUI.getVal('Facto-Conn-Type');
|
|
233
|
-
|
|
234
208
|
if (!tmpName)
|
|
235
209
|
{
|
|
236
210
|
this.pict.views['Pict-Section-Modal'].toast('Connection name is required.', {type: 'warning'});
|
|
237
211
|
return;
|
|
238
212
|
}
|
|
239
213
|
|
|
240
|
-
|
|
241
|
-
|
|
214
|
+
// Pull Type + Config straight from the shared schema-driven view.
|
|
215
|
+
// That view handles SQLite vs server-style providers, MSSQL retry
|
|
216
|
+
// tuning, etc., uniformly via its schema definitions.
|
|
217
|
+
let tmpFormView = this.pict.views['PictSection-ConnectionForm'];
|
|
218
|
+
let tmpConnInfo = (tmpFormView && typeof(tmpFormView.getProviderConfig) === 'function')
|
|
219
|
+
? tmpFormView.getProviderConfig()
|
|
220
|
+
: { Provider: '', Config: {} };
|
|
242
221
|
|
|
243
|
-
if (
|
|
244
|
-
{
|
|
245
|
-
tmpConfig.SQLiteFilePath = this.pict.providers.FactoUI.getVal('Facto-Conn-FilePath');
|
|
246
|
-
if (!tmpConfig.SQLiteFilePath)
|
|
247
|
-
{
|
|
248
|
-
this.pict.views['Pict-Section-Modal'].toast('File path is required for ' + tmpType + ' connections.', {type: 'warning'});
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
else
|
|
222
|
+
if (!tmpConnInfo.Provider)
|
|
253
223
|
{
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
tmpConfig.port = parseInt(this.pict.providers.FactoUI.getVal('Facto-Conn-Port')) || 0;
|
|
257
|
-
tmpConfig.user = this.pict.providers.FactoUI.getVal('Facto-Conn-User');
|
|
258
|
-
tmpConfig.password = this.pict.providers.FactoUI.getVal('Facto-Conn-Password');
|
|
259
|
-
tmpConfig.database = this.pict.providers.FactoUI.getVal('Facto-Conn-Database');
|
|
224
|
+
this.pict.views['Pict-Section-Modal'].toast('Pick a provider type.', {type: 'warning'});
|
|
225
|
+
return;
|
|
260
226
|
}
|
|
261
227
|
|
|
262
228
|
this.pict.providers.Facto.createStoreConnection(
|
|
263
229
|
{
|
|
264
|
-
Name:
|
|
265
|
-
Type:
|
|
266
|
-
Config:
|
|
230
|
+
Name: tmpName,
|
|
231
|
+
Type: tmpConnInfo.Provider,
|
|
232
|
+
Config: tmpConnInfo.Config || {}
|
|
267
233
|
}).then(
|
|
268
234
|
(pResponse) =>
|
|
269
235
|
{
|
|
@@ -277,16 +243,17 @@ class FactoFullConnectionsView extends libPictView
|
|
|
277
243
|
this.pict.providers.Facto.loadStoreConnections().then(
|
|
278
244
|
(pResult) =>
|
|
279
245
|
{
|
|
280
|
-
|
|
246
|
+
|
|
281
247
|
this.refreshConnectionList();
|
|
282
248
|
this.toggleConnectionForm();
|
|
283
249
|
|
|
284
|
-
// Clear
|
|
285
|
-
|
|
286
|
-
|
|
250
|
+
// Clear name input + reset the shared schema-driven
|
|
251
|
+
// form back to its default (first provider, default values).
|
|
252
|
+
let tmpNameEl = document.getElementById('Facto-Conn-Name');
|
|
253
|
+
if (tmpNameEl) tmpNameEl.value = '';
|
|
254
|
+
if (tmpFormView && typeof(tmpFormView.clear) === 'function')
|
|
287
255
|
{
|
|
288
|
-
|
|
289
|
-
if (tmpEl) tmpEl.value = '';
|
|
256
|
+
tmpFormView.clear();
|
|
290
257
|
}
|
|
291
258
|
});
|
|
292
259
|
});
|