Skip to content

Commit

Permalink
Add migrations
Browse files Browse the repository at this point in the history
Add first complete route
Improve endpoints REST style
  • Loading branch information
redblom committed Jun 13, 2024
1 parent 202b9c7 commit df986d4
Show file tree
Hide file tree
Showing 18 changed files with 1,747 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# SPDX-FileCopyrightText: Antoon Prins <[email protected]>
# SPDX-License-Identifier: AGPL-3.0-or-later
/js/* binary
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# SPDX-FileCopyrightText: Antoon Prins <[email protected]>
# SPDX-License-Identifier: AGPL-3.0-or-later
*.iml
.idea
/.php-cs-fixer.cache
/.php_cs.cache
/build/
/vendor/
js/*hot-update.*
node_modules/
214 changes: 214 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
# SPDX-FileCopyrightText: Bernhard Posselt <[email protected]>
# SPDX-License-Identifier: AGPL-3.0-or-later

# Generic Makefile for building and packaging a Nextcloud app which uses npm and
# Composer.
#
# Dependencies:
# * make
# * which
# * curl: used if phpunit and composer are not installed to fetch them from the web
# * tar: for building the archive
# * npm: for building and testing everything JS
#
# If no composer.json is in the app root directory, the Composer step
# will be skipped. The same goes for the package.json which can be located in
# the app root or the js/ directory.
#
# The npm command by launches the npm build script:
#
# npm run build
#
# The npm test command launches the npm test script:
#
# npm run test
#
# The idea behind this is to be completely testing and build tool agnostic. All
# build tools and additional package managers should be installed locally in
# your project, since this won't pollute people's global namespace.
#
# The following npm scripts in your package.json install and update the bower
# and npm dependencies and use gulp as build system (notice how everything is
# run from the node_modules folder):
#
# "scripts": {
# "test": "node node_modules/gulp-cli/bin/gulp.js karma",
# "prebuild": "npm install && node_modules/bower/bin/bower install && node_modules/bower/bin/bower update",
# "build": "node node_modules/gulp-cli/bin/gulp.js"
# },

app_name=invitation
# VERSION=please_set_version
version=$(version)
app_dir_name=$(notdir $(CURDIR))
build_tools_directory=$(CURDIR)/build/tools
source_build_directory=$(CURDIR)/build/artifacts/source
source_package_name=$(source_build_directory)/$(app_name)
appstore_build_directory=$(CURDIR)/build/artifacts/app
appstore_package_name=$(appstore_build_directory)/$(app_name)_$(version)
npm=$(shell which npm 2> /dev/null)
composer=$(shell which composer 2> /dev/null)

all: build

# Code sniffing: PSR-12 is followed
# full check, gives all errors and warnings
.PHONY: php-codesniffer-full
php-codesniffer-full:
$(CURDIR)/vendor/bin/phpcs appinfo/ lib/ templates/ tests/docker/integration-tests/src --standard=PSR12 --report=full

# check for errors only, ignoring warnings
.PHONY: php-codesniffer-errors
php-codesniffer-errors:
$(CURDIR)/vendor/bin/phpcs \
appinfo/ \
lib/ \
templates/ --standard=PSR12 --report=full --warning-severity=0
# TODO tests/docker/integration-tests/src --standard=PSR12 --report=full --warning-severity=0

# should fix (most) errors
.PHONY: php-codesniffer-errors-fix
php-codesniffer-errors-fix:
$(CURDIR)/vendor/bin/phpcbf \
appinfo/ \
lib/ \
templates/ --standard=PSR12
# TODO tests/docker/integration-tests/src --standard=PSR12

# Fetches the PHP and JS dependencies and compiles the JS. If no composer.json
# is present, the composer step is skipped, if no package.json or js/package.json
# is present, the npm step is skipped
.PHONY: build
build:
ifneq (,$(wildcard $(CURDIR)/composer.json))
make composer
endif
ifneq (,$(wildcard $(CURDIR)/package.json))
make npm
endif
ifneq (,$(wildcard $(CURDIR)/js/package.json))
make npm
endif

# Installs and updates the composer dependencies. If composer is not installed
# a copy is fetched from the web
.PHONY: composer
composer:
ifeq (, $(composer))
@echo "No composer command available, downloading a copy from the web"
mkdir -p $(build_tools_directory)
curl -sS https://getcomposer.org/installer | php
mv composer.phar $(build_tools_directory)
php $(build_tools_directory)/composer.phar install --prefer-dist
else
composer install --prefer-dist
endif

# Installs npm dependencies
.PHONY: npm
npm:
ifeq (,$(wildcard $(CURDIR)/package.json))
cd js && $(npm) run build
else
npm run build
endif

# Removes the appstore build
.PHONY: clean
clean:
rm -rf ./build

# Same as clean but also removes dependencies installed by composer, bower and
# npm
.PHONY: distclean
distclean: clean
rm -rf vendor
rm -rf node_modules
rm -rf js/vendor
rm -rf js/node_modules

# Builds the source and appstore package
.PHONY: dist
dist:
make source
make appstore

# Builds the source package
.PHONY: source
source:
rm -rf $(source_build_directory)
mkdir -p $(source_build_directory)
tar cvzf $(source_package_name).tar.gz \
--exclude-vcs \
--exclude="../$(app_name)/build" \
--exclude="../$(app_name)/js/node_modules" \
--exclude="../$(app_name)/node_modules" \
--exclude="../$(app_name)/*.log" \
--exclude="../$(app_name)/js/*.log" \
../$(app_name) \

# Builds the source package for the app store, ignores php and js tests
# command: make version={version_number} buildapp
.PHONY: buildapp
buildapp:
rm -rf $(appstore_build_directory)
mkdir -p $(appstore_build_directory)
# concatenate cd, ls and tar commands with '&&' otherwise the script context will remain the root instead of build
cd build && \
ln -s ../ $(app_name) && \
tar cvzfh $(appstore_package_name).tar.gz \
--exclude="$(app_name)/build" \
--exclude="$(app_name)/release" \
--exclude="$(app_name)/tests" \
--exclude="$(app_name)/Makefile" \
--exclude="$(app_name)/*.log" \
--exclude="$(app_name)/phpunit*xml" \
--exclude="$(app_name)/composer.*" \
--exclude="$(app_name)/js/node_modules" \
--exclude="$(app_name)/js/tests" \
--exclude="$(app_name)/js/test" \
--exclude="$(app_name)/js/*.log" \
--exclude="$(app_name)/js/package.json" \
--exclude="$(app_name)/js/bower.json" \
--exclude="$(app_name)/js/karma.*" \
--exclude="$(app_name)/js/protractor.*" \
--exclude="$(app_name)/package.json" \
--exclude="$(app_name)/bower.json" \
--exclude="$(app_name)/karma.*" \
--exclude="$(app_name)/protractor\.*" \
--exclude="$(app_name)/.*" \
--exclude="$(app_name)/js/.*" \
--exclude-vcs \
$(app_name) && \
rm $(app_name)

# Builds the source package for the app store, includes artifacts required for tests
# command: make version={version_number} buildapp
.PHONY: buildapp-tests
buildapp-tests:
rm -rf $(appstore_build_directory)
mkdir -p $(appstore_build_directory)
# concatenate cd, ls and tar commands with '&&' otherwise the script context will remain the root instead of build
cd build && \
ln -s ../ $(app_name) && \
tar cvzfh $(appstore_package_name).tar.gz \
--exclude="$(app_name)/build" \
--exclude="$(app_name)/Makefile" \
--exclude="$(app_name)/*.log" \
--exclude="$(app_name)/js/node_modules" \
--exclude="$(app_name)/js/tests" \
--exclude="$(app_name)/js/test" \
--exclude="$(app_name)/js/*.log" \
--exclude="$(app_name)/js/package.json" \
--exclude="$(app_name)/js/bower.json" \
--exclude="$(app_name)/js/karma.*" \
--exclude="$(app_name)/js/protractor.*" \
--exclude="$(app_name)/package.json" \
--exclude="$(app_name)/bower.json" \
--exclude="$(app_name)/karma.*" \
--exclude="$(app_name)/protractor\.*" \
--exclude="$(app_name)/.*" \
--exclude="$(app_name)/js/.*" \
--exclude-vcs \
$(app_name) && \
rm $(app_name)
27 changes: 27 additions & 0 deletions appinfo/info.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0"?>
<info xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
<!--
SPDX-FileCopyrightText: Antoon Prins <[email protected]>
SPDX-License-Identifier: CC0-1.0
-->
<id>invitation</id>
<name>Invitation</name>
<summary>Enhanced federated sharing between Nextcloud instances.</summary>
<description><![CDATA[This app gives an enhanced federated sharing user experience by implementing an Invitation Workflow. Through a simple invitation by email to a user of another nextcloud instance the federated user (cloud) IDs are automatically exchanged and saved on each other&#x27;s systems. From thereon both users can easily start federated sharing with each other via the regular file sharing dialog, because the share dialog will also search for and display invited remote users.]]></description>
<version>0.0.1</version>
<licence>agpl</licence>
<author mail="[email protected]" >Antoon Prins</author>
<namespace>Invitation</namespace>
<category>integration</category>
<bugs>https://github.com/sara-nl/nc-invitation/issues</bugs>
<dependencies>
<nextcloud min-version="28" max-version="28"/>
</dependencies>
<navigations>
<navigation>
<name>Invitation</name>
<route>invitation.page.index</route>
</navigation>
</navigations>
</info>
89 changes: 89 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php

// SPDX-FileCopyrightText: Antoon Prins <[email protected]>
// SPDX-License-Identifier: AGPL-3.0-or-later

/**
* Create your routes in here. The name is the lowercase name of the controller
* without the controller part, the stuff after the hash is the method.
* eg. page#index -> OCA\Invitation\Controller\PageController->index()
*
*
* General endpoint syntax follows REST good practices:
* /resource/{resourceProperty}?param=value
*
* Query parameter names are written camel case:
* eg. GET /[email protected]@surf.nl
* Query filter, sorting, pagination, navigation parameter names are written snake case:
* eg. _next,
* _sort,
* timestamp_after ('_after' filter parameter name appended to entity parameter name)
*
*/

