vikunja-api/docs/content/doc/development/migration.md
konrad 9e39399689 Migration (#120)
Go mod tidy

[skip ci] Add modules/migration to docs

[skip ci] update date

fmt

Merge branch 'master' into feature/migration

# Conflicts:
#	pkg/routes/api/v1/info.go

Add docs on how to create a migrator

Add available migrators to /info endpoint

Return a message once everything was migrated successfully

Add swagger docs for wunderlist migration

Docs for migration [skip ci]

Fix due date fixture in migration test

Fix staticcheck

Fix lint

Logging and cleanup

Make the migrator work with real data

Add routes for migration

Fix misspell

Add method to store a full vikunja structure into vikunja

Add getting all data from wunderlist

Add attachment migration from wunderlist

Add done and done at to wunderlist migrator

Add todo

Add wunderlist auth url implementation

Fix lint

Finish wunderlist migration

Added all structs for the wunderlist migratior

Fix owner field being null for user shared namespaces (#119)

Update copyright year (#118)

Add option to disable registration (#117)

Added migrator interface

Co-authored-by: kolaente <k@knt.li>
Reviewed-on: https://kolaente.dev/vikunja/api/pulls/120
2020-01-19 16:52:16 +00:00

3.3 KiB

date title draft type menu
2020-01-19:16:00+02:00 Migrations false doc
sidebar
parent
development

Writing a migrator for Vikunja

It is possible to migrate data from other to-do services to Vikunja. To make this easier, we have put together a few helpers which are documented on this page.

In general, each migrator implements a migrator interface which is then called from a client. The interface makes it possible to use helper methods which handle http an focus only on the implementation of the migrator itself.

Structure

All migrator implementations live in their own package in pkg/modules/migration/<name-of-the-service>. When creating a new migrator, you should place all related code inside that module.

Migrator interface

The migrator interface is defined as follows:

// Migrator is the basic migrator interface which is shared among all migrators
type Migrator interface {
	// Migrate is the interface used to migrate a user's tasks from another platform to vikunja.
	// The user object is the user who's tasks will be migrated.
	Migrate(user *models.User) error
	// AuthURL returns a url for clients to authenticate against.
	// The use case for this are Oauth flows, where the server token should remain hidden and not
	// known to the frontend.
	AuthURL() string
}

Defining http routes

Once your migrator implements the migration interface, it becomes possible to use the helper http handlers. Their usage is very similar to the general web handler:

// This is an example for the Wunderlist migrator
if config.MigrationWunderlistEnable.GetBool() {
    wunderlistMigrationHandler := &migrationHandler.MigrationWeb{
		MigrationStruct: func() migration.Migrator {
			return &wunderlist.Migration{}
		},
	}
	m.GET("/wunderlist/auth", wunderlistMigrationHandler.AuthURL)
	m.POST("/wunderlist/migrate", wunderlistMigrationHandler.Migrate)
}

You should also document the routes with [swagger annotations]({{< ref "../practical-instructions/swagger-docs.md" >}}).

Insertion helper method

There is a method available in the migration package which takes a fully nested Vikunja structure and creates it with all relations. This means you start by adding a namespace, then add lists inside of that namespace, then tasks in the lists and so on.

The root structure must be present as []*models.NamespaceWithLists.

Then call the method like so:

fullVikunjaHierachie, err := convertWunderlistToVikunja(wContent)
if err != nil {
    return
}

err = migration.InsertFromStructure(fullVikunjaHierachie, user)

Configuration

You should add at least an option to enable or disable the migration. Chances are, you'll need some more options for things like client ID and secret (if the other service uses oAuth as an authentication flow).

The easiest way to implement an on/off switch is to check whether your migration service is enabled or not when registering the routes, and then simply don't registering the routes in the case it is disabled.

Making the migrator public in /info

You should make your migrator available in the /info endpoint so that frontends can display options to enable them or not. To do this, add an entry to pkg/routes/api/v1/info.go.