Jira Slack 알림 작업완료

This commit is contained in:
build@lguplus.co.kr 2023-12-08 16:02:10 +09:00
parent 46b55eab7b
commit d4776cb959
10 changed files with 583 additions and 24 deletions

7
.vscode/launch.json vendored
View file

@ -46,6 +46,13 @@
"program": "./bin/main.dart",
"args": ["exe", "-f", "./assets/pipeline-test.yaml"]
},
{
"name": "exe slack",
"request": "launch",
"type": "dart",
"program": "./bin/main.dart",
"args": ["exe", "-f", "./slack_test.yaml"]
},
{
"name": "test",
"request": "launch",

View file

@ -12,6 +12,7 @@ import 'package:oto_cli/oto/commands/build/build_dot_net.dart';
import 'package:oto_cli/oto/commands/build/build_flutter.dart';
import 'package:oto_cli/oto/commands/build/build_ios.dart';
import 'package:oto_cli/oto/commands/git/git.dart';
import 'package:oto_cli/oto/commands/util/jira.dart';
import 'package:oto_cli/oto/commands/util/json.dart';
import 'package:oto_cli/oto/data/command_data.dart';
import 'package:oto_cli/oto/data/jenkins_data.dart';
@ -44,6 +45,7 @@ enum CommandType {
Delete,
Slack,
SlackBuild,
Jira,
Zip,
Shell,
Git,
@ -71,6 +73,7 @@ abstract class Command {
CommandType.Delete: Delete(),
CommandType.Slack: Slack(),
CommandType.SlackBuild: SlackBuild(),
CommandType.Jira: Jira(),
CommandType.Zip: Zip(),
CommandType.Shell: Shell(),
CommandType.Git: Git(),
@ -143,6 +146,16 @@ abstract class Command {
return raw;
}
static dynamic replaceSingleTagValue(String raw) {
dynamic value;
var list = regEx.allMatches(raw);
for (var match in list) {
var param = match.group(2);
value = _getTagValue(param!);
}
return value;
}
Map<String, dynamic> get property {
return Application.instance.property;
}
@ -179,6 +192,10 @@ abstract class Command {
return Command.replaceTagValue(raw);
}
dynamic replaceSingleTag(String raw) {
return Command.replaceSingleTagValue(raw);
}
Future setReturn(String tag, dynamic value) async {
await Command.setReturnValue(tag, value);
}

View file

@ -0,0 +1,95 @@
import 'dart:convert';
import 'package:dart_framework/utils/slack/slack_data.dart';
import 'package:dart_framework/utils/system_util.dart';
import 'package:oto_cli/oto/commands/command.dart';
import 'package:oto_cli/oto/data/command_data.dart';
import 'package:http/http.dart' as http;
import 'package:oto_cli/oto/data/jira_data.dart';
class Jira extends Command {
@override
Future execute(DataCommand command) async {
var data = DataJira.fromJson(command.param);
var text = '${data.id}:${data.token}';
var base64 = base64Encode(utf8.encode(text));
var header =
{
'Content-Type': 'application/json',
'Authorization': 'Basic $base64'
};
var url = 'https://${data.domain}/rest/api/3/issue/${data.issue}';
print(url);
var response = await http.get(Uri.parse(url), headers: header);
if(response.statusCode < 300 && response.statusCode >= 200)
{
print(response.body);
var issue = DataJiraIssue.fromJson(jsonDecode(response.body));
var targetUser = issue.fields?.assignee?.emailAddress;
if(targetUser != null)
{
var slackID = '';
for(var user in data.users) {
if(targetUser.contains(user.mail!)) {
slackID = user.slackID!;
break;
}
}
if(slackID.isNotEmpty) {
var arr = issue.fields?.description?.content;
if(arr != null && arr.isNotEmpty) {
arr = arr.first.content;
if(arr != null && arr.isNotEmpty)
{
var browseUrl = 'https://${data.domain}/browse/${data.issue}';
var slackData = DataSlack(slackID);
slackData.attachments = [];
var attachment = SlackAttachment();
slackData.blocks = [];
var blockSection = SlackBlockSection();
blockSection.text = SlackBlockText();
blockSection.text!.text = ':jira: 새 Jira 업무가 할당되었습니다.';
slackData.blocks!.add(blockSection);
attachment.blocks = [];
blockSection = SlackBlockSection();
blockSection.text = SlackBlockText();
blockSection.text!.type = SlackTextType.mrkdwn;
blockSection.text!.text = '*<$browseUrl|${data.issue}>*';
attachment.blocks!.add(blockSection);
blockSection = SlackBlockSection();
blockSection.text = SlackBlockText();
blockSection.text!.text = arr.first.text;
blockSection.accessory = SlackAccessory();
blockSection.accessory!.type = SlackActionType.button;
blockSection.accessory!.url = browseUrl;
blockSection.accessory!.text = SlackBlockText();
blockSection.accessory!.text!.type = SlackTextType.plain_text;
blockSection.accessory!.text!.text = 'Open';
attachment.blocks!.add(blockSection);
slackData.attachments!.add(attachment);
setReturn(data.return_slack_data, slackData);
}
}
} else {
print('User not found');
}
}
}
else
{
throw Exception('Request Jira API Faile: $url');
}
return simpleFuture;
}
}

View file

@ -1,5 +1,8 @@
// ignore_for_file: avoid_init_to_null
import 'dart:async';
import 'package:dart_framework/utils/slack/slack_data.dart';
import 'package:dart_framework/utils/system_util.dart';
import 'package:oto_cli/oto/commands/command.dart';
import 'package:oto_cli/oto/data/command_data.dart';
@ -9,9 +12,20 @@ import 'package:dart_framework/utils/slack/slack_sender.dart';
class Slack extends Command {
@override
Future execute(DataCommand command) async {
var data = DataSlack.fromJson(command.param);
var data = DataSlackSender.fromJson(command.param);
var sender = SlackSender(data.token);
DataSlack? message = null;
if(data.property != null) {
message = replaceSingleTag(data.property!) as DataSlack;
} else if(data.message != null) {
message = data.message;
}
//To-do: compose parameters
if(message != null) {
await sender.send(message);
} else {
throw Exception('property or message field should not be null');
}
return simpleFuture;
}
@ -24,12 +38,10 @@ class SlackBuild extends Command {
var version = replaceTag(data.version);
var download = replaceTag(data.downloadURL);
var channel = data.channel ?? 'build';
var color = Color.hex(data.color);
var sender = SlackSender();
var sender = SlackSender(data.token);
await sender.sendBuild(data.appName, data.type, color, version, download,
channel: channel);
await sender.sendBuild(data.appName, data.type, color, version, download);
return complete(0, command);
}

View file

@ -1,8 +1,9 @@
// ignore_for_file: non_constant_identifier_names
// ignore_for_file: non_constant_identifier_names, avoid_init_to_null
import 'package:dart_framework/utils/slack/slack_sender.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:dart_framework/core/app_data_manager.dart';
import 'package:dart_framework/utils/slack/slack_data.dart';
import 'package:oto_cli/oto/commands/command.dart';
import 'package:oto_cli/oto/data/pipeline_data.dart';
part 'command_data.g.dart';
@ -264,17 +265,21 @@ class DataRename extends DataFile {
//Slack
@JsonSerializable()
class DataSlack extends DataParam {
DataSlack();
class DataSlackSender extends DataParam {
DataSlackSender();
factory DataSlack.fromJson(Map<String, dynamic> json) =>
_$DataSlackFromJson(json);
Map<String, dynamic> toJson() => _$DataSlackToJson(this);
late String? token;
late String? property;
late DataSlack? message;
factory DataSlackSender.fromJson(Map<String, dynamic> json) =>
_$DataSlackSenderFromJson(json);
Map<String, dynamic> toJson() => _$DataSlackSenderToJson(this);
}
//Slack Build
@JsonSerializable()
class DataSlackBuild extends DataParam {
class DataSlackBuild extends DataSlackSender {
DataSlackBuild();
late String appName;
@ -282,13 +287,44 @@ class DataSlackBuild extends DataParam {
late String color;
late String version;
late String downloadURL;
late String? channel;
factory DataSlackBuild.fromJson(Map<String, dynamic> json) =>
_$DataSlackBuildFromJson(json);
@override
Map<String, dynamic> toJson() => _$DataSlackBuildToJson(this);
}
//Jira
@JsonSerializable()
class DataJira extends DataParam {
DataJira();
late String id;
late String token;
late String domain;
late String issue;
late List<DataJiraUser> users;
@JsonKey(name: 'return-slack-data')
late String return_slack_data;
factory DataJira.fromJson(Map<String, dynamic> json) =>
_$DataJiraFromJson(json);
Map<String, dynamic> toJson() => _$DataJiraToJson(this);
}
//Jira User
@JsonSerializable()
class DataJiraUser extends DataParam {
DataJiraUser();
late String? mail = null;
late String? slackID = null;
factory DataJiraUser.fromJson(Map<String, dynamic> json) =>
_$DataJiraUserFromJson(json);
Map<String, dynamic> toJson() => _$DataJiraUserToJson(this);
}
//Zip
@JsonSerializable()
class DataZip extends DataParam {

View file

@ -51,6 +51,7 @@ const _$CommandTypeEnumMap = {
CommandType.Delete: 'Delete',
CommandType.Slack: 'Slack',
CommandType.SlackBuild: 'SlackBuild',
CommandType.Jira: 'Jira',
CommandType.Zip: 'Zip',
CommandType.Shell: 'Shell',
CommandType.Git: 'Git',
@ -302,32 +303,48 @@ Map<String, dynamic> _$DataRenameToJson(DataRename instance) =>
'renameMap': instance.renameMap,
};
DataSlack _$DataSlackFromJson(Map<String, dynamic> json) =>
DataSlack()..workspace = json['workspace'] as String?;
DataSlackSender _$DataSlackSenderFromJson(Map<String, dynamic> json) =>
DataSlackSender()
..workspace = json['workspace'] as String?
..token = json['token'] as String?
..property = json['property'] as String?
..message = json['message'] == null
? null
: DataSlack.fromJson(json['message'] as Map<String, dynamic>);
Map<String, dynamic> _$DataSlackToJson(DataSlack instance) => <String, dynamic>{
Map<String, dynamic> _$DataSlackSenderToJson(DataSlackSender instance) =>
<String, dynamic>{
'workspace': instance.workspace,
'token': instance.token,
'property': instance.property,
'message': instance.message,
};
DataSlackBuild _$DataSlackBuildFromJson(Map<String, dynamic> json) =>
DataSlackBuild()
..workspace = json['workspace'] as String?
..token = json['token'] as String?
..property = json['property'] as String?
..message = json['message'] == null
? null
: DataSlack.fromJson(json['message'] as Map<String, dynamic>)
..appName = json['appName'] as String
..type = $enumDecode(_$SlackBuildTypeEnumMap, json['type'])
..color = json['color'] as String
..version = json['version'] as String
..downloadURL = json['downloadURL'] as String
..channel = json['channel'] as String?;
..downloadURL = json['downloadURL'] as String;
Map<String, dynamic> _$DataSlackBuildToJson(DataSlackBuild instance) =>
<String, dynamic>{
'workspace': instance.workspace,
'token': instance.token,
'property': instance.property,
'message': instance.message,
'appName': instance.appName,
'type': _$SlackBuildTypeEnumMap[instance.type]!,
'color': instance.color,
'version': instance.version,
'downloadURL': instance.downloadURL,
'channel': instance.channel,
};
const _$SlackBuildTypeEnumMap = {
@ -338,6 +355,39 @@ const _$SlackBuildTypeEnumMap = {
SlackBuildType.web: 'web',
};
DataJira _$DataJiraFromJson(Map<String, dynamic> json) => DataJira()
..workspace = json['workspace'] as String?
..id = json['id'] as String
..token = json['token'] as String
..domain = json['domain'] as String
..issue = json['issue'] as String
..users = (json['users'] as List<dynamic>)
.map((e) => DataJiraUser.fromJson(e as Map<String, dynamic>))
.toList()
..return_slack_data = json['return-slack-data'] as String;
Map<String, dynamic> _$DataJiraToJson(DataJira instance) => <String, dynamic>{
'workspace': instance.workspace,
'id': instance.id,
'token': instance.token,
'domain': instance.domain,
'issue': instance.issue,
'users': instance.users,
'return-slack-data': instance.return_slack_data,
};
DataJiraUser _$DataJiraUserFromJson(Map<String, dynamic> json) => DataJiraUser()
..workspace = json['workspace'] as String?
..mail = json['mail'] as String?
..slackID = json['slackID'] as String?;
Map<String, dynamic> _$DataJiraUserToJson(DataJiraUser instance) =>
<String, dynamic>{
'workspace': instance.workspace,
'mail': instance.mail,
'slackID': instance.slackID,
};
DataZip _$DataZipFromJson(Map<String, dynamic> json) => DataZip()
..workspace = json['workspace'] as String?
..zipFile = json['zipFile'] as String

145
lib/oto/data/jira_data.dart Normal file
View file

@ -0,0 +1,145 @@
// ignore_for_file: avoid_init_to_null
import 'package:json_annotation/json_annotation.dart';
part 'jira_data.g.dart';
//Jira Issue Response
@JsonSerializable()
class DataJiraIssue {
late String? id = null;
late String? self = null;
late String? key = null;
DataJiraIssueField? fields;
DataJiraIssue();
factory DataJiraIssue.fromJson(Map<String, dynamic> json) =>
_$DataJiraIssueFromJson(json);
Map<String, dynamic> toJson() => _$DataJiraIssueToJson(this);
}
//Jira Issue Field
@JsonSerializable()
class DataJiraIssueField {
late DataJiraIssuePriority? priority = null;
late DataJiraIssueStatus? status = null;
late DataJiraUser? assignee = null;
late DataJiraUser? creator = null;
late DataJiraUser? reporter = null;
late DataJiraIssueDesc? description = null;
DataJiraIssueField();
factory DataJiraIssueField.fromJson(Map<String, dynamic> json) =>
_$DataJiraIssueFieldFromJson(json);
Map<String, dynamic> toJson() => _$DataJiraIssueFieldToJson(this);
}
//Jira Issue Priority
@JsonSerializable()
class DataJiraIssuePriority {
late String? self = null;
late String? iconUrl = null;
late String? name = null;
late String? id = null;
DataJiraIssuePriority();
factory DataJiraIssuePriority.fromJson(Map<String, dynamic> json) =>
_$DataJiraIssuePriorityFromJson(json);
Map<String, dynamic> toJson() => _$DataJiraIssuePriorityToJson(this);
}
// Jira Issue Status
@JsonSerializable()
class DataJiraIssueStatus {
late String? self = null;
late String? description = null;
late String? iconUrl = null;
late String? name = null;
late String? id = null;
late DataJiraIssueStatusCategory? statusCategory = null;
DataJiraIssueStatus();
factory DataJiraIssueStatus.fromJson(Map<String, dynamic> json) =>
_$DataJiraIssueStatusFromJson(json);
Map<String, dynamic> toJson() => _$DataJiraIssueStatusToJson(this);
}
// Jira Issue Status Category
@JsonSerializable()
class DataJiraIssueStatusCategory {
late String? self = null;
late int? id = null;
late String? key = null;
late String? colorName = null;
late String? name = null;
DataJiraIssueStatusCategory();
factory DataJiraIssueStatusCategory.fromJson(Map<String, dynamic> json) =>
_$DataJiraIssueStatusCategoryFromJson(json);
Map<String, dynamic> toJson() => _$DataJiraIssueStatusCategoryToJson(this);
}
//Jira Issue User
@JsonSerializable()
class DataJiraUser {
late String? self = null;
late String? accountId = null;
late String? emailAddress = null;
late Map<String, String> avatarUrls;
late String? displayName = null;
late bool? activate = null;
late String? timeZone = null;
late String? accountType = null;
DataJiraUser();
factory DataJiraUser.fromJson(Map<String, dynamic> json) =>
_$DataJiraUserFromJson(json);
Map<String, dynamic> toJson() => _$DataJiraUserToJson(this);
}
// Jira Issue Desc
@JsonSerializable()
class DataJiraIssueDesc {
late int? version = null;
late String? doc = null;
late List<DataJiraContent>? content = null;
DataJiraIssueDesc();
factory DataJiraIssueDesc.fromJson(Map<String, dynamic> json) =>
_$DataJiraIssueDescFromJson(json);
Map<String, dynamic> toJson() => _$DataJiraIssueDescToJson(this);
}
// Jira Issue Content
@JsonSerializable()
class DataJiraContent {
late String? type = null;
late String? text = null;
late List<DataJiraContent>? content = null;
DataJiraContent();
factory DataJiraContent.fromJson(Map<String, dynamic> json) =>
_$DataJiraContentFromJson(json);
Map<String, dynamic> toJson() => _$DataJiraContentToJson(this);
}
// // Jira Issue
// @JsonSerializable()
// class DataJira {
// DataJira();
// factory DataJira.fromJson(Map<String, dynamic> json) =>
// _$DataJiraFromJson(json);
// Map<String, dynamic> toJson() => _$DataJiraToJson(this);
// }

View file

@ -0,0 +1,168 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'jira_data.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
DataJiraIssue _$DataJiraIssueFromJson(Map<String, dynamic> json) =>
DataJiraIssue()
..id = json['id'] as String?
..self = json['self'] as String?
..key = json['key'] as String?
..fields = json['fields'] == null
? null
: DataJiraIssueField.fromJson(json['fields'] as Map<String, dynamic>);
Map<String, dynamic> _$DataJiraIssueToJson(DataJiraIssue instance) =>
<String, dynamic>{
'id': instance.id,
'self': instance.self,
'key': instance.key,
'fields': instance.fields,
};
DataJiraIssueField _$DataJiraIssueFieldFromJson(Map<String, dynamic> json) =>
DataJiraIssueField()
..priority = json['priority'] == null
? null
: DataJiraIssuePriority.fromJson(
json['priority'] as Map<String, dynamic>)
..status = json['status'] == null
? null
: DataJiraIssueStatus.fromJson(json['status'] as Map<String, dynamic>)
..assignee = json['assignee'] == null
? null
: DataJiraUser.fromJson(json['assignee'] as Map<String, dynamic>)
..creator = json['creator'] == null
? null
: DataJiraUser.fromJson(json['creator'] as Map<String, dynamic>)
..reporter = json['reporter'] == null
? null
: DataJiraUser.fromJson(json['reporter'] as Map<String, dynamic>)
..description = json['description'] == null
? null
: DataJiraIssueDesc.fromJson(
json['description'] as Map<String, dynamic>);
Map<String, dynamic> _$DataJiraIssueFieldToJson(DataJiraIssueField instance) =>
<String, dynamic>{
'priority': instance.priority,
'status': instance.status,
'assignee': instance.assignee,
'creator': instance.creator,
'reporter': instance.reporter,
'description': instance.description,
};
DataJiraIssuePriority _$DataJiraIssuePriorityFromJson(
Map<String, dynamic> json) =>
DataJiraIssuePriority()
..self = json['self'] as String?
..iconUrl = json['iconUrl'] as String?
..name = json['name'] as String?
..id = json['id'] as String?;
Map<String, dynamic> _$DataJiraIssuePriorityToJson(
DataJiraIssuePriority instance) =>
<String, dynamic>{
'self': instance.self,
'iconUrl': instance.iconUrl,
'name': instance.name,
'id': instance.id,
};
DataJiraIssueStatus _$DataJiraIssueStatusFromJson(Map<String, dynamic> json) =>
DataJiraIssueStatus()
..self = json['self'] as String?
..description = json['description'] as String?
..iconUrl = json['iconUrl'] as String?
..name = json['name'] as String?
..id = json['id'] as String?
..statusCategory = json['statusCategory'] == null
? null
: DataJiraIssueStatusCategory.fromJson(
json['statusCategory'] as Map<String, dynamic>);
Map<String, dynamic> _$DataJiraIssueStatusToJson(
DataJiraIssueStatus instance) =>
<String, dynamic>{
'self': instance.self,
'description': instance.description,
'iconUrl': instance.iconUrl,
'name': instance.name,
'id': instance.id,
'statusCategory': instance.statusCategory,
};
DataJiraIssueStatusCategory _$DataJiraIssueStatusCategoryFromJson(
Map<String, dynamic> json) =>
DataJiraIssueStatusCategory()
..self = json['self'] as String?
..id = json['id'] as int?
..key = json['key'] as String?
..colorName = json['colorName'] as String?
..name = json['name'] as String?;
Map<String, dynamic> _$DataJiraIssueStatusCategoryToJson(
DataJiraIssueStatusCategory instance) =>
<String, dynamic>{
'self': instance.self,
'id': instance.id,
'key': instance.key,
'colorName': instance.colorName,
'name': instance.name,
};
DataJiraUser _$DataJiraUserFromJson(Map<String, dynamic> json) => DataJiraUser()
..self = json['self'] as String?
..accountId = json['accountId'] as String?
..emailAddress = json['emailAddress'] as String?
..avatarUrls = Map<String, String>.from(json['avatarUrls'] as Map)
..displayName = json['displayName'] as String?
..activate = json['activate'] as bool?
..timeZone = json['timeZone'] as String?
..accountType = json['accountType'] as String?;
Map<String, dynamic> _$DataJiraUserToJson(DataJiraUser instance) =>
<String, dynamic>{
'self': instance.self,
'accountId': instance.accountId,
'emailAddress': instance.emailAddress,
'avatarUrls': instance.avatarUrls,
'displayName': instance.displayName,
'activate': instance.activate,
'timeZone': instance.timeZone,
'accountType': instance.accountType,
};
DataJiraIssueDesc _$DataJiraIssueDescFromJson(Map<String, dynamic> json) =>
DataJiraIssueDesc()
..version = json['version'] as int?
..doc = json['doc'] as String?
..content = (json['content'] as List<dynamic>?)
?.map((e) => DataJiraContent.fromJson(e as Map<String, dynamic>))
.toList();
Map<String, dynamic> _$DataJiraIssueDescToJson(DataJiraIssueDesc instance) =>
<String, dynamic>{
'version': instance.version,
'doc': instance.doc,
'content': instance.content,
};
DataJiraContent _$DataJiraContentFromJson(Map<String, dynamic> json) =>
DataJiraContent()
..type = json['type'] as String?
..text = json['text'] as String?
..content = (json['content'] as List<dynamic>?)
?.map((e) => DataJiraContent.fromJson(e as Map<String, dynamic>))
.toList();
Map<String, dynamic> _$DataJiraContentToJson(DataJiraContent instance) =>
<String, dynamic>{
'type': instance.type,
'text': instance.text,
'content': instance.content,
};

View file

@ -13,10 +13,10 @@ dependencies:
json_annotation: ^4.8.0
resource_importer: ^0.2.0
dart_framework:
# path: '../dart_framework'
git:
url: toki@toki-labs.com:/Volumes/Data2/git/dart_framework.git
ref: master
path: '../dart_framework'
# git:
# url: toki@toki-labs.com:/Volumes/Data2/git/dart_framework.git
# ref: master
dev_dependencies:
lints: ^3.0.0

29
slack_test.yaml Normal file
View file

@ -0,0 +1,29 @@
---
property:
workspace: .
pieline:
id: main
workflow:
- exe: jira
- exe: slack
commands:
- command: Jira
id: jira
param:
id: leedongmyung@lguplus.co.kr
token: ATATT3xFfGF0mklis_51fqVtxqIatXsuUoOsgu6HXdWXh6nTnQOE964iSZnh0QhgUFZejaljlqsZVBA6OlG8hg74lnmyYKPB7M2Et2mAq3TsvvQskT6VwnG4mGXQ1ff-KIhPvfMvGmGzrQ7UeefC5muJusEcmy1nknxZC8CK4OhYQjAc07GnRmg=B6CBDA6F
domain: lgu-cto.atlassian.net
issue: M3-1
users:
- mail: leedongmyung@lguplus.co.kr
slackID: U05A94D57RS
- mail: sunmikk@lguplus.co.kr
id: U03SNLHMMAB
return-slack-data: <!property.slack>
- command: Slack
id: slack
param:
token: xoxb-3923712524596-5927868822833-amGkCRXlwX33LmgVF1dmeMZ5
property: <!property.slack>