declare(strict_types=1);

return [
'resources' => [
'note' => ['url' => '/notes'],
'note_api' => ['url' => '/api/0.1/notes']
],
'routes' => [
// bespoke API - invitation
['name' => 'invitation#index', 'url' => '/index', 'verb' => 'GET'],
['name' => 'invitation#find_by_token', 'url' => '/invitations/{token}', 'verb' => 'GET'],
['name' => 'invitation#find', 'url' => '/invitations', 'verb' => 'GET'],

// unprotected endpoint /invitation
['name' => 'invitation#invitation', 'url' => '/invite/{token}', 'verb' => 'GET'],
['name' => 'invitation#invitation_form', 'url' => '/invitation-form', 'verb' => 'GET'],
['name' => 'invitation#generate_invite', 'url' => '/generate-invite', 'verb' => 'POST'],
['name' => 'invitation#handle_invite', 'url' => '/handle-invite', 'verb' => 'GET'],
['name' => 'invitation#accept_invite', 'url' => '/accept-invite/{token}', 'verb' => 'PUT'],
['name' => 'invitation#decline_invite', 'url' => '/decline-invite/{token}', 'verb' => 'PUT'],
['name' => 'invitation#update', 'url' => '/update-invitation', 'verb' => 'PUT'],

// bespoke API - remote user
['name' => 'remote_user#search', 'url' => '/remote-user/search', 'verb' => 'GET'],
['name' => 'remote_user#get_remote_user', 'url' => '/remote-user', 'verb' => 'GET'],

// bespoke API - mesh registry
['name' => 'mesh_registry#forward_invite', 'url' => '/registry/forward-invite', 'verb' => 'GET'],

// route '/registry/invitation-service-provider' concerns remote invitation service providers
// returns the properties of the invitation service provider like endpoint, domain, name
['name' => 'mesh_registry#invitation_service_provider', 'url' => '/registry/invitation-service-provider', 'verb' => 'GET'],
// adds a remote invitation service provider
['name' => 'mesh_registry#add_invitation_service_provider', 'url' => '/registry/invitation-service-provider', 'verb' => 'POST'],
// update the properties of this invitation service provider
['name' => 'mesh_registry#update_invitation_service_provider', 'url' => '/registry/invitation-service-provider', 'verb' => 'PUT'],
['name' => 'mesh_registry#delete_invitation_service_provider', 'url' => '/registry/invitation-service-provider', 'verb' => 'DELETE'],

// route '/registry/invitation-service-providers' returns all providers
['name' => 'mesh_registry#invitation_service_providers', 'url' => '/registry/invitation-service-providers', 'verb' => 'GET'],

// route '/endpoint' of this instance
['name' => 'mesh_registry#get_endpoint', 'url' => '/registry/endpoint', 'verb' => 'GET'],
['name' => 'mesh_registry#set_endpoint', 'url' => '/registry/endpoint', 'verb' => 'PUT'],

// route '/name' of this instance
['name' => 'mesh_registry#get_name', 'url' => '/registry/name', 'verb' => 'GET'],
['name' => 'mesh_registry#set_name', 'url' => '/registry/name', 'verb' => 'PUT'],

// route '/share-with-invited-users-only' of this instance
['name' => 'mesh_registry#get_allow_sharing_with_invited_users_only', 'url' => '/share-with-invited-users-only', 'verb' => 'GET'],
['name' => 'mesh_registry#set_allow_sharing_with_invited_users_only', 'url' => '/share-with-invited-users-only', 'verb' => 'PUT'],

// OCM - Open Cloud Mesh protocol
['name' => 'ocm#invite_accepted', 'url' => '/ocm/invite-accepted', 'verb' => 'POST'],

// miscellaneous endpoints
['name' => 'page#wayf', 'url' => '/page/wayf', 'verb' => 'GET'],
['name' => 'error#invitation', 'url' => 'error/invitation', 'verb' => 'GET'],

[
'name' => 'note_api#preflighted_cors', 'url' => '/api/0.1/{path}',
'verb' => 'OPTIONS', 'requirements' => ['path' => '.+']
]
]
];
39 changes: 39 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "nextcloud/invitation",
"description": "Enhanced federated sharing between Nextcloud instances.",
"type": "project",
"license": "AGPL-3.0-or-later",
"authors": [
{
"name": "Antoon Prins"
}
],
"require-dev": {
"phpunit/phpunit": "^9",
"sabre/dav": "^4.1",
"sabre/xml": "^2.2",
"symfony/event-dispatcher": "^5.3.11",
"nextcloud/ocp": "dev-stable28",
"psalm/phar": "^5.17.0",
"nextcloud/coding-standard": "^v1.1.1",
"squizlabs/php_codesniffer": "3.*"
},
"scripts": {
"lint": "find . -name \\*.php -not -path './vendor/*' -print0 | xargs -0 -n1 php -l",
"cs:check": "php-cs-fixer fix --dry-run --diff",
"cs:fix": "php-cs-fixer fix",
"psalm": "psalm.phar --threads=1",
"psalm:update-baseline": "psalm.phar --threads=1 --update-baseline",
"psalm:update-baseline:force": "psalm.phar --threads=1 --update-baseline --set-baseline=tests/psalm-baseline.xml",
"psalm:clear": "psalm.phar --clear-cache && psalm --clear-global-cache",
"psalm:fix": "psalm.phar --alter --issues=InvalidReturnType,InvalidNullableReturnType,MissingParamType,InvalidFalsableReturnType"
},
"config": {
"allow-plugins": {
"composer/package-versions-deprecated": true
},
"platform": {
"php": "8.0"
}
}
}
Loading

0 comments on commit df986d4

Please sign in to comment.