Skip to content

Commit

Permalink
docs: add new quickstart for customer registry in javascript
Browse files Browse the repository at this point in the history
  • Loading branch information
pvlugter committed Jun 29, 2022
1 parent 8c9fa7c commit 1a6d02f
Show file tree
Hide file tree
Showing 20 changed files with 1,103 additions and 3 deletions.
43 changes: 43 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,26 @@ jobs:
npm install ../sdk/kalix-io-kalix-javascript-sdk-0.0.0.tgz
npm test
npm pack
- run:
name: "bundle quickstarts"
working_directory: "~/project/docs"
command: |
make bundles
- run:
name: "Test quickstart: JS Customer Registry"
working_directory: "~/tmp"
command: |
# use the bundled quickstart to check against what the user will download
unzip -d js-customer-registry ~/project/docs/build/src/managed/modules/javascript/attachments/js-customer-registry-quickstart.zip
cd js-customer-registry
export KALIX_NPMJS_CODEGEN_BINARY="${HOME}/project/codegen/js-gen-cli/target/native-image/kalix-codegen-js"
npm install --save \
"$HOME/project/sdk/kalix-io-kalix-javascript-sdk-0.0.0.tgz" \
"$HOME/project/npm-js/kalix-scripts/kalix-io-kalix-scripts-1.0.0.tgz" \
"$HOME/project/testkit/kalix-io-testkit-0.0.0.tgz"
npm install
npm run build
npm test
- run:
name: "Test JS Value Entity Counter sample"
command: |
Expand Down Expand Up @@ -343,6 +363,29 @@ jobs:
npm install ../sdk/kalix-io-kalix-javascript-sdk-0.0.0.tgz
DEBUG='testcontainers*' npm run integration-test
npm pack
- run:
name: "bundle quickstarts"
working_directory: "~/project/docs"
command: |
make bundles
- run:
name: "integration tests: js-customer-registry-quickstart"
working_directory: "~/tmp"
command: |
export VERSION_CHECK_ON_STARTUP=false
export KALIX_NPMJS_CODEGEN_BINARY="${HOME}/project/codegen/js-gen-cli/target/native-image/kalix-codegen-js"
source /opt/circleci/.nvm/nvm.sh
pushd ~/project/sdk && nvm install && popd
# use the bundled quickstart to check against what the user will download
unzip -d js-customer-registry ~/project/docs/build/src/managed/modules/javascript/attachments/js-customer-registry-quickstart.zip
cd js-customer-registry
npm install --save \
"$HOME/project/sdk/kalix-io-kalix-javascript-sdk-0.0.0.tgz" \
"$HOME/project/npm-js/kalix-scripts/kalix-io-kalix-scripts-1.0.0.tgz" \
"$HOME/project/testkit/kalix-io-testkit-0.0.0.tgz"
npm install
npm run build
npm run integration-test
- run:
name: "integration tests: samples/js/js-customer-registry"
command: |
Expand Down
3 changes: 2 additions & 1 deletion docs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ examples:
mkdir -p "${managed_examples}"
rsync -a --exclude-from=.examplesignore ../samples/js/js-doc-snippets/ "${managed_examples}/js-doc-snippets/"
rsync -a --exclude-from=.examplesignore ../samples/js/js-customer-registry/ "${managed_examples}/js-customer-registry/"
rsync -a --exclude-from=.examplesignore ../samples/js/js-customer-registry-quickstart/ "${managed_examples}/js-customer-registry-quickstart/"
rsync -a --exclude-from=.examplesignore ../samples/js/js-valueentity-shopping-cart/ "${managed_examples}/js-valueentity-shopping-cart/"
rsync -a --exclude-from=.examplesignore ../samples/js/valueentity-counter/ "${managed_examples}/valueentity-counter/"
rsync -a --exclude-from=.examplesignore ../samples/js/js-eventsourced-shopping-cart/ "${managed_examples}/js-eventsourced-shopping-cart/"
Expand All @@ -56,7 +57,7 @@ examples:
rsync -a --exclude-from=.examplesignore ../samples/ts/ts-replicated-entity-shopping-cart/ "${managed_examples}/ts-replicated-entity-shopping-cart/"

bundles:
bin/bundle.sh --zip "${managed_attachments}/js-customer-registry-quickstart.zip" ../samples/js/js-customer-registry
bin/bundle.sh --zip "${managed_attachments}/js-customer-registry-quickstart.zip" ../samples/js/js-customer-registry-quickstart
bin/bundle.sh --zip "${managed_attachments}/ts-customer-registry-quickstart.zip" ../samples/ts/ts-customer-registry
bin/bundle.sh --zip "${managed_attachments}/js-eventsourced-shopping-cart.zip" ../samples/js/js-eventsourced-shopping-cart
bin/bundle.sh --zip "${managed_attachments}/ts-eventsourced-shopping-cart.zip" ../samples/ts/ts-eventsourced-shopping-cart
Expand Down
2 changes: 1 addition & 1 deletion docs/config/validate-links.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"ignorePatterns": [
{ "pattern": "^https://mvnrepository\\.com" },
{ "pattern": "^http://127.0.0.1:8080" },
{ "pattern": "^https://www.npmjs.com/org/kalix-io", "why": "npmjs having a bad day 2022-06-07" }
]
}
2 changes: 1 addition & 1 deletion docs/dev/src/modules/ROOT/partials/include.adoc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
:minimum_docker_version: 19.03
:minimum_docker_version: 20.10.8

