Skip to content

feat(federation): Federated table sharing#2786

Open
benjaminfrueh wants to merge 2 commits into
mainfrom
feat/federated-sharing
Open

feat(federation): Federated table sharing#2786
benjaminfrueh wants to merge 2 commits into
mainfrom
feat/federated-sharing

Conversation

@benjaminfrueh

@benjaminfrueh benjaminfrueh commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Federated table sharing

Allows sharing tables with users on other Nextcloud instances.

Todo

  • Schema, outgoing share and frontend support
  • Incoming share: create local table on receiving instance
  • Read support: fetch columns and rows from owner instance
  • Write support: create, update and delete rows on remote table
  • Permission propagation
  • Delete local table on receiving instance when table or share is deleted on owner instance
  • Update table metadata (title, emoji) when updated on owner instance
  • Update view metadata (title, emoji) when updated on owner instance
  • View federation
  • Admin config to enable/disable tables federation
  • Unit and E2E tests

🏁 Checklist

  • ⛑️ Tests (unit and/or integration) are included or not needed
  • 🔙 Backport requests are created or not needed: /backport to stableX.X
  • 📅 Milestone is set
  • 🌸 PR title is meaningful (if it should be in the changelog: is it meaningful to users?)

🤖 AI (if applicable)

  • The content of this PR was partly or fully generated using AI

