-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: introduce suggestions for property name #900
base: next
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,9 +15,10 @@ import config from 'shogunApplicationConfig'; | |
|
||
import Header from './Component/Header/Header'; | ||
import ShogunSpinner from './Component/ShogunSpinner/ShogunSpinner'; | ||
import useExecuteWfsDescribeFeatureType, { DescribeFeatureType } from './Hooks/useExecuteWfsDescribeFeatureType'; | ||
import useSHOGunAPIClient from './Hooks/useSHOGunAPIClient'; | ||
import Portal from './Page/Portal/Portal'; | ||
import { appInfoAtom, layerSuggestionListAtom, userInfoAtom } from './State/atoms'; | ||
import { appInfoAtom, layerSuggestionListAtom, userInfoAtom, entityIdAtom } from './State/atoms'; | ||
import { setSwaggerDocs } from './State/static'; | ||
|
||
import ProviderResult = languages.ProviderResult; | ||
|
@@ -29,6 +30,7 @@ const App: React.FC = () => { | |
const [, setAppInfo] = useRecoilState(appInfoAtom); | ||
const [layerSuggestionList, setLayerSuggestionList] = useRecoilState(layerSuggestionListAtom); | ||
const [loadingState, setLoadingState] = useState<'failed' | 'loading' | 'done'>(); | ||
const [entityId, ] = useRecoilState(entityIdAtom); | ||
|
||
const disposableCompletionItemProviderRef = useRef<IDisposable>(); | ||
|
||
|
@@ -66,6 +68,24 @@ const App: React.FC = () => { | |
} | ||
}, [setAppInfo, setUserInfo, client]); | ||
|
||
const executeWfsDescribeFeatureType = useExecuteWfsDescribeFeatureType(); | ||
const getPropertyNames = useCallback(async (layerId: number | undefined) => { | ||
let response: DescribeFeatureType | undefined; | ||
const propertyNames: string[] = []; | ||
if (layerSuggestionList && layerId) { | ||
const layer = layerSuggestionList.filter(item => item.id === layerId)[0]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The state |
||
if (layer) { | ||
response = await executeWfsDescribeFeatureType(layer); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens if |
||
if (response !== undefined) { | ||
response.featureTypes[0].properties.forEach(prop => { | ||
propertyNames.push(prop.name); | ||
}); | ||
} | ||
} | ||
} | ||
return propertyNames; | ||
}, [executeWfsDescribeFeatureType, layerSuggestionList]); | ||
|
||
const registerLayerIdCompletionProvider = useCallback(() => { | ||
if (!monaco) { | ||
return undefined; | ||
|
@@ -120,19 +140,57 @@ const App: React.FC = () => { | |
}); | ||
}, [monaco, client, setLayerSuggestionList, layerSuggestionList]); | ||
|
||
const registerPropertyNameCompletionProvider = useCallback(() => { | ||
if (!monaco || entityId === undefined) { | ||
return undefined; | ||
} | ||
|
||
disposableCompletionItemProviderRef.current = monaco.languages.registerCompletionItemProvider('json', { | ||
triggerCharacters: ['.'], | ||
provideCompletionItems: async (model, position) => { | ||
const lineContent = model.getLineContent(position.lineNumber).trim(); | ||
|
||
if (lineContent.startsWith('"propertyName"')) { | ||
const propertyNames = await getPropertyNames(entityId); | ||
|
||
const currentWord = model.getWordAtPosition(position); | ||
const providerResult: ProviderResult<CompletionList> = { | ||
suggestions: propertyNames.map((prop): CompletionItem => { | ||
return { | ||
insertText: `"${prop}"`, | ||
label: prop, | ||
kind: monaco.languages.CompletionItemKind.Value, | ||
documentation: `${JSON.stringify(prop, null, ' ')}`, | ||
range: { | ||
// replace the current word, if applicable | ||
startColumn: currentWord ? currentWord.startColumn : position.column, | ||
endColumn: currentWord ? currentWord.endColumn : position.column, | ||
startLineNumber: position.lineNumber, | ||
endLineNumber: position.lineNumber, | ||
} | ||
}; | ||
}) | ||
}; | ||
return providerResult; | ||
} | ||
} | ||
}); | ||
}, [monaco, getPropertyNames, entityId]); | ||
|
||
useEffect(() => { | ||
getInitialData(); | ||
}, [getInitialData]); | ||
|
||
useEffect(() => { | ||
registerLayerIdCompletionProvider(); | ||
registerPropertyNameCompletionProvider(); | ||
|
||
return () => { | ||
if (disposableCompletionItemProviderRef.current) { | ||
disposableCompletionItemProviderRef.current.dispose(); | ||
} | ||
}; | ||
}, [registerLayerIdCompletionProvider]); | ||
}, [registerLayerIdCompletionProvider, registerPropertyNameCompletionProvider]); | ||
|
||
if (loadingState === 'loading') { | ||
return ( | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,96 @@ | ||||||
import { | ||||||
useCallback | ||||||
} from 'react'; | ||||||
|
||||||
import { UrlUtil } from '@terrestris/base-util/dist/UrlUtil/UrlUtil'; | ||||||
|
||||||
import Layer from '@terrestris/shogun-util/dist/model/Layer'; | ||||||
import { | ||||||
getBearerTokenHeader | ||||||
} from '@terrestris/shogun-util/dist/security/getBearerTokenHeader'; | ||||||
|
||||||
import useSHOGunAPIClient from './useSHOGunAPIClient'; | ||||||
|
||||||
export type LocalGeometryType = 'MultiPoint' | 'Point' | 'MultiLineString' | 'LineString' | 'MultiPolygon' | 'Polygon'; | ||||||
export type GeometryType = 'gml:MultiPoint' | 'gml:Point' | 'gml:MultiLineString' | | ||||||
'gml:LineString' | 'gml:MultiPolygon' | 'gml:Polygon'; | ||||||
|
||||||
export interface Property { | ||||||
localType: 'int' | 'number' | 'string' | 'boolean' | 'date' | LocalGeometryType; | ||||||
maxOccurs: 0 | 1; | ||||||
minOccurs: 0 | 1; | ||||||
name: string; | ||||||
nillable: boolean; | ||||||
type: 'xsd:int' | 'xsd:number' | 'xsd:string' | 'xsd:boolean' | 'xsd:date' | GeometryType; | ||||||
} | ||||||
|
||||||
export interface FeatureType { | ||||||
typeName: string; | ||||||
properties: Property[]; | ||||||
} | ||||||
|
||||||
export interface DescribeFeatureType { | ||||||
elementFormDefault: string; | ||||||
featureTypes: FeatureType[]; | ||||||
targetNamespace: string; | ||||||
targetPrefix: string; | ||||||
} | ||||||
|
||||||
export const isGeometryType = (propertyType: string): propertyType is GeometryType => { | ||||||
const geometryTypes = [ | ||||||
'gml:MultiPoint', | ||||||
'gml:Point', | ||||||
'gml:MultiLineString', | ||||||
'gml:LineString', | ||||||
'gml:MultiPolygon', | ||||||
'gml:Polygon' | ||||||
]; | ||||||
|
||||||
return geometryTypes.includes(propertyType); | ||||||
}; | ||||||
|
||||||
export const useExecuteWfsDescribeFeatureType = () => { | ||||||
const client = useSHOGunAPIClient(); | ||||||
|
||||||
const executeWfsDescribeFeatureType = useCallback(async (layer: Layer) => { | ||||||
let url = layer.sourceConfig.url; | ||||||
|
||||||
if (!url) { | ||||||
return; | ||||||
} | ||||||
|
||||||
if (url.endsWith('?')) { | ||||||
url = url.slice(0, -1); | ||||||
} | ||||||
|
||||||
const params = { | ||||||
SERVICE: 'WFS', | ||||||
REQUEST: 'DescribeFeatureType', | ||||||
VERSION: '2.0.0', | ||||||
OUTPUTFORMAT: 'application/json', | ||||||
TYPENAMES: layer.sourceConfig.layerNames | ||||||
}; | ||||||
|
||||||
const defaultHeaders = { | ||||||
'Content-Type': 'application/json' | ||||||
}; | ||||||
|
||||||
const response = await fetch(`${url}?${UrlUtil.objectToRequestString(params)}`, { | ||||||
method: 'GET', | ||||||
headers: layer.sourceConfig.useBearerToken ? { | ||||||
...defaultHeaders, | ||||||
...getBearerTokenHeader(client?.getKeycloak()) | ||||||
} : defaultHeaders | ||||||
}); | ||||||
|
||||||
if (!response.ok) { | ||||||
throw new Error('No successful response while executing a WFS-Transaction'); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
|
||||||
return await response.json() as DescribeFeatureType; | ||||||
}, [client]); | ||||||
|
||||||
return executeWfsDescribeFeatureType; | ||||||
}; | ||||||
|
||||||
export default useExecuteWfsDescribeFeatureType; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.