Skip to content

Commit

Permalink
improve error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
Kull Zacharias (IT-SCF-F-BIM) committed May 23, 2024
1 parent b76611d commit 75cc252
Show file tree
Hide file tree
Showing 21 changed files with 624 additions and 4,848 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:
path: |
build/assets/*
build/index.html
serve.js
serve.cjs
- name: 'If build on master branch, bump version on develop'
if: ${{ github.ref_name == 'master' }}
Expand Down
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,8 @@ typings/

dist

build
build

config.json

server.*
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,31 +20,31 @@ The backend provider can be selected on startup. Supported backend providers are
## Usage
* download latest release zip file: `wget https://github.com/zzeekk/generic-referencedata-editor/releases/latest/download/editor.zip`
* unzip
* open "dist/index.html" in browser
* open `build/index.html` in browser
* choose provider and enter informations to access data file in repository, or upload schema and data
* edit data records
* commit data to repository or download data using buttons in the upper right of the table view

### without cross-site scripting (Bitbucket Server & Cloud)
### without cross-site scripting (Bitbucket Server)
Start a local https server to forward api request to repository server:
* complete basic stpeps above
* make sure nodejs is installed
* create config.json with following properties:
- "port": port to use
- "bitbucketServerUrl" Bitbucket server Url
- "sslKeyFile" + "sslCertFile": if you have no certificate for the server, you can create self-signed certificate for testing with the following steps:
* create `config.json` with following properties:
- `port`: port to use
- `bitbucketServerUrl` Bitbucket server Url
- `sslKeyFile` + `sslCertFile`: if you have no certificate for the server, you can create self-signed certificate for testing with the following steps:
- openssl genrsa -out server.key 2048
- openssl req -new -key server.key -out server.crt.-req
- openssl req -new -key server.key -out server.crt.req
- openssl x509 -req -in server.crt.req -signkey server.key -out server.crt
* run server: `node serve.js`
* open "dist/index.html" in browser, choose provider and use localhost as hostname
* run server: `node serve.cjs`
* open "https://localhost:<port>" in browser, choose Provider BitbucketServer

## Build
* make sure nodejs & npm are installed
* clone git repository
* install yarn package manager: `npm install --global yarn`
* `yarn build`
* check build folder for artifacts
* check `build` folder for artifacts

## Built With
* react
Expand Down
Binary file removed dist/448c34a56d699c29117adc64c43affeb.woff2
Binary file not shown.
288 changes: 0 additions & 288 deletions dist/89889688147bd7575d6327160d64e760.svg

This file was deleted.

Binary file removed dist/e18bbf611f2a2e43afc071aa2f4e1512.ttf
Binary file not shown.
Binary file removed dist/f4769f9bdb7466be65088239c12046d1.eot
Binary file not shown.
Binary file removed dist/fa2772327f55d8198301fdb8bcfc8158.woff
Binary file not shown.
15 changes: 0 additions & 15 deletions dist/index.html

This file was deleted.

4,333 changes: 0 additions & 4,333 deletions dist/refedit.bundle.js

This file was deleted.

9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@
"typescript": "^4.7.4",
"vite": "5"
},
"devDependencies": {},
"devDependencies": {
"express": "^4.19.2",
"http-proxy-middleware": "^3.0.0"
},
"scripts": {
"start": "vite --force",
"build": "vite build",
Expand All @@ -63,8 +66,8 @@
]
},
"peerDependencies": {
"react": "18.0.2",
"react-dom": "18.0.2"
"react": "18",
"react-dom": "18"
},
"homepage": "./"
}
34 changes: 34 additions & 0 deletions serve.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

var express = require('express');
var https = require('https');
var fs = require('fs');
var { createProxyMiddleware } = require('http-proxy-middleware');
var config = require('./config.json');
if (!config.bitbucketServerUrl) throw new Error("bitbucketServerUrl missing in config.json");
if (!config.port) throw new Error("port missing in config.json");
if (!config.sslKeyFile) throw new Error("sslKeyFile missing in config.json");
if (!config.sslCertFile) throw new Error("sslCertFile missing in config.json");

/**
* Configure proxy middleware
*/
var bitbucketServerProxy = createProxyMiddleware({
target: config.bitbucketServerUrl,
pathRewrite: {'^/api/server' : ''},
changeOrigin: true
});
var devProxy = createProxyMiddleware({
target: 'http://localhost:5173/',
});

var app = express();
app.use('/api/server', bitbucketServerProxy);
//app.use('/', express.static('build'));
app.use('/', devProxy); // use to forward to dev server started with `yarn start

