본문으로 건너뛰기

예제

Contributor 만들기

Workshop Note에 게시·보관 권한을 추가하고 역할 편집기에 나타난 결과를 확인합니다.

예제 유형 레시피

개요

Contributor 만들기 예제

이 예제는 앞의 Resource Generator에서 만든 Workshop App의 Note에 게시와 보관이라는 업무 권한을 추가합니다. Resource Generator가 만든 조회·생성·수정·삭제 권한은 그대로 두고, Resource 바깥의 업무 동작을 Contributor로 확장하는 흐름입니다.

이 작업이 끝나면

생기는 것확인 위치
WorkshopWorkflowPermissionspackages/workshop/src/Contribution/
workshop.note.publish, workshop.note.archivePermission catalog
노트 게시, 노트 보관 선택지설정 → 역할 → 새 역할 → 권한 → Workshop → 노트

Workshop 노트 권한이 추가된 역할 편집기

위 화면은 이 예제를 적용하고 권한 catalog를 동기화한 실제 결과입니다. 검색창에 안정적인 key prefix인 workshop.note.를 입력하면 Resource Generator가 만든 네 CRUD 권한과 이번 예제의 노트 게시, 노트 보관이 함께 보입니다.

Contributor는 권한 정의를 catalog에 추가할 뿐, App 메뉴나 노트 상세 화면에 게시·보관 버튼을 자동으로 만들지는 않습니다. 버튼, Route, Policy가 이 권한을 소비해야 실제 업무 기능이 완성됩니다.

어떤 계약을 구현하고 Core가 어떻게 받는가

이 예제의 WorkshopWorkflowPermissions는 Core 클래스를 상속하지 않습니다. App SDK의 Nexia\Permission\Contracts\PermissionContribution 인터페이스를 구현하고, catalogPermissionDefinitions()가 정적 권한 정의를 반환하게 합니다.

WorkshopAppManifest::contributionLocations()
  → NexiaContributionRegistry::classesImplementing(PermissionContribution::class)
  → WorkshopWorkflowPermissions::catalogPermissionDefinitions()
  → AppRegistry::permissionDefinitions()
  → PermissionContributionRegistry → PermissionCatalog
  → nexia-access:sync-permissions가 tenant permission 행으로 동기화
  → Access catalog API가 설치된 App의 권한만 역할 편집기에 전달

Resource Generator가 만든 NoteModule은 Resource 전체를 표현하므로 AbstractResourceModule을 상속하고 ContributesResourcePermissions를 통해 CRUD 권한을 제공합니다. 반면 게시·보관은 Resource의 기본 CRUD가 아닌 별도 업무 동작이므로, 여기서는 작은 독립 Contributor가 PermissionContribution만 직접 구현합니다. 즉 파일 위치만으로 동작하는 것이 아니라 등록된 discovery root 안의 구체 클래스가 해당 SDK 인터페이스를 구현했기 때문에 Core가 발견합니다.

발견과 동기화도 서로 다른 단계입니다. 발견은 코드의 정의를 catalog에 올리고, nexia-access:sync-permissions는 그 catalog를 tenant DB에 materialize합니다. 어느 단계도 사용자에게 권한을 부여하지 않습니다.

1. Contributor 파일 추가

App Generator가 만든 WorkshopAppManifest는 src/Contribution/을 discovery 위치로 이미 등록합니다. 예제 파일을 그 아래로 복사합니다.

코드 예시
Shell
cp docs/developers/examples/contributor/WorkshopWorkflowPermissions.php \
  packages/workshop/src/Contribution/WorkshopWorkflowPermissions.php

핵심 구현은 App SDK의 공개 계약만 사용합니다.

코드 예시
PHP
final class WorkshopWorkflowPermissions implements PermissionContribution
{
    public static function catalogPermissionDefinitions(): array
    {
        return PermissionDefinition::many(
            appKey: 'workshop',
            resource: 'note',
            actions: ['publish', 'archive'],
            assignmentScope: AssignmentScope::LegalEntity,
        );
    }
}

