summaryrefslogtreecommitdiff
path: root/lisp/service
diff options
context:
space:
mode:
authorckonstanski <carlos.konstanski@olo.com>2025-10-07 06:53:09 -0600
committerckonstanski <carlos.konstanski@olo.com>2025-10-07 06:53:09 -0600
commit50273c110856e18e3ba563b0f62b7273a4dc022b (patch)
treecc58541bb299c659c5fd98774d3693aa99ff092a /lisp/service
parent9adba239b937df2830a5110adaa1dae17b7dd7d7 (diff)
initial commit
Diffstat (limited to 'lisp/service')
-rw-r--r--lisp/service/asg-recycle-service.lisp65
-rw-r--r--lisp/service/auth-service.lisp21
-rw-r--r--lisp/service/base-service.lisp8
-rw-r--r--lisp/service/generic-form.lisp87
-rw-r--r--lisp/service/git-update-service.lisp40
-rw-r--r--lisp/service/home-service.lisp18
-rw-r--r--lisp/service/menu-service.lisp59
-rw-r--r--lisp/service/octopus-environments-with-roles-service.lisp47
-rw-r--r--lisp/service/octopus-latest-deployments-service.lisp41
-rw-r--r--lisp/service/octopus-machines-with-roles-service.lisp42
-rw-r--r--lisp/service/octopus-ode-deploy-release-service.lisp50
-rw-r--r--lisp/service/octopus-projects-with-roles-service.lisp44
-rw-r--r--lisp/service/rest-service.lisp36
-rw-r--r--lisp/service/tfcloud-apply-service.lisp59
14 files changed, 617 insertions, 0 deletions
diff --git a/lisp/service/asg-recycle-service.lisp b/lisp/service/asg-recycle-service.lisp
new file mode 100644
index 0000000..821abb5
--- /dev/null
+++ b/lisp/service/asg-recycle-service.lisp
@@ -0,0 +1,65 @@
+;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
+(declaim (optimize (speed 0) (safety 3) (debug 3)))
+
+(in-package :snow)
+
+(defclass asg-recycle-service (rest-service)
+ ()
+ (:documentation ""))
+
+(defun asg-recycle-json ()
+ (with-noauth (instance asg-recycle-service)
+ t))
+
+(defclass asg-recycle/view-service (asg-recycle-service)
+ ((form :initarg :form
+ :initform nil
+ :accessor form)
+ (location-p :initform nil))
+ (:documentation ""))
+
+(defun asg-recycle-view-json ()
+ (with-noauth (instance asg-recycle/view-service)
+ (setf (form instance)
+ (make-form "asg-recycle-form"
+ nil
+ t
+ `((:name "profile" :label "Profile" :field-type "text" :required "required" :value ,(or (session-value :profile) ""))
+ (:name "region" :label "Region" :field-type "text" :required "required" :value ,(or (session-value :region) (region *webapp*)))
+ (:name "filter" :label "Filter" :field-type "text" :required "required" :value ,(or (session-value :asg-recycle-filter) ""))
+ (:label "Find Matching ASGs" :field-type "button" :onclick "on_asg_recycle_results_clicked()"))))))
+
+(defclass asg-recycle/results-service (asg-recycle/view-service)
+ ((results :initarg :results
+ :initform nil
+ :accessor results)
+ (location-p :initform nil))
+ (:documentation ""))
+
+(defun asg-recycle-results-json (profile region filter)
+ (with-noauth (instance asg-recycle/results-service)
+ (let* ((aws (make-instance 'aws :profile profile :region region))
+ (asgs (get-asgs aws filter)))
+ (setf (results instance) (mapcar (lambda (asg)
+ (sanitize-json asg))
+ asgs))
+ (setf (session-value :profile) profile)
+ (setf (session-value :region) region)
+ (setf (session-value :asg-recycle-filter) filter)
+ (setf (session-value :asg-recycle-asgs) asgs)
+ (setf (form instance)
+ (make-form "asg-recycle-submit-form"
+ nil
+ t
+ `((:label "Recycle All Hosts in ASGs" :field-type "button" :onclick "on_asg_recycle_results_submit_clicked('all')")
+ (:label "Recycle One Host in ASGs" :field-type "button" :onclick "on_asg_recycle_results_submit_clicked('one')")))))))
+
+(defclass asg-recycle/results-submit-service (asg-recycle-service)
+ ((location-p :initform nil))
+ (:documentation ""))
+
+(defun asg-recycle-results-submit-json (howmany)
+ (with-noauth (instance asg-recycle/results-submit-service)
+ (loop for asg in (session-value :asg-recycle-asgs)
+ do (setf (howmany asg) howmany)
+ (recycle-asg asg))))
diff --git a/lisp/service/auth-service.lisp b/lisp/service/auth-service.lisp
new file mode 100644
index 0000000..cdf9550
--- /dev/null
+++ b/lisp/service/auth-service.lisp
@@ -0,0 +1,21 @@
+;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
+(declaim (optimize (speed 0) (safety 3) (debug 3)))
+
+(in-package :snow)
+
+(defclass auth-service (rest-service)
+ ()
+ (:documentation ""))
+
+(defmethod initialize-instance :after ((auth-service auth-service) &key)
+ (when (not (string= (session-value :permissions) "admin"))
+ (setf (location auth-service) "/home")
+ (setf (errormsg auth-service) "You are not authorized to access this resource.")))
+
+(defmacro with-noauth ((instance rest-service) &body body)
+ `(let ((,instance (make-instance ',rest-service)))
+ ,@body
+ (when (location-p ,instance)
+ (setf (session-value :message) nil)
+ (setf (session-value :errormsg) nil))
+ (org-ckons-json::objects-to-json `(,,instance))))
diff --git a/lisp/service/base-service.lisp b/lisp/service/base-service.lisp
new file mode 100644
index 0000000..6760ada
--- /dev/null
+++ b/lisp/service/base-service.lisp
@@ -0,0 +1,8 @@
+;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
+(declaim (optimize (speed 0) (safety 3) (debug 3)))
+
+(in-package :snow)
+
+(defclass base-service ()
+ ()
+ (:documentation ""))
diff --git a/lisp/service/generic-form.lisp b/lisp/service/generic-form.lisp
new file mode 100644
index 0000000..7902ff6
--- /dev/null
+++ b/lisp/service/generic-form.lisp
@@ -0,0 +1,87 @@
+;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
+(declaim (optimize (speed 0) (safety 3) (debug 3)))
+
+(in-package :snow)
+
+(defclass generic-form (base-service)
+ ((name :initarg :name
+ :initform nil
+ :accessor name)
+ (http-method :initarg :http-method
+ :initform "POST"
+ :accessor http-method)
+ (action :initarg :action
+ :initform nil
+ :accessor action)
+ (required-p :initarg :required-p
+ :initform nil
+ :accessor required-p)
+ (form-fields :initarg :form-fields
+ :initform nil
+ :accessor form-fields))
+ (:documentation ""))
+
+(defclass form-field (base-service)
+ ((name :initarg :name
+ :initform nil
+ :accessor name)
+ (label :initarg :label
+ :initform nil
+ :accessor label)
+ (value :initarg :value
+ :initform nil
+ :accessor value)
+ (checked :initarg :checked
+ :initform nil
+ :accessor checked)
+ (field-type :initarg :field-type
+ :initform nil
+ :accessor field-type)
+ (required :initarg :required
+ :initform nil
+ :accessor required)
+ (dismiss :initarg :dismiss
+ :initform nil
+ :accessor dismiss)
+ (options :initarg :options
+ :initform nil
+ :accessor options)
+ (onclick :initarg :onclick
+ :initform nil
+ :accessor onclick)
+ (onchange :initarg :onchange
+ :initform nil
+ :accessor onchange))
+ (:documentation ""))
+
+(defclass option (base-service)
+ ((label :initarg :label
+ :initform nil
+ :accessor label)
+ (value :initarg :value
+ :initform nil
+ :accessor value))
+ (:documentation ""))
+
+(defun make-form (name action required-p fields)
+ (make-instance 'generic-form
+ :name name
+ :action action
+ :required-p required-p
+ :form-fields (mapcar (lambda (field)
+ (make-instance 'form-field
+ :name (getf field :name)
+ :label (getf field :label)
+ :value (getf field :value)
+ :checked (getf field :checked)
+ :field-type (getf field :field-type)
+ :required (getf field :required)
+ :dismiss (getf field :dismiss)
+ :options (mapcar (lambda (option)
+ (make-instance 'option
+ :label (getf option :label)
+ :value (getf option :value)))
+ (getf field :options))
+ :onclick (getf field :onclick)
+ :onchange (getf field :onchange)))
+ fields)))
diff --git a/lisp/service/git-update-service.lisp b/lisp/service/git-update-service.lisp
new file mode 100644
index 0000000..31b1c32
--- /dev/null
+++ b/lisp/service/git-update-service.lisp
@@ -0,0 +1,40 @@
+;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
+(declaim (optimize (speed 0) (safety 3) (debug 3)))
+
+(in-package :snow)
+
+(defclass git-update-service (rest-service)
+ ()
+ (:documentation ""))
+
+(defun git-update-json ()
+ (with-noauth (instance git-update-service)
+ t))
+
+(defclass git-update/view-service (git-update-service)
+ ((form :initarg :form
+ :initform nil
+ :accessor form)
+ (location-p :initform nil))
+ (:documentation ""))
+
+(defun git-update-view-json ()
+ (with-noauth (instance git-update/view-service)
+ (setf (form instance)
+ (make-form "git-update-form"
+ nil
+ t
+ `((:label "Update Git Checkouts" :field-type "button" :onclick "on_git_update_view_clicked()"))))))
+
+(defclass git-update/results-service (git-update-service)
+ ((stdout :initarg :stdout
+ :initform nil
+ :accessor stdout)
+ (location-p :initform nil))
+ (:documentation ""))
+
+(defun git-update-results-json ()
+ (with-noauth (instance git-update/results-service)
+ (cl-fad:with-output-to-temporary-file (f :template "/tmp/snow/temp-%")
+ (update-git (make-instance 'git-update :stdout (pathname f)))
+ (setf (stdout instance) (uiop:native-namestring (pathname f))))))
diff --git a/lisp/service/home-service.lisp b/lisp/service/home-service.lisp
new file mode 100644
index 0000000..509be5e
--- /dev/null
+++ b/lisp/service/home-service.lisp
@@ -0,0 +1,18 @@
+;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
+(declaim (optimize (speed 0) (safety 3) (debug 3)))
+
+(in-package :snow)
+
+(defclass home-service (rest-service)
+ ((content :initarg :content
+ :initform nil
+ :accessor content))
+ (:documentation ""))
+
+(defmethod initialize-instance :after ((home-service home-service) &key)
+ (setf (content home-service) (format nil "Welcome to the ~a" (title *webapp*))))
+
+(defun home-json (&optional message errormsg)
+ (with-noauth (instance home-service)
+ (when message (setf (message instance) message))
+ (when errormsg (setf (errormsg instance) errormsg))))
diff --git a/lisp/service/menu-service.lisp b/lisp/service/menu-service.lisp
new file mode 100644
index 0000000..7911e32
--- /dev/null
+++ b/lisp/service/menu-service.lisp
@@ -0,0 +1,59 @@
+;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
+(declaim (optimize (speed 0) (safety 3) (debug 3)))
+
+(in-package :snow)
+
+(defparameter *menu-config* '((:id "a_menu_home" :label "Home" :handler "/home" :permissions "t")
+ (:id "a_menu_git_update" :label "Git Update" :handler "/git-update" :permissions "t")
+ (:id "a_menu_asg_recycle" :label "ASG Recycle" :handler "/asg-recycle" :permissions "t")
+ (:id "a_menu_tfcloud_apply" :label "TFCloud Apply" :handler "/tfcloud-apply" :permissions "t")
+ (:id "a_menu_octopus_machines_with_roles" :label "Octo Machines With Roles" :handler "/octopus-machines-with-roles" :permissions "t")
+ (:id "a_menu_octopus_environments_with_roles" :label "Octo Envs With Roles" :handler "/octopus-environments-with-roles" :permissions "t")
+ (:id "a_menu_octopus_projects_with_roles" :label "Octo Projects With Roles" :handler "/octopus-projects-with-roles" :permissions "t")
+ (:id "a_menu_octopus_ode_deploy_release" :label "Deploy Octo Release to ODEs" :handler "/octopus-ode-deploy-release" :permissions "t")
+ (:id "a_menu_octopus_latest_deployments" :label "Octo Latest Deployments" :handler "/octopus-latest-deployments" :permissions "t")))
+
+(defclass menuitem ()
+ ((id :initarg :id
+ :initform nil
+ :accessor id)
+ (label :initarg :label
+ :initform nil
+ :accessor label)
+ (handler :initarg :handler
+ :initform nil
+ :accessor handler)
+ (permissions :initarg :permissions
+ :initform nil
+ :accessor permissions)
+ (children :initarg :children
+ :initform nil
+ :accessor children))
+ (:documentation ""))
+
+(defclass menu-service (base-service)
+ ((menuitems :initarg :menuitems
+ :initform nil
+ :accessor menuitems)
+ (location-p :initarg :location-p
+ :initform nil
+ :accessor location-p))
+ (:documentation ""))
+
+(defmethod initialize-instance :after ((menu-service menu-service) &key)
+ (setf (menuitems menu-service)
+ (mapcar (lambda (x)
+ (make-instance 'menuitem
+ :id (getf x :id)
+ :label (getf x :label)
+ :handler (getf x :handler)))
+ (remove-if 'null (mapcar (lambda (x)
+ (when (find-if (lambda (y)
+ (string= (getf x :permissions) y))
+ `("t" ,(session-value :permissions)))
+ x))
+ *menu-config*)))))
+
+(defun menu-json ()
+ (with-noauth (instance menu-service)
+ t))
diff --git a/lisp/service/octopus-environments-with-roles-service.lisp b/lisp/service/octopus-environments-with-roles-service.lisp
new file mode 100644
index 0000000..8cd494e
--- /dev/null
+++ b/lisp/service/octopus-environments-with-roles-service.lisp
@@ -0,0 +1,47 @@
+;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
+(declaim (optimize (speed 0) (safety 3) (debug 3)))
+
+(in-package :snow)
+
+(defclass octopus-environments-with-roles-service (rest-service)
+ ()
+ (:documentation ""))
+
+(defun octopus-environments-with-roles-json ()
+ (with-noauth (instance octopus-environments-with-roles-service)
+ t))
+
+(defclass octopus-environments-with-roles/view-service (octopus-environments-with-roles-service)
+ ((form :initarg :form
+ :initform nil
+ :accessor form)
+ (location-p :initform nil))
+ (:documentation ""))
+
+(defun octopus-environments-with-roles-view-json ()
+ (with-noauth (instance octopus-environments-with-roles/view-service)
+ (setf (form instance)
+ (make-form "octopus-environments-with-roles-form"
+ nil
+ t
+ `((:name "roles" :label "Roles (comma or space separated)" :field-type "text" :required "required" :value ,(or (session-value :octopus-roles) ""))
+ (:name "ode_only_p" :label "ODE only?" :field-type "checkbox")
+ (:label "Find Envs With Roles" :field-type "button" :onclick "on_octopus_environments_with_roles_results_clicked()"))))))
+
+(defclass octopus-environments-with-roles/results-service (octopus-environments-with-roles/view-service)
+ ((results :initarg :results
+ :initform nil
+ :accessor results)
+ (location-p :initform nil))
+ (:documentation ""))
+
+(defun octopus-environments-with-roles-results-json (ode_only_p roles)
+ (with-noauth (instance octopus-environments-with-roles/results-service)
+ (with-octopus (octopus)
+ (let ((environments (get-octopus-environments-with-roles octopus
+ :ode-only-p (string= "true" ode_only_p)
+ :roles (remove-if (lambda (x)
+ (org-ckons-core::null-or-empty-p x))
+ (cl-ppcre:split "[, ]" roles)))))
+ (setf (results instance) environments)
+ (setf (session-value :octopus-roles) roles)))))
diff --git a/lisp/service/octopus-latest-deployments-service.lisp b/lisp/service/octopus-latest-deployments-service.lisp
new file mode 100644
index 0000000..4b786bd
--- /dev/null
+++ b/lisp/service/octopus-latest-deployments-service.lisp
@@ -0,0 +1,41 @@
+;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
+(declaim (optimize (speed 0) (safety 3) (debug 3)))
+
+(in-package :snow)
+
+(defclass octopus-latest-deployments-service (rest-service)
+ ()
+ (:documentation ""))
+
+(defun octopus-latest-deployments-json ()
+ (with-noauth (instance octopus-latest-deployments-service)
+ t))
+
+(defclass octopus-latest-deployments/view-service (octopus-latest-deployments-service)
+ ((form :initarg :form
+ :initform nil
+ :accessor form)
+ (location-p :initform nil))
+ (:documentation ""))
+
+(defun octopus-latest-deployments-view-json ()
+ (with-noauth (instance octopus-latest-deployments/view-service)
+ (setf (form instance)
+ (make-form "octopus-latest-deployments-form"
+ nil
+ t
+ `((:name "ode_only_p" :label "ODE only?" :field-type "checkbox")
+ (:label "Find Latest Deployments" :field-type "button" :onclick "on_octopus_latest_deployments_results_clicked()"))))))
+
+(defclass octopus-latest-deployments/results-service (octopus-latest-deployments/view-service)
+ ((results :initarg :results
+ :initform nil
+ :accessor results)
+ (location-p :initform nil))
+ (:documentation ""))
+
+(defun octopus-latest-deployments-results-json (ode_only_p)
+ (with-noauth (instance octopus-latest-deployments/results-service)
+ (with-octopus (octopus)
+ (let ((deployments (get-octopus-latest-deployments octopus :ode-only-p (string= "true" ode_only_p))))
+ (setf (results instance) deployments)))))
diff --git a/lisp/service/octopus-machines-with-roles-service.lisp b/lisp/service/octopus-machines-with-roles-service.lisp
new file mode 100644
index 0000000..b9bf74f
--- /dev/null
+++ b/lisp/service/octopus-machines-with-roles-service.lisp
@@ -0,0 +1,42 @@
+;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
+(declaim (optimize (speed 0) (safety 3) (debug 3)))
+
+(in-package :snow)
+
+(defclass octopus-machines-with-roles-service (rest-service)
+ ()
+ (:documentation ""))
+
+(defun octopus-machines-with-roles-json ()
+ (with-noauth (instance octopus-machines-with-roles-service)
+ t))
+
+(defclass octopus-machines-with-roles/view-service (octopus-machines-with-roles-service)
+ ((form :initarg :form
+ :initform nil
+ :accessor form)
+ (location-p :initform nil))
+ (:documentation ""))
+
+(defun octopus-machines-with-roles-view-json ()
+ (with-noauth (instance octopus-machines-with-roles/view-service)
+ (setf (form instance)
+ (make-form "octopus-machines-with-roles-form"
+ nil
+ t
+ `((:name "roles" :label "Roles (comma or space separated)" :field-type "text" :required "required" :value ,(or (session-value :octopus-roles) ""))
+ (:label "Find Machines With Roles" :field-type "button" :onclick "on_octopus_machines_with_roles_results_clicked()"))))))
+
+(defclass octopus-machines-with-roles/results-service (octopus-machines-with-roles/view-service)
+ ((results :initarg :results
+ :initform nil
+ :accessor results)
+ (location-p :initform nil))
+ (:documentation ""))
+
+(defun octopus-machines-with-roles-results-json (roles)
+ (with-noauth (instance octopus-machines-with-roles/results-service)
+ (with-octopus (octopus)
+ (let ((machines (get-octopus-machines-with-roles octopus (remove-if (lambda (x) (org-ckons-core::null-or-empty-p x)) (cl-ppcre:split "[, ]" roles)))))
+ (setf (results instance) (loop for machine in machines collect (sanitize machine)))
+ (setf (session-value :octopus-roles) roles)))))
diff --git a/lisp/service/octopus-ode-deploy-release-service.lisp b/lisp/service/octopus-ode-deploy-release-service.lisp
new file mode 100644
index 0000000..411fc15
--- /dev/null
+++ b/lisp/service/octopus-ode-deploy-release-service.lisp
@@ -0,0 +1,50 @@
+;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
+(declaim (optimize (speed 0) (safety 3) (debug 3)))
+
+(in-package :snow)
+
+(defclass octopus-ode-deploy-release-service (rest-service)
+ ()
+ (:documentation ""))
+
+(defun octopus-ode-deploy-release-json ()
+ (with-noauth (instance octopus-ode-deploy-release-service)
+ t))
+
+(defclass octopus-ode-deploy-release/view-service (octopus-ode-deploy-release-service)
+ ((form :initarg :form
+ :initform nil
+ :accessor form)
+ (location-p :initform nil))
+ (:documentation ""))
+
+(defun octopus-ode-deploy-release-view-json ()
+ (with-noauth (instance octopus-ode-deploy-release/view-service)
+ (setf (form instance)
+ (make-form "octopus-ode-deploy-release-form"
+ nil
+ t
+ `((:name "project_name" :label "Project Name" :field-type "text" :required "required" :value ,(or (session-value :octopus-project-name) ""))
+ (:name "version" :label "Release" :field-type "text" :required "required" :value ,(or (session-value :octopus-release) ""))
+ (:name "ode_names" :label "ODE Names (comma or space separated) (\"all\" for all ODEs)" :field-type "text" :required "required" :value ,(or (session-value :octopus-ode-names) ""))
+ (:label "Deploy Release to ODEs" :field-type "button" :onclick "on_octopus_ode_deploy_release_results_clicked()"))))))
+
+(defclass octopus-ode-deploy-release/results-service (octopus-ode-deploy-release/view-service)
+ ((stdout :initarg :stdout
+ :initform nil
+ :accessor stdout)
+ (location-p :initform nil))
+ (:documentation ""))
+
+(defun octopus-ode-deploy-release-results-json (project_name version ode_names)
+ (with-noauth (instance octopus-ode-deploy-release/results-service)
+ (cl-fad:with-output-to-temporary-file (f :template "/tmp/snow/temp-%")
+ (do-octopus-ode-deploy-release (make-instance 'octopus-ode-deploy-release
+ :project-name project_name
+ :version version
+ :ode-names (cl-ppcre:split "[, ]" ode_names)
+ :stdout (pathname f)))
+ (setf (stdout instance) (uiop:native-namestring (pathname f))))
+ (setf (session-value :octopus-project-name) project_name)
+ (setf (session-value :octopus-release) version)
+ (setf (session-value :octopus-ode-names) ode_names)))
diff --git a/lisp/service/octopus-projects-with-roles-service.lisp b/lisp/service/octopus-projects-with-roles-service.lisp
new file mode 100644
index 0000000..c3d9a67
--- /dev/null
+++ b/lisp/service/octopus-projects-with-roles-service.lisp
@@ -0,0 +1,44 @@
+;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
+(declaim (optimize (speed 0) (safety 3) (debug 3)))
+
+(in-package :snow)
+
+(defclass octopus-projects-with-roles-service (rest-service)
+ ()
+ (:documentation ""))
+
+(defun octopus-projects-with-roles-json ()
+ (with-noauth (instance octopus-projects-with-roles-service)
+ t))
+
+(defclass octopus-projects-with-roles/view-service (octopus-projects-with-roles-service)
+ ((form :initarg :form
+ :initform nil
+ :accessor form)
+ (location-p :initform nil))
+ (:documentation ""))
+
+(defun octopus-projects-with-roles-view-json ()
+ (with-noauth (instance octopus-projects-with-roles/view-service)
+ (setf (form instance)
+ (make-form "octopus-projects-with-roles-form"
+ nil
+ t
+ `((:name "roles" :label "Roles (comma or space separated)" :field-type "text" :required "required" :value ,(or (session-value :octopus-roles) ""))
+ (:label "Find Projects With Roles" :field-type "button" :onclick "on_octopus_projects_with_roles_results_clicked()"))))))
+
+(defclass octopus-projects-with-roles/results-service (octopus-projects-with-roles/view-service)
+ ((stdout :initarg :stdout
+ :initform nil
+ :accessor stdout)
+ (location-p :initform nil))
+ (:documentation ""))
+
+(defun octopus-projects-with-roles-results-json (roles)
+ (with-noauth (instance octopus-projects-with-roles/results-service)
+ (cl-fad:with-output-to-temporary-file (f :template "/tmp/snow/temp-%")
+ (get-octopus-projects-with-roles (make-instance 'octopus-process-step
+ :target-roles (remove-if (lambda (x) (org-ckons-core::null-or-empty-p x)) (cl-ppcre:split "[, ]" roles))
+ :stdout (pathname f)))
+ (setf (stdout instance) (uiop:native-namestring (pathname f))))
+ (setf (session-value :octopus-roles) roles)))
diff --git a/lisp/service/rest-service.lisp b/lisp/service/rest-service.lisp
new file mode 100644
index 0000000..a060863
--- /dev/null
+++ b/lisp/service/rest-service.lisp
@@ -0,0 +1,36 @@
+;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
+(declaim (optimize (speed 0) (safety 3) (debug 3)))
+
+(in-package :snow)
+
+(defclass rest-service (base-service)
+ ((location :initarg :location
+ :initform nil
+ :accessor location)
+ (location-p :initarg :location-p
+ :initform t
+ :accessor location-p)
+ (message :initarg :message
+ :initform nil
+ :accessor message)
+ (errormsg :initarg :errormsg
+ :initform nil
+ :accessor errormsg))
+ (:documentation ""))
+
+(defmethod initialize-instance :after ((rest-service rest-service) &key)
+ (when (location-p rest-service)
+ (if (message rest-service)
+ (setf (session-value :message) (message rest-service))
+ (setf (message rest-service) (session-value :message)))
+ (if (errormsg rest-service)
+ (setf (session-value :errormsg) (errormsg rest-service))
+ (setf (errormsg rest-service) (session-value :errormsg)))
+ (when (null (location rest-service))
+ (setf (location rest-service) (type-to-path rest-service)))))
+
+(defun location-json (&optional (location "/home"))
+ (format nil "{\"location\":\"~a\"}" location))
+
+(defun type-to-path (rest-type)
+ (concatenate 'string "/" (ppcre:regex-replace "-service" (string-downcase (type-of rest-type)) "/")))
diff --git a/lisp/service/tfcloud-apply-service.lisp b/lisp/service/tfcloud-apply-service.lisp
new file mode 100644
index 0000000..ac5f6f0
--- /dev/null
+++ b/lisp/service/tfcloud-apply-service.lisp
@@ -0,0 +1,59 @@
+;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
+(declaim (optimize (speed 0) (safety 3) (debug 3)))
+
+(in-package :snow)
+
+(defclass tfcloud-apply-service (rest-service)
+ ()
+ (:documentation ""))
+
+(defun tfcloud-apply-json ()
+ (with-noauth (instance tfcloud-apply-service)
+ t))
+
+(defclass tfcloud-apply/view-service (tfcloud-apply-service)
+ ((form :initarg :form
+ :initform nil
+ :accessor form)
+ (location-p :initform nil))
+ (:documentation ""))
+
+(defun tfcloud-apply-view-json ()
+ (with-noauth (instance tfcloud-apply/view-service)
+ (setf (form instance)
+ (make-form "tfcloud-apply-form"
+ nil
+ t
+ `((:name "filter" :label "Filter" :field-type "text" :required "required" :value ,(or (session-value :tfcloud-apply-filter) ""))
+ (:label "Find Matching Workspaces" :field-type "button" :onclick "on_tfcloud_apply_results_clicked()"))))))
+
+(defclass tfcloud-apply/results-service (tfcloud-apply/view-service)
+ ((results :initarg :results
+ :initform nil
+ :accessor results)
+ (location-p :initform nil))
+ (:documentation ""))
+
+(defun tfcloud-apply-results-json (filter)
+ (with-noauth (instance tfcloud-apply/results-service)
+ (with-tfcloud (tfcloud)
+ (let ((workspaces (get-tfcloud-workspaces tfcloud filter)))
+ (setf (results instance) (mapcar (lambda (workspace)
+ (sanitize-json workspace))
+ workspaces))
+ (setf (session-value :tfcloud-apply-filter) filter)
+ (setf (session-value :tfcloud-apply-workspaces) workspaces)
+ (setf (form instance)
+ (make-form "tfcloud-apply-submit-form"
+ nil
+ t
+ `((:label "Apply Workspaces" :field-type "button" :onclick "on_tfcloud_apply_results_submit_clicked()"))))))))
+
+(defclass tfcloud-apply/results-submit-service (tfcloud-apply-service)
+ ((location-p :initform nil))
+ (:documentation ""))
+
+(defun tfcloud-apply-results-submit-json ()
+ (with-noauth (instance tfcloud-apply/results-submit-service)
+ (loop for workspace in (session-value :tfcloud-apply-workspaces)
+ do (apply-tfcloud-workspace workspace))))