mirror of
https://github.com/alamion/obsidian-jira-sync.git
synced 2026-07-22 05:43:04 +00:00
field mapping fix, statistics.md fixes and optimizations.
This commit is contained in:
parent
b0c1e4dfd4
commit
534c63d019
8 changed files with 303 additions and 183 deletions
|
|
@ -1,187 +1,201 @@
|
|||
---
|
||||
jira_selected_week: 2025-03-10
|
||||
jira_selected_week_data: '[]'
|
||||
---
|
||||
|
||||
|
||||
```dataviewjs
|
||||
const tasksDirectory = "Kanban";
|
||||
const show_max_weeks = 3
|
||||
|
||||
const show_max_weeks = 3;
|
||||
|
||||
// Get all markdown files in the Kanban directory
|
||||
const files = app.vault.getMarkdownFiles().filter(file => file.path.startsWith(tasksDirectory));
|
||||
|
||||
let timekeepBlocks = [];
|
||||
// Regular expression to extract issue key (assuming it's in format like "PROJECT-123" in the file content)
|
||||
const issueKeyRegex = /(?:^|\s)([A-Z]+-\d+)(?:\s|$)/;
|
||||
|
||||
async function processFiles() {
|
||||
for (const file of files) {
|
||||
const content = await app.vault.read(file);
|
||||
const timekeepMatches = content.match(/```timekeep\n([\s\S]*?)```/g);
|
||||
let timekeepBlocks = [];
|
||||
|
||||
// Process all files in one go
|
||||
await Promise.all(files.map(async file => {
|
||||
const content = await app.vault.read(file);
|
||||
|
||||
// Extract issue key from file content instead of using file.key
|
||||
let issueKey = null;
|
||||
const keyMatch = content.match(issueKeyRegex);
|
||||
if (keyMatch) {
|
||||
issueKey = keyMatch[1];
|
||||
}
|
||||
|
||||
// Match all timekeep blocks using a single regex
|
||||
const timekeepMatches = content.match(/```timekeep\n([\s\S]*?)```/g);
|
||||
|
||||
if (timekeepMatches) {
|
||||
timekeepMatches.forEach(block => {
|
||||
try {
|
||||
// Extract JSON content and parse it
|
||||
const jsonContent = block.replace(/```timekeep\n/, "").replace(/```/, "").trim();
|
||||
const data = JSON.parse(jsonContent);
|
||||
|
||||
timekeepBlocks.push({
|
||||
file: file.name,
|
||||
path: file.path,
|
||||
issueKey: issueKey, // Use extracted issue key
|
||||
data: data
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Error parsing JSON in ${file.name}:`, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
if (timekeepMatches) {
|
||||
timekeepMatches.forEach(block => {
|
||||
try {
|
||||
const jsonContent = block.replace(/```timekeep\n/, "").replace(/```/, "").trim();
|
||||
const data = JSON.parse(jsonContent);
|
||||
timekeepBlocks.push({
|
||||
file: file.name,
|
||||
path: file.path,
|
||||
data: data,
|
||||
jira_task: file.jira
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Error parsing JSON in ${file.name}:`, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
// Group entries by week
|
||||
const groupedByWeek = {};
|
||||
|
||||
const groupedByWeek = {};
|
||||
const jiraData = {};
|
||||
window.jiraData = jiraData;
|
||||
// Process all timekeep blocks once
|
||||
timekeepBlocks.forEach(block => {
|
||||
processEntries(block.data.entries, block.file, block.issueKey, "", groupedByWeek);
|
||||
});
|
||||
|
||||
// Trim old entries to show only the most recent weeks
|
||||
trimOldEntries(groupedByWeek, show_max_weeks);
|
||||
|
||||
// Process all timekeep blocks with recursive entries
|
||||
timekeepBlocks.forEach(block => {
|
||||
processEntries(block.data.entries, block.file, "", groupedByWeek, jiraData, block);
|
||||
});
|
||||
|
||||
trimOldEntries(groupedByWeek, show_max_weeks)
|
||||
trimOldEntries(jiraData, show_max_weeks)
|
||||
|
||||
// Display results
|
||||
if (Object.keys(groupedByWeek).length > 0) {
|
||||
const sortedWeeks = Object.keys(groupedByWeek).sort()
|
||||
for (const week of sortedWeeks) {
|
||||
// Display results
|
||||
if (Object.keys(groupedByWeek).length > 0) {
|
||||
// Sort weeks chronologically
|
||||
const sortedWeeks = Object.keys(groupedByWeek).sort();
|
||||
|
||||
for (const week of sortedWeeks) {
|
||||
const entries = groupedByWeek[week];
|
||||
dv.header(3, `Week of ${week}`);
|
||||
dv.table(
|
||||
["Task", "Block Path", "Start Time", "End Time", "Duration"],
|
||||
entries.map(entry => [
|
||||
entry.file,
|
||||
entry.blockPath,
|
||||
entry.startTime,
|
||||
entry.endTime,
|
||||
entry.duration
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
let options = '';
|
||||
for (const [week, entries] of Object.entries(jiraData)) {
|
||||
options += `option(${week}),\n`;
|
||||
}
|
||||
if (options.length > 2) options = options.substring(0, options.length - 2);
|
||||
|
||||
dv.paragraph(`\`\`\`meta-bind
|
||||
INPUT[inlineSelect(${options}): jira_selected_week]
|
||||
dv.header(3, `Week of ${week}`);
|
||||
dv.table(
|
||||
["Task", "Block Path", "Start Time", "Duration"],
|
||||
entries.map(entry => [
|
||||
entry.file,
|
||||
entry.blockPath,
|
||||
entry.startTime,
|
||||
entry.duration
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
// Create options for metabind select
|
||||
const options = sortedWeeks
|
||||
.map(week => `option('${JSON.stringify(groupedByWeek[week])}', ${week})`)
|
||||
.join(",\n");
|
||||
|
||||
// Display meta-bind controls
|
||||
dv.paragraph(`\`\`\`meta-bind
|
||||
INPUT[inlineSelect(option('[]', 'select week'), ${options}): jira_selected_week_data]
|
||||
\`\`\``);
|
||||
|
||||
dv.paragraph(`\`\`\`meta-bind-button
|
||||
label: Send
|
||||
|
||||
dv.paragraph(`\`\`\`meta-bind-button
|
||||
label: Send worklog to Jira
|
||||
hidden: false
|
||||
id: ""
|
||||
style: primary
|
||||
actions:
|
||||
- type: command
|
||||
command: obsidian-jira-sync:sent_week
|
||||
command: obsidian-jira-sync:sent_week
|
||||
\`\`\``);
|
||||
} else {
|
||||
dv.paragraph("No entries found.");
|
||||
}
|
||||
} else {
|
||||
dv.paragraph("No entries found.");
|
||||
}
|
||||
}
|
||||
|
||||
// Recursive function to process entries and their subentries
|
||||
function processEntries(entries, fileName, parentPath, groupedByWeek, jiraData, originalBlock) {
|
||||
if (!entries || !Array.isArray(entries)) return;
|
||||
// Process entries recursively with improved efficiency
|
||||
function processEntries(entries, fileName, issueKey, parentPath, groupedByWeek) {
|
||||
if (!entries || !Array.isArray(entries)) return;
|
||||
|
||||
entries.forEach(entry => {
|
||||
// Create the current block path
|
||||
const currentPath = parentPath ? `${parentPath} > ${entry.name}` : entry.name;
|
||||
|
||||
// Process this entry if it has a start time
|
||||
if (entry.startTime) {
|
||||
const startTime = new Date(entry.startTime);
|
||||
const weekStart = getWeekStartDate(startTime);
|
||||
const weekKey = weekStart.toISOString().split('T')[0];
|
||||
for (const entry of entries) {
|
||||
// Create the current block path
|
||||
const currentPath = parentPath ? `${parentPath} > ${entry.name}` : entry.name;
|
||||
|
||||
// Process this entry if it has a start time
|
||||
if (entry.startTime) {
|
||||
const startTime = new Date(entry.startTime);
|
||||
const weekStart = getWeekStartDate(startTime);
|
||||
const weekKey = weekStart.toISOString().split('T')[0];
|
||||
|
||||
if (!groupedByWeek[weekKey]) groupedByWeek[weekKey] = [];
|
||||
if (!jiraData[weekKey]) jiraData[weekKey] = [];
|
||||
// Initialize the week array if it doesn't exist
|
||||
if (!groupedByWeek[weekKey]) groupedByWeek[weekKey] = [];
|
||||
|
||||
// Calculate duration
|
||||
let duration;
|
||||
let endTime = entry.endTime ? new Date(entry.endTime) : new Date();
|
||||
|
||||
if (entry.endTime) {
|
||||
duration = formatDuration(endTime - startTime);
|
||||
} else {
|
||||
duration = "ongoing";
|
||||
}
|
||||
// Calculate duration
|
||||
let duration;
|
||||
let endTime;
|
||||
|
||||
if (entry.endTime) {
|
||||
endTime = new Date(entry.endTime);
|
||||
duration = formatDuration(endTime - startTime);
|
||||
} else {
|
||||
endTime = new Date();
|
||||
duration = "ongoing";
|
||||
}
|
||||
|
||||
// Add to grouped data
|
||||
groupedByWeek[weekKey].push({
|
||||
file: fileName.replace(".md", ""),
|
||||
blockPath: currentPath,
|
||||
startTime: formatDate(startTime, 'DD-MM-YYYY HH:mm'),
|
||||
endTime: entry.endTime ? formatDate(endTime, 'DD-MM-YYYY HH:mm') : "ongoing",
|
||||
duration: duration
|
||||
});
|
||||
|
||||
// Add to Jira data
|
||||
jiraData[weekKey].push({
|
||||
task: originalBlock,
|
||||
time_block: {
|
||||
...entry,
|
||||
path: currentPath
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Process subentries recursively
|
||||
if (entry.subEntries && Array.isArray(entry.subEntries)) {
|
||||
processEntries(entry.subEntries, fileName, currentPath, groupedByWeek, jiraData, originalBlock);
|
||||
}
|
||||
});
|
||||
// Add to grouped data
|
||||
groupedByWeek[weekKey].push({
|
||||
file: fileName.replace(".md", ""),
|
||||
issueKey: issueKey,
|
||||
blockPath: currentPath,
|
||||
startTime: formatDate(startTime, 'DD-MM-YYYY HH:mm'),
|
||||
endTime: entry.endTime ? formatDate(endTime, 'DD-MM-YYYY HH:mm') : "ongoing",
|
||||
duration: duration
|
||||
});
|
||||
}
|
||||
|
||||
// Process subentries recursively
|
||||
if (entry.subEntries && Array.isArray(entry.subEntries)) {
|
||||
processEntries(entry.subEntries, fileName, issueKey, currentPath, groupedByWeek);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Date formatting helper
|
||||
function formatDate(date, format) {
|
||||
const pad = (num) => num.toString().padStart(2, '0');
|
||||
const pad = (num) => num.toString().padStart(2, '0');
|
||||
|
||||
const year = date.getFullYear();
|
||||
const month = pad(date.getMonth() + 1);
|
||||
const day = pad(date.getDate());
|
||||
const hours = pad(date.getHours());
|
||||
const minutes = pad(date.getMinutes());
|
||||
const seconds = pad(date.getSeconds());
|
||||
const year = date.getFullYear();
|
||||
const month = pad(date.getMonth() + 1);
|
||||
const day = pad(date.getDate());
|
||||
const hours = pad(date.getHours());
|
||||
const minutes = pad(date.getMinutes());
|
||||
|
||||
return format
|
||||
.replace('YYYY', year)
|
||||
.replace('MM', month)
|
||||
.replace('DD', day)
|
||||
.replace('HH', hours)
|
||||
.replace('mm', minutes)
|
||||
.replace('ss', seconds);
|
||||
return format
|
||||
.replace('YYYY', year)
|
||||
.replace('MM', month)
|
||||
.replace('DD', day)
|
||||
.replace('HH', hours)
|
||||
.replace('mm', minutes);
|
||||
}
|
||||
|
||||
// Get the start date of the week for a given date
|
||||
function getWeekStartDate(date) {
|
||||
const day = date.getDay();
|
||||
const diff = date.getDate() - day + (day === 0 ? -6 : 1);
|
||||
return new Date(date.setDate(diff));
|
||||
const result = new Date(date);
|
||||
const day = result.getDay();
|
||||
const diff = result.getDate() - day + (day === 0 ? -6 : 1); // Adjust when day is Sunday
|
||||
result.setDate(diff);
|
||||
return new Date(result.setHours(0, 0, 0, 0)); // Start of the day
|
||||
}
|
||||
|
||||
// Format milliseconds to readable duration
|
||||
function formatDuration(ms) {
|
||||
const seconds = Math.floor(ms / 1000) % 60;
|
||||
const minutes = Math.floor(ms / (1000 * 60)) % 60;
|
||||
const hours = Math.floor(ms / (1000 * 60 * 60));
|
||||
return `${hours}h ${minutes}m ${seconds}s`;
|
||||
const seconds = Math.floor(ms / 1000) % 60;
|
||||
const minutes = Math.floor(ms / (1000 * 60)) % 60;
|
||||
const hours = Math.floor(ms / (1000 * 60 * 60));
|
||||
return `${hours}h ${minutes}m ${seconds}s`;
|
||||
}
|
||||
|
||||
function trimOldEntries(example_object, max_days) {
|
||||
let dates = Object.keys(example_object).sort();
|
||||
|
||||
while (dates.length > max_days) {
|
||||
let oldestDate = dates.shift();
|
||||
delete example_object[oldestDate];
|
||||
}
|
||||
// Remove oldest entries beyond the maximum number of weeks to display
|
||||
function trimOldEntries(groupedByWeek, max_weeks) {
|
||||
const dates = Object.keys(groupedByWeek).sort();
|
||||
|
||||
while (dates.length > max_weeks) {
|
||||
const oldestDate = dates.shift();
|
||||
delete groupedByWeek[oldestDate];
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the main function
|
||||
processFiles();
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import { Notice } from "obsidian";
|
||||
import JiraPlugin from "../main";
|
||||
import {authenticate, fetchIssueTransitions} from "../api";
|
||||
import {updateIssueFromFile, updateStatusFromFile} from "../file_operations/createUpdateIssue";
|
||||
import {IssueSearchModal} from "../modals";
|
||||
import {fetchAndOpenIssue} from "./getIssue";
|
||||
import {updateStatusFromFile} from "../file_operations/createUpdateIssue";
|
||||
import {IssueStatusModal} from "../modals/IssueStatusModal";
|
||||
import {getCurrentFileMainInfo} from "../file_operations/common_prepareData";
|
||||
import {JiraTransitionType} from "../interfaces";
|
||||
|
|
@ -35,8 +33,8 @@ export async function updateIssueStatus(plugin: JiraPlugin): Promise<void> {
|
|||
new Notice("No active file");
|
||||
return;
|
||||
}
|
||||
const issueKey = await getCurrentFileMainInfo(plugin);
|
||||
const issueTransitions = await fetchIssueTransitions(plugin, issueKey);
|
||||
const {issueKey} = await getCurrentFileMainInfo(plugin);
|
||||
const issueTransitions = await fetchIssueTransitions(plugin, issueKey as string);
|
||||
// Update the issue with all data from the file
|
||||
new IssueStatusModal(plugin.app, issueTransitions, async (transition: JiraTransitionType) => {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ export interface JiraSettings {
|
|||
sessionCookieName: string;
|
||||
templatePath: string;
|
||||
fieldMappings: Record<string, FieldMapping>;
|
||||
fieldMappingsStrings: Record<string, { toJira: string; fromJira: string }>;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -202,6 +202,10 @@ export class JiraSettingTab extends PluginSettingTab {
|
|||
}
|
||||
});
|
||||
|
||||
// Store the string representations directly in a separate property
|
||||
this.plugin.settings.fieldMappingsStrings = mappings;
|
||||
|
||||
// Also convert to functions for runtime use
|
||||
const transformedMappings: Record<string, FieldMapping> = {};
|
||||
for (const [fieldName, { toJira, fromJira }] of Object.entries(mappings)) {
|
||||
const toJiraFn = safeStringToFunction(toJira);
|
||||
|
|
@ -217,7 +221,7 @@ export class JiraSettingTab extends PluginSettingTab {
|
|||
}
|
||||
}
|
||||
|
||||
// Save as stringified JSON
|
||||
// Save the functional mappings
|
||||
this.plugin.settings.fieldMappings = transformedMappings;
|
||||
await this.plugin.saveSettings();
|
||||
};
|
||||
|
|
@ -240,33 +244,77 @@ export class JiraSettingTab extends PluginSettingTab {
|
|||
cls: "reset-field-mappings-btn"
|
||||
})
|
||||
setIcon(resetBtn, "refresh-cw")
|
||||
|
||||
resetBtn.addEventListener("click", () => {
|
||||
this.plugin.settings.fieldMappings = fieldMappings;
|
||||
this.plugin.settings.fieldMappings = {};
|
||||
this.plugin.settings.fieldMappingsStrings = {};
|
||||
loadExistingMappings();
|
||||
this.plugin.saveSettings();
|
||||
});
|
||||
|
||||
const reloadBtn = buttonView.createEl("button", {
|
||||
text: "",
|
||||
cls: "reset-field-mappings-btn"
|
||||
})
|
||||
setIcon(reloadBtn, "list-restart")
|
||||
reloadBtn.addEventListener("click", () => {
|
||||
this.plugin.settings.fieldMappings = fieldMappings;
|
||||
|
||||
// Also reset the string representations with default values
|
||||
const defaultMappingsStrings: Record<string, { toJira: string; fromJira: string }> = {};
|
||||
for (const [fieldName, mapping] of Object.entries(fieldMappings)) {
|
||||
defaultMappingsStrings[fieldName] = {
|
||||
toJira: functionToArrowString(mapping.toJira),
|
||||
fromJira: functionToArrowString(mapping.fromJira)
|
||||
};
|
||||
}
|
||||
this.plugin.settings.fieldMappingsStrings = defaultMappingsStrings;
|
||||
|
||||
loadExistingMappings();
|
||||
this.plugin.saveSettings();
|
||||
});
|
||||
|
||||
// Load existing mappings if available
|
||||
const loadExistingMappings = () => {
|
||||
if (this.plugin.settings.fieldMappings) {
|
||||
try {
|
||||
fieldsList.innerHTML = "";
|
||||
const existingMappings = this.plugin.settings.fieldMappings;
|
||||
for (const [fieldName, mapping] of Object.entries(existingMappings)) {
|
||||
if (mapping && typeof mapping === 'object' && 'toJira' in mapping && 'fromJira' in mapping) {
|
||||
addFieldMapping(
|
||||
fieldName,
|
||||
functionToArrowString(mapping.toJira),
|
||||
functionToArrowString(mapping.fromJira)
|
||||
);
|
||||
}
|
||||
// Clear existing field list
|
||||
fieldsList.innerHTML = "";
|
||||
|
||||
// If we have string representations stored, use those
|
||||
if (this.plugin.settings.fieldMappingsStrings &&
|
||||
Object.keys(this.plugin.settings.fieldMappingsStrings).length > 0) {
|
||||
|
||||
const savedMappings = this.plugin.settings.fieldMappingsStrings;
|
||||
for (const [fieldName, mapping] of Object.entries(savedMappings)) {
|
||||
if (mapping && typeof mapping === 'object' && 'toJira' in mapping && 'fromJira' in mapping) {
|
||||
addFieldMapping(
|
||||
fieldName,
|
||||
mapping.toJira,
|
||||
mapping.fromJira
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to parse existing field mappings", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
loadExistingMappings()
|
||||
// Otherwise, try to use the function mappings and convert them to strings
|
||||
else if (this.plugin.settings.fieldMappings &&
|
||||
Object.keys(this.plugin.settings.fieldMappings).length > 0) {
|
||||
|
||||
const existingMappings = this.plugin.settings.fieldMappings;
|
||||
for (const [fieldName, mapping] of Object.entries(existingMappings)) {
|
||||
if (mapping && typeof mapping === 'object' && 'toJira' in mapping && 'fromJira' in mapping) {
|
||||
addFieldMapping(
|
||||
fieldName,
|
||||
functionToArrowString(mapping.toJira),
|
||||
functionToArrowString(mapping.fromJira)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the string representations for future use
|
||||
saveFieldMappings();
|
||||
}
|
||||
};
|
||||
|
||||
// Make sure to call this function after it's defined
|
||||
loadExistingMappings();
|
||||
|
||||
// Add example mappings section
|
||||
const examplesSection = mappingSection.createDiv({ cls: "mapping-examples" });
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import {JiraSettings} from "../interfaces";
|
||||
import {fieldMappings} from "../tools/mappingObsidianJiraFields";
|
||||
// import {fieldMappings} from "../tools/mappingObsidianJiraFields";
|
||||
|
||||
/**
|
||||
* Default plugin settings
|
||||
|
|
@ -11,5 +11,7 @@ export const DEFAULT_SETTINGS: JiraSettings = {
|
|||
issuesFolder: "jira-issues",
|
||||
sessionCookieName: "JSESSIONID",
|
||||
templatePath: "",
|
||||
fieldMappings: fieldMappings,
|
||||
fieldMappings: {},
|
||||
fieldMappingsStrings: {}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import {Notice} from "obsidian";
|
||||
|
||||
const forbiddenPatterns = ["document", "window", "eval", "Function", "fetch"]; // Prevent unsafe code execution
|
||||
import { jiraToMarkdown, markdownToJira } from "../markdown_html";
|
||||
|
||||
|
|
@ -18,11 +20,31 @@ export function safeStringToFunction(fnString: string): Function | null {
|
|||
// Ensure function body does not contain unsafe references
|
||||
if (!isValidFunctionBody(body)) {
|
||||
console.warn(`Unsafe function detected: ${fnString}`);
|
||||
new Notice(`Unsafe function detected: ${fnString}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create a new function dynamically
|
||||
return new Function(params, `return ${body};`);
|
||||
// Create a function that has access to our utility functions
|
||||
return function(...args: any[]) {
|
||||
// Create a context with our utility functions
|
||||
const context = {
|
||||
jiraToMarkdown,
|
||||
markdownToJira
|
||||
};
|
||||
|
||||
// Create a new function with the utility functions in scope
|
||||
const fn = new Function(
|
||||
...params.split(',').map(p => p.trim()),
|
||||
`
|
||||
const jiraToMarkdown = this.jiraToMarkdown;
|
||||
const markdownToJira = this.markdownToJira;
|
||||
return ${body};
|
||||
`
|
||||
);
|
||||
|
||||
// Execute the function with our context
|
||||
return fn.apply(context, args);
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Failed to parse function string:", error);
|
||||
return null;
|
||||
|
|
@ -33,18 +55,35 @@ export function functionToArrowString(fn: Function): string {
|
|||
try {
|
||||
const fnStr = fn.toString().trim();
|
||||
|
||||
// Match function body and parameters from normal or arrow function formats
|
||||
const arrowMatch = fnStr.match(/^\s*\(?([\w\s,]*)\)?\s*=>\s*(.+)$/s);
|
||||
// Handle arrow functions (both single-line and multi-line)
|
||||
const arrowMatch = fnStr.match(/^\s*\(?([^)]*)\)?\s*=>\s*(.+)$/s);
|
||||
if (arrowMatch) {
|
||||
return `(${arrowMatch[1].trim()}) => ${arrowMatch[2].trim()}`;
|
||||
const params = arrowMatch[1].trim();
|
||||
const body = arrowMatch[2].trim();
|
||||
return `(${params}) => ${body}`;
|
||||
} else {
|
||||
console.error("Failed to parse arrow function:", fnStr);
|
||||
}
|
||||
|
||||
const normalFunctionMatch = fnStr.match(/^function\s*\(?([\w\s,]*)\)?\s*\{([\s\S]*)\}$/);
|
||||
if (normalFunctionMatch) {
|
||||
return `(${normalFunctionMatch[1].trim()}) => ${normalFunctionMatch[2].trim().replace(/^\s*return\s*/, '').replace(/\s*;\s*$/, '')}`;
|
||||
// Handle regular named and anonymous functions
|
||||
const functionMatch = fnStr.match(/^(?:function\s+[\w$]*\s*)?\(([^)]*)\)\s*\{([\s\S]*)\}$/);
|
||||
if (functionMatch) {
|
||||
const params = functionMatch[1].trim();
|
||||
let body = functionMatch[2].trim();
|
||||
|
||||
// If body contains just a return statement, simplify it
|
||||
const returnMatch = body.match(/^\s*return\s+([\s\S]*?)\s*;\s*$/);
|
||||
if (returnMatch) {
|
||||
body = returnMatch[1];
|
||||
}
|
||||
|
||||
return `(${params}) => ${body}`;
|
||||
} else {
|
||||
console.error("Failed to parse function:", fnStr);
|
||||
}
|
||||
|
||||
return fnStr; // Fallback, if we can't match the expected pattern
|
||||
// Return original string if no patterns match
|
||||
return fnStr;
|
||||
} catch (error) {
|
||||
console.error("Error converting function to string:", error);
|
||||
return "";
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { JiraIssue } from "../interfaces";
|
||||
import {jiraToMarkdown, markdownToJira} from "../markdown_html";
|
||||
import {TFile} from "obsidian";
|
||||
import {Notice, TFile} from "obsidian";
|
||||
import JiraPlugin from "../main";
|
||||
import {extractAllJiraSyncValuesFromContent, updateJiraSyncContent} from "./sectionTools";
|
||||
|
||||
|
|
@ -112,10 +112,16 @@ export function localToJiraFields(
|
|||
if (key in customFieldMappings) {
|
||||
const mapping = customFieldMappings[key];
|
||||
|
||||
// Skip fields that shouldn't be sent to Jira
|
||||
if (mapping.toJira(value) === null) continue;
|
||||
try {
|
||||
// Skip fields that shouldn't be sent to Jira
|
||||
if (mapping.toJira(value) === null) continue;
|
||||
|
||||
jiraFields[key] = mapping.toJira(value);
|
||||
jiraFields[key] = mapping.toJira(value);
|
||||
} catch (e) {
|
||||
console.error(`Error mapping for ${key}: ${e}`);
|
||||
new Notice(`Error mapping for ${key}: ${e}`);
|
||||
|
||||
}
|
||||
}
|
||||
// Handle custom fields
|
||||
else {
|
||||
|
|
@ -178,7 +184,13 @@ export function updateLocalRecordsFromJira(
|
|||
if (key in frontmatter || key === 'key' || key === 'summary') {
|
||||
let value = issue.fields[key];
|
||||
if (key in customFieldMappings) {
|
||||
value = customFieldMappings[key].fromJira(issue, {...frontmatter, ...sections});
|
||||
try{
|
||||
value = customFieldMappings[key].fromJira(issue, {...frontmatter, ...sections});
|
||||
} catch (e) {
|
||||
console.error(`Error mapping for ${key}: ${e}`);
|
||||
new Notice(`Error mapping for ${key}: ${e}`);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (value !== null && value !== undefined) {
|
||||
frontmatter[key] = value;
|
||||
|
|
@ -192,7 +204,13 @@ export function updateLocalRecordsFromJira(
|
|||
if (key in sections) {
|
||||
let value = issue.fields[key];
|
||||
if (key in customFieldMappings) {
|
||||
value = customFieldMappings[key].fromJira(issue, {...frontmatter, ...sections});
|
||||
try {
|
||||
value = customFieldMappings[key].fromJira(issue, {...frontmatter, ...sections});
|
||||
} catch (e) {
|
||||
console.error(`Error mapping for ${key}: ${e}`);
|
||||
new Notice(`Error mapping for ${key}: ${e}`);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (value !== null && value !== undefined) {
|
||||
sections[key] = value;
|
||||
|
|
|
|||
Loading…
Reference in a new issue