gitlab能够设置在实现某些操作(例如:提交pr,合并pr,提出issue...)能够对接钉钉机器人,从而将这些信息实时推送到钉钉群中。
尽管钉钉也对gitlab设置了官网的机器人然而可能并不能很好的满足咱们的需要,或者说gitlab所收回的申请并不合乎钉钉机器人所要求的格局。
这时候咱们就须要一个相似中转站的货色来满足咱们的要求——接管gitlab所收回的申请,将其转换为钉钉机器人能够失常接管的格局再依据这些数据结构一个新的申请来调用咱们的钉钉机器人从而实现咱们所须要的性能。
本文基于传送门编写。
流程图:
咱们如果间接运行该我的项目的话咱们会发现当咱们人为模仿gitlab发出请求时会收到以下响应:
{ "errcode":310000, "errmsg":"sign not match"}
报错信息提醒:签名不匹配,在查阅钉钉官网文档后就会发现如果咱们采纳“加签”办法对机器人进行平安设置时如果没有对申请的url依据设置的serect进行解决就会产生这样的报错。
钉钉机器人平安设置
依据下面给出的钉钉官网文档中的内容咱们能够写出上面这个函数对url进行解决:
public String encode(String secret) throws Exception { //获取工夫戳 Long timestamp = System.currentTimeMillis(); //把工夫戳和密钥拼接成字符串,两头退出一个换行符 String stringToSign = timestamp + "\n" + secret; //申明一个Mac对象,用来操作字符串 Mac mac = Mac.getInstance("HmacSHA256"); //初始化,设置Mac对象操作的字符串是UTF-8类型,加密形式是SHA256 mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256")); //把字符串转化成字节模式 byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8")); //新建一个Base64编码对象 Base64.Encoder encoder = Base64.getEncoder(); //把下面的字符串进行Base64加密后再进行URL编码 String sign = URLEncoder.encode(new String(encoder.encodeToString(signData)),"UTF-8"); String result = "×tamp=" + timestamp + "&sign=" + sign; return result; }
之后咱们只须要再从C层获取到serect再传给这个函数应用即可。
对于serect:
咱们再gitlab上设置好serect后,gitlab会将其退出到申请的header中供咱们获取即下图中的X-Gitlab-Token。
之后咱们再依据gitlab发送的申请批改一下C层对接的URL就能够失常地应用这个我的项目。
在C层匹配到相应url后获取申请中的json,X-Gitlab-Event,X-Gitlab-Token并调用对应M层函数。
@RestController@RequestMapping("/oapi.dingtalk.com/robot/send")@Slf4jpublic class GitLabController { @Autowired private GitLabNotifyService gitLabNotifyService; @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseVo pushHook(@RequestBody String json, @RequestHeader(name = "X-Gitlab-Event") String event, @RequestHeader(name = "X-Gitlab-Token") String secret) throws IOException{ System.out.println("触发推送"); gitLabNotifyService.handleEventData(json,event,secret); return ResponseUtil.ok(); }
String secret; @Override public void handleEventData(String json, String eventName, String secret) throws IOException { System.out.println("json:" + json); System.out.println("eventName:" + eventName); //缓存secret this.secret = secret; //依据哈希表返回X-Gitlab-Event对应的beanName String handleBeanName = EventMapper.getHandleBeanName(eventName); //依据beanName获取相应的service。 EventService eventService = (EventService) applicationContextProvider.getBean(handleBeanName); eventService.handleEvent(json); }
其中的applicationContextProvider实现了ApplicationContextAware接口用于设置上下文实例,之后咱们能够依据上下文实例的getBean办法来获取对应的sevice。
@Componentpublic class ApplicationContextProvider implements ApplicationContextAware { /** * 上下文对象实例 */ private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } /** * 获取applicationContext */ public ApplicationContext getApplicationContext() { return applicationContext; } /** * 通过name获取 Bean. */ public Object getBean(String name) { return getApplicationContext().getBean(name); }}
之后咱们以pushService为例阐明:
@Overridepublic void handleEvent(String json) throws IOException { //依据json数据设置gitLabPushRequest GitLabPushRequest gitLabPushRequest = covertJson(json); String text = "- event : " + gitLabPushRequest.getObjectKind() + " \n" + "- project : " + gitLabPushRequest.getProject().getName() + "\n" + "- author : " + gitLabPushRequest.getUserName() + "\n" + "- branch :" + gitLabPushRequest.getRef() + "\n" + "- total_commits :" + gitLabPushRequest.getTotalCommitsCount() + "\n"; //依据gitLabPushRequest设置要推送给钉钉的MarkDownMessage dingPushService.pushMarkDownMessage(new MarkDownMessage(MessageTypeConstant.MARKDOWN_TYPE, new MarkDownMessage.MarkDown(gitLabPushRequest.getObjectKind(), text))); }
其中的MarkDownMessage是钉钉官网给出的几种可供钉钉机器人辨认的数据格式之一,上面给出对应的钉钉官网文档。
自定义机器人的接入
dingPushService:
public void pushMarkDownMessage(MarkDownMessage markDownMessage) { DingResponse<Void> response = dingTalkApi.pushMarkDownMessage(markDownMessage); if (!SUCCESSS_CODE.equals(response.getErrcode())) { throw new UnknownException("error"); }}
之后咱们须要再调用dingTalkApi下的pushMarkDownMessage函数
dingTalkApi:
public DingResponse<Void> pushMarkDownMessage(MarkDownMessage markDownMessage) { HttpClientResponse httpClientResponse; ... //依据sercret批改dingTalkUrl dingTalkUrl = dingTalkUrl + this.encode(gitLabNotifyService.getDingSecret()); httpClientResponse = httpClientWrapper.postReturnHttpResponse(Collections.singletonMap("Content-Type","application/json"),dingTalkUrl, JsonUtil.serializeToJson(markDownMessage,true)); return CommonHttpUtils.handleHttpResponse(httpClientResponse, new TypeReference<DingResponse<Void>>() { ...}
这外面调用了httpClientWrapper.postReturnHttpResponse进行从新结构申请,并向钉钉机器人发动申请,随后钉钉机器人接管到申请后便可依据申请进行信息的推送。
public HttpClientResponse postReturnHttpResponse(Map<String, String> headerMap, String url, String bodyStr) throws IOException { HttpPost httpPost = getHttpPost(headerMap, url, bodyStr); return convert2HttpClientResponse(httpClient.execute(httpPost)); }private HttpPost getHttpPost(Map<String, String> headerMap, String url, String bodyStr) { HttpPost httpPost = new HttpPost(url); if (headerMap != null) { headerMap.forEach(httpPost::addHeader); } httpPost.setEntity(new StringEntity(bodyStr, "UTF-8")); return httpPost; }