Source RapidContext_Storage.js

1/*
2 * RapidContext <https://www.rapidcontext.com/>
3 * Copyright (c) 2007-2024 Per Cederberg. All rights reserved.
4 *
5 * This program is free software: you can redistribute it and/or
6 * modify it under the terms of the BSD license.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 *
12 * See the RapidContext LICENSE for more details.
13 */
14
15/**
16 * Provides functions for accessing the server storage (BETA).
17 * @namespace RapidContext.Storage
18 */
19(function (window) {
20
21    /**
22     * Returns a storage URL for a resource. If the resource is an
23     * object, its 'type' and 'id' properties will be used to form
24     * the path.
25     *
26     * @param {string|Object} pathOrObj the path or object
27     *
28     * @return {string} the URL to the resource
29     *
30     * @throws {Error} if the object didn't have both 'type' and 'id'
31     *     properties
32     *
33     * @private
34     */
35    function storageUrl(pathOrObj) {
36        var ident = (typeof(pathOrObj) === "string") ? pathOrObj : path(pathOrObj);
37        if (!ident) {
38            throw new Error("Invalid object or storage path");
39        }
40        return "rapidcontext/storage/" + ident.replace(/^\//, "");
41    }
42
43    /**
44     * Returns the storage path for an object. The object must have
45     * both the 'type' and 'id' properties set.
46     *
47     * @param {Object} obj the object to store
48     *
49     * @return {string} the storage path, or null if not available
50     *
51     * @memberof RapidContext.Storage
52     */
53    function path(obj) {
54        var type = obj && obj.type && obj.type.split("/")[0];
55        return (type && obj.id) ? type + "/" + obj.id : null;
56    }
57
58    /**
59     * Reads an object on a storage path. Note that this will return
60     * a JSON representation of the object, regardless of the actual
61     * object type.
62     *
63     * @param {string|Object} pathOrObj the path or object to read
64     *
65     * @return {Promise} a `RapidContext.Async` promise that will
66     *         resolve with the object data
67     *
68     * @memberof RapidContext.Storage
69     */
70    function read(pathOrObj) {
71        var url = storageUrl(pathOrObj);
72        url += url.endsWith("/") ? "index.json" : ".json";
73        url += "?_=" + (+new Date() % 100000);
74        return RapidContext.App.loadJSON(url, null, null);
75    }
76
77    /**
78     * Writes an object to a storage path. Any previous object on the
79     * specified path will be removed. If a path is specified without
80     * data, only the removal is performed.
81     *
82     * @param {string|Object} pathOrObj the path or object to write
83     * @param {Object} [data] the object to write (if path was string)
84     *
85     * @return {Promise} a `RapidContext.Async` promise that will
86     *         resolve when the object has been written
87     *
88     * @memberof RapidContext.Storage
89     */
90    function write(pathOrObj, data) {
91        var url = storageUrl(pathOrObj);
92        if (typeof(pathOrObj) == "string" && data == null) {
93            return RapidContext.App.loadXHR(url, null, { method: "DELETE" });
94        } else {
95            var headers = { "Content-Type": "application/json" };
96            var opts = { method: "POST", headers: headers };
97            return RapidContext.App.loadXHR(url + ".yaml", data || pathOrObj, opts);
98        }
99    }
100
101    /**
102     * Updates an object with properties from a partial object. The
103     * properties in the partial object will overwrite any previous
104     * properties with the same name in the destination object. No
105     * merging of property values will be performed.
106     *
107     * @param {string|Object} pathOrObj the path or object to write
108     * @param {Object} [data] the partial object (changes) to write
109     *
110     * @return {Promise} a `RapidContext.Async` promise that will
111     *         resolve with the updated data object on success
112     *
113     * @memberof RapidContext.Storage
114     */
115    function update(pathOrObj, data) {
116        var url = storageUrl(pathOrObj);
117        var newPath = path(data);
118        var headers = { "Content-Type": "application/json" };
119        if (newPath && newPath != path(pathOrObj)) {
120            headers["X-Move-To"] = newPath + ".yaml";
121        }
122        var opts = { method: "PATCH", headers: headers };
123        return RapidContext.App.loadJSON(url, data || pathOrObj, opts);
124    }
125
126    // Create namespaces & export symbols
127    var RapidContext = window.RapidContext || (window.RapidContext = {});
128    var Storage = RapidContext.Storage || (RapidContext.Storage = {});
129    Object.assign(Storage, { path, read, write, update });
130
131})(this);
132