git.fiddlerwoaroof.com
Raw Blame History
(in-package :mfa-tool)
(defpackage :mfa-tool.stack
  (:use :cl)
  (:export #:stack-interface
           #:format-stack-status))
(in-package :mfa-tool.stack)

(defun dispatch-with-action-creator (action-creator)
  (lambda (store data)
    (mfa-tool.store:dispatch store
                             (funcall action-creator data))))

(defmacro with-pp ((pane) &body body)
  `(capi:apply-in-pane-process ,pane
                               (lambda ()
                                 ,@body)))

(defun human-readable-stack-status (stack)
  (nstring-capitalize
   (substitute #\space #\_
               (string (daydreamer.aws-result:stack-status stack)))))
(defun format-stack-status (stream stack &optional colon-p at-sign-p)
  (declare (ignore colon-p at-sign-p))
  (princ (human-readable-stack-status stack)
         stream))

(defun get-output-columns (type col1 col2)
  `((:title ,(format nil "~a Name" type)
     :adjust :right
     :width (character ,(max (+ (length type) 5) col1)))
    (:title "Value"
     :adjust :left
     :width (character ,(max (+ (length type) 5) col2)))))

;; doesn't work, seems to cause segfaults, but would be a cool feature :(
(defun go-to-console (interface stack)
  (with-pp (interface)
    (mfa-tool::open-url
     (format nil "https://us-west-2.console.aws.amazon.com/cloudformation/home?region=us-west-2#/stacks/stackinfo?stackId=~a"
             (quri:url-encode (daydreamer.aws-result:stack-id stack))))))

(capi:define-interface stack-interface (capi:interface mfa-tool.stack-store:stack-store)
  ()
  (:panes
   (region-chooser capi:option-pane
                   :reader region-chooser
                   ;; :external-max-width '(character 35)
                   :items (list :|us-east-1| :|us-east-2|
                                :|us-west-1| :|us-west-2|
                                :|ca-central-1|
                                :|eu-central-1|
                                :|eu-west-1| :|eu-west-2|)
                   :selected-item (mfa-tool.aws-dispatcher::region capi:interface)
                   :selection-callback (dispatch-with-action-creator 'mfa-tool.aws-dispatcher::update-region)
                   :callback-type :interface-data)

   (stack-chooser capi:list-panel
                  :reader stack-chooser
                  ;; :external-max-width '(character 35)
                  :items ()
                  :print-function 'daydreamer.aws-result:stack-name
                  :selection-callback (dispatch-with-action-creator 'mfa-tool.aws-dispatcher:select-stack)
                  :callback-type :interface-data)

   (refresh-status capi:push-button
                   :text "Refresh"
                   :callback-type :interface
                   :callback (lambda (store)
                               (mfa-tool.store:dispatch store :refresh)))
   #+(or)
   (open-web-console capi:push-button
                     :text "Open"
                     :callback-type :interface
                     :callback (lambda (store)
                                 (go-to-console
                                  (mfa-tool.stack-store:selected-stack store))))
   (status-display capi:display-pane
                   :background :transparent
                   :reader status-display
                   :text "")

   (outputs-display capi:multi-column-list-panel
                    :columns (get-output-columns "Output" 10 10)
                    :header-args (list :selection-callback :sort)
                    :sort-descriptions (list (capi:make-sorting-description
                                              :type "Output Name"
                                              :key 'car
                                              :sort 'string-lessp
                                              :reverse-sort 'string-greaterp)
                                             (capi:make-sorting-description
                                              :type "Value"
                                              :key 'cadr
                                              :sort 'string-lessp
                                              :reverse-sort 'string-greaterp))
                    :items nil
                    :vertical-scroll t
                    :reader outputs-display
                    :visible-min-height '(character 10)
                    :visible-min-width '(character 50))
   (parameters-display capi:multi-column-list-panel
                       :columns (get-output-columns "Parameter" 10 10)
                       :header-args (list :selection-callback :sort)
                       :sort-descriptions (list (capi:make-sorting-description
                                                 :type "Parameter Name"
                                                 :key 'car
                                                 :sort 'string-lessp
                                                 :reverse-sort 'string-greaterp)
                                                (capi:make-sorting-description
                                                 :type "Value"
                                                 :key 'cadr
                                                 :sort 'string-lessp
                                                 :reverse-sort 'string-greaterp))
                       :items nil
                       :vertical-scroll t
                       :reader parameters-display
                       :visible-min-height '(character 10)
                       :visible-min-width '(character 50)))
  (:layouts
   (key-layout capi:column-layout
               '(region-chooser
                 stack-chooser)
               :visible-max-width '(character 35))
   (status-layout capi:row-layout
                  '(status-display
                    nil
                    #+(or)open-web-console
                    refresh-status)
                  :adjust :center)
   (attribute-layout capi:column-layout
                     '(status-layout
                       parameters-display
                       outputs-display))
   (main-layout capi:row-layout
                '(key-layout
                  attribute-layout)))
  (:default-initargs
   :layout 'main-layout
   :title "Stack Explorer"
   :visible-min-width 800))

(defmethod mfa-tool.store:execute :after ((interface stack-interface) (_ mfa-tool.aws-dispatcher:update-stacks))
  (with-pp (interface)
    (with-accessors ((stack-chooser stack-chooser)) interface
      (setf (capi:collection-items stack-chooser) (mfa-tool.stack-store:available-stacks interface)
            (capi:choice-selected-item
             (stack-chooser interface)) (mfa-tool.stack-store:selected-stack interface)))))

(defun max-widths (cols)
  (loop for (col1 col2) in cols
        maximizing (length col1) into len1
        maximizing (length col2) into len2
        finally (return (list len1 len2))))

(defmethod mfa-tool.store:execute :after ((interface stack-interface) (_ mfa-tool.aws-dispatcher:select-stack))
  (with-pp (interface)
    (with-accessors ((status-display status-display) (selected-stack mfa-tool.stack-store:selected-stack)
                     (outputs-display outputs-display) (outputs mfa-tool.stack-store:outputs)
                     (parameters-display parameters-display) (parameters mfa-tool.stack-store:parameters))
        interface
      (capi:modify-multi-column-list-panel-columns
       outputs-display :columns (apply 'get-output-columns "Output" (max-widths outputs)))
      (capi:modify-multi-column-list-panel-columns
       parameters-display :columns (apply 'get-output-columns "Parameter" (max-widths parameters)))

      (when (not (eq (capi:choice-selected-item (stack-chooser interface))
                     (mfa-tool.aws-dispatcher:stack _)))
        (setf (capi:choice-selected-item (stack-chooser interface)) (mfa-tool.stack-store:selected-stack interface)))
      (setf (capi:display-pane-text status-display)
            (format nil "~a: ~/mfa-tool.stack:format-stack-status/"
                    (daydreamer.aws-result:stack-name selected-stack)
                    selected-stack)
            (capi:collection-items parameters-display) parameters
            (capi:collection-items outputs-display) outputs))))