forked from ryanmwangi/CalMerger
Compare commits
6 commits
29208d8b4d
...
227ec8a211
Author | SHA1 | Date | |
---|---|---|---|
227ec8a211 | |||
5769c9ce5b | |||
6a428c55e0 | |||
56e5f69cbb | |||
e45c433797 | |||
c4c723d60a |
7 changed files with 140 additions and 5 deletions
8
.dockerignore
Normal file
8
.dockerignore
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
node_modules
|
||||||
|
npm-debug.log
|
||||||
|
.DS_Store
|
||||||
|
tests
|
||||||
|
*.test.js
|
||||||
|
*.log
|
||||||
|
.git
|
||||||
|
.env
|
44
README.md
44
README.md
|
@ -39,6 +39,50 @@ The application also generates a unique URL for the merged calendar and updates
|
||||||
```bash
|
```bash
|
||||||
npm start
|
npm start
|
||||||
```
|
```
|
||||||
|
## Building and Running with Docker
|
||||||
|
|
||||||
|
### 1. Build the Docker Image
|
||||||
|
|
||||||
|
Run the following command to build the Docker image:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build -t calmerger-app .
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Run the Docker Container
|
||||||
|
|
||||||
|
To start the container, use:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d --name calmerger -p 3000:3000 calmerger-app
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
This maps the container's port `3000` to the host system's port `3000`. The application will be accessible at [http://localhost:3000](http://localhost:3000).
|
||||||
|
|
||||||
|
### 3. Using Docker Compose (Optional)
|
||||||
|
|
||||||
|
If you prefer to use Docker Compose, ensure you have a `docker-compose.yml` file in your project directory. Then, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
This will automatically build and start the container based on the configuration in the `docker-compose.yml` file.
|
||||||
|
|
||||||
|
### 4. Stopping the Docker Container
|
||||||
|
|
||||||
|
To stop the running container, use:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker stop calmerger
|
||||||
|
```
|
||||||
|
|
||||||
|
To remove the container:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker rm calmerger
|
||||||
|
```
|
||||||
|
|
||||||
## Running Tests
|
## Running Tests
|
||||||
|
|
||||||
|
|
20
dockerfile
Normal file
20
dockerfile
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# Use an official Node.js runtime as a parent image
|
||||||
|
FROM node:18-alpine
|
||||||
|
|
||||||
|
# Set working directory inside the container
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
# Copy package.json and package-lock.json for installing dependencies
|
||||||
|
COPY package*.json ./
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN npm install --production
|
||||||
|
|
||||||
|
# Copy the rest of the project files
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Expose the port your application runs on (if applicable)
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
# Command to run the application
|
||||||
|
CMD ["node", "src/app.js"]
|
|
@ -3,7 +3,7 @@
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node src/app.js",
|
"start": "node src/app.js",
|
||||||
"test": "jest ./test"
|
"test": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js ./test"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.7.7",
|
"axios": "^1.7.7",
|
||||||
|
|
|
@ -34,9 +34,58 @@ function hasTZID(rawProperty) {
|
||||||
// Function to process DTSTART/DTEND
|
// Function to process DTSTART/DTEND
|
||||||
function processDateTimeProperty(event, propertyName, newEvent) {
|
function processDateTimeProperty(event, propertyName, newEvent) {
|
||||||
const rawProperty = event.getFirstProperty(propertyName)?.toICALString();
|
const rawProperty = event.getFirstProperty(propertyName)?.toICALString();
|
||||||
if (!rawProperty) return;
|
if (!rawProperty) {
|
||||||
|
console.log(`No raw property found for ${propertyName}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const dateTime = event.getFirstPropertyValue(propertyName);
|
console.log(`Raw property: ${rawProperty}`); // Log the raw property
|
||||||
|
|
||||||
|
// Check if it's a date-based event (VALUE=DATE)
|
||||||
|
if (rawProperty.includes('VALUE=DATE')) {
|
||||||
|
console.log(`Date-based event detected for ${propertyName}: ${rawProperty}`);
|
||||||
|
|
||||||
|
// Split to get the date part (should be in the format YYYYMMDD)
|
||||||
|
const dateOnly = rawProperty.split(':')[1]; // e.g., "20231225"
|
||||||
|
console.log(`Extracted date string: ${dateOnly}`);
|
||||||
|
|
||||||
|
if (!dateOnly) {
|
||||||
|
console.error(`Error: Could not extract date from ${rawProperty}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the date string is valid (no dashes, just YYYYMMDD)
|
||||||
|
const year = dateOnly.slice(0, 4);
|
||||||
|
const month = dateOnly.slice(4, 6);
|
||||||
|
const day = dateOnly.slice(6, 8);
|
||||||
|
|
||||||
|
console.log(`Parsed date: ${year}-${month}-${day}`);
|
||||||
|
|
||||||
|
// Check if the date is valid
|
||||||
|
if (!year || !month || !day || isNaN(new Date(`${year}-${month}-${day}`))) {
|
||||||
|
console.error(`Invalid date parsed from raw property: ${rawProperty}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formattedDate = dateOnly; // Use the date string as is (YYYYMMDD format)
|
||||||
|
console.log(`Formatted date: ${formattedDate}`);
|
||||||
|
|
||||||
|
// Log before adding the property to ensure it's correct
|
||||||
|
console.log(`Adding date-based property with value: ${propertyName};VALUE=DATE:${formattedDate}`);
|
||||||
|
|
||||||
|
// Correct property name usage (DTSTART not dtstart)
|
||||||
|
const property = new ICAL.Property(propertyName.toUpperCase(), newEvent); // Use uppercase "DTSTART"
|
||||||
|
property.setValue(`VALUE=DATE:${formattedDate}`);
|
||||||
|
|
||||||
|
// Log the property object before adding it to ensure everything is correct
|
||||||
|
console.log(`Property to add:`, property);
|
||||||
|
|
||||||
|
newEvent.addProperty(property);
|
||||||
|
} else {
|
||||||
|
console.log(`Time-based event detected for ${propertyName}: ${rawProperty}`);
|
||||||
|
|
||||||
|
// Time-based event processing (existing logic)
|
||||||
|
const dateTime = event.getFirstPropertyValue(propertyName);
|
||||||
const dateTimeString = dateTime.toString();
|
const dateTimeString = dateTime.toString();
|
||||||
|
|
||||||
const property = new ICAL.Property(propertyName, newEvent);
|
const property = new ICAL.Property(propertyName, newEvent);
|
||||||
|
@ -48,8 +97,10 @@ function processDateTimeProperty(event, propertyName, newEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
newEvent.addProperty(property);
|
newEvent.addProperty(property);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Create a top-level VCALENDAR component
|
// Create a top-level VCALENDAR component
|
||||||
export function createCalendarComponent(name) {
|
export function createCalendarComponent(name) {
|
||||||
const calendarComponent = new ICAL.Component(['vcalendar', [], []]);
|
const calendarComponent = new ICAL.Component(['vcalendar', [], []]);
|
||||||
|
@ -157,7 +208,7 @@ export function addEventsToCalendar(calendarComponent, results, overrideFlag = f
|
||||||
export function saveCalendarFile(filename, content) {
|
export function saveCalendarFile(filename, content) {
|
||||||
const normalizedContent = content.replace(/\r?\n/g, '\r\n').trimEnd(); // Normalize to CRLF
|
const normalizedContent = content.replace(/\r?\n/g, '\r\n').trimEnd(); // Normalize to CRLF
|
||||||
const filePath = path.join(MERGED_CALENDARS_DIR, filename);
|
const filePath = path.join(MERGED_CALENDARS_DIR, filename);
|
||||||
// console.log(`Saving calendar data to file: ${filePath}`);
|
console.log(`Saving calendar data to file: ${filePath}`);
|
||||||
fs.writeFileSync(filePath, normalizedContent);
|
fs.writeFileSync(filePath, normalizedContent);
|
||||||
return filePath;
|
return filePath;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,12 @@ import request from 'supertest';
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import { jest } from '@jest/globals';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
|
// ESM equivalent of __dirname
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
const CALENDARS_DIR = path.join(__dirname, 'calendar');
|
const CALENDARS_DIR = path.join(__dirname, 'calendar');
|
||||||
const TEST_CALENDARS_DIR = path.join(__dirname, 'test_calendars');
|
const TEST_CALENDARS_DIR = path.join(__dirname, 'test_calendars');
|
||||||
|
@ -10,7 +16,7 @@ const EXPECTED_OUTPUTS_DIR = path.join(__dirname, 'expected_outputs');
|
||||||
let server;
|
let server;
|
||||||
process.chdir(__dirname)
|
process.chdir(__dirname)
|
||||||
// console.log(process.cwd());
|
// console.log(process.cwd());
|
||||||
const app = require('../src/server').default;
|
import app from '../src/server.js';
|
||||||
|
|
||||||
const normalizeLineEndings = (str) => str.replace(/\r\n/g, '\r\n').trimEnd(); // Normalize to CRLF
|
const normalizeLineEndings = (str) => str.replace(/\r\n/g, '\r\n').trimEnd(); // Normalize to CRLF
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,12 @@ import ICAL from '../src/lib/ical.timezones';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import { jest } from '@jest/globals';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
|
// ESM equivalent of __dirname
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
// Describe the test suite for Calendar Utility Functions
|
// Describe the test suite for Calendar Utility Functions
|
||||||
describe('Calendar Utility Functions', () => {
|
describe('Calendar Utility Functions', () => {
|
||||||
|
|
Loading…
Add table
Reference in a new issue