func (s *yorkieServer) DetachDocument(
	ctx context.Context,
	req *api.DetachDocumentRequest,
) (*api.DetachDocumentResponse, error) {
	pack, err := converter.FromChangePack(req.ChangePack)
	if err != nil {
		return nil, err
	}
	if err := auth.VerifyAccess(ctx, s.backend, &types.AccessInfo{
		Method:     types.DetachDocument,
		Attributes: auth.AccessAttributes(pack),
	}); err != nil {
		return nil, err
	}
	if pack.HasChanges() {
		locker, err := s.backend.Coordinator.NewLocker(
			ctx,
			packs.NewPushPullKey(pack.DocumentKey),
		)
		if err != nil {
			return nil, err
		}
		if err := locker.Lock(ctx); err != nil {
			return nil, err
		}
		defer func() {
			if err := locker.Unlock(ctx); err != nil {
				log.Logger.Error(err)
			}
		}()
	}
	clientInfo, docInfo, err := clients.FindClientAndDocument(
		ctx,
		s.backend,
		req.ClientId,
		pack,
		false,
	)
	if err != nil {
		return nil, err
	}
	if err := clientInfo.EnsureDocumentAttached(docInfo.ID); err != nil {
		return nil, err
	}
	if err := clientInfo.DetachDocument(docInfo.ID); err != nil {
		return nil, err
	}
	pulled, err := packs.PushPull(ctx, s.backend, clientInfo, docInfo, pack)
	if err != nil {
		return nil, err
	}
	pbChangePack, err := converter.ToChangePack(pulled)
	if err != nil {
		return nil, err
	}
	return &api.DetachDocumentResponse{
		ChangePack: pbChangePack,
	}, nil
}
DetachDocument detaches the given document to the client.
package rpc
import (
	"context"
	gotime "time"
	"github.com/yorkie-team/yorkie/api"
	"github.com/yorkie-team/yorkie/api/converter"
	"github.com/yorkie-team/yorkie/internal/log"
	"github.com/yorkie-team/yorkie/pkg/document/key"
	"github.com/yorkie-team/yorkie/pkg/types"
	"github.com/yorkie-team/yorkie/yorkie/auth"
	"github.com/yorkie-team/yorkie/yorkie/backend"
	"github.com/yorkie-team/yorkie/yorkie/backend/sync"
	"github.com/yorkie-team/yorkie/yorkie/clients"
	"github.com/yorkie-team/yorkie/yorkie/packs"
)
type yorkieServer struct {
	backend    *backend.Backend
	serviceCtx context.Context
}
func newYorkieServer(serviceCtx context.Context, be *backend.Backend) *yorkieServer {
	return &yorkieServer{
		backend:    be,
		serviceCtx: serviceCtx,
	}
}
func (s *yorkieServer) ActivateClient(
	ctx context.Context,
	req *api.ActivateClientRequest,
) (*api.ActivateClientResponse, error) {
	if req.ClientKey == "" {
		return nil, clients.ErrInvalidClientKey
	}
	if err := auth.VerifyAccess(ctx, s.backend, &types.AccessInfo{
		Method: types.ActivateClient,
	}); err != nil {
		return nil, err
	}
	client, err := clients.Activate(ctx, s.backend, req.ClientKey)
	if err != nil {
		return nil, err
	}
	return &api.ActivateClientResponse{
		ClientKey: client.Key,
		ClientId:  client.ID.Bytes(),
	}, nil
}
func (s *yorkieServer) DeactivateClient(
	ctx context.Context,
	req *api.DeactivateClientRequest,
) (*api.DeactivateClientResponse, error) {
	if len(req.ClientId) == 0 {
		return nil, clients.ErrInvalidClientID
	}
	if err := auth.VerifyAccess(ctx, s.backend, &types.AccessInfo{
		Method: types.DeactivateClient,
	}); err != nil {
		return nil, err
	}
	client, err := clients.Deactivate(ctx, s.backend, req.ClientId)
	if err != nil {
		return nil, err
	}
	return &api.DeactivateClientResponse{
		ClientId: client.ID.Bytes(),
	}, nil
}
func (s *yorkieServer) AttachDocument(
	ctx context.Context,
	req *api.AttachDocumentRequest,
) (*api.AttachDocumentResponse, error) {
	pack, err := converter.FromChangePack(req.ChangePack)
	if err != nil {
		return nil, err
	}
	if err := auth.VerifyAccess(ctx, s.backend, &types.AccessInfo{
		Method:     types.AttachDocument,
		Attributes: auth.AccessAttributes(pack),
	}); err != nil {
		return nil, err
	}
	if pack.HasChanges() {
		locker, err := s.backend.Coordinator.NewLocker(
			ctx,
			packs.NewPushPullKey(pack.DocumentKey),
		)
		if err != nil {
			return nil, err
		}
		if err := locker.Lock(ctx); err != nil {
			return nil, err
		}
		defer func() {
			if err := locker.Unlock(ctx); err != nil {
				log.Logger.Error(err)
			}
		}()
	}
	clientInfo, docInfo, err := clients.FindClientAndDocument(
		ctx,
		s.backend,
		req.ClientId,
		pack,
		true,
	)
	if err != nil {
		return nil, err
	}
	if err := clientInfo.AttachDocument(docInfo.ID); err != nil {
		return nil, err
	}
	pulled, err := packs.PushPull(ctx, s.backend, clientInfo, docInfo, pack)
	if err != nil {
		return nil, err
	}
	pbChangePack, err := converter.ToChangePack(pulled)
	if err != nil {
		return nil, err
	}
	return &api.AttachDocumentResponse{
		ChangePack: pbChangePack,
	}, nil
}