// LEARNINGCOLLECTIONAPI — NEURAL KNOWLEDGE REPOSITORY

Kotlin 2.3.20 · Spring Boot 4.0.4 · Spring Data JPA + Spring Data REST · MySQL · QueryDSL 5.1.0 · SpringDoc OpenAPI 1.8.0 · Gradle 9.4.1 · JDK 17 toolchain

1 REST controller (LearningController) · 10 convenience GET endpoints + auto-generated /learning CRUD · 1 JPA entity (LearningCollection) · HTTP backing service for the zsh-learn plugin

Report GitHub Issues
// Color scheme

>_SAVE WHAT YOU LEARN. QUERY IT. QUIZ YOURSELF. OVER HTTP.

LearningCollectionAPI is a Kotlin / Spring Boot REST service — the backing store for the zsh-learn shell plugin. Each learning is one row in a single LearningCollection table (id, learning, category, dateAdded), persisted in MySQL through Spring Data JPA. The service combines two interfaces: a hand-written LearningController exposing ten convenience GET endpoints (add / filter-search / recent / random / dump), and the Spring Data REST auto-generated CRUD surface under /learning backed by LCRepo. You GET /add?learning=… from the shell, GET /filter?learning=… to search, and GET /random or /recent/{count} to quiz. No request bodies, no auth layer — everything is a query-string or path-variable GET.

Quickstart

MySQL on localhost:3306 with a schema named root must be live; the JDK 17 toolchain and Kotlin / Gradle versions are managed by the wrapper:

# clone + boot (Spring Boot default port 8080)
git clone https://github.com/MenkeTechnologies/LearningCollectionAPI
cd LearningCollectionAPI
./gradlew bootRun

# build + run diagnostics, then package
./gradlew build
./gradlew bootJar
java -jar build/libs/learningcollection-0.0.1-SNAPSHOT.jar

# forge an OCI container image
./gradlew bootBuildImage

Datasource config lives in src/main/resources/application.properties: jdbc:mysql://localhost:3306/root, user root, empty password, UTC server timezone. Once up, save and query from the shell:

# save a learning (category defaults to "programming")
curl 'http://localhost:8080/add?learning=zsh+globbing+qualifiers'

# search by substring (case-sensitive LIKE %text%)
curl 'http://localhost:8080/filter?learning=zsh'

# quiz: one random learning, full record
curl http://localhost:8080/random

# last 5 learnings, text only
curl http://localhost:8080/recents/5

Full reference: README on GitHub. OpenAPI / Swagger UI is wired via SpringDoc (springdoc-openapi-ui + springdoc-openapi-data-rest).

The domain — one entity, one table

The entire data model is a single JPA entity, LearningCollection (@Table(name = "LearningCollection")), with four columns. There is no second table, no join, no embedded type.

FieldTypeJPA mappingNotes
idLong@Id @GeneratedValue(IDENTITY)Auto-increment primary key, defaults to 0 before persist.
learningString@ColumnThe learning text itself — the thing being saved / searched / quizzed.
categoryString@ColumnSet to the DEFAULT_CAT constant ("programming") by the /add endpoint.
dateAddedjava.util.Date@ColumnStamped with Date() at save time. Recency ordering relies on insertion order, not this field.

The entity implements Serializable (serialVersionUID = 1L) and has both a four-arg constructor (used by /add) and a no-arg constructor (used by JPA / deserialization).

Two interfaces — convenience GETs vs auto CRUD

LearningController (hand-written)

A single @RestController exposing ten @GetMapping endpoints. Designed for shell calls — every operation is a GET with a query param or path variable, no request body. Covers save (/add), substring search (/filter), recency (/recent / /recents), randomness (/random / /randoms), and a raw /dump.

LCRepo (Spring Data REST)

@RepositoryRestResource(path = "learning") over CrudRepository<LearningCollection, Long>. Spring Data REST auto-exposes full CRUD (GET / POST / PUT / PATCH / DELETE) under /learning with HAL pagination — no controller code written for it.