:tab-icon: image:ROOT:new-tab.svg[width=12]
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
= Quickstart: Customer Registry in JavaScript

include::ROOT:partial$include.adoc[]
include::javascript:partial$attributes.adoc[]

Learn how to create a customer registry in JavaScript, package it into a container, and run it on Kalix.

== Before you begin

* If you're new to Kalix, https://console.kalix.io[create an account{tab-icon}, window="new"] so you can try it out for free.
* You'll also need to install the https://docs.kalix.io/kalix/install-kalix.html[Kalix CLI, window="new-doc"] to deploy from a terminal window.
* For this quickstart, you'll also need
** https://docs.docker.com/engine/install[Docker {minimum_docker_version} or higher, window="new"]
** https://nodejs.org/en/download/[Node.js {minimum_node_version}, window="new"]
** https://github.com/fullstorydev/grpcurl#installation[`grpcurl`, window="new"]

[NOTE]
====
If you want to bypass writing code and jump straight to the deployment:
. Download the source code using the Kalix CLI:
+
[source,command line]
----
kalix quickstart download customer-registry-javascript
----
. Skip to <<Package and deploy your service>>.
====

== Writing the Customer Registry

. From the command line, create a directory with the basic structure for your project using a template:
+
[source,command line]
----
npx @kalix-io/create-kalix-entity@latest customer-registry --template basic
----

. Change into the project directory:
+
[source,command line]
----
cd customer-registry
----

. Download and install project dependencies:
+
[source,command line]
----
npm install
----

== Define the external API

The Customer Registry service will create or retrieve a customer, including their name, email, and mailing address. The `customer_api.proto` will contain the external API your clients will invoke.

. Create a `proto` directory.
+
[source,command line]
----
mkdir proto
----

. Create a `customer_api.proto` file and save it in the `proto` directory.

. Add declarations for:
+
* The protobuf syntax version, `proto3`.
* The package name, `customer.api`.
* Import `google/protobuf/empty.proto` and Kalix `kalix/annotations.proto`.
+
[source,proto,indent=0]
.proto/customer_api.proto
----
include::javascript:example$js-customer-registry-quickstart/proto/customer_api.proto[tag=declarations]
----

. Add the service endpoint. The service endpoint is annotated with `kalix.codegen` indicating we want to generate a Value Entity for this service.
+
[source,proto,indent=0]
.proto/customer_api.proto
----
include::javascript:example$js-customer-registry-quickstart/proto/customer_api.proto[tag=service]
----

. Add messages to define the fields that comprise a `Customer` object (and its compound `Address`):
+
[source,proto,indent=0]
.proto/customer_api.proto
----
include::javascript:example$js-customer-registry-quickstart/proto/customer_api.proto[tag=messages]
----

. Add the message that will identify which customer to retrieve for the `GetCustomer` message:
+
[source,proto,indent=0]
.proto/customer_api.proto
----
include::javascript:example$js-customer-registry-quickstart/proto/customer_api.proto[tag=method-messages]
----

== Define the domain model

