~edwargix/git.sr.ht

aaa1f6f287109aaea232a2ce5c86ec0774147a6a — Adnan Maolood 2 years ago 4e8fd8c
api/loaders: Refactor RepositoriesByOwnerRepoName
M api/graph/schema.resolvers.go => api/graph/schema.resolvers.go +2 -2
@@ 222,7 222,7 @@ func (r *mutationResolver) CreateRepository(ctx context.Context, name string, vi
					return valid.Errorf(ctx, "cloneUrl", "Invalid username")
				}
				repo, err := loaders.ForContext(ctx).
					RepositoriesByOwnerRepoName.Load([2]string{entity, repoName})
					RepositoriesByOwnerRepoName.Load(loaders.OwnerRepoName{entity, repoName})
				if err != nil {
					panic(err)
				} else if repo == nil {


@@ 1281,7 1281,7 @@ func (r *treeResolver) Entries(ctx context.Context, obj *model.Tree, cursor *cor

func (r *userResolver) Repository(ctx context.Context, obj *model.User, name string) (*model.Repository, error) {
	// TODO: Load repository with user ID instead of username. Needs a new loader.
	return loaders.ForContext(ctx).RepositoriesByOwnerRepoName.Load([2]string{obj.Username, name})
	return loaders.ForContext(ctx).RepositoriesByOwnerRepoName.Load(loaders.OwnerRepoName{obj.Username, name})
}

func (r *userResolver) Repositories(ctx context.Context, obj *model.User, cursor *coremodel.Cursor, filter *coremodel.Filter) (*model.RepositoryCursor, error) {

M api/loaders/generate.go => api/loaders/generate.go +1 -1
@@ 4,6 4,6 @@
package loaders

//go:generate ./gen RepositoriesByIDLoader int api/graph/model.Repository
//go:generate ./gen RepositoriesByOwnerRepoNameLoader [2]string api/graph/model.Repository
//go:generate ./gen RepositoriesByOwnerRepoNameLoader OwnerRepoName api/graph/model.Repository
//go:generate ./gen UsersByIDLoader int api/graph/model.User
//go:generate ./gen UsersByNameLoader string api/graph/model.User

M api/loaders/middleware.go => api/loaders/middleware.go +27 -25
@@ 3,7 3,9 @@ package loaders
import (
	"context"
	"database/sql"
	"database/sql/driver"
	"errors"
	"fmt"
	"net/http"
	"time"



@@ 166,37 168,37 @@ func fetchRepositoriesByID(ctx context.Context) func(ids []int) ([]*model.Reposi
	}
}

func fetchRepositoriesByOwnerRepoName(ctx context.Context) func(names [][2]string) ([]*model.Repository, []error) {
	return func(names [][2]string) ([]*model.Repository, []error) {
		repos := make([]*model.Repository, len(names))
type OwnerRepoName struct {
	Owner    string
	RepoName string
}

func (or OwnerRepoName) Value() (driver.Value, error) {
	return fmt.Sprintf("(%q,%q)", or.Owner, or.RepoName), nil
}

func fetchRepositoriesByOwnerRepoName(ctx context.Context) func([]OwnerRepoName) ([]*model.Repository, []error) {
	return func(ownerRepoNames []OwnerRepoName) ([]*model.Repository, []error) {
		repos := make([]*model.Repository, len(ownerRepoNames))
		if err := database.WithTx(ctx, &sql.TxOptions{
			Isolation: 0,
			ReadOnly:  true,
		}, func(tx *sql.Tx) error {
			var (
				err    error
				rows   *sql.Rows
				_names []string = make([]string, len(names))
				err  error
				rows *sql.Rows
			)
			for i, name := range names {
				// This is a hack, but it works around limitations with PostgreSQL
				// and is guaranteed to work because / is invalid in both usernames
				// and repo names
				_names[i] = name[0] + "/" + name[1]
			}
			query := database.
				Select(ctx).
				Prefix(`WITH user_repo AS (
					SELECT
						substring(un for position('/' in un)-1) AS owner,
						substring(un from position('/' in un)+1) AS repo
					FROM unnest(?::text[]) un)`, pq.Array(_names)).
				Prefix(`WITH owner_repo_names AS (
					SELECT owner, repo_name
					FROM unnest(?::owner_repo_name[]))`, pq.GenericArray{ownerRepoNames}).
				Columns(database.Columns(ctx, (&model.Repository{}).As(`repo`))...).
				Columns(`u.username`).
				Columns(`o.owner`).
				Distinct().
				From(`user_repo ur`).
				Join(`"user" u on ur.owner = u.username`).
				Join(`repository repo ON ur.repo = repo.name
				From(`owner_repo_names o`).
				Join(`"user" u on o.owner = u.username`).
				Join(`repository repo ON o.repo_name = repo.name
					AND u.id = repo.owner_id`).
				LeftJoin(`access ON repo.id = access.repo_id`).
				Where(sq.Or{


@@ 209,7 211,7 @@ func fetchRepositoriesByOwnerRepoName(ctx context.Context) func(names [][2]strin
			}
			defer rows.Close()

			reposByOwnerRepoName := map[[2]string]*model.Repository{}
			reposByOwnerRepoName := map[OwnerRepoName]*model.Repository{}
			for rows.Next() {
				var ownerName string
				repo := model.Repository{}


@@ 217,14 219,14 @@ func fetchRepositoriesByOwnerRepoName(ctx context.Context) func(names [][2]strin
					database.Scan(ctx, &repo), &ownerName)...); err != nil {
					panic(err)
				}
				reposByOwnerRepoName[[2]string{ownerName, repo.Name}] = &repo
				reposByOwnerRepoName[OwnerRepoName{ownerName, repo.Name}] = &repo
			}
			if err = rows.Err(); err != nil {
				panic(err)
			}

			for i, name := range names {
				repos[i] = reposByOwnerRepoName[name]
			for i, or := range ownerRepoNames {
				repos[i] = reposByOwnerRepoName[or]
			}
			return nil
		}); err != nil {

A gitsrht/alembic/versions/822baa9910cd_add_owner_repo_name_custom_type.py => gitsrht/alembic/versions/822baa9910cd_add_owner_repo_name_custom_type.py +29 -0
@@ 0,0 1,29 @@
"""Add owner_repo_name custom type

Revision ID: 822baa9910cd
Revises: 0a3d114e8a18
Create Date: 2022-02-16 10:06:54.271103

"""

# revision identifiers, used by Alembic.
revision = '822baa9910cd'
down_revision = '0a3d114e8a18'

from alembic import op
import sqlalchemy as sa


def upgrade():
    op.execute("""
    CREATE TYPE owner_repo_name AS (
        owner text,
        repo_name text
    );
    """)


def downgrade():
    op.execute("""
    DROP TYPE owner_repo_name;
    """)