PermissionDefinition::many()가 두 action을 각각 workshop.note.publish와 workshop.note.archive로 정규화합니다.

2. 발견과 동기화

먼저 dry run으로 발견 결과와 변경 예정 사항을 확인합니다.

코드 예시
Shell
TENANT=abc123def456 sh docs/developers/examples/contributor/verify.sh

의도한 두 권한이 보이면 실제 tenant catalog를 동기화합니다.

코드 예시
Shell
task artisan -- nexia-access:sync-permissions --tenant=abc123def456 --no-ansi

동기화는 권한을 사용자에게 자동 부여하지 않습니다. 접근 관리자가 Role에 권한을 담고, Legal Entity 범위의 Access Grant로 사용자에게 배정해야 합니다.

3. Contributor 수정

예를 들어 보관 대신 복원을 제공한다면 action을 바꿉니다.

코드 예시
PHP
actions: ['publish', 'restore'],

그 뒤 runtime cache와 permission catalog를 다시 갱신합니다. 코드에서 workshop.note.archive를 지워도 tenant DB의 기존 권한과 배정이 즉시 삭제되지는 않습니다. 사용처와 Access Grant를 확인한 뒤 명시적인 permission retirement 절차를 사용합니다.

자주 발생하는 오류

증상원인해결다시 확인
Doctor의 contributor 수가 늘지 않음클래스 namespace가 틀렸거나 파일이 Manifest의 contribution root 밖에 있음WorkshopWorkflowPermissions.php를 packages/workshop/src/Contribution/에 두고 PermissionContribution 구현을 확인verify.sh가 Contributor discovered.를 출력
sync에서 중복 key 오류NoteModule이 소유한 CRUD key와 새 action key가 겹침Resource 기본 action을 다시 선언하지 말고 publish, archive처럼 별도 업무 action만 게시dry run에 workshop.note.publish와 workshop.note.archive만 새 변경으로 표시됨
권한은 역할 화면에 있지만 버튼이 없음Contributor는 권한 정의만 게시하고 Route·Policy·frontend 동작은 만들지 않음해당 업무 동작의 App Route, Policy와 UI를 별도로 구현허용된 사용자에게 구현한 동작이 보이고 backend 요청도 통과
Role에 권한을 넣었지만 실행할 수 없음Role이 현재 Legal Entity 범위의 Access Grant로 사용자에게 배정되지 않음현재 Legal Entity에서 사용자에게 Role을 배정같은 Legal Entity를 선택한 뒤 요청이 인가됨

포함된 파일

Permission Contributor

docs/developers/examples/contributor/WorkshopWorkflowPermissions.php
코드 예시
PHP
<?php

declare(strict_types=1);

namespace Amuzcorp\Nexia\Workshop\Contribution;

use Nexia\Permission\AssignmentScope;
use Nexia\Permission\Contracts\PermissionContribution;
use Nexia\Permission\PermissionDefinition;

/** Permissions for Note workflow actions that are not generated CRUD actions. */
final class WorkshopWorkflowPermissions implements PermissionContribution
{
    public static function catalogPermissionDefinitions(): array
    {
        return PermissionDefinition::many(
            appKey: 'workshop',
            resource: 'note',
            actions: ['publish', 'archive'],
            assignmentScope: AssignmentScope::LegalEntity,
        );
    }
}

발견 갱신과 확인

docs/developers/examples/contributor/verify.sh
코드 예시
Shell
#!/usr/bin/env sh
set -eu

: "${TENANT:?Set TENANT to the practice tenant ID shown by tenants:list.}"

test -f packages/workshop/src/Contribution/WorkshopWorkflowPermissions.php

task artisan -- nexia-runtime:refresh-runtime-caches --if-route-cached --no-ansi
task artisan -- nexia-apps:doctor-package-app workshop --no-ansi
task artisan -- nexia-access:sync-permissions --tenant="$TENANT" --dry-run --no-ansi

printf '%s\n' 'Contributor discovered. Apply the permission sync, then inspect Settings > Roles > New role.'