[과제] Dooray API를 이용하여 태스크 생성 자동화 프로그램 구현

hwwwa·2023년 2월 7일
0

과제 내용

과제 설명

  • 특정 두레이 프로젝트에 일일 스크럼 태스크를 자동 등록해주는 프로그램 작성
    • 일일 태스크 생성: [2023/01/10 (화) 일일 스크럼] 등 일일 스크럼 태스크를 1주일 전 매일 자동 생성
    • 주간 태스크 생성: 2023/01/10(화) 데이터운영팀 주간 업무 회의 의 주간 회의 태스크는 다음주 회의 태스크 자동 생성
  • 2023 일일스크럼 혹은 2023 주간업무회의 태스크 하위에 신규 생성 태스크 추가
  • 등록자/담당자 특정 멤버 지정
  • 추가적으로 메신저 hook 등 기타 두레이 API를 사용한 연동 기능 자유롭게 추가 가능

참고자료

환경

  • 언어/환경 자유롭게 사용 (지난번 과제에 사용한 인스턴스 사용)

과제 리뷰 방법

  • 프로그램 시연
  • 프로그램 로직 설명 자료

개발 환경

  • MacOS
  • intel 7i / x86_64
  • Shell Script

Dependency

jq

  • JSON 포맷의 데이터를 다루는 커맨드라인 유틸리티
  • MacOS에서 설치
brew install jq
  • CentOS에서 설치
sudo yum install -y epel-release
sudo yum install -y jq

프로세스 순서도

코드 설명

create_scrum.sh

일주일 뒤의 일일 스크럼 태스크를 등록하는 프로그램

  • shell 설정
#!/usr/bin/env bash
export LANG=ko_KR.UTF-8
export PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin
  • 사용자 입력이 필요한 정보
# API token 지정
token="토큰 입력"

# 프로젝트 분류 지정 ( public(일반프로젝트) | private(개인프로젝트) )
project_type="private"

# 프로젝트의 이름
project_name="@inhwa.jo"

# 담당자 email 지정
member_email="~~@nhn.com"

# 태그 이름 지정 - 스크럼
tag_name="스크럼"

# 상위 스크럼 제목 지정
parent_title=`date -v "+1w" "+[%Y 스크럼]"`

# 스크럼 제목 설정 (현재 날짜에서 1주일 더하기)
title=`date -v "+1w" "+[%Y/%m/%d (%a) 일일 스크럼]"`

# 업무 내용 지정(markdown 형태)
content="* 오늘 한 일 \n* 앞으로 할 일 \n* 회의 및 교육 참석 \n* 기타 - 휴가 등"
  • 위에 지정한 내용에 따라 필요한 정보를 API를 통해 받아오기
# API 요청시에 공통으로 쓰이는 url
url="https://api.dooray.com"

# 지정한 프로젝트의 id를 검색하여 받아오기
project_id=`curl -X GET ${url}/project/v1/projects?type=${project_type} \
                 -H 'Authorization: dooray-api '${token}'' \
                 | jq '.result[]' \
                 | jq 'select(.code == "'${project_name}'")' \
                 | jq -r '.id'`

# 담당자 id를 검색하여 받아오기
member_id=`curl -X GET ${url}/common/v1/members?externalEmailAddresses={${member_email}} \
                -H 'Authorization: dooray-api '${token}'' \ 
                | jq -r '.result[].id'`

# 태그 id를 검색하여 받아오기
tag_id=`curl -X GET ${url}/project/v1/projects/${project_id}/tags \
             -H 'Authorization: dooray-api '${token}'' \
             | jq '.result[]' \
             | jq 'select(.name == "'${tag_name}'")' \
             | jq -r '.id'`

# 상위 업무의 id를 검색하여 받아오기
parent_id=`curl -X GET ${url}/project/v1/projects/${project_id}/posts?tagIds=${tag_id} \
                -H 'Authorization: dooray-api '${token}'' \ 
                | jq '.result[]' \
                | jq --arg parent_title "$parent_title" 'select(.subject==$parent_title)' \
                | jq -r '.id'`
  • 상위 스크럼 업무가 존재하는지 확인 및 생성
