(ns auto-ap.graphql (:require [com.walmartlabs.lacinia.util :refer [attach-resolvers]] [com.walmartlabs.lacinia.schema :as schema] [com.walmartlabs.lacinia :refer [execute]] [com.walmartlabs.lacinia.executor :as executor] [com.walmartlabs.lacinia.resolve :as resolve] [buddy.auth :refer [throw-unauthorized]] [auto-ap.utils :refer [by]] [auto-ap.graphql.utils :refer [assert-admin can-see-client? assert-can-see-client]] [auto-ap.datomic :refer [uri merge-query]] [datomic.api :as d] [clj-time.coerce :as coerce] [auto-ap.datomic.clients :as d-clients] [auto-ap.datomic.checks :as d-checks] [auto-ap.datomic.users :as d-users] [auto-ap.datomic.invoices :as d-invoices] [auto-ap.datomic.vendors :as d-vendors] [auto-ap.graphql.users :as gq-users] [auto-ap.graphql.yodlee-merchants :as ym] [auto-ap.graphql.ledger :as gq-ledger] [auto-ap.graphql.accounts :as gq-accounts] [auto-ap.graphql.clients :as gq-clients] [auto-ap.graphql.vendors :as gq-vendors] [auto-ap.graphql.checks :as gq-checks] [auto-ap.graphql.invoices :as gq-invoices] [auto-ap.graphql.transactions :as gq-transactions] [auto-ap.graphql.transaction-rules :as gq-transaction-rules] [auto-ap.time :as time] [clojure.walk :as walk] [clojure.string :as str]) (:import (clojure.lang IPersistentMap))) (def integreat-schema { :scalars {:id {:parse (schema/as-conformer #(when % (Long/parseLong %))) :serialize (schema/as-conformer #(.toString %))} :ident {:parse (schema/as-conformer (fn [x] {:db/ident x})) :serialize (schema/as-conformer #(or (:ident %) (:db/ident %) %))} :iso_date {:parse (schema/as-conformer #(time/parse % time/iso-date)) :serialize (schema/as-conformer #(time/unparse % time/iso-date))} :money {:parse (schema/as-conformer #(if (and (string? %) (not (str/blank? %))) (Double/parseDouble %) %)) :serialize (schema/as-conformer #(if (double? %) (str %) %)) } :percentage {:parse (schema/as-conformer #(if (and (string? %) (not (str/blank? %))) (Double/parseDouble %) %)) :serialize (schema/as-conformer #(if (double? %) (str %) %))}} :objects { :location_match {:fields {:location {:type 'String} :match {:type 'String}}} :client {:fields {:id {:type :id} :name {:type 'String} :code {:type 'String} :email {:type 'String} :address {:type :address} :location_matches {:type '(list :location_match)} :locations {:type '(list String)} :matches {:type '(list String)} :bank_accounts {:type '(list :bank_account)}}} :contact {:fields {:id {:type :id} :name {:type 'String} :email {:type 'String} :phone {:type 'String}}} :bank_account {:fields {:id {:type :id } :type {:type :ident} :number {:type 'String} :sort_order {:type 'Int} :visible {:type 'Boolean} :routing {:type 'String} :code {:type 'String} :check_number {:type 'Int} :name {:type 'String} :bank_code {:type 'String} :bank_name {:type 'String} :yodlee_account_id {:type 'Int} :locations {:type '(list String)}}} :balance_sheet_account {:fields {:id {:type 'String} :amount {:type 'String} :location {:type 'String} :numeric_code {:type 'Int} :account_type {:type :account_type} :name {:type 'String}}} :balance_sheet {:fields {:balance_sheet_accounts {:type '(list :balance_sheet_account)} :comparable_balance_sheet_accounts {:type '(list :balance_sheet_account)}}} :address {:fields {:street1 {:type 'String} :street2 {:type 'String} :city {:type 'String} :state {:type 'String} :zip {:type 'String}}} :terms_override {:fields {:id {:type :id} :client {:type :client} :terms {:type 'Int} }} :account_override {:fields {:id {:type :id} :client {:type :client} :account {:type :account}}} :vendor {:fields {:id {:type :id} :name {:type 'String} :code {:type 'String} :terms {:type 'Int} :hidden {:type 'Boolean} :terms_overrides {:type '(list :terms_override)} :account_overrides {:type '(list :account_override)} :print_as {:type 'String} :primary_contact {:type :contact} :secondary_contact {:type :contact} :address {:type :address} :default_account {:type :account} :invoice_reminder_schedule {:type 'String}}} :reminder {:fields {:id {:type 'Int} :email {:type 'String} :subject {:type 'String} :body {:type 'String} :scheduled {:type 'String} :sent {:type 'String} :vendor {:type :vendor} }} :journal_entry_line {:fields {:id {:type :id} :account {:type :account} :location {:type 'String} :debit {:type 'String} :credit {:type 'String}}} :journal_entry {:fields {:id {:type :id} :source {:type 'String} :amount {:type 'String} :note {:type 'String} :cleared_against {:type 'String} :client {:type :client} :vendor {:type :vendor} :date {:type 'String} :line_items {:type '(list :journal_entry_line)}}} :check {:fields {:id {:type :id} :type {:type 'String} :amount {:type 'String} :vendor {:type :vendor} :date {:type 'String} :bank_account {:type :bank_account} :memo {:type 'String} :s3_url {:type 'String} :check_number {:type 'Int} :status {:type 'String} :invoices {:type '(list :invoice_payment)} }} :payment {:fields {:id {:type :id} :type {:type :payment_type} :original_id {:type 'Int} :amount {:type 'String} :vendor {:type :vendor} :client {:type :client} :date {:type 'String} :bank_account {:type :bank_account} :memo {:type 'String} :s3_url {:type 'String} :check_number {:type 'Int} :status {:type :ident} :transaction {:type :transaction} :invoices {:type '(list :invoice_payment)} }} :yodlee_merchant {:fields {:id {:type :id} :yodlee_id {:type 'String} :name {:type 'String}}} :transaction {:fields {:id {:type :id} :amount {:type 'String} :description_original {:type 'String} :description_simple {:type 'String} :location {:type 'String} :status {:type 'String} :yodlee_merchant {:type :yodlee_merchant} :client {:type :client} :accounts {:type '(list :invoices_expense_accounts)} :payment {:type :payment} :vendor {:type :vendor} :bank_account {:type :bank_account} :date {:type 'String} :post_date {:type 'String} :approval_status {:type :transaction_approval_status} :matched_rule {:type :transaction_rule}}} :transaction_rule {:fields {:id {:type :id} :note {:type 'String} :client {:type :client} :bank_account {:type :bank_account} :yodlee_merchant {:type :yodlee_merchant} :description {:type 'String} :amount_lte {:type 'String} :amount_gte {:type 'String} :dom_lte {:type 'Int} :dom_gte {:type 'Int} :vendor {:type :vendor} :accounts {:type '(list :percentage_account)} :transaction_approval_status {:type :transaction_approval_status}}} :invoice_payment {:fields {:id {:type :id} :amount {:type 'String} :invoice_id {:type 'String} :payment_id {:type 'String} :payment {:type :payment} :invoice {:type :invoice}}} :user {:fields {:id {:type :id} :name {:type 'String} :role {:type 'String} :clients {:type '(list :client)}}} :account {:fields {:id {:type :id} :numeric_code {:type 'Int} :type {:type :ident} :account_set {:type 'String} :location {:type 'String} :name {:type 'String}}} :invoices_expense_accounts {:fields {:id {:type :id} :invoice_id {:type 'String} :account {:type :account} :location {:type 'String} :amount {:type 'String}}} :percentage_account {:fields {:id {:type :id} :account {:type :account} :location {:type 'String} :percentage {:type :percentage}}} :invoice {:fields {:id {:type :id} :original_id {:type 'Int} :client_identifier {:type 'String} :total {:type 'String} :outstanding_balance {:type 'String} :invoice_number {:type 'String} :status {:type 'String} :expense_accounts {:type '(list :invoices_expense_accounts)} :date {:type :iso_date} :due {:type :iso_date} :client_id {:type 'Int} :payments {:type '(list :invoice_payment)} :vendor {:type :vendor} :client {:type :client}}} :invoice_page {:fields {:invoices {:type '(list :invoice)} :count {:type 'Int} :total {:type 'Int} :start {:type 'Int} :end {:type 'Int}}} :payment_page {:fields {:payments {:type '(list :payment)} :count {:type 'Int} :total {:type 'Int} :start {:type 'Int} :end {:type 'Int}}} :transaction_page {:fields {:transactions {:type '(list :transaction)} :count {:type 'Int} :total {:type 'Int} :start {:type 'Int} :end {:type 'Int}}} :transaction_rule_page {:fields {:transaction_rules {:type '(list :transaction_rule)} :count {:type 'Int} :total {:type 'Int} :start {:type 'Int} :end {:type 'Int}}} :ledger_page {:fields {:journal_entries {:type '(list :journal_entry)} :count {:type 'Int} :total {:type 'Int} :start {:type 'Int} :end {:type 'Int}}} :reminder_page {:fields {:reminders {:type '(list :reminder)} :count {:type 'Int} :total {:type 'Int} :start {:type 'Int} :end {:type 'Int}}} :check_result {:fields {:invoices {:type '(list :invoice)} :pdf_url {:type 'String}}} :expense_account_stat {:fields {:account {:type :account} :total {:type 'String}}} :invoice_stat {:fields {:name {:type 'String} :paid {:type 'String} :unpaid {:type 'String}}} :import_ledger_entry_result {:fields {:external_id {:type 'String} :error {:type 'String} :status {:type 'String}}} :import_ledger_result {:fields {:successful {:type '(list :import_ledger_entry_result)} :existing {:type '(list :import_ledger_entry_result)} :errors {:type '(list :import_ledger_entry_result)} }} } :queries {:expense_account_stats {:type '(list :expense_account_stat) :args {:client_id {:type :id}} :resolve :get-expense-account-stats} :test_transaction_rule {:type '(list :transaction) :args {:transaction_rule {:type :edit_transaction_rule}} :resolve :test-transaction-rule} :run_transaction_rule {:type '(list :transaction) :args {:transaction_rule_id {:type :id}} :resolve :run-transaction-rule} :invoice_stats {:type '(list :invoice_stat) :args {:client_id {:type :id}} :resolve :get-invoice-stats} :potential_payment_matches {:type '(list :payment) :args {:transaction_id {:type :id}} :resolve :get-potential-payments} :potential_transaction_rule_matches {:type '(list :transaction_rule) :args {:transaction_id {:type :id}} :resolve :get-transaction-rule-matches} :balance_sheet {:type :balance_sheet :args {:client_id {:type :id} :date {:type :iso_date}} :resolve :get-balance-sheet} :profit_and_loss {:type :balance_sheet :args {:client_id {:type :id} :from_date {:type :iso_date} :to_date {:type :iso_date}} :resolve :get-profit-and-loss} :invoice_page {:type '(list :invoice_page) :args {:import_status {:type 'String} :date_range {:type :date_range} :status {:type 'String} :client_id {:type :id} :vendor_id {:type :id} :invoice_number_like {:type 'String} :start {:type 'Int} :sort {:type '(list :sort_item)}} :resolve :get-invoice-page} :all_invoices {:type '(list :invoice) :args {:client_id {:type :id} :client_code {:type 'String} :original_id {:type 'Int} :statuses {:type '(list String)}} :resolve :get-all-invoices} :accounts {:type '(list :account) :args {:account_set {:type 'String}} :resolve :get-accounts} :all_payments {:type '(list :payment) :args {:client_id {:type :id} :client_code {:type 'String} :original_id {:type 'Int} :statuses {:type '(list String)}} :resolve :get-all-payments} :yodlee_merchants {:type '(list :yodlee_merchant) :args {} :resolve :get-yodlee-merchants} :transaction_page {:type '(list :transaction_page) :args {:client_id {:type :id} :vendor_id {:type :id} :bank_account_id {:type :id} :date_range {:type :date_range} :amount_lte {:type :money} :amount_gte {:type :money} :description {:type 'String} :start {:type 'Int} :sort {:type '(list :sort_item)} :approval_status {:type :transaction_approval_status}} :resolve :get-transaction-page} :transaction_rule_page {:type :transaction_rule_page :args {:client_id {:type :id} :start {:type 'Int} :sort {:type '(list :sort_item)} :asc {:type 'Boolean}} :resolve :get-transaction-rule-page} :ledger_page {:type :ledger_page :args {:client_id {:type :id} :vendor_id {:type :id} :bank_account_id {:type :id} :from_date {:type :iso_date} :to_date {:type :iso_date} :location {:type 'String} :from_numeric_code {:type 'Int} :to_numeric_code {:type 'Int} :start {:type 'Int} :sort {:type '(list :sort_item)}} :resolve :get-ledger-page} :payment_page {:type '(list :payment_page) :args {:client_id {:type :id} :vendor_id {:type :id} :date_range {:type :date_range} :check_number_like {:type 'String} :start {:type 'Int} :sort {:type '(list :sort_item)}} :resolve :get-payment-page} :client {:type '(list :client) :resolve :get-client} :vendor {:type '(list :vendor) :resolve :get-vendor} :user {:type '(list :user) :resolve :get-user} } :input-objects { :sort_item {:fields {:sort_key {:type 'String} :sort_name {:type 'String} :asc {:type 'Boolean}}} :invoice_payment_amount {:fields {:invoice_id {:type :id} :amount {:type 'Float}}} :edit_location_match {:fields {:location {:type 'String} :match {:type 'String}}} :date_range {:fields {:start {:type :iso_date} :end {:type :iso_date}}} :import_ledger_line_item {:fields {:account_identifier {:type 'String} :location {:type 'String} :debit {:type 'String} :credit {:type 'String}}} :import_ledger_entry {:fields {:source {:type 'String} :external_id {:type 'String} :client_code {:type 'String} :date {:type 'String} :vendor_name {:type 'String} :amount {:type 'String} :note {:type 'String} :cleared_against {:type 'String} :line_items {:type '(list :import_ledger_line_item)}}} :edit_client {:fields {:id {:type :id} :name {:type 'String} :code {:type 'String} :email {:type 'String} :address {:type :add_address} :locations {:type '(list String)} :matches {:type '(list String)} :location_matches {:type '(list :edit_location_match)} :bank_accounts {:type '(list :edit_bank_account)}}} :edit_bank_account {:fields {:id {:type :id } :code {:type 'String} :type {:type :bank_account_type} :number {:type 'String} :check_number {:type 'Int} :visible {:type 'Boolean} :sort_order {:type 'Int} :name {:type 'String} :bank_code {:type 'String} :routing {:type 'String} :bank_name {:type 'String} :locations {:type '(list String)} :yodlee_account_id {:type 'Int}}} :edit_user {:fields {:id {:type :id} :name {:type 'String} :role {:type 'String} :clients {:type '(list String)}}} :add_contact {:fields {:id {:type :id} :name {:type 'String} :email {:type 'String} :phone {:type 'String}}} :add_address {:fields {:street1 {:type 'String} :street2 {:type 'String} :city {:type 'String} :state {:type 'String} :zip {:type 'String}}} :add_terms_override {:fields {:id {:type :id} :client_id {:type :id} :terms {:type 'Int} }} :add_account_override {:fields {:id {:type :id} :client_id {:type :id} :account_id {:type :id}}} :add_vendor {:fields {:id {:type :id} :name {:type 'String} :terms {:type 'Int} :terms_overrides {:type '(list :add_terms_override)} :code {:type 'String} :hidden {:type 'Boolean} :print_as {:type 'String} :primary_contact {:type :add_contact} :secondary_contact {:type :add_contact} :address {:type :add_address} :default_account_id {:type :id} :account_overrides {:type '(list :add_account_override)} :invoice_reminder_schedule {:type 'String}}} :edit_expense_account {:fields {:id {:type :id} :account_id {:type :id} :location {:type 'String} :amount {:type 'String}}} :add_invoice {:fields {:id {:type :id} :invoice_number {:type 'String} :expense_accounts {:type '(list :edit_expense_account)} :location {:type :iso_date} :date {:type :iso_date} :due {:type :iso_date} :client_id {:type :id} :vendor_id {:type :id} :vendor_name {:type 'String} :total {:type 'Float}}} :edit_invoice {:fields {:id {:type :id} :invoice_number {:type 'String} :expense_accounts {:type '(list :edit_expense_account)} :date {:type :iso_date} :due {:type :iso_date} :total {:type 'Float}}} :edit_transaction {:fields {:id {:type :id} :vendor_id {:type :id} :approval_status {:type :transaction_approval_status} :accounts {:type '(list :edit_expense_account)}}} :edit_percentage_account {:fields {:id {:type :id} :account_id {:type :id} :location {:type 'String} :percentage {:type :percentage}}} :edit_transaction_rule {:fields {:id {:type :id} :description {:type 'String} :note {:type 'String} :bank_account_id {:type :id} :client_id {:type :id} :yodlee_merchant_id {:type :id} :amount_lte {:type :money} :amount_gte {:type :money} :dom_lte {:type 'Int} :dom_gte {:type 'Int} :vendor_id {:type :id} :accounts {:type '(list :edit_percentage_account)} :transaction_approval_status {:type :transaction_approval_status}}} :edit_account {:fields {:id {:type :id} :type {:type :account_type} :numeric_code {:type 'Int} :location {:type 'String} :account_set {:type 'String} :name {:type 'String}}}} :enums {:payment_type {:values [{:enum-value :check} {:enum-value :cash} {:enum-value :debit}]} :bank_account_type {:values [{:enum-value :check} {:enum-value :credit} {:enum-value :cash}]} :account_type {:values [{:enum-value :dividend} {:enum-value :expense} {:enum-value :asset} {:enum-value :liability} {:enum-value :equity} {:enum-value :revenue}]} :transaction_approval_status {:values [{:enum-value :approved} {:enum-value :unapproved} {:enum-value :requires_feedback} {:enum-value :excluded}]}} :mutations {:reject_invoices {:type '(list :id) :args {:invoices {:type '(list :id)}} :resolve :mutation/reject-invoices} :approve_invoices {:type '(list :id) :args {:invoices {:type '(list :id)}} :resolve :mutation/approve-invoices} :unapprove_transactions {:type '(list :transaction_page) :args {:client_id {:type :id} :vendor_id {:type :id} :bank_account_id {:type :id} :date_range {:type :date_range} :amount_lte {:type :money} :amount_gte {:type :money} :description {:type 'String} :start {:type 'Int} :sort {:type '(list :sort_item)} :approval_status {:type :transaction_approval_status}} :resolve :mutation/unapprove-transactions} :merge_vendors {:type :id :args {:from {:type :id} :to {:type :id}} :resolve :mutation/merge-vendors} :add_and_print_invoice {:type :check_result :args {:invoice {:type :add_invoice} :bank_account_id {:type :id} :type {:type :payment_type}} :resolve :mutation/add-and-print-invoice} :print_checks {:type :check_result :args {:invoice_payments {:type '(list :invoice_payment_amount)} :bank_account_id {:type :id} :type {:type :payment_type} :client_id {:type :id}} :resolve :mutation/print-checks} :add_handwritten_check {:type :check_result :args {:invoice_payments {:type '(list :invoice_payment_amount)} :date {:type 'String} :check_number {:type 'Int} :bank_account_id {:type :id}} :resolve :mutation/add-handwritten-check} :edit_user {:type :user :args {:edit_user {:type :edit_user}} :resolve :mutation/edit-user} :edit_client {:type :client :args {:edit_client {:type :edit_client}} :resolve :mutation/edit-client} :upsert_vendor {:type :vendor :args {:vendor {:type :add_vendor}} :resolve :mutation/upsert-vendor} :upsert_transaction_rule {:type :transaction_rule :args {:transaction_rule {:type :edit_transaction_rule}} :resolve :mutation/upsert-transaction-rule} :add_invoice {:type :invoice :args {:invoice {:type :add_invoice}} :resolve :mutation/add-invoice} :import_ledger {:type :import_ledger_result :args {:entries {:type '(list :import_ledger_entry)}} :resolve :mutation/import-ledger} :edit_invoice {:type :invoice :args {:invoice {:type :edit_invoice}} :resolve :mutation/edit-invoice} :upsert_account {:type :account :args {:account {:type :edit_account}} :resolve :mutation/upsert-account} :edit_transaction {:type :transaction :args {:transaction {:type :edit_transaction}} :resolve :mutation/edit-transaction} :match_transaction {:type :transaction :args {:transaction_id {:type :id} :payment_id {:type :id}} :resolve :mutation/match-transaction} :match_transaction_rules {:type '(list :transaction) :args {:transaction_ids {:type '(list :id)} :all {:type 'Boolean} :transaction_rule_id {:type :id}} :resolve :mutation/match-transaction-rules} :void_invoice {:type :invoice :args {:invoice_id {:type :id}} :resolve :mutation/void-invoice} :unvoid_invoice {:type :invoice :args {:invoice_id {:type :id}} :resolve :mutation/unvoid-invoice} :void_payment {:type :payment :args {:payment_id {:type :id}} :resolve :mutation/void-payment} :edit_expense_accounts {:type :invoice :args {:invoice_id {:type :id} :expense_accounts {:type '(list :edit_expense_account)}} :resolve :mutation/edit-expense-accounts}}}) (defn snake->kebab [s] (str/replace s #"_" "-")) (defn kebab [x] (keyword (snake->kebab (name x)))) (defn kebab->snake [s] (str/replace s #"-" "_")) (defn snake [x] (keyword (kebab->snake (name x)))) (defn ->graphql [m] (walk/postwalk (fn [node] (cond (keyword? node) (snake node) :else node)) m)) (defn <-graphql [m] (walk/postwalk (fn [node] (cond (keyword? node) (kebab node) :else node)) m)) (defn get-all-payments [context args value] (assert-admin (:id context)) (map ->graphql (first (d-checks/get-graphql (assoc (<-graphql args) :count Integer/MAX_VALUE))))) (defn get-user [context args value] (assert-admin (:id context)) (doto (let [users (d-users/get-graphql args)] (->graphql users)) println)) (defn get-vendor [context args value] (->graphql (d-vendors/get-graphql (assoc args :id (:id context))))) (defn print-checks [context args value] (assert-can-see-client (:id context) (:client_id args)) (->graphql (gq-checks/print-checks (map (fn [i] {:invoice-id (:invoice_id i) :amount (:amount i)}) (:invoice_payments args)) (:client_id args) (:bank_account_id args) (:type args)))) (defn get-expense-account-stats [context {:keys [client_id] } value] (let [result (cond-> {:query {:find ['?account '?account-name '(sum ?amount)] :in ['$] :where []} :args [(d/db (d/connect uri)) client_id]} client_id (merge-query {:query {:in ['?c]} :args [client_id]}) (not client_id) (merge-query {:query {:where ['[?c :client/name]]}}) true (merge-query {:query {:where ['[?i :invoice/client ?c] '[?i :invoice/expense-accounts ?expense-account] '[?expense-account :invoice-expense-account/account ?account] '[?account :account/name ?account-name] '[?expense-account :invoice-expense-account/amount ?amount]]}}) true (d/query ))] (for [[account-id account-name total] result] {:account {:id account-id :name account-name} :total total }))) (defn categorize [x] (cond (<= x 0) :due (<= x 30 ) :due-30 (<= x 60 ) :due-60 :else :due-later)) (defn get-invoice-stats [context {:keys [client_id] } value] (let [result (cond-> {:query {:find ['?name '(sum ?outstanding-balance) '(sum ?total)] :in ['$] :where []} :args [(d/db (d/connect uri)) client_id]} client_id (merge-query {:query {:in ['?c]} :args [client_id]}) (not client_id) (merge-query {:query {:where ['[?c :client/name]]}}) true (merge-query {:query {:where ['[?i :invoice/client ?c] '[?i :invoice/outstanding-balance ?outstanding-balance] '[?i :invoice/total ?total] '[?i :invoice/date ?date] '[(.toInstant ^java.util.Date ?date) ?d2] '[(.between java.time.temporal.ChronoUnit/DAYS (java.time.Instant/now) ?d2 ) ?d3] '[(+ 30 ?d3) ?d4] '[(auto-ap.graphql/categorize ?d4) ?name]]}}) true (d/query )) result (group-by first result)] (for [[id name] [[:due "Due"] [:due-30 "0-30 days"] [:due-60 "31-60 days"] [:due-later ">60 days"] ] :let [[[_ outstanding-balance total] ] (id result nil) outstanding-balance (or outstanding-balance 0) total (or total 0)]] {:name name :unpaid outstanding-balance :paid (if (= :due id) 0 (- total outstanding-balance))}))) (def schema (-> integreat-schema (attach-resolvers {:get-invoice-page gq-invoices/get-invoice-page :get-all-invoices gq-invoices/get-all-invoices :get-all-payments get-all-payments :get-payment-page gq-checks/get-payment-page :get-potential-payments gq-checks/get-potential-payments :get-accounts gq-accounts/get-accounts :get-transaction-page gq-transactions/get-transaction-page :get-ledger-page gq-ledger/get-ledger-page :get-balance-sheet gq-ledger/get-balance-sheet :get-profit-and-loss gq-ledger/get-profit-and-loss :get-transaction-rule-page gq-transaction-rules/get-transaction-rule-page :get-transaction-rule-matches gq-transaction-rules/get-transaction-rule-matches :get-expense-account-stats get-expense-account-stats :get-invoice-stats get-invoice-stats :get-yodlee-merchants ym/get-yodlee-merchants :get-client gq-clients/get-client :get-user get-user :mutation/add-handwritten-check gq-checks/add-handwritten-check :mutation/print-checks print-checks :mutation/reject-invoices gq-invoices/reject-invoices :mutation/approve-invoices gq-invoices/approve-invoices :mutation/edit-user gq-users/edit-user :mutation/add-invoice gq-invoices/add-invoice :mutation/add-and-print-invoice gq-invoices/add-and-print-invoice :mutation/edit-invoice gq-invoices/edit-invoice :mutation/edit-transaction gq-transactions/edit-transaction :mutation/unapprove-transactions gq-transactions/unapprove-transactions :mutation/upsert-transaction-rule gq-transaction-rules/upsert-transaction-rule :test-transaction-rule gq-transaction-rules/test-transaction-rule :run-transaction-rule gq-transaction-rules/run-transaction-rule :mutation/match-transaction gq-transactions/match-transaction :mutation/match-transaction-rules gq-transactions/match-transaction-rules :mutation/edit-client gq-clients/edit-client :mutation/upsert-vendor gq-vendors/upsert-vendor :mutation/upsert-account gq-accounts/upsert-account :mutation/merge-vendors gq-vendors/merge-vendors :mutation/void-invoice gq-invoices/void-invoice :mutation/unvoid-invoice gq-invoices/unvoid-invoice :mutation/void-payment gq-checks/void-check :mutation/edit-expense-accounts gq-invoices/edit-expense-accounts :mutation/import-ledger gq-ledger/import-ledger :get-vendor get-vendor}) schema/compile)) (defn simplify "Converts all ordered maps nested within the map into standard hash maps, and sequences into vectors, which makes for easier constants in the tests, and eliminates ordering problems." [m] (walk/postwalk (fn [node] (cond (instance? IPersistentMap node) (into {} node) (seq? node) (vec node) (keyword? node) (kebab node) :else node)) m)) (defn query ([id q] (query id q nil )) ([id q v] (println "executing graphql query" id q v) (try (let [result (time (simplify (execute schema q v {:id id})))] (when (seq (:errors result)) (throw (ex-info "GraphQL error" {:result result}))) result) (catch Exception e (if-let [v (:validation-error (ex-data e))] (println "validation error" v) (do (.printStackTrace e) (println e ))) (throw e))))) #_(query nil "{ ledger_page { count }}" nil)