Skip to content

Commit

Permalink
Mirror Repo
Browse files Browse the repository at this point in the history
  • Loading branch information
github-actions[bot] committed Apr 18, 2024
0 parents commit 9e1663e
Show file tree
Hide file tree
Showing 54 changed files with 24,948 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# EditorConfig is multi-language & growing, see: https://EditorConfig.org

[*.{kt,kts}]
indent_size=2
18 changes: 18 additions & 0 deletions .github/workflows/gradle-update.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Update Gradle Wrapper

on:
workflow_dispatch:
schedule:
- cron: "15 21 * * *"

jobs:
update-gradle-wrapper:
runs-on: self-hosted

steps:
- uses: actions/checkout@v3

- name: Update Gradle Wrapper
uses: gradle-update/update-gradle-wrapper-action@v1
with:
reviewers: sukolenvo
109 changes: 109 additions & 0 deletions .github/workflows/pr-tasks.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
on:
issue_comment:
pull_request_review_comment:
pull_request_review:
pull_request:
types:
- opened
- edited
push:

jobs:
pr_commented:
name: PR comment
runs-on: self-hosted
steps:
- uses: actions/github-script@v6
with:
script: |
async function processPullRequest(github, owner, repo, pull_number) {
const pr = await github.rest.pulls.get({
owner,
repo,
pull_number
})
if (pr.status !== 200) {
throw "Failed to load pr: " + pr
}
var comments = []
comments.push(pr.data.body)
const issueComments = await github.paginate(github.rest.issues.listComments.endpoint.merge({
owner,
repo,
issue_number: pull_number
}))
comments.push(...issueComments.map(comment => comment.body))
const prComments = await github.paginate(github.rest.pulls.listReviewComments.endpoint.merge({
owner,
repo,
pull_number
}))
comments.push(...prComments.map(comment => comment.body))
const prReviews = await github.paginate(github.rest.pulls.listReviews.endpoint.merge({
owner,
repo,
pull_number
}))
comments.push(...prReviews.map(comment => comment.body))
var openTasks = []
var closedTasks = []
comments
.filter(comment => comment !== null)
.filter(comment => comment !== undefined)
.flatMap(comment => comment.split("\n"))
.map(line => line.trim())
.filter(line => line.length > 0)
.forEach(line => {
if (line.startsWith("- [ ]")) {
openTasks.push(line.substring(5).trim())
} else if (line.startsWith("- [x]")) {
closedTasks.push(line.substring(5).trim())
}
})
var state = {
state: "success",
description: "No tasks in this PR"
}
if (openTasks.length === 1) {
state.state = "failure"
state.description = "Task is not completed: '" + openTasks[0] + "'"
} else if (openTasks.length > 1) {
state.state = "failure"
state.description = openTasks.length + " open task(s)"
} else if (openTasks.length === 0) {
if (closedTasks.length > 0) {
state.description = closedTasks.length + " tasks closed"
}
}
state.description = state.description.substring(0, 139)
await github.rest.repos.createCommitStatus({
...state,
owner,
repo,
sha: pr.data.head.sha,
context: "pr-tasks-workflow"
})
}
if (context.eventName === "issue_comment" && context.payload.issue.pull_request === undefined) {
console.log("Issue comment is ignored")
return
}
if (context.eventName === "push") {
const result = await github.rest.repos.listPullRequestsAssociatedWithCommit({
owner: context.payload.repository.owner.name,
repo: context.payload.repository.name,
commit_sha: context.payload.head_commit.id
})
if (result.status !== 200) {
throw "Failed to PRs for commit: " + result
}
result.data.forEach(pr => {
processPullRequest(github, context.issue.owner, context.issue.repo, pr.number)
})
} else {
processPullRequest(github, context.issue.owner, context.issue.repo, context.issue.number)
}
25 changes: 25 additions & 0 deletions .github/workflows/stale-issues.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Close inactive issues
on:
workflow_dispatch:
schedule:
- cron: "30 1 * * *"

jobs:
close-issues:
runs-on: self-hosted
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v5
with:
days-before-issue-stale: 30
days-before-issue-close: 14
stale-issue-label: "stale"
stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
stale-pr-message: "This PR is stale because it has been open for 5 days with no activity."
close-pr-message: "This PR was closed because it has been inactive for 14 days since being marked as stale."
days-before-pr-stale: 5
days-before-pr-close: 14
repo-token: ${{ secrets.GITHUB_TOKEN }}
20 changes: 20 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.idea
.ideaDataSources
dataSources
build/
src/gen/
.gradle/
gradle.properties
**/.classpath
**/.project
**/.settings
**/target
**/*.iml
**/*.imlt
release.properties
out/
**/generated/
**/*.ipr
**/*.iws
**/.DS_Store
.gcache
2 changes: 2 additions & 0 deletions .sdkmanrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Set JVM via `sdk use` in rootDir
java=17.0.8-amzn
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
build-fast:
./gradlew build -x test -x check

