feat: items page

This commit is contained in:
trev 2025-03-14 14:14:35 -04:00
parent f58b3cc18d
commit 50c8d18df9
17 changed files with 2384 additions and 54 deletions

View File

@ -18,5 +18,7 @@
--color-subtext-1: #bac2de;
--color-text: #cdd6f4;
--color-sky: #89dceb;
--color-red: #f38ba8;
}

View File

@ -0,0 +1,296 @@
// @generated by protoc-gen-es v2.2.3 with parameter "target=ts"
// @generated from file item/v1/item.proto (package item.v1, syntax proto3)
/* eslint-disable */
import type { GenFile, GenMessage, GenService } from "@bufbuild/protobuf/codegenv1";
import { fileDesc, messageDesc, serviceDesc } from "@bufbuild/protobuf/codegenv1";
import type { Timestamp } from "@bufbuild/protobuf/wkt";
import { file_google_protobuf_timestamp } from "@bufbuild/protobuf/wkt";
import type { Message } from "@bufbuild/protobuf";
/**
* Describes the file item/v1/item.proto.
*/
export const file_item_v1_item: GenFile = /*@__PURE__*/
fileDesc("ChJpdGVtL3YxL2l0ZW0ucHJvdG8SB2l0ZW0udjEinAEKBEl0ZW0SDwoCaWQYASABKA1IAIgBARIMCgRuYW1lGAIgASgJEhMKC2Rlc2NyaXB0aW9uGAMgASgJEg0KBXByaWNlGAQgASgCEhAKCHF1YW50aXR5GAUgASgNEi4KBWFkZGVkGAYgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcEgBiAEBQgUKA19pZEIICgZfYWRkZWQiHAoOR2V0SXRlbVJlcXVlc3QSCgoCaWQYASABKA0iLgoPR2V0SXRlbVJlc3BvbnNlEhsKBGl0ZW0YASABKAsyDS5pdGVtLnYxLkl0ZW0i3wEKD0dldEl0ZW1zUmVxdWVzdBIuCgVzdGFydBgBIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBIAIgBARIsCgNlbmQYAiABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wSAGIAQESEwoGZmlsdGVyGAMgASgJSAKIAQESEgoFbGltaXQYBCABKA1IA4gBARITCgZvZmZzZXQYBSABKA1IBIgBAUIICgZfc3RhcnRCBgoEX2VuZEIJCgdfZmlsdGVyQggKBl9saW1pdEIJCgdfb2Zmc2V0Ij8KEEdldEl0ZW1zUmVzcG9uc2USHAoFaXRlbXMYASADKAsyDS5pdGVtLnYxLkl0ZW0SDQoFY291bnQYAiABKAQiMAoRQ3JlYXRlSXRlbVJlcXVlc3QSGwoEaXRlbRgBIAEoCzINLml0ZW0udjEuSXRlbSIxChJDcmVhdGVJdGVtUmVzcG9uc2USGwoEaXRlbRgBIAEoCzINLml0ZW0udjEuSXRlbSIwChFVcGRhdGVJdGVtUmVxdWVzdBIbCgRpdGVtGAEgASgLMg0uaXRlbS52MS5JdGVtIjEKElVwZGF0ZUl0ZW1SZXNwb25zZRIbCgRpdGVtGAEgASgLMg0uaXRlbS52MS5JdGVtIh8KEURlbGV0ZUl0ZW1SZXF1ZXN0EgoKAmlkGAEgASgNIhQKEkRlbGV0ZUl0ZW1SZXNwb25zZTLrAgoLSXRlbVNlcnZpY2USPgoHR2V0SXRlbRIXLml0ZW0udjEuR2V0SXRlbVJlcXVlc3QaGC5pdGVtLnYxLkdldEl0ZW1SZXNwb25zZSIAEkEKCEdldEl0ZW1zEhguaXRlbS52MS5HZXRJdGVtc1JlcXVlc3QaGS5pdGVtLnYxLkdldEl0ZW1zUmVzcG9uc2UiABJHCgpDcmVhdGVJdGVtEhouaXRlbS52MS5DcmVhdGVJdGVtUmVxdWVzdBobLml0ZW0udjEuQ3JlYXRlSXRlbVJlc3BvbnNlIgASRwoKVXBkYXRlSXRlbRIaLml0ZW0udjEuVXBkYXRlSXRlbVJlcXVlc3QaGy5pdGVtLnYxLlVwZGF0ZUl0ZW1SZXNwb25zZSIAEkcKCkRlbGV0ZUl0ZW0SGi5pdGVtLnYxLkRlbGV0ZUl0ZW1SZXF1ZXN0GhsuaXRlbS52MS5EZWxldGVJdGVtUmVzcG9uc2UiAEKdAQoLY29tLml0ZW0udjFCCUl0ZW1Qcm90b1ABWkZnaXRodWIuY29tL3Nwb3RkZW1vNC90cmV2c3RhY2svc2VydmVyL2ludGVybmFsL3NlcnZpY2VzL2l0ZW0vdjE7aXRlbXYxogIDSVhYqgIHSXRlbS5WMcoCB0l0ZW1cVjHiAhNJdGVtXFYxXEdQQk1ldGFkYXRh6gIISXRlbTo6VjFiBnByb3RvMw", [file_google_protobuf_timestamp]);
/**
* @generated from message item.v1.Item
*/
export type Item = Message<"item.v1.Item"> & {
/**
* @generated from field: optional uint32 id = 1;
*/
id?: number;
/**
* @generated from field: string name = 2;
*/
name: string;
/**
* @generated from field: string description = 3;
*/
description: string;
/**
* @generated from field: float price = 4;
*/
price: number;
/**
* @generated from field: uint32 quantity = 5;
*/
quantity: number;
/**
* @generated from field: optional google.protobuf.Timestamp added = 6;
*/
added?: Timestamp;
};
/**
* Describes the message item.v1.Item.
* Use `create(ItemSchema)` to create a new message.
*/
export const ItemSchema: GenMessage<Item> = /*@__PURE__*/
messageDesc(file_item_v1_item, 0);
/**
* @generated from message item.v1.GetItemRequest
*/
export type GetItemRequest = Message<"item.v1.GetItemRequest"> & {
/**
* @generated from field: uint32 id = 1;
*/
id: number;
};
/**
* Describes the message item.v1.GetItemRequest.
* Use `create(GetItemRequestSchema)` to create a new message.
*/
export const GetItemRequestSchema: GenMessage<GetItemRequest> = /*@__PURE__*/
messageDesc(file_item_v1_item, 1);
/**
* @generated from message item.v1.GetItemResponse
*/
export type GetItemResponse = Message<"item.v1.GetItemResponse"> & {
/**
* @generated from field: item.v1.Item item = 1;
*/
item?: Item;
};
/**
* Describes the message item.v1.GetItemResponse.
* Use `create(GetItemResponseSchema)` to create a new message.
*/
export const GetItemResponseSchema: GenMessage<GetItemResponse> = /*@__PURE__*/
messageDesc(file_item_v1_item, 2);
/**
* @generated from message item.v1.GetItemsRequest
*/
export type GetItemsRequest = Message<"item.v1.GetItemsRequest"> & {
/**
* @generated from field: optional google.protobuf.Timestamp start = 1;
*/
start?: Timestamp;
/**
* @generated from field: optional google.protobuf.Timestamp end = 2;
*/
end?: Timestamp;
/**
* @generated from field: optional string filter = 3;
*/
filter?: string;
/**
* @generated from field: optional uint32 limit = 4;
*/
limit?: number;
/**
* @generated from field: optional uint32 offset = 5;
*/
offset?: number;
};
/**
* Describes the message item.v1.GetItemsRequest.
* Use `create(GetItemsRequestSchema)` to create a new message.
*/
export const GetItemsRequestSchema: GenMessage<GetItemsRequest> = /*@__PURE__*/
messageDesc(file_item_v1_item, 3);
/**
* @generated from message item.v1.GetItemsResponse
*/
export type GetItemsResponse = Message<"item.v1.GetItemsResponse"> & {
/**
* @generated from field: repeated item.v1.Item items = 1;
*/
items: Item[];
/**
* @generated from field: uint64 count = 2;
*/
count: bigint;
};
/**
* Describes the message item.v1.GetItemsResponse.
* Use `create(GetItemsResponseSchema)` to create a new message.
*/
export const GetItemsResponseSchema: GenMessage<GetItemsResponse> = /*@__PURE__*/
messageDesc(file_item_v1_item, 4);
/**
* @generated from message item.v1.CreateItemRequest
*/
export type CreateItemRequest = Message<"item.v1.CreateItemRequest"> & {
/**
* @generated from field: item.v1.Item item = 1;
*/
item?: Item;
};
/**
* Describes the message item.v1.CreateItemRequest.
* Use `create(CreateItemRequestSchema)` to create a new message.
*/
export const CreateItemRequestSchema: GenMessage<CreateItemRequest> = /*@__PURE__*/
messageDesc(file_item_v1_item, 5);
/**
* @generated from message item.v1.CreateItemResponse
*/
export type CreateItemResponse = Message<"item.v1.CreateItemResponse"> & {
/**
* @generated from field: item.v1.Item item = 1;
*/
item?: Item;
};
/**
* Describes the message item.v1.CreateItemResponse.
* Use `create(CreateItemResponseSchema)` to create a new message.
*/
export const CreateItemResponseSchema: GenMessage<CreateItemResponse> = /*@__PURE__*/
messageDesc(file_item_v1_item, 6);
/**
* @generated from message item.v1.UpdateItemRequest
*/
export type UpdateItemRequest = Message<"item.v1.UpdateItemRequest"> & {
/**
* @generated from field: item.v1.Item item = 1;
*/
item?: Item;
};
/**
* Describes the message item.v1.UpdateItemRequest.
* Use `create(UpdateItemRequestSchema)` to create a new message.
*/
export const UpdateItemRequestSchema: GenMessage<UpdateItemRequest> = /*@__PURE__*/
messageDesc(file_item_v1_item, 7);
/**
* @generated from message item.v1.UpdateItemResponse
*/
export type UpdateItemResponse = Message<"item.v1.UpdateItemResponse"> & {
/**
* @generated from field: item.v1.Item item = 1;
*/
item?: Item;
};
/**
* Describes the message item.v1.UpdateItemResponse.
* Use `create(UpdateItemResponseSchema)` to create a new message.
*/
export const UpdateItemResponseSchema: GenMessage<UpdateItemResponse> = /*@__PURE__*/
messageDesc(file_item_v1_item, 8);
/**
* @generated from message item.v1.DeleteItemRequest
*/
export type DeleteItemRequest = Message<"item.v1.DeleteItemRequest"> & {
/**
* @generated from field: uint32 id = 1;
*/
id: number;
};
/**
* Describes the message item.v1.DeleteItemRequest.
* Use `create(DeleteItemRequestSchema)` to create a new message.
*/
export const DeleteItemRequestSchema: GenMessage<DeleteItemRequest> = /*@__PURE__*/
messageDesc(file_item_v1_item, 9);
/**
* @generated from message item.v1.DeleteItemResponse
*/
export type DeleteItemResponse = Message<"item.v1.DeleteItemResponse"> & {
};
/**
* Describes the message item.v1.DeleteItemResponse.
* Use `create(DeleteItemResponseSchema)` to create a new message.
*/
export const DeleteItemResponseSchema: GenMessage<DeleteItemResponse> = /*@__PURE__*/
messageDesc(file_item_v1_item, 10);
/**
* @generated from service item.v1.ItemService
*/
export const ItemService: GenService<{
/**
* @generated from rpc item.v1.ItemService.GetItem
*/
getItem: {
methodKind: "unary";
input: typeof GetItemRequestSchema;
output: typeof GetItemResponseSchema;
},
/**
* @generated from rpc item.v1.ItemService.GetItems
*/
getItems: {
methodKind: "unary";
input: typeof GetItemsRequestSchema;
output: typeof GetItemsResponseSchema;
},
/**
* @generated from rpc item.v1.ItemService.CreateItem
*/
createItem: {
methodKind: "unary";
input: typeof CreateItemRequestSchema;
output: typeof CreateItemResponseSchema;
},
/**
* @generated from rpc item.v1.ItemService.UpdateItem
*/
updateItem: {
methodKind: "unary";
input: typeof UpdateItemRequestSchema;
output: typeof UpdateItemResponseSchema;
},
/**
* @generated from rpc item.v1.ItemService.DeleteItem
*/
deleteItem: {
methodKind: "unary";
input: typeof DeleteItemRequestSchema;
output: typeof DeleteItemResponseSchema;
},
}> = /*@__PURE__*/
serviceDesc(file_item_v1_item, 0);