REST surface — LearningController endpoints

Ten convenience endpoints, all GET. “Compressed” below means the response is just the learning text strings; “full” means complete LearningCollection records (id / learning / category / dateAdded). Recency is computed as findAll().reversed() (newest-inserted first), randomness as findAll().shuffled().

MethodPathHandlerReturns
GET/add?learning=<text>addSaves a new LearningCollection (category = "programming", dateAdded = now); returns the saved entity.
GET/filter?learning=<text>filterLearnSubstring search via findAllByLearningContaining; returns matching learning strings.
GET/recentslearningItemRecentShortDefaultLast SHORT_CNT (20) learnings, text only.
GET/recents/{count}getLearningItemRecentShortLast {count} learnings, text only.
GET/recent/{count}getLearningItemRecentLast {count} learnings, full records.
GET/randomlearningItemOne random learning, full record.
GET/randomslearningItemCountShortOne random learning, text only.
GET/randoms/{count}getLearningItemCountShort{count} random learnings, text only.
GET/random/{count}getLearningItemCount{count} random learnings, full records.
GET/dumpgetDumpStreams mysqldump --extended-insert=FALSE root to the servlet output stream (full MySQL extraction).

Auto-generated CRUD — /learning

Because LCRepo is annotated @RepositoryRestResource(collectionResourceRel = "learning", path = "learning") and extends CrudRepository, Spring Data REST exposes a standard hypermedia CRUD surface under /learning with zero controller code. findAllByLearningContaining and findAllByCategoryContaining are surfaced as search resources.

# Spring Data REST CRUD (auto-generated from LCRepo)
GET    /learning              # paged collection (HAL)
GET    /learning/{id}         # one resource
POST   /learning              # create (JSON body)
PUT    /learning/{id}         # replace
PATCH  /learning/{id}         # partial update
DELETE /learning/{id}         # remove
GET    /learning/search       # exported derived queries
       # findAllByLearningContaining, findAllByCategoryContaining

Note that findAllByCategoryContaining exists on the repository and is exposed under the Spring Data REST search resource, but is not wired into any LearningController endpoint — only findAllByLearningContaining is used by the hand-written /filter route.

CORS & configuration

WebConfig implements both WebMvcConfigurer and RepositoryRestConfigurer and opens CORS for all origins on every path (addMapping("/**")) — once for MVC controllers, once for the Spring Data REST layer. This lets the zsh-learn plugin (and any browser client) call the API cross-origin.

ConstantValueUsed by
SHORT_CNT20/recents default page size.
DB_NAME"root"Schema name embedded in the DUMP command.
DEFAULT_CAT"programming"Category assigned to every learning created via /add.
DUMP"mysqldump --extended-insert=FALSE root"The shell command /dump executes via Runtime.exec.

Source layout

src/main/kotlin/com/menketechnologies/learningcollection/
├── LearningCollectionApplication.kt   # @SpringBootApplication bootloader
├── LearningController.kt               # @RestController — 10 GET endpoints
├── LearningCollection.kt               # @Entity — id | learning | category | dateAdded
├── LCRepo.kt                           # CrudRepository + @RepositoryRestResource
├── Consts.kt                           # SHORT_CNT, DB_NAME, DEFAULT_CAT, DUMP
└── WebConfig.kt                        # CORS for MVC + Spring Data REST
src/main/resources/
└── application.properties              # MySQL datasource + JPA config
src/test/kotlin/com/menketechnologies/learningcollection/
└── 23 test files                       # controller / entity / repo / config coverage

6 main Kotlin files (147 LOC) plus 23 test files (6,155 LOC). The engineering report (report.html) breaks down the test suite, the dialect of each endpoint, and the full dependency manifest.

License & usage

Authored by MenkeTechnologies. Built with Kotlin on the JVM. The persisted data is yours — a plain MySQL table you can /dump at any time. Part of the MenkeTechnologiesMeta umbrella.