clean:
./gradlew clean

test:
./gradlew check test
95 changes: 95 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# WiseTime SQL Time Post Connector

## About

The WiseTime SQL Post Time Connector connects [WiseTime](https://wisetime.com) to SQL databases, and will automatically:

* Record a new time registration in the Database, based on the provided Queries whenever a user posts time to WiseTime

In order to use the WiseTime SQL Post Time Connector, you will need a [WiseTime Connect](https://wisetime.io/docs/connect/) API key.

## Configuration

Configuration is done through environment variables. The following configuration options are required.

| Environment Variable | Description |
|----------------------|-------------------------------------------------------------------------------------------------------------------------|
| API_KEY | Your WiseTime Connect API Key |
| JDBC_URL | The JDBC URL for your database |
| DB_USER | Username to use to connect to the database |
| DB_PASSWORD | Password to use to connect to the database |
| NARRATIVE_PATH | Path to the narrative formatter template. This is used to provide information about posted time to be used on invoices. |
| TIME_POST_SQL_PATH | Path to yaml file containing all required time posting queries |
| TAG_UPSERT_PATH | The WiseTime tag upload folder, must be set to the same value as in your tag sync connector |


The following configuration options are optional.

| Environment Variable | Description |
|-----------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| CALLER_KEY | The caller key that WiseTime should provide with post time webhook calls. The connector does not authenticate Webhook calls if not set. |
| DATA_DIR | If set, the connector will use the directory as the location for storing data to keep track of the already posted time. By default, WiseTime Connector will create a temporary dir under `/tmp` as its data storage. |
| RECEIVE_POSTED_TIME | If unset, this defaults to `LONG_POLL`: use long polling to fetch posted time. Optional parameters are `WEBHOOK` to start up a server to listen for posted time. `DISABLED` no handling for posted time |
| WEBHOOK_PORT | The connector will listen to this port e.g. 8090, if RECEIVE_POSTED_TIME is set to `WEBHOOK`. Defaults to 8080. |
| LOG_LEVEL | Define log level. Available values are: `TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR` and `OFF`. Default is `INFO`. |
| TIMEZONE | The timezone to use when posting time, e.g. `Australia/Perth`. Defaults to `UTC`. |
| NARRATIVE_INTERNAL_PATH | Path to the internal narrative formatter template. This is optional and can be used to provide additional information about posted time for internal use |
| ACTIVITY_TYPE_MANDATORY | Are activity types mandatory for posting time? Defaults to `true` |

### Post Time Queries

The queries for posting time are provided by a yaml file located in `TIME_POST_SQL_PATH`.

The yaml file must contain the following exactly the following 5 queries. See `examples/time_post_sql.yaml` for a complete example.

#### canQueryDb
A query that executes successfully when the DB connection is healthy. Should not modify the database.

#### createWorklog
Should insert the posted time into the database. Should accept the following parameters and doesn't have a return value.

| Parameter | Description |
|---------------------|-----------------------------------------------------------------------------------------------------------------------|
| :matterId | Id of the matter to post to. |
| :userId | Id of the user to attribute the time to. |
| :activityCode | Activity code for the posted time. |
| :narrative | Main narrative that can be used on invoices. |
| :narrativeInternal | Optional, only set when `NARRATIVE_INTERNAL_PATH` is set. Additional information on the posted time for internal use. |
| :startTime | Start time of the posted time. Uses the timezone set via `TIMEZONE`. |
| :durationSecs | Total time worked in seconds. |
| :chargeableTimeSecs | Billable time in seconds. |

#### doesActivityCodeExist
Should verify that a provided activity code exists in the database. Should a single row with arbitrary content if the code exists, or an empty set otherwise.

| Parameter | Description |
|---------------------|--------------------------------|
| :activityCode | The activity code to verify. |

#### findMatterIdByTagName
Should return the matter id of the provided tag name. Should return a single row with a single value, or an empty set otherwise.

| Parameter | Description |
|---------------------|--------------------------------|
| :tagName | The name of the tag to find. |

#### findUserId
Should return the user id of the provided login id or email. Should return a single row with a single value, or an empty set otherwise.

| Parameter | Description |
|---------------------|--------------------------------------------------------------|
| :emailOrExternalId | The email or external id of the user, as set up in Wisetime. |

## Running the WiseTime SQL Post Time Connector

The easiest way to run the SQL Post Time Connector is using Docker. Please see the example folder for an example docker-compose.yaml and other necessary configuration files.

If you are using `RECEIVE_POSTED_TIME=WEBHOOK`: Note that you need to define port forwarding in the docker run command (and similarly any docker-compose.yaml definition). If you set the webhook port other than default (8080) you must also add the WEBHOOK_PORT environment variable to match the docker ports definition.

## Building

To build a Docker image of the WiseTime SQL Post Time Connector, run:

```text
make docker
```
Loading

0 comments on commit 9e1663e

Please sign in to comment.