implement organization repo

This commit is contained in:
Aleksandr Trushkin
2024-03-24 16:51:39 +03:00
parent 88a3cae4fa
commit 9d2efcc1c4
21 changed files with 1946 additions and 456 deletions

View File

@ -1,3 +1,2 @@
with-expecter: true with-expecter: true
keeptree: True keeptree: True

View File

@ -1,4 +1,4 @@
version: '3' version: "3"
env: env:
CGO_ENABLED: 0 CGO_ENABLED: 0
@ -17,9 +17,11 @@ vars:
tasks: tasks:
install_tools: install_tools:
cmds: cmds:
- go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.55.2 - "[[ ! -f $GOBIN/golangci-lint ]] && go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.55.2 || echo golang-ci lint installed"
- go install github.com/a-h/templ/cmd/templ@v0.2.513 - "[[ ! -f $GOBIN/templ ]] && go install github.com/a-h/templ/cmd/templ@v0.2.513 || echo templ installed"
- "[[ ! -f $GOBIN/mockery ]] && go install github.com/vektra/mockery/v2@v2.42.1 || echo mockery installed"
generate: generate:
run: once
cmds: cmds:
- "$GOBIN/templ generate" - "$GOBIN/templ generate"
sources: sources:
@ -28,16 +30,27 @@ tasks:
generates: generates:
- "internal/kurious/ports/http/templ/*.go" - "internal/kurious/ports/http/templ/*.go"
- "internal/kurious/ports/http/bootstrap/*.go" - "internal/kurious/ports/http/bootstrap/*.go"
deps:
- install_tools
mocks:
run: once
cmd: "go generate ./internal/..."
deps:
- install_tools
check: check:
run: once
cmds: cmds:
- "$GOBIN/golangci-lint run ./..." - "$GOBIN/golangci-lint run ./..."
deps: deps:
- generate - generate
- mocks
test: test:
run: once
cmds: cmds:
- go test ./internal/... - go test ./internal/...
deps: deps:
- generate - generate
- mocks
build_web: build_web:
cmds: cmds:
- go build -o $GOBIN/kuriousweb -v -ldflags '{{.LDFLAGS}}' cmd/kuriweb/*.go - go build -o $GOBIN/kuriousweb -v -ldflags '{{.LDFLAGS}}' cmd/kuriweb/*.go
@ -48,7 +61,7 @@ tasks:
deps: [check, test] deps: [check, test]
build_dev_cli: build_dev_cli:
cmds: cmds:
- go build -o $GOBIN/sravnicli -v -ldflags '{{.LDFLAGS}}' cmd/dev/sravnicli/*.go - go build -o $GOBIN/sravnicli -v -ldflags '{{.LDFLAGS}}' cmd/dev/sravnicli/*.go
deps: [check, test] deps: [check, test]
build: build:
cmds: cmds:

3
go.mod
View File

@ -1,6 +1,6 @@
module git.loyso.art/frx/kurious module git.loyso.art/frx/kurious
go 1.21 go 1.22
require ( require (
github.com/a-h/templ v0.2.513 github.com/a-h/templ v0.2.513
@ -30,6 +30,7 @@ require (
github.com/ncruces/go-strftime v0.1.9 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/yandex-cloud/go-genproto v0.0.0-20231120081503-a21e9fe75162 // indirect github.com/yandex-cloud/go-genproto v0.0.0-20231120081503-a21e9fe75162 // indirect
github.com/ydb-platform/ydb-go-genproto v0.0.0-20231012155159-f85a672542fd // indirect github.com/ydb-platform/ydb-go-genproto v0.0.0-20231012155159-f85a672542fd // indirect
github.com/ydb-platform/ydb-go-yc-metadata v0.6.1 // indirect github.com/ydb-platform/ydb-go-yc-metadata v0.6.1 // indirect

4
go.sum
View File

@ -785,6 +785,8 @@ github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcD
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
@ -798,6 +800,8 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/teris-io/cli v1.0.1 h1:J6jnVHC552uqx7zT+Ux0++tIvLmJQULqxVhCid2u/Gk= github.com/teris-io/cli v1.0.1 h1:J6jnVHC552uqx7zT+Ux0++tIvLmJQULqxVhCid2u/Gk=
github.com/teris-io/cli v1.0.1/go.mod h1:V9nVD5aZ873RU/tQXLSXO8FieVPQhQvuNohsdsKXsGw= github.com/teris-io/cli v1.0.1/go.mod h1:V9nVD5aZ873RU/tQXLSXO8FieVPQhQvuNohsdsKXsGw=
github.com/vektra/mockery/v2 v2.42.1 h1:z7l3O4jCzRZat3rm9jpHc8lzpR8bs1VBii7bYtl3KQs=
github.com/vektra/mockery/v2 v2.42.1/go.mod h1:XNTE9RIu3deGAGQRVjP1VZxGpQNm0YedZx4oDs3prr8=
github.com/yandex-cloud/go-genproto v0.0.0-20211115083454-9ca41db5ed9e/go.mod h1:HEUYX/p8966tMUHHT+TsS0hF/Ca/NYwqprC5WXSDMfE= github.com/yandex-cloud/go-genproto v0.0.0-20211115083454-9ca41db5ed9e/go.mod h1:HEUYX/p8966tMUHHT+TsS0hF/Ca/NYwqprC5WXSDMfE=
github.com/yandex-cloud/go-genproto v0.0.0-20231120081503-a21e9fe75162 h1:xCzizLC090MiLWEV3aziL5YIKrSVTRXX2DXlRGeQ6sA= github.com/yandex-cloud/go-genproto v0.0.0-20231120081503-a21e9fe75162 h1:xCzizLC090MiLWEV3aziL5YIKrSVTRXX2DXlRGeQ6sA=
github.com/yandex-cloud/go-genproto v0.0.0-20231120081503-a21e9fe75162/go.mod h1:HEUYX/p8966tMUHHT+TsS0hF/Ca/NYwqprC5WXSDMfE= github.com/yandex-cloud/go-genproto v0.0.0-20231120081503-a21e9fe75162/go.mod h1:HEUYX/p8966tMUHHT+TsS0hF/Ca/NYwqprC5WXSDMfE=

View File

@ -1,183 +1,208 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<title>Test page</title> <title>Test page</title>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<link <link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"
rel="stylesheet" rel="stylesheet"
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN"
crossorigin="anonymous" crossorigin="anonymous"
/> />
<script <script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
crossorigin="anonymous" crossorigin="anonymous"
></script> ></script>
</head> </head>
<body data-bs-theme="dark"> <body data-bs-theme="dark">
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container-fluid">
<a class="navbar-brand" href="#">Kurious</a>
<button
class="navbar-toggler"
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarNavAltMarkup"
aria-controls="navbarNavAltMarkup"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse" id="navbarNavAltMarkup">
<div class="navbar-nav">
<a
class="nav-link"
aria-current="page"
href="/index.html"
>Home</a
>
<a class="nav-link" href="/courses.html">Courses</a>
<a class="nav-link active" href="/core.html"
>About us</a
>
</div>
</div>
</div>
</nav>
<nav class="navbar navbar-expand-lg bg-body-tertiary"> <div class="container">
<div class="container-fluid"> <nav
<a class="navbar-brand" href="#">Kurious</a> style="--bs-breadcrumb-divider: &quot;>&quot;"
<button aria-label="breadcrumb"
class="navbar-toggler" >
type="button" <ol class="breadcrumb">
data-bs-toggle="collapse" <li class="breadcrumb-item"><a href="#">Home</a></li>
data-bs-target="#navbarNavAltMarkup" <li class="breadcrumb-item" aria-current="page">
aria-controls="navbarNavAltMarkup" <a href="#">Course</a>
aria-expanded="false" </li>
aria-label="Toggle navigation" <li class="breadcrumb-item active" aria-current="page">
> Theme
<span class="navbar-toggler-icon"></span> </li>
</button> </ol>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup"> </nav>
<div class="navbar-nav">
<a class="nav-link" aria-current="page" href="/htmlexamples/index.html">Home</a> <div class="row row-cols-1 row-cols-md-4 g-4">
<a class="nav-link" href="/htmlexamples/courses.html">Courses</a> <div class="col">
<a class="nav-link active" href="/htmlexamples/core.html">About us</a> <div class="card">
</div> <img
src="https://placehold.co/128x128"
class="card-img-top"
alt=""
/>
<div class="card-body">
<h5 class="card-title">
Lorem, ipsum dolor sit amet consectetur
adipisicing elit.
</h5>
<p class="card-text">
Lorem ipsum dolor sit amet consectetur
adipisicing elit. Enim omnis vero, reiciendis
obcaecati perferendis excepturi nostrum nobis
itaque modi dignissimos ...
</p>
<!-- <a href="#" class="btn btn-primary">Go somewhere</a> -->
</div>
<div class="list-group">
<a href="#" class="btn btn-primary"
>Buy for 399.99$</a
>
<small class="text-body-secondary"></small>
</div>
<div class="card-footer text-end">
<small class="text-body-secondary col"
>399.99$</small
>
</div>
</div>
</div>
<div class="col">
<div class="card">
<img
src="https://placehold.co/128x128"
class="card-img-top"
alt=""
/>
<div class="card-body">
<h5 class="card-title">
Lorem, ipsum dolor sit amet consectetur
adipisicing elit.
</h5>
<p class="card-text">
Lorem ipsum dolor sit amet consectetur
adipisicing elit. Enim omnis vero, reiciendis
obcaecati perferendis excepturi nostrum nobis
itaque modi dignissimos ...
</p>
<a href="#" class="btn btn-primary">Go somewhere</a>
</div>
<div class="card-footer text-end">
<small class="text-body-secondary">399.99$</small>
</div>
</div>
</div>
<div class="col">
<div class="card">
<img
src="https://placehold.co/128x128"
class="card-img-top"
alt=""
/>
<div class="card-body">
<h5 class="card-title">
Lorem, ipsum dolor sit amet consectetur
adipisicing elit.
</h5>
<p class="card-text">
Lorem ipsum dolor sit amet consectetur
adipisicing elit. Enim omnis vero, reiciendis
obcaecati perferendis excepturi nostrum nobis
itaque modi dignissimos ...
</p>
<a href="#" class="btn btn-primary">Go somewhere</a>
</div>
<div class="card-footer text-end">
<small class="text-body-secondary">399.99$</small>
</div>
</div>
</div>
<div class="col">
<div class="card">
<img
src="https://placehold.co/128x128"
class="card-img-top"
alt=""
/>
<div class="card-body">
<h5 class="card-title">
Lorem, ipsum dolor sit amet consectetur
adipisicing elit.
</h5>
<p class="card-text">
Lorem ipsum dolor sit amet consectetur
adipisicing elit. Enim omnis vero, reiciendis
obcaecati perferendis excepturi nostrum nobis
itaque modi dignissimos ...
</p>
<a href="#" class="btn btn-primary">Go somewhere</a>
</div>
<div class="card-footer text-end">
<small class="text-body-secondary">399.99$</small>
</div>
</div>
</div>
<div class="col">
<div class="card">
<img
src="https://placehold.co/128x128"
class="card-img-top"
alt=""
/>
<div class="card-body">
<h5 class="card-title">
Lorem, ipsum dolor sit amet consectetur
adipisicing elit.
</h5>
<p class="card-text">
Lorem ipsum dolor sit amet consectetur
adipisicing elit. Enim omnis vero, reiciendis
obcaecati perferendis excepturi nostrum nobis
itaque modi dignissimos ...
</p>
<a href="#" class="btn btn-primary">Go somewhere</a>
</div>
<div class="card-footer text-end">
<small class="text-body-secondary">399.99$</small>
</div>
</div>
</div>
</div>
</div> </div>
</div> </body>
</nav>
<div class="container">
<nav style="--bs-breadcrumb-divider: '>'" aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="#">Home</a></li>
<li class="breadcrumb-item" aria-current="page">
<a href="#">Course</a>
</li>
<li class="breadcrumb-item active" aria-current="page">Theme</li>
</ol>
</nav>
<div class="row row-cols-1 row-cols-md-4 g-4">
<div class="col">
<div class="card">
<img
src="https://placehold.co/128x128"
class="card-img-top"
alt=""
/>
<div class="card-body">
<h5 class="card-title">
Lorem, ipsum dolor sit amet consectetur adipisicing elit.
</h5>
<p class="card-text">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Enim
omnis vero, reiciendis obcaecati perferendis excepturi nostrum
nobis itaque modi dignissimos ...
</p>
<!-- <a href="#" class="btn btn-primary">Go somewhere</a> -->
</div>
<div class="list-group">
<a href="#" class="btn btn-primary">Buy for 399.99$</a>
<small class="text-body-secondary"></small>
</div>
<div class="card-footer text-end">
<small class="text-body-secondary col">399.99$</small>
</div>
</div>
</div>
<div class="col">
<div class="card">
<img
src="https://placehold.co/128x128"
class="card-img-top"
alt=""
/>
<div class="card-body">
<h5 class="card-title">
Lorem, ipsum dolor sit amet consectetur adipisicing elit.
</h5>
<p class="card-text">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Enim
omnis vero, reiciendis obcaecati perferendis excepturi nostrum
nobis itaque modi dignissimos ...
</p>
<a href="#" class="btn btn-primary">Go somewhere</a>
</div>
<div class="card-footer text-end">
<small class="text-body-secondary">399.99$</small>
</div>
</div>
</div>
<div class="col">
<div class="card">
<img
src="https://placehold.co/128x128"
class="card-img-top"
alt=""
/>
<div class="card-body">
<h5 class="card-title">
Lorem, ipsum dolor sit amet consectetur adipisicing elit.
</h5>
<p class="card-text">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Enim
omnis vero, reiciendis obcaecati perferendis excepturi nostrum
nobis itaque modi dignissimos ...
</p>
<a href="#" class="btn btn-primary">Go somewhere</a>
</div>
<div class="card-footer text-end">
<small class="text-body-secondary">399.99$</small>
</div>
</div>
</div>
<div class="col">
<div class="card">
<img
src="https://placehold.co/128x128"
class="card-img-top"
alt=""
/>
<div class="card-body">
<h5 class="card-title">
Lorem, ipsum dolor sit amet consectetur adipisicing elit.
</h5>
<p class="card-text">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Enim
omnis vero, reiciendis obcaecati perferendis excepturi nostrum
nobis itaque modi dignissimos ...
</p>
<a href="#" class="btn btn-primary">Go somewhere</a>
</div>
<div class="card-footer text-end">
<small class="text-body-secondary">399.99$</small>
</div>
</div>
</div>
<div class="col">
<div class="card">
<img
src="https://placehold.co/128x128"
class="card-img-top"
alt=""
/>
<div class="card-body">
<h5 class="card-title">
Lorem, ipsum dolor sit amet consectetur adipisicing elit.
</h5>
<p class="card-text">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Enim
omnis vero, reiciendis obcaecati perferendis excepturi nostrum
nobis itaque modi dignissimos ...
</p>
<a href="#" class="btn btn-primary">Go somewhere</a>
</div>
<div class="card-footer text-end">
<small class="text-body-secondary">399.99$</small>
</div>
</div>
</div>
</div>
</div>
</body>
</html> </html>

View File

@ -1,144 +1,206 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head>
<title>Test page</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN"
crossorigin="anonymous"
/>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
crossorigin="anonymous"
></script>
<link rel="stylesheet" href="/assets/style.css" />
</head>
<head> <body data-bs-theme="dark" style="margin: 0">
<title>Test page</title> <header>
<meta charset="UTF-8" /> <nav class="navbar navbar-expand-lg bg-body-tertiary w-auto">
<meta name="viewport" content="width=device-width, initial-scale=1" /> <div class="container-fluid">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" <a class="navbar-brand" href="/index.html">Kurious</a>
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous" /> <button
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" class="navbar-toggler"
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" type="button"
crossorigin="anonymous"></script> data-bs-toggle="collapse"
<link rel="stylesheet" href="/assets/style.css"> data-bs-target="#navbarSupportedContent"
</head> aria-controls="navbarSupportedContent"
aria-expanded="false"
<body data-bs-theme="dark" style="margin: 0;"> aria-label="Toggle navigation"
<header> >
<nav class="navbar navbar-expand-lg bg-body-tertiary w-auto"> <span class="navbar-toggler-icon"></span>
<div class="container-fluid"> </button>
<a class="navbar-brand" href="/htmlexamples/index.html">Kurious</a> <div
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" class="navbar-collapse collapse"
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> id="navbarSupportedContent"
<span class="navbar-toggler-icon"></span> >
</button> <ul class="navbar-nav mb-lg-0 mb-2 me-auto">
<div class="collapse navbar-collapse" id="navbarSupportedContent"> <li class="nav-item">
<ul class="navbar-nav me-auto mb-2 mb-lg-0"> <a
<li class="nav-item"> class="nav-link"
<a class="nav-link" aria-current="page" href="/htmlexamples/index.html">Home</a> aria-current="page"
</li> href="/index.html"
<li class="nav-item"> >Home</a
<a class="nav-link active" aria-current="page" href="/htmlexamples/courses.html">Courses</a> >
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="/htmlexamples/core.html">About us</a> <a
</li> class="nav-link active"
</ul> aria-current="page"
</div> href="/courses.html"
</div> >Courses</a
</nav> >
</header> </li>
<li class="nav-item">
<div class="container"> <a class="nav-link" href="/core.html"
>About us</a
<section class="row header"> >
<nav class="mt-4" style="--bs-breadcrumb-divider: '/';" aria-label="breadcrumb"> </li>
<ol class="breadcrumb"> </ul>
<li class="breadcrumb-item"><a href="#">Main</a></li> </div>
<li class="breadcrumb-item" aria-current="page"><a href="#">Languages</a></li>
<li class="breadcrumb-item active" aria-current="page">Japanese</li>
</ol>
</nav>
</section>
<section class="row filters">
<div class="col-8">
<div class="input-group">
<span class="input-group-text">Filter categories</span>
<select class="form-select" id="inputGroupSelect04" aria-label="Example select with button addon">
<option selected>All</option>
<option value="1">Programming</option>
<option value="2">Design</option>
<option value="3">Business</option>
</select>
<select class="form-select" id="inputGroupSelect04" aria-label="Example select with button addon">
<option selected>All</option>
<option value="1">Web development</option>
<option value="2">Backend</option>
<option value="3">Frontend</option>
</select>
<button class="btn btn-outline-secondary" type="button">> Go</button>
</div>
</div>
</section>
<section class="row first-class-group">
<h1 class="title">Languages</h1>
<p>A languages category provides all courses to help learn language</p>
<div class="filter-content d-flex mb-3">
<div class="p-2">
<select class="form-select" id="inputGroupSelect04" aria-label="Example select with button addon">
<option selected>Pick a school</option>
<option value="1">First school in the row</option>
<option value="2">Second but not the shortest named school</option>
<option value="3">Third small</option>
</select>
</div>
<div class="p-2">
<select class="form-select" id="inputGroupSelect04" aria-label="Example select with button addon">
<option selected>Sort by</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
<option value="4">Threerrrrrrrrrrrrrrrrrr</option>
</select>
</div>
<div class="ms-auto p-2">
<div class="btn btn-primary">Promocodes</div>
</div>
</div>
<div class="block second-class-group">
<h2 class="title">Japanese</h2>
<p>Looking for a course to learn japanese language?</p>
<div class="row g-4">
<div class="col-12 col-md-6 col-lg-3">
<div class="card">
<img src="https://placehold.co/128x128" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title">Card title with a long naming</h5>
<div class="input-group d-flex">
<a href="#" class="btn text btn-outline-primary flex-grow-1">Open ></a>
<span class="input-group-text justify-content-end flex-fill">500$</span>
</div> </div>
</div> </nav>
</div> </header>
</div>
<div class="container">
<section class="row header">
<nav
class="mt-4"
style="--bs-breadcrumb-divider: &quot;/&quot;"
aria-label="breadcrumb"
>
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="#">Main</a></li>
<li class="breadcrumb-item" aria-current="page">
<a href="#">Languages</a>
</li>
<li class="breadcrumb-item active" aria-current="page">
Japanese
</li>
</ol>
</nav>
</section>
<section class="row filters">
<div class="col-8">
<div class="input-group">
<span class="input-group-text">Filter categories</span>
<select
class="form-select"
id="inputGroupSelect04"
aria-label="Example select with button addon"
>
<option selected>All</option>
<option value="1">Programming</option>
<option value="2">Design</option>
<option value="3">Business</option>
</select>
<select
class="form-select"
id="inputGroupSelect04"
aria-label="Example select with button addon"
>
<option selected>All</option>
<option value="1">Web development</option>
<option value="2">Backend</option>
<option value="3">Frontend</option>
</select>
<button class="btn btn-outline-secondary" type="button">
> Go
</button>
</div>
</div>
</section>
<section class="row first-class-group">
<h1 class="title">Languages</h1>
<p>
A languages category provides all courses to help learn
language
</p>
<div class="filter-content d-flex mb-3">
<div class="p-2">
<select
class="form-select"
id="inputGroupSelect04"
aria-label="Example select with button addon"
>
<option selected>Pick a school</option>
<option value="1">First school in the row</option>
<option value="2">
Second but not the shortest named school
</option>
<option value="3">Third small</option>
</select>
</div>
<div class="p-2">
<select
class="form-select"
id="inputGroupSelect04"
aria-label="Example select with button addon"
>
<option selected>Sort by</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
<option value="4">Threerrrrrrrrrrrrrrrrrr</option>
</select>
</div>
<div class="ms-auto p-2">
<div class="btn btn-primary">Promocodes</div>
</div>
</div>
<div class="second-class-group block">
<h2 class="title">Japanese</h2>
<p>Looking for a course to learn japanese language?</p>
<div class="row g-4">
<div class="col-12 col-md-6 col-lg-3">
<div class="card">
<img
src="https://placehold.co/128x128"
class="card-img-top"
alt="..."
/>
<div class="card-body">
<h5 class="card-title">
Card title with a long naming
</h5>
<div class="input-group d-flex">
<a
href="#"
class="btn text btn-outline-primary flex-grow-1"
>Open ></a
>
<span
class="input-group-text justify-content-end flex-fill"
>500$</span
>
</div>
</div>
</div>
</div>
</div>
</div>
<hr />
</section>
<footer class="row">
<div class="text-end">
<p>(c) All right reserved</p>
</div>
</footer>
</div> </div>
</body>
</div> </html>
<hr>
</section>
<footer class="row">
<div class="text-end">
<p>(c) All right reserved</p>
</div>
</footer>
</div>
</body>

View File

@ -1,93 +1,126 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head>
<title>Test page</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN"
crossorigin="anonymous"
/>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
crossorigin="anonymous"
></script>
<link rel="stylesheet" href="/assets/style.css" />
</head>
<head> <body data-bs-theme="dark" style="margin: 0">
<title>Test page</title> <header>
<meta charset="UTF-8" /> <nav class="navbar navbar-expand-lg bg-body-tertiary w-auto">
<meta name="viewport" content="width=device-width, initial-scale=1" /> <div class="container-fluid">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" <a class="navbar-brand" href="/index.html">Kurious</a>
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous" /> <button
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" class="navbar-toggler"
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" type="button"
crossorigin="anonymous"></script> data-bs-toggle="collapse"
<link rel="stylesheet" href="/assets/style.css"> data-bs-target="#navbarSupportedContent"
</head> aria-controls="navbarSupportedContent"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span class="navbar-toggler-icon"></span>
</button>
<div
class="navbar-collapse collapse"
id="navbarSupportedContent"
>
<ul class="navbar-nav mb-lg-0 mb-2 me-auto">
<li class="nav-item">
<a
class="nav-link active"
aria-current="page"
href="/index.html"
>Home</a
>
</li>
<li class="nav-item">
<a
class="nav-link"
aria-current="page"
href="/courses.html"
>Courses</a
>
</li>
<li class="nav-item">
<a class="nav-link" href="/core.html"
>About us</a
>
</li>
</ul>
</div>
</div>
</nav>
</header>
<body data-bs-theme="dark" style="margin: 0;"> <div class="container">
<header> <div class="row upper mb-4 text-center" style="min-height: 4rem">
<nav class="navbar navbar-expand-lg bg-body-tertiary w-auto"> <p class="justify-content-center">Some header about courses</p>
<div class="container-fluid"> </div>
<a class="navbar-brand" href="/htmlexamples/index.html">Kurious</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" <div class="row categories">
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> <div class="col">
<span class="navbar-toggler-icon"></span> <div class="block">
</button> <ul>
<div class="collapse navbar-collapse" id="navbarSupportedContent"> <p>Category 1</p>
<ul class="navbar-nav me-auto mb-2 mb-lg-0"> <li>
<li class="nav-item"> <div class="block">item</div>
<a class="nav-link active" aria-current="page" href="/htmlexamples/index.html">Home</a> </li>
</li> <li>
<li class="nav-item"> <div class="block">item</div>
<a class="nav-link" aria-current="page" href="/htmlexamples/courses.html">Courses</a> </li>
</li> <li>
<li class="nav-item"> <div class="block">item</div>
<a class="nav-link" href="/htmlexamples/core.html">About us</a> </li>
</li> </ul>
</ul> </div>
</div>
<div class="col">
<div class="block">
<ul>
<p>Category 2</p>
<li>
<div class="block">item</div>
</li>
<li>
<div class="block">item</div>
</li>
<li>
<div class="block">item</div>
</li>
</ul>
</div>
</div>
<div class="col">
<div class="block">
<ul>
<p>Category 3</p>
<li>
<div class="block">item</div>
</li>
<li>
<div class="block">item</div>
</li>
<li>
<div class="block">item</div>
</li>
</ul>
</div>
</div>
</div>
</div> </div>
</div> </body>
</nav> </html>
</header>
<div class="container">
<div class="row upper text-center mb-4" style="min-height: 4rem;">
<p class="justify-content-center">Some header about courses</p>
</div>
<div class="row categories">
<div class="col">
<div class="block"><ul>
<p>Category 1</p>
<li>
<div class="block">item</div>
</li>
<li>
<div class="block">item</div>
</li>
<li>
<div class="block">item</div>
</li>
</ul></div>
</div>
<div class="col">
<div class="block"><ul>
<p>Category 2</p>
<li>
<div class="block">item</div>
</li>
<li>
<div class="block">item</div>
</li>
<li>
<div class="block">item</div>
</li>
</ul></div>
</div>
<div class="col">
<div class="block"><ul>
<p>Category 3</p>
<li>
<div class="block">item</div>
</li>
<li>
<div class="block">item</div>
</li>
<li>
<div class="block">item</div>
</li>
</ul></div>
</div>
</div>
</div>
</body>

View File

@ -31,7 +31,7 @@ type Client interface {
ListEducationalProducts( ListEducationalProducts(
ctx context.Context, ctx context.Context,
params ListEducationProductsParams, params ListEducationProductsParams,
) (result listEducationProductsResponse, err error) ) (result ListEducationProductsResponse, err error)
ListEducationalProductsFilterCount( ListEducationalProductsFilterCount(
ctx context.Context, ctx context.Context,
params ListEducationProductsParams, params ListEducationProductsParams,
@ -177,7 +177,7 @@ type listEducationProductsRequest struct {
SortDirection string `json:"sortDirection"` SortDirection string `json:"sortDirection"`
} }
type listEducationProductsResponse struct { type ListEducationProductsResponse struct {
Items []Course `json:"items"` Items []Course `json:"items"`
Organizations map[string]Organization `json:"organizations"` Organizations map[string]Organization `json:"organizations"`
@ -188,7 +188,7 @@ type listEducationProductsResponse struct {
func (c *client) ListEducationalProducts( func (c *client) ListEducationalProducts(
ctx context.Context, ctx context.Context,
params ListEducationProductsParams, params ListEducationProductsParams,
) (result listEducationProductsResponse, err error) { ) (result ListEducationProductsResponse, err error) {
const urlPath = "/v1/education/products" const urlPath = "/v1/education/products"
const defaultLimit = 1 const defaultLimit = 1
const defaultSortProp = "advertising.position" const defaultSortProp = "advertising.position"

View File

@ -0,0 +1,208 @@
// Code generated by mockery v2.42.1. DO NOT EDIT.
package mocks
import (
context "context"
sravni "git.loyso.art/frx/kurious/internal/common/client/sravni"
mock "github.com/stretchr/testify/mock"
)
// Client is an autogenerated mock type for the Client type
type Client struct {
mock.Mock
}
type Client_Expecter struct {
mock *mock.Mock
}
func (_m *Client) EXPECT() *Client_Expecter {
return &Client_Expecter{mock: &_m.Mock}
}
// GetMainPageState provides a mock function with given fields:
func (_m *Client) GetMainPageState() (*sravni.PageState, error) {
ret := _m.Called()
if len(ret) == 0 {
panic("no return value specified for GetMainPageState")
}
var r0 *sravni.PageState
var r1 error
if rf, ok := ret.Get(0).(func() (*sravni.PageState, error)); ok {
return rf()
}
if rf, ok := ret.Get(0).(func() *sravni.PageState); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*sravni.PageState)
}
}
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Client_GetMainPageState_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetMainPageState'
type Client_GetMainPageState_Call struct {
*mock.Call
}
// GetMainPageState is a helper method to define mock.On call
func (_e *Client_Expecter) GetMainPageState() *Client_GetMainPageState_Call {
return &Client_GetMainPageState_Call{Call: _e.mock.On("GetMainPageState")}
}
func (_c *Client_GetMainPageState_Call) Run(run func()) *Client_GetMainPageState_Call {
_c.Call.Run(func(args mock.Arguments) {
run()
})
return _c
}
func (_c *Client_GetMainPageState_Call) Return(_a0 *sravni.PageState, _a1 error) *Client_GetMainPageState_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *Client_GetMainPageState_Call) RunAndReturn(run func() (*sravni.PageState, error)) *Client_GetMainPageState_Call {
_c.Call.Return(run)
return _c
}
// ListEducationalProducts provides a mock function with given fields: ctx, params
func (_m *Client) ListEducationalProducts(ctx context.Context, params sravni.ListEducationProductsParams) (sravni.ListEducationProductsResponse, error) {
ret := _m.Called(ctx, params)
if len(ret) == 0 {
panic("no return value specified for ListEducationalProducts")
}
var r0 sravni.ListEducationProductsResponse
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, sravni.ListEducationProductsParams) (sravni.ListEducationProductsResponse, error)); ok {
return rf(ctx, params)
}
if rf, ok := ret.Get(0).(func(context.Context, sravni.ListEducationProductsParams) sravni.ListEducationProductsResponse); ok {
r0 = rf(ctx, params)
} else {
r0 = ret.Get(0).(sravni.ListEducationProductsResponse)
}
if rf, ok := ret.Get(1).(func(context.Context, sravni.ListEducationProductsParams) error); ok {
r1 = rf(ctx, params)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Client_ListEducationalProducts_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListEducationalProducts'
type Client_ListEducationalProducts_Call struct {
*mock.Call
}
// ListEducationalProducts is a helper method to define mock.On call
// - ctx context.Context
// - params sravni.ListEducationProductsParams
func (_e *Client_Expecter) ListEducationalProducts(ctx interface{}, params interface{}) *Client_ListEducationalProducts_Call {
return &Client_ListEducationalProducts_Call{Call: _e.mock.On("ListEducationalProducts", ctx, params)}
}
func (_c *Client_ListEducationalProducts_Call) Run(run func(ctx context.Context, params sravni.ListEducationProductsParams)) *Client_ListEducationalProducts_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(sravni.ListEducationProductsParams))
})
return _c
}
func (_c *Client_ListEducationalProducts_Call) Return(result sravni.ListEducationProductsResponse, err error) *Client_ListEducationalProducts_Call {
_c.Call.Return(result, err)
return _c
}
func (_c *Client_ListEducationalProducts_Call) RunAndReturn(run func(context.Context, sravni.ListEducationProductsParams) (sravni.ListEducationProductsResponse, error)) *Client_ListEducationalProducts_Call {
_c.Call.Return(run)
return _c
}
// ListEducationalProductsFilterCount provides a mock function with given fields: ctx, params
func (_m *Client) ListEducationalProductsFilterCount(ctx context.Context, params sravni.ListEducationProductsParams) (sravni.ProductsFilterCount, error) {
ret := _m.Called(ctx, params)
if len(ret) == 0 {
panic("no return value specified for ListEducationalProductsFilterCount")
}
var r0 sravni.ProductsFilterCount
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, sravni.ListEducationProductsParams) (sravni.ProductsFilterCount, error)); ok {
return rf(ctx, params)
}
if rf, ok := ret.Get(0).(func(context.Context, sravni.ListEducationProductsParams) sravni.ProductsFilterCount); ok {
r0 = rf(ctx, params)
} else {
r0 = ret.Get(0).(sravni.ProductsFilterCount)
}
if rf, ok := ret.Get(1).(func(context.Context, sravni.ListEducationProductsParams) error); ok {
r1 = rf(ctx, params)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Client_ListEducationalProductsFilterCount_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListEducationalProductsFilterCount'
type Client_ListEducationalProductsFilterCount_Call struct {
*mock.Call
}
// ListEducationalProductsFilterCount is a helper method to define mock.On call
// - ctx context.Context
// - params sravni.ListEducationProductsParams
func (_e *Client_Expecter) ListEducationalProductsFilterCount(ctx interface{}, params interface{}) *Client_ListEducationalProductsFilterCount_Call {
return &Client_ListEducationalProductsFilterCount_Call{Call: _e.mock.On("ListEducationalProductsFilterCount", ctx, params)}
}
func (_c *Client_ListEducationalProductsFilterCount_Call) Run(run func(ctx context.Context, params sravni.ListEducationProductsParams)) *Client_ListEducationalProductsFilterCount_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(sravni.ListEducationProductsParams))
})
return _c
}
func (_c *Client_ListEducationalProductsFilterCount_Call) Return(result sravni.ProductsFilterCount, err error) *Client_ListEducationalProductsFilterCount_Call {
_c.Call.Return(result, err)
return _c
}
func (_c *Client_ListEducationalProductsFilterCount_Call) RunAndReturn(run func(context.Context, sravni.ListEducationProductsParams) (sravni.ProductsFilterCount, error)) *Client_ListEducationalProductsFilterCount_Call {
_c.Call.Return(run)
return _c
}
// NewClient creates a new instance of Client. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewClient(t interface {
mock.TestingT
Cleanup(func())
}) *Client {
mock := &Client{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@ -12,6 +12,6 @@ func (NoopClient) GetMainPageState() (*PageState, error) {
return nil, errors.ErrNotImplemented return nil, errors.ErrNotImplemented
} }
func (NoopClient) ListEducationalProducts(context.Context, ListEducationProductsParams) (listEducationProductsResponse, error) { func (NoopClient) ListEducationalProducts(context.Context, ListEducationProductsParams) (ListEducationProductsResponse, error) {
return listEducationProductsResponse{}, errors.ErrNotImplemented return ListEducationProductsResponse{}, errors.ErrNotImplemented
} }

View File

@ -1,5 +1,7 @@
package adapters package adapters
import "strings"
type domainer[T any] interface { type domainer[T any] interface {
AsDomain() T AsDomain() T
} }
@ -7,3 +9,16 @@ type domainer[T any] interface {
func asDomainFunc[T any, U domainer[T]](in U) (out T) { func asDomainFunc[T any, U domainer[T]](in U) (out T) {
return in.AsDomain() return in.AsDomain()
} }
func joinColumns(columns []string) string {
return strings.Join(columns, ",")
}
func namedArgColumns(columns []string) string {
out := make([]string, len(columns))
for i, col := range columns {
out[i] = ":" + col
}
return joinColumns(out)
}

View File

@ -21,7 +21,7 @@ var (
"courses_count", "courses_count",
} }
learningCategoryColumnsStr = strings.Join(learningCategoryColumns, ",") learningCategoryColumnsStr = joinColumns(learningCategoryColumns)
) )
type learningCategoryDB struct { type learningCategoryDB struct {

View File

@ -76,6 +76,7 @@ func (s *sqliteLearningCategoriesRepositorySuite) TestUpsert() {
repo := s.connection.LearningCategory() repo := s.connection.LearningCategory()
gotCategory, err := repo.Get(s.ctx, categoryID) gotCategory, err := repo.Get(s.ctx, categoryID)
s.ErrorIs(err, domain.ErrNotFound) s.ErrorIs(err, domain.ErrNotFound)
s.Empty(gotCategory)
createdCategory := domain.LearningCategory{ createdCategory := domain.LearningCategory{
ID: categoryID, ID: categoryID,

View File

@ -1,34 +0,0 @@
package adapters
import (
"database/sql"
"time"
"git.loyso.art/frx/kurious/internal/kurious/domain"
)
type organizationDB struct {
ID string `db:"id"`
ExternalID sql.NullString `db:"external_id"`
Alias string `db:"alias"`
Name string `db:"name"`
Site string `db:"site"`
Logo string `db:"logo"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
DeletedAt sql.NullTime `db:"deleted_at"`
}
func (o *organizationDB) AsDomain() domain.Organization {
return domain.Organization{
ID: o.ID,
ExternalID: nullStringAsDomain(o.ExternalID),
Alias: o.Alias,
Name: o.Name,
Site: o.Site,
LogoLink: o.Logo,
CreatedAt: o.CreatedAt,
UpdatedAt: o.UpdatedAt,
DeletedAt: nullTimeAsDomain(o.DeletedAt),
}
}

View File

@ -0,0 +1,156 @@
package adapters
import (
"context"
"database/sql"
"errors"
"fmt"
"log/slog"
"time"
"git.loyso.art/frx/kurious/internal/kurious/domain"
"github.com/jmoiron/sqlx"
)
var (
organizationColumns = []string{
"id",
"external_id",
"alias",
"name",
"site",
"logo",
"created_at",
"updated_at",
"deleted_at",
}
organizationColumnsStr = joinColumns(organizationColumns)
organizationColumnsArgsStr = namedArgColumns(organizationColumns)
)
type organizationDB struct {
ID string `db:"id"`
ExternalID sql.NullString `db:"external_id"`
Alias string `db:"alias"`
Name string `db:"name"`
Site string `db:"site"`
Logo string `db:"logo"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
DeletedAt sql.NullTime `db:"deleted_at"`
}
func (o *organizationDB) AsDomain() domain.Organization {
return domain.Organization{
ID: o.ID,
ExternalID: nullStringAsDomain(o.ExternalID),
Alias: o.Alias,
Name: o.Name,
Site: o.Site,
LogoLink: o.Logo,
CreatedAt: o.CreatedAt,
UpdatedAt: o.UpdatedAt,
DeletedAt: nullTimeAsDomain(o.DeletedAt),
}
}
func (o *organizationDB) FromDomain(in domain.Organization) {
*o = organizationDB{
ID: in.ID,
ExternalID: nullableValueAsString(in.ExternalID),
Alias: in.Alias,
Name: in.Name,
Site: in.Site,
Logo: in.LogoLink,
CreatedAt: in.CreatedAt,
UpdatedAt: in.UpdatedAt,
DeletedAt: nullableValueAsTime(in.DeletedAt),
}
}
func (c *sqliteConnection) Organization() domain.OrganizationRepository {
return &sqliteOrganizationRepository{
db: c.db,
log: c.log.With("repository", "organization"),
}
}
type sqliteOrganizationRepository struct {
db *sqlx.DB
log *slog.Logger
}
func (r *sqliteOrganizationRepository) Get(ctx context.Context, params domain.GetOrganizationParams) (out domain.Organization, err error) {
const queryTemplate = "SELECT %s FROM organizations WHERE 1=1"
query := fmt.Sprintf(queryTemplate, organizationColumnsStr)
args := make([]any, 0, 2)
if params.ID.Valid() {
args = append(args, params.ID.Value())
query += " AND id = ?"
}
if params.ExternalID.Valid() {
args = append(args, params.ExternalID.Value())
query += " AND external_id = ?"
}
var orgdb organizationDB
err = r.db.GetContext(ctx, &orgdb, query, args...)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return out, domain.ErrNotFound
}
return out, fmt.Errorf("executing query: %w", err)
}
return orgdb.AsDomain(), nil
}
func (r *sqliteOrganizationRepository) Create(ctx context.Context, params domain.CreateOrganizationParams) (out domain.Organization, err error) {
const queryTemplate = `INSERT INTO organizations (%[1]s) VALUES (%[2]s) RETURNING %[1]s`
query := fmt.Sprintf(queryTemplate, organizationColumnsStr, organizationColumnsArgsStr)
stmt, err := r.db.PrepareNamedContext(ctx, query)
if err != nil {
return out, fmt.Errorf("preparing statement: %w", err)
}
var orgdb organizationDB
now := time.Now().UTC().Truncate(time.Second)
err = stmt.GetContext(ctx, &orgdb, organizationDB{
ID: params.ID,
ExternalID: nullableValueAsString(params.ExternalID),
Alias: params.Alias,
Name: params.Name,
Site: params.Site,
Logo: params.LogoLink,
CreatedAt: now,
UpdatedAt: now,
DeletedAt: sql.NullTime{},
})
if err != nil {
return out, fmt.Errorf("executing query: %w", err)
}
return orgdb.AsDomain(), nil
}
func (r *sqliteOrganizationRepository) Delete(ctx context.Context, id string) error {
const query = `DELETE FROM organizations WHERE id = ?`
result, err := r.db.ExecContext(ctx, query, id)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return domain.ErrNotFound
}
return fmt.Errorf("executing query: %w", err)
}
affected, _ := result.RowsAffected()
if affected == 0 {
return domain.ErrNotFound
}
return nil
}

View File

@ -0,0 +1,71 @@
package adapters
import (
"testing"
"git.loyso.art/frx/kurious/internal/common/nullable"
"git.loyso.art/frx/kurious/internal/kurious/domain"
"github.com/stretchr/testify/suite"
)
func TestSqilteOrganizations(t *testing.T) {
suite.Run(t, new(sqliteOrganzationRepositorySuite))
}
type sqliteOrganzationRepositorySuite struct {
sqliteBaseSuite
}
func (s *sqliteOrganzationRepositorySuite) TearDownTest() {
_ = s.connection.db.MustExecContext(s.ctx, "DELETE FROM organizations")
}
func (s *sqliteOrganzationRepositorySuite) TestGet() {
var orgdb organizationDB
err := s.connection.db.GetContext(
s.ctx, &orgdb,
`INSERT INTO organizations (`+organizationColumnsStr+`)`+
` VALUES ("test-id", "test-ext-id", "test-alias", "test-name", "test-site", "test-logo", CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL)`+
` RETURNING `+organizationColumnsStr,
)
s.NoError(err)
expectedOrganization := orgdb.AsDomain()
getParams := domain.GetOrganizationParams{
ID: nullable.NewValue(orgdb.ID),
}
gotOrganization, err := s.connection.Organization().Get(s.ctx, getParams)
s.NoError(err)
s.Equal(expectedOrganization, gotOrganization)
}
func (s *sqliteOrganzationRepositorySuite) TestCreate() {
expectedOrganization := domain.Organization{
ID: "test-id",
ExternalID: nullable.NewValue("test-ext-id"),
Alias: "test-alias",
Name: "test-name",
Site: "test-site",
LogoLink: "test-logo",
}
gotOrganization, err := s.connection.Organization().Create(s.ctx, domain.CreateOrganizationParams{
ID: expectedOrganization.ID,
ExternalID: expectedOrganization.ExternalID,
Alias: expectedOrganization.Alias,
Name: expectedOrganization.Name,
Site: expectedOrganization.Site,
LogoLink: expectedOrganization.LogoLink,
})
s.NoError(err)
s.NotEmpty(gotOrganization.CreatedAt)
s.NotEmpty(gotOrganization.UpdatedAt)
s.Empty(gotOrganization.DeletedAt)
expectedOrganization.CreatedAt = gotOrganization.CreatedAt
expectedOrganization.UpdatedAt = gotOrganization.UpdatedAt
s.Equal(expectedOrganization, gotOrganization)
}

View File

@ -0,0 +1,534 @@
// Code generated by mockery v2.42.1. DO NOT EDIT.
package mocks
import (
context "context"
domain "git.loyso.art/frx/kurious/internal/kurious/domain"
mock "github.com/stretchr/testify/mock"
)
// CourseRepository is an autogenerated mock type for the CourseRepository type
type CourseRepository struct {
mock.Mock
}
type CourseRepository_Expecter struct {
mock *mock.Mock
}
func (_m *CourseRepository) EXPECT() *CourseRepository_Expecter {
return &CourseRepository_Expecter{mock: &_m.Mock}
}
// Create provides a mock function with given fields: _a0, _a1
func (_m *CourseRepository) Create(_a0 context.Context, _a1 domain.CreateCourseParams) (domain.Course, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for Create")
}
var r0 domain.Course
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, domain.CreateCourseParams) (domain.Course, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(context.Context, domain.CreateCourseParams) domain.Course); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Get(0).(domain.Course)
}
if rf, ok := ret.Get(1).(func(context.Context, domain.CreateCourseParams) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// CourseRepository_Create_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Create'
type CourseRepository_Create_Call struct {
*mock.Call
}
// Create is a helper method to define mock.On call
// - _a0 context.Context
// - _a1 domain.CreateCourseParams
func (_e *CourseRepository_Expecter) Create(_a0 interface{}, _a1 interface{}) *CourseRepository_Create_Call {
return &CourseRepository_Create_Call{Call: _e.mock.On("Create", _a0, _a1)}
}
func (_c *CourseRepository_Create_Call) Run(run func(_a0 context.Context, _a1 domain.CreateCourseParams)) *CourseRepository_Create_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(domain.CreateCourseParams))
})
return _c
}
func (_c *CourseRepository_Create_Call) Return(_a0 domain.Course, _a1 error) *CourseRepository_Create_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *CourseRepository_Create_Call) RunAndReturn(run func(context.Context, domain.CreateCourseParams) (domain.Course, error)) *CourseRepository_Create_Call {
_c.Call.Return(run)
return _c
}
// CreateBatch provides a mock function with given fields: _a0, _a1
func (_m *CourseRepository) CreateBatch(_a0 context.Context, _a1 ...domain.CreateCourseParams) error {
_va := make([]interface{}, len(_a1))
for _i := range _a1 {
_va[_i] = _a1[_i]
}
var _ca []interface{}
_ca = append(_ca, _a0)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
if len(ret) == 0 {
panic("no return value specified for CreateBatch")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, ...domain.CreateCourseParams) error); ok {
r0 = rf(_a0, _a1...)
} else {
r0 = ret.Error(0)
}
return r0
}
// CourseRepository_CreateBatch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'CreateBatch'
type CourseRepository_CreateBatch_Call struct {
*mock.Call
}
// CreateBatch is a helper method to define mock.On call
// - _a0 context.Context
// - _a1 ...domain.CreateCourseParams
func (_e *CourseRepository_Expecter) CreateBatch(_a0 interface{}, _a1 ...interface{}) *CourseRepository_CreateBatch_Call {
return &CourseRepository_CreateBatch_Call{Call: _e.mock.On("CreateBatch",
append([]interface{}{_a0}, _a1...)...)}
}
func (_c *CourseRepository_CreateBatch_Call) Run(run func(_a0 context.Context, _a1 ...domain.CreateCourseParams)) *CourseRepository_CreateBatch_Call {
_c.Call.Run(func(args mock.Arguments) {
variadicArgs := make([]domain.CreateCourseParams, len(args)-1)
for i, a := range args[1:] {
if a != nil {
variadicArgs[i] = a.(domain.CreateCourseParams)
}
}
run(args[0].(context.Context), variadicArgs...)
})
return _c
}
func (_c *CourseRepository_CreateBatch_Call) Return(_a0 error) *CourseRepository_CreateBatch_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *CourseRepository_CreateBatch_Call) RunAndReturn(run func(context.Context, ...domain.CreateCourseParams) error) *CourseRepository_CreateBatch_Call {
_c.Call.Return(run)
return _c
}
// Delete provides a mock function with given fields: ctx, id
func (_m *CourseRepository) Delete(ctx context.Context, id string) error {
ret := _m.Called(ctx, id)
if len(ret) == 0 {
panic("no return value specified for Delete")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
r0 = rf(ctx, id)
} else {
r0 = ret.Error(0)
}
return r0
}
// CourseRepository_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete'
type CourseRepository_Delete_Call struct {
*mock.Call
}
// Delete is a helper method to define mock.On call
// - ctx context.Context
// - id string
func (_e *CourseRepository_Expecter) Delete(ctx interface{}, id interface{}) *CourseRepository_Delete_Call {
return &CourseRepository_Delete_Call{Call: _e.mock.On("Delete", ctx, id)}
}
func (_c *CourseRepository_Delete_Call) Run(run func(ctx context.Context, id string)) *CourseRepository_Delete_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(string))
})
return _c
}
func (_c *CourseRepository_Delete_Call) Return(_a0 error) *CourseRepository_Delete_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *CourseRepository_Delete_Call) RunAndReturn(run func(context.Context, string) error) *CourseRepository_Delete_Call {
_c.Call.Return(run)
return _c
}
// Get provides a mock function with given fields: ctx, id
func (_m *CourseRepository) Get(ctx context.Context, id string) (domain.Course, error) {
ret := _m.Called(ctx, id)
if len(ret) == 0 {
panic("no return value specified for Get")
}
var r0 domain.Course
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string) (domain.Course, error)); ok {
return rf(ctx, id)
}
if rf, ok := ret.Get(0).(func(context.Context, string) domain.Course); ok {
r0 = rf(ctx, id)
} else {
r0 = ret.Get(0).(domain.Course)
}
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
r1 = rf(ctx, id)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// CourseRepository_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get'
type CourseRepository_Get_Call struct {
*mock.Call
}
// Get is a helper method to define mock.On call
// - ctx context.Context
// - id string
func (_e *CourseRepository_Expecter) Get(ctx interface{}, id interface{}) *CourseRepository_Get_Call {
return &CourseRepository_Get_Call{Call: _e.mock.On("Get", ctx, id)}
}
func (_c *CourseRepository_Get_Call) Run(run func(ctx context.Context, id string)) *CourseRepository_Get_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(string))
})
return _c
}
func (_c *CourseRepository_Get_Call) Return(_a0 domain.Course, _a1 error) *CourseRepository_Get_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *CourseRepository_Get_Call) RunAndReturn(run func(context.Context, string) (domain.Course, error)) *CourseRepository_Get_Call {
_c.Call.Return(run)
return _c
}
// GetByExternalID provides a mock function with given fields: ctx, id
func (_m *CourseRepository) GetByExternalID(ctx context.Context, id string) (domain.Course, error) {
ret := _m.Called(ctx, id)
if len(ret) == 0 {
panic("no return value specified for GetByExternalID")
}
var r0 domain.Course
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string) (domain.Course, error)); ok {
return rf(ctx, id)
}
if rf, ok := ret.Get(0).(func(context.Context, string) domain.Course); ok {
r0 = rf(ctx, id)
} else {
r0 = ret.Get(0).(domain.Course)
}
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
r1 = rf(ctx, id)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// CourseRepository_GetByExternalID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetByExternalID'
type CourseRepository_GetByExternalID_Call struct {
*mock.Call
}
// GetByExternalID is a helper method to define mock.On call
// - ctx context.Context
// - id string
func (_e *CourseRepository_Expecter) GetByExternalID(ctx interface{}, id interface{}) *CourseRepository_GetByExternalID_Call {
return &CourseRepository_GetByExternalID_Call{Call: _e.mock.On("GetByExternalID", ctx, id)}
}
func (_c *CourseRepository_GetByExternalID_Call) Run(run func(ctx context.Context, id string)) *CourseRepository_GetByExternalID_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(string))
})
return _c
}
func (_c *CourseRepository_GetByExternalID_Call) Return(_a0 domain.Course, _a1 error) *CourseRepository_GetByExternalID_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *CourseRepository_GetByExternalID_Call) RunAndReturn(run func(context.Context, string) (domain.Course, error)) *CourseRepository_GetByExternalID_Call {
_c.Call.Return(run)
return _c
}
// List provides a mock function with given fields: _a0, _a1
func (_m *CourseRepository) List(_a0 context.Context, _a1 domain.ListCoursesParams) (domain.ListCoursesResult, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for List")
}
var r0 domain.ListCoursesResult
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, domain.ListCoursesParams) (domain.ListCoursesResult, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(context.Context, domain.ListCoursesParams) domain.ListCoursesResult); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Get(0).(domain.ListCoursesResult)
}
if rf, ok := ret.Get(1).(func(context.Context, domain.ListCoursesParams) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// CourseRepository_List_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'List'
type CourseRepository_List_Call struct {
*mock.Call
}
// List is a helper method to define mock.On call
// - _a0 context.Context
// - _a1 domain.ListCoursesParams
func (_e *CourseRepository_Expecter) List(_a0 interface{}, _a1 interface{}) *CourseRepository_List_Call {
return &CourseRepository_List_Call{Call: _e.mock.On("List", _a0, _a1)}
}
func (_c *CourseRepository_List_Call) Run(run func(_a0 context.Context, _a1 domain.ListCoursesParams)) *CourseRepository_List_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(domain.ListCoursesParams))
})
return _c
}
func (_c *CourseRepository_List_Call) Return(_a0 domain.ListCoursesResult, _a1 error) *CourseRepository_List_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *CourseRepository_List_Call) RunAndReturn(run func(context.Context, domain.ListCoursesParams) (domain.ListCoursesResult, error)) *CourseRepository_List_Call {
_c.Call.Return(run)
return _c
}
// ListCourseThematics provides a mock function with given fields: _a0, _a1
func (_m *CourseRepository) ListCourseThematics(_a0 context.Context, _a1 domain.ListCourseThematicsParams) (domain.ListCourseThematicsResult, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for ListCourseThematics")
}
var r0 domain.ListCourseThematicsResult
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, domain.ListCourseThematicsParams) (domain.ListCourseThematicsResult, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(context.Context, domain.ListCourseThematicsParams) domain.ListCourseThematicsResult); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Get(0).(domain.ListCourseThematicsResult)
}
if rf, ok := ret.Get(1).(func(context.Context, domain.ListCourseThematicsParams) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// CourseRepository_ListCourseThematics_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListCourseThematics'
type CourseRepository_ListCourseThematics_Call struct {
*mock.Call
}
// ListCourseThematics is a helper method to define mock.On call
// - _a0 context.Context
// - _a1 domain.ListCourseThematicsParams
func (_e *CourseRepository_Expecter) ListCourseThematics(_a0 interface{}, _a1 interface{}) *CourseRepository_ListCourseThematics_Call {
return &CourseRepository_ListCourseThematics_Call{Call: _e.mock.On("ListCourseThematics", _a0, _a1)}
}
func (_c *CourseRepository_ListCourseThematics_Call) Run(run func(_a0 context.Context, _a1 domain.ListCourseThematicsParams)) *CourseRepository_ListCourseThematics_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(domain.ListCourseThematicsParams))
})
return _c
}
func (_c *CourseRepository_ListCourseThematics_Call) Return(_a0 domain.ListCourseThematicsResult, _a1 error) *CourseRepository_ListCourseThematics_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *CourseRepository_ListCourseThematics_Call) RunAndReturn(run func(context.Context, domain.ListCourseThematicsParams) (domain.ListCourseThematicsResult, error)) *CourseRepository_ListCourseThematics_Call {
_c.Call.Return(run)
return _c
}
// ListLearningTypes provides a mock function with given fields: _a0
func (_m *CourseRepository) ListLearningTypes(_a0 context.Context) (domain.ListLearningTypeResult, error) {
ret := _m.Called(_a0)
if len(ret) == 0 {
panic("no return value specified for ListLearningTypes")
}
var r0 domain.ListLearningTypeResult
var r1 error
if rf, ok := ret.Get(0).(func(context.Context) (domain.ListLearningTypeResult, error)); ok {
return rf(_a0)
}
if rf, ok := ret.Get(0).(func(context.Context) domain.ListLearningTypeResult); ok {
r0 = rf(_a0)
} else {
r0 = ret.Get(0).(domain.ListLearningTypeResult)
}
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// CourseRepository_ListLearningTypes_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListLearningTypes'
type CourseRepository_ListLearningTypes_Call struct {
*mock.Call
}
// ListLearningTypes is a helper method to define mock.On call
// - _a0 context.Context
func (_e *CourseRepository_Expecter) ListLearningTypes(_a0 interface{}) *CourseRepository_ListLearningTypes_Call {
return &CourseRepository_ListLearningTypes_Call{Call: _e.mock.On("ListLearningTypes", _a0)}
}
func (_c *CourseRepository_ListLearningTypes_Call) Run(run func(_a0 context.Context)) *CourseRepository_ListLearningTypes_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context))
})
return _c
}
func (_c *CourseRepository_ListLearningTypes_Call) Return(_a0 domain.ListLearningTypeResult, _a1 error) *CourseRepository_ListLearningTypes_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *CourseRepository_ListLearningTypes_Call) RunAndReturn(run func(context.Context) (domain.ListLearningTypeResult, error)) *CourseRepository_ListLearningTypes_Call {
_c.Call.Return(run)
return _c
}
// UpdateCourseDescription provides a mock function with given fields: ctx, id, description
func (_m *CourseRepository) UpdateCourseDescription(ctx context.Context, id string, description string) error {
ret := _m.Called(ctx, id, description)
if len(ret) == 0 {
panic("no return value specified for UpdateCourseDescription")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok {
r0 = rf(ctx, id, description)
} else {
r0 = ret.Error(0)
}
return r0
}
// CourseRepository_UpdateCourseDescription_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'UpdateCourseDescription'
type CourseRepository_UpdateCourseDescription_Call struct {
*mock.Call
}
// UpdateCourseDescription is a helper method to define mock.On call
// - ctx context.Context
// - id string
// - description string
func (_e *CourseRepository_Expecter) UpdateCourseDescription(ctx interface{}, id interface{}, description interface{}) *CourseRepository_UpdateCourseDescription_Call {
return &CourseRepository_UpdateCourseDescription_Call{Call: _e.mock.On("UpdateCourseDescription", ctx, id, description)}
}
func (_c *CourseRepository_UpdateCourseDescription_Call) Run(run func(ctx context.Context, id string, description string)) *CourseRepository_UpdateCourseDescription_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(string), args[2].(string))
})
return _c
}
func (_c *CourseRepository_UpdateCourseDescription_Call) Return(_a0 error) *CourseRepository_UpdateCourseDescription_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *CourseRepository_UpdateCourseDescription_Call) RunAndReturn(run func(context.Context, string, string) error) *CourseRepository_UpdateCourseDescription_Call {
_c.Call.Return(run)
return _c
}
// NewCourseRepository creates a new instance of CourseRepository. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewCourseRepository(t interface {
mock.TestingT
Cleanup(func())
}) *CourseRepository {
mock := &CourseRepository{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@ -0,0 +1,199 @@
// Code generated by mockery v2.42.1. DO NOT EDIT.
package mocks
import (
context "context"
domain "git.loyso.art/frx/kurious/internal/kurious/domain"
mock "github.com/stretchr/testify/mock"
)
// LearningCategoryRepository is an autogenerated mock type for the LearningCategoryRepository type
type LearningCategoryRepository struct {
mock.Mock
}
type LearningCategoryRepository_Expecter struct {
mock *mock.Mock
}
func (_m *LearningCategoryRepository) EXPECT() *LearningCategoryRepository_Expecter {
return &LearningCategoryRepository_Expecter{mock: &_m.Mock}
}
// Get provides a mock function with given fields: _a0, _a1
func (_m *LearningCategoryRepository) Get(_a0 context.Context, _a1 string) (domain.LearningCategory, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for Get")
}
var r0 domain.LearningCategory
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, string) (domain.LearningCategory, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(context.Context, string) domain.LearningCategory); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Get(0).(domain.LearningCategory)
}
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// LearningCategoryRepository_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get'
type LearningCategoryRepository_Get_Call struct {
*mock.Call
}
// Get is a helper method to define mock.On call
// - _a0 context.Context
// - _a1 string
func (_e *LearningCategoryRepository_Expecter) Get(_a0 interface{}, _a1 interface{}) *LearningCategoryRepository_Get_Call {
return &LearningCategoryRepository_Get_Call{Call: _e.mock.On("Get", _a0, _a1)}
}
func (_c *LearningCategoryRepository_Get_Call) Run(run func(_a0 context.Context, _a1 string)) *LearningCategoryRepository_Get_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(string))
})
return _c
}
func (_c *LearningCategoryRepository_Get_Call) Return(_a0 domain.LearningCategory, _a1 error) *LearningCategoryRepository_Get_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *LearningCategoryRepository_Get_Call) RunAndReturn(run func(context.Context, string) (domain.LearningCategory, error)) *LearningCategoryRepository_Get_Call {
_c.Call.Return(run)
return _c
}
// List provides a mock function with given fields: _a0
func (_m *LearningCategoryRepository) List(_a0 context.Context) ([]domain.LearningCategory, error) {
ret := _m.Called(_a0)
if len(ret) == 0 {
panic("no return value specified for List")
}
var r0 []domain.LearningCategory
var r1 error
if rf, ok := ret.Get(0).(func(context.Context) ([]domain.LearningCategory, error)); ok {
return rf(_a0)
}
if rf, ok := ret.Get(0).(func(context.Context) []domain.LearningCategory); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]domain.LearningCategory)
}
}
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// LearningCategoryRepository_List_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'List'
type LearningCategoryRepository_List_Call struct {
*mock.Call
}
// List is a helper method to define mock.On call
// - _a0 context.Context
func (_e *LearningCategoryRepository_Expecter) List(_a0 interface{}) *LearningCategoryRepository_List_Call {
return &LearningCategoryRepository_List_Call{Call: _e.mock.On("List", _a0)}
}
func (_c *LearningCategoryRepository_List_Call) Run(run func(_a0 context.Context)) *LearningCategoryRepository_List_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context))
})
return _c
}
func (_c *LearningCategoryRepository_List_Call) Return(_a0 []domain.LearningCategory, _a1 error) *LearningCategoryRepository_List_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *LearningCategoryRepository_List_Call) RunAndReturn(run func(context.Context) ([]domain.LearningCategory, error)) *LearningCategoryRepository_List_Call {
_c.Call.Return(run)
return _c
}
// Upsert provides a mock function with given fields: _a0, _a1
func (_m *LearningCategoryRepository) Upsert(_a0 context.Context, _a1 domain.LearningCategory) error {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for Upsert")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, domain.LearningCategory) error); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Error(0)
}
return r0
}
// LearningCategoryRepository_Upsert_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Upsert'
type LearningCategoryRepository_Upsert_Call struct {
*mock.Call
}
// Upsert is a helper method to define mock.On call
// - _a0 context.Context
// - _a1 domain.LearningCategory
func (_e *LearningCategoryRepository_Expecter) Upsert(_a0 interface{}, _a1 interface{}) *LearningCategoryRepository_Upsert_Call {
return &LearningCategoryRepository_Upsert_Call{Call: _e.mock.On("Upsert", _a0, _a1)}
}
func (_c *LearningCategoryRepository_Upsert_Call) Run(run func(_a0 context.Context, _a1 domain.LearningCategory)) *LearningCategoryRepository_Upsert_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(domain.LearningCategory))
})
return _c
}
func (_c *LearningCategoryRepository_Upsert_Call) Return(_a0 error) *LearningCategoryRepository_Upsert_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *LearningCategoryRepository_Upsert_Call) RunAndReturn(run func(context.Context, domain.LearningCategory) error) *LearningCategoryRepository_Upsert_Call {
_c.Call.Return(run)
return _c
}
// NewLearningCategoryRepository creates a new instance of LearningCategoryRepository. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewLearningCategoryRepository(t interface {
mock.TestingT
Cleanup(func())
}) *LearningCategoryRepository {
mock := &LearningCategoryRepository{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@ -0,0 +1,198 @@
// Code generated by mockery v2.42.1. DO NOT EDIT.
package mocks
import (
context "context"
domain "git.loyso.art/frx/kurious/internal/kurious/domain"
mock "github.com/stretchr/testify/mock"
)
// OrganizationRepository is an autogenerated mock type for the OrganizationRepository type
type OrganizationRepository struct {
mock.Mock
}
type OrganizationRepository_Expecter struct {
mock *mock.Mock
}
func (_m *OrganizationRepository) EXPECT() *OrganizationRepository_Expecter {
return &OrganizationRepository_Expecter{mock: &_m.Mock}
}
// Create provides a mock function with given fields: _a0, _a1
func (_m *OrganizationRepository) Create(_a0 context.Context, _a1 domain.CreateOrganizationParams) (domain.Organization, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for Create")
}
var r0 domain.Organization
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, domain.CreateOrganizationParams) (domain.Organization, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(context.Context, domain.CreateOrganizationParams) domain.Organization); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Get(0).(domain.Organization)
}
if rf, ok := ret.Get(1).(func(context.Context, domain.CreateOrganizationParams) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// OrganizationRepository_Create_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Create'
type OrganizationRepository_Create_Call struct {
*mock.Call
}
// Create is a helper method to define mock.On call
// - _a0 context.Context
// - _a1 domain.CreateOrganizationParams
func (_e *OrganizationRepository_Expecter) Create(_a0 interface{}, _a1 interface{}) *OrganizationRepository_Create_Call {
return &OrganizationRepository_Create_Call{Call: _e.mock.On("Create", _a0, _a1)}
}
func (_c *OrganizationRepository_Create_Call) Run(run func(_a0 context.Context, _a1 domain.CreateOrganizationParams)) *OrganizationRepository_Create_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(domain.CreateOrganizationParams))
})
return _c
}
func (_c *OrganizationRepository_Create_Call) Return(_a0 domain.Organization, _a1 error) *OrganizationRepository_Create_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *OrganizationRepository_Create_Call) RunAndReturn(run func(context.Context, domain.CreateOrganizationParams) (domain.Organization, error)) *OrganizationRepository_Create_Call {
_c.Call.Return(run)
return _c
}
// Delete provides a mock function with given fields: ctx, id
func (_m *OrganizationRepository) Delete(ctx context.Context, id string) error {
ret := _m.Called(ctx, id)
if len(ret) == 0 {
panic("no return value specified for Delete")
}
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
r0 = rf(ctx, id)
} else {
r0 = ret.Error(0)
}
return r0
}
// OrganizationRepository_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete'
type OrganizationRepository_Delete_Call struct {
*mock.Call
}
// Delete is a helper method to define mock.On call
// - ctx context.Context
// - id string
func (_e *OrganizationRepository_Expecter) Delete(ctx interface{}, id interface{}) *OrganizationRepository_Delete_Call {
return &OrganizationRepository_Delete_Call{Call: _e.mock.On("Delete", ctx, id)}
}
func (_c *OrganizationRepository_Delete_Call) Run(run func(ctx context.Context, id string)) *OrganizationRepository_Delete_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(string))
})
return _c
}
func (_c *OrganizationRepository_Delete_Call) Return(_a0 error) *OrganizationRepository_Delete_Call {
_c.Call.Return(_a0)
return _c
}
func (_c *OrganizationRepository_Delete_Call) RunAndReturn(run func(context.Context, string) error) *OrganizationRepository_Delete_Call {
_c.Call.Return(run)
return _c
}
// Get provides a mock function with given fields: _a0, _a1
func (_m *OrganizationRepository) Get(_a0 context.Context, _a1 domain.GetOrganizationParams) (domain.Organization, error) {
ret := _m.Called(_a0, _a1)
if len(ret) == 0 {
panic("no return value specified for Get")
}
var r0 domain.Organization
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, domain.GetOrganizationParams) (domain.Organization, error)); ok {
return rf(_a0, _a1)
}
if rf, ok := ret.Get(0).(func(context.Context, domain.GetOrganizationParams) domain.Organization); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Get(0).(domain.Organization)
}
if rf, ok := ret.Get(1).(func(context.Context, domain.GetOrganizationParams) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// OrganizationRepository_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get'
type OrganizationRepository_Get_Call struct {
*mock.Call
}
// Get is a helper method to define mock.On call
// - _a0 context.Context
// - _a1 domain.GetOrganizationParams
func (_e *OrganizationRepository_Expecter) Get(_a0 interface{}, _a1 interface{}) *OrganizationRepository_Get_Call {
return &OrganizationRepository_Get_Call{Call: _e.mock.On("Get", _a0, _a1)}
}
func (_c *OrganizationRepository_Get_Call) Run(run func(_a0 context.Context, _a1 domain.GetOrganizationParams)) *OrganizationRepository_Get_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(domain.GetOrganizationParams))
})
return _c
}
func (_c *OrganizationRepository_Get_Call) Return(_a0 domain.Organization, _a1 error) *OrganizationRepository_Get_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *OrganizationRepository_Get_Call) RunAndReturn(run func(context.Context, domain.GetOrganizationParams) (domain.Organization, error)) *OrganizationRepository_Get_Call {
_c.Call.Return(run)
return _c
}
// NewOrganizationRepository creates a new instance of OrganizationRepository. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
func NewOrganizationRepository(t interface {
mock.TestingT
Cleanup(func())
}) *OrganizationRepository {
mock := &OrganizationRepository{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@ -76,6 +76,11 @@ type CourseRepository interface {
UpdateCourseDescription(ctx context.Context, id string, description string) error UpdateCourseDescription(ctx context.Context, id string, description string) error
} }
type GetOrganizationParams struct {
ID nullable.Value[string]
ExternalID nullable.Value[string]
}
type CreateOrganizationParams struct { type CreateOrganizationParams struct {
ID string ID string
ExternalID nullable.Value[string] ExternalID nullable.Value[string]
@ -88,7 +93,7 @@ type CreateOrganizationParams struct {
//go:generate mockery --name OrganizationRepository //go:generate mockery --name OrganizationRepository
type OrganizationRepository interface { type OrganizationRepository interface {
Get(ctx context.Context) (Organization, error) Get(context.Context, GetOrganizationParams) (Organization, error)
Create(context.Context, CreateOrganizationParams) (Organization, error) Create(context.Context, CreateOrganizationParams) (Organization, error)
Delete(ctx context.Context, id string) error Delete(ctx context.Context, id string) error
} }

View File

@ -4,7 +4,7 @@ CREATE TABLE learning_categories (
courses_count INT NOT NULL DEFAULT 0 courses_count INT NOT NULL DEFAULT 0
); );
create table organization ( create table organizations (
id TEXT PRIMARY KEY, id TEXT PRIMARY KEY,
external_id TEXT NULL, external_id TEXT NULL,
alias TEXT NOT NULL, alias TEXT NOT NULL,
@ -16,4 +16,4 @@ create table organization (
deleted_at DATETIME NULL deleted_at DATETIME NULL
); );
CREATE INDEX idx_organization_external_id ON organization (external_id); CREATE INDEX idx_organization_external_id ON organizations (external_id);