@bartcant Thank you. This is extremely helpful to me and I am studying this equipment finance application.
It is indeed a great learning material. I do love to see how daml could solve business problems.
Thank you very much for your advice.
@bartcant Thank you. This is extremely helpful to me and I am studying this equipment finance application.
It is indeed a great learning material. I do love to see how daml could solve business problems.
Thank you very much for your advice.
It will take me some time to reproduce so I can make sure this is the issue, but at first glance it looks like there are two problems with this block: the location
should be /v1/stream
, not /v/stream
, and it should have a line that sets the Connection
header:
proxy_set_header Connection "Upgrade";
From MDN:
Note :
Connection: upgrade
must be set wheneverUpgrade
is sent.
@Gary_Verhaegen I have revised the nginx.conf as follows but the same errors occurred on my Chrome:
server {
listen 80;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location /v1 {
proxy_pass http://json:7575;
}
location /v1/stream {
proxy_pass http://json:7575;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Host $host;
proxy_set_header Connection "Upgrade";
}
}
The React build for the example really drives me nut. Looking at the Chrome errors, the request URL is:
ws://localhost:7575/v1/stream/query
and the exercise URL is:
https://api/data/localhost/v1/create
Do these have anything to do with the nginx location blocks? I donât know as I have no idea on the React codes.
Please advise me further on this and it seems I am close to the end of this learning exercise. Many Many Thanks.
nginx.conf
seems OK to me, but Iâm a bit perplexed by the URLs youâre showing. Do you have any idea where those api/data/localhost
are coming from? Also do you only have issues with streaming, or are exercises also failing?
Could you perhaps share your entire setup, including how to build the various Docker images involved?
@Gary_Verhaegen Let me repeat what I have done.
First, I git cloned the daml-ui-template and renamed it as âmy-appâ. Then I followed the Readme and âmake buildâ everything. Thereafter, I âdaml startâ the sandbox as well as âyarn startâ the UI. The my-app is running on http://localhost:3000. So far, everything is fine and I can play around with it to create and exercise and the UI will update automatically.
Since the React was also built. I tried it with âserve -s buildâ and open my Chrome on http://localhost:5000. The UI login screen did show up and after login (as Alice or Bob), I can see the UI without any data. This was expected and I opened the Chrome developer tool to find the following two errors:
WebSocket connection to 'wss://api:5000/data/localhost:5000/v1/stream/query' failed:
useStreamQuery: WebSocket connection failed.
I was not surprised to read them as I am sure the UI was not connected to the sandbox or api server. The funny thing happened when I tried with http://172.20.10.5:5000 as prompted ( 172.20.10.5 is my mac IP). I got a completely blank screen. The Chrome developer tool showed the following errors:
DOMException: Failed to construct 'WebSocket': The URL 'wss://api.20.10.5:5000:5000/data/172/v1/stream/query' is invalid.
Uncaught DOMException: Failed to construct 'WebSocket': The URL 'wss://api.20.10.5:5000:5000/data/172/v1/stream/query' is invalid.
I did not know what was going on as I have no React knowledge to find out what went wrong.
Anyway, I followed your previous advice and build up a âmy-damlâ docker image:
FROM digitalasset/daml-sdk:1.7.0
USER root
RUN mkdir /app
COPY .daml/dist/ /app/
RUN chown -R daml:daml /app
USER daml
I run the image for two containers:
docker run --name sandbox my-daml daml sandbox --address 0.0.0.0 --ledgerid my-app /app/my-app-0.0.1.dar
docker run --name json --link sandbox my-daml daml json-api --ledger-host sandbox --ledger-port 6865 --http-port 7575 --address 0.0.0.0
I set up my dockerfile to build the image âmy-nginxâ:
FROM nginx:stable-alpine
COPY build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
with nginx.conf as:
server {
listen 80;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location /v1 {
proxy_pass http://json:7575;
}
location /v1/stream {
proxy_pass http://json:7575;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Host $host;
proxy_set_header Connection "Upgrade";
}
}
I run this my-nginx image as:
docker run --name nginx --link json -p 80:80 my-nginx
I open the Chrome browser http://localhost and again see the UI without any data. The Chrome developer tool shows the two errors as:
WebSocket connection to 'wss://api/data/localhost/v1/stream/query' failed:
useStreamQuery: WebSocket connection failed
I tried to create a new asset and got the error:
Uncaught (in promise) TypeError: Network request failed
at XMLHttpRequest.E.s.onerror
Another strange thing happened when I go to http://172.20.10.5 and the errors become:
WebSocket connection to 'wss://api.20.10.5/data/172/v1/stream/query' failed:
useStreamQuery: WebSocket connection failed.
Please take note that the IP address was somehow changed.
The above are what I have tried in the last several weeks but still failed. I did try the create-daml-app template and I think its React build cannot be served either. Please advice.
Sorry to take so much of your time but I do really want to see how things are set up for production.
I can reproduce this.
No idea what âserve
â is or why youâd expect it to work here. I donât have a serve
binary on my machine. Iâll ignore this for now and skip to the Docker bits.
âŚ
Ok, this took a bit a digging, but I found out the issue(s). The first one is with the template itself, which seems to assume itâs getting deployed to Daml Hub and nothing else. So no connection gets established because the JS code mangles the URL.
To get it to work, youâll need to change the ui/src/config.tsx
file to match your setup. Specifically:
diff --git a/ui/src/config.ts b/ui/src/config.ts
index c2421f5..5ab2971 100755
--- a/ui/src/config.ts
+++ b/ui/src/config.ts
@@ -5,12 +5,12 @@ export const isLocalDev = process.env.NODE_ENV === 'development';
let host = window.location.host.split('.')
const applicationId = 'daml-ui-template'
-export const ledgerId = isLocalDev ? applicationId : host[0];
+export const ledgerId = isLocalDev ? applicationId : 'my-app';
-let apiUrl = host.slice(1)
-apiUrl.unshift('api')
+//let apiUrl = host.slice(1)
+//apiUrl.unshift('api')
-export const httpBaseUrl = isLocalDev ? undefined : ('https://' + apiUrl.join('.') + (window.location.port ? ':' + window.location.port : '') + '/data/' + ledgerId + '/');
+export const httpBaseUrl = isLocalDev ? undefined : ('http://' + host.join('.') + (window.location.port ? ':' + window.location.port : '') /*+ '/data/' + ledgerId*/ + '/');
// Unfortunately, the development server of `create-react-app` does not proxy
// websockets properly. Thus, we need to bypass it and talk to the JSON API
Going over those changes:
'my-app'
).apiUrl
variable.https
here so we need to set the url to http
, otherwise it will infer a (secure) wss
connection, which the browser will not be able to establish. Switching to http
ensures we get a ws
connection instead, which will work./data/ledgerid
bits from the URL.Note that Iâm expanding those changes so you can understand the issues and whatâs happening here. A simple way to make it work is to just set httpBaseUrl
to undefined
, regardless of whether weâre in development or not.
Youâll need to rerun make build
and rebuild your my-nginx
Docker image after that change.
At this point the network connections will work, but there is still one problem: the JSON API is going to refuse any connection from nginx, because it is running in âproductionâ mode and our current setup is insecure. The JSON API really wants you to use HTTPS in production between the browser and nginx (which is a good thing, but not quite an option here). Youâll need to add a flag to the json api startup command to tell it to accept HTTP connections: --allow-insecure-tokens
.
With those two changes (config.tsx
and --allow-insecure-tokens
), you should be able to get everything working.
Just to clarify these lines, the ones that start with a single -
need to be removed, and the ones that start with a single +
need to be added. The -
and +
themselves should not be in the final file, theyâre just indicators of what to add/remove.
Really each of these are just showing small changes to the original line (ex. host[0]
to 'my-app'
; or let
to //let
) but this is the way itâs represented in the diff format that Gary generated.
Right. For the avoidance of doubt, here is a working version of config.ts
in full:
import * as jwt from "jsonwebtoken";
export const isLocalDev = process.env.NODE_ENV === 'development';
let host = window.location.host.split('.')
const applicationId = 'daml-ui-template'
export const ledgerId = isLocalDev ? applicationId : 'my-app';
//let apiUrl = host.slice(1)
//apiUrl.unshift('api')
export const httpBaseUrl = undefined;
// Unfortunately, the development server of `create-react-app` does not proxy
// websockets properly. Thus, we need to bypass it and talk to the JSON API
// directly in development mode.
export const wsBaseUrl = isLocalDev ? 'ws://localhost:7575/' : undefined;
export const createToken = (party : string) => jwt.sign({ "https://daml.com/ledger-api": { ledgerId, applicationId, admin: true, actAs: [party], readAs: [party] } }, "secret")
let loginUrl = host.slice(1)
loginUrl.unshift('login')
export const dablLoginUrl = loginUrl.join('.') + (window.location.port ? ':' + window.location.port : '') + '/auth/login?ledgerId=' + ledgerId;
export const damlPartyKey = applicationId + ".daml.party";
export const damlTokenKey = applicationId + ".daml.token";
Getting an app to work both locally and on projectdable with user friendly names at the same time was an initial pain point for us as well âŚ
@gyorgybalazsi solved this by modifying the ui-template and implemented an approach where we can comment/uncomment a portion of our code in src/useKnownParties.tsx to
1 . get the known parties that are setup under projectdable
or
2. use a set of parties that are setup during a start-up script for the local version of the app
// Usage in components:
// import {useKnownParties} from '../UseKnownParties'
// Within the component:
// const {displayName, partyIdentifier} = useKnownParties ()
// YOU NEED THESE IMPORTS FOR RUNNING LOCALLY - START
import React, { useEffect } from 'react';
import { useLedger, useParty } from '@daml/react';
import Ledger, { PartyInfo } from '@daml/ledger';
// YOU NEED THESE IMPORTS FOR RUNNING LOCALLY - END
// YOU NEED THIS IMPORT FOR RUNNING ON DAML HB - START
/* import { PartyInfo } from '@daml/ledger'; */
// YOU NEED THIS IMPORT FOR RUNNING ON DAML HB - END
export function useKnownParties () {
// YOU NEED THIS PART FOR RUNNING LOCALLY - START
const [knownParties, setKnownParties] = React.useState<PartyInfo[]>([]);
const ledger: Ledger = useLedger();
useEffect(() => {
const getKnownParties = async () => {
let lst = await ledger.listKnownParties();
setKnownParties(lst);
} ;
getKnownParties()
}, [ledger]);
// YOU NEED THIS PART FOR RUNNING LOCALLY - END
// YOU NEED THIS PART FOR RUNNING ON DAML HUB - START
/* const knownParties : PartyInfo[] = require('./parties.json') */
// YOU NEED THIS PART FOR RUNNING ON DAML HUB - END
return {
displayName : (id: string ): string => {
return knownParties.filter((x : PartyInfo) => x.identifier === id)[0]?.displayName || id
},
partyIdentifier : (displayName: string): string => {
return knownParties.filter((x : PartyInfo) => x.displayName === displayName)[0]?.identifier || displayName
},
knownPartyDisplayNames: knownParties.map((x : PartyInfo) => x.displayName || x.identifier)
}
}
full source code at
https://github.com/RethinkLedgers/ef-smartcontract
@Gary_Verhaegen After revising the config.ts and the json api, I can now get everything working. This is extremely useful for me to see how the UI can connect to the daml ledger and I can now proceed to explore further.
BTW, I find it very intuitive to see how you use the sdk docker image. I will also try to run triggers and scripts with the image.
I have to thank both of you @Gary_Verhaegen @anthony again for the detailed explanations and advices. A billion thanks and all the best to you all there.
Itâs really great to see that it all worked out @EllisAllison!
You guys rock @Gary_Verhaegen @anthony! Always there to support forum members
Youâre very welcome but I think Gary deserves most of the thanks