# 상위 업무 생성 POST 요청에 사용할 json 형태의 body
posts_body=$(cat <<EOF
{
	"users": {
		"to": [{
			"type": "member",
            "member": {
                "organizationMemberId": "${member_id}"
            }
        }]
    },
    "subject": "${parent_title}",
    "body": {
        "mimeType": "text/x-markdown",
        "content": "${content}"
    },
    "dueDateFlag": false,
    "milestoneId": null,
    "tagIds": ["${tag_id}"],
    "priority": "none"
}
EOF
)

# 상위 업무가 존재하는지 확인
if [ -z "${parent_id}" ]
then	# 존재하지 않는 경우 상위 업무 생성
	now=`date "+%Y/%m/%d %H:%M"`
	echo -e "${now}>\033[95m ${parent_title} 상위 태스크가 존재하지 않습니다. 상위 태스크를 생성합니다...\033[0m"

	# 상위 업무 생성 POST Request
	result=`curl -d "${posts_body}" \
                 -X POST ${url}/project/v1/projects/${project_id}/posts \
                 -H 'Content-Type: application/json' \
                 -H 'Authorization: dooray-api '${token}''`

	# 업무 생성 성공여부 확인
	success=`echo ${result} | jq '.header.isSuccessful'`

	now=`date "+%Y/%m/%d %H:%M"`
	if [ "${success}" == "true" ]
	then    # 업무 생성에 성공한 경우
        # 생성된 업무의 id를 상위 업무 id로 지정
	    parent_id=`echo ${result} | jq -r '.result.id'`
            echo -e "${now}>\033[92m ${parent_title} 태스크가 생성되었습니다.\033[0m"
		curl -d '{"text":"상위 스크럼 태스크를 생성하였습니다.","organizationMemberId":"'${member_id}'"}' \
        -X POST ${url}/messenger/v1/channels/direct-send \
        -H 'Content-Type: application/json' \
        -H 'Authorization: dooray-api '${token}'' > /dev/null
	else    # 업무 생성에 실패한 경우 오류 메세지를 함께 출력
	    echo -e "${now}>\033[95m ${parent_title} 태스크 생성에 실패하였습니다.\033[0m"
	    echo ${result} | jq '.header.resultMessage'
		curl -d '{"text":"상위 스크럼 태스크 생성에 실패하였습니다.","organizationMemberId":"'${member_id}'"}' \
            -X POST ${url}/messenger/v1/channels/direct-send \
            -H 'Content-Type: application/json' \
            -H 'Authorization: dooray-api '${token}'' > /dev/null
        exit 0
	fi
fi
  • 다음주의 스크럼 업무가 존재하는지 확인
    • 존재한다면 오류 메세지를 띄우고 종료
# 생성하려는 날짜의 스크럼이 이미 존재하는지 확인
check=`curl -X GET ${url}/project/v1/projects/${project_id}/posts\?tagIds\=${tag_id} \
            -H 'Authorization: dooray-api '${token}'' \ 
            | jq | fgrep -w "${title}"`

if [ -n "${check}" ]
then	# 이미 스크럼이 존재하는 경우 프로그램 종료
	now=`date "+%Y/%m/%d %H:%M"`
	echo -e "${now}>\033[95m ${title} 태스크가 이미 존재합니다.\033[0m"
	curl -d '{"text":"다음주의 스크럼 태스크가 이미 존재합니다.","organizationMemberId":"'${member_id}'"}' \
         -X POST ${url}/messenger/v1/channels/direct-send \
         -H 'Content-Type: application/json' \
         -H 'Authorization: dooray-api '${token}'' > /dev/null
	exit 0
fi
  • 다음주의 스크럼 업무 생성
# 업무 생성 POST 요청에 사용할 json 형태의 body
posts_body=$(cat <<EOF
{
    "parentPostId": "${parent_id}",
	"users": {
		"to": [{
			"type": "member",
            "member": {
                "organizationMemberId": "${member_id}"
            }
        }]
    },
    "subject": "${title}",
    "body": {
        "mimeType": "text/x-markdown",
        "content": "${content}"
    },
    "dueDateFlag": false,
    "milestoneId": null,
    "tagIds": ["${tag_id}"],
    "priority": "none"
}
EOF
)