var privateKey = fs.readFileSync(config.sslKeyFile, 'utf8');
var certificate = fs.readFileSync(config.sslCertFile, 'utf8');
var options = {key: privateKey, cert: certificate};
https.createServer(options, app).listen(config.port);

console.log('Server and Proxy started, open https://localhost:'+config.port);
26 changes: 0 additions & 26 deletions serve.js

This file was deleted.

12 changes: 6 additions & 6 deletions src/BitbucketCloudProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ interface LoginInput {
password: string;
}

function LoginForm(props: {provider: BitbucketCloudProvider, defaults: LoginInput, login: (LoginInput) => void, setProvider: (DataProvider) => void}) {
function LoginForm(props: {provider: BitbucketCloudProvider, defaults: LoginInput, login: (LoginInput) => void, setProvider: (DataProvider) => void, showError: (string) => void}) {
const { handleSubmit, register, formState } = useForm<LoginInput>({
defaultValues: props.defaults,
});
Expand Down Expand Up @@ -77,8 +77,8 @@ export class BitbucketCloudProvider extends DataProvider {
})
};

getLoginForm(params: any, setProvider: (DataProvider) => void) {
return <LoginForm provider={this} defaults={params as LoginInput} login={x => this.login(this,x)} setProvider={setProvider}/>
getLoginForm(params: any, setProvider: (DataProvider) => void, showError: (string) => void) {
return <LoginForm provider={this} defaults={params as LoginInput} login={x => this.login(this,x)} setProvider={setProvider} showError={showError}/>
};

login(that: BitbucketCloudProvider, input: LoginInput) {
Expand Down Expand Up @@ -119,13 +119,13 @@ export class BitbucketCloudProvider extends DataProvider {

saveData(msg: string) {
if(!this.loginInput) throw Error( "Connection parameters not set.");
this.data?.then(d => {
return this.data!.then(d => {
var postData = new FormData();
postData.append(this.loginInput!.path!, JSON.stringify(d,undefined,2));
postData.append("message", msg);
postData.append("branch", this.loginInput!.branch!);
this.makeRequest("", "POST", postData);
this.changedRecords = []; // reset changed records
return this.makeRequest("", "POST", postData)
.then(() => {this.changedRecords = []}) // reset changed records
});
};
}
69 changes: 47 additions & 22 deletions src/BitbucketServerProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,27 @@ interface LoginInput {
password: string;
}

function LoginForm(props: {provider: BitbucketServerProvider, defaults: LoginInput, login: (LoginInput) => void, setProvider: (DataProvider) => void}) {
function LoginForm(props: {provider: BitbucketServerProvider, defaults: LoginInput, login: (LoginInput) => Promise<void>, setProvider: (DataProvider) => void, showError: (string) => void}) {
const { handleSubmit, register, formState } = useForm<LoginInput>({
defaultValues: props.defaults,
});
const { errors } = formState;

function submit(x: LoginInput) {
props.login(x);
props.setProvider(props.provider);
new Promise(() => console.log("start login"))
.then(() => props.login(x))
.then(() => props.setProvider(props.provider))
.catch(e => {
console.log("submit", String(e));
props.showError(String(e));
props.provider.reset();
throw e;
});
}

return (<>
<Box component="form" onSubmit={handleSubmit(submit)} noValidate sx={{ mt: 1 }}>
<TextField margin="dense" size="small" fullWidth label="Email Address" {...register("user", {required: true})} error={!!errors.user}/>
<TextField margin="dense" size="small" fullWidth label="UserId" {...register("user", {required: true})} error={!!errors.user}/>
<TextField margin="dense" size="small" fullWidth label="Password" type="password" {...register("password", {required: true})} error={!!errors.password}/>
<TextField margin="dense" size="small" fullWidth label="Project" {...register("project", {required: true})} error={!!errors.project}/>
<TextField margin="dense" size="small" fullWidth label="Repository" {...register("repo", {required: true})} error={!!errors.repo}/>
Expand All @@ -39,8 +46,8 @@ function LoginForm(props: {provider: BitbucketServerProvider, defaults: LoginInp
export class BitbucketServerProvider extends DataProvider {

// bitbucket server, according to https://docs.atlassian.com/bitbucket-server/rest/5.8.0/bitbucket-rest.html
// must use proxy because of CORS
private baseUrl = "/bitbucketServerProxy/rest/api/1.0"
// must use proxy at localhost /api/server because of CORS
private baseUrl = "/api/server/rest/api/1.0"

// config variables
private loginInput?: LoginInput;
Expand All @@ -53,11 +60,16 @@ export class BitbucketServerProvider extends DataProvider {

getName() {
return 'BitbucketServer'
};
}

reset() {
this.data = undefined;
this.config = undefined;
}

private getSchemaPath(): string {
if (this.loginInput!.path?.match(/\.json+$/)) {
return this.loginInput!.path.replace(/\.json+$/, ".config.json"); // replace extension .json -> .config.json
return this.loginInput!.path.replace(/\.json+$/, ".schema.json"); // replace extension .json -> .schema.json
} else {
throw Error( "Data path must have extension .json, but is "+this.loginInput!.path);
}
Expand All @@ -73,30 +85,36 @@ export class BitbucketServerProvider extends DataProvider {
headers.append("Authorization", this.authVal!);
// fetch
return fetch(repoUrl+path, {method: method, headers: headers, body: (body ? JSON.stringify(body) : undefined)})
.catch( response => {
console.error("makeRequest "+this.loginInput!.path, response);
return Promise.reject( "makeRequest Http error: " + response.status + (response.data!=""? " ("+response.data+")" : ""));
.then( response => {
if (response.ok) return response;
else throw Error( "Http error: " + response.status + " " + response.statusText + " (" + method + " "+repoUrl+path +")");
})
};

getLoginForm(params: any, setProvider: (DataProvider) => void) {
return <LoginForm provider={this} defaults={params as LoginInput} login={x => this.login(this,x)} setProvider={setProvider}/>
getLoginForm(params: any, setProvider: (DataProvider) => void, showError: (string) => void) {
return <LoginForm provider={this} defaults={params as LoginInput} login={x => this.login(this,x)} setProvider={setProvider} showError={showError}/>
};

login(that: BitbucketServerProvider, input: LoginInput) {
login(that: BitbucketServerProvider, input: LoginInput): Promise<void> {
that.loginInput = input;
that.authVal = "Basic " + btoa( that.loginInput!.user + ":" + that.loginInput!.password );
// test request
that.calcDataId()
return that.calcDataId()
};

getData() {
if(!this.loginInput) throw Error( "Connection parameters not set.");
if(!this.data) {
this.data = this.makeRequest("/raw/"+this.loginInput!.path+"?at="+this.loginInput!.branch)
.then( response => response.json())
.then( data => (isArray(data) ? data as []: Promise.reject("getData: response is not a JSon Array")));
this.rememberDataCommitId();
.then( data => {
if (isArray(data)) return data as [];
else throw Error("getData: response is not a JSon Array");
})
.then( data => {
// side effect
return this.rememberDataCommitId().then(() => data);
})
}
return this.data;
};
Expand All @@ -106,7 +124,10 @@ export class BitbucketServerProvider extends DataProvider {
if(!this.config) {
this.config = this.makeRequest("/raw/"+this.getSchemaPath()+"?at="+this.loginInput!.branch)
.then( response => response.json())
.then( data => (isObject(data) ? data as {}: Promise.reject("getSchema: response is not a JSon Object")));
.then( data => {
if (isObject(data)) return data as {}
else throw Error("getSchema: response is not a JSon Object");
});
}
return this.config;
};
Expand All @@ -115,9 +136,12 @@ export class BitbucketServerProvider extends DataProvider {
this.dataCommitId = this.makeRequest("/commits?path="+this.loginInput!.path+"&until="+this.loginInput!.branch+"&limit=1")
.then( response => response.json())
.then( data => {
console.log("got data commitId", data);
return (data.values.length>0 ? data.values[0].id : null);
if (data.values.length>0) {
console.log("got data commitId: " + data.values[0].id);
return data.values[0].id;
} else throw Error("Got no commitId in response: "+ data);
});
return this.dataCommitId;
}

getDataName() {
Expand All @@ -131,7 +155,7 @@ export class BitbucketServerProvider extends DataProvider {

saveData(msg: string) {
if(!this.loginInput) throw Error( "Connection parameters not set.");
Promise.all([this.data, this.dataCommitId]).then(([data,commitId]) => {
return Promise.all([this.data, this.dataCommitId]).then(([data,commitId]) => {
var postData = new FormData();
postData.append("content", JSON.stringify(data,undefined,2));
postData.append("message", msg);
Expand All @@ -141,9 +165,10 @@ export class BitbucketServerProvider extends DataProvider {
.then( response => response.json())
.then( data => {
console.log("new commitId", data);
this.changedRecords = []; // reset changed records
return data.id;
});
this.changedRecords = []; // reset changed records
return this.dataCommitId.then(() => {});
});
};
}
Loading

0 comments on commit 75cc252

Please sign in to comment.