"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.APIClient = exports.APIError = void 0;
const tslib_1 = require("tslib");
const logger_1 = require("@refloat/logger");
const core_1 = require("@urql/core");
const fs_1 = require("fs");
const path_1 = require("path");
const http_1 = (0, tslib_1.__importDefault)(require("http"));
const https_1 = (0, tslib_1.__importDefault)(require("https"));
const cross_fetch_1 = (0, tslib_1.__importDefault)(require("cross-fetch"));
const schema_1 = require("../generated/schema");
const retry_1 = (0, tslib_1.__importDefault)(require("../utils/retry"));
const tags_1 = require("../utils/tags");
const logger = logger_1.Logger.create('api-client');
class APIError extends Error {
    constructor(error) {
        super(error.message);
        this.originalError = error;
    }
}
exports.APIError = APIError;
const agents = {
    http: new http_1.default.Agent({ keepAlive: true }),
    https: new https_1.default.Agent({ keepAlive: true }),
};
class APIClient {
    constructor(uri) {
        this.uri = uri;
        this.token = null;
        this.client = (0, core_1.createClient)({
            url: this.uri.href + 'gql',
            fetch: (input, { headers = {}, ...options } = {}) => (0, cross_fetch_1.default)(input, {
                ...options,
                headers: {
                    ...headers,
                    ...this.headers(),
                },
                agent: (url) => url.protocol === 'https:' ? agents.https : agents.http,
            }),
        });
    }
    async register(agent) {
        const { agent: { register }, } = await this.retry('register-agent', () => this.client
            .mutation(schema_1.RegisterDocument, {
            ...agent,
            tags: tags_1.defaultTags,
        })
            .toPromise());
        this.token = register.token;
        return register;
    }
    async acquireBuild() {
        var _a;
        const { agent } = await this.retry('acquire-build', () => this.client.mutation(schema_1.AcquireBuildDocument).toPromise());
        return (_a = agent.build.acquire) !== null && _a !== void 0 ? _a : null;
    }
    async acquireJob() {
        var _a;
        const { agent } = await this.retry('acquire-job', () => this.client.mutation(schema_1.AcquireJobDocument).toPromise());
        return (_a = agent.job.acquire) !== null && _a !== void 0 ? _a : null;
    }
    async configureBuild(build, sha, dependencies, config) {
        await this.retry('configure-build', () => this.client
            .mutation(schema_1.ConfigureBuildDocument, { build, sha, dependencies, config })
            .toPromise());
    }
    async configureBuildError(build, error, { notFound, sha, }) {
        await this.retry('configure-build-error', () => this.client
            .mutation(schema_1.ConfigureBuildErrorDocument, { build, error, notFound, sha })
            .toPromise());
    }
    async start(stepId, date) {
        await this.retry('start-step', () => this.client
            .mutation(schema_1.StartStepDocument, { id: stepId, date: date.toISOString() })
            .toPromise());
    }
    async progress(stepId, buffer) {
        await this.retry('update-step', async () => {
            const res = await (0, cross_fetch_1.default)(this.uri.href + `api/agent/step/update/${stepId}`, {
                body: buffer,
                method: 'POST',
                headers: this.headers(),
            });
            if (res.status !== 201) {
                throw new Error(`HTTP Error ${res.status}: ${res.statusText}`);
            }
            return { data: 'ok' };
        });
    }
    async complete(stepId, date, success) {
        await this.retry('complete-step', () => this.client
            .mutation(schema_1.CompleteStepDocument, {
            id: stepId,
            date: date.toISOString(),
            success,
        })
            .toPromise());
    }
    async upload({ stepId, cwd, path, }) {
        const res = await new Promise((resolve, reject) => (0, cross_fetch_1.default)(this.uri.href + `artifact/${stepId}/${encodeURIComponent(path)}`, {
            body: (0, fs_1.createReadStream)((0, path_1.join)(cwd, path)).on('error', reject),
            method: 'PUT',
            headers: this.headers(),
        })
            .then(resolve)
            .catch(reject));
        if (res.status < 200 || res.status >= 300) {
            console.error(await res.text());
            throw new APIError(`Failed to upload file: ${path}`);
        }
    }
    async retry(name, run, options = {}) {
        return (0, retry_1.default)(async () => {
            const result = run();
            const promise = typeof result.toPromise === 'function' ? result.toPromise() : result;
            const { data, error } = await promise;
            if (error || !data) {
                throw new APIError(error);
            }
            return data;
        }, {
            onRetry: ({ error, attempt, delay }) => logger.warn(`request error, retrying in ${delay / 1000}s..`, {
                request: name,
                attempt,
                error,
            }),
            shouldRetry: (error) => {
                var _a, _b;
                const graphQLErrors = (_b = (_a = error === null || error === void 0 ? void 0 : error.originalError) === null || _a === void 0 ? void 0 : _a.graphQLErrors) !== null && _b !== void 0 ? _b : error === null || error === void 0 ? void 0 : error.graphQLErrors;
                if (graphQLErrors === null || graphQLErrors === void 0 ? void 0 : graphQLErrors.length) {
                    return graphQLErrors.some((err) => {
                        var _a, _b;
                        const code = (_b = (_a = err === null || err === void 0 ? void 0 : err.extensions) === null || _a === void 0 ? void 0 : _a.response) === null || _b === void 0 ? void 0 : _b.statusCode;
                        return code && code >= 500;
                    });
                }
                return true;
            },
            ...options,
        });
    }
    headers() {
        return {
            Authorization: this.token ? `Bearer ${this.token}` : '',
        };
    }
}
exports.APIClient = APIClient;
//# sourceMappingURL=api-client.js.map