# 업무 생성 POST Request
result=`curl -d "${posts_body}" \
             -X POST https://api.dooray.com/project/v1/projects/${project_id}/posts \
             -H 'Content-Type: application/json' \
             -H 'Authorization: dooray-api '${token}''`

# 업무 생성 성공여부 확인
success=`echo ${result} | jq '.header.isSuccessful'`

now=`date "+%Y/%m/%d %H:%M"`
if [ "${success}" == "true" ]
then	# 업무 생성에 성공한 경우
	echo -e "${now}>\033[92m ${title} 태스크가 생성되었습니다.\033[0m"
	curl -d '{"text":"다음주의 스크럼 태스크를 생성하였습니다.","organizationMemberId":"'${member_id}'"}' \
         -X POST ${url}/messenger/v1/channels/direct-send \
         -H 'Content-Type: application/json' \
         -H 'Authorization: dooray-api '${token}'' > /dev/null
else	# 업무 생성에 실패한 경우 오류 메세지를 함께 출력
	echo -e "${now}>\033[95m ${title} 태스크 생성에 실패하였습니다.\033[0m"
	echo ${result} | jq '.header.resultMessage'
	curl -d '{"text":"다음주의 스크럼 태스크 생성에 실패하였습니다.","organizationMemberId":"'${member_id}'"}' \
         -X POST ${url}/messenger/v1/channels/direct-send \
         -H 'Content-Type: application/json' \
         -H 'Authorization: dooray-api '${token}'' > /dev/null
fi
  • 성공적으로 스크럼 태스크가 생성된 경우
    • 2023 일일스크럼이 생성되었고 상위 태스크로 잘 지정된 것을 확인 가능
    • 메신저로 생성 유무가 잘 전송된 것을 확인 가능
  • 이미 해당 날짜의 스크럼이 생성되어 있는 경우
  • 태스크 생성에 실패한 경우


create_weekly_meeting.sh

  • 일주일 뒤의 주간 회의 태스크를 등록하는 프로그램 (위와 유사한 로직)
#!/usr/bin/env bash
export LANG=ko_KR.UTF-8
export PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin


# API token 지정
token="토큰 입력"

# 프로젝트 분류 지정 ( public | private )
project_type="private"

# 프로젝트의 이름
project_name="@inhwa.jo"

# 담당자 email 지정
member_email="inhwa.jo@nhn.com"

# 태그 이름 지정 - 주간업무 회의
tag_name="주간업무 회의"

# 상위 업무 제목 지정
parent_title=`date -v "+1w" "+%Y 주간업무회의"`

# 주간 업무 회의 제목 설정 (현재 날짜에서 1주일 더하기)
title=`date -v "+1w" "+%Y/%m/%d(%a) 데이터운영팀 주간 업무 회의"`

# 업무 내용 지정(markdown 형태)
content="${title}"


# --------------(수정 불필요)-------------------


# API 요청시에 공통으로 쓰이는 url
url="https://api.dooray.com"

# 지정한 프로젝트의 id를 검색하여 받아오기
project_id=`curl -X GET -H 'Authorization: dooray-api '${token}'' ${url}/project/v1/projects?type=${project_type} | jq '.result[]' | jq 'select(.code == "'${project_name}'")' | jq -r '.id'`

# 담당자 id를 검색하여 받아오기
member_id=`curl -X GET -H 'Authorization: dooray-api '${token}'' ${url}/common/v1/members?externalEmailAddresses={${member_email}} | jq -r '.result[].id'`

# 태그 id를 검색하여 받아오기
tag_id=`curl -X GET -H 'Authorization: dooray-api '${token}'' ${url}/project/v1/projects/${project_id}/tags | jq '.result[]' | jq --arg tag_name "$tag_name" 'select(.name==$tag_name)' | jq -r '.id'`

