@openinc/parse-server-opendash 3.19.2 → 3.20.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/dist/featuremap.json +1 -0
- package/dist/features/config/helper/isFeatureEnabled.d.ts +2 -1
- package/dist/features/documentation/config/defaults.d.ts +39 -0
- package/dist/features/documentation/config/defaults.js +142 -0
- package/dist/features/documentation/config/index.d.ts +1 -0
- package/dist/features/documentation/config/index.js +17 -0
- package/dist/features/documentation/core/Cleanup.d.ts +30 -0
- package/dist/features/documentation/core/Cleanup.js +134 -0
- package/dist/features/documentation/core/Converter.d.ts +9 -0
- package/dist/features/documentation/core/Converter.js +65 -0
- package/dist/features/documentation/core/Importer.d.ts +21 -0
- package/dist/features/documentation/core/Importer.js +133 -0
- package/dist/features/documentation/core/Organizer.d.ts +11 -0
- package/dist/features/documentation/core/Organizer.js +52 -0
- package/dist/features/documentation/core/index.d.ts +4 -0
- package/dist/features/documentation/core/index.js +20 -0
- package/dist/features/documentation/functions/importDocs.d.ts +2 -0
- package/dist/features/documentation/functions/importDocs.js +40 -0
- package/dist/features/documentation/index.d.ts +8 -0
- package/dist/features/documentation/index.js +34 -0
- package/dist/features/documentation/services/ConfigApplier.d.ts +11 -0
- package/dist/features/documentation/services/ConfigApplier.js +110 -0
- package/dist/features/documentation/services/ContentLoader.d.ts +6 -0
- package/dist/features/documentation/services/ContentLoader.js +40 -0
- package/dist/features/documentation/services/FeatureFilter.d.ts +34 -0
- package/dist/features/documentation/services/FeatureFilter.js +154 -0
- package/dist/features/documentation/services/GitHubClient.d.ts +43 -0
- package/dist/features/documentation/services/GitHubClient.js +140 -0
- package/dist/features/documentation/services/MetadataEnricher.d.ts +5 -0
- package/dist/features/documentation/services/MetadataEnricher.js +29 -0
- package/dist/features/documentation/services/StructureBuilder.d.ts +7 -0
- package/dist/features/documentation/services/StructureBuilder.js +43 -0
- package/dist/features/documentation/services/TreeNormalizer.d.ts +10 -0
- package/dist/features/documentation/services/TreeNormalizer.js +73 -0
- package/dist/features/documentation/services/index.d.ts +7 -0
- package/dist/features/documentation/services/index.js +23 -0
- package/dist/features/documentation/types/GitHubTypes.d.ts +25 -0
- package/dist/features/documentation/types/GitHubTypes.js +2 -0
- package/dist/features/documentation/types/StructureTypes.d.ts +114 -0
- package/dist/features/documentation/types/StructureTypes.js +2 -0
- package/dist/features/documentation/types/index.d.ts +2 -0
- package/dist/features/documentation/types/index.js +18 -0
- package/dist/functions/openinc-documentation-import-docs.d.ts +1 -0
- package/dist/functions/openinc-documentation-import-docs.js +23 -0
- package/dist/hooks/Documentation_Config.d.ts +1 -0
- package/dist/hooks/Documentation_Config.js +17 -0
- package/dist/hooks/Tenant.js +3 -0
- package/dist/hooks/_Role.js +3 -3
- package/dist/index.js +12 -0
- package/dist/types/Documentation_Category.d.ts +6 -0
- package/dist/types/Documentation_Category.js +12 -0
- package/dist/types/Documentation_Config.d.ts +21 -0
- package/dist/types/Documentation_Config.js +29 -0
- package/dist/types/Documentation_Document.d.ts +6 -0
- package/dist/types/Documentation_Document.js +12 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +5 -3
- package/package.json +3 -1
- package/schema/Documentation_Category.json +9 -0
- package/schema/Documentation_Config.json +50 -0
- package/schema/Documentation_Document.json +9 -0
package/dist/featuremap.json
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"OD3_Dashboard": "MONITORING",
|
|
19
19
|
"OD3_Documentation_Category": "DOCUMENTATION",
|
|
20
20
|
"OD3_Documentation_Document": "DOCUMENTATION",
|
|
21
|
+
"OD3_Documentation_Config": "DOCUMENTATION",
|
|
21
22
|
"OD3_GTFS_Agency": "GTFS",
|
|
22
23
|
"OD3_GTFS_Bikes_Allowed": "GTFS",
|
|
23
24
|
"OD3_GTFS_Calendar": "GTFS",
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type Feature = "CORE" | "MONITORING" | "BDE" | "GTFS" | "KNOWLEDGE" | "MAINTENANCE" | "DOCUMENTATION" | "MIAAS";
|
|
2
|
+
export declare function isFeatureEnabled(feature: Feature): boolean;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { FileConfig, FolderConfig, ImportOptions } from "../types";
|
|
2
|
+
export declare const FOLDER_CONFIG_ALLOWED_KEYS: readonly ["title", "order", "icon", "files", "feature"];
|
|
3
|
+
export type FolderConfigAllowedKey = (typeof FOLDER_CONFIG_ALLOWED_KEYS)[number];
|
|
4
|
+
export declare const FILE_CONFIG_ALLOWED_KEYS: readonly ["order", "icon", "title", "locationPattern", "locations", "feature"];
|
|
5
|
+
export type FileConfigAllowedKey = (typeof FILE_CONFIG_ALLOWED_KEYS)[number];
|
|
6
|
+
/**
|
|
7
|
+
* Default configuration for documentation import
|
|
8
|
+
*/
|
|
9
|
+
export declare const DEFAULT_IMPORT_CONFIG: Partial<ImportOptions>;
|
|
10
|
+
export declare const DEFAULT_FILE_CONFIG: Required<Pick<FileConfig, "order" | "icon">>;
|
|
11
|
+
/**
|
|
12
|
+
* Preset configurations for different use cases
|
|
13
|
+
*/
|
|
14
|
+
export declare const IMPORT_PRESETS: {
|
|
15
|
+
DOCS_AND_CONFIG: {
|
|
16
|
+
includeExtensions: string[];
|
|
17
|
+
defaultFolderConfig: {
|
|
18
|
+
order: number;
|
|
19
|
+
icon: string;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
MEDIA_ONLY: {
|
|
23
|
+
includeExtensions: string[];
|
|
24
|
+
defaultFolderConfig: {
|
|
25
|
+
template: string;
|
|
26
|
+
tags: string[];
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Helper function to merge configuration
|
|
32
|
+
*/
|
|
33
|
+
export declare function createImportConfig(overrides?: Partial<ImportOptions>, preset?: keyof typeof IMPORT_PRESETS): ImportOptions;
|
|
34
|
+
/**
|
|
35
|
+
* Sanitize a raw (parsed) folder config object by picking only allowed keys
|
|
36
|
+
* and recursively sanitizing file configs.
|
|
37
|
+
*/
|
|
38
|
+
export declare function sanitizeFolderConfig(raw: any): FolderConfig;
|
|
39
|
+
export declare function sanitizeFileConfig(raw: any): FileConfig;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.IMPORT_PRESETS = exports.DEFAULT_FILE_CONFIG = exports.DEFAULT_IMPORT_CONFIG = exports.FILE_CONFIG_ALLOWED_KEYS = exports.FOLDER_CONFIG_ALLOWED_KEYS = void 0;
|
|
4
|
+
exports.createImportConfig = createImportConfig;
|
|
5
|
+
exports.sanitizeFolderConfig = sanitizeFolderConfig;
|
|
6
|
+
exports.sanitizeFileConfig = sanitizeFileConfig;
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Allowed Config Keys (central source of truth)
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
exports.FOLDER_CONFIG_ALLOWED_KEYS = [
|
|
11
|
+
"title",
|
|
12
|
+
"order",
|
|
13
|
+
"icon",
|
|
14
|
+
"files",
|
|
15
|
+
"feature",
|
|
16
|
+
];
|
|
17
|
+
exports.FILE_CONFIG_ALLOWED_KEYS = [
|
|
18
|
+
"order",
|
|
19
|
+
"icon",
|
|
20
|
+
"title",
|
|
21
|
+
"locationPattern",
|
|
22
|
+
"locations",
|
|
23
|
+
"feature",
|
|
24
|
+
];
|
|
25
|
+
/**
|
|
26
|
+
* Default configuration for documentation import
|
|
27
|
+
*/
|
|
28
|
+
exports.DEFAULT_IMPORT_CONFIG = {
|
|
29
|
+
organization: "open-inc",
|
|
30
|
+
repository: "documentation",
|
|
31
|
+
branch: process.env.OI_DOCUMENTATION_GITHUB_BRANCH || "main",
|
|
32
|
+
rootPath: "docs", // Start from the docs folder
|
|
33
|
+
fileFilter: {
|
|
34
|
+
// Only include markdown and JSON files by default
|
|
35
|
+
includeExtensions: ["md", "json"],
|
|
36
|
+
// Exclude common non-documentation files
|
|
37
|
+
excludeExtensions: [],
|
|
38
|
+
// Path patterns (examples)
|
|
39
|
+
// includePaths: ["docs/**", "*.md"],
|
|
40
|
+
// excludePaths: ["node_modules/**", ".git/**", "**/.DS_Store"],
|
|
41
|
+
},
|
|
42
|
+
defaultFolderConfig: {
|
|
43
|
+
order: 999,
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
exports.DEFAULT_FILE_CONFIG = {
|
|
47
|
+
order: 999,
|
|
48
|
+
icon: "fa:file",
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Preset configurations for different use cases
|
|
52
|
+
*/
|
|
53
|
+
exports.IMPORT_PRESETS = {
|
|
54
|
+
// Documentation + configuration
|
|
55
|
+
DOCS_AND_CONFIG: {
|
|
56
|
+
includeExtensions: ["md", "json"],
|
|
57
|
+
defaultFolderConfig: {
|
|
58
|
+
order: 999,
|
|
59
|
+
icon: "fa:folder",
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
// Media files
|
|
63
|
+
MEDIA_ONLY: {
|
|
64
|
+
includeExtensions: ["png", "jpg", "jpeg", "gif", "svg", "pdf"],
|
|
65
|
+
defaultFolderConfig: {
|
|
66
|
+
template: "media-gallery",
|
|
67
|
+
tags: ["media", "assets"],
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Helper function to merge configuration
|
|
73
|
+
*/
|
|
74
|
+
function createImportConfig(overrides = {}, preset) {
|
|
75
|
+
const baseConfig = { ...exports.DEFAULT_IMPORT_CONFIG };
|
|
76
|
+
// Apply preset if provided
|
|
77
|
+
if (preset && exports.IMPORT_PRESETS[preset]) {
|
|
78
|
+
baseConfig.fileFilter = {
|
|
79
|
+
...baseConfig.fileFilter,
|
|
80
|
+
...exports.IMPORT_PRESETS[preset],
|
|
81
|
+
};
|
|
82
|
+
// Apply preset's default folder config if it exists
|
|
83
|
+
if (exports.IMPORT_PRESETS[preset].defaultFolderConfig) {
|
|
84
|
+
baseConfig.defaultFolderConfig = {
|
|
85
|
+
...baseConfig.defaultFolderConfig,
|
|
86
|
+
...exports.IMPORT_PRESETS[preset].defaultFolderConfig,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Apply user overrides
|
|
91
|
+
const finalConfig = {
|
|
92
|
+
...baseConfig,
|
|
93
|
+
...overrides,
|
|
94
|
+
fileFilter: {
|
|
95
|
+
...baseConfig.fileFilter,
|
|
96
|
+
...overrides.fileFilter,
|
|
97
|
+
},
|
|
98
|
+
defaultFolderConfig: {
|
|
99
|
+
...baseConfig.defaultFolderConfig,
|
|
100
|
+
...overrides.defaultFolderConfig,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
// Ensure required fields are present
|
|
104
|
+
if (!finalConfig.token) {
|
|
105
|
+
finalConfig.token = process.env.OI_DOCUMENTATION_GITHUB_ACCESS_TOKEN || "";
|
|
106
|
+
}
|
|
107
|
+
return finalConfig;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Sanitize a raw (parsed) folder config object by picking only allowed keys
|
|
111
|
+
* and recursively sanitizing file configs.
|
|
112
|
+
*/
|
|
113
|
+
function sanitizeFolderConfig(raw) {
|
|
114
|
+
if (!raw || typeof raw !== "object")
|
|
115
|
+
return {};
|
|
116
|
+
const folder = {};
|
|
117
|
+
for (const key of exports.FOLDER_CONFIG_ALLOWED_KEYS) {
|
|
118
|
+
if (key in raw) {
|
|
119
|
+
// files handled specially below
|
|
120
|
+
if (key !== "files")
|
|
121
|
+
folder[key] = raw[key];
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (raw.files && typeof raw.files === "object") {
|
|
125
|
+
const files = {};
|
|
126
|
+
for (const [base, cfg] of Object.entries(raw.files)) {
|
|
127
|
+
files[base] = sanitizeFileConfig(cfg);
|
|
128
|
+
}
|
|
129
|
+
folder.files = files;
|
|
130
|
+
}
|
|
131
|
+
return folder;
|
|
132
|
+
}
|
|
133
|
+
function sanitizeFileConfig(raw) {
|
|
134
|
+
if (!raw || typeof raw !== "object")
|
|
135
|
+
return {};
|
|
136
|
+
const fileCfg = {};
|
|
137
|
+
for (const key of exports.FILE_CONFIG_ALLOWED_KEYS) {
|
|
138
|
+
if (key in raw)
|
|
139
|
+
fileCfg[key] = raw[key];
|
|
140
|
+
}
|
|
141
|
+
return fileCfg;
|
|
142
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./defaults";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./defaults"), exports);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { _User, Tenant } from "../../../types";
|
|
2
|
+
import { DocumentationStructure } from "../types";
|
|
3
|
+
/**
|
|
4
|
+
* Service for cleaning up old documentation that no longer exists in the repository
|
|
5
|
+
*/
|
|
6
|
+
export declare class DocumentationCleanup {
|
|
7
|
+
/**
|
|
8
|
+
* Remove old default documentation that is no longer present in the new import
|
|
9
|
+
*/
|
|
10
|
+
static cleanupOldDocumentation(newStructure: DocumentationStructure, user: _User | undefined, tenant: Tenant | undefined): Promise<{
|
|
11
|
+
deletedDocuments: number;
|
|
12
|
+
deletedCategories: number;
|
|
13
|
+
}>;
|
|
14
|
+
/**
|
|
15
|
+
* Collect all git paths from the documentation structure
|
|
16
|
+
*/
|
|
17
|
+
private static collectGitPaths;
|
|
18
|
+
/**
|
|
19
|
+
* Recursively collect folder paths
|
|
20
|
+
*/
|
|
21
|
+
private static collectFolderPaths;
|
|
22
|
+
/**
|
|
23
|
+
* Find all existing default documents for the given user/tenant
|
|
24
|
+
*/
|
|
25
|
+
private static findExistingDefaultDocuments;
|
|
26
|
+
/**
|
|
27
|
+
* Find all existing default categories for the given user/tenant
|
|
28
|
+
*/
|
|
29
|
+
private static findExistingDefaultCategories;
|
|
30
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DocumentationCleanup = void 0;
|
|
4
|
+
const types_1 = require("../../../types");
|
|
5
|
+
/**
|
|
6
|
+
* Service for cleaning up old documentation that no longer exists in the repository
|
|
7
|
+
*/
|
|
8
|
+
class DocumentationCleanup {
|
|
9
|
+
/**
|
|
10
|
+
* Remove old default documentation that is no longer present in the new import
|
|
11
|
+
*/
|
|
12
|
+
static async cleanupOldDocumentation(newStructure, user, tenant) {
|
|
13
|
+
console.log(`[DocumentationCleanup] Starting cleanup of old default documentation`);
|
|
14
|
+
// Collect all new git paths from the structure
|
|
15
|
+
const newGitPaths = new Set();
|
|
16
|
+
this.collectGitPaths(newStructure, newGitPaths);
|
|
17
|
+
console.log(`[DocumentationCleanup] Found ${newGitPaths.size} current git paths`);
|
|
18
|
+
// Find all existing default documentation for this user/tenant
|
|
19
|
+
const existingDocuments = await this.findExistingDefaultDocuments(user, tenant);
|
|
20
|
+
const existingCategories = await this.findExistingDefaultCategories(user, tenant);
|
|
21
|
+
console.log(`[DocumentationCleanup] Found ${existingDocuments.length} existing documents and ${existingCategories.length} existing categories`);
|
|
22
|
+
// Identify documents to delete
|
|
23
|
+
const documentsToDelete = existingDocuments.filter((doc) => {
|
|
24
|
+
const gitPath = doc.get("gitPath");
|
|
25
|
+
return gitPath && !newGitPaths.has(gitPath);
|
|
26
|
+
});
|
|
27
|
+
// Identify categories to delete
|
|
28
|
+
const categoriesToDelete = existingCategories.filter((cat) => {
|
|
29
|
+
const gitPath = cat.get("gitPath");
|
|
30
|
+
return gitPath && !newGitPaths.has(gitPath);
|
|
31
|
+
});
|
|
32
|
+
console.log(`[DocumentationCleanup] Identified ${documentsToDelete.length} documents and ${categoriesToDelete.length} categories for deletion`);
|
|
33
|
+
// Delete old documents
|
|
34
|
+
let deletedDocuments = 0;
|
|
35
|
+
for (const doc of documentsToDelete) {
|
|
36
|
+
try {
|
|
37
|
+
console.log(`[DocumentationCleanup] Deleting document: ${doc.get("title")} (gitPath: ${doc.get("gitPath")})`);
|
|
38
|
+
await doc.destroy({ useMasterKey: true });
|
|
39
|
+
deletedDocuments++;
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
console.error(`[DocumentationCleanup] Failed to delete document ${doc.id}:`, error);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// Delete old categories (delete in reverse order to handle parent-child relationships)
|
|
46
|
+
let deletedCategories = 0;
|
|
47
|
+
const sortedCategories = categoriesToDelete.sort((a, b) => {
|
|
48
|
+
// Sort by gitPath depth (deeper paths first) to delete children before parents
|
|
49
|
+
const aDepth = (a.get("gitPath") || "").split("/").length;
|
|
50
|
+
const bDepth = (b.get("gitPath") || "").split("/").length;
|
|
51
|
+
return bDepth - aDepth;
|
|
52
|
+
});
|
|
53
|
+
for (const cat of sortedCategories) {
|
|
54
|
+
try {
|
|
55
|
+
console.log(`[DocumentationCleanup] Deleting category: ${cat.get("name")} (gitPath: ${cat.get("gitPath")})`);
|
|
56
|
+
await cat.destroy({ useMasterKey: true });
|
|
57
|
+
deletedCategories++;
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
console.error(`[DocumentationCleanup] Failed to delete category ${cat.id}:`, error);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
console.log(`[DocumentationCleanup] Cleanup complete: deleted ${deletedDocuments} documents and ${deletedCategories} categories`);
|
|
64
|
+
return { deletedDocuments, deletedCategories };
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Collect all git paths from the documentation structure
|
|
68
|
+
*/
|
|
69
|
+
static collectGitPaths(structure, gitPaths) {
|
|
70
|
+
// Collect file paths
|
|
71
|
+
for (const file of structure.allFiles) {
|
|
72
|
+
gitPaths.add(file.path);
|
|
73
|
+
}
|
|
74
|
+
// Collect folder paths recursively
|
|
75
|
+
this.collectFolderPaths(structure.root, "", gitPaths);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Recursively collect folder paths
|
|
79
|
+
*/
|
|
80
|
+
static collectFolderPaths(folder, currentPath, gitPaths) {
|
|
81
|
+
// Add current folder path (except root)
|
|
82
|
+
if (currentPath) {
|
|
83
|
+
gitPaths.add(currentPath);
|
|
84
|
+
}
|
|
85
|
+
// Recursively process subfolders
|
|
86
|
+
for (const [name, subfolder] of folder.subfolders) {
|
|
87
|
+
const subfolderPath = currentPath ? `${currentPath}/${name}` : name;
|
|
88
|
+
this.collectFolderPaths(subfolder, subfolderPath, gitPaths);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Find all existing default documents for the given user/tenant
|
|
93
|
+
*/
|
|
94
|
+
static async findExistingDefaultDocuments(user, tenant) {
|
|
95
|
+
const query = new Parse.Query(types_1.Documentation_Document)
|
|
96
|
+
.equalTo("isDefault", true)
|
|
97
|
+
.exists("gitPath"); // Only documents with gitPath (from repository)
|
|
98
|
+
if (tenant) {
|
|
99
|
+
query.equalTo("tenant", tenant);
|
|
100
|
+
}
|
|
101
|
+
if (user) {
|
|
102
|
+
query.equalTo("user", user);
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
return await query.find({ useMasterKey: true });
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
console.error(`[DocumentationCleanup] Failed to query existing documents:`, error);
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Find all existing default categories for the given user/tenant
|
|
114
|
+
*/
|
|
115
|
+
static async findExistingDefaultCategories(user, tenant) {
|
|
116
|
+
const query = new Parse.Query(types_1.Documentation_Category)
|
|
117
|
+
.equalTo("isDefault", true)
|
|
118
|
+
.exists("gitPath"); // Only categories with gitPath (from repository)
|
|
119
|
+
if (tenant) {
|
|
120
|
+
query.equalTo("tenant", tenant);
|
|
121
|
+
}
|
|
122
|
+
if (user) {
|
|
123
|
+
query.equalTo("user", user);
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
return await query.find({ useMasterKey: true });
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
console.error(`[DocumentationCleanup] Failed to query existing categories:`, error);
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
exports.DocumentationCleanup = DocumentationCleanup;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { _User, Tenant } from "../../../types";
|
|
2
|
+
import { ImportResult } from "../types";
|
|
3
|
+
export declare class DocumentationConverter {
|
|
4
|
+
private readonly results;
|
|
5
|
+
constructor(importResults: ImportResult);
|
|
6
|
+
convert(user?: _User, tenant?: Tenant): Promise<void>;
|
|
7
|
+
private convertFolderToCategory;
|
|
8
|
+
private convertFileToDocumentation;
|
|
9
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DocumentationConverter = void 0;
|
|
4
|
+
const types_1 = require("../../../types");
|
|
5
|
+
const USER_ID = process.env.OI_DOCUMENTATION_USER_ID;
|
|
6
|
+
const TENANT_ID = process.env.OI_DOCUMENTATION_TENANT_ID;
|
|
7
|
+
class DocumentationConverter {
|
|
8
|
+
constructor(importResults) {
|
|
9
|
+
this.results = importResults;
|
|
10
|
+
}
|
|
11
|
+
async convert(user, tenant) {
|
|
12
|
+
// Implement your conversion logic here
|
|
13
|
+
if (!this.results.structure)
|
|
14
|
+
return;
|
|
15
|
+
this.convertFolderToCategory(this.results.structure.root, undefined, user, tenant);
|
|
16
|
+
}
|
|
17
|
+
async convertFolderToCategory(folder, parentCategory, user, tenant) {
|
|
18
|
+
const existingCategory = await new Parse.Query(types_1.Documentation_Category)
|
|
19
|
+
.equalTo("gitPath", folder.path)
|
|
20
|
+
.equalTo("isDefault", true)
|
|
21
|
+
.equalTo("user", user)
|
|
22
|
+
.equalTo("tenant", tenant)
|
|
23
|
+
.first({ useMasterKey: true });
|
|
24
|
+
const folderCategory = existingCategory || new types_1.Documentation_Category();
|
|
25
|
+
folderCategory.set("name", folder.config.title || folder.name);
|
|
26
|
+
folderCategory.set("icon", folder.config.icon);
|
|
27
|
+
folderCategory.set("order", folder.config.order || 999);
|
|
28
|
+
folderCategory.set("parent", parentCategory);
|
|
29
|
+
folderCategory.set("isDefault", true);
|
|
30
|
+
folderCategory.set("gitPath", folder.path);
|
|
31
|
+
if (user)
|
|
32
|
+
folderCategory.set("user", user);
|
|
33
|
+
if (tenant || user)
|
|
34
|
+
folderCategory.set("tenant", tenant ?? user?.get("tenant"));
|
|
35
|
+
const savedCategory = await folderCategory.save(null, {
|
|
36
|
+
useMasterKey: true,
|
|
37
|
+
});
|
|
38
|
+
for (const subfolder of folder.subfolders.values())
|
|
39
|
+
await this.convertFolderToCategory(subfolder, savedCategory, user, tenant);
|
|
40
|
+
for (const file of folder.files)
|
|
41
|
+
await this.convertFileToDocumentation(file, savedCategory, user, tenant);
|
|
42
|
+
}
|
|
43
|
+
async convertFileToDocumentation(file, category, user, tenant) {
|
|
44
|
+
const existingDoc = await new Parse.Query(types_1.Documentation_Document)
|
|
45
|
+
.equalTo("gitPath", file.path)
|
|
46
|
+
.equalTo("isDefault", true)
|
|
47
|
+
.first({ useMasterKey: true });
|
|
48
|
+
const doc = existingDoc || new types_1.Documentation_Document();
|
|
49
|
+
doc.set("title", file.config?.title || file.name);
|
|
50
|
+
doc.set("content", file.content || "");
|
|
51
|
+
doc.set("icon", file.config?.icon);
|
|
52
|
+
doc.set("order", file.config?.order || 999);
|
|
53
|
+
doc.set("category", category);
|
|
54
|
+
doc.set("locationPattern", file.config?.locationPattern);
|
|
55
|
+
doc.set("locations", file.config?.locations || []);
|
|
56
|
+
doc.set("isDefault", true);
|
|
57
|
+
doc.set("gitPath", file.path);
|
|
58
|
+
if (user)
|
|
59
|
+
doc.set("user", user);
|
|
60
|
+
if (tenant || user)
|
|
61
|
+
doc.set("tenant", tenant ?? user?.get("tenant"));
|
|
62
|
+
await doc.save(null, { useMasterKey: true });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
exports.DocumentationConverter = DocumentationConverter;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ImportOptions, ImportResult } from "../types";
|
|
2
|
+
import { _User, Tenant } from "../../../types";
|
|
3
|
+
/**
|
|
4
|
+
* Main Documentation Importer class
|
|
5
|
+
*/
|
|
6
|
+
export declare class DocumentationImporter {
|
|
7
|
+
private githubClient;
|
|
8
|
+
constructor(token: string);
|
|
9
|
+
/**
|
|
10
|
+
* Import documentation from a GitHub repository
|
|
11
|
+
*/
|
|
12
|
+
importFromRepository(options: ImportOptions, user: _User | undefined, tenant: Tenant | undefined): Promise<ImportResult>;
|
|
13
|
+
/**
|
|
14
|
+
* Validate that a branch exists
|
|
15
|
+
*/
|
|
16
|
+
private validateBranch;
|
|
17
|
+
/**
|
|
18
|
+
* Get file content from GitHub
|
|
19
|
+
*/
|
|
20
|
+
getFileContent(owner: string, repo: string, path: string, ref?: string): Promise<any>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DocumentationImporter = void 0;
|
|
4
|
+
const services_1 = require("../services");
|
|
5
|
+
const types_1 = require("../../../types");
|
|
6
|
+
const Organizer_1 = require("./Organizer");
|
|
7
|
+
const Cleanup_1 = require("./Cleanup");
|
|
8
|
+
/**
|
|
9
|
+
* Main Documentation Importer class
|
|
10
|
+
*/
|
|
11
|
+
class DocumentationImporter {
|
|
12
|
+
constructor(token) {
|
|
13
|
+
this.githubClient = new services_1.GitHubClient(token);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Import documentation from a GitHub repository
|
|
17
|
+
*/
|
|
18
|
+
async importFromRepository(options, user, tenant) {
|
|
19
|
+
// Validate token
|
|
20
|
+
const gitUser = await this.githubClient.validateToken();
|
|
21
|
+
// Get repository info
|
|
22
|
+
const repo = await this.githubClient.getRepository(options.organization, options.repository);
|
|
23
|
+
// Determine branch to use
|
|
24
|
+
const branch = options.branch || repo.default_branch;
|
|
25
|
+
// Validate branch exists
|
|
26
|
+
await this.validateBranch(options.organization, options.repository, branch);
|
|
27
|
+
// Get branch info (commit SHA)
|
|
28
|
+
const branchInfo = await this.githubClient.getBranch(options.organization, options.repository, branch);
|
|
29
|
+
const latestCommitSha = branchInfo.commit.sha;
|
|
30
|
+
// Attempt to load previously imported commit SHA from Parse
|
|
31
|
+
let previousCommitSha;
|
|
32
|
+
let stateObj;
|
|
33
|
+
try {
|
|
34
|
+
stateObj = await new Parse.Query(types_1.Documentation_Config)
|
|
35
|
+
.equalTo("tenant", tenant)
|
|
36
|
+
.equalTo("user", user)
|
|
37
|
+
.first({
|
|
38
|
+
useMasterKey: true,
|
|
39
|
+
});
|
|
40
|
+
previousCommitSha = stateObj?.get("lastCommitSha");
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
console.log(`Could not load previous commit SHA: ${err.message}`);
|
|
44
|
+
}
|
|
45
|
+
// Early exit if unchanged
|
|
46
|
+
if (previousCommitSha && previousCommitSha === latestCommitSha) {
|
|
47
|
+
return {
|
|
48
|
+
metadata: {
|
|
49
|
+
totalFiles: 0,
|
|
50
|
+
fileTypes: [],
|
|
51
|
+
// importedAt still reflects the check time
|
|
52
|
+
importedAt: new Date(),
|
|
53
|
+
repository: `${options.organization}/${options.repository}`,
|
|
54
|
+
branch,
|
|
55
|
+
commitSha: latestCommitSha,
|
|
56
|
+
previousCommitSha,
|
|
57
|
+
skipped: true,
|
|
58
|
+
skipReason: "unchanged",
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
const treeData = await this.githubClient.getTree(options.organization, options.repository, latestCommitSha);
|
|
63
|
+
// Organize structure with filtering and root path
|
|
64
|
+
const structure = await Organizer_1.DocumentationOrganizer.organizeRepositoryStructure(treeData, options.fileFilter, options.rootPath, this.githubClient, options.organization, options.repository, branch, options.defaultFolderConfig);
|
|
65
|
+
// Clean up old documentation that is no longer in the repository
|
|
66
|
+
// This only runs when commit SHA has changed (not on first import when previousCommitSha is undefined)
|
|
67
|
+
let cleanupResult = { deletedDocuments: 0, deletedCategories: 0 };
|
|
68
|
+
if (previousCommitSha) {
|
|
69
|
+
console.log(`[DocumentationImporter] Repository content changed, cleaning up old documentation...`);
|
|
70
|
+
try {
|
|
71
|
+
cleanupResult = await Cleanup_1.DocumentationCleanup.cleanupOldDocumentation(structure, user, tenant);
|
|
72
|
+
console.log(`[DocumentationImporter] Cleanup completed: ${cleanupResult.deletedDocuments} documents and ${cleanupResult.deletedCategories} categories removed`);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
console.error(`[DocumentationImporter] Cleanup failed:`, error);
|
|
76
|
+
// Continue with import even if cleanup fails
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
console.log(`[DocumentationImporter] First import detected, skipping cleanup`);
|
|
81
|
+
}
|
|
82
|
+
// Persist latest commit SHA for future runs
|
|
83
|
+
try {
|
|
84
|
+
if (!stateObj) {
|
|
85
|
+
stateObj = new types_1.Documentation_Config();
|
|
86
|
+
}
|
|
87
|
+
stateObj.set("lastCommitSha", latestCommitSha);
|
|
88
|
+
if (tenant)
|
|
89
|
+
stateObj.set("tenant", tenant);
|
|
90
|
+
if (user)
|
|
91
|
+
stateObj.set("user", user);
|
|
92
|
+
await stateObj.save(null, { useMasterKey: true });
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
console.log(`Could not persist latest commit SHA: ${err.message}`);
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
structure,
|
|
99
|
+
metadata: {
|
|
100
|
+
totalFiles: structure.allFiles.length,
|
|
101
|
+
fileTypes: Array.from(structure.filesByExtension.keys()),
|
|
102
|
+
importedAt: new Date(),
|
|
103
|
+
repository: `${options.organization}/${options.repository}`,
|
|
104
|
+
branch,
|
|
105
|
+
commitSha: latestCommitSha,
|
|
106
|
+
previousCommitSha,
|
|
107
|
+
cleanup: cleanupResult,
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Validate that a branch exists
|
|
113
|
+
*/
|
|
114
|
+
async validateBranch(owner, repo, branch) {
|
|
115
|
+
try {
|
|
116
|
+
const branches = await this.githubClient.getBranches(owner, repo);
|
|
117
|
+
const branchNames = branches.map((b) => b.name);
|
|
118
|
+
if (!branchNames.includes(branch)) {
|
|
119
|
+
throw new Error(`Branch '${branch}' not found. Available branches: ${branchNames.join(", ")}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
console.log(`Could not validate branches: ${error}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Get file content from GitHub
|
|
128
|
+
*/
|
|
129
|
+
async getFileContent(owner, repo, path, ref) {
|
|
130
|
+
return this.githubClient.getFileContent(owner, repo, path, ref);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
exports.DocumentationImporter = DocumentationImporter;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { GitHubTreeResponse, DocumentationStructure, FileFilterOptions, FolderConfig } from "../types";
|
|
2
|
+
import { GitHubClient } from "../services/GitHubClient";
|
|
3
|
+
/**
|
|
4
|
+
* Documentation Structure Organizer
|
|
5
|
+
*/
|
|
6
|
+
export declare class DocumentationOrganizer {
|
|
7
|
+
/**
|
|
8
|
+
* Organize GitHub tree data into a hierarchical structure
|
|
9
|
+
*/
|
|
10
|
+
static organizeRepositoryStructure(treeData: GitHubTreeResponse, fileFilter?: FileFilterOptions, rootPath?: string, githubClient?: GitHubClient, owner?: string, repo?: string, branch?: string, defaultFolderConfig?: Partial<FolderConfig>): Promise<DocumentationStructure>;
|
|
11
|
+
}
|