Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
bb92b7b
chore: bump to 0.0.8-SNAPSHOT
Jonathan-Zollinger Jan 29, 2026
dfb73a0
feat: allow project to org assignment
Jonathan-Zollinger Jan 29, 2026
2efb318
fix: don't print logs of any level to console
Jonathan-Zollinger Jan 29, 2026
0e53f89
Feat make create user helper private (#64)
HMS-Victory Feb 4, 2026
c1d89d9
Tests querying orgs by slug (#66)
HMS-Victory Feb 5, 2026
2034918
build: remove test tasks
Jonathan-Zollinger Feb 27, 2026
c801042
refactor!: add core submodule
Jonathan-Zollinger Feb 27, 2026
8319865
fix: create user with multipart request
Jonathan-Zollinger Mar 2, 2026
69d5091
fix: add retry for intermittent failures
Jonathan-Zollinger Mar 3, 2026
47a1565
refactor: add cli submodule
Jonathan-Zollinger Mar 4, 2026
f5dfdcd
refactor(test): reuse user and orgs between tests
Jonathan-Zollinger Mar 4, 2026
98548ee
fix(build): define main class in cli
Jonathan-Zollinger Mar 4, 2026
02408ec
ci: set workflow to test core and cli modules
Jonathan-Zollinger Mar 4, 2026
bb967d6
feat: add getAllUserInformation()
Jonathan-Zollinger Mar 4, 2026
2c1b4bd
fix: accommodate year-zero date-time response
Jonathan-Zollinger Mar 4, 2026
9619d20
refactor: rename root project to devkit
Jonathan-Zollinger Mar 6, 2026
7149252
add graph api calls (#73)
Jonathan-Zollinger Mar 9, 2026
e8f8081
refactor: simplify annotations
Jonathan-Zollinger Mar 9, 2026
cd6bae8
refactor(test): make test auth client more clear
Jonathan-Zollinger Mar 9, 2026
b8357f5
refactor: set strong types for dates and emails
Jonathan-Zollinger Mar 9, 2026
80007ac
refactor CreateEvent method with dynamic query builder (#88)
Jonathan-Zollinger Mar 17, 2026
fda9156
test: test searchOrganization() query parser
HMS-Victory Mar 19, 2026
70a50b5
fix(test): expect 201 or 200 when creating users
Jonathan-Zollinger Mar 23, 2026
041a25a
fix(test): set auth as "" in unauthenticated context
Jonathan-Zollinger Mar 23, 2026
a30cac6
introduce mock emails in tests
Jonathan-Zollinger Mar 24, 2026
79179d7
refactor: throw error if graph response has errors
Jonathan-Zollinger Apr 24, 2026
d7575af
build: set versions in gradle.properties
Jonathan-Zollinger Apr 24, 2026
440634b
refactor: use lombok to set logger
Jonathan-Zollinger Apr 24, 2026
af8843d
refactor(test): create new projects between tests
Jonathan-Zollinger Apr 24, 2026
1eabb71
refactor(test): Tests only need JUSTSERVE_TOKEN
Jonathan-Zollinger Apr 25, 2026
66b23ae
docs: revamp README
Jonathan-Zollinger Apr 25, 2026
46edeb0
docs: update docs with current testing info
Jonathan-Zollinger Apr 25, 2026
0f83ccb
ci: update token env var name
Jonathan-Zollinger Apr 25, 2026
33a9bea
refactor: add a retry client filter for any 500 errors
Jonathan-Zollinger Apr 25, 2026
12c3f42
refactor: ignore failures to cleanup from tests
Jonathan-Zollinger Apr 25, 2026
b8e34f5
zollinger/migrate to reactive client (#93)
Jonathan-Zollinger Apr 27, 2026
690d1fc
Add naming convention (#94)
HMS-Victory May 13, 2026
b4dba21
feat: add observer submodule
Jonathan-Zollinger May 12, 2026
d5cfee4
fix(test): bad UUID now returns 500 error
Jonathan-Zollinger May 13, 2026
03cec32
chore: bump version to 0.1.0
Jonathan-Zollinger May 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 9 additions & 16 deletions .github/workflows/TestAndCompile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,20 @@ name: GraalVM Native Image Test run and build tests
on: [pull_request, workflow_dispatch]
jobs:
build:
name: test and compile on ${{ matrix.os }}
name: test and compile on self-hosted
runs-on: self-hosted
# runs-on: ${{ matrix.os }}
# strategy:
# matrix:
# os: [macos-latest, windows-latest, ubuntu-latest]
env:
TEST_TOKEN: ${{ secrets.MICRONAUT_HTTP_SERVICES_JUSTSERVE_TOKEN }}
JUSTSERVE_TOKEN: ${{ secrets.MICRONAUT_HTTP_SERVICES_JUSTSERVE_TOKEN }}
JAVA_HOME: ${{ secrets.JAVA_HOME }}

steps:
- uses: actions/checkout@v4
- uses: graalvm/setup-graalvm@v1
with:
java-version: '21'
distribution: 'graalvm'
github-token: ${{ secrets.GITHUB_TOKEN }}
native-image-job-reports: 'true'
- name: call gradle to run tests and then compile
run: ./gradlew test nativeCompile
- name: Run core tests
run: ./gradlew :core:test
- name: Run cli tests and compile
run: ./gradlew :cli:test :cli:nativeCompile
- name: upload binary
uses: actions/upload-artifact@v4
with:
# name: justserve-${{ matrix.os }}
name: justserve
path: build/native/nativeCompile/justserve*
path: cli/build/native/nativeCompile/justserve*
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"java.configuration.updateBuildConfiguration": "automatic"
}
13 changes: 7 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@ Prerequisites:
clone the codebase, then cd into the repo and install the dependencies with gradle

```sh
git clone git@github.com:JustServe-Resources/cli.git
git clone git@github.com:JustServe-Resources/devkit.git

./gradlew assemble
```

## Branch off of main

It's a good practice not to put your changes in the main branch. Branch naming conventions aren't enforced, naming my branches with a `tag`/`task` convention are suggested, similar to [Conventional Commits] naming strategy.
Don't make any changes to the main branch - these will be denied even if you try to. Branch naming conventions aren't enforced, though naming branches with a `my-last-name`/`task-name` convention isn't a bad idea.

See our [style guide](https://github.com/Graqr#general-styling-guide) for supported coding practices. This project enforces [Conventional Commits], which is checked with each commit.
Most importantly, see our [style guide](https://github.com/Graqr#general-styling-guide) for our coding standards. This project enforces [Conventional Commits], which is checked with each commit.
## Test your change

Adequate acceptance testing is to be included with pull requests for new code. See our [style guide] for our testing standards. A portion of the codebase is generated during the build process. Using gradle's `build` task will both assemble and run tests.
Expand All @@ -35,12 +35,13 @@ Adequate acceptance testing is to be included with pull requests for new code. S
```

## Validate this builds properly
This project compiles to a native executable specific to your OS. This is different from a normal java build process. Compiling this repo into an executable is not a short process. See [graal's docs] for options like quick build mode
This Micronaut application supports AOT (Ahead-of-Time) compilation to a GraalVM Native Image. This process produces a platform-specific binary with instant startup and lower memory overhead, though the compilation itself is resource-intensive. See [graal's docs] for optimization flags like quick build mode.
Running the test suite prior to the native build allows GraalVM to leverage profile-guided data for a more performant executable.
```sh
./gradlew nativeCompile
./gradlew :cli:test :cli:nativeCompile
```
> [!NOTE]
> This build may pass on your OS but may fail on the other OS for which this cli compiles. These will be built and tested during PR checks
> This build may pass on your OS but may fail on another OS for which this cli compiles. These will be built and tested during PR checks


## Submit a pull request
Expand Down
22 changes: 18 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
## JustServe Cli Tool
# JustServe Resources

The JustServe Cli tool is an admin tool for JustServe Specialists and administrators.
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=JustServe-Resources_cli&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=JustServe-Resources_cli)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=JustServe-Resources_cli&metric=coverage)](https://sonarcloud.io/summary/new_code?id=JustServe-Resources_cli)
[![Duplicated Lines (%)](https://sonarcloud.io/api/project_badges/measure?project=JustServe-Resources_cli&metric=duplicated_lines_density)](https://sonarcloud.io/summary/new_code?id=JustServe-Resources_cli)
[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=JustServe-Resources_cli&metric=bugs)](https://sonarcloud.io/summary/new_code?id=JustServe-Resources_cli)
[![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=JustServe-Resources_cli&metric=code_smells)](https://sonarcloud.io/summary/new_code?id=JustServe-Resources_cli)
[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=JustServe-Resources_cli&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=JustServe-Resources_cli)
[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=JustServe-Resources_cli&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=JustServe-Resources_cli)
[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=JustServe-Resources_cli&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=JustServe-Resources_cli)

This tool is very much under development and whose api is subject to change with each release. Standard versioning is used for this project to delineate breaking releases.
This repository is architected as a modular, multi-module Micronaut project. At its center is the `core` module, a
reusable library providing a thoroughly tested HTTP client for the JustServe API.

Other modules leverage the `core` library to build specific applications. The `cli` module, for instance, is a GraalVM native command-line application that consumes the `core` client to provide administrative tooling.

As the project evolves, we adhere to semantic versioning. The API is subject to change, and any breaking modifications will be clearly communicated through major version increments.

## Cli Tool

### Install

Expand Down Expand Up @@ -37,7 +51,7 @@ echo $env:java_home
</ol>
</details>

To generate the executable for your system, run `./gradlew nativeCompile`. The executable will be generated in the build directory (`\build\native\nativeCompile\`).
To generate the executable for your system, run `./gradlew :cli:nativeCompile`. The executable will be generated in the build directory (`cli/build/native/nativeCompile/`).

### Authenticate

Expand Down
30 changes: 27 additions & 3 deletions STYLE_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,39 @@ The name of the game is readability and [testability]. The following styling spe

### Testing

- Tests should never run against production.
- Use your development environment's auth token assigned to the `JUSTSERVE_TOKEN` environment variable in tests.
- Methods and Features are to have [adequate] unit and integration tests written before any pull request can be accepted.
- Because we use lombok, we don't need to test setters and getters. Using getters and setters is the preferred way to access class fields.
- Unit test count is to scale appropriately according to the complexity of the method.
- Features are to have [adequate] integration and end-to-end tests.
- Fixes are to have [adequate] unit, integration and end-to-end tests included with the fix for the sake of [regression testing].
- Tests should only test one thing
- e.g. `Set store location with zip code.`
- e.g. `Fail to set store location using invalid zip code`
- e.g. `Set store location by city name`
- e.g. `Set project owner.`
- e.g. `Can NOT to set project owner with invalid UUID`
- e.g. `Can update an Org description`
- Use data-driven testing to validate logic across all permutations of documented behavior like all `EventType` variants below.
```groovy
@Unroll("can set contact info for #eventType.name() event")
def "can set contact info for #eventType event"() {
given:
def event = baseEventBuilder()
.contactEmail(faker.internet().emailAddress())
.contactName(faker.name().fullName())
.contactPhone(faker.phoneNumber().phoneNumber())
.build()
def vars = new CreateEventVariables().setProjectId(projectIds[eventType]).setProjectEvent(event)

when:
client.createEvent(new CreateEventMutation(vars))

then:
noExceptionThrown()

where:
eventType << [EventType.DTL, EventType.Ongoing, EventType.MultipleDTL]
}
```

#### Adequate Testing Coverage
Adequate testing is determined by the method's documentation (this is why all methods require docs). Testing is surgical and specific; test exactly what is documented, no more and no less. If it's in the docs, then [test it]! The only exception to this is that [branches of code] should be covered in testing, which may not be documented.
Expand Down
99 changes: 8 additions & 91 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,93 +1,10 @@
import org.apache.tools.ant.filters.ReplaceTokens
import java.util.*

plugins {
id("groovy")
id("io.micronaut.application") version "4.5.3"
id("com.gradleup.shadow") version "8.3.6"
id("io.micronaut.openapi") version "4.5.3"
id("org.graalvm.buildtools.native") version "0.10.6"
id("org.asciidoctor.jvm.convert") version "3.3.2"
}

version = project.properties["justserveCliVersion"]!!
group = "org.justserve"

apply(from = "gradle/asciidoc.gradle")

repositories {
mavenCentral()
}

dependencies {
annotationProcessor("org.projectlombok:lombok")
annotationProcessor("info.picocli:picocli-codegen")
annotationProcessor("io.micronaut.serde:micronaut-serde-processor")
implementation("info.picocli:picocli")
implementation("info.picocli:picocli-jansi-graalvm:1.2.0")
implementation("org.fusesource.jansi:jansi:2.4.2")
implementation("info.picocli:picocli-shell-jline3:4.7.6")
implementation("org.jline:jline:3.30.5")
implementation("io.micronaut:micronaut-http-client")
implementation("io.micronaut.picocli:micronaut-picocli")
implementation("io.micronaut.serde:micronaut-serde-jackson")
implementation("io.micronaut:micronaut-retry")
implementation("org.simplejavamail:simple-java-mail:8.12.6")
implementation("org.jsoup:jsoup:1.21.2")
testImplementation("net.datafaker:datafaker:2.5.1")
compileOnly("org.projectlombok:lombok")
runtimeOnly("ch.qos.logback:logback-classic")
runtimeOnly("org.yaml:snakeyaml")
}


application {
mainClass = "org.justserve.cli.JustServeCommand"
}

java {
sourceCompatibility = JavaVersion.toVersion("21")
targetCompatibility = JavaVersion.toVersion("21")
}

tasks.withType<ProcessResources> {
val props = Properties()
file("gradle.properties").inputStream().use { props.load(it) }
filesMatching("**/application.yml") {
filter(mapOf("tokens" to props), ReplaceTokens::class.java)
}
}

micronaut {
testRuntime("spock2")
openapi {
version = "6.16.0"
client(file("src/main/resources/schema.yml")) {
apiPackageName = "org.justserve.client"
modelPackageName = "org.justserve.model"
useReactive = false
useAuth = false
lombok.set(true)
clientId = "justserve"
apiNameSuffix = "Client"
alwaysUseGenerateHttpResponse = true
}
}
processing {
incremental(true)
annotations("org.justserve.*")
}
}

graalvmNative.binaries {
named("main") {
imageName.set("justserve")
buildArgs.add("--color=always")
buildArgs.add("-march=native")
}
}


tasks.named<io.micronaut.gradle.docker.NativeImageDockerfile>("dockerfileNative") {
jdkVersion = "21"
id("it.nicolasfarabegoli.conventional-commits") version "3.1.3"
id("io.micronaut.application") version "4.6.2" apply false
id("io.micronaut.library") version "4.5.3" apply false
id("io.micronaut.openapi") version "4.5.3" apply false
id("io.micronaut.aot") version "4.6.2" apply false
id("com.gradleup.shadow") version "8.3.9" apply false
id("org.graalvm.buildtools.native") version "0.10.6" apply false
id("com.github.hauner.jarTest") version "1.1.0" apply false
}
78 changes: 78 additions & 0 deletions cli/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import org.apache.tools.ant.filters.ReplaceTokens
import java.util.*

plugins {
id("groovy")
id("io.micronaut.application")
id("com.gradleup.shadow")
id("org.graalvm.buildtools.native")
}

group = "org.justserve"
version = project.properties["justserveCliVersion"]!!

repositories {
mavenCentral()
}

dependencies {
annotationProcessor("org.projectlombok:lombok")
annotationProcessor("info.picocli:picocli-codegen")
annotationProcessor("io.micronaut.serde:micronaut-serde-processor")
annotationProcessor("io.micronaut.validation:micronaut-validation")
implementation("io.micronaut.validation:micronaut-validation")
implementation("io.micronaut.reactor:micronaut-reactor")
implementation("info.picocli:picocli")
implementation("info.picocli:picocli-jansi-graalvm:${project.properties["jansiGraalvmVersion"]}")
implementation("org.fusesource.jansi:jansi:${project.properties["jansiVersion"]}")
implementation("info.picocli:picocli-shell-jline3:${project.properties["picocliShellVersion"]}")
implementation("org.jline:jline:${project.properties["jlineVersion"]}")
implementation("io.micronaut.picocli:micronaut-picocli")
implementation("org.simplejavamail:simple-java-mail:${project.properties["simpleJavaMailVersion"]}")
implementation("io.micronaut.serde:micronaut-serde-jackson")
implementation("io.micronaut:micronaut-http-client")
implementation("org.jsoup:jsoup:${project.properties["jsoupVersion"]}")
implementation(project(":core"))
testImplementation("net.datafaker:datafaker:${project.properties["datafakerVersion"]}")
testImplementation("org.apache.commons:commons-lang3:${project.properties["commonsLangVersion"]}")
testImplementation(project(path = ":core", configuration = "testArchives"))
compileOnly("org.projectlombok:lombok")
runtimeOnly("ch.qos.logback:logback-classic")
runtimeOnly("org.yaml:snakeyaml")
}


application {
mainClass = "org.justserve.JustServeCommand"
}
java {
sourceCompatibility = JavaVersion.toVersion("21")
targetCompatibility = JavaVersion.toVersion("21")
}

tasks.withType<ProcessResources> {
val props = Properties()
file("../gradle.properties").inputStream().use { props.load(it) }
filesMatching("**/application.yml") {
filter(mapOf("tokens" to props), ReplaceTokens::class.java)
}
}

micronaut {
testRuntime("spock2")
processing {
incremental(true)
annotations("org.justserve.*")
}
}

tasks.named<io.micronaut.gradle.docker.NativeImageDockerfile>("dockerfileNative") {
jdkVersion = "21"
}

graalvmNative.binaries {
named("main") {
imageName.set("justserve")
buildArgs.add("--color=always")
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
package org.justserve.cli;
package org.justserve;

import io.micronaut.configuration.picocli.PicocliRunner;
import org.justserve.cli.command.BaseCommand;
import org.justserve.cli.command.GetTempPassword;
import org.justserve.cli.command.MakeOrgAdmin;
import org.justserve.cli.command.UnReassignProjects;
import org.justserve.cli.util.JustServeVersionProvider;
import org.justserve.command.*;
import org.justserve.util.JustServeVersionProvider;
import picocli.CommandLine.Command;
import picocli.CommandLine.ParameterException;
import picocli.jansi.graalvm.AnsiConsole;

@Command(subcommands = {GetTempPassword.class, MakeOrgAdmin.class, UnReassignProjects.class},
@Command(subcommands = {GetTempPassword.class, MakeOrgAdmin.class, UnReassignProjects.class, AssignOrgToProject.class},
mixinStandardHelpOptions = true,
name = "justserve", versionProvider = JustServeVersionProvider.class,
description = "justserve-cli is a terminal tool to help specialists and admin using JustServe")
Expand Down
Loading
Loading