# 상위 업무의 id를 검색하여 받아오기
parent_id=`curl -X GET -H 'Authorization: dooray-api '${token}'' ${url}/project/v1/projects/${project_id}/posts?tagIds=${tag_id} | jq '.result[]' | jq --arg parent_title "$parent_title" 'select(.subject==$parent_title)' | jq -r '.id'`

# 상위 업무 생성 POST 요청에 사용할 json 형태의 body
posts_body=$(cat <<EOF
{
    "users": {
        "to": [{
            "type": "member",
            "member": {
                "organizationMemberId": "${member_id}"
            }
        }]
    },
    "subject": "${parent_title}",
    "body": {
        "mimeType": "text/x-markdown",
        "content": "${content}"
    },
    "dueDateFlag": false,
    "milestoneId": null,
    "tagIds": ["${tag_id}"],
    "priority": "none"
}
EOF
)

# 상위 업무가 존재하는지 확인dd
if [ -z "${parent_id}" ]
then    # 존재하지 않는 경우 상위 업무 생성
    now=`date "+%Y/%m/%d %H:%M"`
    echo -e "${now}>\033[95m ${parent_title} 상위 태스크가 존재하지 않습니다. 상위 태스크를 생성합니다...\033[0m"

    # 상위 업무 생성 POST Request
    result=`curl -d "${posts_body}" -X POST -H 'Content-Type: application/json' -H 'Authorization: dooray-api '${token}'' ${url}/project/v1/projects/${project_id}/posts`

    # 업무 생성 성공여부 확인
    success=`echo ${result} | jq '.header.isSuccessful'`

    now=`date "+%Y/%m/%d %H:%M"`
    if [ "${success}" == "true" ]
    then    # 업무 생성에 성공한 경우
        # 생성된 업무의 id를 상위 업무 id로 지정
        parent_id=`echo ${result} | jq -r '.result.id'`
        echo -e "${now}>\033[92m ${parent_title} 태스크가 생성되었습니다.\033[0m"
		curl -d '{"text":"상위 주간업무회의 태스크를 생성하였습니다.","organizationMemberId":"'${member_id}'"}' -X POST -H 'Content-Type: application/json' -H 'Authorization: dooray-api '${token}'' ${url}/messenger/v1/channels/direct-send > /dev/null
    else    # 업무 생성에 실패한 경우 오류 메세지를 함께 출력
        echo -e "${now}>\033[95m ${parent_title} 태스크 생성에 실패하였습니다.\033[0m"
        echo ${result} | jq '.header.resultMessage'
		curl -d '{"text":"상위 주간업무회의 태스크 생성에 실패하였습니다.","organizationMemberId":"'${member_id}'"}' -X POST -H 'Content-Type: application/json' -H 'Authorization: dooray-api '${token}'' ${url}/messenger/v1/channels/direct-send > /dev/null
        exit 0
    fi
fi

# 다음 주의 주간 업무 회의 태스크 생성

# 생성하려는 날짜의 주간 업무 회의가 이미 존재하는지 확인
check=`curl -X GET -H 'Authorization: dooray-api '${token}'' ${url}/project/v1/projects/${project_id}/posts\?tagIds\=${tag_id} | jq | fgrep -w "${title}"`

if [ -n "${check}" ]
then	# 이미 스크럼이 존재하는 경우 프로그램 종료
	now=`date "+%Y/%m/%d %H:%M"`
	echo -e "${now}>\033[95m ${title} 태스크가 이미 존재합니다.\033[0m"
	curl -d '{"text":"다음주의 주간업무회의 태스크가 이미 존재합니다.","organizationMemberId":"'${member_id}'"}' -X POST -H 'Content-Type: application/json' -H 'Authorization: dooray-api '${token}'' ${url}/messenger/v1/channels/direct-send > /dev/null
	exit 0
fi
# 주간 업무 회의가 없는 경우 생성

# 업무 생성 POST 요청에 사용할 json 형태의 body
posts_body=$(cat <<EOF
{
    "parentPostId": "${parent_id}",
    "users": {
        "to": [{
            "type": "member",
            "member": {
                "organizationMemberId": "${member_id}"
            }
        }]
    },
    "subject": "${title}",
    "body": {
        "mimeType": "text/x-markdown",
        "content": "${content}"
    },
    "dueDateFlag": false,
    "milestoneId": null,
    "tagIds": ["${tag_id}"],
    "priority": "none"
}
EOF
)

