mirror of https://github.com/statping/statping
notifier updates for UX, UI updates, cypress testing
parent
9d396e012d
commit
74d77b6300
|
@ -1,3 +1,9 @@
|
|||
# 0.90.25
|
||||
- Added string response on OnTest for Notifiers
|
||||
- Modified UI to show user the response for a Notifier.
|
||||
- Modified some Notifiers title's
|
||||
- Added more Cypress e2e testing
|
||||
|
||||
# 0.90.24
|
||||
- Fixed login form from not showing
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ context('Groups Tests', () => {
|
|||
cy.visit('/dashboard/services')
|
||||
cy.get('.sortable_groups > tr').should('have.length', 3)
|
||||
|
||||
cy.get('.sortable_groups > tr').eq(0).contains('PRIVATE')
|
||||
cy.get('.sortable_groups > tr').eq(0).contains('PUBLIC')
|
||||
cy.get('.sortable_groups > tr').eq(1).contains('PUBLIC')
|
||||
cy.get('.sortable_groups > tr').eq(2).contains('PRIVATE')
|
||||
})
|
||||
|
@ -48,17 +48,28 @@ context('Groups Tests', () => {
|
|||
cy.get('button[type="submit"]').click()
|
||||
})
|
||||
|
||||
it('should edit Group', () => {
|
||||
cy.visit('/dashboard/services')
|
||||
cy.get('.sortable_groups > tr').eq(0).find('.btn-outline-secondary').click()
|
||||
cy.get('#title').should('have.value', 'Test Group')
|
||||
cy.get('#title').clear().type('Updated Group')
|
||||
cy.get('button[type="submit"]').click()
|
||||
|
||||
cy.get('.sortable_groups > tr').eq(0).contains('Updated Group')
|
||||
})
|
||||
|
||||
it('should confirm new groups', () => {
|
||||
cy.visit('/dashboard/services')
|
||||
cy.get('.sortable_groups > tr').should('have.length', 5)
|
||||
|
||||
cy.get('.sortable_groups > tr').eq(0).contains('PUBLIC')
|
||||
cy.get('.sortable_groups > tr').eq(0).contains('Test Group')
|
||||
cy.get('.sortable_groups > tr').eq(0).contains('Updated Group')
|
||||
cy.get('.sortable_groups > tr').eq(1).contains('PRIVATE')
|
||||
cy.get('.sortable_groups > tr').eq(1).contains('Test Private Group')
|
||||
})
|
||||
|
||||
it('should delete new groups', () => {
|
||||
cy.visit('/dashboard/services')
|
||||
cy.get('.sortable_groups > tr').eq(0).find('.btn-danger').click()
|
||||
cy.get('.sortable_groups > tr').eq(1).find('.btn-danger').click()
|
||||
cy.get('.sortable_groups > tr').should('have.length', 3)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import "../support/commands"
|
||||
|
||||
context('Messages Tests', () => {
|
||||
context('Annoucements Tests', () => {
|
||||
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -43,4 +43,10 @@ context('Messages Tests', () => {
|
|||
cy.get('tbody > tr').should('have.length', 3)
|
||||
})
|
||||
|
||||
it('should confirm delete Message', () => {
|
||||
cy.visit('/dashboard/messages')
|
||||
cy.get('tbody > tr').eq(0).find('.btn-danger').click()
|
||||
cy.get('tbody > tr').should('have.length', 2)
|
||||
})
|
||||
|
||||
})
|
||||
|
|
|
@ -25,12 +25,26 @@ context('Notifier Tests', () => {
|
|||
cy.getCookies().should('have.length', 1)
|
||||
})
|
||||
|
||||
// uzrwstmtd69hi4wgzsj27q2v29mtpu
|
||||
it('should confirm notifiers are installed', () => {
|
||||
cy.visit('/dashboard/settings')
|
||||
cy.get('#notifiers_tabs > a').should('have.length', 9)
|
||||
cy.get('#notifiers_tabs > a').should('have.length', 10)
|
||||
|
||||
cy.get('#api_key').should('not.have.value', '')
|
||||
cy.get('#api_secret').should('not.have.value', '')
|
||||
})
|
||||
|
||||
it('should test and save notifier', () => {
|
||||
cy.visit('/dashboard/settings')
|
||||
cy.get('#notifiers_tabs > a').should('have.length', 10)
|
||||
cy.get('#notifiers_tabs > #v-pills-command-tab').click()
|
||||
|
||||
cy.get('#v-pills-command-tab > .form-control').eq(0).clear().type('/bin/sh')
|
||||
cy.get('#v-pills-command-tab > .form-control').eq(1).clear().type('echo "success"')
|
||||
cy.get('#v-pills-command-tab > .form-control').eq(2).clear().type('echo "failure"')
|
||||
|
||||
cy.get('#v-pills-command-tab > .card-body > .btn').eq(0).click()
|
||||
cy.get('#v-pills-command-tab > .card-body > .btn').eq(1).click()
|
||||
})
|
||||
|
||||
})
|
||||
|
|
|
@ -26,7 +26,7 @@ context('Services Tests', () => {
|
|||
|
||||
it('should goto services', () => {
|
||||
cy.visit('/dashboard/services')
|
||||
cy.get('#services_list > tr').should('have.length', 5)
|
||||
cy.get('#services_list > tr').should('have.length', 6)
|
||||
cy.get('.sortable_groups > tr').should('have.length', 3)
|
||||
})
|
||||
|
||||
|
@ -79,7 +79,7 @@ context('Services Tests', () => {
|
|||
|
||||
it('should create new ICMP service', () => {
|
||||
cy.visit('/dashboard/create_service')
|
||||
cy.get('#name').clear().type('ICMP Service')
|
||||
cy.get('#name').clear().type('ICMP Service')
|
||||
cy.get('#service_type').select('icmp')
|
||||
cy.get('#service_url').clear().type('8.8.8.8')
|
||||
|
||||
|
@ -93,16 +93,16 @@ context('Services Tests', () => {
|
|||
|
||||
it('should confirm new services', () => {
|
||||
cy.visit('/dashboard/services')
|
||||
cy.get('#services_list > tr').should('have.length', 9)
|
||||
cy.get('#services_list > tr').should('have.length', 10)
|
||||
})
|
||||
|
||||
it('should delete new services', () => {
|
||||
cy.visit('/dashboard/services')
|
||||
cy.get('#services_list > tr').eq(0).find('.btn-danger').click()
|
||||
cy.get('#services_list > tr').eq(0).find('.btn-danger').click()
|
||||
cy.get('#services_list > tr').eq(0).find('.btn-danger').click()
|
||||
cy.get('#services_list > tr').eq(0).find('.btn-danger').click()
|
||||
cy.get('#services_list > tr').should('have.length', 4)
|
||||
cy.get('#services_list > tr').eq(0).find('a.btn-danger').click()
|
||||
cy.get('#services_list > tr').eq(1).find('a.btn-danger').click()
|
||||
cy.get('#services_list > tr').eq(2).find('a.btn-danger').click()
|
||||
cy.get('#services_list > tr').eq(3).find('a.btn-danger').click()
|
||||
cy.get('#services_list > tr').should('have.length', 6)
|
||||
})
|
||||
|
||||
})
|
||||
|
|
|
@ -27,7 +27,7 @@ context('Settings Tests', () => {
|
|||
|
||||
it('should confirm notifiers are installed', () => {
|
||||
cy.visit('/dashboard/settings')
|
||||
cy.get('#notifiers_tabs > a').should('have.length', 9)
|
||||
cy.get('#notifiers_tabs > a').should('have.length', 10)
|
||||
|
||||
cy.get('#api_key').should('not.have.value', '')
|
||||
cy.get('#api_secret').should('not.have.value', '')
|
||||
|
@ -36,7 +36,7 @@ context('Settings Tests', () => {
|
|||
it('should update Statping settings', () => {
|
||||
cy.visit('/dashboard/settings')
|
||||
|
||||
cy.get('#project').clear().type('Statping Cypress Tests')
|
||||
cy.get('#project').clear().type('Statping Updated')
|
||||
cy.get('#description').clear().type('Statping can use Cypress e2e testing to make it more stable!')
|
||||
cy.get('#domain').clear().type('http://localhost:8888')
|
||||
cy.get('#footer').clear().type('Statping Custom Footer')
|
||||
|
@ -46,7 +46,7 @@ context('Settings Tests', () => {
|
|||
it('should confirm Statping settings', () => {
|
||||
cy.visit('/dashboard/settings')
|
||||
|
||||
cy.get('#project').should('have.value', 'Statping Cypress Tests')
|
||||
cy.get('#project').should('have.value', 'Statping Updated')
|
||||
cy.get('#description').should('have.value', 'Statping can use Cypress e2e testing to make it more stable!')
|
||||
cy.get('#domain').should('have.value', 'http://localhost:8888')
|
||||
cy.get('#footer').should('have.value', 'Statping Custom Footer')
|
||||
|
@ -59,6 +59,13 @@ context('Settings Tests', () => {
|
|||
cy.get('.footer').should('contain', 'Statping Custom Footer')
|
||||
})
|
||||
|
||||
it('should regenerate API Keys', () => {
|
||||
cy.visit('/dashboard/settings')
|
||||
cy.get('#regenkeys').click()
|
||||
cy.get('#api_key').should('not.have.value', '')
|
||||
cy.get('#api_secret').should('not.have.value', '')
|
||||
})
|
||||
|
||||
it('should create Local Assets', () => {
|
||||
cy.visit('/dashboard/settings')
|
||||
cy.get('#v-pills-style-tab').click()
|
||||
|
|
|
@ -39,13 +39,52 @@ context('Users Tests', () => {
|
|||
cy.get('#password_confirm').clear().type('password123')
|
||||
|
||||
cy.get('button[type="submit"]').click()
|
||||
cy.get('#users_table > tr').should('have.length', 2)
|
||||
})
|
||||
|
||||
// it('should confirm new user', () => {
|
||||
// cy.visit('/dashboard/users')
|
||||
// cy.get('#users_table > tr').should('have.length', 2)
|
||||
// cy.get('#users_table > tr').eq(0).contains('admin')
|
||||
// cy.get('#users_table > tr').eq(1).contains('admin2')
|
||||
// })
|
||||
it('should create new Admin User', () => {
|
||||
cy.visit('/dashboard/users')
|
||||
cy.get('#username').clear().type('admin3')
|
||||
cy.get('#admin_switch').click()
|
||||
cy.get('#email').clear().type('info@admin3.com')
|
||||
cy.get('#password').clear().type('password123')
|
||||
cy.get('#password_confirm').clear().type('password123')
|
||||
|
||||
cy.get('button[type="submit"]').click()
|
||||
cy.get('#users_table > tr').should('have.length', 3)
|
||||
})
|
||||
|
||||
it('should confirm new user', () => {
|
||||
cy.visit('/dashboard/users')
|
||||
cy.get('#users_table > tr').should('have.length', 3)
|
||||
cy.get('#users_table > tr').eq(0).contains('admin')
|
||||
cy.get('#users_table > tr').eq(1).contains('admin2')
|
||||
cy.get('#users_table > tr').eq(2).contains('admin3')
|
||||
|
||||
cy.get('#users_table > tr').eq(0).contains('ADMIN')
|
||||
cy.get('#users_table > tr').eq(1).contains('USER')
|
||||
cy.get('#users_table > tr').eq(2).contains('ADMIN')
|
||||
})
|
||||
|
||||
it('should confirm edit user', () => {
|
||||
cy.visit('/dashboard/users')
|
||||
cy.get('#users_table > tr').should('have.length', 3)
|
||||
cy.get('#users_table > tr').eq(2).find('a.edit-user').click()
|
||||
cy.get('#email').should('have.value', 'info@admin3.com')
|
||||
cy.get('#email').clear().type('info@updated.com')
|
||||
cy.get('#password').type('password123')
|
||||
cy.get('#password_confirm').type('password123')
|
||||
cy.get('button[type="submit"]').click()
|
||||
|
||||
cy.get('#users_table > tr').should('have.length', 3)
|
||||
})
|
||||
|
||||
it('should delete new users', () => {
|
||||
cy.visit('/dashboard/users')
|
||||
cy.get('#users_table > tr').should('have.length', 3)
|
||||
cy.get('#users_table > tr').eq(2).find('a.btn-danger').click()
|
||||
cy.get('#users_table > tr').eq(1).find('a.btn-danger').click()
|
||||
cy.get('#users_table > tr').should('have.length', 1)
|
||||
})
|
||||
|
||||
})
|
||||
|
|
|
@ -14,15 +14,22 @@
|
|||
</thead>
|
||||
<tbody id="users_table">
|
||||
|
||||
<tr v-for="(user, index) in users" v-bind:key="index" >
|
||||
<tr v-for="(user, index) in users" v-bind:key="user.id" >
|
||||
<td>{{user.username}}</td>
|
||||
<td v-if="user.admin"><span class="badge badge-danger">ADMIN</span></td>
|
||||
<td v-if="!user.admin"><span class="badge badge-primary">USER</span></td>
|
||||
<td>
|
||||
<span class="badge" :class="{'badge-danger': user.admin, 'badge-primary': !user.admin}">
|
||||
{{user.admin ? 'ADMIN' : 'USER'}}
|
||||
</span>
|
||||
</td>
|
||||
<td class="d-none d-md-table-cell">{{niceDate(user.updated_at)}}</td>
|
||||
<td class="text-right">
|
||||
<div class="btn-group">
|
||||
<a @click.prevent="editUser(user, edit)" href="" class="btn btn-outline-secondary"><font-awesome-icon icon="user" /> Edit</a>
|
||||
<a @click.prevent="deleteUser(user)" v-if="index !== 0" href="" class="btn btn-danger"><font-awesome-icon icon="times" /></a>
|
||||
<a @click.prevent="editUser(user, edit)" class="btn btn-outline-secondary edit-user">
|
||||
<font-awesome-icon icon="user" /> Edit
|
||||
</a>
|
||||
<a @click.prevent="deleteUser(user)" v-if="index !== 0" class="btn btn-danger delete-user">
|
||||
<font-awesome-icon icon="times" />
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
<template>
|
||||
<button v-html="loading ? loadLabel : label" @click.prevent="runAction" type="submit" :disabled="loading || disabled" class="btn btn-block" :class="{class: !loading, 'btn-outline-light': loading}">
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'LoadButton',
|
||||
props: {
|
||||
action: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
class: {
|
||||
type: String,
|
||||
default: "btn-primary"
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
loadLabel: "<div class=\"spinner-border text-dark\"><span class=\"sr-only\">Loading</span></div>"
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async runAction() {
|
||||
this.loading = true;
|
||||
await this.action();
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
</style>
|
|
@ -35,8 +35,13 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="error" class="alert alert-danger col-12" role="alert">{{error}}</div>
|
||||
<div v-if="success" class="alert alert-success col-12" role="alert">{{notifier.title}} appears to be working!</div>
|
||||
<div v-if="error && !success" class="alert alert-danger col-12" role="alert">
|
||||
{{error}}<p v-if="response">Response:<br>{{response}}</p>
|
||||
</div>
|
||||
<div v-if="success" class="alert alert-success col-12" role="alert">
|
||||
{{notifier.title}} appears to be working!
|
||||
<p v-if="response">Response:<br>{{response}}</p>
|
||||
</div>
|
||||
|
||||
<div class="card text-black-50 bg-white mb-3">
|
||||
<div class="card-body">
|
||||
|
@ -79,6 +84,7 @@ export default {
|
|||
loading: false,
|
||||
loadingTest: false,
|
||||
error: null,
|
||||
response: null,
|
||||
success: false,
|
||||
saved: false,
|
||||
form: {},
|
||||
|
@ -130,6 +136,7 @@ export default {
|
|||
} else {
|
||||
this.error = tested.error
|
||||
}
|
||||
this.response = tested.response
|
||||
this.loadingTest = false
|
||||
},
|
||||
}
|
||||
|
|
|
@ -13,9 +13,9 @@
|
|||
<input v-model="user.username" type="text" class="form-control" id="username" placeholder="Username" required autocorrect="off" autocapitalize="none" v-bind:readonly="user.id">
|
||||
</div>
|
||||
<div class="col-6 col-md-4">
|
||||
<span @click="user.admin = !!user.admin" class="switch">
|
||||
<input v-model="user.admin" type="checkbox" class="switch" id="switch-normal" v-bind:checked="user.admin">
|
||||
<label for="switch-normal">Administrator</label>
|
||||
<span id="admin_switch" @click="user.admin = !!user.admin" class="switch">
|
||||
<input v-model="user.admin" type="checkbox" class="switch" id="user_admin_switch" v-bind:checked="user.admin">
|
||||
<label for="user_admin_switch">Administrator</label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -39,11 +39,12 @@
|
|||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-12">
|
||||
<button type="submit" @click="saveUser"
|
||||
<LoadButton
|
||||
class="btn-primary"
|
||||
:disabled="loading || !user.username || !user.email || !user.password || !user.confirm_password || (user.password !== user.confirm_password)"
|
||||
class="btn btn-block" :class="{'btn-primary': !user.id, 'btn-secondary': user.id}">
|
||||
{{loading ? "Loading..." : user.id ? "Update User" : "Create User"}}
|
||||
</button>
|
||||
:action="saveUser"
|
||||
:label="user.id ? 'Update User' : 'Create User'"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert alert-danger d-none" id="alerter" role="alert"></div>
|
||||
|
@ -54,10 +55,12 @@
|
|||
|
||||
<script>
|
||||
import Api from "../API";
|
||||
import LoadButton from "@/components/Elements/LoadButton";
|
||||
|
||||
export default {
|
||||
name: 'FormUser',
|
||||
props: {
|
||||
components: {LoadButton},
|
||||
props: {
|
||||
in_user: {
|
||||
type: Object
|
||||
},
|
||||
|
@ -89,8 +92,7 @@
|
|||
this.user = {}
|
||||
this.edit(false)
|
||||
},
|
||||
async saveUser(e) {
|
||||
e.preventDefault();
|
||||
async saveUser() {
|
||||
this.loading = true
|
||||
if (this.user.id) {
|
||||
await this.updateUser()
|
||||
|
@ -103,8 +105,7 @@
|
|||
let user = this.user
|
||||
delete user.confirm_password
|
||||
await Api.user_create(user)
|
||||
const users = await Api.users()
|
||||
this.$store.commit('setUsers', users)
|
||||
await this.update()
|
||||
this.user = {}
|
||||
},
|
||||
async updateUser() {
|
||||
|
@ -114,9 +115,12 @@
|
|||
}
|
||||
delete user.confirm_password
|
||||
await Api.user_update(user)
|
||||
await this.update()
|
||||
this.edit(false)
|
||||
},
|
||||
async update() {
|
||||
const users = await Api.users()
|
||||
this.$store.commit('setUsers', users)
|
||||
this.edit(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
<h6 class="mt-4 text-muted">Notifiers</h6>
|
||||
|
||||
<div id="notifiers_tabs">
|
||||
<a v-for="(notifier, index) in $store.getters.notifiers" v-bind:key="`${notifier.method}_${index}`" @click.prevent="changeTab" class="nav-link text-capitalize" v-bind:class="{active: liClass(`v-pills-${notifier.method.toLowerCase()}-tab`)}" v-bind:id="`v-pills-${notifier.method.toLowerCase()}-tab`" data-toggle="pill" v-bind:href="`#v-pills-${notifier.method.toLowerCase()}`" role="tab" v-bind:aria-controls="`v-pills-${notifier.method.toLowerCase()}`" aria-selected="false">
|
||||
<font-awesome-icon :icon="iconName(notifier.icon)" class="mr-2"/> {{notifier.method}}
|
||||
<a v-for="(notifier, index) in notifiers" v-bind:key="`${notifier.method}_${index}`" @click.prevent="changeTab" class="nav-link text-capitalize" v-bind:class="{active: liClass(`v-pills-${notifier.method.toLowerCase()}-tab`)}" v-bind:id="`v-pills-${notifier.method.toLowerCase()}-tab`" data-toggle="pill" v-bind:href="`#v-pills-${notifier.method.toLowerCase()}`" role="tab" v-bind:aria-controls="`v-pills-${notifier.method.toLowerCase()}`" aria-selected="false">
|
||||
<font-awesome-icon :icon="iconName(notifier.icon)" class="mr-2"/> {{notifier.title}}
|
||||
<span v-if="notifier.enabled" class="badge badge-pill float-right mt-1" :class="{'badge-success': !liClass(`v-pills-${notifier.method.toLowerCase()}-tab`), 'badge-light': liClass(`v-pills-${notifier.method.toLowerCase()}-tab`), 'text-dark': liClass(`v-pills-${notifier.method.toLowerCase()}-tab`)}">ON</span>
|
||||
</a>
|
||||
</div>
|
||||
|
@ -90,7 +90,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<small class="form-text text-muted">API Secret is used for read, create, update and delete routes</small>
|
||||
<small class="form-text text-muted">You can <a href="#" @click="renewApiKeys">Regenerate API Keys</a> if you need to.</small>
|
||||
<small class="form-text text-muted">You can <a href="#" id="regenkeys" @click="renewApiKeys">Regenerate API Keys</a> if you need to.</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -129,7 +129,7 @@
|
|||
<OAuth :oauth="core.oauth"/>
|
||||
</div>
|
||||
|
||||
<div v-for="(notifier, index) in notifiers" v-bind:key="`${notifier.title}_${index}`" class="tab-pane fade" v-bind:class="{active: liClass(`v-pills-${notifier.method.toLowerCase()}-tab`), show: liClass(`v-pills-${notifier.method.toLowerCase()}-tab`)}" v-bind:id="`v-pills-${notifier.method.toLowerCase()}-tab`" role="tabpanel" v-bind:aria-labelledby="`v-pills-${notifier.method.toLowerCase()}-tab`">
|
||||
<div v-for="(notifier, index) in notifiers" v-bind:key="`${notifier.method}_${index}`" class="tab-pane fade" v-bind:class="{active: liClass(`v-pills-${notifier.method.toLowerCase()}-tab`), show: liClass(`v-pills-${notifier.method.toLowerCase()}-tab`)}" v-bind:id="`v-pills-${notifier.method.toLowerCase()}-tab`" role="tabpanel" v-bind:aria-labelledby="`v-pills-${notifier.method.toLowerCase()}-tab`">
|
||||
<Notifier :notifier="notifier"/>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -72,16 +72,18 @@ func testNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
notif := services.ReturnNotifier(notifer.Method)
|
||||
err = notif.OnTest()
|
||||
out, err := notif.OnTest()
|
||||
|
||||
resp := ¬ifierTestResp{
|
||||
Success: err == nil,
|
||||
Error: err,
|
||||
Success: err == nil,
|
||||
Response: out,
|
||||
Error: err,
|
||||
}
|
||||
returnJson(resp, w, r)
|
||||
}
|
||||
|
||||
type notifierTestResp struct {
|
||||
Success bool `json:"success"`
|
||||
Error error `json:"error,omitempty"`
|
||||
Success bool `json:"success"`
|
||||
Response string `json:"response,omitempty"`
|
||||
Error error `json:"error,omitempty"`
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ func (c *commandLine) Select() *notifications.Notification {
|
|||
|
||||
var Command = &commandLine{¬ifications.Notification{
|
||||
Method: "command",
|
||||
Title: "Shell Command",
|
||||
Title: "Command",
|
||||
Description: "Shell Command allows you to run a customized shell/bash Command on the local machine it's running on.",
|
||||
Author: "Hunter Long",
|
||||
AuthorUrl: "https://github.com/hunterlong",
|
||||
|
@ -33,37 +33,30 @@ var Command = &commandLine{¬ifications.Notification{
|
|||
Form: []notifications.NotificationForm{{
|
||||
Type: "text",
|
||||
Title: "Shell or Bash",
|
||||
Placeholder: "/bin/bash",
|
||||
Placeholder: "/usr/bin/curl",
|
||||
DbField: "host",
|
||||
SmallText: "You can use '/bin/sh', '/bin/bash' or even an absolute path for an application.",
|
||||
SmallText: "You can use '/bin/sh', '/bin/bash', '/usr/bin/curl' or an absolute path for an application.",
|
||||
}, {
|
||||
Type: "text",
|
||||
Title: "Command to Run on OnSuccess",
|
||||
Placeholder: "curl google.com",
|
||||
Placeholder: "http://localhost:8080/health",
|
||||
DbField: "var1",
|
||||
SmallText: "This Command will run every time a service is receiving a Successful event.",
|
||||
SmallText: "This Command will run when a service is receiving a Successful event.",
|
||||
}, {
|
||||
Type: "text",
|
||||
Title: "Command to Run on OnFailure",
|
||||
Placeholder: "curl offline.com",
|
||||
Placeholder: "http://localhost:8080/health",
|
||||
DbField: "var2",
|
||||
SmallText: "This Command will run every time a service is receiving a Failing event.",
|
||||
SmallText: "This Command will run when a service is receiving a Failing event.",
|
||||
}}},
|
||||
}
|
||||
|
||||
func runCommand(app string, cmd ...string) (string, string, error) {
|
||||
utils.Log.Infof("Command notifier sending: %s %s", app, strings.Join(cmd, " "))
|
||||
outStr, errStr, err := utils.Command(app, cmd...)
|
||||
return outStr, errStr, err
|
||||
}
|
||||
|
||||
// OnFailure for commandLine will trigger failing service
|
||||
func (c *commandLine) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||
msg := c.GetValue("var2")
|
||||
tmpl := ReplaceVars(msg, s, f)
|
||||
_, _, err := runCommand(c.Host, tmpl)
|
||||
return err
|
||||
}
|
||||
|
||||
// OnSuccess for commandLine will trigger successful service
|
||||
func (c *commandLine) OnSuccess(s *services.Service) error {
|
||||
msg := c.GetValue("var1")
|
||||
|
@ -72,11 +65,19 @@ func (c *commandLine) OnSuccess(s *services.Service) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// OnTest for commandLine triggers when this notifier has been saved
|
||||
func (c *commandLine) OnTest() error {
|
||||
cmds := strings.Split(c.Var1, " ")
|
||||
in, out, err := runCommand(c.Host, cmds...)
|
||||
utils.Log.Infoln(in)
|
||||
utils.Log.Infoln(out)
|
||||
// OnFailure for commandLine will trigger failing service
|
||||
func (c *commandLine) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||
msg := c.GetValue("var2")
|
||||
tmpl := ReplaceVars(msg, s, f)
|
||||
_, _, err := runCommand(c.Host, tmpl)
|
||||
return err
|
||||
}
|
||||
|
||||
// OnTest for commandLine triggers when this notifier has been saved
|
||||
func (c *commandLine) OnTest() (string, error) {
|
||||
tmpl := ReplaceVars(c.Var1, exampleService, exampleFailure)
|
||||
in, out, err := runCommand(c.Host, tmpl)
|
||||
utils.Log.Infoln(in)
|
||||
utils.Log.Infoln(out)
|
||||
return out, err
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/statping/statping/types/failures"
|
||||
"github.com/statping/statping/types/notifications"
|
||||
"github.com/statping/statping/types/notifier"
|
||||
|
@ -61,23 +60,22 @@ func (d *discord) OnSuccess(s *services.Service) error {
|
|||
}
|
||||
|
||||
// OnSave triggers when this notifier has been saved
|
||||
func (d *discord) OnTest() error {
|
||||
func (d *discord) OnTest() (string, error) {
|
||||
outError := errors.New("Incorrect discord URL, please confirm URL is correct")
|
||||
message := `{"content": "Testing the discord notifier"}`
|
||||
contents, _, err := utils.HttpRequest(Discorder.Host, "POST", "application/json", nil, bytes.NewBuffer([]byte(message)), time.Duration(10*time.Second), true)
|
||||
if string(contents) == "" {
|
||||
return nil
|
||||
return "", nil
|
||||
}
|
||||
var dtt discordTestJson
|
||||
err = json.Unmarshal(contents, &dtt)
|
||||
if err != nil {
|
||||
return outError
|
||||
return string(contents), outError
|
||||
}
|
||||
if dtt.Code == 0 {
|
||||
return outError
|
||||
return string(contents), outError
|
||||
}
|
||||
fmt.Println("discord: ", string(contents))
|
||||
return nil
|
||||
return string(contents), nil
|
||||
}
|
||||
|
||||
type discordTestJson struct {
|
||||
|
|
|
@ -186,7 +186,7 @@ func (e *emailer) OnSuccess(s *services.Service) error {
|
|||
}
|
||||
|
||||
// OnTest triggers when this notifier has been saved
|
||||
func (e *emailer) OnTest() error {
|
||||
func (e *emailer) OnTest() (string, error) {
|
||||
testService := &services.Service{
|
||||
Id: 1,
|
||||
Name: "Example Service",
|
||||
|
@ -201,14 +201,16 @@ func (e *emailer) OnTest() error {
|
|||
LastResponse: "<html>this is an example response</html>",
|
||||
CreatedAt: utils.Now().Add(-24 * time.Hour),
|
||||
}
|
||||
subject := fmt.Sprintf("Service %v is Back Online", testService.Name)
|
||||
email := &emailOutgoing{
|
||||
To: e.Var2,
|
||||
Subject: fmt.Sprintf("Service %v is Back Online", testService.Name),
|
||||
Subject: subject,
|
||||
Template: mainEmailTemplate,
|
||||
Data: testService,
|
||||
From: e.Var1,
|
||||
}
|
||||
return e.dialSend(email)
|
||||
err := e.dialSend(email)
|
||||
return subject, err
|
||||
}
|
||||
|
||||
func (e *emailer) dialSend(email *emailOutgoing) error {
|
||||
|
|
|
@ -43,28 +43,31 @@ var LineNotify = &lineNotifier{¬ifications.Notification{
|
|||
}
|
||||
|
||||
// Send will send a HTTP Post with the Authorization to the notify-api.line.me server. It accepts type: string
|
||||
func (l *lineNotifier) sendMessage(message string) error {
|
||||
func (l *lineNotifier) sendMessage(message string) (string, error) {
|
||||
v := url.Values{}
|
||||
v.Set("message", message)
|
||||
headers := []string{fmt.Sprintf("Authorization=Bearer %v", l.ApiSecret)}
|
||||
_, _, err := utils.HttpRequest("https://notify-api.line.me/api/notify", "POST", "application/x-www-form-urlencoded", headers, strings.NewReader(v.Encode()), time.Duration(10*time.Second), true)
|
||||
return err
|
||||
content, _, err := utils.HttpRequest("https://notify-api.line.me/api/notify", "POST", "application/x-www-form-urlencoded", headers, strings.NewReader(v.Encode()), time.Duration(10*time.Second), true)
|
||||
return string(content), err
|
||||
}
|
||||
|
||||
// OnFailure will trigger failing service
|
||||
func (l *lineNotifier) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||
msg := fmt.Sprintf("Your service '%v' is currently offline!", s.Name)
|
||||
return l.sendMessage(msg)
|
||||
_, err := l.sendMessage(msg)
|
||||
return err
|
||||
}
|
||||
|
||||
// OnSuccess will trigger successful service
|
||||
func (l *lineNotifier) OnSuccess(s *services.Service) error {
|
||||
msg := fmt.Sprintf("Service %s is online!", s.Name)
|
||||
return l.sendMessage(msg)
|
||||
_, err := l.sendMessage(msg)
|
||||
return err
|
||||
}
|
||||
|
||||
// OnTest triggers when this notifier has been saved
|
||||
func (l *lineNotifier) OnTest() error {
|
||||
func (l *lineNotifier) OnTest() (string, error) {
|
||||
msg := fmt.Sprintf("Testing if Line Notifier is working!")
|
||||
return l.sendMessage(msg)
|
||||
_, err := l.sendMessage(msg)
|
||||
return msg, err
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ func (m *mobilePush) Select() *notifications.Notification {
|
|||
|
||||
var Mobile = &mobilePush{¬ifications.Notification{
|
||||
Method: "mobile",
|
||||
Title: "Mobile Notifications",
|
||||
Title: "Mobile",
|
||||
Description: `Receive push notifications on your Mobile device using the Statping App. You can scan the Authentication QR Code found in Settings to get the Mobile app setup in seconds.
|
||||
<p align="center"><a href="https://play.google.com/store/apps/details?id=com.statping"><img src="https://img.cjx.io/google-play.svg"></a><a href="https://itunes.apple.com/us/app/apple-store/id1445513219"><img src="https://img.cjx.io/app-store-badge.svg"></a></p>`,
|
||||
Author: "Hunter Long",
|
||||
|
@ -97,7 +97,7 @@ func (m *mobilePush) OnSuccess(s *services.Service) error {
|
|||
}
|
||||
|
||||
// OnTest triggers when this notifier has been saved
|
||||
func (m *mobilePush) OnTest() error {
|
||||
func (m *mobilePush) OnTest() (string, error) {
|
||||
msg := &pushArray{
|
||||
Message: "Testing the Mobile Notifier",
|
||||
Title: "Testing Notifications",
|
||||
|
@ -107,18 +107,18 @@ func (m *mobilePush) OnTest() error {
|
|||
}
|
||||
body, err := pushRequest(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
var output mobileResponse
|
||||
err = json.Unmarshal(body, &output)
|
||||
if err != nil {
|
||||
return err
|
||||
return string(body), err
|
||||
}
|
||||
if len(output.Logs) == 0 {
|
||||
return nil
|
||||
return string(body), err
|
||||
} else {
|
||||
firstLog := output.Logs[0].Error
|
||||
return fmt.Errorf("Mobile Notification error: %v", firstLog)
|
||||
return string(body), fmt.Errorf("Mobile Notification error: %v", firstLog)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
package notifiers
|
||||
|
||||
import (
|
||||
"github.com/statping/statping/types/services"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAllNotifiers(t *testing.T) {
|
||||
|
||||
notifiers := []notifierTest{
|
||||
{
|
||||
Notifier: Command,
|
||||
RequiredENV: nil,
|
||||
},
|
||||
{
|
||||
Notifier: Discorder,
|
||||
RequiredENV: []string{"DISCORD_URL"},
|
||||
},
|
||||
{
|
||||
Notifier: email,
|
||||
RequiredENV: []string{"EMAIL_HOST", "EMAIL_USER", "EMAIL_PASS", "EMAIL_OUTGOING", "EMAIL_SEND_TO", "EMAIL_PORT"},
|
||||
},
|
||||
{
|
||||
Notifier: Mobile,
|
||||
RequiredENV: []string{"MOBILE_ID", "MOBILE_NUMBER"},
|
||||
},
|
||||
{
|
||||
Notifier: Pushover,
|
||||
RequiredENV: []string{"PUSHOVER_TOKEN", "PUSHOVER_API"},
|
||||
},
|
||||
{
|
||||
Notifier: slacker,
|
||||
RequiredENV: []string{"SLACK_URL"},
|
||||
},
|
||||
{
|
||||
Notifier: Telegram,
|
||||
RequiredENV: []string{"TELEGRAM_TOKEN", "TELEGRAM_CHANNEL"},
|
||||
},
|
||||
{
|
||||
Notifier: Twilio,
|
||||
RequiredENV: []string{"TWILIO_SID", "TWILIO_SECRET", "TWILIO_FROM", "TWILIO_TO"},
|
||||
},
|
||||
{
|
||||
Notifier: Webhook,
|
||||
RequiredENV: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, n := range notifiers {
|
||||
|
||||
if !getEnvs(n.RequiredENV) {
|
||||
t.Skip()
|
||||
continue
|
||||
}
|
||||
|
||||
Add(n.Notifier)
|
||||
|
||||
err := n.Notifier.OnSuccess(exampleService)
|
||||
assert.Nil(t, err)
|
||||
|
||||
err = n.Notifier.OnFailure(exampleService, exampleFailure)
|
||||
assert.Nil(t, err)
|
||||
|
||||
err = n.Notifier.OnTest()
|
||||
assert.Nil(t, err)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func getEnvs(env []string) bool {
|
||||
for _, v := range env {
|
||||
if os.Getenv(v) == "" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type notifierTest struct {
|
||||
Notifier services.ServiceNotifier
|
||||
RequiredENV []string
|
||||
}
|
|
@ -52,34 +52,37 @@ var Pushover = &pushover{¬ifications.Notification{
|
|||
}
|
||||
|
||||
// Send will send a HTTP Post to the Pushover API. It accepts type: string
|
||||
func (t *pushover) sendMessage(message string) error {
|
||||
func (t *pushover) sendMessage(message string) (string, error) {
|
||||
v := url.Values{}
|
||||
v.Set("token", t.ApiSecret)
|
||||
v.Set("user", t.ApiKey)
|
||||
v.Set("message", message)
|
||||
rb := strings.NewReader(v.Encode())
|
||||
|
||||
_, _, err := utils.HttpRequest(pushoverUrl, "POST", "application/x-www-form-urlencoded", nil, rb, time.Duration(10*time.Second), true)
|
||||
content, _, err := utils.HttpRequest(pushoverUrl, "POST", "application/x-www-form-urlencoded", nil, rb, time.Duration(10*time.Second), true)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
return err
|
||||
return string(content), err
|
||||
}
|
||||
|
||||
// OnFailure will trigger failing service
|
||||
func (t *pushover) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||
msg := fmt.Sprintf("Your service '%v' is currently offline!", s.Name)
|
||||
return t.sendMessage(msg)
|
||||
_, err := t.sendMessage(msg)
|
||||
return err
|
||||
}
|
||||
|
||||
// OnSuccess will trigger successful service
|
||||
func (t *pushover) OnSuccess(s *services.Service) error {
|
||||
msg := fmt.Sprintf("Your service '%v' is currently online!", s.Name)
|
||||
return t.sendMessage(msg)
|
||||
_, err := t.sendMessage(msg)
|
||||
return err
|
||||
}
|
||||
|
||||
// OnTest will test the Pushover SMS messaging
|
||||
func (t *pushover) OnTest() error {
|
||||
func (t *pushover) OnTest() (string, error) {
|
||||
msg := fmt.Sprintf("Testing the Pushover Notifier")
|
||||
return t.sendMessage(msg)
|
||||
content, err := t.sendMessage(msg)
|
||||
return content, err
|
||||
}
|
||||
|
|
|
@ -58,16 +58,16 @@ func (s *slack) sendSlack(msg string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *slack) OnTest() error {
|
||||
func (s *slack) OnTest() (string, error) {
|
||||
contents, resp, err := utils.HttpRequest(s.Host, "POST", "application/json", nil, bytes.NewBuffer([]byte(`{"text":"testing message"}`)), time.Duration(10*time.Second), true)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if string(contents) != "ok" {
|
||||
return errors.New("the slack response was incorrect, check the URL")
|
||||
return string(contents), errors.New("the slack response was incorrect, check the URL")
|
||||
}
|
||||
return nil
|
||||
return string(contents), nil
|
||||
}
|
||||
|
||||
// OnFailure will trigger failing service
|
||||
|
|
|
@ -51,7 +51,7 @@ var Telegram = &telegram{¬ifications.Notification{
|
|||
}
|
||||
|
||||
// Send will send a HTTP Post to the Telegram API. It accepts type: string
|
||||
func (t *telegram) sendMessage(message string) error {
|
||||
func (t *telegram) sendMessage(message string) (string, error) {
|
||||
apiEndpoint := fmt.Sprintf("https://api.telegram.org/bot%v/sendMessage", t.ApiSecret)
|
||||
|
||||
v := url.Values{}
|
||||
|
@ -65,27 +65,30 @@ func (t *telegram) sendMessage(message string) error {
|
|||
if !success {
|
||||
errorOut := telegramError(contents)
|
||||
out := fmt.Sprintf("Error code %v - %v", errorOut.ErrorCode, errorOut.Description)
|
||||
return errors.New(out)
|
||||
return string(contents), errors.New(out)
|
||||
}
|
||||
return err
|
||||
return string(contents), err
|
||||
}
|
||||
|
||||
// OnFailure will trigger failing service
|
||||
func (t *telegram) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||
msg := fmt.Sprintf("Your service '%v' is currently offline!", s.Name)
|
||||
return t.sendMessage(msg)
|
||||
_, err := t.sendMessage(msg)
|
||||
return err
|
||||
}
|
||||
|
||||
// OnSuccess will trigger successful service
|
||||
func (t *telegram) OnSuccess(s *services.Service) error {
|
||||
msg := fmt.Sprintf("Your service '%v' is currently online!", s.Name)
|
||||
return t.sendMessage(msg)
|
||||
_, err := t.sendMessage(msg)
|
||||
return err
|
||||
}
|
||||
|
||||
// OnTest will test the Twilio SMS messaging
|
||||
func (t *telegram) OnTest() error {
|
||||
func (t *telegram) OnTest() (string, error) {
|
||||
msg := fmt.Sprintf("Testing the Twilio SMS Notifier on your Statping server")
|
||||
return t.sendMessage(msg)
|
||||
content, err := t.sendMessage(msg)
|
||||
return content, err
|
||||
}
|
||||
|
||||
func telegramSuccess(res []byte) (bool, telegramResponse) {
|
||||
|
|
|
@ -61,7 +61,7 @@ var Twilio = &twilio{¬ifications.Notification{
|
|||
}
|
||||
|
||||
// Send will send a HTTP Post to the Twilio SMS API. It accepts type: string
|
||||
func (t *twilio) sendMessage(message string) error {
|
||||
func (t *twilio) sendMessage(message string) (string, error) {
|
||||
twilioUrl := fmt.Sprintf("https://api.twilio.com/2010-04-01/Accounts/%v/Messages.json", t.GetValue("api_key"))
|
||||
|
||||
v := url.Values{}
|
||||
|
@ -75,25 +75,27 @@ func (t *twilio) sendMessage(message string) error {
|
|||
if !success {
|
||||
errorOut := twilioError(contents)
|
||||
out := fmt.Sprintf("Error code %v - %v", errorOut.Code, errorOut.Message)
|
||||
return errors.New(out)
|
||||
return string(contents), errors.New(out)
|
||||
}
|
||||
return err
|
||||
return string(contents), err
|
||||
}
|
||||
|
||||
// OnFailure will trigger failing service
|
||||
func (t *twilio) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||
msg := fmt.Sprintf("Your service '%v' is currently offline!", s.Name)
|
||||
return t.sendMessage(msg)
|
||||
_, err := t.sendMessage(msg)
|
||||
return err
|
||||
}
|
||||
|
||||
// OnSuccess will trigger successful service
|
||||
func (t *twilio) OnSuccess(s *services.Service) error {
|
||||
msg := fmt.Sprintf("Your service '%v' is currently online!", s.Name)
|
||||
return t.sendMessage(msg)
|
||||
_, err := t.sendMessage(msg)
|
||||
return err
|
||||
}
|
||||
|
||||
// OnTest will test the Twilio SMS messaging
|
||||
func (t *twilio) OnTest() error {
|
||||
func (t *twilio) OnTest() (string, error) {
|
||||
msg := fmt.Sprintf("Testing the Twilio SMS Notifier")
|
||||
return t.sendMessage(msg)
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ type webhooker struct {
|
|||
|
||||
var Webhook = &webhooker{¬ifications.Notification{
|
||||
Method: webhookMethod,
|
||||
Title: "HTTP webhooker",
|
||||
Title: "Webhook",
|
||||
Description: "Send a custom HTTP request to a specific URL with your own body, headers, and parameters.",
|
||||
Author: "Hunter Long",
|
||||
AuthorUrl: "https://github.com/hunterlong",
|
||||
|
@ -113,16 +113,17 @@ func (w *webhooker) sendHttpWebhook(body string) (*http.Response, error) {
|
|||
return resp, err
|
||||
}
|
||||
|
||||
func (w *webhooker) OnTest() error {
|
||||
func (w *webhooker) OnTest() (string, error) {
|
||||
body := ReplaceVars(w.Var2, exampleService, exampleFailure)
|
||||
resp, err := w.sendHttpWebhook(body)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
content, err := ioutil.ReadAll(resp.Body)
|
||||
utils.Log.Infoln(fmt.Sprintf("Webhook notifier received: '%v'", string(content)))
|
||||
return err
|
||||
out := fmt.Sprintf("Webhook notifier received: '%v'", string(content))
|
||||
utils.Log.Infoln(out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// OnFailure will trigger failing service
|
||||
|
|
|
@ -9,5 +9,5 @@ import (
|
|||
type Notifier interface {
|
||||
OnSuccess(*services.Service) error // OnSuccess is triggered when a service is successful
|
||||
OnFailure(*services.Service, *failures.Failure) error // OnFailure is triggered when a service is failing
|
||||
OnTest() error // OnTest is triggered for testing
|
||||
OnTest() (string, error) // OnTest is triggered for testing
|
||||
}
|
||||
|
|
|
@ -28,6 +28,6 @@ func FindNotifier(method string) *notifications.Notification {
|
|||
type ServiceNotifier interface {
|
||||
OnSuccess(*Service) error // OnSuccess is triggered when a service is successful
|
||||
OnFailure(*Service, *failures.Failure) error // OnFailure is triggered when a service is failing
|
||||
OnTest() error // OnTest is triggered for testing
|
||||
OnTest() (string, error) // OnTest is triggered for testing
|
||||
Select() *notifications.Notification // OnTest is triggered for testing
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.90.24
|
||||
0.90.25
|
||||
|
|
Loading…
Reference in New Issue