1
0
Fork 0

Add otrs support

This commit is contained in:
Malte Brandy 2018-07-30 20:15:41 +02:00
parent 5a7d41300c
commit 8dabbb8c20
No known key found for this signature in database
GPG key ID: 226A2D41EF5378C9
12 changed files with 3368 additions and 134 deletions

Binary file not shown.

View file

@ -1,7 +1,7 @@
{ config, lib, pkgs , ... }:
with lib;
let
inherit (config.m-0.private) me gitlab github;
inherit (config.m-0.private) me gitlab github otrs;
in {
options.m-0.bugwarrior.enable = mkEnableOption "Sync tasks from issuetrackers";
config = mkIf config.m-0.bugwarrior.enable {
@ -43,8 +43,13 @@ config = mkIf config.m-0.bugwarrior.enable {
};
Service = {
Type = "oneshot";
Environment="PATH=${pkgs.taskwarrior}/bin:${pkgs.eventd}/bin:${pkgs.gnugrep}/bin";
ExecStart="${pkgs.bugwarrior}/bin/bugwarrior-pull";
Environment=''PATH=${pkgs.taskwarrior}/bin:${pkgs.eventd}/bin OTRS_USER=${me.user} OTRS_PASSWORD=${otrs.password} OTRS_QUEUES="${otrs.queues}" OTRS_OWNERS="${otrs.owners}" OTRS_HOST=${otrs.host}'';
ExecStart= let
update = pkgs.writeShellScriptBin "update" ''
${pkgs.bugwarrior}/bin/bugwarrior-pull
${pkgs.rust_scripts}/bin/sync_otrs
true
''; in "${update}/bin/update";
};
};
timers.bugwarrior = {

View file

@ -1,7 +1,7 @@
{ config, lib, pkgs , ... }:
with lib;
let
inherit (config.m-0.private) me gitlab;
inherit (config.m-0.private) me;
in {
options.m-0.update_tasks.enable = mkEnableOption "Update Tasks";
config = mkIf config.m-0.update_tasks.enable {

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,6 @@
[package]
name = "rust-scripts"
version = "0.1.0"
authors = ["Malte Brandy <malte.brandy@maralorn.de>"]
[dependencies]
dialog = { git = "https://git.darmstadt.ccc.de/maralorn/dialog-rs.git" }
@ -16,3 +15,6 @@ serde_yaml = "0.7"
maildir = "0.1.1"
mailparse = "0.5.1"
regex = "0.2"
serde_json = "1.0"
reqwest = "0.8"
config = "0.9.0"

View file

@ -0,0 +1,5 @@
with import <unstable> {};
stdenv.mkDerivation {
name = "habitask";
buildInputs = [ pkgs.openssl pkgs.pkgconfig ];
}

View file

@ -0,0 +1,15 @@
extern crate rust_scripts;
extern crate task_hookrs;
use rust_scripts::error::Result;
use rust_scripts::otrs::sync_otrs;
use task_hookrs::cache::TaskCache;
use task_hookrs::status::TaskStatus;
fn main() -> Result<()> {
let mut cache = TaskCache::new(vec![TaskStatus::Deleted]);
cache.load()?;
sync_otrs(&mut cache)?;
cache.write()?;
Ok(())
}

View file

@ -874,6 +874,14 @@ Do you want to change the state? (Esc to cancel)",
.arg("-e")
.arg(read_command)
.output()?;
} else if task.gen_name() == Some(&"cda-otrs".to_owned()) {
let ticket_number = task.gen_id().chain_err(|| "Missing Ticketnumber")?;
let url = format!(
"https://tickets.darmstadt.ccc.de/otrs/index.pl?Action=AgentTicketZoom;TicketID={}",
ticket_number
);
str2cmd("firefox --new-window").arg(url).output()?;
bail!(EK::WorkingOnTask(print_task(task)));
} else {
bail!(EK::WorkingOnTask(print_task(task)));
}

View file

@ -12,6 +12,8 @@ extern crate serde_yaml;
extern crate maildir;
extern crate mailparse;
extern crate regex;
extern crate reqwest;
extern crate config;
pub mod hotkeys;
pub mod generate;
@ -21,14 +23,12 @@ pub mod kassandra;
pub mod tasktree;
pub mod well_known;
pub mod mail;
pub mod otrs;
#[allow(renamed_and_removed_lints)]
pub mod error {
use task_hookrs::error as terror;
use dialog::errors as derror;
use serde_yaml;
use maildir;
use mailparse;
error_chain! {
links {
TaskError(terror::Error, terror::ErrorKind);
@ -36,10 +36,12 @@ pub mod error {
}
foreign_links {
Io(::std::io::Error);
Yaml(serde_yaml::Error);
Yaml(::serde_yaml::Error);
PathError(::std::env::JoinPathsError);
MailDir(maildir::MailEntryError);
MailParse(mailparse::MailParseError);
MailDir(::maildir::MailEntryError);
MailParse(::mailparse::MailParseError);
Config(::config::ConfigError);
HttpError(::reqwest::Error);
}
errors {
WorkingOnTask(t: String) {

View file

@ -0,0 +1,173 @@
use task_hookrs::task::TaskBuilder;
use task_hookrs::cache::TaskCache;
use error::{Result, ResultExt};
use generate::{GeneratedTask, TaskGenerator};
use reqwest::Client;
use config::{Config, Environment};
#[derive(Debug, Deserialize)]
struct Ticket {
#[serde(rename = "TicketID")]
number: u32,
#[serde(rename = "Title")]
title: String,
#[serde(rename = "CustomerUserID")]
customer: String,
}
#[derive(Debug, Clone, Deserialize)]
struct Settings {
user: String,
password: String,
queues: String,
owners: String,
host: String,
}
#[derive(Debug, Serialize)]
struct QueueRequest {
#[serde(rename = "UserLogin")]
user: String,
#[serde(rename = "Password")]
password: String,
#[serde(rename = "Queues")]
queues: Vec<String>,
#[serde(rename = "States")]
states: Vec<String>,
#[serde(rename = "OwnerIDs")]
owners: Vec<u32>,
}
#[derive(Debug, Deserialize)]
struct QueueResponse {
#[serde(rename = "TicketID")]
tickets: Vec<String>,
}
#[derive(Debug, Serialize)]
struct Credentials {
#[serde(rename = "UserLogin")]
user: String,
#[serde(rename = "Password")]
password: String,
}
#[derive(Debug, Deserialize)]
struct TicketResponse {
#[serde(rename = "Ticket")]
ticket: Vec<Ticket>,
}
impl From<Settings> for Credentials {
fn from(settings: Settings) -> Self {
Credentials {
user: settings.user,
password: settings.password,
}
}
}
impl From<Settings> for QueueRequest {
fn from(settings: Settings) -> Self {
QueueRequest {
user: settings.user,
password: settings.password,
queues: settings
.queues
.split(", ")
.into_iter()
.map(|s| s.to_string())
.collect(),
states: vec!["open".to_owned(), "new".to_owned()],
owners: settings
.owners
.split(", ")
.into_iter()
.map(|id| id.parse().unwrap())
.collect(),
}
}
}
impl Settings {
fn get() -> Result<Self> {
let mut settings = Config::new();
settings.merge(Environment::with_prefix("otrs"))?;
let settings = settings.try_into()?;
Ok(settings)
}
}
struct OTRSSync {
client: Client,
settings: Settings,
}
impl OTRSSync {
fn new() -> Result<Self> {
Ok(OTRSSync {
client: Client::new(),
settings: Settings::get()?,
})
}
fn get_ids(&self) -> Result<Vec<String>> {
let mut builder = self.client.post(&self.queue_url());
let request_body = QueueRequest::from(self.settings.clone());
builder.json(&request_body);
let mut response = builder.send()?;
let response = response.json::<QueueResponse>()?;
Ok(response.tickets)
}
fn get_ticket(&self, id: String) -> Result<Ticket> {
let mut builder = self.client.post(&self.ticket_url(id));
let request_body = Credentials::from(self.settings.clone());
builder.json(&request_body);
let mut response = builder.send()?;
let response = response.json::<TicketResponse>()?;
Ok(response.ticket.into_iter().next().chain_err(
|| "No Tickets",
)?)
}
fn queue_url(&self) -> String {
self.base_url() + "TicketSearch"
}
fn base_url(&self) -> String {
format!(
"https://{}/otrs/nph-genericinterface.pl/Webservice/GenericConnector/",
&self.settings.host
)
}
fn ticket_url(&self, id: String) -> String {
self.base_url() + "TicketGet/" + &id
}
fn get_tickets(&self) -> Result<Vec<Ticket>> {
self.get_ids()?
.into_iter()
.map(|id| self.get_ticket(id))
.collect()
}
}
pub fn sync_otrs(cache: &mut TaskCache) -> Result<()> {
let sync = OTRSSync::new()?;
let tickets = sync.get_tickets()?;
let tasks = tickets.into_iter().map(|ticket| {
let mut t = TaskBuilder::default()
.description(format!("CDA: {}: {}", ticket.customer, ticket.title))
.build()
.expect("TaskBuilding failed inspite of set description");
t.set_gen_name(Some("cda-otrs"));
t.set_gen_id(Some(ticket.number.to_string()));
t
});
cache.generate(tasks)?;
cache.write()?;
Ok(())
}

View file

@ -5,7 +5,7 @@ in {
# channel = 18.03
imports = [
<home-manager/nixos>
/home/maralorn/git/home-manager/nixos
../common
./modules/laptop.nix
./modules/server