Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: OCC and OCS Calendar Import/Export #49995

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

SebastianKrupinski
Copy link
Contributor

@SebastianKrupinski SebastianKrupinski commented Dec 30, 2024

Summary

This adds the ability to export calendars via the OCS and OCC.

OCC Export

Command: calendar:export
Arguments: userId calendarId format filepath

OCC Import

Command: calendar:import
Arguments: userId calendarId format filepath
Options: errors, validation, supersede, show-created, show-updated, show-skipped, show-errors

OCS Export

Endpoint: /ocs/v2.php/calendar/export
Request GET

/ocs/v2.php/calendar/export?id=personal&format=xcal&user=user1

Request: POST

{
    "id": "personal",
    "format": "xcal", (optional "ical, jcal, xcal", defaults to "ical")
	"options": {
		"rangeStart": 0,
		"rangeCount": 100
	},
	"user": "user1" (optional admin permissions required)
}

OCS Import

Endpoint: /ocs/v2.php/calendar/import
Request: POST

{
	"id": "personal",
	"options": {
		"format": "xcal", (optional "ical, jcal, xcal", defaults to "ical")
		"validation": 0, (0 - no validate, 1 - validate and skip, 2 - validate and error)
		"errors": 0, (0 - continue, 1 - fail)
		"supersede": true
	},
	"user": "user1", (optional admin permissions required)
	"data": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><calendar><event><title>Meeting</title><date>2025-02-18</date></event></calendar>"
}

Performance

  • Export performance bottle neck seems to be CPU core max out due to single process execution, tests on dev setup with 16k of events takes about 5min or 60 items / second
  • Import performance bottle neck seems to be database write performance, tests on dev setup with 16k of events, takes about 30min or 10 items / second

Checklist

@SebastianKrupinski SebastianKrupinski added the 2. developing Work in progress label Dec 30, 2024
@SebastianKrupinski SebastianKrupinski self-assigned this Dec 30, 2024
@SebastianKrupinski SebastianKrupinski force-pushed the enh/issues-563-calendar-import-export branch from b0ee614 to 5198d6b Compare January 18, 2025 19:26
@SebastianKrupinski SebastianKrupinski force-pushed the enh/issues-563-calendar-import-export branch from c69a4fb to 8a6dd83 Compare January 26, 2025 18:15
@SebastianKrupinski
Copy link
Contributor Author

Hi @tcitworld

I was wondering if you would be willing to test this out with some of your larger live data calendars?

@SebastianKrupinski SebastianKrupinski force-pushed the enh/issues-563-calendar-import-export branch from 59a2223 to 67dc029 Compare January 27, 2025 21:35
@SebastianKrupinski SebastianKrupinski force-pushed the enh/issues-563-calendar-import-export branch from 67dc029 to 8ad0991 Compare February 4, 2025 17:18
@SebastianKrupinski SebastianKrupinski marked this pull request as ready for review February 4, 2025 17:19
@SebastianKrupinski SebastianKrupinski force-pushed the enh/issues-563-calendar-import-export branch from 8ad0991 to 213c1aa Compare February 4, 2025 17:20
@SebastianKrupinski SebastianKrupinski force-pushed the enh/issues-563-calendar-import-export branch from a2f9ff4 to 8eff6e4 Compare February 11, 2025 19:37
Copy link
Member

@ChristophWurst ChristophWurst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First round of feedback

*
* @since 32.0.0
*
* @return array
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this be typed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, as long a pslam plays nice.

* @return array
*/
public function import(CalendarImportOptions $options, callable $generator): array {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the blank lines at a method start are strange. please try to avoid them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

/**
* Calendar Export Service
*
* @since 32.0.0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI the @since annotations are used for public API. You don't have to add this for internal structures, because they won't be used in apps.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH, I add these as a way of tracking when something was added, so when I look though our code I can see how new or old something is, and if there are fixes to back port is easy to see how far back you can go.

Do you want me to remove them?

Comment on lines +26 to +27
public function __construct() {
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔥

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean fire as in, this is great or burn it? Lol

// construct options object
$options = new CalendarExportOptions();
// evaluate if provided format is supported
if ($format !== null && !in_array($format, $this->exportService::FORMATS)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

always use in_array in strict mode

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines +41 to +42
#[ApiRoute(verb: 'GET', url: '/export', root: '/calendar')]
#[ApiRoute(verb: 'POST', url: '/export', root: '/calendar')]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get why it is GET and POST

return new DataResponse(['error' => 'user not found'], Http::STATUS_BAD_REQUEST);
}
} else {
$userId = $this->userSession->getUser()->getUID();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

opening the route unauthenticated with no userId passed will lead to a NPE when calling getUID

Copy link
Contributor Author

@SebastianKrupinski SebastianKrupinski Feb 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested this with Insomnia, by sending a request without authentication or cookies.

Requests that are NOT authenticated get rejected by the initial check as expected.

if (!$this->userSession->isLoggedIn()) {
    return new DataResponse([], Http::STATUS_UNAUTHORIZED);
}

Is this sufficient or am I missing something?

};

return new StreamGeneratorResponse($this->exportService->export($calendar, $options), $contentType);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unnecessary blank line

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

#[ApiRoute(verb: 'POST', url: '/import', root: '/calendar')]
#[UserRateLimit(limit: 1, period: 60)]
#[NoAdminRequired]
public function index(string $id, array $options, string $data, ?string $user = null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing return type hint

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing types for options

return new DataResponse(['error' => 'user not found'], Http::STATUS_BAD_REQUEST);
}
} else {
$userId = $this->userSession->getUser()->getUID();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NPE

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comments on the CalendarExportController.

@ChristophWurst
Copy link
Member

When adding a new controller for HTTP routes I'm wondering if we shouldn't got for OCS and openapi specs right away. Could you please check how doable that is with the different input/output formats you want to support?

@SebastianKrupinski
Copy link
Contributor Author

Found a new internal use for this, this morning, calendar subscriptions can use the same import code to reduce redundant code, complexity and increase performance.

@SebastianKrupinski
Copy link
Contributor Author

When adding a new controller for HTTP routes I'm wondering if we shouldn't got for OCS and openapi specs right away. Could you please check how doable that is with the different input/output formats you want to support?

I think we can make it work, Import is good already, just struggling getting a proper phpdoc export

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2. developing Work in progress
Projects
Status: 🏗️ In progress
Development

Successfully merging this pull request may close these issues.

3 participants