{"id":28194251,"url":"https://github.com/monkey-projects/oci-os","last_synced_at":"2026-02-23T01:32:14.120Z","repository":{"id":179394445,"uuid":"663424750","full_name":"monkey-projects/oci-os","owner":"monkey-projects","description":"Clojure library to access Oracle OCI object storage","archived":false,"fork":false,"pushed_at":"2025-01-23T08:08:50.000Z","size":111,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-26T17:56:51.812Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/monkey-projects.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-07-07T09:03:12.000Z","updated_at":"2025-01-23T08:08:53.000Z","dependencies_parsed_at":"2023-10-27T10:30:06.841Z","dependency_job_id":"8c645f79-e42a-4a80-9b00-b57ac7c962b7","html_url":"https://github.com/monkey-projects/oci-os","commit_stats":null,"previous_names":["monkey-projects/oci-os"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/monkey-projects/oci-os","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/monkey-projects%2Foci-os","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/monkey-projects%2Foci-os/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/monkey-projects%2Foci-os/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/monkey-projects%2Foci-os/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/monkey-projects","download_url":"https://codeload.github.com/monkey-projects/oci-os/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/monkey-projects%2Foci-os/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29734468,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-22T20:09:16.275Z","status":"ssl_error","status_checked_at":"2026-02-22T20:09:13.750Z","response_time":110,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-05-16T13:11:42.965Z","updated_at":"2026-02-23T01:32:14.098Z","avatar_url":"https://github.com/monkey-projects.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Monkey Projects OCI Object Storage\n\nThis is a Clojure library to access the [Oracle OCI Object Storage API](https://docs.oracle.com/en-us/iaas/Content/Object/home.htm).  You could of course use the OCI provided Java lib instead,\nbut I've found that it's pretty cumbersome to use.  Also, since it automatically\nmarshalls everything into Java POJOs, it's not very efficient.  The Clojure way\nis to simply process the incoming objects as a data structure.\n\nAnother reason why I wrote this is because I want to use it in a GraalVM native\nimage, and the Java lib simply has too many dependencies which make it difficult\nto build a native image and it would be bloated anyway.\n\n## Structure\n\nThe lib provides two layers to access the API.  One is the low-level access,\nthat is just a thin layer on top of the REST calls.  On top of that there is\na layer that provides convenience functions for often-used scenarios.\n\nUnder the hood it uses [Martian](https://github.com/oliyh/martian) to send HTTP\nrequests to the OCI API.  We're using the [Aleph](https://aleph.io)\n[plugin](https://github.com/monkey-projects/martian-aleph), because that http client\nis able to download large objects (files) without consuming much memory.\n\n## Usage\n\n[![Clojars Project](https://img.shields.io/clojars/v/com.monkeyprojects/oci-os.svg)](https://clojars.org/com.monkeyprojects/oci-os)\n\nInclude the library in your project:\n```clojure\n{:deps {com.monkeyprojects/oci-os {:mvn/version ..latest..}}}\n```\n\nThen include the namespace and create a context:\n```clojure\n(require '[monkey.oci.os.core :as os])\n\n;; Configuration, must contain the necessary properties to connect,\n;; see oci-sign for that\n(def config {:user-ocid ... }\n(def ctx (os/make-client config))\n\n;; Now you can make requests\n(def bucket-ns @(os/get-namespace ctx))  ; Returns the bucket namespace\n@(os/list-objects ctx {:ns bucket-ns :bucket-name \"my-bucket\"}) ; Lists bucket objects\n```\n\nIn order to gain access, you must provide the necessary configuration.  This\nis then used to sign the request.  See the [oci-sign library](https://github.com/monkey-projects/oci-sign)\nfor details, but you will need your tenancy OCID, user OCID, private key, key fingerprint\nand the region you're targeting.\n\nThe functions in the `core` namespace will unwrap the response by default, returning the\nresponse body on success.  If there is a failure, an exception will be thrown, with the\nfull response in the `ex-data`.  See below on how to send requests and gain access to\nthe raw response map.\n\n### Uploading Files\n\nCreating or updating files is done with the `put-object` function.  The options map should\ncontain the `:ns` (namespace), `:bucket-name`, `:object-name` and `:contents` values.  The\ncontents is a string with the file contents.  Request signature require calculating an SHA256\nhash for the body, so streaming is not supported.  For larger files, you should use multipart\nrequests (see below).\n\nThe upload and download requests don't produce JSON so the calls return the underlying\nAleph response, which contains also a `:body` value.\n```clojure\n@(os/put-object ctx {:ns \"...\" :bucket-name \"test-bucket\" :object-name \"test.txt\" :contents \"this is a test file\"})\n;; This will return an empty string on success\n\n;; Now you can download the file as well\n@(os/get-object {:ns \"...\" :bucket-name \"test-bucket\" :object-name \"test.txt\"})\n;; Returns the file contents from the :body.  Depending on the content type,\n;; this can be a string or an input stream.\n```\n\nBy default the `Content-Type` is `application/octet-stream`.  But you can override this by\nspecifying the raw header in the request options:\n```clojure\n@(os/put-object {:ns \"...\"\n                 ...\n\t\t :contents \"File contents\"\n\t\t :martian.core/request {:headers {\"content-type\" \"text.plain\"}}})\n```\nThis will explicitly pass in the `Content-Type` header to the backend, which will also\nbe returned when you download the file.\n\n### Multipart Uploads\n\nOften you will want to upload very large files, or maybe even streams of which you don't know\nbeforehand how large they are (e.g. logfiles).  For this you can use\n[multipart uploads](https://docs.oracle.com/en-us/iaas/Content/Object/Tasks/usingmultipartuploads.htm#Using_Multipart_Uploads).  With these you can upload a large object in chunks.  This library provides a\nwrapper around this, found in the [monkey.oci.os.stream](src/monkey/oci/os/stream.clj) namespace.\n\nThere are two main functions here: `stream-\u003emultipart` and `input-stream-\u003emultipart`.  The first\ntakes a [Manifold stream](https://cljdoc.org/d/manifold/manifold/0.4.2/doc/streams) and uploads\neach incoming message as a new multipart object.  This is useful for real-time streaming uploads.\n\nThe second takes a regular Java `InputStream` and uploads it until EOF has been reached, or\nthe stream is closed.  After that, it commits the multipart and the object is created in the\nbucket.  Be sure to close the stream yourself.  This is most useful for large files.  An example:\n\n```clojure\n(require '[monkey.oci.os.stream :as oss])\n(require '[manifold.deferred :as md])\n(require '[clojure.java.io :as io])\n\n;; Open the stream\n(def is (io/input-stream \"/path/to/very-large-file\"))\n;; Upload it\n(md/chain\n (oss/input-stream-\u003emultipart ctx\n  {:ns \"my-bucket-ns\"\n   :object-name \"/destination/path\"\n   :bucket-name \"my-bucket\"\n   :input-stream \"is\"})\n (fn [r]\n   ;; Close when EOF reached\n   (.close is)\n   r))\n;; This will return the result of the commit operation, after closing the file.\n```\n\nYou can also use a `finally` handler, to ensure the file is closed even in the case of errors.\nYou can also pass in `:close? true` in the options to do this.  The options map accepts the\nfollowing values:\n\n|Key|Required?|Default value|Description|\n|---|---|---|---|\n|`:ns`|Yes||The namespace where the bucket resides|\n|`:bucket-nane`|Yes||The name of the bucket to upload to|\n|`:object-name`|Yes||The name of the destination object|\n|`:input-stream`|Yes||Input stream to read from|\n|`:content-type`|No|`application/binary`|The content type to add as metadata|\n|`:close?`|No|`false`|Should the stream be closed after upload?|\n|`:buf-size`|No|`0x10000`|Max size of each part that is being uploaded|\n|`:progress`|No|`nil`|A function that will be invoked after each part upload|\n\nA `progress` fn can be passed if you want to be notified of upload progress.  It receives\na structure with the input arguments as well as the upload id (as assigned by OCI) and\nthe total number of bytes already uploaded up to that point.\n\n### Low-level Calls\n\nShould you need access to the full response, for example to read certain headers like `ETag`,\nyou can send requests using the lower-level `monkey.oci.os.martian` namespace.  These contain\nabout the same functions (one for every defined route), but they won't interpret the response,\nand instead return the full response map.\n\n```clojure\n(require '[monkey.oci.os.martian :as m])\n\n@(m/head-object {:ns ...})\n;; This will return the full response, with :headers to inspect, etc...\n```\n\nThis allows you to have more control over how requests are handled.  This can also be useful\nshould you want to handle 'expected' 4xx responses, instead of catching exceptions (which is\nbad form if you're actually expecting it to happen, right?)\n\n### Metadata\n\nObject storage allows storing additional custom information with objects in the form of\n[metadata](https://docs.oracle.com/en-us/iaas/Content/Object/Tasks/managingobjects.htm#HeadersAndMetadata).\nThese are passed on as headers when you use the `put-object` call.  If you use multipart\nuploads, you should pass a `:metadata` property in the body.  The `input-stream-\u003emultipart`\nalso allows `:metadata` in the options map.  Note that metadata keys **must** start with\n`:opc-meta-`, otherwise they will be ignored.\n\n```clojure\n;; Explicitly pass headers using the :martian.core/headers property\n(os/put-object ctx {:object-name \"...\" :martian.core/headers {:opc-meta-test-key \"test value\"}})\n\n;; Or when using multipart\n(oss/input-stream-\u003emultipart ctx {... :metadata {:opc-meta-test-key \"test value\"}})\n```\n\nSince the core functions automatically unwrap the response and only return the body, you\ncan't get the values of the metadata like this.  Instead, you will have to resort to the\nlow level calls instead.\n\n```clojure\n;; Fetch object details\n(def r @(m/get-object ctx {:object-name \"...\"}))\n\n;; Headers will contain the opc-meta-... values\n(:headers r)\n;; =\u003e {:opc-meta-test-key \"test value\"}, among others\n```\n\n## TODO\n\n - Add something that automagically generates the Martian routes from the OCI provided Java libs.\n   (Or find the OpenAPI specs.)\n\n## Copyright\n\nCopyright (c) 2023-2025 by Monkey Projects BV.\n\n[MIT License](LICENSE)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmonkey-projects%2Foci-os","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmonkey-projects%2Foci-os","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmonkey-projects%2Foci-os/lists"}