View File

@ -1,8 +1,8 @@
import { createConnectTransport } from "@connectrpc/connect-web"
import { Code, ConnectError, createClient } from "@connectrpc/connect"
import { Code, ConnectError, createClient, type Interceptor } from "@connectrpc/connect"
import { AuthService } from "$lib/services/user/v1/auth_pb";
import { UserService } from "$lib/services/user/v1/user_pb";
import type { Interceptor } from "@connectrpc/connect";
import { ItemService } from "$lib/services/item/v1/item_pb";
import { goto } from "$app/navigation";
const redirector: Interceptor = (next) => async (req) => {
@ -23,4 +23,5 @@ const transport = createConnectTransport({
});
export const AuthClient = createClient(AuthService, transport);
export const UserClient = createClient(UserService, transport);
export const UserClient = createClient(UserService, transport);
export const ItemClient = createClient(ItemService, transport);

View File

@ -0,0 +1,51 @@
<script lang="ts">
import { Dialog } from 'bits-ui';
import { fade } from 'svelte/transition';
import type { Snippet } from 'svelte';
let {
trigger,
content,
open = $bindable(false)
}: { trigger: Snippet; content: Snippet; open: boolean } = $props();
</script>
<Dialog.Root bind:open>
<Dialog.Trigger>
{@render trigger()}
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay forceMount>
{#snippet child({ props, open })}
{#if open}
<div
{...props}
transition:fade={{
duration: 100
}}
>
<div class="fixed inset-0 z-50 mt-[50px] bg-black/50 transition-all"></div>
</div>
{/if}
{/snippet}
</Dialog.Overlay>
<Dialog.Content forceMount>
{#snippet child({ props, open })}
{#if open}
<div
{...props}
transition:fade={{
duration: 100
}}
>
<div
class="bg-mantle border-surface-0 fixed inset-0 left-[50%] top-[50%] z-50 size-fit w-96 -translate-x-1/2 -translate-y-1/2 transform overflow-y-auto rounded-xl border pb-1 drop-shadow-md"
>
{@render content()}
</div>
</div>
{/if}
{/snippet}
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>

View File

@ -34,7 +34,7 @@
},
{
name: 'Items',
href: '/items',
href: '/items/',
icon: LayoutList
},
{

View File

@ -0,0 +1,332 @@
<script lang="ts">
import { ItemClient } from '$lib/transport';
import { Plus, Trash, Pencil } from '@lucide/svelte';
import { timestampFromDate, timestampDate } from '@bufbuild/protobuf/wkt';
import { Dialog, Button } from 'bits-ui';
import { fade } from 'svelte/transition';
import { toast } from 'svelte-sonner';
import { ConnectError } from '@connectrpc/connect';
import Modal from '$lib/ui/Modal.svelte';
import { SvelteMap } from 'svelte/reactivity';
// Config
let limit: number = $state(10);
let offset: number = $state(0);
let start = $state(new Date(new Date().setDate(new Date().getDate() - 1)));
let end = $state(new Date());
let filter = $state('');
// Items
let items = $state(getItems());
let count: number = $state(0);
// Open
let addedOpen = $state(false);
let deletesOpen: SvelteMap<number, boolean> = new SvelteMap();
let editsOpen: SvelteMap<number, boolean> = new SvelteMap();
async function getItems() {
return await ItemClient.getItems({
limit: limit,
offset: offset,
start: timestampFromDate(start),
end: timestampFromDate(end),
filter: filter
}).then((resp) => {
count = Number(resp.count);
return resp.items;
});
}
async function updateItems() {
let i = getItems();
i.then(() => {
items = i;
});
}
</script>
<div
class="border-surface-0 bg-mantle mx-4 mt-2 overflow-x-auto rounded border-x border-t drop-shadow-md"
>
<table class="w-full table-auto border-collapse text-left rtl:text-right">
<thead>
<tr class="border-surface-0 border-b">
<th scope="col" class="text-subtext-0 px-6 py-3 font-normal">Added</th>
<th scope="col" class="text-subtext-0 px-6 py-3 font-normal">Name</th>
<th scope="col" class="text-subtext-0 px-6 py-3 font-normal">Description</th>
<th scope="col" class="text-subtext-0 px-6 py-3 font-normal">Price</th>
<th scope="col" class="text-subtext-0 px-6 py-3 font-normal">Quantity</th>
<th class="w-0"></th>
</tr>
</thead>
<tbody>
{#await items}
<tr class="border-surface-0 border-b">
<td class="px-6 py-3"><div class="bg-surface-2 m-2 h-3 animate-pulse rounded"></div></td>
<td class="px-6 py-3"><div class="bg-surface-2 m-2 h-3 animate-pulse rounded"></div></td>
<td class="px-6 py-3"><div class="bg-surface-2 m-2 h-3 animate-pulse rounded"></div></td>
<td class="px-6 py-3"><div class="bg-surface-2 m-2 h-3 animate-pulse rounded"></div></td>
<td class="px-6 py-3"><div class="bg-surface-2 m-2 h-3 animate-pulse rounded"></div></td>
</tr>
{:then items}
{#each items as item}
<tr class="border-surface-0 border-b">
<td class="px-6 py-3">
{item.added ? timestampDate(item.added).toLocaleString() : ''}
</td>
<td class="px-6 py-3">{item.name}</td>
<td class="px-6 py-3">{item.description}</td>
<td class="px-6 py-3">{item.price}</td>
<td class="px-6 py-3">{item.quantity}</td>
<td class="pr-2">
<div class="flex gap-2">
<Modal bind:open={
() => editsOpen.has(item.id!) ? editsOpen.get(item.id!)! : editsOpen.set(item.id!, false) && editsOpen.get(item.id!)!,
(value) => editsOpen.set(item.id!, value)
}>
{#snippet trigger()}
<button
class="bg-text text-crust hover:brightness-120 block cursor-pointer rounded p-2 drop-shadow-md"
>
<Pencil />
</button>
{/snippet}
{#snippet content()}
<h1 class="border-surface-0 border-b py-3 text-center text-xl font-bold">
Edit {item.name}
</h1>
<form
onsubmit={async (e) => {
e.preventDefault();
const form = e.target as HTMLFormElement;
const formData = new FormData(form);
const name = formData.get('name')?.toString();
const description = formData.get('description')?.toString();
const price = formData.get('price')?.toString();
const quantity = formData.get('quantity')?.toString();
try {
const response = await ItemClient.updateItem({
item: {
id: item.id,
name: name,
description: description,
price: parseFloat(price ?? '0'),
quantity: parseInt(quantity ?? '0')
}
});
if (response.item && item.id) {
toast.success(`item "${name}" saved`);
editsOpen.set(item.id, false)
await updateItems();
}
} catch (err) {
const error = ConnectError.from(err);
toast.error(error.rawMessage);
}
}}
>
<div class="flex flex-col gap-4 p-3">
<div class="flex flex-col gap-1">
<label for="name" class="text-sm">Name</label>
<input
id="name"
name="name"
type="text"
class="border-surface-0 rounded border p-2 text-sm"
value={item.name}
/>
</div>
<div class="flex flex-col gap-1">
<label for="description" class="text-sm">Description</label>
<input
id="description"
name="description"
type="text"
class="border-surface-0 rounded border p-2 text-sm"
value={item.description}
/>
</div>
<div class="flex flex-col gap-1">
<label for="price" class="text-sm">Price</label>
<input
id="price"
name="price"
type="number"
class="border-surface-0 rounded border p-2 text-sm"
value={item.price}
/>
</div>
<div class="flex flex-col gap-1">
<label for="quantity" class="text-sm">Quantity</label>
<input
id="quantity"
name="quantity"
type="number"
class="border-surface-0 rounded border p-2 text-sm"
value={item.quantity}
/>
</div>
<Button.Root
type="submit"
class="bg-sky text-crust hover:brightness-120 w-20 cursor-pointer rounded p-2 px-4 text-sm transition-all"
>
Submit
</Button.Root>
</div>
</form>
{/snippet}
</Modal>
<Modal bind:open={
() => deletesOpen.has(item.id!) ? deletesOpen.get(item.id!)! : deletesOpen.set(item.id!, false) && deletesOpen.get(item.id!)!,
(value) => deletesOpen.set(item.id!, value)
}>
{#snippet trigger()}
<button
class="bg-red text-crust hover:brightness-120 block cursor-pointer rounded p-2 drop-shadow-md"
>
<Trash />
</button>
{/snippet}
{#snippet content()}
<h1 class="border-surface-0 border-b py-3 text-center text-xl font-bold">
Delete {item.name}
</h1>
<form
onsubmit={async (e) => {
e.preventDefault();
try {
await ItemClient.deleteItem({
id: item.id
});
toast.success(`item "${item.name}" deleted`);
deletesOpen.set(item.id!, false);
await updateItems();
} catch (err) {
const error = ConnectError.from(err);
toast.error(error.rawMessage);
}
}}
>
<div class="flex flex-col gap-4 p-3">
<span class="text-center">Are you sure you want to delete "{item.name}"?</span
>
<div class="flex justify-center gap-4">
<Button.Root
type="submit"
class="bg-sky text-crust hover:brightness-120 cursor-pointer rounded p-2 px-4 text-sm transition-all"
>
Confirm
</Button.Root>
</div>
</div>
</form>
{/snippet}
</Modal>
</div>
</td>
</tr>
{/each}
{/await}
</tbody>
</table>
</div>
<div class="mx-4 mt-1 flex justify-end">
<Modal bind:open={addedOpen}>
{#snippet trigger()}
<button
class="bg-sky text-crust hover:brightness-120 cursor-pointer rounded p-2 px-4 drop-shadow-md"
>
<Plus />
</button>
{/snippet}
{#snippet content()}
<h1 class="border-surface-0 border-b py-3 text-center text-xl font-bold">Add Item</h1>
<form
onsubmit={async (e) => {
e.preventDefault();
const form = e.target as HTMLFormElement;
const formData = new FormData(form);
const name = formData.get('name')?.toString();
const description = formData.get('description')?.toString();
const price = formData.get('price')?.toString();
const quantity = formData.get('quantity')?.toString();
try {
const response = await ItemClient.createItem({
item: {
name: name,
description: description,
price: parseFloat(price ?? '0'),
quantity: parseInt(quantity ?? '0')
}
});
if (response.item) {
form.reset();
toast.success(`item "${name}" added`);
addedOpen = false;
await updateItems();
}
} catch (err) {
const error = ConnectError.from(err);
toast.error(error.rawMessage);
}
}}
>
<div class="flex flex-col gap-4 p-3">
<div class="flex flex-col gap-1">
<label for="name" class="text-sm">Name</label>
<input
id="name"
name="name"
type="text"
class="border-surface-0 rounded border p-2 text-sm"
/>
</div>
<div class="flex flex-col gap-1">
<label for="description" class="text-sm">Description</label>
<input
id="description"
name="description"
type="text"
class="border-surface-0 rounded border p-2 text-sm"
/>
</div>
<div class="flex flex-col gap-1">
<label for="price" class="text-sm">Price</label>
<input
id="price"
name="price"
type="number"
class="border-surface-0 rounded border p-2 text-sm"
/>
</div>
<div class="flex flex-col gap-1">
<label for="quantity" class="text-sm">Quantity</label>
<input
id="quantity"
name="quantity"
type="number"
class="border-surface-0 rounded border p-2 text-sm"
/>
</div>
<Button.Root
type="submit"
class="bg-sky text-crust hover:brightness-120 w-fit cursor-pointer rounded p-2 px-4 text-sm transition-all"
>
Submit
</Button.Root>
</div>
</form>
{/snippet}
</Modal>
</div>

View File

@ -15,50 +15,225 @@ components:
scheme: bearer
bearerFormat: JWT
schemas:
user.v1.LoginRequest:
google.protobuf.Timestamp:
type: string
format: date-time
description: |-
A Timestamp represents a point in time independent of any time zone or local
calendar, encoded as a count of seconds and fractions of seconds at
nanosecond resolution. The count is relative to an epoch at UTC midnight on
January 1, 1970, in the proleptic Gregorian calendar which extends the
Gregorian calendar backwards to year one.
All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
second table is needed for interpretation, using a [24-hour linear
smear](https://developers.google.com/time/smear).
The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
restricting to that range, we ensure that we can convert to and from [RFC
3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
# Examples
Example 1: Compute Timestamp from POSIX `time()`.
Timestamp timestamp;
timestamp.set_seconds(time(NULL));
timestamp.set_nanos(0);
Example 2: Compute Timestamp from POSIX `gettimeofday()`.
struct timeval tv;
gettimeofday(&tv, NULL);
Timestamp timestamp;
timestamp.set_seconds(tv.tv_sec);
timestamp.set_nanos(tv.tv_usec * 1000);
Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
// A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
// is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
Timestamp timestamp;
timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
long millis = System.currentTimeMillis();
Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
.setNanos((int) ((millis % 1000) * 1000000)).build();
Example 5: Compute Timestamp from Java `Instant.now()`.
Instant now = Instant.now();
Timestamp timestamp =
Timestamp.newBuilder().setSeconds(now.getEpochSecond())
.setNanos(now.getNano()).build();
Example 6: Compute Timestamp from current time in Python.
timestamp = Timestamp()
timestamp.GetCurrentTime()
# JSON Mapping
In JSON format, the Timestamp type is encoded as a string in the
[RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the
format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z"
where {year} is always expressed using four digits while {month}, {day},
{hour}, {min}, and {sec} are zero-padded to two digits each. The fractional
seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution),
are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone
is required. A proto3 JSON serializer should always use UTC (as indicated by
"Z") when printing the Timestamp type and a proto3 JSON parser should be
able to accept both UTC and other timezones (as indicated by an offset).
For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past
01:30 UTC on January 15, 2017.
In JavaScript, one can convert a Date object to this format using the
standard
[toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
method. In Python, a standard `datetime.datetime` object can be converted
to this format using
[`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with
the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use
the Joda Time's [`ISODateTimeFormat.dateTime()`](
http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()
) to obtain a formatter capable of generating timestamps in this format.
item.v1.CreateItemRequest:
type: object
properties:
username:
type: string
title: username
password:
type: string
title: password
title: LoginRequest
item:
title: item
$ref: '#/components/schemas/item.v1.Item'
title: CreateItemRequest
additionalProperties: false
user.v1.LoginResponse:
item.v1.CreateItemResponse:
type: object
properties:
token:
type: string
title: token
title: LoginResponse
item:
title: item
$ref: '#/components/schemas/item.v1.Item'
title: CreateItemResponse
additionalProperties: false
user.v1.LogoutRequest:
type: object
title: LogoutRequest
additionalProperties: false
user.v1.LogoutResponse:
type: object
title: LogoutResponse
additionalProperties: false
user.v1.SignUpRequest:
item.v1.DeleteItemRequest:
type: object
properties:
username:
type: string
title: username
password:
type: string
title: password
confirmPassword:
type: string
title: confirm_password
title: SignUpRequest
id:
type: integer
title: id
title: DeleteItemRequest
additionalProperties: false
user.v1.SignUpResponse:
item.v1.DeleteItemResponse:
type: object
title: SignUpResponse
title: DeleteItemResponse
additionalProperties: false
item.v1.GetItemRequest:
type: object
properties:
id:
type: integer
title: id
title: GetItemRequest
additionalProperties: false
item.v1.GetItemResponse:
type: object
properties:
item:
title: item
$ref: '#/components/schemas/item.v1.Item'
title: GetItemResponse
additionalProperties: false
item.v1.GetItemsRequest:
type: object
properties:
start:
title: start
nullable: true
$ref: '#/components/schemas/google.protobuf.Timestamp'
end:
title: end
nullable: true
$ref: '#/components/schemas/google.protobuf.Timestamp'
filter:
type: string
title: filter
nullable: true
limit:
type: integer
title: limit
nullable: true
offset:
type: integer
title: offset
nullable: true
title: GetItemsRequest
additionalProperties: false
item.v1.GetItemsResponse:
type: object
properties:
items:
type: array
items:
$ref: '#/components/schemas/item.v1.Item'
title: items
count:
type:
- integer
- string
title: count
format: int64
title: GetItemsResponse
additionalProperties: false
item.v1.Item:
type: object
properties:
id:
type: integer
title: id
nullable: true
name:
type: string
title: name
description:
type: string
title: description
price:
type: number
title: price
format: float
quantity:
type: integer
title: quantity
added:
title: added
nullable: true
$ref: '#/components/schemas/google.protobuf.Timestamp'
title: Item
additionalProperties: false
item.v1.UpdateItemRequest:
type: object
properties:
item:
title: item
$ref: '#/components/schemas/item.v1.Item'
title: UpdateItemRequest
additionalProperties: false
item.v1.UpdateItemResponse:
type: object
properties:
item:
title: item
$ref: '#/components/schemas/item.v1.Item'
title: UpdateItemResponse
additionalProperties: false
connect-protocol-version:
type: number
@ -117,6 +292,51 @@ components:
additionalProperties: true
additionalProperties: true
description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message.
user.v1.LoginRequest:
type: object
properties:
username:
type: string
title: username
password:
type: string
title: password
title: LoginRequest
additionalProperties: false
user.v1.LoginResponse:
type: object
properties:
token:
type: string
title: token
title: LoginResponse
additionalProperties: false
user.v1.LogoutRequest:
type: object
title: LogoutRequest
additionalProperties: false
user.v1.LogoutResponse:
type: object
title: LogoutResponse
additionalProperties: false
user.v1.SignUpRequest:
type: object
properties:
username:
type: string
title: username
password:
type: string
title: password
confirmPassword:
type: string
title: confirm_password
title: SignUpRequest
additionalProperties: false
user.v1.SignUpResponse:
type: object
title: SignUpResponse
additionalProperties: false
user.v1.APIKeyRequest:
type: object
properties:
@ -157,6 +377,181 @@ components:
security:
- bearerAuth: []
paths:
/item.v1.ItemService/GetItem:
post:
tags:
- item.v1.ItemService
summary: GetItem
operationId: item.v1.ItemService.GetItem
parameters:
- name: Connect-Protocol-Version
in: header
required: true
schema:
$ref: '#/components/schemas/connect-protocol-version'
- name: Connect-Timeout-Ms
in: header
schema:
$ref: '#/components/schemas/connect-timeout-header'
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/item.v1.GetItemRequest'
required: true
responses:
default:
description: Error
content:
application/json:
schema:
$ref: '#/components/schemas/connect.error'
"200":
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/item.v1.GetItemResponse'
/item.v1.ItemService/GetItems:
post:
tags:
- item.v1.ItemService
summary: GetItems
operationId: item.v1.ItemService.GetItems
parameters:
- name: Connect-Protocol-Version
in: header
required: true
schema:
$ref: '#/components/schemas/connect-protocol-version'
- name: Connect-Timeout-Ms
in: header
schema:
$ref: '#/components/schemas/connect-timeout-header'
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/item.v1.GetItemsRequest'
required: true
responses:
default:
description: Error
content:
application/json:
schema:
$ref: '#/components/schemas/connect.error'
"200":
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/item.v1.GetItemsResponse'
/item.v1.ItemService/CreateItem:
post:
tags:
- item.v1.ItemService
summary: CreateItem
operationId: item.v1.ItemService.CreateItem
parameters:
- name: Connect-Protocol-Version
in: header
required: true
schema:
$ref: '#/components/schemas/connect-protocol-version'
- name: Connect-Timeout-Ms
in: header
schema:
$ref: '#/components/schemas/connect-timeout-header'
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/item.v1.CreateItemRequest'
required: true
responses:
default:
description: Error
content:
application/json:
schema:
$ref: '#/components/schemas/connect.error'
"200":
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/item.v1.CreateItemResponse'
/item.v1.ItemService/UpdateItem:
post:
tags:
- item.v1.ItemService
summary: UpdateItem
operationId: item.v1.ItemService.UpdateItem
parameters:
- name: Connect-Protocol-Version
in: header
required: true
schema:
$ref: '#/components/schemas/connect-protocol-version'
- name: Connect-Timeout-Ms
in: header
schema:
$ref: '#/components/schemas/connect-timeout-header'
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/item.v1.UpdateItemRequest'
required: true
responses:
default:
description: Error
content:
application/json:
schema:
$ref: '#/components/schemas/connect.error'
"200":
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/item.v1.UpdateItemResponse'
/item.v1.ItemService/DeleteItem:
post:
tags:
- item.v1.ItemService
summary: DeleteItem
operationId: item.v1.ItemService.DeleteItem
parameters:
- name: Connect-Protocol-Version
in: header
required: true
schema:
$ref: '#/components/schemas/connect-protocol-version'
- name: Connect-Timeout-Ms
in: header
schema:
$ref: '#/components/schemas/connect-timeout-header'
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/item.v1.DeleteItemRequest'
required: true
responses:
default:
description: Error
content:
application/json:
schema:
$ref: '#/components/schemas/connect.error'
"200":
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/item.v1.DeleteItemResponse'
/user.v1.AuthService/Login:
post:
tags:
@ -333,5 +728,6 @@ paths:
schema:
$ref: '#/components/schemas/user.v1.APIKeyResponse'
tags:
- name: item.v1.ItemService
- name: user.v1.AuthService
- name: user.v1.UserService

View File

@ -68,9 +68,6 @@
# Svelte frontend
nodejs_22
# Openapi gen
openapi-generator-cli
# Helper scripts
(writeShellApplication {
name = "run";
@ -91,6 +88,8 @@
wait $P1
wait $P2
wait $P3
kill $P1 $P2 $P3
'';
})

60
proto/item/v1/item.proto Normal file
View File

@ -0,0 +1,60 @@
syntax = "proto3";
package item.v1;
import "google/protobuf/timestamp.proto";
message Item {
optional uint32 id = 1;
string name = 2;
string description = 3;
float price = 4;
uint32 quantity = 5;
optional google.protobuf.Timestamp added = 6;
}
service ItemService {
rpc GetItem (GetItemRequest) returns (GetItemResponse) {}
rpc GetItems (GetItemsRequest) returns (GetItemsResponse) {}
rpc CreateItem (CreateItemRequest) returns (CreateItemResponse) {}
rpc UpdateItem (UpdateItemRequest) returns (UpdateItemResponse) {}
rpc DeleteItem (DeleteItemRequest) returns (DeleteItemResponse) {}
}
message GetItemRequest {
uint32 id = 1;
}
message GetItemResponse {
Item item = 1;
}
message GetItemsRequest {
optional google.protobuf.Timestamp start = 1;
optional google.protobuf.Timestamp end = 2;
optional string filter = 3;
optional uint32 limit = 4;
optional uint32 offset = 5;
}
message GetItemsResponse {
repeated Item items = 1;
uint64 count = 2;
}
message CreateItemRequest {
Item item = 1;
}
message CreateItemResponse {
Item item = 1;
}
message UpdateItemRequest {
Item item = 1;
}
message UpdateItemResponse {
Item item = 1;
}
message DeleteItemRequest {
uint32 id = 1;
}
message DeleteItemResponse {}

View File

@ -6,7 +6,7 @@ import (
)
func Migrate(db *gorm.DB) error {
err := db.AutoMigrate(&models.User{})
err := db.AutoMigrate(&models.User{}, &models.Item{})
if err != nil {
return err
}

View File

@ -0,0 +1,171 @@
package handlers
import (
"context"
"errors"
"fmt"
"net/http"
"time"
"connectrpc.com/connect"
"github.com/spotdemo4/trevstack/server/internal/interceptors"
"github.com/spotdemo4/trevstack/server/internal/models"
itemv1 "github.com/spotdemo4/trevstack/server/internal/services/item/v1"
"github.com/spotdemo4/trevstack/server/internal/services/item/v1/itemv1connect"
"gorm.io/gorm"
)
type ItemHandler struct {
db *gorm.DB
key []byte
}
func (h *ItemHandler) GetItem(ctx context.Context, req *connect.Request[itemv1.GetItemRequest]) (*connect.Response[itemv1.GetItemResponse], error) {
userid, ok := interceptors.UserFromContext(ctx)
if !ok {
return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("unauthenticated"))
}
// Get item
item := models.Item{}
if err := h.db.First(&item, "id = ? AND user_id = ?", req.Msg.Id, userid).Error; err != nil {
return nil, connect.NewError(connect.CodeNotFound, err)
}
res := connect.NewResponse(&itemv1.GetItemResponse{
Item: item.ToConnectV1(),
})
return res, nil
}
func (h *ItemHandler) GetItems(ctx context.Context, req *connect.Request[itemv1.GetItemsRequest]) (*connect.Response[itemv1.GetItemsResponse], error) {
userid, ok := interceptors.UserFromContext(ctx)
if !ok {
return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("unauthenticated"))
}
// Filters
sql := h.db.Where("user_id = ?", userid)
if req.Msg.Start != nil {
sql = sql.Where("added >= ?", req.Msg.Start.AsTime())
}
if req.Msg.End != nil {
sql = sql.Where("added <= ?", req.Msg.End.AsTime())
}
if req.Msg.Filter != nil {
sql = sql.Where("name LIKE ?", fmt.Sprintf("%%%s%%", *req.Msg.Filter))
}
// Uncounted filters
sqlu := sql.Session(&gorm.Session{})
if req.Msg.Limit != nil {
sqlu = sqlu.Limit(int(*req.Msg.Limit))
}
if req.Msg.Offset != nil {
sqlu = sqlu.Offset(int(*req.Msg.Offset))
}
// Get items & count
items := []models.Item{}
var count int64
if err := sqlu.Order("added desc").Find(&items).Error; err != nil {
return nil, connect.NewError(connect.CodeNotFound, err)
}
if err := sql.Model(&items).Count(&count).Error; err != nil {
return nil, connect.NewError(connect.CodeInternal, err)
}
// Convert to connect v1 items
resItems := []*itemv1.Item{}
for _, item := range items {
resItems = append(resItems, item.ToConnectV1())
}
res := connect.NewResponse(&itemv1.GetItemsResponse{
Items: resItems,
Count: uint64(count),
})
return res, nil
}
func (h *ItemHandler) CreateItem(ctx context.Context, req *connect.Request[itemv1.CreateItemRequest]) (*connect.Response[itemv1.CreateItemResponse], error) {
userid, ok := interceptors.UserFromContext(ctx)
if !ok {
return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("unauthenticated"))
}
// Create item
item := models.Item{
Name: req.Msg.Item.Name,
Description: req.Msg.Item.Description,
Price: req.Msg.Item.Price,
Quantity: int(req.Msg.Item.Quantity),
Added: time.Now(),
UserID: uint(userid),
}
if err := h.db.Create(&item).Error; err != nil {
return nil, connect.NewError(connect.CodeInternal, err)
}
res := connect.NewResponse(&itemv1.CreateItemResponse{
Item: item.ToConnectV1(),
})
return res, nil
}
func (h *ItemHandler) UpdateItem(ctx context.Context, req *connect.Request[itemv1.UpdateItemRequest]) (*connect.Response[itemv1.UpdateItemResponse], error) {
userid, ok := interceptors.UserFromContext(ctx)
if !ok {
return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("unauthenticated"))
}
// Validate
if req.Msg.Item.Id == nil {
return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("id is required"))
}
// Update item
item := models.Item{
ID: *req.Msg.Item.Id,
Name: req.Msg.Item.Name,
Description: req.Msg.Item.Description,
Price: req.Msg.Item.Price,
Quantity: int(req.Msg.Item.Quantity),
UserID: uint(userid),
}
if err := h.db.Where("id = ? AND user_id = ?", req.Msg.Item.Id, userid).Updates(&item).Error; err != nil {
return nil, connect.NewError(connect.CodeInternal, err)
}
res := connect.NewResponse(&itemv1.UpdateItemResponse{
Item: item.ToConnectV1(),
})
return res, nil
}
func (h *ItemHandler) DeleteItem(ctx context.Context, req *connect.Request[itemv1.DeleteItemRequest]) (*connect.Response[itemv1.DeleteItemResponse], error) {
userid, ok := interceptors.UserFromContext(ctx)
if !ok {
return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("unauthenticated"))
}
// Delete item
if err := h.db.Delete(&models.Item{}, "id = ? AND user_id = ?", req.Msg.Id, userid).Error; err != nil {
return nil, connect.NewError(connect.CodeInternal, err)
}
res := connect.NewResponse(&itemv1.DeleteItemResponse{})
return res, nil
}
func NewItemHandler(db *gorm.DB, key string) (string, http.Handler) {
interceptors := connect.WithInterceptors(interceptors.NewAuthInterceptor(key))
return itemv1connect.NewItemServiceHandler(
&ItemHandler{
db: db,
key: []byte(key),
},
interceptors,
)
}

View File

@ -24,11 +24,12 @@ func WithAuthRedirect(next http.Handler, key string) http.Handler {
}
switch pathItems[1] {
case "auth":
next.ServeHTTP(w, r)
return
case "auth":
fallthrough
case "_app":
fallthrough
case "favicon.png":
next.ServeHTTP(w, r)
return

View File

@ -2,7 +2,7 @@ package interceptors
import (
"context"
"log"
"errors"
"sync"
"time"
@ -44,8 +44,11 @@ func (i *ratelimitInterceptor) WrapUnary(next connect.UnaryFunc) connect.UnaryFu
return next(ctx, req)
}
// Get ip
log.Println(req.Peer().Addr)
// Get user agent
limiter := i.getVisitor(req.Header().Get("User-Agent"))
if limiter.Allow() == false {
return nil, connect.NewError(connect.CodeResourceExhausted, errors.New("rate limit exceeded"))
}
return next(ctx, req)
})
@ -65,22 +68,25 @@ func (i *ratelimitInterceptor) WrapStreamingHandler(next connect.StreamingHandle
ctx context.Context,
conn connect.StreamingHandlerConn,
) error {
// Get ip
log.Println(conn.Peer().Query)
// Get user agent
limiter := i.getVisitor(conn.RequestHeader().Get("User-Agent"))
if limiter.Allow() == false {
return connect.NewError(connect.CodeResourceExhausted, errors.New("rate limit exceeded"))
}
return next(ctx, conn)
})
}
func (i *ratelimitInterceptor) getVisitor(ip string) *rate.Limiter {
func (i *ratelimitInterceptor) getVisitor(userAgent string) *rate.Limiter {
i.mu.Lock()
defer i.mu.Unlock()
v, exists := i.visitors[ip]
v, exists := i.visitors[userAgent]
if !exists {
limiter := rate.NewLimiter(1, 3)
// Include the current time when creating a new visitor.
i.visitors[ip] = &visitor{limiter, time.Now()}
i.visitors[userAgent] = &visitor{limiter, time.Now()}
return limiter
}

View File

@ -0,0 +1,33 @@
package models
import (
"time"
itemv1 "github.com/spotdemo4/trevstack/server/internal/services/item/v1"
"google.golang.org/protobuf/types/known/timestamppb"
)
type Item struct {
ID uint32 `gorm:"primaryKey"`
Name string
Description string
Price float32
Quantity int
Added time.Time
// User
UserID uint
User User
}
func (i Item) ToConnectV1() *itemv1.Item {
return &itemv1.Item{
Id: &i.ID,
Name: i.Name,
Description: i.Description,
Price: i.Price,
Quantity: uint32(i.Quantity),
Added: timestamppb.New(i.Added),
}
}

View File

@ -0,0 +1,759 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.5
// protoc (unknown)
// source: item/v1/item.proto
package itemv1
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type Item struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id *uint32 `protobuf:"varint,1,opt,name=id,proto3,oneof" json:"id,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"`
Price float32 `protobuf:"fixed32,4,opt,name=price,proto3" json:"price,omitempty"`
Quantity uint32 `protobuf:"varint,5,opt,name=quantity,proto3" json:"quantity,omitempty"`
Added *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=added,proto3,oneof" json:"added,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *Item) Reset() {
*x = Item{}
mi := &file_item_v1_item_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *Item) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Item) ProtoMessage() {}
func (x *Item) ProtoReflect() protoreflect.Message {
mi := &file_item_v1_item_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Item.ProtoReflect.Descriptor instead.
func (*Item) Descriptor() ([]byte, []int) {
return file_item_v1_item_proto_rawDescGZIP(), []int{0}
}
func (x *Item) GetId() uint32 {
if x != nil && x.Id != nil {
return *x.Id
}
return 0
}
func (x *Item) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *Item) GetDescription() string {
if x != nil {
return x.Description
}
return ""
}
func (x *Item) GetPrice() float32 {
if x != nil {
return x.Price
}
return 0
}
func (x *Item) GetQuantity() uint32 {
if x != nil {
return x.Quantity
}
return 0
}
func (x *Item) GetAdded() *timestamppb.Timestamp {
if x != nil {
return x.Added
}
return nil
}
type GetItemRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetItemRequest) Reset() {
*x = GetItemRequest{}
mi := &file_item_v1_item_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetItemRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetItemRequest) ProtoMessage() {}
func (x *GetItemRequest) ProtoReflect() protoreflect.Message {
mi := &file_item_v1_item_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetItemRequest.ProtoReflect.Descriptor instead.
func (*GetItemRequest) Descriptor() ([]byte, []int) {
return file_item_v1_item_proto_rawDescGZIP(), []int{1}
}
func (x *GetItemRequest) GetId() uint32 {
if x != nil {
return x.Id
}
return 0
}
type GetItemResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Item *Item `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetItemResponse) Reset() {
*x = GetItemResponse{}
mi := &file_item_v1_item_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetItemResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetItemResponse) ProtoMessage() {}
func (x *GetItemResponse) ProtoReflect() protoreflect.Message {
mi := &file_item_v1_item_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetItemResponse.ProtoReflect.Descriptor instead.
func (*GetItemResponse) Descriptor() ([]byte, []int) {
return file_item_v1_item_proto_rawDescGZIP(), []int{2}
}
func (x *GetItemResponse) GetItem() *Item {
if x != nil {
return x.Item
}
return nil
}
type GetItemsRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Start *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=start,proto3,oneof" json:"start,omitempty"`
End *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=end,proto3,oneof" json:"end,omitempty"`
Filter *string `protobuf:"bytes,3,opt,name=filter,proto3,oneof" json:"filter,omitempty"`
Limit *uint32 `protobuf:"varint,4,opt,name=limit,proto3,oneof" json:"limit,omitempty"`
Offset *uint32 `protobuf:"varint,5,opt,name=offset,proto3,oneof" json:"offset,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetItemsRequest) Reset() {
*x = GetItemsRequest{}
mi := &file_item_v1_item_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetItemsRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetItemsRequest) ProtoMessage() {}
func (x *GetItemsRequest) ProtoReflect() protoreflect.Message {
mi := &file_item_v1_item_proto_msgTypes[3]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetItemsRequest.ProtoReflect.Descriptor instead.
func (*GetItemsRequest) Descriptor() ([]byte, []int) {
return file_item_v1_item_proto_rawDescGZIP(), []int{3}
}
func (x *GetItemsRequest) GetStart() *timestamppb.Timestamp {
if x != nil {
return x.Start
}
return nil
}
func (x *GetItemsRequest) GetEnd() *timestamppb.Timestamp {
if x != nil {
return x.End
}
return nil
}
func (x *GetItemsRequest) GetFilter() string {
if x != nil && x.Filter != nil {
return *x.Filter
}
return ""
}
func (x *GetItemsRequest) GetLimit() uint32 {
if x != nil && x.Limit != nil {
return *x.Limit
}
return 0
}
func (x *GetItemsRequest) GetOffset() uint32 {
if x != nil && x.Offset != nil {
return *x.Offset
}
return 0
}
type GetItemsResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Items []*Item `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"`
Count uint64 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *GetItemsResponse) Reset() {
*x = GetItemsResponse{}
mi := &file_item_v1_item_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *GetItemsResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetItemsResponse) ProtoMessage() {}
func (x *GetItemsResponse) ProtoReflect() protoreflect.Message {
mi := &file_item_v1_item_proto_msgTypes[4]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetItemsResponse.ProtoReflect.Descriptor instead.
func (*GetItemsResponse) Descriptor() ([]byte, []int) {
return file_item_v1_item_proto_rawDescGZIP(), []int{4}
}
func (x *GetItemsResponse) GetItems() []*Item {
if x != nil {
return x.Items
}
return nil
}
func (x *GetItemsResponse) GetCount() uint64 {
if x != nil {
return x.Count
}
return 0
}
type CreateItemRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Item *Item `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *CreateItemRequest) Reset() {
*x = CreateItemRequest{}
mi := &file_item_v1_item_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *CreateItemRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CreateItemRequest) ProtoMessage() {}
func (x *CreateItemRequest) ProtoReflect() protoreflect.Message {
mi := &file_item_v1_item_proto_msgTypes[5]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CreateItemRequest.ProtoReflect.Descriptor instead.
func (*CreateItemRequest) Descriptor() ([]byte, []int) {
return file_item_v1_item_proto_rawDescGZIP(), []int{5}
}
func (x *CreateItemRequest) GetItem() *Item {
if x != nil {
return x.Item
}
return nil
}
type CreateItemResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Item *Item `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *CreateItemResponse) Reset() {
*x = CreateItemResponse{}
mi := &file_item_v1_item_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *CreateItemResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CreateItemResponse) ProtoMessage() {}
func (x *CreateItemResponse) ProtoReflect() protoreflect.Message {
mi := &file_item_v1_item_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CreateItemResponse.ProtoReflect.Descriptor instead.
func (*CreateItemResponse) Descriptor() ([]byte, []int) {
return file_item_v1_item_proto_rawDescGZIP(), []int{6}
}
func (x *CreateItemResponse) GetItem() *Item {
if x != nil {
return x.Item
}
return nil
}
type UpdateItemRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Item *Item `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *UpdateItemRequest) Reset() {
*x = UpdateItemRequest{}
mi := &file_item_v1_item_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *UpdateItemRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*UpdateItemRequest) ProtoMessage() {}
func (x *UpdateItemRequest) ProtoReflect() protoreflect.Message {
mi := &file_item_v1_item_proto_msgTypes[7]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use UpdateItemRequest.ProtoReflect.Descriptor instead.
func (*UpdateItemRequest) Descriptor() ([]byte, []int) {
return file_item_v1_item_proto_rawDescGZIP(), []int{7}
}
func (x *UpdateItemRequest) GetItem() *Item {
if x != nil {
return x.Item
}
return nil
}
type UpdateItemResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Item *Item `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *UpdateItemResponse) Reset() {
*x = UpdateItemResponse{}
mi := &file_item_v1_item_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *UpdateItemResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*UpdateItemResponse) ProtoMessage() {}
func (x *UpdateItemResponse) ProtoReflect() protoreflect.Message {
mi := &file_item_v1_item_proto_msgTypes[8]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use UpdateItemResponse.ProtoReflect.Descriptor instead.
func (*UpdateItemResponse) Descriptor() ([]byte, []int) {
return file_item_v1_item_proto_rawDescGZIP(), []int{8}
}
func (x *UpdateItemResponse) GetItem() *Item {
if x != nil {
return x.Item
}
return nil
}
type DeleteItemRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Id uint32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *DeleteItemRequest) Reset() {
*x = DeleteItemRequest{}
mi := &file_item_v1_item_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *DeleteItemRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DeleteItemRequest) ProtoMessage() {}
func (x *DeleteItemRequest) ProtoReflect() protoreflect.Message {
mi := &file_item_v1_item_proto_msgTypes[9]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DeleteItemRequest.ProtoReflect.Descriptor instead.
func (*DeleteItemRequest) Descriptor() ([]byte, []int) {
return file_item_v1_item_proto_rawDescGZIP(), []int{9}
}
func (x *DeleteItemRequest) GetId() uint32 {
if x != nil {
return x.Id
}
return 0
}
type DeleteItemResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *DeleteItemResponse) Reset() {
*x = DeleteItemResponse{}
mi := &file_item_v1_item_proto_msgTypes[10]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *DeleteItemResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DeleteItemResponse) ProtoMessage() {}
func (x *DeleteItemResponse) ProtoReflect() protoreflect.Message {
mi := &file_item_v1_item_proto_msgTypes[10]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DeleteItemResponse.ProtoReflect.Descriptor instead.
func (*DeleteItemResponse) Descriptor() ([]byte, []int) {
return file_item_v1_item_proto_rawDescGZIP(), []int{10}
}
var File_item_v1_item_proto protoreflect.FileDescriptor
var file_item_v1_item_proto_rawDesc = string([]byte{
0x0a, 0x12, 0x69, 0x74, 0x65, 0x6d, 0x2f, 0x76, 0x31, 0x2f, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x1a, 0x1f, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74,
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xcb,
0x01, 0x0a, 0x04, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x13, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x02, 0x69, 0x64, 0x88, 0x01, 0x01, 0x12, 0x12, 0x0a, 0x04,
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18,
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69,
0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28,
0x02, 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x71, 0x75, 0x61, 0x6e,
0x74, 0x69, 0x74, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x71, 0x75, 0x61, 0x6e,
0x74, 0x69, 0x74, 0x79, 0x12, 0x35, 0x0a, 0x05, 0x61, 0x64, 0x64, 0x65, 0x64, 0x18, 0x06, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48,
0x01, 0x52, 0x05, 0x61, 0x64, 0x64, 0x65, 0x64, 0x88, 0x01, 0x01, 0x42, 0x05, 0x0a, 0x03, 0x5f,
0x69, 0x64, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x61, 0x64, 0x64, 0x65, 0x64, 0x22, 0x20, 0x0a, 0x0e,
0x47, 0x65, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e,
0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x22, 0x34,
0x0a, 0x0f, 0x47, 0x65, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x21, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x0d, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04,
0x69, 0x74, 0x65, 0x6d, 0x22, 0x82, 0x02, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x49, 0x74, 0x65, 0x6d,
0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x35, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72,
0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74,
0x61, 0x6d, 0x70, 0x48, 0x00, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x88, 0x01, 0x01, 0x12,
0x31, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54,
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x01, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x88,
0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01,
0x28, 0x09, 0x48, 0x02, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12,
0x19, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x03,
0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x6f, 0x66,
0x66, 0x73, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x04, 0x52, 0x06, 0x6f, 0x66,
0x66, 0x73, 0x65, 0x74, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x73, 0x74, 0x61, 0x72,
0x74, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x65, 0x6e, 0x64, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x66, 0x69,
0x6c, 0x74, 0x65, 0x72, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x42, 0x09,
0x0a, 0x07, 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x4d, 0x0a, 0x10, 0x47, 0x65, 0x74,
0x49, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a,
0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x69,
0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, 0x65,
0x6d, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28,
0x04, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x36, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61,
0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a,
0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x69, 0x74,
0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d,
0x22, 0x37, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x49,
0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x36, 0x0a, 0x11, 0x55, 0x70, 0x64,
0x61, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21,
0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x69,
0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65,
0x6d, 0x22, 0x37, 0x0a, 0x12, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e,
0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x22, 0x23, 0x0a, 0x11, 0x44, 0x65,
0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x22,
0x14, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xeb, 0x02, 0x0a, 0x0b, 0x49, 0x74, 0x65, 0x6d, 0x53, 0x65,
0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3e, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x49, 0x74, 0x65, 0x6d,
0x12, 0x17, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x74,
0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x69, 0x74, 0x65, 0x6d,
0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x41, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x49, 0x74, 0x65, 0x6d,
0x73, 0x12, 0x18, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49,
0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x69, 0x74,
0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61,
0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x1a, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31,
0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65,
0x61, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x00, 0x12, 0x47, 0x0a, 0x0a, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x12,
0x1a, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x69, 0x74,
0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x47, 0x0a, 0x0a, 0x44, 0x65,
0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x1a, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x2e,
0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x69, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x44,
0x65, 0x6c, 0x65, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x22, 0x00, 0x42, 0x9d, 0x01, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x2e, 0x69, 0x74, 0x65, 0x6d,
0x2e, 0x76, 0x31, 0x42, 0x09, 0x49, 0x74, 0x65, 0x6d, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01,
0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x70, 0x6f,
0x74, 0x64, 0x65, 0x6d, 0x6f, 0x34, 0x2f, 0x74, 0x72, 0x65, 0x76, 0x73, 0x74, 0x61, 0x63, 0x6b,
0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x69, 0x74, 0x65, 0x6d, 0x2f, 0x76,
0x31, 0x3b, 0x69, 0x74, 0x65, 0x6d, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x49, 0x58, 0x58, 0xaa, 0x02,
0x07, 0x49, 0x74, 0x65, 0x6d, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x07, 0x49, 0x74, 0x65, 0x6d, 0x5c,
0x56, 0x31, 0xe2, 0x02, 0x13, 0x49, 0x74, 0x65, 0x6d, 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42,
0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x08, 0x49, 0x74, 0x65, 0x6d, 0x3a,
0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
})
var (
file_item_v1_item_proto_rawDescOnce sync.Once
file_item_v1_item_proto_rawDescData []byte
)
func file_item_v1_item_proto_rawDescGZIP() []byte {
file_item_v1_item_proto_rawDescOnce.Do(func() {
file_item_v1_item_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_item_v1_item_proto_rawDesc), len(file_item_v1_item_proto_rawDesc)))
})
return file_item_v1_item_proto_rawDescData
}
var file_item_v1_item_proto_msgTypes = make([]protoimpl.MessageInfo, 11)
var file_item_v1_item_proto_goTypes = []any{
(*Item)(nil), // 0: item.v1.Item
(*GetItemRequest)(nil), // 1: item.v1.GetItemRequest
(*GetItemResponse)(nil), // 2: item.v1.GetItemResponse
(*GetItemsRequest)(nil), // 3: item.v1.GetItemsRequest
(*GetItemsResponse)(nil), // 4: item.v1.GetItemsResponse
(*CreateItemRequest)(nil), // 5: item.v1.CreateItemRequest
(*CreateItemResponse)(nil), // 6: item.v1.CreateItemResponse
(*UpdateItemRequest)(nil), // 7: item.v1.UpdateItemRequest
(*UpdateItemResponse)(nil), // 8: item.v1.UpdateItemResponse
(*DeleteItemRequest)(nil), // 9: item.v1.DeleteItemRequest
(*DeleteItemResponse)(nil), // 10: item.v1.DeleteItemResponse
(*timestamppb.Timestamp)(nil), // 11: google.protobuf.Timestamp
}
var file_item_v1_item_proto_depIdxs = []int32{
11, // 0: item.v1.Item.added:type_name -> google.protobuf.Timestamp
0, // 1: item.v1.GetItemResponse.item:type_name -> item.v1.Item
11, // 2: item.v1.GetItemsRequest.start:type_name -> google.protobuf.Timestamp
11, // 3: item.v1.GetItemsRequest.end:type_name -> google.protobuf.Timestamp
0, // 4: item.v1.GetItemsResponse.items:type_name -> item.v1.Item
0, // 5: item.v1.CreateItemRequest.item:type_name -> item.v1.Item
0, // 6: item.v1.CreateItemResponse.item:type_name -> item.v1.Item
0, // 7: item.v1.UpdateItemRequest.item:type_name -> item.v1.Item
0, // 8: item.v1.UpdateItemResponse.item:type_name -> item.v1.Item
1, // 9: item.v1.ItemService.GetItem:input_type -> item.v1.GetItemRequest
3, // 10: item.v1.ItemService.GetItems:input_type -> item.v1.GetItemsRequest
5, // 11: item.v1.ItemService.CreateItem:input_type -> item.v1.CreateItemRequest
7, // 12: item.v1.ItemService.UpdateItem:input_type -> item.v1.UpdateItemRequest
9, // 13: item.v1.ItemService.DeleteItem:input_type -> item.v1.DeleteItemRequest
2, // 14: item.v1.ItemService.GetItem:output_type -> item.v1.GetItemResponse
4, // 15: item.v1.ItemService.GetItems:output_type -> item.v1.GetItemsResponse
6, // 16: item.v1.ItemService.CreateItem:output_type -> item.v1.CreateItemResponse
8, // 17: item.v1.ItemService.UpdateItem:output_type -> item.v1.UpdateItemResponse
10, // 18: item.v1.ItemService.DeleteItem:output_type -> item.v1.DeleteItemResponse
14, // [14:19] is the sub-list for method output_type
9, // [9:14] is the sub-list for method input_type
9, // [9:9] is the sub-list for extension type_name
9, // [9:9] is the sub-list for extension extendee
0, // [0:9] is the sub-list for field type_name
}
func init() { file_item_v1_item_proto_init() }
func file_item_v1_item_proto_init() {
if File_item_v1_item_proto != nil {
return
}
file_item_v1_item_proto_msgTypes[0].OneofWrappers = []any{}
file_item_v1_item_proto_msgTypes[3].OneofWrappers = []any{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_item_v1_item_proto_rawDesc), len(file_item_v1_item_proto_rawDesc)),
NumEnums: 0,
NumMessages: 11,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_item_v1_item_proto_goTypes,
DependencyIndexes: file_item_v1_item_proto_depIdxs,
MessageInfos: file_item_v1_item_proto_msgTypes,
}.Build()
File_item_v1_item_proto = out.File
file_item_v1_item_proto_goTypes = nil
file_item_v1_item_proto_depIdxs = nil
}

View File

@ -0,0 +1,220 @@
// Code generated by protoc-gen-connect-go. DO NOT EDIT.
//
// Source: item/v1/item.proto
package itemv1connect
import (
connect "connectrpc.com/connect"
context "context"
errors "errors"
v1 "github.com/spotdemo4/trevstack/server/internal/services/item/v1"
http "net/http"
strings "strings"
)
// This is a compile-time assertion to ensure that this generated file and the connect package are
// compatible. If you get a compiler error that this constant is not defined, this code was
// generated with a version of connect newer than the one compiled into your binary. You can fix the
// problem by either regenerating this code with an older version of connect or updating the connect
// version compiled into your binary.
const _ = connect.IsAtLeastVersion1_13_0
const (
// ItemServiceName is the fully-qualified name of the ItemService service.
ItemServiceName = "item.v1.ItemService"
)
// These constants are the fully-qualified names of the RPCs defined in this package. They're
// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route.
//
// Note that these are different from the fully-qualified method names used by
// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to
// reflection-formatted method names, remove the leading slash and convert the remaining slash to a
// period.
const (
// ItemServiceGetItemProcedure is the fully-qualified name of the ItemService's GetItem RPC.
ItemServiceGetItemProcedure = "/item.v1.ItemService/GetItem"
// ItemServiceGetItemsProcedure is the fully-qualified name of the ItemService's GetItems RPC.
ItemServiceGetItemsProcedure = "/item.v1.ItemService/GetItems"
// ItemServiceCreateItemProcedure is the fully-qualified name of the ItemService's CreateItem RPC.
ItemServiceCreateItemProcedure = "/item.v1.ItemService/CreateItem"
// ItemServiceUpdateItemProcedure is the fully-qualified name of the ItemService's UpdateItem RPC.
ItemServiceUpdateItemProcedure = "/item.v1.ItemService/UpdateItem"
// ItemServiceDeleteItemProcedure is the fully-qualified name of the ItemService's DeleteItem RPC.
ItemServiceDeleteItemProcedure = "/item.v1.ItemService/DeleteItem"
)
// ItemServiceClient is a client for the item.v1.ItemService service.
type ItemServiceClient interface {
GetItem(context.Context, *connect.Request[v1.GetItemRequest]) (*connect.Response[v1.GetItemResponse], error)
GetItems(context.Context, *connect.Request[v1.GetItemsRequest]) (*connect.Response[v1.GetItemsResponse], error)
CreateItem(context.Context, *connect.Request[v1.CreateItemRequest]) (*connect.Response[v1.CreateItemResponse], error)
UpdateItem(context.Context, *connect.Request[v1.UpdateItemRequest]) (*connect.Response[v1.UpdateItemResponse], error)
DeleteItem(context.Context, *connect.Request[v1.DeleteItemRequest]) (*connect.Response[v1.DeleteItemResponse], error)
}
// NewItemServiceClient constructs a client for the item.v1.ItemService service. By default, it uses
// the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends
// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or
// connect.WithGRPCWeb() options.
//
// The URL supplied here should be the base URL for the Connect or gRPC server (for example,
// http://api.acme.com or https://acme.com/grpc).
func NewItemServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) ItemServiceClient {
baseURL = strings.TrimRight(baseURL, "/")
itemServiceMethods := v1.File_item_v1_item_proto.Services().ByName("ItemService").Methods()
return &itemServiceClient{
getItem: connect.NewClient[v1.GetItemRequest, v1.GetItemResponse](
httpClient,
baseURL+ItemServiceGetItemProcedure,
connect.WithSchema(itemServiceMethods.ByName("GetItem")),
connect.WithClientOptions(opts...),
),
getItems: connect.NewClient[v1.GetItemsRequest, v1.GetItemsResponse](
httpClient,
baseURL+ItemServiceGetItemsProcedure,
connect.WithSchema(itemServiceMethods.ByName("GetItems")),
connect.WithClientOptions(opts...),
),
createItem: connect.NewClient[v1.CreateItemRequest, v1.CreateItemResponse](
httpClient,
baseURL+ItemServiceCreateItemProcedure,
connect.WithSchema(itemServiceMethods.ByName("CreateItem")),
connect.WithClientOptions(opts...),
),
updateItem: connect.NewClient[v1.UpdateItemRequest, v1.UpdateItemResponse](
httpClient,
baseURL+ItemServiceUpdateItemProcedure,
connect.WithSchema(itemServiceMethods.ByName("UpdateItem")),
connect.WithClientOptions(opts...),
),
deleteItem: connect.NewClient[v1.DeleteItemRequest, v1.DeleteItemResponse](
httpClient,
baseURL+ItemServiceDeleteItemProcedure,
connect.WithSchema(itemServiceMethods.ByName("DeleteItem")),
connect.WithClientOptions(opts...),
),
}
}
// itemServiceClient implements ItemServiceClient.
type itemServiceClient struct {
getItem *connect.Client[v1.GetItemRequest, v1.GetItemResponse]
getItems *connect.Client[v1.GetItemsRequest, v1.GetItemsResponse]
createItem *connect.Client[v1.CreateItemRequest, v1.CreateItemResponse]
updateItem *connect.Client[v1.UpdateItemRequest, v1.UpdateItemResponse]
deleteItem *connect.Client[v1.DeleteItemRequest, v1.DeleteItemResponse]
}
// GetItem calls item.v1.ItemService.GetItem.
func (c *itemServiceClient) GetItem(ctx context.Context, req *connect.Request[v1.GetItemRequest]) (*connect.Response[v1.GetItemResponse], error) {
return c.getItem.CallUnary(ctx, req)
}
// GetItems calls item.v1.ItemService.GetItems.
func (c *itemServiceClient) GetItems(ctx context.Context, req *connect.Request[v1.GetItemsRequest]) (*connect.Response[v1.GetItemsResponse], error) {
return c.getItems.CallUnary(ctx, req)
}
// CreateItem calls item.v1.ItemService.CreateItem.
func (c *itemServiceClient) CreateItem(ctx context.Context, req *connect.Request[v1.CreateItemRequest]) (*connect.Response[v1.CreateItemResponse], error) {
return c.createItem.CallUnary(ctx, req)
}
// UpdateItem calls item.v1.ItemService.UpdateItem.
func (c *itemServiceClient) UpdateItem(ctx context.Context, req *connect.Request[v1.UpdateItemRequest]) (*connect.Response[v1.UpdateItemResponse], error) {
return c.updateItem.CallUnary(ctx, req)
}
// DeleteItem calls item.v1.ItemService.DeleteItem.
func (c *itemServiceClient) DeleteItem(ctx context.Context, req *connect.Request[v1.DeleteItemRequest]) (*connect.Response[v1.DeleteItemResponse], error) {
return c.deleteItem.CallUnary(ctx, req)
}
// ItemServiceHandler is an implementation of the item.v1.ItemService service.
type ItemServiceHandler interface {
GetItem(context.Context, *connect.Request[v1.GetItemRequest]) (*connect.Response[v1.GetItemResponse], error)
GetItems(context.Context, *connect.Request[v1.GetItemsRequest]) (*connect.Response[v1.GetItemsResponse], error)
CreateItem(context.Context, *connect.Request[v1.CreateItemRequest]) (*connect.Response[v1.CreateItemResponse], error)
UpdateItem(context.Context, *connect.Request[v1.UpdateItemRequest]) (*connect.Response[v1.UpdateItemResponse], error)
DeleteItem(context.Context, *connect.Request[v1.DeleteItemRequest]) (*connect.Response[v1.DeleteItemResponse], error)
}
// NewItemServiceHandler builds an HTTP handler from the service implementation. It returns the path
// on which to mount the handler and the handler itself.
//
// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf
// and JSON codecs. They also support gzip compression.
func NewItemServiceHandler(svc ItemServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) {
itemServiceMethods := v1.File_item_v1_item_proto.Services().ByName("ItemService").Methods()
itemServiceGetItemHandler := connect.NewUnaryHandler(
ItemServiceGetItemProcedure,
svc.GetItem,
connect.WithSchema(itemServiceMethods.ByName("GetItem")),
connect.WithHandlerOptions(opts...),
)
itemServiceGetItemsHandler := connect.NewUnaryHandler(
ItemServiceGetItemsProcedure,
svc.GetItems,
connect.WithSchema(itemServiceMethods.ByName("GetItems")),
connect.WithHandlerOptions(opts...),
)
itemServiceCreateItemHandler := connect.NewUnaryHandler(
ItemServiceCreateItemProcedure,
svc.CreateItem,
connect.WithSchema(itemServiceMethods.ByName("CreateItem")),
connect.WithHandlerOptions(opts...),
)
itemServiceUpdateItemHandler := connect.NewUnaryHandler(
ItemServiceUpdateItemProcedure,
svc.UpdateItem,
connect.WithSchema(itemServiceMethods.ByName("UpdateItem")),
connect.WithHandlerOptions(opts...),
)
itemServiceDeleteItemHandler := connect.NewUnaryHandler(
ItemServiceDeleteItemProcedure,
svc.DeleteItem,
connect.WithSchema(itemServiceMethods.ByName("DeleteItem")),
connect.WithHandlerOptions(opts...),
)
return "/item.v1.ItemService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case ItemServiceGetItemProcedure:
itemServiceGetItemHandler.ServeHTTP(w, r)
case ItemServiceGetItemsProcedure:
itemServiceGetItemsHandler.ServeHTTP(w, r)
case ItemServiceCreateItemProcedure:
itemServiceCreateItemHandler.ServeHTTP(w, r)
case ItemServiceUpdateItemProcedure:
itemServiceUpdateItemHandler.ServeHTTP(w, r)
case ItemServiceDeleteItemProcedure:
itemServiceDeleteItemHandler.ServeHTTP(w, r)
default:
http.NotFound(w, r)
}
})
}
// UnimplementedItemServiceHandler returns CodeUnimplemented from all methods.
type UnimplementedItemServiceHandler struct{}
func (UnimplementedItemServiceHandler) GetItem(context.Context, *connect.Request[v1.GetItemRequest]) (*connect.Response[v1.GetItemResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("item.v1.ItemService.GetItem is not implemented"))
}
func (UnimplementedItemServiceHandler) GetItems(context.Context, *connect.Request[v1.GetItemsRequest]) (*connect.Response[v1.GetItemsResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("item.v1.ItemService.GetItems is not implemented"))
}
func (UnimplementedItemServiceHandler) CreateItem(context.Context, *connect.Request[v1.CreateItemRequest]) (*connect.Response[v1.CreateItemResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("item.v1.ItemService.CreateItem is not implemented"))
}
func (UnimplementedItemServiceHandler) UpdateItem(context.Context, *connect.Request[v1.UpdateItemRequest]) (*connect.Response[v1.UpdateItemResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("item.v1.ItemService.UpdateItem is not implemented"))
}
func (UnimplementedItemServiceHandler) DeleteItem(context.Context, *connect.Request[v1.DeleteItemRequest]) (*connect.Response[v1.DeleteItemResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("item.v1.ItemService.DeleteItem is not implemented"))
}

View File

@ -114,6 +114,7 @@ func main() {
api := http.NewServeMux()
api.Handle(withCORS(handlers.NewAuthHandler(db, env.Key)))
api.Handle(withCORS(handlers.NewUserHandler(db, env.Key)))
api.Handle(withCORS(handlers.NewItemHandler(db, env.Key)))
// Serve web interface
mux := http.NewServeMux()
@ -153,7 +154,9 @@ func main() {
}
}()
server.ListenAndServe()
if err := server.ListenAndServe(); err != nil {
log.Fatal(err)
}
}
// withCORS adds CORS support to a Connect HTTP handler.