From 0801f395fa41711274fcdd88e7ff286cef25cf4d Mon Sep 17 00:00:00 2001 From: James Talbot Date: Thu, 12 Feb 2026 14:00:12 -0700 Subject: [PATCH 1/3] feat(wip): support bounded endpoint --- src/main/resources/schema.yml | 61 +++++++++++++++++++ .../client/UserBoundedClientSpec.groovy | 22 +++++++ 2 files changed, 83 insertions(+) create mode 100644 src/test/groovy/org/justserve/client/UserBoundedClientSpec.groovy diff --git a/src/main/resources/schema.yml b/src/main/resources/schema.yml index d68296f..bdfa059 100644 --- a/src/main/resources/schema.yml +++ b/src/main/resources/schema.yml @@ -278,6 +278,39 @@ paths: description: Bad Request '500': description: Internal Server Error + /api/v1/users/{userId}/bounded: + get: + tags: + - User + parameters: + - name: userId + in: path + required: true + schema: + type: string + - name: showLeads + in: query + schema: + type: boolean + default: true + - name: showFull + in: query + schema: + type: boolean + default: true + - name: levels + in: query + schema: + type: integer + format: int32 + default: 1 + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: "#/components/schemas/UserResultBounded" /api/v1/users/securitycontext/bearer: get: description: "Retrieves the admin context for a given user (ie org ID's, church unit id's, role levels, etc). if no user is provided, it will return the context for the currently authenticated user" @@ -659,6 +692,34 @@ components: type: string description: "The unique ID of the user." additionalProperties: false + UserResultBounded: + type: object + properties: + id: + type: string + nullable: true + firstName: + type: string + nullable: true + lastName: + type: string + nullable: true + role: + $ref: '#/components/schemas/Role' + isLead: + type: boolean + email: + type: string + nullable: true + phone: + type: string + nullable: true + keywords: + type: string + nullable: true + isActive: + type: boolean + additionalProperties: false UserSearchRequest: description: | search query used in a few different endpoints diff --git a/src/test/groovy/org/justserve/client/UserBoundedClientSpec.groovy b/src/test/groovy/org/justserve/client/UserBoundedClientSpec.groovy new file mode 100644 index 0000000..ea74e72 --- /dev/null +++ b/src/test/groovy/org/justserve/client/UserBoundedClientSpec.groovy @@ -0,0 +1,22 @@ +package org.justserve.client + +import io.micronaut.test.extensions.spock.annotation.MicronautTest +import org.justserve.JustServeSpec +import org.justserve.model.UserResultBounded + +@MicronautTest +class UserBoundedClientSpec extends JustServeSpec { + + def "Should be able to get details for a specific user without facing errors"(UserClient client) { + when: + UserResultBounded result = client.apiV1UsersUserIdBoundedGet(readOnlyUser.uuid) + + then: + noExceptionThrown() + + where: + client | _ + userClient | _ +// noAuthUserClient | _ + } +} From dc9fa3b39eca68c9f17e14c92ef5097eed760499 Mon Sep 17 00:00:00 2001 From: JamesTalbot Date: Tue, 17 Feb 2026 14:25:40 -0700 Subject: [PATCH 2/3] fix(build): remove custom test task --- build.gradle.kts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 67fc9c7..20a000a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -59,15 +59,16 @@ tasks.withType { } } -tasks.withType { - testLogging { - events("passed", "skipped", "failed") - showStandardStreams = true - exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL - } - // Exclude EmailParserSpec until we can provide test .eml files without PII data - exclude("**/EmailParserSpec.class") -} + +//tasks.withType { +// testLogging { +// events("passed", "skipped", "failed") +// showStandardStreams = true +// exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL +// } +//// Exclude EmailParserSpec until we can provide test .eml files without PII data +// exclude("**/EmailParserSpec.class") +//} micronaut { testRuntime("spock2") From 65b0a03dc99d617af4de28911857e7be9602d147 Mon Sep 17 00:00:00 2001 From: JamesTalbot Date: Thu, 19 Feb 2026 10:46:56 -0700 Subject: [PATCH 3/3] feat: add test for the user client bounded requests --- src/main/resources/schema.yml | 3 + .../client/UserBoundedClientSpec.groovy | 22 ---- .../justserve/client/UserClientSpec.groovy | 30 ++++- .../org/justserve/util/EmailParserSpec.groovy | 121 ------------------ 4 files changed, 29 insertions(+), 147 deletions(-) delete mode 100644 src/test/groovy/org/justserve/client/UserBoundedClientSpec.groovy delete mode 100644 src/test/groovy/org/justserve/util/EmailParserSpec.groovy diff --git a/src/main/resources/schema.yml b/src/main/resources/schema.yml index bdfa059..d99f240 100644 --- a/src/main/resources/schema.yml +++ b/src/main/resources/schema.yml @@ -280,6 +280,7 @@ paths: description: Internal Server Error /api/v1/users/{userId}/bounded: get: + operationId: getAllInfo tags: - User parameters: @@ -288,6 +289,7 @@ paths: required: true schema: type: string + format: uuid - name: showLeads in: query schema: @@ -697,6 +699,7 @@ components: properties: id: type: string + format: uuid nullable: true firstName: type: string diff --git a/src/test/groovy/org/justserve/client/UserBoundedClientSpec.groovy b/src/test/groovy/org/justserve/client/UserBoundedClientSpec.groovy deleted file mode 100644 index ea74e72..0000000 --- a/src/test/groovy/org/justserve/client/UserBoundedClientSpec.groovy +++ /dev/null @@ -1,22 +0,0 @@ -package org.justserve.client - -import io.micronaut.test.extensions.spock.annotation.MicronautTest -import org.justserve.JustServeSpec -import org.justserve.model.UserResultBounded - -@MicronautTest -class UserBoundedClientSpec extends JustServeSpec { - - def "Should be able to get details for a specific user without facing errors"(UserClient client) { - when: - UserResultBounded result = client.apiV1UsersUserIdBoundedGet(readOnlyUser.uuid) - - then: - noExceptionThrown() - - where: - client | _ - userClient | _ -// noAuthUserClient | _ - } -} diff --git a/src/test/groovy/org/justserve/client/UserClientSpec.groovy b/src/test/groovy/org/justserve/client/UserClientSpec.groovy index a5eec60..194bbe0 100644 --- a/src/test/groovy/org/justserve/client/UserClientSpec.groovy +++ b/src/test/groovy/org/justserve/client/UserClientSpec.groovy @@ -1,11 +1,15 @@ package org.justserve.client +import io.micronaut.http.HttpResponse +import io.micronaut.http.HttpStatus import io.micronaut.http.client.exceptions.HttpClientResponseException +import io.micronaut.http.exceptions.HttpException import net.datafaker.Faker import org.apache.commons.lang3.RandomStringUtils import org.justserve.JustServeSpec import org.justserve.TestUser import org.justserve.model.UserHashRequestByEmail +import org.justserve.model.UserResultBounded import static io.micronaut.http.HttpStatus.UNAUTHORIZED @@ -20,7 +24,7 @@ class UserClientSpec extends JustServeSpec { client.createUser( user.firstName, user.lastName, - RandomStringUtils.insecure().nextAlphanumeric(20)+ "@fake.com", + RandomStringUtils.insecure().nextAlphanumeric(20) + "@fake.com", user.password, user.zipcode, user.locale, @@ -68,8 +72,26 @@ class UserClientSpec extends JustServeSpec { exception.status == expectedError where: - expectedError | client | _ - null | userClient | _ - UNAUTHORIZED | noAuthUserClient | _ + expectedError | client | _ + null | userClient | _ + UNAUTHORIZED | noAuthUserClient | _ + } + + def "Should be able to get details for a specific user without facing errors"(UserClient client, HttpStatus expectedError) { + when: + HttpResponse response = client.getAllInfo(readOnlyUser.uuid, true, true, 1) + + then: + if (!expectedError) { + response.body() != null + return + } + def exception = thrown(HttpClientResponseException) + exception.status == expectedError + + where: + expectedError | client | _ + null | userClient | _ + UNAUTHORIZED | noAuthUserClient | _ } } diff --git a/src/test/groovy/org/justserve/util/EmailParserSpec.groovy b/src/test/groovy/org/justserve/util/EmailParserSpec.groovy deleted file mode 100644 index e708e63..0000000 --- a/src/test/groovy/org/justserve/util/EmailParserSpec.groovy +++ /dev/null @@ -1,121 +0,0 @@ -package org.justserve.util - -import io.micronaut.core.io.ResourceResolver -import io.micronaut.test.extensions.spock.annotation.MicronautTest -import jakarta.inject.Inject -import org.jsoup.nodes.Document -import org.justserve.cli.util.JustServeEmailParserError -import spock.lang.Shared -import spock.lang.Specification - -import java.util.stream.Collectors -import java.util.stream.Stream - -@MicronautTest -class EmailParserSpec extends Specification { - - @Shared - @Inject - ResourceResolver resourceResolver - - @Shared - Map testEmails - - @Shared - Map testTrackingUrls - - - def setupSpec() { - testEmails = new HashMap<>() - Stream.of("sara-anderson-email.eml", "test-with-automated-email.eml", "test-without-automated-email.eml").forEach { file -> - def resource = resourceResolver.getResourceAsStream("classpath:$file") - resource.ifPresent { stream -> - try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) { - testEmails.put(file.replace(".eml", ""), reader.lines().collect(Collectors.joining(System.lineSeparator()))) - } catch (IOException e) { - throw new RuntimeException("Failed to read test file: $file", e) - } - } - } - - testTrackingUrls = new HashMap<>() - def yamlResource = resourceResolver.getResourceAsStream("classpath:projects.yaml") - yamlResource.ifPresent { stream -> - try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) { - testTrackingUrls = reader.lines() - .filter(line -> line.contains(':')) - .collect(Collectors.toMap( - { line -> ((line as String).split(':', 2)[0] as String).replaceAll('"', '').trim() }, - { line -> ((line as String).split(':', 2)[1] as String).replaceAll('"', '').trim() } - )) - } catch (IOException e) { - throw new RuntimeException("Failed to read projects.yaml", e) - } - } - } - - def "validate parse can read in an eml file and take in the html body of the email"() { - when: - def doc = EmailParser.parse(fileContent as String) - - then: - if (!(title as String).toLowerCase().contains("without")) { - null != doc - return - } - def error = thrown(JustServeEmailParserError) - error.message == "Email is not a JustServe generated email." - - - where: - [title, fileContent] << testEmails.collect { key, value -> [key, value] } - } - - - def "validate getProjectIdFromUglyUrl will properly get #projectName's id from the provided ugly url"() { - when: - UUID projectID = EmailParser.getProjectIDFromUglyUrl(uglyUrl as String) - - then: - null != projectID - noExceptionThrown() - - where: - [projectName, uglyUrl] << testTrackingUrls.collect { key, value -> [key, value] } - } - - def "Can use 'getProjects' to parse jsoup doc in #title file"() { - given: - Document doc = EmailParser.parse(fileContent as String) - - when: - Map> projects = EmailParser.getProjects(doc) - - then: - if (!(title as String).toLowerCase().contains("without")) { - projects.size() > 0 - return - } - def error = thrown(JustServeEmailParserError) - error.message == "Email does not contain an HTML body." - - where: - [title, fileContent] << testEmails.findAll { key, _ -> !key.toLowerCase().contains("without") }.collect { key, value -> [key, value] } - } - - def "Can use 'getProjects' to parse entire #title file"() { - when: - Map> projects = EmailParser.getProjects(fileContent as String) - - then: - if (!(title as String).toLowerCase().contains("without")) { - projects.size() > 0 - return - } - def error = thrown(JustServeEmailParserError) - error.message == "Email is not a JustServe generated email." - - where: - [title, fileContent] << testEmails.collect { key, value -> [key, value] } - } -}