The `customer_domain.proto` contains all the internal data objects (https://docs.kalix.io/reference/glossary.html#entity[Entities, window="new"]). The https://docs.kalix.io/reference/glossary.html#value_entity[Value Entity, window="new"] in this quickstart is a Key/Value store that stores only the latest updates.

. Create a `customer_domain.proto` file and save it in the `proto` directory.

. Add declarations for the proto syntax and domain package.
+
[source,proto,indent=0]
.proto/customer_domain.proto
----
include::javascript:example$js-customer-registry-quickstart/proto/customer_domain.proto[tag=declarations]
----

. Add the `CustomerState` message with fields for the customer data, and the `Address` message:
+
[source,proto,indent=0]
.proto/customer_domain.proto
----
include::javascript:example$js-customer-registry-quickstart/proto/customer_domain.proto[tag=domain]
----

. Run the `build` script from the project root directory to generate source classes, based on the protobuf definitions, in which you can add the business logic:
+
[source,command line]
----
npm run build
----

== Create command handlers

Command handlers, as the name suggests, handle incoming requests before persisting them.

. If it's not open already, open the generated `src/customer.js` file for editing.

. Modify the `Create` handler by adding the logic to handle the command. The complete function should include the following:
+
[source,javascript,indent=0]
.src/customer.js
----
include::example$js-customer-registry-quickstart/src/customer.js[tag=create]
----
+
* The incoming message contains the request data from your client and the command handler updates the state of the customer.

. Modify the `GetCustomer` handler as follows to handle the `GetCustomerRequest` command:
+
[source, javascript, indent=0]
.src/customer.js
----
include::example$js-customer-registry-quickstart/src/customer.js[tag=getCustomer]
----
+
* If that customer doesn't exist, processing the command fails.
* If the customer exists, the reply message contains the customer's information.
* The conversion between the domain CustomerState and the external API is straightforward, as they have the same fields.

[NOTE]
====
The `src/index.js` file already contains the required code to start your service and register it with Kalix.
====

== Package and deploy your service

To build and publish the container image and then deploy the service, follow these steps:

. If you haven't done so yet, sign in to your Kalix account. If this is your first time using Kalix, this will let you register an account, https://docs.kalix.io/projects/create-project.html[create your first project], and set this project as the default.
+
[source,command line]
----
kalix auth login
----

. Update the `config.dockerImage` setting in the `package.json` file with your container registry.

. Use the `deploy` script to build the container image, publish it to the container registry as configured in the `package.json` file, and then automatically https://docs.kalix.io/services/deploy-service.html#_deploy[deploy the service] to Kalix using `kalix`:
+
[source,command line]
----
npm run deploy
----

. You can https://docs.kalix.io/services/deploy-service.html#_verify_service_status[verify the status of the deployed service] using:
+
[source,command line]
----
kalix service list
----

== Invoke your service

Once the service has started successfully, you can https://docs.kalix.io/services/invoke-service.html#_testing_and_development[start a proxy locally] to access the service:

[source,command line]
----
kalix service proxy customer-registry --grpcui
----

The `--grpcui` option also starts and opens a https://docs.kalix.io/services/invoke-service.html#_using_the_built_in_graphical_client[gRPC web UI] for exploring and invoking the service (available at http://127.0.0.1:8080/ui/).

Or you can use command line gRPC or HTTP clients, such as `grpcurl` or `curl`, to invoke the service through the proxy at `localhost:8080`, using plaintext connections.

A customer can be created using the `Create` method on `CustomerService`, in the gRPC web UI, or with `grpcurl`:

[source,command line]
----
grpcurl \
-d '{
"customer_id": "abc123",
"email": "someone@example.com",
"name": "Someone",
"address": {
"street": "123 Some Street",
"city": "Somewhere"
}
}' \
--plaintext localhost:8080 \
customer.api.CustomerService/Create
----

The `GetCustomer` method can be used to retrieve this customer, in the gRPC web UI, or with `grpcurl`:

[source,command line]
----
grpcurl \
-d '{"customer_id": "abc123"}' \
--plaintext localhost:8080 \
customer.api.CustomerService/GetCustomer
----

You can https://docs.kalix.io/services/invoke-service.html#_exposing_services_to_the_internet[expose the service to the internet]. A generated hostname will be returned from the expose command:

[source,command line]
----
kalix service expose customer-registry
----

== Next steps

* You can learn more about xref:javascript:value-entity.adoc[Value Entities].
* Do another https://docs.kalix.io/quickstart/sc-eventsourced-entity-javascript.html[Quickstart] to learn about Event Sourcing and xref:javascript:eventsourced.adoc[Event Sourced Entities].
5 changes: 5 additions & 0 deletions samples/js/js-customer-registry-quickstart/.bundleignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.gitignore
lib
node_modules
package-lock.json
user-function.desc
5 changes: 5 additions & 0 deletions samples/js/js-customer-registry-quickstart/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/README.md
/docker-compose.yml
/lib/generated
/node_modules
/user-function.desc
5 changes: 5 additions & 0 deletions samples/js/js-customer-registry-quickstart/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/lib/generated
/node_modules
/user-function.desc
# test quickstarts without lock file
/package-lock.json
48 changes: 48 additions & 0 deletions samples/js/js-customer-registry-quickstart/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# This Dockerfile uses multi-stage build process.
# See https://docs.docker.com/develop/develop-images/multistage-build/

# Stage 1: Downloading dependencies and building the application
FROM node:14.19-buster-slim AS builder

RUN apt-get update && apt-get install -y python3 make g++ && rm -rf /var/lib/apt/lists/*

# Set the working directory
WORKDIR /home/node

# Install app dependencies
COPY package*.json ./
RUN npm ci

# Copy sources and build the app
COPY --chown=node . .
RUN npm run build

# Remove dev packages
# (the rest will be copied to the production image at stage 2)
RUN npm prune --production

# Stage 2: Building the production image
FROM node:14.19-buster-slim

# Set the working directory
WORKDIR /home/node

# Copy dependencies
COPY --from=builder --chown=node /home/node/node_modules node_modules/

# Copy the app
COPY --from=builder --chown=node \
/home/node/package*.json \
/home/node/user-function.desc \
./
COPY --from=builder --chown=node /home/node/proto ./proto
COPY --from=builder --chown=node /home/node/src ./src
COPY --from=builder --chown=node /home/node/lib ./lib

# Run the app as an unprivileged user for extra security.
USER node

# Run
EXPOSE 8080
# Call node directly to get SIGTERM for graceful shutdown
CMD ["node", "src/index.js"]
Loading

0 comments on commit 1a6d02f

Please sign in to comment.