@benjaminfrueh
benjaminfrueh marked this pull request as draft July 3, 2026 14:40
@benjaminfrueh
benjaminfrueh requested a review from grnd-alt July 3, 2026 14:41
@benjaminfrueh benjaminfrueh changed the title feat(federation): schema, outgoing share and frontend support feat(federation): Federated Table Sharing Jul 3, 2026
@benjaminfrueh benjaminfrueh changed the title feat(federation): Federated Table Sharing feat(federation): Federated table sharing Jul 3, 2026
@AndyScherzinger AndyScherzinger added enhancement New feature or request 2. developing Work in progress labels Jul 3, 2026
@github-project-automation github-project-automation Bot moved this to 🧭 Planning evaluation (don't pick) in 📝 Productivity team Jul 3, 2026
@benjaminfrueh benjaminfrueh moved this from 🧭 Planning evaluation (don't pick) to 🏗️ In progress in 📝 Productivity team Jul 3, 2026
@benjaminfrueh
benjaminfrueh force-pushed the feat/federated-sharing branch 7 times, most recently from 403289e to ba97e23 Compare July 8, 2026 07:13
Comment thread lib/Controller/FederationColumnOCSController.php Outdated
@benjaminfrueh
benjaminfrueh force-pushed the feat/federated-sharing branch from 4da0386 to 35b4e5e Compare July 13, 2026 16:28
@benjaminfrueh
benjaminfrueh marked this pull request as ready for review July 14, 2026 14:33
@benjaminfrueh
benjaminfrueh force-pushed the feat/federated-sharing branch 2 times, most recently from 1bf2714 to e948b30 Compare July 14, 2026 14:46
@benjaminfrueh benjaminfrueh added this to the v2.3.0 milestone Jul 14, 2026
@benjaminfrueh
benjaminfrueh force-pushed the feat/federated-sharing branch 5 times, most recently from 8a02508 to 63c7628 Compare July 16, 2026 08:50
@benjaminfrueh
benjaminfrueh force-pushed the feat/federated-sharing branch from 63c7628 to c66887c Compare July 16, 2026 12:38

@grnd-alt grnd-alt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Did not entirely check all code paths yet. But the general structure looks good 🔥

What I noticed though: when creating a federated share it instantly shows as a new table on the receiving user's view. In Deck I think we're sending a notification that the user has to confirm before it shows in the app, so user's don't get spammed.

@benjaminfrueh
benjaminfrueh force-pushed the feat/federated-sharing branch 7 times, most recently from 5be9580 to 4beef8f Compare July 17, 2026 07:57
@benjaminfrueh

Copy link
Copy Markdown
Contributor Author

Did not entirely check all code paths yet. But the general structure looks good 🔥

What I noticed though: when creating a federated share it instantly shows as a new table on the receiving user's view. In Deck I think we're sending a notification that the user has to confirm before it shows in the app, so user's don't get spammed.

Thanks for the feedback! After checking Deck's shareReceived(), it also seems to insert the board immediately and just sends an informational notification without accept/decline flow.
An "Pending shares" acceptance flow, similar to how Files handles federated shares would maybe be a good addition to both deck and tables federated sharing - maybe we can consider it as a follow-up feature?

@benjaminfrueh
benjaminfrueh force-pushed the feat/federated-sharing branch from 4beef8f to a38af3d Compare July 17, 2026 14:10
@benjaminfrueh
benjaminfrueh requested a review from grnd-alt July 17, 2026 14:11
Assisted-by: Claude:claude-sonnet-4-6
Signed-off-by: Benjamin Frueh <benjamin.frueh@gmail.com>
@blizzz
blizzz force-pushed the feat/federated-sharing branch from a38af3d to e6485cc Compare July 22, 2026 18:30
namespace OCA\Tables\AppInfo;

use Exception;
use OC\OCM\OCMSignatoryManager;

@blizzz blizzz Jul 22, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Would be cool (and more sustainable) when there will be a public interface for this, but definitely out of scope for now, might be accompanied by an issue against server if there is not one yet.

(On second look it might be more complicated than that)

@blizzz blizzz left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

some things that would be good to have, but no definite blocker by reading the code

Comment thread lib/Controller/Api1Controller.php Outdated
if ($this->federationService->isNodeFederated($tableId, 'table')) {
$table = $this->tableService->find($tableId, true);
return new DataResponse($this->federationService->getColumns($table));
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Smells a bit as if it wants to be in the try-block, too.

Comment thread lib/Controller/Api1Controller.php Outdated
if ($this->federationService->isNodeFederated($viewId, 'view')) {
$view = $this->viewService->find($viewId, true);
return new DataResponse($this->federationService->getColumns($view));
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

same here

Comment thread lib/Controller/RowController.php Outdated
if ($this->federationService->isNodeFederated($tableId, 'table')) {
$table = $this->tableService->find($tableId, true);
return new DataResponse($this->federationService->getRows($table));
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

and this might want to be within the handleError() callback?

Comment thread lib/Controller/RowController.php Outdated
public function presentInView(int $id, int $viewId): DataResponse {
if ($this->federationService->isNodeFederated($viewId, 'view')) {
return new DataResponse(['present' => true]);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

same here

#[RequirePermission(permission: Application::PERMISSION_UPDATE, typeParam: 'nodeCollection')]
#[ApiRoute(verb: 'PUT', url: '/api/2/{nodeCollection}/{nodeId}/rows/{rowId}', requirements: ['nodeCollection' => '(tables|views)', 'nodeId' => '(\d+)'])]
public function updateRow(string $nodeCollection, int $nodeId, int $rowId, mixed $data): DataResponse {
if (is_string($data)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

$data could be of type string|array then?

Comment thread lib/Federation/FederationProxy.php
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;

class Version002003Date20260630000000 extends SimpleMigrationStep {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

afraid this is sorted not at latest and might not run, but i might be wrong

}

public function notifyPermissionUpdate(Share $share): void {
$this->proxy->sendNotification(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

what if any of those requests do not get through (maybe the remote nextcloud is being updated), will they be retried?

Comment thread psalm.xml
<referencedClass name="Doctrine\DBAL\Types\Types" />
<referencedClass name="OC\Core\Command\Base" />
<referencedClass name="OC\Http\Client\Response" />
<referencedClass name="OC\OCM\OCMSignatoryManager" />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

stub would be preferred

Comment thread psalm.xml
<referencedClass name="Doctrine\DBAL\Schema\Table" />
<referencedClass name="Doctrine\DBAL\Types\Types" />
<referencedClass name="OC\Core\Command\Base" />
<referencedClass name="OC\Http\Client\Response" />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this should have an interface?

Comment thread lib/Federation/FederationProxy.php
$body->rewind();

if (!is_array(json_decode($content, true))) {
throw new \Exception('Error parsing JSON response', $status);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Here and and other places, throwing a generic \Exception is broad, and so would be catching them.

Comment thread lib/Controller/Api1Controller.php Outdated
#[CORS]
#[OpenAPI(scope: OpenAPI::SCOPE_DEFAULT)]
public function indexTableColumns(int $tableId, ?int $viewId): DataResponse {
if ($this->federationService->isNodeFederated($tableId, 'table')) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

and this actually throws when federation is disabled

@blizzz blizzz Jul 22, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

same in the method below
and in RowController
and in the places in the RowOCSController

Signed-off-by: samin-z <samin.zavarkesh@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

2. developing Work in progress AI assisted enhancement New feature or request

Projects

Status: 🏗️ In progress

Development

Successfully merging this pull request may close these issues.

6 participants