# 업무 생성 POST Request
result=`curl -d "${posts_body}" -X POST -H 'Content-Type: application/json' -H 'Authorization: dooray-api '${token}'' ${url}/project/v1/projects/${project_id}/posts`

# 업무 생성 성공여부 확인
success=`echo ${result} | jq '.header.isSuccessful'`

now=`date "+%Y/%m/%d %H:%M"`
if [ ${success} == "true" ]; then
	echo -e "${now}>\033[92m ${title} 태스크가 생성되었습니다.\033[0m"
	curl -d '{"text":"다음주의 주간업무회의 태스크를 생성하였습니다.","organizationMemberId":"'${member_id}'"}' -X POST -H 'Content-Type: application/json' -H 'Authorization: dooray-api '${token}'' ${url}/messenger/v1/channels/direct-send > /dev/null
else
	echo -e "${now}>\033[95m ${title} 태스크 생성에 실패하였습니다.\033[0m"
	echo ${result} | jq '.header.resultMessage'
	curl -d '{"text":"다음주의 주간업무회의 태스크 생성에 실패하였습니다.","organizationMemberId":"'${member_id}'"}' -X POST -H 'Content-Type: application/json' -H 'Authorization: dooray-api '${token}'' ${url}/messenger/v1/channels/direct-send > /dev/null
fi
  • 성공적으로 스크럼 태스크가 생성된 경우
    • 2023 주간업무회의가 생성되었고 상위 태스크로 잘 지정된 것을 확인 가능
    • 메신저로 생성 유무가 잘 전송된 것을 확인 가능
  • 이미 해당 날짜의 태스크가 생성되어 있는 경우
  • 태스크 생성에 실패한 경우

crontab을 사용하여 지정 시간에 작업을 반복하도록 등록

  • crontab 설정 편집
$ sudo crontab -e
# 매주 평일(월~금) 오전 9시 00분에 일일 스크럼 업무 생성 프로그램 실행 후 결과를 로그 파일에 기록 #
00 9 * * 1-5 /Users/nhn/dooray/create_scrum.sh >> /Users/nhn/dooray/create_task.log

# 매주 화요일 오전 9시 01분에 주간 회의 업무 생성 프로그램 실행 후 결과를 로그 파일에 기록 #
01 9 * * 2 /Users/nhn/dooray/create_weekly_meeting.sh >> /Users/nhn/dooray/create_task.log
  • 실행 결과가 로그에 잘 기록되는 것을 확인할 수 있음

error 해결

  • shell을 단순 호출하였을 때에는 정상 동작하였지만 crontab으로 실행했을 때 오류 발생
  • shell에 대한 설정은 /etc/profile, /etc/bashrc로 구성됨
    • /etc/profile 파일의 경우 로그인 shell(로그인 시에만)에 적용
    • /etc/bashrc 파일의 경우 shell이 생성될때마다 적용
  • 하지만 /etc/bashrc에 환경변수를 설정해주었으나 동일한 오류 발생
  • shell script 상단에 환경변수를 등록해주어 오류 해결 가능

요일이 영문으로 설정되는 오류

  • 로그인 shell의 언어는 한국어로 설정되어있지만, crontab에서 불러오는 환경변수에는 영문으로 설정되어 있음
    • [2023/01/27 (금)]이 아닌 [2023/01/27 (Fri)]로 등록됨
  • 현재 로그인 shell의 언어 설정 확인
$ env | grep LANG
LANG=ko_KR.UTF-8
  • shell script 상단에 LANG 환경변수 작성
#!/usr/bin/env bash
export LANG=ko_KR.UTF-8

command not found

  • 스크립트에 사용한 jq command를 찾지 못함
    • PATH에 대한 환경변수를 읽어오지 못하였기 때문
  • 현재 로그인 shell의 PATH 환경변수 확인
$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin
  • shell script 상단에 PATH 환경변수 작성
#!/usr/bin/env bash
export PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin

0개의 댓글