Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-Claude) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
142 lines
4.3 KiB
TypeScript
142 lines
4.3 KiB
TypeScript
import soap from 'soap';
|
|
import { XMLParser } from 'fast-xml-parser';
|
|
import type { Client } from 'soap';
|
|
import type {
|
|
SageConfig,
|
|
SoapReadOptions,
|
|
SoapQueryOptions,
|
|
SoapResult,
|
|
SoapMessage,
|
|
SoapTechInfo,
|
|
} from '../types/index.js';
|
|
|
|
export class SoapClient {
|
|
private config: SageConfig;
|
|
private client: Client | null = null;
|
|
private xmlParser: XMLParser;
|
|
|
|
constructor(config: SageConfig) {
|
|
this.config = config;
|
|
this.xmlParser = new XMLParser({
|
|
ignoreAttributes: false,
|
|
attributeNamePrefix: '@_',
|
|
});
|
|
}
|
|
|
|
private async getClient(): Promise<Client> {
|
|
if (!this.client) {
|
|
const wsdlUrl = `${this.config.url}/soap-wsdl/syracuse/collaboration/syracuse/CAdxWebServiceXmlCC?wsdl`;
|
|
this.client = await soap.createClientAsync(wsdlUrl, {});
|
|
this.client.setSecurity(
|
|
new soap.BasicAuthSecurity(this.config.user, this.config.password),
|
|
);
|
|
const soapEndpoint = `${this.config.url}/soap-generic/syracuse/collaboration/syracuse/CAdxWebServiceXmlCC`;
|
|
this.client.setEndpoint(soapEndpoint);
|
|
}
|
|
return this.client;
|
|
}
|
|
|
|
private buildCallContext() {
|
|
return {
|
|
codeLang: this.config.language,
|
|
codeUser: '',
|
|
password: '',
|
|
poolAlias: this.config.poolAlias,
|
|
poolId: '',
|
|
requestConfig: 'adxwss.optreturn=JSON&adxwss.beautify=false',
|
|
};
|
|
}
|
|
|
|
// SOAP caveat: status returns as STRING "1", empty arrays as undefined
|
|
private normalizeResult(raw: unknown): SoapResult {
|
|
const r = raw as Record<string, unknown> | undefined;
|
|
return {
|
|
status: parseInt(String(r?.status ?? '0'), 10),
|
|
data: this.parseResultXml(r?.resultXml),
|
|
messages: this.normalizeMessages(r?.messages),
|
|
technicalInfos: this.normalizeTechInfos(r?.technicalInfos),
|
|
};
|
|
}
|
|
|
|
private parseResultXml(resultXml: unknown): unknown {
|
|
if (!resultXml || typeof resultXml !== 'string') return null;
|
|
try {
|
|
return JSON.parse(resultXml);
|
|
} catch {
|
|
try {
|
|
return this.xmlParser.parse(resultXml);
|
|
} catch {
|
|
return resultXml;
|
|
}
|
|
}
|
|
}
|
|
|
|
private normalizeMessages(raw: unknown): SoapMessage[] {
|
|
if (!raw) return [];
|
|
const arr = Array.isArray(raw) ? raw : [raw];
|
|
return arr.map((m: Record<string, unknown>) => ({
|
|
type: parseInt(String(m?.type ?? '0'), 10),
|
|
message: String(m?.message ?? ''),
|
|
}));
|
|
}
|
|
|
|
private normalizeTechInfos(raw: unknown): SoapTechInfo[] {
|
|
if (!raw) return [];
|
|
const arr = Array.isArray(raw) ? raw : [raw];
|
|
return arr.map((t: Record<string, unknown>) => ({
|
|
type: String(t?.type ?? ''),
|
|
message: String(t?.message ?? ''),
|
|
}));
|
|
}
|
|
|
|
async read(options: SoapReadOptions): Promise<SoapResult> {
|
|
const client = await this.getClient();
|
|
const [response] = await client.readAsync({
|
|
callContext: this.buildCallContext(),
|
|
publicName: options.publicName,
|
|
objectKeys: Object.entries(options.key).map(([key, value]) => ({
|
|
key,
|
|
value,
|
|
})),
|
|
});
|
|
const res = response as Record<string, unknown>;
|
|
return this.normalizeResult(res?.readReturn ?? res);
|
|
}
|
|
|
|
async query(options: SoapQueryOptions): Promise<SoapResult> {
|
|
const client = await this.getClient();
|
|
const listSize = Math.min(options.listSize || 20, 200);
|
|
const [response] = await client.queryAsync({
|
|
callContext: this.buildCallContext(),
|
|
publicName: options.publicName,
|
|
objectKeys: [],
|
|
listSize,
|
|
});
|
|
const res = response as Record<string, unknown>;
|
|
return this.normalizeResult(res?.queryReturn ?? res);
|
|
}
|
|
|
|
async getDescription(publicName: string): Promise<SoapResult> {
|
|
const client = await this.getClient();
|
|
const [response] = await client.getDescriptionAsync({
|
|
callContext: this.buildCallContext(),
|
|
publicName,
|
|
});
|
|
const res = response as Record<string, unknown>;
|
|
return this.normalizeResult(res?.getDescriptionReturn ?? res);
|
|
}
|
|
|
|
async healthCheck(): Promise<{ status: string; latencyMs: number }> {
|
|
const start = Date.now();
|
|
try {
|
|
await this.getClient();
|
|
return { status: 'connected', latencyMs: Date.now() - start };
|
|
} catch (error) {
|
|
return {
|
|
status: `error: ${error instanceof Error ? error.message : String(error)}`,
|
|
latencyMs: Date.now() - start,
|
|
};
|
|
}
|
|
}
|
|
}
|