Browse Source

初始化提交

master
SisMaker 4 years ago
commit
0ca7199039
52 changed files with 10189 additions and 0 deletions
  1. +23
    -0
      .gitignore
  2. +21
    -0
      LICENSE
  3. +41
    -0
      README.md
  4. +286
    -0
      VelocyStream.md
  5. +188
    -0
      VelocyStream_zh.md
  6. +202
    -0
      include/agHttpCli.hrl
  7. +6
    -0
      include/erlArango.hrl
  8. +8
    -0
      rebar.config
  9. +360
    -0
      src/agApi/agAdminMonitor.erl
  10. +82
    -0
      src/agApi/agAnalyzers.erl
  11. +426
    -0
      src/agApi/agAqls.erl
  12. +140
    -0
      src/agApi/agAsyncResultHandling.erl
  13. +233
    -0
      src/agApi/agBulkImportExport.erl
  14. +107
    -0
      src/agApi/agCluster.erl
  15. +482
    -0
      src/agApi/agCollections.erl
  16. +98
    -0
      src/agApi/agDbMgr.erl
  17. +500
    -0
      src/agApi/agDocuments.erl
  18. +43
    -0
      src/agApi/agEdges.erl
  19. +43
    -0
      src/agApi/agEndPoints.erl
  20. +399
    -0
      src/agApi/agFoxxServices.erl
  21. +961
    -0
      src/agApi/agGeneralGraphs.erl
  22. +116
    -0
      src/agApi/agHotBackup.erl
  23. +413
    -0
      src/agApi/agIndexes.erl
  24. +241
    -0
      src/agApi/agMiscFuns.erl
  25. +75
    -0
      src/agApi/agRepairJobs.erl
  26. +734
    -0
      src/agApi/agReplication.erl
  27. +10
    -0
      src/agApi/agSimpleQueries.erl
  28. +92
    -0
      src/agApi/agTasks.erl
  29. +204
    -0
      src/agApi/agTransactions.erl
  30. +13
    -0
      src/agApi/agTraversals.erl
  31. +240
    -0
      src/agApi/agUserMgr.erl
  32. +216
    -0
      src/agApi/agViews.erl
  33. +78
    -0
      src/agHttpCli/agAgencyPoolMgrExm.erl
  34. +188
    -0
      src/agHttpCli/agAgencyPoolMgrIns.erl
  35. +15
    -0
      src/agHttpCli/agAgencyPool_sup.erl
  36. +96
    -0
      src/agHttpCli/agAgencyUtils.erl
  37. +278
    -0
      src/agHttpCli/agHttpCli.erl
  38. +281
    -0
      src/agHttpCli/agHttpProtocol.erl
  39. +45
    -0
      src/agHttpCli/agKvsToBeam.erl
  40. +127
    -0
      src/agHttpCli/agMiscUtils.erl
  41. +77
    -0
      src/agHttpCli/agSslAgencyExm.erl
  42. +401
    -0
      src/agHttpCli/agSslAgencyIns.erl
  43. +77
    -0
      src/agHttpCli/agTcpAgencyExm.erl
  44. +400
    -0
      src/agHttpCli/agTcpAgencyIns.erl
  45. +77
    -0
      src/agHttpCli/agVstAgencyExm.erl
  46. +400
    -0
      src/agHttpCli/agVstAgencyIns.erl
  47. +438
    -0
      src/agHttpCli/vst.erl
  48. +11
    -0
      src/erlArango.app.src
  49. +11
    -0
      src/erlArango_app.erl
  50. +30
    -0
      src/erlArango_sup.erl
  51. +155
    -0
      src/user_default.erl
  52. +1
    -0
      start.sh

+ 23
- 0
.gitignore View File

@ -0,0 +1,23 @@
.eunit
*.o
*.beam
*.plt
erl_crash.dump
.concrete/DEV_MODE
ebin
# rebar 2.x
.rebar
rel/example_project
ebin/*.beam
deps
# rebar 3
.rebar3
_build/
_checkouts/
rebar.lock
# idea
.idea
*.iml

+ 21
- 0
LICENSE View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 AICells
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

+ 41
- 0
README.md View File

@ -0,0 +1,41 @@
# erlArango
arangodb erlang driver
erlang otp21.2+ arangodb 3.6.2 3.7
# Feature
Efficient, fast and easy to use.
1. To make this driver as efficient as possible, customizations encapsulate an HTTP1.1 client(agHttpCli) with connection pooling.
Comparisons between packaged agHttpCli and similar HTTP client tests are available:[Address](https://github.com/SisMaker/httpc_bench)
2. This driver can use connection pooling or simply establish multiple connections in a single process (non-connection pooling mode) for various data operations.
Synchronous and asynchronous operations are supported when using connection pooling,
and you need to save the requestId extra if you want to use asynchronous operations Waiting for the received data to return,
the API encapsulated by the current driver all USES synchronous operation, and can be modified if asynchronous operation is needed.
Only synchronous operations are supported for single-process operations.
In single-process mode, compared with connection pooling mode, data replication between processes can be reduced once.
For operation of large amount of data, database connection can be established separately in data management process instead of connection pooling.
3. The connection pooling mode and connectionless pool mode API interface ensures the identity, does not need to be treated differently,
and is easy to understand and change between connection pooling mode and connectionless pool mode.
# Batch requests are not supported
https://www.arangodb.com/docs/stable/http/batch-request.html
# compile
rebar get-deps; rebar compile or rebar3 compile
Note: If you build Jiffy on The Windows platform, you will need to set up an additional compilation environment. [See jiffy for details](https://github.com/SisMaker/erlUtils/tree/master/src/docs)
# how to use
rebar: erl -pa ./ebin -pa ./deps/jiffy/ebin or
revar3: rebar3 shell
Non-connection pooling mode
Make a connection first
{ok, Socket} = agHttpCli:connect([]). %% Use default Settings
%% Then you can then call various apis using Socket as the first argument
agMgrDb:curDbInfo(Socket).
Connection pooling mode
application:ensure_all_started(erlArango). %% start app
agHttpCli:startPool(poolName, [], []). %% start pool
%% Then you can then invoke various apis using poolName as the first argument
agMgrDb:curDbInfo(poolName).

+ 286
- 0
VelocyStream.md View File

@ -0,0 +1,286 @@
Client / Server Communication (VST 1.1)
=======================================
Version 1.1.0 of 23 November 2016
HTTP
----
Use VelocyPack as body. Content-Type is `"application/vpack"`
Binary Protocol
---------------
This is not a request / response protocol. It is symmetric (in
principle). Messages can be sent back and forth, pipelined, multiplexed,
uni-directional or bi-directional.
It is possible that a message generates
- no response
- exactly one response
- multiple responses
The VelocyStream does **not** impose or specify or require one of the
above behaviors. The application must define a behavior in general or on
a per-request basis, see below. The server and client must then
implement that behavior.
### Message vs. Chunk
The consumer (client or server) will deal with messages. A message
consists of one or more VelocyPacks (or in some cases of certain parts
of binary data). How many VelocyPacks are part of a message is
completely application dependent, see below for ArangoDB.
It is possible that the messages are very large. As messages can be
multiplexed over one connection, large messages need to be split into
chunks. The sender/receiver class will accept a vector of VelocyPacks,
split them into chunks, send these chunks over the wire, assemble these
chunks, generates a vector of VelocyPacks and passes this to the
consumer.
### Chunks
In order to allow reassemble chunks, each package is prefixed by a small
header. A chunk is always at least 24 bytes long. The byte order is
ALWAYS little endian. The format of a chunk is the following, regardless
on whether it is the first in a message or a subsequent one:
| name | type | description |
| --------------- | ------------------------- | --- |
| length | uint32\_t | total length in bytes of the current chunk, including this header |
| chunkX | uint32\_t | chunk/isFirstChunk (upper 31bits/lowest bit), details see below |
| messageId | uint64\_t | a unique identifier, it is the responsibility of the sender to generate such an identifier (zero is reserved for not set ID) |
| messageLength | uint64\_t | the total size of the message. |
| Binary data | binary data blob | size b1 |
Clarification: "chunk" and "isFirstChunk" are combined into an unsigned
32bit value. Therefore it will be encoded as
uint32_t chunkX
and extracted as
chunk = chunkX >> 1
isFirstChunk = chunkX & 0x1
For the first chunk of a message, the low bit of the second uint32\_t is
set, for all subsequent ones it is reset. In the first chunk of a
message, the number "chunk" is the total number of chunks in the
message, in all subsequent chunks, the number "chunk" is the current
number of this chunk.
The total size of the data package is (24 + b1) bytes. This number is
stored in the length field. If one needs to send messages larger than
UINT32\_MAX, then these messages must be chunked. In general it is a
good idea to restrict the maximal size to a few megabytes.
**Notes:**
When sending a (small) message, it is important (for performance reasons)
to ensure that only one TCP
packet is sent. For example, by using sendmmsg under Linux
([*https://blog.cloudflare.com/how-to-receive-a-million-packets/*](https://blog.cloudflare.com/how-to-receive-a-million-packets/))
Implementors should nevertheless be aware that in TCP/IP one cannot
enforce this and so implementations must always be aware that some part
of the network stack can split packets and the payload might arrive in
multiple parts!
ArangoDB
========
### Request / Response
For an ArangoDB client, the request is of the following format, the
array is a VelocyPack array:
[
/* 0 - version: */ 1, // [int]
/* 1 - type: */ 1, // [int] 1=Req, 2=Res,..
/* 2 - database: */ "test", // [string]
/* 3 - requestType: */ 1, // [int] 0=Delete, ...
/* 4 - request: */ "/_api/collection", // [string\]
/* 5 - parameter: */ { force: true }, // [[string]->[string]]
/* 6 - meta: */ { x-arangodb: true } // [[string]->[string]]
]
Body (binary data)
If database is missing (entry is `null`), then "\_system" is assumed.
`type`:
1 = Request
2 = Response (final response for this message id)
3 = Response (but at least one more response will follow)
1000 = Authentication
`requestType`:
0 = DELETE
1 = GET
2 = POST
3 = PUT
4 = HEAD (not used in VPP)
5 = PATCH
6 = OPTIONS (not used in VPP)
For example:
The HTTP request
http://localhost:8529/_db/test/_admin/echo?a=1&b=2&c[]=1&c[]=3
With header:
X-ArangoDB-Async: true
is equivalent to
[
1, // version
1, // type
"test", // database
1, // requestType GET
"/_admin/echo", // request path
{ // parameters
a: 1,
b: 2,
c: [ 1, 3 ]
},
{ // meta
x-arangodb-async: true
}
]
The request is a message beginning with one VelocyPack. This VelocyPack
always contains the header fields, parameters and request path. If the
meta field does not contain a content type, then the default
`"application/vpack"` is assumed and the body will be one or multiple
VelocyPack object.
The response will be
[
1, // 0 - version
2 or 3, // 1 - type
400, // 2 - responseCode
{ etag: "1234" } // 3 - meta: [[str]->[str]]
]
Body (binary data)
Request can be pipelined or mixed. The responses are mapped using the
"messageId" in the header. It is the responsibility of the **sender** to
generate suitable "messageId" values.
The default content-type is `"application/vpack"`.
### Authentication
A connection can be authenticated with the following message:
[
1, // version
1000, // type
"plain", // encryption
"admin", // user
"plaintext", // password
]
or
[
1, // version
1000, // type
"jwt", // encryption
"abcd..." // token
]
The response is
{ "error": false }
if successful or
{
"error": true,
"errorMessage": "MESSAGE",
"errorCode": CODE
}
if not successful, and in this case the connection is closed by the server.
One can acquire a JWT token in the same way as with HTTP using the
open, unauthenticated route `/_open/auth` with the same semantics as
in the HTTP version. In this way, the complete authentication can be
done in a single session via JWT.
### Content-Type and Accept
In general the content-type will be VPP, that is the body is an object
stored as VelocyPack.
Sometimes it is necessary to respond with unstructured data, like text,
css or html. The body will be a VelocyPack object containing just a
binary attribute and the content-type will be set accordingly.
The rules are as follows.
#### Http
Request: Content-Type
- `"application/json"`: the body contains the JSON string representation
- `"application/vpack"`: the body contains a velocy pack
There are some handler that allow lists of JSON (seperared by newline).
In this case we also allow multiple velocy packs without any separator.
Request: Accept
- `"application/json"`: send a JSON string representation in the body,
if possible
- `"application/vpack"`: send velocy pack in the body, if possible
If the request asked for `"application/json"` or `"application/vpack"` and
the handler produces something else (i.e. `"application/html"`), then the
accept is ignored.
If the request asked `"application/json"` and the handler produces
`"application/vpack"`, then the VPACK is converted into JSON.
If the request asked `"application/vpack"` and the handler produces
"application/json", then the JSON is converted into VPACK.
#### VPP
Similar to HTTP with the exception: the "Accept" header is not supported
and `"application/json"` will always be converted into
"application/vpack". This means that the body contains one or more
velocy-packs. In general it will contain one - notable exception being
the import.
If the handler produces something else (i.e. `"application/html"`), then
The body will be a binary blob (instead of a velocy-pack) and the
content-type will be set accordingly.
The first bytes sent after a connection (the "client" side - even if the
program is bi-directional, there is a server listening to a port and a
client connecting to a port) are
VST/1.1\r\n\r\n
(11 Bytes)

+ 188
- 0
VelocyStream_zh.md View File

@ -0,0 +1,188 @@
Client / Server Communication (VST 1.1)
=======================================
Version 1.1.0 of 23 November 2016
# HTTP
使用VelocyPack作为身体。内容类型为"application/vpack"
Binary Protocol
---------------
这不是请求/响应协议。它是对称的(原则上)。消息可以来回发送,流水线,多路复用,单向或双向。
可能会生成一条消息
没有反应
只是一个回应
多重回应
该VelocyStream并没有强加或指定或要求上述行为之一。
应用程序必须在一般情况下或根据每个请求定义行为,
请参见下文。然后,服务器和客户端必须实现该行为。
### Message vs. Chunk
使用者(客户端或服务器)将处理消息。一条消息包含一个或多个VelocyPack(在某些情况下是二进制数据的某些部分)。
消息中有多少VelocyPacks完全取决于应用程序,请参阅下文了解ArangoDB。
消息可能很大。由于可以通过一个连接对消息进行多路复用,因此需要将大消息拆分为多个块。
发送者/接收者类将接受一个VelocyPacks向量,将其分割成块,通过导线发送这些块,组装这些块,
生成一个VelocyPacks向量并将其传递给消费者。
### Chunks
为了允许重组块,每个程序包都以一个 header 作为前缀。块始终至少为24个字节长。字节顺序始终是小端。
块的格式如下,无论它是消息中的第一个消息还是随后的消息:
名称 类型 描述
length uint32_t 当前块(包括此标头)的总长度(以字节为单位)
chunkX uint32_t chunk / isFirstChunk(高31位/最低位),详细信息请参见下文
messageId uint64_t 唯一标识符,发送方有责任生成这样的标识符(zero is reserved for not set ID)
messageLength uint64_t the total size of the message.
Binary data binary data blob size b1 |
声明:"chunk" and "isFirstChunk" are combined into an unsigned 32bit value.
Therefore it will be encoded as
uint32_t chunkX and extracted as
chunk = chunkX >> 1
isFirstChunk = chunkX & 0x1
对于消息的第一块,设置第二个uint32_t的低位,对于所有后续消息,将其复位。
在消息的第一个块中,数字 chunk 是消息中的块总数,在所有后续块中,数字 chunk 是该块的当前number。
数据包的总大小为(24 + b1)字节。此数字存储在length字段中。
如果需要发送大于UINT32_MAX的消息,则必须将这些消息分块。通常,最好将最大大小限制为几兆字节。
### **Notes:**
发送(小)消息时,(出于性能原因)确保仅发送一个TCP数据包非常重要。
例如,通过在Linux下使用sendmmsg
([https://blog.cloudflare.com/how-to-receive-a-million-packets](https://blog.cloudflare.com/how-to-receive-a-million-packets/))
但是,实现者应该意识到,在TCP / IP中不能强制执行此操作,因此实现者必须始终意识到,
网络堆栈的某些部分可以拆分数据包,并且有效负载可能分成多个部分!
ArangoDB
========
### Request / Response
对于ArangoDB客户端,请求的格式如下,该数组是VelocyPack数组:
[
/* 0 - version: */ 1, // [int]
/* 1 - type: */ 1, // [int] 1=Req, 2=Res,..
/* 2 - database: */ "test", // [string]
/* 3 - requestType: */ 1, // [int] 0=Delete, ...
/* 4 - request: */ "/_api/collection", // [string\]
/* 5 - parameter: */ { force: true }, // [[string]->[string]] http的请求参数列表
/* 6 - meta: */ { x-arangodb: true } // [[string]->[string]] http的header
]
Body (binary data)
如果数据库未设置(entry is `null`),则数据库为“ _system”。
#### type:
1 = Request
2 = Response (final response for this message id)
3 = Response (but at least one more response will follow)
1000 = Authentication
#### requestType:
0 = DELETE
1 = GET
2 = POST
3 = PUT
4 = HEAD (not used in VPP)
5 = PATCH
6 = OPTIONS (not used in VPP)
### For example:
HTTP请求
http://localhost:8529/_db/test/_admin/echo?a=1&b=2&c[]=1&c[]=3
With header::
X-ArangoDB-Async: true
is equivalent to
[
1, // version
1, // type
"test", // database
1, // requestType GET
"/_admin/echo", // request path
{ // parameters
a: 1,
b: 2,
c: [ 1, 3 ]
},
{ // meta
x-arangodb-async: true
}
]
该请求是一条以VelocyPack开头的消息。这个VelocyPack始终包含标题字段,参数和请求路径。
如果meta字段不包含内容类型,则采用默认值 "application/vpack",并且正文将是一个或多个VelocyPack对象。
#### The response will be
[
1, // 0 - version
2 or 3, // 1 - type
400, // 2 - responseCode
{ etag: "1234" } // 3 - meta: [[str]->[str]]
]
#### Body (binary data)
请求可以通过管道传输或混合。使用标头中的“ messageId”映射响应。发送者有责任生成合适的“ messageId”值。
默认内容类型为"application/vpack"。
### Authentication
A connection can be authenticated with the following message:
[
1, // version
1000, // type
"plain", // encryption
"admin", // user
"plaintext", // password
]
or
[
1, // version
1000, // type
"jwt", // encryption
"abcd..." // token
]
The response is
{ "error": false }
if successful or
{
"error": true,
"errorMessage": "MESSAGE",
"errorCode": CODE
}
如果不成功,则在这种情况下服务器将关闭连接。可以使用/_open/auth与HTTP版本相同的语义,使用未经身份验证的开放式路由,以与HTTP相同的方式获取JWT令牌。这样,可以通过JWT在单个会话中完成完整的身份验证。
### Content-Type and Accept
通常,内容类型将是VPP,即主体是存储为VelocyPack的对象。
有时有必要使用非结构化数据(例如文本,css或html)进行响应。主体将是一个仅包含二进制属性的VelocyPack对象,并将相应地设置content-type。
规则如下。
#### Http
Request: Content-Type
"application/json"`: the body contains the JSON string representation
`"application/vpack"`: the body contains a velocy pack
有一些处理程序允许JSON列表(由换行符分隔)。在这种情况下,我们还允许不使用任何分隔符的多个速度包。
Request: Accept
"application/json":如果可能,在正文中发送JSON字符串表示形式
"application/vpack":如果可能的话,在体内发送速度包
如果请求是"application/json"或"application/vpack"处理程序产生了其他请求(即"application/html"),那么将忽略接受。
如果请求被请求"application/json"并且处理程序产生 "application/vpack",则VPACK将转换为JSON。
如果请求被请求"application/vpack",并且处理程序生成“ application / json”,则JSON将转换为VPACK。
#### VPP
与HTTP相似,不同之处在于:不支持“ Accept”标头,并且"application/json"始终将其转换为“ application / vpack”。这意味着主体包含一个或多个速度包。通常,它将包含一个-值得注意的例外是导入。
如果处理程序产生了其他东西(即"application/html"),则主体将是一个二进制blob(而不是一个velocy-pack),并且将相应地设置content-type。
连接后发送的第一个字节(“客户端”端-即使程序是双向的,也有服务器在监听端口,而客户端在连接端口)
VST/1.1\r\n\r\n
(11 Bytes)

+ 202
- 0
include/agHttpCli.hrl View File

@ -0,0 +1,202 @@
%% agency
-define(agAgencyPoolMgr, agAgencyPoolMgr).
%% beam cache
-define(agBeamPool, agBeamPool).
-define(agBeamAgency, agBeamAgency).
%%
-define(DEFAULT_BASE_URL, <<"http://127.0.0.1:8529">>).
-define(DEFAULT_DBNAME, <<"_system">>).
-define(DEFAULT_USER, <<"root">>).
-define(DEFAULT_PASSWORD, <<"156736">>).
-define(DEFAULT_BACKLOG_SIZE, 1024).
-define(DEFAULT_CONNECT_TIMEOUT, 5000).
-define(DEFAULT_POOL_SIZE, 16).
-define(DEFAULT_IS_RECONNECT, true).
-define(DEFAULT_RECONNECT_MIN, 500).
-define(DEFAULT_RECONNECT_MAX, 120000).
-define(DEFAULT_TIMEOUT, infinity).
-define(DEFAULT_PID, self()).
-define(DEFAULT_SOCKET_OPTS, [binary, {active, true}, {nodelay, true}, {delay_send, true}, {keepalive, true}, {recbuf, 1048576}, {send_timeout, 5000}, {send_timeout_close, true}]).
-define(GET_FROM_LIST(Key, List), agMiscUtils:getListValue(Key, List, undefined)).
-define(GET_FROM_LIST(Key, List, Default), agMiscUtils:getListValue(Key, List, Default)).
-define(WARN(Tag, Format, Data), agMiscUtils:warnMsg(Tag, Format, Data)).
-define(miDoNetConnect, miDoNetConnect).
-record(miRequest, {
method :: method()
, path :: path()
, headers :: headers()
, body :: body()
, requestId :: tuple()
, fromPid :: pid()
, overTime = infinity :: timeout()
, isSystem = false :: boolean()
}).
-record(miRequestRet, {
requestId :: requestId(),
reply :: term()
}).
-record(reconnectState, {
min :: non_neg_integer(),
max :: non_neg_integer() | infinity,
current :: non_neg_integer() | undefined
}).
-record(srvState, {
poolName :: poolName(),
serverName :: serverName(),
userPassWord :: binary(),
host :: binary(),
dbName :: binary(),
rn :: binary:cp(),
rnrn :: binary:cp(),
reconnectState :: undefined | reconnectState(),
socket :: undefined | ssl:sslsocket(),
timerRef :: undefined | reference()
}).
-record(recvState, {
stage = header :: header | body | done, %% http(tcp)
contentLength :: undefined | non_neg_integer() | chunked,
statusCode :: undefined | 100..505,
headers :: undefined | [binary()],
buffer = <<>> :: binary(),
body = <<>> :: binary()
}).
-record(cliState, {
isHeadMethod = false :: boolean(), %% <<"HEAD">>
%method = undefined :: undefined | method(),
requestsIns = [] :: list(),
requestsOuts = [] :: list(),
backlogNum = 0 :: integer(),
backlogSize = 0 :: integer(),
status = leisure :: waiting | leisure,
curInfo = undefined :: tuple(),
recvState = undefined :: recvState() | undefined
}).
-record(dbOpts, {
host :: host(),
port :: 0..65535,
hostname :: hostName(),
dbName :: binary(),
protocol :: protocol(),
poolSize :: poolSize(),
userPassword :: binary(),
socketOpts :: socketOpts()
}).
-record(agencyOpts, {
reconnect :: boolean(),
backlogSize :: backlogSize(),
reconnectTimeMin :: pos_integer(),
reconnectTimeMax :: pos_integer()
}).
-type miRequest() :: #miRequest{}.
-type miRequestRet() :: #miRequestRet{}.
-type recvState() :: #recvState{}.
-type srvState() :: #srvState{}.
-type cliState() :: #cliState{}.
-type reconnectState() :: #reconnectState{}.
-type poolName() :: atom().
-type poolNameOrSocket() :: atom() | socket().
-type serverName() :: atom().
-type protocol() :: ssl | tcp.
-type method() :: binary().
-type headers() :: [{iodata(), iodata()}].
-type body() :: iodata() | undefined.
-type path() :: binary().
-type host() :: binary().
-type hostName() :: string().
-type poolSize() :: pos_integer().
-type backlogSize() :: pos_integer() | infinity.
-type requestId() :: {serverName(), reference()}.
-type socket() :: inet:socket() | ssl:sslsocket().
-type socketOpts() :: [gen_tcp:connect_option() | ssl:tls_client_option()].
-type error() :: {error, term()}.
-type dbCfg() ::
{baseUrl, binary()} |
{dbName, binary()} |
{user, binary()} |
{password, binary()} |
{poolSize, poolSize()} |
{socketOpts, socketOpts()}.
-type agencyCfg() ::
{reconnect, boolean()} |
{backlogSize, backlogSize()} |
{reconnectTimeMin, pos_integer()} |
{reconnectTimeMax, pos_integer()}.
-type dbCfgs() :: [dbCfg()].
-type dbOpts() :: #dbOpts{}.
-type agencyCfgs() :: [agencyCfg()].
-type agencyOpts() :: #agencyOpts{}.
%% http header
%% -type header() ::
%% 'Cache-Control' |
%% 'Connection' |
%% 'Date' |
%% 'Pragma'|
%% 'Transfer-Encoding' |
%% 'Upgrade' |
%% 'Via' |
%% 'Accept' |
%% 'Accept-Charset'|
%% 'Accept-Encoding' |
%% 'Accept-Language' |
%% 'Authorization' |
%% 'From' |
%% 'Host' |
%% 'If-Modified-Since' |
%% 'If-Match' |
%% 'If-None-Match' |
%% 'If-Range'|
%% 'If-Unmodified-Since' |
%% 'Max-Forwards' |
%% 'Proxy-Authorization' |
%% 'Range'|
%% 'Referer' |
%% 'User-Agent' |
%% 'Age' |
%% 'Location' |
%% 'Proxy-Authenticate'|
%% 'Public' |
%% 'Retry-After' |
%% 'Server' |
%% 'Vary' |
%% 'Warning'|
%% 'Www-Authenticate' |
%% 'Allow' |
%% 'Content-Base' |
%% 'Content-Encoding'|
%% 'Content-Language' |
%% 'Content-Length' |
%% 'Content-Location'|
%% 'Content-Md5' |
%% 'Content-Range' |
%% 'Content-Type' |
%% 'Etag'|
%% 'Expires' |
%% 'Last-Modified' |
%% 'Accept-Ranges' |
%% 'Set-Cookie'|
%% 'Set-Cookie2' |
%% 'X-Forwarded-For' |
%% 'Cookie' |
%% 'Keep-Alive' |
%% 'Proxy-Connection' |
%% binary() |
%% string().

+ 6
- 0
include/erlArango.hrl View File

@ -0,0 +1,6 @@
-define(AgGet, <<"GET ">>).
-define(AgPut, <<"PUT ">>).
-define(AgPost, <<"POST ">>).
-define(AgHead, <<"HEAD ">>).
-define(AgPatch, <<"PATCH ">>).
-define(AgDelete, <<"DELETE ">>).

+ 8
- 0
rebar.config View File

@ -0,0 +1,8 @@
{erl_opts, [{i, "include"}]}.
{edoc_opts, [{preprocess, true}]}.
{deps, [
{eVPack, {git, "http://192.168.0.88:53000/SisMaker/eVPack.git", {branch, master}}},
{jiffy, {git, "https://github.com/davisp/jiffy.git", {tag, "1.0.5"}}}
%% {jsx, {git, "https://github.com/talentdeficit/jsx.git", {tag, "v3.0.0"}}}
]}.

+ 360
- 0
src/agApi/agAdminMonitor.erl View File

@ -0,0 +1,360 @@
-module(agAdminMonitor).
-include("erlArango.hrl").
-compile(inline).
-compile({inline_size, 128}).
-compile([export_all, nowarn_export_all]).
% doc_address:https://www.arangodb.com/docs/stable/http/administration-and-monitoring.html
% Permalink读取全局日志
%
% GET /_admin/log
%
% upto upto必须为
% fatal 0
% error或1
% warning或2
% info或3
% debug 4 info
% level upto和level是互斥的
% start使lid值start
% size
% offset
% search search中指定的文本的日志条目
% sortsort为ascsort为desc asc
% JSON对象
% HTTP 200
% lid@LIT {lid}
% level
% timestamp1970-01-01
% text
% topic
% totalAmount
% 400up或level指定了无效值
% 500
getAdminLog(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/log">>, [], undefined).
getAdminLog(PoolNameOrSocket, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_admin/log", QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
%
% GET /_admin/log/level
% JSON对象
%
% 200
% 500
getAdminLogLevel(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/log/level">>, [], undefined).
%
% PUT /_admin/log/level
% JSON对象是必需的
% agency
% agencycomm
% authentication
%
% cache
% cluster
% collector
%
%
% config
% datafiles
%
%
% general
% graphs
%
%
% mmap
%
% pregel
%
%
%
% rocksdb
% ssl
% startup
%
% syscall
% 线
% trx
% v8
% views
% ldap
% audit-authentication
% audit-authorization
% audit-database
% audit-collection
% audit-view
% audit-document
% audit-service
% JSON对象
% JSON对象
% json来设置所有功能的日志级别
%
% -ArangoDB将关闭
% -
% -
% -
% -
% ---使
%
% 200
% 400JSON时返回
% 405使HTTP方法时返回
% 500
modifyAdminLogLevel(PoolNameOrSocket, MapData) ->
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, <<"/_admin/log/level">>, [], BodyStr).
%
% GET /_admin/statistics
% _admin / statistics-description返回的描述分组在一起的 访userTimeuserTime的值存储在同名属性中
% count为单位的总计 counts为单位的分发列表
% RocksDB存储引擎DBServer在事务提交到群集范围之前完成使
% HTTP 200
% errorfalse
% codeHTTP状态码-200
% time
% errorMessage
% enabledtrue
%
% minorPageFaultspagefaults
% majorPageFaultspagefaults
% userTime使CPU时间
% systemTime使CPU时间
% numberOfThreads线
% residentSizeRSS
% residentSizePercentRSS
% virtualSizeVSS
% client使
% sum
% count
% counts
% connectionTime
% totalTime
% requestTime
% queueTime
% ioTimeIO时间
% bytesSent
% bytesReceived
% httpConnectionshttp连接数
% http
% requestsTotalhttp请求总数
% requestsAsynchttp请求的总数
% RequestsGet使GET动词的请求数
% requestHead使HEAD动词的请求数
% requestPost使POST动词的请求数
% requestsPut使PUT动词的请求数
% requestsPatch使PATCH动词的请求数
% requestsDelete使DELETE动词的请求数
% requestsOptions使OPTIONS动词的请求数
% requestOther使
%
%
% physicalMemory
% Transactions
%
%
%
% middleCommits
% v8ContextV8 JavaScript上下文的统计信息
% V8上下文的数量
% busyV8上下文的数量
% dirty使使
% free使V8上下文的数量
% max--javascript.v8-contexts配置生成的V8上下文总数
% V8内存/使/10
% contextIdID
% tMax10
% countOfTimes10
% heapMax10
% heapMin10
% 线线V8或jemalloc的线程和系统线程
% scheduler-threads线
% 线
% 线
getAdminProps(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/statistics">>, [], undefined).
%
%
% GET /_admin/statistics-description
% / _admin / statistics返回的统计信息的描述
%
% group
% name
% description
%
% group
%
% name
% description
%
% cuts
%
% HTTP 200
%
% group
% name
% description
%
% group
%
% name
% description
%
% cuts
%
% codeHTTP状态码
% false
getAdminStatisticsDesc(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/statistics-description">>, [], undefined).
% TLS
% TLS数据的摘要
% TLS数据CA
% GET /_admin/server/tls
% TLS数据的摘要JSON响应将包含result具有以下组件的字段
% keyfile
% clientCACA的信息
% 使SNISNI
%
% JSON对象
% SHA256SHA256的字符串
% certificatesJSON数组
% privateKeySHA256keyfile clientCASHA256的JSON字符串
% API
%
% 200API将返回HTTP 200
getAdminTLS(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/server/tls">>, [], undefined).
% TLS数据的重新加载并返回摘要永久链接
% TLS数据CA
% POST /_admin/server/tls
% API调用触发所有TLS数据的重新加载JSON响应与相应的GET请求完全相同
% API
%
% 200API将返回HTTP 200
% 403使APIHTTP 403 FORBIDDEN
triggerAdminTLS(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_admin/server/tls">>, [], undefined).
%
% GET /_admin/metrics
% Prometheus格式返回实例的当前指标Prometheus收集
% 使arangodb_或rocksdb_字符串发布
% API添加到Prometheus配置文件中进行收集
%
% 200
% 404使--server.export-metrics-api false API API
getAdminMetrics(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/metrics">>, [], undefined).
%
%
%
% GET /_admin/server/mode
% json响应将包含一个mode值为readonly或的字段default1004ERROR_READ_ONLY11ERROR_FORBIDDEN
% API
%
% 200API将返回HTTP 200
getAdminServerMode(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/server/mode">>, [], undefined).
% ID
% ID
% GET /_admin/server/id
% ID
%
% 200
% 500
getAdminServerId(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/server/id">>, [], undefined).
%
% GET /_admin/server/role
% role属性中返回
% SINGLE
%
% PRIMARYDBServer
% 使
%
% UNDEFINEDUNDEFINED
% HTTP 200
%
% codeHTTP状态码200
% errorNum
% [ SINGLEPRIMARYSECONDARYAGENTUNDEFINED ]
getAdminServerRole(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/server/role">>, [], undefined).
%
% GET /_admin/server/availability
%
% API使
%
% 200API将返回HTTP 200
% 503HTTP 503
getAdminServerAvailability(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/server/availability">>, [], undefined).
% DBserver
%
% GET /_admin/clusterStatistics
%
% DBserverDBserver的统计信息
%
% 200
% 400ID
% 403
getAdminClusterProps(PoolNameOrSocket, DBserver) ->
Path = <<"/_admin/clusterStatistics?DBserver=", (agMiscUtils:toBinary(DBserver))/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
% Permalink
%
% GET /_admin/cluster/health
% JSON对象codeerrorerrorNumerrorMessage字段适当
% ClusterIdUUID字符串
% Health
% <nodeID>Health将由节点ID键入
% Endpoint
% Role"AGENT""COORDINATOR""DBSERVER"
% CanBeDeleted
% Version使ArangoDB的版本字符串
% Engine使
% Statusdbservers节点运行状况"GOOD""BAD"15"FAILED"
%
%
% SyncStatusStatus"UNKNOWN""UNDEFINED""STARTUP""STOPPING""STOPPED""SERVING""SHUTDOWN"
% LastAckedTimeISO 8601
% ShortName"Coordinator0001"
% TimestampISO 8601
% Host
%
% AdvertisedEndpointIP地址或负载平衡器
%
% LeaderID
% Leadingtruefalse
% LastAckedTimeacked以秒为单位
%
% 200
getAdminClusterHealth(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/cluster/health">>, [], undefined).
%
% POST /_admin/routing/reload
%
%
% 200
reloadAdminRouting(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_admin/routing/reload">>, [], undefined).

+ 82
- 0
src/agApi/agAnalyzers.erl View File

@ -0,0 +1,82 @@
-module(agAnalyzers).
-include("erlArango.hrl").
-compile(inline).
-compile({inline_size, 128}).
-compile([export_all, nowarn_export_all]).
% doc_address:https://www.arangodb.com/docs/stable/http/analyzers.html
% HTTP接口
% /_api/analyzer端点访问用于管理ArangoSearch Analyzer的RESTful API
%
%
% POST /_api/analyzer
% JSON对象是必需的
% name
% type
% properties
% features
%
%
% 200
% 201
% 400
% 403使
newAnalyzer(PoolNameOrSocket, MapData) ->
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_api/analyzer">>, [], BodyStr).
%
% GET /_api/analyzer/{analyzer-name}
%
% Analyzer-name
%
% name
% type
% properties
% features
%
% 200
% 404
getAnalyzer(PoolNameOrSocket, AnalyzerName) ->
Path = <<"/_api/analyzer/", AnalyzerName/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
%
% GET /_api/analyzer
%
% name
% type
% properties
% features
%
% 200
getAnalyzerList(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/analyzer">>, [], undefined).
%
% DELETE /_api/analyzer/{analyzer-name}
%
% Analyzer-name
%
% force 使使false
% analyzer-name标识的Analyzer配置
%
%
% name
%
% 200
% 400
% 403
% 404
% 409使
delAnalyzer(PoolNameOrSocket, AnalyzerName) ->
Path = <<"/_api/analyzer/", AnalyzerName/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).
delAnalyzer(PoolNameOrSocket, AnalyzerName, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/analyzer/", AnalyzerName/binary, QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).

+ 426
- 0
src/agApi/agAqls.erl View File

@ -0,0 +1,426 @@
-module(agAqls).
-include("erlArango.hrl").
-compile(inline).
-compile({inline_size, 128}).
-compile([export_all, nowarn_export_all]).
% doc_address:
% AQL Query Cursors:
% https://www.arangodb.com/docs/stable/http/aql-query-cursor.html
% AQL Query:
% https://www.arangodb.com/docs/stable/http/aql-query-cursor.html
% AQL Query Results Cache:
% https://www.arangodb.com/docs/stable/http/aql-query-cursor.html
% AQL User Functions Management:
% https://www.arangodb.com/docs/stable/http/aql-query-cursor.html
% AQL操作
% AQL查询游标的HTTP接口
% ArangoDB查询的HTTP接口的简介AQL的结果和简单查询作为游标返回便
% HTTP POST请求将查询详细信息从客户端传送到服务器
%
%
%
%
% 使
%
% batchSize属性来控制此数字
% hasMore属性来检查是否已检索到完整的结果集false
% 使
% batchSize属性设置的数量id属性中 hasMore属性将设置为true
% HTTP访问游标
%
%
% POST /_api/cursor
% JSON对象
% JSON对象是必需的
% query
% count count count count
% batchSize使BATCHSIZE的值 0
% ttl使30
% cache使AQL查询结果缓存的标志false
% memoryLimit使0
% bindVars/
% options/
% FULLCOUNTLIMIT子句 FULLCOUNT{ ... , "extra": { "stats": { "fullCount": 123 } } }FULLCOUNT属性将包含的文档数量的结果应用于在查询的最后顶层限制之前MySQL的SQL_CALC_FOUND_ROWS暗示LIMIT优化使LIMIT子句并且在查询中实际使用LIMIT子句时fullCount属性才可能出现在结果中
% maxPlansAQL查询优化器创建的最大计划数
% maxWarningCount10
% failOnWarningtrue时使false时--query.fail-on-warning用于设置failOnWarning的默认值
% streamtruefalse时arangod实例上API进行访问ttl使使MMFiles上的写锁cachecount并且fullCount不适用于流查询false
% Optimizer
% rules-+all-all禁用所有规则
% profiletrue或1Extra Return属性的子属性配置文件中返回其他查询概要信息2Extra Return属性的子属性stats.nodes中包含每个查询计划节点的执行统计信息extra.plan中返回
% satelliteSyncWaitEnterprise Edition参数允许配置DB-Server将有多长时间使查询中涉及的Satellite集合同步60.0
% maxRuntime0.0
% maxTransactionSizeRocksDB存储引擎的尊敬
% middleCommitSizeRocksDB存储引擎的尊敬
% middleCommitCountRocksDB存储引擎的尊敬
% skipInaccessibleCollectionsAQL查询访访AQL查询访访
%
% POST请求的主体中以JSON表示形式传递
% HTTP 201
% errorfalse
% codeHTTP状态码
% result
% hasMore
% countcount属性的情况下执行的
% idID
% extraJSON对象 extra.stats子属性将包含已修改的文档数和由于错误而无法修改的文档数ignoreErrors查询选项
% cached return属性将不包含任何stats子属性
%
% JSON格式不正确或请求中缺少查询规范HTTP 400
% JSON格式不正确或请求中缺少查询规范使HTTP 400
% JSON对象
% errortrue
% codeHTTP状态码
% errorNum
% errorMessage
% 使HTTP 400
%
% 404访HTTP 404
% 405使HTTP方法HTTP 405
newCursor(PoolNameOrSocket, MapData) ->
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_api/cursor">>, [], BodyStr).
%
% PUT /_api/cursor/{cursor-identifier}
%
% cursor-identifier
%
% id
%
% hasMorefalse
% count
% 使hasMore返回truehasMore为falsehasMore属性的值为 false
%
% 200HTTP 200
% 400使HTTP 404
% 404使HTTP 404
nextCursor(PoolNameOrSocket, CursorId) ->
Path = <<"/_api/cursor/", (agMiscUtils:toBinary(CursorId))/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], undefined).
%
% DELETE /_api/cursor/{cursor-identifier}
%
% cursor-identifierID
%
% 使HTTP DELETE请求在任何较早的时间显式销毁游标ID必须作为URL的一部分包含在内
%
%
% 202
% 404404使
delCursor(PoolNameOrSocket, CursorId) ->
Path = <<"/_api/cursor/", (agMiscUtils:toBinary(CursorId))/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).
% AQL查询的HTTP接口
%
%
% ArangoDB有一个HTTP接口AQL查询HTTP接口来检索任何有效AQL查询的执行计划
% AQL查询
% AQL查询并返回有关它的信息
% POST /_api/explain
% JSON对象
% JSON对象是必需的
% querybindVars中传递options属性中传递查询的其他选项
% bindVars/
% options
% allPlanstruefalse
% maxNumberOfPlans
% Optimizer
% rules-+all-all禁用所有规则
%
% AQL查询HTTP POST请求将查询字符串发送到服务器
%
% allPlans选项
% warningsstats属性以及一些优化程序统计信息allPlans设置为false 使allPlans
% JSON对象
% nodes
% estimatedCost
% collections使collections
% rules
% variables使
%
% 200使HTTP 200plan属性中返回最佳执行计划allPlansallPlans属性中返回一系列计划
% 400HTTP 400JSON对象中的错误详细信息HTTP 400
% 404访HTTP 404
explainQuery(PoolNameOrSocket, MapData) ->
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_api/explain">>, [], BodyStr).
% AQL查询并返回有关它的信息
% POST /_api/query
% JSON对象是必需的
% queryHTTP POST请求将查询字符串传递到服务器
% /api/cursor
%
% 200使HTTP 200bindVars属性中返回在查询中找到的绑定参数的名称collections属性中返回查询中使用的collections的数组JSON ast属性将包含查询的抽象语法树表示形式ast的格式在将来的ArangoDB版本中可能会发生变化ArangoDB如何解释给定查询
% 400HTTP 400JSON对象中的错误详细信息
parseQuery(PoolNameOrSocket, MapData) ->
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_api/query">>, [], BodyStr).
%
% ArangoDB具有HTTP接口AQL查询列表和慢速AQL查询列表使APIHTTP请求的数据库中启用查询跟踪
% AQL查询跟踪的配置
% GET /_api/query/properties
% JSON对象
% enabledtrue false
% trackSlowQueriestrueslowQueryThreshold中设置的值 enabled属性设置为true
% trackBindVarstrue使
% maxSlowQueries
% slowQueryThresholdslowQueryThreshold的值以秒为单位指定
% maxQueryStringLength使使
%
% 200
% 400HTTP 400
getQueryProps(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/query/properties">>, [], undefined).
% AQL查询跟踪的配置
% PUT /_api/query/properties
% JSON对象是必需的
% enabledtrue false
% trackSlowQueriestrueslowQueryThreshold中设置的值 enabled属性设置为true
% trackBindVarstrue使
% maxSlowQueries
% slowQueryThresholdslowQueryThreshold的值以秒为单位指定
% maxQueryStringLength使使
% HTTP请求主体的属性属性中传递JSON对象
% HTTP响应中返回当前属性集
%
% 200
% 400HTTP 400
changeQueryProps(PoolNameOrSocket, MapData) ->
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, <<"/_api/query/properties">>, [], BodyStr).
% AQL查询的列表
% GET /_api/query/current
% AQL查询JSON对象
% idID
% query
% bindVars使
% started
% runTime
% state
% stream使
%
% 200
% 400HTTP 400
currentQuery(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/query/current">>, [], undefined).
% AQL查询的列表
% GET /_api/query/slow
% AQL查询maxSlowQueries slowQueryThreshold
% JSON对象
% idID
% query
% bindVars使
% started
% runTime
% state
% stream使
%
% 200
% 400HTTP 400
getSlowQuery(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/query/slow">>, [], undefined).
% AQL查询列表
% DELETE /_api/query/slow
% AQL查询列表
%
% 200HTTP 200
% 400使HTTP 400
delSlowQuery(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, <<"/_api/query/slow">>, [], undefined).
%
% AQL查询也可以在服务器上终止ArangoDB通过HTTP接口提供了终止功能IDIDkill标志
% AQL查询
% DELETE /_api/query/{query-id}
%
% query-idID
%
%
% 200HTTP 200
% 400使HTTP 400
% 404ID的查询时HTTP 404
killQuery(PoolNameOrSocket, QueryId) ->
Path = <<"/_api/query/", (agMiscUtils:toBinary(QueryId))/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).
% AQL查询结果缓存的HTTP接口
% AQL查询结果缓存的API方法
% AQL查询结果缓存中存储结果的列表
% GET /_api/query-cache/entries
% AQL查询结果JSON对象
% hash
% query
% bindVars
% size
% results/
% started
% hits访 0
% runTime
% dataSources使/
%
% 200
% 400HTTP 400
getQueryCaches(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/query-cache/entries">>, [], undefined).
% AQL查询结果缓存中的所有结果
% DELETE /_api/query-cache
%
%
% 200HTTP 200
% 400使HTTP 400
clearQueryCaches(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, <<"/_api/query-cache">>, [], undefined).
% AQL查询结果缓存的全局配置
% GET /_api/query-cache/properties
% AQL查询结果缓存配置JSON对象
% modeAQL查询结果缓存运行的模式offon或demand
% maxResults
% maxResultsSize
% maxEntrySize
% includeSystem
%
% 200
% 400HTTP 400
getQCacheProps(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/query-cache/properties">>, [], undefined).
% AQL查询结果缓存属性
% PUT /_api/query-cache/properties
% JSON对象是必需的
% modeAQL查询缓存应以哪种模式运行offon或demand
% maxResults
% maxResultsSize
% maxEntrySize
% includeSystem
% HTTP响应中返回当前属性集
% 使AQL查询缓存的全局属性HTTP请求主体的属性属性中传递JSON对象
%
% 200
% 400HTTP 400
changeQCacheProps(PoolNameOrSocket, MapData) ->
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, <<"/_api/query-cache/properties">>, [], BodyStr).
% AQL用户功能管理固定链接
% AQL用户功能的ArangoDB HTTP接口的简介AQL用户功能是一种使用用户定义的JavaScript代码扩展ArangoDB查询语言AQL
% AQL用户功能及其含义的概述 AQL
% HTTP接口提供用于添加AQL用户功能的API
% _aqlfunctions中访访
% AQL用户功能
% POST /_api/aqlfunction
% JSON对象是必需的
% name
% code
% isDeterministicisDeterministic属性是当前未使用但对于优化可以在以后使用
% HTTP 200HTTP 400
% HTTP 200使HTTP 200
% errorfalse
% codeHTTP状态码
% isNewlyCreatedfalse
% HTTP 201使HTTP 201
% errorfalse
% codeHTTP状态码
% isNewlyCreatedtrue
% HTTP 400JSON格式不正确或请求中缺少必需数据使HTTP 400
% errortrue
% codeHTTP状态码
% errorNum
% errorMessage
newUserFun(PoolNameOrSocket, MapData) ->
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_api/aqlfunction">>, [], BodyStr).
% AQL用户功能
% DELETE /_api/aqlfunction/{name}
%
% nameAQL用户功能的名称
%
% group - 0
% falseHTTP 404
% name标识的现有AQL用户功能或功能组
% HTTP 200使HTTP 200
% errorfalse
% codeHTTP状态码
% DeleteCount1group设置为false时true>= 0group
% HTTP 400使HTTP 400
% errortrue
% codeHTTP状态码
% errorNum
% errorMessage
% HTTP 404使HTTP 404
% errortrue
% codeHTTP状态码
% errorNum
% errorMessage
delUserFun(PoolNameOrSocket, UserFunName) ->
Path = <<"/_api/aqlfunction/", (agMiscUtils:toBinary(UserFunName))/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).
delUserFun(PoolNameOrSocket, UserFunName, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/aqlfunction/", (agMiscUtils:toBinary(UserFunName))/binary, QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).
% AQL用户功能
% GET /_api/aqlfunction
%
% namespaceresult下的命名空间namespace返回所有已注册的AQL用户函数
% AQL用户功能
% JSON数组result下找到的所有用户函数
% HTTP 200HTTP 200
% errorfalse
% codeHTTP状态码
% result
% name
% code
% isDeterministicisDeterministic属性是当前未使用但对于优化可以在以后使用
% HTTP 400使HTTP 400
% errortrue
% codeHTTP状态码
% errorNum
% errorMessage
getUserFuns(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/aqlfunction">>, [], undefined).
getUserFuns(PoolNameOrSocket, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/aqlfunction", QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).

+ 140
- 0
src/agApi/agAsyncResultHandling.erl View File

@ -0,0 +1,140 @@
-module(agAsyncResultHandling).
-include("erlArango.hrl").
-compile(inline).
-compile({inline_size, 128}).
-compile([export_all, nowarn_export_all]).
% doc_address:https://www.arangodb.com/docs/stable/http/async-results-management.html
% HTTP接口
%
% ArangoDB提供了执行客户端请求的各种方法
%
%
% ArangoDB是多线程服务器线
%
% ArangoDB
%
% 线
%
% 使HTTP连接 HttpJobPutCancel下的内容
%
%
% 1.4使ArangoDBHTTP标头x-arango-asynctrueArangoDB会将请求放入内存任务队列中HTTP请求返回HTTP 202
%
% HTTP 202
%
%
%
%
%
% --server.maximal-queue-size确定HTTP 500
%
% 使 HttpJobPutCancel
%
%
% HTTP标头x-arango-asyncArangoDB服务器如上所述异步执行操作HTTP响应标头x-arango-async-id中返回作业IDID与/ _api / job上的HTTP API结合使用
%
% API询问ArangoDB服务器ID传递给异步作业APIID取消运行异步作业HttpJobPutCancel
%
% ArangoDB将保留通过x-arango-async
%
% API还提供了用于垃圾回收的方法使ArangoDB不会人为地限制尚未获取的结果的数量
%
%
%
%
%
%
% 使ID取消异步运行的作业HttpJobPutCancel中所述PUT请求
%
% C ++JavaScript代码执行CRUD操作直接在C ++AQL查询和事务由JavaScript代码执行JavaScript代码使JavaScript线程中触发不可捕获的异常C ++
%
% DB-Server的任务
%
%
%
%
% PUT /_api/job/{job-id}
%
% job-idID
% job-id标识的异步作业的结果job-id调用一次此方法HTTP标头x-arango-async-job-id
%
% 204job-id请求的作业仍在待处理x-arango-async-id HTTP标头
% 400IDx-arango-async-id HTTP标头
% 404404x-arango-async-id HTTP标头
getAsyncJobRet(PoolNameOrSocket, JodId) ->
Path = <<"/_api/job/", (agMiscUtils:toBinary(JodId))/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], undefined).
%
% PUT /_api/job/{job-id}/cancel
%
% job-idID
% ID标识的当前正在运行的作业
%
% 200
% 400IDx-arango-async-id HTTP标头
% 404404x-arango-async-id HTTP标头
cancelAsyncJob(PoolNameOrSocket, JodId) ->
Path = <<"/_api/job/", (agMiscUtils:toBinary(JodId))/binary, "/cancel">>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], undefined).
%
% DELETE /_api/job/{type}#by-type
%
% type
% all
% expiredstamp必须是UNIX时间戳
% an actual job-id
%
% stampUNIX时间戳记
% 使
%
% 200
% 400type或值无效
% 404type为job-idid的异步作业404
delAsyncJobRet(PoolNameOrSocket, TypeOrJodId) ->
Path = <<"/_api/job/", (agMiscUtils:toBinary(TypeOrJodId))/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).
delAsyncJobRet(PoolNameOrSocket, TypeOrJodId, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/job/", (agMiscUtils:toBinary(TypeOrJodId))/binary, QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).
%
% GET /_api/job/{job-id}
%
% job-idID
% HTTP响应代码来确定处理状态
%
% 200job-id请求的作业200
% 204job-id请求的作业仍在待处理
% 404404
getAsyncJobStatus(PoolNameOrSocket, JodId) ->
Path = <<"/_api/job/", (agMiscUtils:toBinary(JodId))/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
% ID
% GET /_api/job/{type}#by-type
%
% type done pending done将使该方法返回可以获取其结果的异步作业的IDpending将返回尚未完成的异步作业的ID
%
% countID数使
% ID列表使
%
% 200
% 400type或值无效
getAsyncJobList(PoolNameOrSocket, Type) ->
Path = <<"/_api/job/", (agMiscUtils:toBinary(Type))/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
getAsyncJobList(PoolNameOrSocket, Type, Count) ->
Path = <<"/_api/job/", (agMiscUtils:toBinary(Type))/binary, "?count=", (agMiscUtils:toBinary(Count))/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).

+ 233
- 0
src/agApi/agBulkImportExport.erl View File

@ -0,0 +1,233 @@
-module(agBulkImportExport).
-include("erlArango.hrl").
-compile(inline).
-compile({inline_size, 128}).
-compile([export_all, nowarn_export_all]).
% doc_address:https://www.arangodb.com/docs/stable/http/bulk-imports.html
% HTTP接口
% ArangoDB提供了一个HTTP接口
% JSON格式提供
% JSON文档
%
% / _api / import使HTTP POST请求将数据发送到此URLPOST请求的正文中
%
% waitForSync查询参数设置为true
% complete查询参数设置为true使使
% complete具有除true以外的其他值
% details查询参数设置为true使API返回有关无法导入的文档的详细信息details为truedetails属性false或省略
% JSON编码的列表中导入文档
% POST /_api/import#document
%
% collection
% fromPrefix_from属性中值的可选前缀_from输入值之前_from
% toPrefix_to属性中值的可选前缀_to输入值之前_to
% overwrite true或yes
% waitForSync
% onDuplicate
% error
% update使
% replace
% ignore
% _key属性时updatereplace和ignore才起作用
%
% complete true或yes使使
% detailstrue或yesdetails
%
%
% JSON编码的属性值数组组成JSON编码的属性名称数组
% collection-nameJSON编码的属性名称数组JSON编码的属性值数组
%
% JSON对象
% created
% errors
% emptydocuments或只能包含大于零的值auto
% updated/onDuplicate update或replace
% ignored onDuplicate设置为ignore
% detailsdetails设置为truedetails属性
%
%
% 201
% 400type包含无效值collection指定no
% 404collection或导入边的_from或_to属性引用未知集合
% 409complete则返回 true
% 500500
docImport(PoolNameOrSocket, ListOfList, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/import", QueryBinary/binary>>,
BodyStr = <<<<(jiffy:encode(OneList))/binary, "\n">> || OneList <- ListOfList>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr).
% JSON导入文档
% POST /_api/import#json
%
% type type可以具有以下值
% documents使JSON编码的文档JSON对象需要用换行符分隔
% list使JSON编码数组
% auto documents或list
% collection
% fromPrefix_from属性中值的可选前缀_from输入值之前_from
% toPrefix_to属性中值的可选前缀_to输入值之前_to
% overwrite true或yes
% waitForSync
% onDuplicate
% error
% update使
% replace
% ignore
% _key属性时updatereplace和ignore才起作用
% complete true或yes使使
% detailstrue或yesdetails
%
% JSON编码的对象数组JSON对象的字符串
% collection-nameJSON表示形式必须作为POST请求的主体传递JSON对象JSON数组
%
% JSON对象
% created
% errors
% emptydocuments或只能包含大于零的值auto
% updated/onDuplicate update或replace
% ignored onDuplicate设置为ignore
% detailsdetails设置为truedetails属性
%
% 201
% 400type包含无效值collection指定no
% 404collection或导入边的_from或_to属性引用未知集合
% 409complete则返回 true
% 500500
jsonImport(PoolNameOrSocket, MapDataList, QueryPars) ->
case lists:keyfind(type, 1, QueryPars) of
{type, list} ->
BodyStr = jiffy:encode(MapDataList);
{type, documents} ->
BodyStr = <<<<(jiffy:encode(OneList))/binary, "\n">> || OneList <- MapDataList>>;
_ ->
BodyStr = MapDataList
end,
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/import", QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr).
% ------->
% JSON文档
% JSON文档HTTP POST请求的正文中使
%
% { _key key1} { _key key2}
%
% 使documents
%
% JSON数组中的自包含JSON文档
%
%
%
% [
% { "_key": "key1", ... },
% { "_key": "key2", ... },
% ...
% ]
% JSON数据中允许使用任何空格JSON格式的结果数组arangosh中ArangoDB中使ArangoDB解析整个数组
%
% 使array
%
% auto将使服务器自动检测数据是按行的JSON文档=JSON数组=
%
%
%
% curl --data-binary @- -X POST --dump - "http://localhost:8529/_api/import?type=documents&collection=test"
% { "name" : "test", "gender" : "male", "age" : 39 }
% { "type" : "bird", "name" : "robin" }
%
% HTTP/1.1 201 Created
% Server: ArangoDB
% Connection: Keep-Alive
% Content-type: application/json; charset=utf-8
%
% {"error":false,"created":2,"empty":0,"errors":0}
% HTTP 201errors属性中返回该文档0
%
% details参数设置为truedetails
%
% 使HTTP POST请求正文的第一行必须是JSON数组JSON数组
%
% curl --data-binary @- -X POST --dump - "http://localhost:8529/_api/import?collection=test"
% [ "firstName", "lastName", "age", "gender" ]
% [ "Joe", "Public", 42, "male" ]
% [ "Jane", "Doe", 31, "female" ]
%
% HTTP/1.1 201 Created
% Server: ArangoDB
% Connection: Keep-Alive
% Content-type: application/json; charset=utf-8
%
% {"error":false,"created":2,"empty":0,"errors":0}
% HTTP 201errors属性中返回该文档empty属性中返回
%
% details参数设置为truedetails
%
%
% _from和_to属性
% HTTP接口
% HTTP请求中向ArangoDB发送单独的操作
% ArangoDB提供了一个批处理请求API使API批量向ArangoDB发送多个操作/HTTP请求并且各个请求的结果彼此不依赖时
% URL / _api / batch处理程序发出多部分HTTP POST请求来使用ArangoDB的批处理API Content-type为multipart / form-dataArangoDB将使用此边界将批处理请求分解为各个部分ArangoDB将多部分请求分为其各个部分时ArangoDB将生成一个多部分HTTP响应5ArangoDB还将发送包含5个部分的多部分响应
%
% Content-type: application/x-arango-batchpart
% Content-Id Content-IdContent-IdContent-Id的唯一性Content-type和可选Content-Id标头之后Windows换行符\ r \ n \ r \ nHTTP请求Windows换行符
% Content-typeapplication / x-arango-batchpart是MIME部分的标头HTTP请求MIME部分的正文部分
% HTTP方法URL和HTTP协议版本开头HTTP标头\ r \ n \ r \ n HTTP请求
% 3使 XXXsubpartXXX
% *******************************************************************
% HTTP接口
% *******************************************************************
% 使
% POST /_api/export
%
% collection
% JSON对象是必需的
% flushtrueWAL刷新操作WAL复制到集合的数据文件中flushWait秒的额外等待时间使WAL收集器也可以更改调整后的文档元数据以指向数据文件false
% flushWait10flush设置为true时
% count count count count
% batchSize使
% limitlimit属性或将其设置为0将导致不使用任何限制使
% ttl使
% restrict
% type使 include or exclude
% fields
% API相比API生成的内部数据结构更轻便
% /_api/cursorREST API中相似的方式返回文档hasMore属性将设置为 falsehasMore属性将设置为trueid属性将包含游标id
% 退
%
% WAL
% API或设置flush属性之前发出WAL刷新请求 WAL冲洗便WAL复制到集合数据文件
% 使HTTP 201 JSON对象
% JSON对象具有以下属性
% error false
% codeHTTP状态码
% result
% hasMore
% countcount属性的情况下执行的
% idID
% JSON格式不正确或请求中缺少查询规范使HTTP 400
% JSON对象
% errortrue
% codeHTTP状态码
% errorNum
% errorMessage
% 使ttl值来调整此空闲时间
% API
%
% 201
% 400JSON表示格式错误或请求中缺少查询规范
% 404访HTTP 404
% 405使HTTP方法HTTP 405
% 501API使HTTP 501
docExport(PoolNameOrSocket, CollName, MapData) ->
Path = <<"/_api/export?collection=", CollName/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr).

+ 107
- 0
src/agApi/agCluster.erl View File

@ -0,0 +1,107 @@
-module(agCluster).
-include("erlArango.hrl").
-compile(inline).
-compile({inline_size, 128}).
-compile([export_all, nowarn_export_all]).
%% doc_address:https://www.arangodb.com/docs/stable/http/cluster.html
% HTTP接口
% ArangoDB群集的REST API
% ID
%
%
%
%
%
% distributeShardsLike集在描述修理章节
% ID
% GET /_admin/server/id
% ID
%
% 200
% 500
serverId(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/server/id">>, [], undefined).
%
% GET /_admin/server/role
% role属性中返回
% SINGLE
% COORDINATOR
% PRIMARYDB-Server
% SECONDARY使
% AGENT
% UNDEFINEDUNDEFINED
% HTTP 200
% error
% codeHTTP状态码200
% errorNum
% role[ SINGLECOORDINATORPRIMARYSECONDARYAGENTUNDEFINED ]
serverRole(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/server/role">>, [], undefined).
%
% DB-Server的统计信息
% GET /_admin/clusterStatistics
%
% DBserverDB-Server的统计信息
%
% 200
% 400ID
% 403
clusterStats(PoolNameOrSocket, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/clusterStatistics", QueryBinary/binary>>, [], undefined).
%
% GET /_admin/cluster/health
% JSON对象codeerrorerrorNumerrorMessage字段适当
% ClusterIdUUID字符串
% Health
% <nodeID>Health将由节点ID键入
% Endpoint
% Role"AGENT""COORDINATOR""DBSERVER"
% CanBeDeleted
% Version使ArangoDB的版本字符串
% Engine使
% StatusDB-Servers节点运行状况"GOOD""BAD"15"FAILED"
%
%
% SyncStatusStatus"UNKNOWN""UNDEFINED""STARTUP""STOPPING""STOPPED""SERVING""SHUTDOWN"
% LastAckedTimeISO 8601
% ShortName"Coordinator0001"
% TimestampISO 8601
% Host
%
% AdvertisedEndpointIP地址或负载平衡器
%
% LeaderID
% Leadingtruefalse
% LastAckedTimeacked以秒为单位
%
% 200
clusterHealth(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/cluster/health">>, [], undefined).
%
% PUT /_admin/cluster/maintenance
% API60
% "on""off"
%
% 200
% 400
% 501
% 504
setClusterMaintenance(PoolNameOrSocket, OnOrOff) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, <<"/_admin/cluster/maintenance">>, [], OnOrOff).
%%%%%%%%%%%%%%% Agency ??????????????????????????

+ 482
- 0
src/agApi/agCollections.erl View File

@ -0,0 +1,482 @@
-module(agCollections).
-include("erlArango.hrl").
-compile(inline).
-compile({inline_size, 128}).
-compile([export_all, nowarn_export_all]).
% doc_address:https://www.arangodb.com/docs/stable/http/collection.html
% HTTP接口
% ArangoDB集合的HTTP接口的简介
%
%
% 使访document
%
%
% 使ArangoDB 1.1访ArangoDB 1.2使访ArangoDB当前使用64位无符号整数值在内部维护集合IDID返回给客户端时ArangoDB会将它们放入字符串中ID使ArangoDB返回的集合ID视为不透明字符串
%
% ID已返回为整数ArangoDB 1.1
%
%
% _线-ArangoDB中的命名约定
%
%
% ArangoDB允许为每个集合使用密钥生成器_key属性自动生成值ArangoDB将使用传统的密钥生成器使
%
% 01
%
% 12345
%
% 使5
%
% 16111621
%
% 使
%
% HTTP方法POSTGETPUTDELETE
%
%
% ArangoDB中的所有集合都具有唯一的标识符和唯一的名称ArangoDB在内部使用集合的唯一标识符来查找集合ArangoDB管理使访使
%
% http://server:port/_api/collection/collection-name
% 7254820demoURL为
%
% http://localhost:8529/_api/collection/demo
%
%POST /_api/collection
%
%
% waitForSyncReplication10
% forceReplicationFactor10
%
%JSON对象是必需的
% name
% waitForSynctruefalse
% doCompacttrueMMFiles存储引擎有意义
% journalSize10485761 MiBMMFiles存储引擎有意义
% isSystemtruecollection-name 线API实现者来创建系统集合使false
% isVolatiletrue使ArangoDB不会对磁盘CRC校验和falseMMFiles存储引擎有意义
% schemarulelevel并且message必须遵循文档架构验证中记录的规则
% keyOptionskeyOptions JSON数组或者JSON对象
% type uuid的和填充的
% 16RocksDB配合使用的理想选择 使UUID密钥生成器生成通用唯一的128位密钥
% allowUserKeystrue_key属性中提供自己的键值 false_key属性中提供自己的密钥值被视为错误
%
% offset
% type2type值有效
% 2
% 3
% numberOfShards1
% shardKeys[ _key]
% plicationFactor1DB-Server上保留多少个副本1k的值表示保留k-1"satellite"SatelliteCollection DB-Servers的数量匹配
% DB服务器上 leader follower
%
% writeConcern1DB服务器上同步每个分片需要多少个副本writeConcern的值 ReplicationFactor
% DistributionShardsLike 使仿
% shardingStrategyArangoDB 3.4shardingStrategy 使使
%
% community-compat3.4ArangoDB社区版使用的默认分片
% enterprise-compat3.4ArangoDB企业版使用的默认分片
% enterprise-smart-edge-compat3.4ArangoDB Enterprise Edition中的智能边缘集合使用的默认分片
% hash3.4
% enterprise-hash-smart-edge3.4
% enterprise-hash-smart-edgeArangoDB
% smartJoinAttributeSmartJoin集合的分片键值
% 使 distributedShardsLike属性设置为另一个集合的名称shardKeys属性设置为单个shard key属性smartJoinAttribute中存储的值都必须是字符串
%
% 400HTTP 400
% 404HTTP 404
% HTTP 200
newColl(PoolNameOrSocket, MapData) ->
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_api/collection">>, [], BodyStr).
newColl(PoolNameOrSocket, MapData, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/collection", QueryBinary/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr).
%
% DELETE /_api/collection/{collection-name}
% 3.4.0使ID访问集合
%
% collection-name
%
% isSystemtrue才能删除系统集合
% collection-name标识的集合
%
%
% id
%
% 400HTTP 400
% 404HTTP 404
delColl(PoolNameOrSocket, CollName) ->
Path = <<"/_api/collection/", CollName/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).
delColl(PoolNameOrSocket, CollName, IsSystem) ->
case IsSystem of
true ->
Path = <<"/_api/collection/", CollName/binary, "?isSystem=true">>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined);
_ ->
Path = <<"/_api/collection/", CollName/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined)
end.
%
% PUT /_api/collection/{collection-name}/truncate
% 3.4.0使ID访问集合
%
% collection-name
%
%
% 400HTTP 400
% 404 HTTP 404
clearColl(PoolNameOrSocket, CollName) ->
Path = <<"/_api/collection/", CollName/binary, "/truncate">>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], undefined).
%
% GET /_api/collection/{collection-name}
% 3.4.0使ID访问集合
%
% collection-name
%
% id
% name
% status
% 1
% 2
% 3
% 4
% 5
% 6
%
% type
% 2
% 3
% isSystemtrue
%
% 404HTTP 404
collInfo(PoolNameOrSocket, CollName) ->
Path = <<"/_api/collection/", CollName/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
%
% GET /_api/collection/{collection-name}/properties
% 3.4.0使ID访问集合
%
% collection-name
% 400HTTP 400
% 404 HTTP 404
% HTTP 200
collProps(PoolNameOrSocket, CollName) ->
Path = <<"/_api/collection/", (CollName)/binary, "/properties">>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
%
% GET /_api/collection/{collection-name}/count
% 3.4.0使ID访问集合
%
% collection-name
%
% count
%
% 400HTTP 400
% 404 HTTP 404
collCount(PoolNameOrSocket, CollName) ->
Path = <<"/_api/collection/", CollName/binary, "/count">>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
%
% GET /_api/collection/{collection-name}/figures
% 3.4.0使ID访问集合
%
% collection-name
%
% HTTP 200
% count
% figures
% indexes
% count
% size
% 400HTTP 400
% 404 HTTP 404
collFigures(PoolNameOrSocket, CollName) ->
Path = <<"/_api/collection/", CollName/binary, "/figures">>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
%
% PUT /_api/collection/{collection-name}/responsibleShard
%
% collection-name
% json
% JSON对象
% ID
% JSON文档
% shardId属性的JSON对象ID
%
%
% 200ID
% 400HTTP 400 HTTP 400
% 404 HTTP 404
% 501HTTP 501
% eg: MapData = #{'_key' => testkey, value => 23}
collResponsibleShard(PoolNameOrSocket, CollName, MapData) ->
Path = <<"/_api/collection/", CollName/binary, "/responsibleShard">>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], BodyStr).
% ID
% GET /_api/collection/{collection-name}/shards
%
% collection-name
%
% true
% ID的JSON数组
% details参数设置为trueID作为对象属性键的JSON对象
%
%
% 200
% 400HTTP 400
% 404 HTTP 404
% 501HTTP 501
collShards(PoolNameOrSocket, CollName) ->
Path = <<"/_api/collection/", CollName/binary, "/shards">>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
collShards(PoolNameOrSocket, CollName, IsDetails) ->
case IsDetails of
true ->
Path = <<"/_api/collection/", CollName/binary, "/shards?details=true">>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined);
_ ->
Path = <<"/_api/collection/", CollName/binary, "/shards">>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined)
end.
% ID
% GET /_api/collection/{collection-name}/revision
% 3.4.0使ID访问集合
%
% collection-name
% IDID是服务器生成的字符串使
% revisionID的字符串形式
%
% 400HTTP 400
% 404 HTTP 404
collRev(PoolNameOrSocket, CollName) ->
Path = <<"/_api/collection/", CollName/binary, "/revision">>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
%
% GET /_api/collection/{collection-name}/checksum
% 3.4.0使ID访问集合
%
% collection-name
%
% withRevisionsID
% withData
% ID
% ArangoDB实例上的两个集合是否包含相同的内容
% _key系统属性来计算_from和_to也将包含在计算中
% withRevisions设置为trueID_rev系统属性
% withData提供值为true的值 使
% JSON对象
% checksum
% ID的字符串形式
%
%
% 400HTTP 400
% 404 HTTP 404
collChecksum(PoolNameOrSocket, CollName) ->
Path = <<"/_api/collection/", CollName/binary, "/checksum">>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
collChecksum(PoolNameOrSocket, CollName, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/collection/", CollName/binary, "/checksum", QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
%
% GET /_api/collection
% 3.4.0使ID访问集合
%
% excludeSystem
% 使使
% excludeSystem提供值为true的值
%
% 200
collList(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/collection">>, [], undefined).
collList(PoolNameOrSocket, IsExcludeSystem) ->
case IsExcludeSystem of
false ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/collection">>, [], undefined);
_ ->
Path = <<"/_api/collection?excludeSystem=true">>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined)
end.
%
% PUT /_api/collection/{collection-name}/load
% 3.4.0使ID访问集合
%
% collection-name
%
%
% countcount设置为 false可以加快加载集合的速度
%
% id
% name
% countcount输入参数设置为true或未指定时才返回
% status
% type
% 2
% 3
% isSystemtrue
%
% 400HTTP 400
% 404 HTTP 404
loadColl(PoolNameOrSocket, CollName) ->
Path = <<"/_api/collection/", CollName/binary, "/load">>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], undefined).
loadColl(PoolNameOrSocket, CollName, MapData) ->
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, <<"/_api/collection/", CollName/binary, "/load">>, [], BodyStr).
%
% PUT /_api/collection/{collection-name}/unload
% 3.4.0使ID访问集合
%
% collection-name使
% id
% name
% status
% type
% 2
% 3
% isSystemtrue
%
% 400HTTP 400
% 404HTTP 404
unloadColl(PoolNameOrSocket, CollName) ->
Path = <<"/_api/collection/", CollName/binary, "/unload">>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], undefined).
%
% PUT /_api/collection/{collection-name}/loadIndexesIntoMemory
% 3.4.0使ID访问集合
%
% collection-namecollection的所有索引条目缓存到主内存中
% RocksDB存储引擎上有用MMFiles引擎中
% RocksDB上
% result设置为的对象true
%
% 200
% 400HTTP 400
% 404HTTP 404
collLoadIndexesIntoMemory(PoolNameOrSocket, CollName) ->
Path = <<"/_api/collection/", CollName/binary, "/loadIndexesIntoMemory">>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], undefined).
%
% PUT /_api/collection/{collection-name}/properties
% 3.4.0使ID访问集合
%
% collection-name
%
% waitForSynctrue
% journalSize10485761 MBjournalSize值时
% schemarulelevel并且message必须遵循文档架构验证中记录的规则
%
%
% id
% name
% waitForSync
% journalSize
% status
% type
% 2
% 3
% isSystemtrue
% isVolatiletrueArangoDB不会将数据写入或同步到磁盘
% doCompact
% keyOptionsJSON对象
% typeuuid的
% allowUserKeystrue_key属性中提供自己的键值 false_key属性中提供自己的密钥值被视为错误
% schemanullrulelevel并且message必须遵循文档架构验证中记录的规则
% waitForSyncjournalSize和name之外使
%
% 400HTTP 400
% 404 HTTP 404
collChangeProps(PoolNameOrSocket, CollName, MapData) ->
Path = <<"/_api/collection/", CollName/binary, "/properties">>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr).
%
% PUT /_api/collection/{collection-name}/rename
% 3.4.0使ID访问集合
%
% collection-name
%
% name
%
% id
% name
% status
% type
% 2
% 3
% isSystemtrue
% _graphs在当前数据库中该集合内的所有图形定义中重命名
%
%
% 400HTTP 400
% 404 HTTP 404
renameColl(PoolNameOrSocket, OldName, NewName) ->
Path = <<"/_api/collection/", OldName/binary, "/rename">>,
NameStr = jiffy:encode(NewName),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], <<"{\"name\":", NameStr/binary, "}">>).
%
% PUT /_api/collection/{collection-name}/rotate
%
% collection-name
% Rotate方法的目的是使文件中的数据可用于压缩
%
%
% true
% MMFiles存储引擎
%
% 400HTTP 400
% 404HTTP 404
% 3.7
collRotate(PoolNameOrSocket, CollName) ->
Path = <<"/_api/collection/", CollName/binary, "/rotate">>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], undefined).
%
% PUT /_api/collection/{collection-name}/recalculateCount
%
% collection-name
%
%
% true
% RocksDB存储引擎
%
% 200HTTP 200
% 404HTTP 404
collRecount(PoolNameOrSocket, CollName) ->
Path = <<"/_api/collection/", CollName/binary, "/recalculateCount">>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], undefined).

+ 98
- 0
src/agApi/agDbMgr.erl View File

@ -0,0 +1,98 @@
-module(agDbMgr).
-include("erlArango.hrl").
-compile(inline).
-compile({inline_size, 128}).
-compile([export_all, nowarn_export_all]).
%% doc_address:https://www.arangodb.com/docs/stable/http/database-database-management.html
% 使HTTP管理数据库
% ArangoDB用于管理数据库的HTTP接口的简介
% HTTP接口提供创建和删除单个数据库的操作HTTP方法POST DELETEGET方法来检索现有数据库的数组
% _system访访
%
% 使
% 使
% Foxx应用程序也仅在已安装数据库的上下文中可用ArangoDB附带的系统应用程序Web界面访Foxx应用程序则无法访问
%
%
% GET /_api/database/current
%
% JSON对象
% name
% idID()
% path(, mmfiles引擎有效)
% isSystem_system数据库
% sharding()
% ReplicationFactor
% writeConcern
%
% 200
% 400
% 404
curDbInfo(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/database/current">>, [], undefined).
% 访
% GET /_api/database/user
% 访
%
% 200
% 400
visitDbs(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/database/user">>, [], undefined).
%
% GET /_api/database
%
% _system数据库中检索数据库列表
% 使GET用户API来获取可用数据库的列表
%
% 200
% 400
% 403_system数据库中执行
allDbs(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/database">>, [], undefined, true).
%
% POST /_api/database
% JSON对象是必需的
% name
% options
% sharding flexible single
% ReplicationFactor satelliteDB-Server1
% writeConcernDB服务器上同步每个分片需要多少个副本writeConcern的值 ReplicationFactorusersusers或不包含任何用户使 root访
% usersusers或不包含任何用户使 root访
% username
% passwd
% activetruefalse
% extraJSON对象Extra中包含的数据 ArangoDB不会进一步解释
%
% JSON对象true
% _system数据库中创建新数据库
%
% 201
% 400
% 403_system数据库中执行
% 409
newDb(PoolNameOrSocket, MapData) ->
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_api/database">>, [], BodyStr, true).
%
% DELETE /_api/database/{database-name}
%
% database-name
%
% _system数据库中删除数据库_SYSTEM数据库本身不能被丢弃
%
% 200
% 400
% 403_system数据库中执行
% 404
delDb(PoolNameOrSocket, Name) ->
Path = <<"/_api/database/", Name/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined, true).

+ 500
- 0
src/agApi/agDocuments.erl View File

@ -0,0 +1,500 @@
-module(agDocuments).
-include("erlArango.hrl").
-compile(inline).
-compile({inline_size, 128}).
-compile([export_all, nowarn_export_all]).
%% doc_address:https://www.arangodb.com/docs/stable/http/document.html
% HTTP接口
%
%
% ArangoDB中的文档是JSON对象
%
% {
% "_id" : "myusers/3456789",
% "_key" : "3456789",
% "_rev" : "14253647",
% "firstName" : "John",
% "lastName" : "Doe",
% "address" : {
% "street" : "Road To Nowhere 1",
% "city" : "Gotham"
% },
% "hobbies" : [
% {"name": "swimming", "howFavorite": 10},
% {"name": "biking", "howFavorite": 6},
% {"name": "programming", "howFavorite": 4}
% ]
% }
% _id _key _rev_key创建文档时_id_key值是不可变的_rev值由ArangoDB自动维护
%
% _key属性/
%
%
% 使_key每个文档的属性中ArangoDB自动在集合的主索引中建立索引_key值是不变的_key属性ArangoDB将自动生成文档密钥使_key
%
% 使keyOptions属性创建集合
%
% 使keyOptions它可以完全禁止用户指定的键使_key
%
%
% ArangoDB中的每个文档都有一个修订版本system属性中 _rev
%
% _rev值在单个服务器设置中的所有文档和所有集合中都是唯一的_rev即使在同一毫秒内编写
%
% _rev属性可以用作查询的前提 _rev属性
%
% ArangoDB会将该文档的新版本写入预写日志文件
%
% _key存在同一文档的多个修订版访使
%
% _key_rev值恢复文档的旧版本
%
%
% ArangoDB尝试尽可能遵守现有的HTTP标准HTTP标头Etag设置为使用双引号括起来的文档修订版
%
% HTTP方法POSTGET HEADPUTPATCH和DELETE
%
% 使 使HTTP方法HEAD检查文档的修订
%
%
% ArangoDB 3.0API已扩展为不仅可以处理单个文档HTTP协议的开销以及客户端和服务器之间的各个网络往返使JSON对象数组代替单个文档API描述
%
% GETHEAD和DELETE HTTP操作通常不允许传递消息正文
%
% URI
% 使URI检索任何文档
%
% http://server:port/_api/document/<document-handle>
% demo/362549736URL为
%
% http://localhost:8529/_api/document/demo/362549736
% URL架构未明确指定 _system将使用默认数据库使URL模式
%
% http://server:port/_db/<database-name>/_api/document/<document-handle>
%
%
% http://localhost:8529/_db/mydb/_api/document/demo/362549736
% 使URL格式
%
% Etag HTTP标头中返回
%
% 使GET获取文档使If-None-Match标头HTTP 412
%
% 使If-Match标头HTTP 412
%
% GET /_api/document/{collection}/{key}
%
% collection
% key
%
% If-None-Match If-None-MatchEtagEtag不同HTTP 304
% If-Match If-MatchEtagEtag相同HTTP 412
% document-id标识的文档_id包含文档标识符_key包含唯一标识给定集合中的文档的键_rev包含修订版
%
% 200
% 304 If-None-Match
% 404
% 412 If-Match412_rev属性中包含找到的文档的当前修订_id和_key
getDoc(PoolNameOrSocket, CollName, Key) ->
Path = <<"/_api/document/", CollName/binary, "/", (agMiscUtils:toBinary(Key))/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
getDoc(PoolNameOrSocket, CollName, Key, Headers) ->
Path = <<"/_api/document/", CollName/binary, "/", (agMiscUtils:toBinary(Key))/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, Headers, undefined).
%
% HEAD /_api/document/{collection}/{key}
%
% collection
% key
%
% If-None-Match If-None-MatchEtagEtagHTTP 200Etag相同HTTP 304
% If-Match If-MatchEtagEtag相同HTTP 412
% GET使
%
% 200
% 304 If-None-Match
% 404
% 412 If-Match412Etag标头中包含找到的文档的当前版本
getDocHead(PoolNameOrSocket, CollName, Key) ->
Path = <<"/_api/document/", CollName/binary, "/", (agMiscUtils:toBinary(Key))/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgHead, Path, [], undefined).
getDocHead(PoolNameOrSocket, CollName, Key, Headers) ->
Path = <<"/_api/document/", CollName/binary, "/", (agMiscUtils:toBinary(Key))/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgHead, Path, Headers, undefined).
%
% POST /_api/document/{collection}
%
% collection
%
% collection ArangoDB版本<3.0URL路径为/ _api / documentURL路径中指定集合
% waitForSync
% returnNew new属性下返回完整的新文档
% returnOld old属性下返回完整的旧文档使
% silenttrue
% overwritetrue_key的文档
% overwriteMode
% "ignore"_key值的文档使RETURN OLD使 RETURN NEWnull
% "replace"_key值的文档overwrite模式但覆盖 true时使
% "update"_key值的文档使keepNull和 mergeObjects参数进一步控制覆盖模式
% "conflict"_key值的文档使false或未设置
% keepNull使update-insert命令删除现有属性URL查询参数keepNull与 false一起使用patch命令的行为null的现有文档
% mergeObjectsfalsetruetrue
% json
% JSON表示形式
% _key的文档_key_key
% _id和_rev属性被忽略URL部分或查询参数集合分别计数
% Location标头将包含新创建的文档的路径Etag的头字段包含了文档的修订
% silent未设置为trueJSON对象
% _id包含新创建的文档的文档标识符
% _key包含文档密钥
% _rev包含文档修订版
% collection参数waitForSync为false
% 使waitForSync标志waitForSync也可用于强制将文档创建操作同步到磁盘waitForSync查询参数可用于强制执行此特定操作的同步使waitForSync参数设置为true waitForSync参数或将其设置为falsewaitForSync行为waitForSync查询参数不能用于禁用同步用于具有默认集合waitForSync值为true
% returnNew为truenew属性下返回完整的新文档
%
% 201waitForSync为true
% 202waitForSync为false
% 400JSON表示形式
% 404collection指定的collection未知
% 409409
newDoc(PoolNameOrSocket, CollName, MapData) ->
Path = <<"/_api/document/", CollName/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr).
newDoc(PoolNameOrSocket, CollName, MapData, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/document/", CollName/binary, QueryBinary/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr).
%
% PUT /_api/document/{collection}/{key}
%
% collection
% key
%
% waitForSync
% ignoreRevstrue_rev属性false_rev属性作为前提
% returnOldold属性下
% returnNew new属性下还返回完整的新文档
% silenttrue
%
% If-Match使if-match HTTP标头有条件地根据目标修订版ID替换文档
% json
% JSON表示形式
%
% _key属性的值以及用作分片键的属性均不得更改
% If-Match标头
% If-Match且ignoreRevs为false_rev属性
% HTTP 412
% HTTP 201HTTP 202waitForSyncEtag标头字段包含文档的新修订版Location标头包含完整的URLURL下
% ArangoDB可能会回答
% 使waitForSync标志waitForSync仍可用于强制将文档替换操作同步到磁盘waitForSync查询参数可用于强制特定操作的同步使waitForSync参数设置为truewaitForSync参数或将其设置为 falsewaitForSync行为waitForSync查询参数不能用于禁用同步用于具有默认集合waitForSync值为true
% silent未设置为trueJSON对象 _id包含更新的文档的已知文档ID_key _rev包含新文档的修订版
% returnOld为trueold属性下返回文档的完整先前修订版
% returnNew为truenew属性下返回完整的新文档
% HTTP 404
%
% 201waitForSync为true
% 202waitForSync为false
% 400JSON表示形式
% 404
% 412_rev _id和_key
replaceDoc(PoolNameOrSocket, CollName, Key, MapData) ->
Path = <<"/_api/document/", CollName/binary, "/", (agMiscUtils:toBinary(Key))/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr).
replaceDoc(PoolNameOrSocket, CollName, Key, MapData, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/document/", CollName/binary, "/", (agMiscUtils:toBinary(Key))/binary, QueryBinary/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr).
replaceDoc(PoolNameOrSocket, CollName, Key, MapData, QueryPars, Headers) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/document/", CollName/binary, "/", (agMiscUtils:toBinary(Key))/binary, QueryBinary/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, Headers, BodyStr).
%
% PATCH /_api/document/{collection}/{key}
%
% collection
% key
%
% keepNull使patch命令删除现有属性URL查询参数keepNull与false一起使用patch命令的行为null的现有文档
% mergeObjectsfalsetrue true
% waitForSync
% ignoreRevstrue_rev属性false_rev属性作为前提
% returnOldold属性下
% returnNew new属性下还返回完整的新文档
% silenttrue
%
% If-Match使if-match HTTP标头有条件地根据目标修订版ID更新文档
% json
% JSON表示形式作为对象
% document-id标识的文档JSON文档
% _key属性的值以及用作分片键的属性均不得更改
% null会导致默认情况下为该属性保存null值
% If-Match标头
% If-Match且ignoreRevs为false_rev属性
% HTTP 412
% HTTP 201HTTP 202waitForSyncEtag标头字段包含文档的新修订版Location标头包含一个可以查询文档的完整URL
% ArangoDB可能会以未发现的错误回答
% 使waitForSync标志waitForSync仍可用于强制将更新的文档操作同步到磁盘waitForSync查询参数可用于强制特定操作的同步使waitForSync参数设置为truewaitForSync参数或将其设置为 falsewaitForSync行为waitForSync查询参数不能用于禁用同步用于具有默认集合waitForSync值为true
% silent未设置为trueJSON对象 _id包含更新的文档的已知文档ID_key _rev包含新文档的修订版
% returnOld为trueold属性下返回文档的完整先前修订版
% returnNew为truenew属性下返回完整的新文档
% HTTP 404
%
% 201waitForSync为true
% 202waitForSync为false
% 400JSON表示形式
% 404
% 412_rev _id和_key
updateDoc(PoolNameOrSocket, CollName, Key, MapData) ->
Path = <<"/_api/document/", CollName/binary, "/", (agMiscUtils:toBinary(Key))/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, [], BodyStr).
updateDoc(PoolNameOrSocket, CollName, Key, MapData, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/document/", CollName/binary, "/", (agMiscUtils:toBinary(Key))/binary, QueryBinary/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, [], BodyStr).
updateDoc(PoolNameOrSocket, CollName, Key, MapData, QueryPars, Headers) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/document/", CollName/binary, "/", (agMiscUtils:toBinary(Key))/binary, QueryBinary/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, Headers, BodyStr).
%
% DELETE /_api/document/{collection}/{key}
%
% collection
% key
%
% waitForSync
% returnOldold属性下
% silenttrue
%
% If-Match使if-match HTTP标头有条件地根据目标修订版ID删除文档
% silent未设置为trueJSON对象 _id包含已删除文档的已知文档ID_key _rev包含文档修订版
% waitForSync参数或将其设置为falsewaitForSync行为waitForSync查询参数不能用于禁用同步用于具有默认集合waitForSync
% returnOld为trueold属性下返回文档的完整先前修订版
%
% 200waitForSync为true
% 202waitForSync为false
% 404
% 412-match_rev属性中包含找到的文档的当前修订_id和_key
delDoc(PoolNameOrSocket, CollName, Key) ->
Path = <<"/_api/document/", CollName/binary, "/", (agMiscUtils:toBinary(Key))/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).
delDoc(PoolNameOrSocket, CollName, Key, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/document/", CollName/binary, "/", (agMiscUtils:toBinary(Key))/binary, QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).
delDoc(PoolNameOrSocket, CollName, Key, QueryPars, Headers) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/document/", CollName/binary, "/", (agMiscUtils:toBinary(Key))/binary, QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, Headers, undefined).
%
% ArangoDB支持批量处理文档 使API变体可使客户端分摊整批文档中的单个请求的开销ArangoDB
% ArangoDB将继续处理其余操作X-Arango-Error-Codes标头将包含发生的错误代码及其多重性的映射 1205:10,1210:17101205 171210
% JSON数组
%
% PUT /_api/document/{collection}#get
%
% collection
%
% onlygettrue
% ignoreRevstrue_rev字段的值
% _key标识的文档_key值JSON数组
% _key字段的值_rev ignoreRevs设置为false
% ArangoDB可能会回答
% _id包含文档标识符_key包含唯一标识给定集合中的文档的键_rev包含修订版
%
% 200
% 400JSON表示形式
% 404
% _rev条件不满足 使
getDocs(PoolNameOrSocket, CollName, KeyOrMapDataList) ->
QueryBinary = agMiscUtils:spellQueryPars([{onlyget, true}]),
Path = <<"/_api/document/", CollName/binary, QueryBinary/binary>>,
BodyStr = jiffy:encode(KeyOrMapDataList),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr).
getDocs(PoolNameOrSocket, CollName, KeyOrMapDataList, QueryPars) ->
LastQueryPars =
case lists:keyfind(onlyget, 1, QueryPars) of
{onlyget, true} ->
QueryPars;
_ ->
lists:keystore(onlyget, 1, QueryPars, {onlyget, true})
end,
QueryBinary = agMiscUtils:spellQueryPars(LastQueryPars),
Path = <<"/_api/document/", CollName/binary, QueryBinary/binary>>,
BodyStr = jiffy:encode(KeyOrMapDataList),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr).
%
% POST /_api/document/{collection}#multiple
%
% collection
%
% collectionArangoDB版本<3.0URL路径为/ _api / documentURL路径中指定集合
% waitForSync
% returnNew new属性下返回完整的新文档
% returnOld old属性下返回完整的旧文档使
% silenttrue
% overwritetrue_key的文档
% json
%
% _key的文档_key_key
% JSON数组error设置为trueerrorCode设置为发生的错误代码
% _id和_rev属性被忽略URL部分或查询参数集合分别计数
% silent未设置为trueJSON对象数组
% _id包含新创建的文档的文档标识符
% _key包含文档密钥
% _rev包含文档修订版
% collection参数waitForSync为false
% 使waitForSync标志waitForSync也可用于强制将文档创建操作同步到磁盘waitForSync查询参数可用于强制执行此特定操作的同步使waitForSync参数设置为true waitForSync参数或将其设置为falsewaitForSync行为waitForSync查询参数不能用于禁用同步用于具有默认集合waitForSync值为true
% returnNew为truenew属性下返回完整的新文档
% HTTP头的文件已经发生了错误的X阿朗戈-120510,121017101205171210
%
% 201waitForSync为true
% 202waitForSync为false并且已处理操作
% 400JSON表示形式
% 404collection指定的collection未知
% MapDataList的顺序返回执行结果
newDocs(PoolNameOrSocket, CollName, MapDataList) ->
Path = <<"/_api/document/", CollName/binary>>,
BodyStr = jiffy:encode(MapDataList),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr).
newDocs(PoolNameOrSocket, CollName, MapDataList, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/document/", CollName/binary, QueryBinary/binary>>,
BodyStr = jiffy:encode(MapDataList),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr).
%
% PUT /_api/document/{collection}
%
% collectionURL参数是替换文档的集合的名称
%
% waitForSync
% ignoreRevstrue_rev属性false_rev属性作为前提
% returnOldold属性下还返回更改后的文档的完整先前修订
% returnNew new属性下还返回完整的新文档
% json
% JSON表示形式
% _key属性指定
% _key属性的值以及用作sharding keys的属性均不得更改
% ignoreRevs为false_rev属性
% ArangoDB可能会回答
% 使waitForSync标志waitForSync仍可用于强制将文档替换操作同步到磁盘waitForSync查询参数可用于强制特定操作的同步使waitForSync参数设置为truewaitForSync参数或将其设置为 falsewaitForSync行为waitForSync查询参数不能用于禁用同步用于具有默认集合waitForSync值为true
% JSON数组 _id包含每个更新文档的已知文档ID _key包含用于唯一标识给定集合中的文档的键_rev包含新文档修订版error设置为true errorCode设置为错误代码
% returnOld为trueold属性下返回文档的完整先前修订版
% returnNew为truenew属性下返回完整的新文档
% 201202HTTP标头X-Arango-Error-Codes120017,120510171200101205
%
% 201waitForSync为true
% 202waitForSync为false并且已处理操作
% 400JSON表示形式
% 404
replaceDocs(PoolNameOrSocket, CollName, MapDataList) ->
Path = <<"/_api/document/", CollName/binary>>,
BodyStr = jiffy:encode(MapDataList),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr).
replaceDocs(PoolNameOrSocket, CollName, MapDataList, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/document/", CollName/binary, QueryBinary/binary>>,
BodyStr = jiffy:encode(MapDataList),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr).
%
% PATCH /_api/document/{collection}
%
% collection
%
% keepNull使patch命令删除现有属性URL查询参数keepNull与false一起使用patch命令的行为null的现有文档
% mergeObjectsfalsetrue true
% waitForSync
% ignoreRevstrue_rev属性false_rev属性作为前提
% returnOldold属性下还返回更改后的文档的完整先前修订
% returnNew new属性下还返回完整的新文档
% json
% JSON表示形式是对象
% _key属性指定JSON数组
% _key属性的值以及用作分片键的属性均不得更改
% null会导致默认情况下为该属性保存null值
% ignoreRevs为false_rev属性
% ArangoDB可能会以未发现的错误回答
% 使waitForSync标志waitForSync仍可用于强制将文档替换操作同步到磁盘waitForSync查询参数可用于强制特定操作的同步使waitForSync参数设置为truewaitForSync参数或将其设置为 falsewaitForSync行为waitForSync查询参数不能用于禁用同步用于具有默认集合waitForSync值为true
% JSON数组 _id包含每个更新文档的已知文档ID _key包含用于唯一标识给定集合中的文档的键_rev包含新文档修订版error设置为true errorCode设置为错误代码
% returnOld为trueold属性下返回文档的完整先前修订版
% returnNew为truenew属性下返回完整的新文档
% 201202HTTP标头X-Arango-Error-Codes120017,120510171200101205
%
% 201waitForSync为true
% 202waitForSync为false并且已处理操作
% 400JSON表示形式
% 404
updateDocs(PoolNameOrSocket, CollName, MapDataList) ->
Path = <<"/_api/document/", CollName/binary>>,
BodyStr = jiffy:encode(MapDataList),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, [], BodyStr).
updateDocs(PoolNameOrSocket, CollName, MapDataList, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/document/", CollName/binary, QueryBinary/binary>>,
BodyStr = jiffy:encode(MapDataList),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, [], BodyStr).
%
% DELETE /_api/document/{collection}
%
% collection
%
% waitForSync
% returnOldold属性下
% ignoreRevstrue_rev属性
% json
% JSON数组
% _key属性的对象API调用从collection中删除所有指定的文档_rev属性
% JSON对象_id包含已删除文档的已知文档ID_key包含用于唯一标识给定集合中的文档的键_rev包含文档修订版 errorCode集到的错误代码被构建的
% waitForSync参数或将其设置为falsewaitForSync行为waitForSync查询参数不能用于禁用同步用于具有默认集合waitForSync
% returnOld为trueold属性下返回文档的完整先前修订版
% 200202HTTP标头X-Arango-Error-Codes120017,120510171200101205
%
% 200waitForSync为true
% 202waitForSync为false
% 404
delDocs(PoolNameOrSocket, CollName, KeyOrMapDataList) ->
Path = <<"/_api/document/", CollName/binary, "/">>,
BodyStr = jiffy:encode(KeyOrMapDataList),
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], BodyStr).
delDocs(PoolNameOrSocket, CollName, KeyOrMapDataList, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/document/", CollName/binary, QueryBinary/binary>>,
BodyStr = jiffy:encode(KeyOrMapDataList),
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], BodyStr).

+ 43
- 0
src/agApi/agEdges.erl View File

@ -0,0 +1,43 @@
-module(agEdges).
-include("erlArango.hrl").
-compile(inline).
-compile({inline_size, 128}).
-compile([export_all, nowarn_export_all]).
%% doc_address:https://www.arangodb.com/docs/stable/http/edge.html
%
% ArangoDB中的所有文档都有一个文档句柄使URI检索任何文档
%
% http://server:port/_api/document/<document-handle>
% 访使URL格式
%
% http://server:port/_api/document/<document-handle>
% _id属性中的文档句柄是demo / 362549736URL为
%
% http://localhost:8529/_api/document/demo/362549736
% URL方案没有明确指定数据库名称使使URL模式
%
% http://server:port/_db/<database-name>/_api/document/<document-handle>
%
%
% http://localhost:8529/_db/mydb/_api/document/demo/362549736
% 使URL格式
%
% GET /_api/edges/{collection-id}
%
% collection-idID或者边集合名
%
% vertexID
% direction in or out
% vertex标识的顶点开始或结束的边数组
%
% 200
% 400
% 404
getEdges(PoolNameOrSocket, CollName, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/edges/", CollName/binary, QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).

+ 43
- 0
src/agApi/agEndPoints.erl View File

@ -0,0 +1,43 @@
-module(agEndPoints).
-include("erlArango.hrl").
-compile(inline).
-compile({inline_size, 128}).
-compile([export_all, nowarn_export_all]).
% doc_address:https://www.arangodb.com/docs/stable/http/endpoints.html
% HTTP接口
% API /_api/endpoint已弃用/_api/cluster/endpoints可以找到所有当前的Coordinator端点
%
% ArangoDB服务器可以在多个端点上侦听传入的请求
%
% 使 --server.endpointArangoDB的配置文件或命令行中指定端点ArangoDB的默认终结点是tcp//127.0.0.18529 tcp// localhost8529
%
% _system访访
%
% API调用返回有关所有协调器终结点的信息
% GET /_api/cluster/endpoints
% endpoints的对象endpointerror属性设置为 true
% HTTP 200
% errortrue
% codeHTTP状态码-200
%
% tcp://[::1]:8530
% 501
getClusterEndpoints(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/cluster/endpoints">>, [], undefined, true).
%
% API调用返回所有端点
% GET /_api/endpoint
% 使3.4.0
%
% JSON对象的JSON数组 entrypoint
% 使
%
% 200
% 400
% 405使HTTP方法HTTP 405

+ 399
- 0
src/agApi/agFoxxServices.erl View File

@ -0,0 +1,399 @@
-module(agFoxxServices).
-include("erlArango.hrl").
-compile(inline).
-compile({inline_size, 128}).
-compile([export_all, nowarn_export_all]).
% doc_address:https://www.arangodb.com/docs/stable/http/foxx.html
% Foxx HTTP API
% Foxx服务
%
% Foxx及其JavaScript API的更多信息Foxx章节
% Foxx服务管理
% Foxx服务的ArangoDB HTTP接口的简介
%
% GET /_api/foxx
%
% excludeSystem
%
%
% mount
% developmenttrue
% legacy2.8true
%
%
% name
% versionsemver兼容的版本字符串
%
% 200
getFoxxList(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/foxx">>, [], undefined).
getFoxxList(PoolNameOrSocket, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/foxx", QueryBinary/binary>>, [], undefined).
%
%
% GET /_api/foxx/service
%
% mount
%
%
% mount
% path
% developmenttrue
% legacy2.8true
% manifestJSON清单
%
% name
% versionsemver兼容的版本字符串
%
% 200
% 400
getFoxxService(PoolNameOrSocket, Mount) ->
Path = <<"/_api/foxx/service?mount=", (agMiscUtils:toBinary(Mount))/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
%
% POST /_api/foxx
%
% mount
% true为启用开发模式
% false不运行服务的安装脚本
% legacytrue以2.8
%
%
% application/zipzip捆绑包
% application/javascriptJavaScript文件
% application/jsonJSON
% multipart/form-data
%
% configurationJSON对象
% JSON对象
% sourceURL或绝对路径
% 使zip捆绑包或独立JavaScript文件的文件字段
% 使JavaScript文件时HTTP端点main与在服务清单字段中定义的相同
% URL访URLsource是文件系统路径URL都应解析为zip包JavaScript文件或
% 使
%
% 201
installFoxx(PoolNameOrSocket, MapData, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/foxx", QueryBinary/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr).
%
% DELETE /_api/foxx/service
%
% mount
% teardown false不运行服务的拆卸脚本
%
%
%
% 204
uninstallFoxx(PoolNameOrSocket, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/foxx/service", QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).
%
% PUT /_api/foxx/service
%
% mount
% teardown false不运行旧服务的拆卸脚本
% setup false不运行新服务的安装脚本
% legacytrue以2.8 Legacy兼容模式安装新服务
% force true强制安装服务使
%
%
%
% application/zipzip捆绑包
% application/javascriptJavaScript文件
% application/jsonJSON
% multipart/form-data
%
% configurationJSON对象
% JSON对象
% sourceURL或绝对路径
% 使zip捆绑包或独立JavaScript文件的文件字段
% 使JavaScript文件时HTTP端点main与在服务清单字段中定义的相同
% URL访URLsource是文件系统路径URL都应解析为zip包JavaScript文件或
% 使
%
% 200
replaceFoxx(PoolNameOrSocket, MapData, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/foxx/service", QueryBinary/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr).
%
% PATCH /_api/foxx/service
%
% mount
% teardowntrue运行旧服务的拆卸脚本
% setupfalse不运行新服务的安装脚本
% legacytrue以2.8 Legacy兼容模式安装新服务
% forcetrue强制安装服务使
% 使
%
%
% application/zipzip捆绑包
% application/javascriptJavaScript文件
% application/jsonJSON
% multipart/form-data
%
% configurationJSON对象
% JSON对象
% sourceURL或绝对路径
% 使zip捆绑包或独立JavaScript文件的文件字段
% 使JavaScript文件时HTTP端点main与在服务清单字段中定义的相同
% URL访URLsource是文件系统路径URL都应解析为zip包JavaScript文件或
% 使
%
% 200
upgradeFoxx(PoolNameOrSocket, MapData, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/foxx/service", QueryBinary/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, [], BodyStr).
% Foxx服务配置/
% Foxx服务配置和依赖关系的ArangoDB HTTP接口的简介
%
%
% GET /_api/foxx/configuration
%
% mount
%
%
%
% 200
getFoxxConfig(PoolNameOrSocket, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/foxx/configuration", QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
%
% PATCH /_api/foxx/configuration
%
% mount
% json
% JSON对象映射配置选项名称为其新值
%
%
%
% 200
updateFoxxConfig(PoolNameOrSocket, MapData, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/foxx/configuration", QueryBinary/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, [], BodyStr).
%
% PUT /_api/foxx/configuration
%
% mount
% json
% JSON对象映射配置选项名称为其新值
%
%
%
% 200
replaceFoxxConfig(PoolNameOrSocket, MapData, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/foxx/configuration", QueryBinary/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr).
%
% GET /_api/foxx/dependencies
%
% mount
%
%
%
% 200
getFoxxDependencies(PoolNameOrSocket, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/foxx/dependencies", QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
%
% PATCH /_api/foxx/dependencies
%
% mount
% json
% JSON对象
%
%
%
% 200
updateFoxxDependencies(PoolNameOrSocket, MapData, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/foxx/dependencies", QueryBinary/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, [], BodyStr).
%
% PUT /_api/foxx/dependencies
%
% mount
% json
% JSON对象
%
%
%
% 200
replaceFoxxDependencies(PoolNameOrSocket, MapData, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/foxx/dependencies", QueryBinary/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr).
% Foxx服务杂项
%
%
% GET /_api/foxx/scripts
%
% mount
%
%
%
% 200
getFoxxScripts(PoolNameOrSocket, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/foxx/scripts", QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
%
% POST /_api/foxx/scripts/{name}
%
%
%
% mount
% json
% JSON值
%
%
%
% 200
runFoxxScripts(PoolNameOrSocket, ScriptName, MapData, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/foxx/scripts/", ScriptName/binary, QueryBinary/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr).
%
% POST /_api/foxx/tests
%
% mount
% reporter 使
% idiomatic 使Accept标头无关
% filter
%
%
% default
% suite
% stream
% xunitXUnit / JUnit兼容的结构
% tapTAP兼容流
%
% 使application/x-ldjson将导致响应主体被格式化为以换行符分隔的JSON流
% 使text/plain或时text/*TAP报告
% 使xunit报告程序时application/xml或text/xml将导致响应正文被格式化为XML而不是JSONML
% prettyprinted JSON
%
% 200
runFoxxTest(PoolNameOrSocket, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/foxx/tests", QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], undefined).
%
% POST /_api/foxx/development
%
% mount
%
%
% ArangoDB时
%
% 200
enableFoxxDevelopment(PoolNameOrSocket, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/foxx/development", QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], undefined).
%
% DELETE /_api/foxx/development
%
% mount
%
% ArangoDB时
%
% 200
disableFoxxDevelopment(PoolNameOrSocket, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/foxx/development", QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).
%
% GET /_api/foxx/readme
%
% mount
% README或README.md文件的内容
%
% 200
% 204
getFoxxReadme(PoolNameOrSocket, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/foxx/readme", QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
%
% GET /_api/foxx/swagger
%
% mount
% Swagger API描述
% API的与OpenAPI 2.0JSON描述
%
% 200
getFoxxSwagger(PoolNameOrSocket, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/foxx/swagger", QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
%
% POST /_api/foxx/download
%
% mount
% zip捆绑包
%
% ArangoDB实例上安装的服务的版本
%
% 200
% 400
downloadFoxxBundle(PoolNameOrSocket, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/foxx/download", QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], undefined).
%
% POST /_api/foxx/commit
%
% replace使
%
%
%
% 204
commitFoxxState(PoolNameOrSocket, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/foxx/commit", QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], undefined).

+ 961
- 0
src/agApi/agGeneralGraphs.erl View File

@ -0,0 +1,961 @@
-module(agGeneralGraphs).
-include("erlArango.hrl").
-compile(inline).
-compile({inline_size, 128}).
-compile([export_all, nowarn_export_all]).
%% doc_address:https://www.arangodb.com/docs/stable/http/gharial.html
%
% REST接口
%
%
% GET /_api/gharial
%
% HTTP 200
% errortruefalse
% code
%
% graph
% name
% edgeDefinitions
% orphanCollections
% numberOfShards
% _idID值
% _rev
% ReplicationFactor使
% isSmartSmartGraph
% smartGraphAttribute
graphList(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/gharial">>, [], undefined).
%
%
% POST /_api/gharial
%
% waitForSync
% JSON对象是必需的
% name
% edgeDefinitions
% collection :
% from: from
% to : to
% orphanCollections
% isSmart
% optionsJSON对象
% smartGraphAttributeisSmart为trueSmartGraph中的每个顶点都必须具有此属性
% numberOfShards使
% replicationFactor使"satellite"SatelliteGraph numberOfShardsminReplicationFactor和writeConcern
% writeConcernDB服务器上同步每个分片需要多少个副本writeConcern的值 ReplicationFactor
%
% _graphs集合启用了waitForSync HTTP 201
% errortruefalse
% code
% graph
% _graphs集合禁用了waitForSyncHTTP 202
% errortruefalse
% code
% graph
% HTTP 400
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 403
% Administrate 访
% Read Only
% 访使
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 409使
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
newGraph(PoolNameOrSocket, MapData) ->
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_api/gharial">>, [], BodyStr).
newGraph(PoolNameOrSocket, MapData, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/gharial", QueryBinary/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr).
%
% GET /_api/gharial/{graph}
%
%
% 404
% HTTP 200
% errortruefalse
% code
% graph
% HTTP 404
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
getGraph(PoolNameOrSocket, GraphName) ->
Path = <<"/_api/gharial/", GraphName/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
%
% DELETE /_api/gharial/{graph}
%
%
%
% dropCollections使
% 使
% 201_graphs集合启用了waitForSync
% 202_graphs集合禁用了waitForSync
% HTTP 403
% Administrate 访
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 404
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
delGraph(PoolNameOrSocket, GraphName) ->
Path = <<"/_api/gharial/", GraphName/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).
delGraph(PoolNameOrSocket, GraphName, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/gharial/", GraphName/binary, QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).
% 使
% GET /_api/gharial/{graph}/vertex
%
% graph
%
% HTTP 200
% errortruefalse
% code
% collectionsedgeDefinitions和孤儿中包括集合
% HTTP 404
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
vertexCollList(PoolNameOrSocket, GraphName) ->
Path = <<"/_api/gharial/", GraphName/binary, "/vertex">>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
%
% POST /_api/gharial/{graph}/vertex
%
% graph
%
% _graphs集合启用waitForSync HTTPHTTP 201
% errortruefalse
% code
% graph
% _graphs集合禁用了waitForSync HTTPHTTP 202
% errortruefalse
% code
% graph
% HTTP 400
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 403
% Administrate 访
% Read Only 访使
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 404
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
%% MapData = #{"collection" => "otherVertices"}
addVertexColl(PoolNameOrSocket, GraphName, MapData) ->
Path = <<"/_api/gharial/", GraphName/binary, "/vertex">>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr).
%
% DELETE /_api/gharial/{graph}/vertex/{collection}
%
% graph
% collection
%
% dropCollection使
% 使使
% HTTP 200waitForSync为true
% errortruefalse
% code
% graph
% HTTP 202waitForSync为false
% errortruefalse
% code
% graph
% HTTP 400使
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 403
% Administrate 访
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 404
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
delVertexColl(PoolNameOrSocket, GraphName, CollName) ->
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).
delVertexColl(PoolNameOrSocket, GraphName, CollName, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary, QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).
%
% GET /_api/gharial/{graph}/edge
%
% graph
%
% HTTP 200
% errortruefalse
% code
% collectionsedgeDefinitions和孤儿中包括集合
% HTTP 404
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
edgeDefList(PoolNameOrSocket, GraphName) ->
Path = <<"/_api/gharial/", GraphName/binary, "/edge">>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
%
% POST /_api/gharial/{graph}/edge
%
% graph
% JSON对象是必需的
% collection使
% from
% to
%
% 使使 v1 v2 e v2 v1 e
% HTTP 201_graphs集合启用了waitForSync
% errortruefalse
% code
% graph
% HTTP 202_graphs集合禁用了waitForSync
% errortruefalse
% code
% graph
% HTTP 400使
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 403
% Administrate 访
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 404
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
addEdgeDef(PoolNameOrSocket, GraphName, MapData) ->
Path = <<"/_api/gharial/", GraphName/binary, "/edge">>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr).
%
% PUT /_api/gharial/{graph}/edge/{definition}#definition
%
% graph
% definition使
%
%
% waitForSync
% dropCollections使
% JSON对象是必需的
% collection使
% from
% to
%
% HTTP 201waitForSync为true
% errortruefalse
% code
% graph
% HTTP 202waitForSync为false
% errortruefalse
% code
% graph
% HTTP 400使
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 403
% Administrate 访
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 404
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% EdgeDefName MapData中的collection from to
replaceEdgeDef(PoolNameOrSocket, GraphName, EdgeDefName, MapData) ->
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", EdgeDefName/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr).
replaceEdgeDef(PoolNameOrSocket, GraphName, EdgeDefName, MapData, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", EdgeDefName/binary, QueryBinary/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr).
%
% DELETE /_api/gharial/{graph}/edge/{definition}#definition
%
% graph
% definition使
%
% waitForSync
% dropCollections使
% 使
% HTTP 201waitForSync为true
% errortruefalse
% code
% graph
% HTTP 202waitForSync为false
% errortruefalse
% code
% graph
% HTTP 403
% Administrate 访
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 404
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
delEdgeDef(PoolNameOrSocket, GraphName, EdgeDefName) ->
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", EdgeDefName/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).
delEdgeDef(PoolNameOrSocket, GraphName, EdgeDefName, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", EdgeDefName/binary, QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).
%
%
% POST /_api/gharial/{graph}/vertex/{collection}
%
% graph
% collection
%
% waitForSync
% returnNew
%
% JSON对象
%
% HTTP 201waitForSync为true
% errortruefalse
% vertex
% newArangoDB生成的所有内部属性returnNew为true时存在
% HTTP 202waitForSync为false
% errortruefalse
% code
% vertex
% newArangoDB生成的所有内部属性returnNew为true时存在
% HTTP 403
% Read Only 访
% Write 访
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 404
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
newVertex(PoolNameOrSocket, GraphName, CollName, MapData) ->
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr).
newVertex(PoolNameOrSocket, GraphName, CollName, MapData, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary, QueryBinary/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr).
%
% GET /_api/gharial/{graph}/vertex/{collection}/{vertex}
%
% graph
% collection
% vertex_key属性
%
% revif-match标头作为替代方法
%
% if-match If-MatchEtagEtag相同HTTP 412rev中提供Etag
% if-none-match If-None-MatchEtagEtag不同时HTTP 304
%
% HTTP 200
% errortruefalse
% code
% vertex
% HTTP 304if-none-match标头
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 403
% Read Only 访
% Read Only 访
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 404
%
%
%
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 412if-match标头
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
getVertex(PoolNameOrSocket, GraphName, CollName, VertexKey) ->
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary, "/", (agMiscUtils:toBinary(VertexKey))/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
getVertex(PoolNameOrSocket, GraphName, CollName, VertexKey, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary, "/", (agMiscUtils:toBinary(VertexKey))/binary, QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
getVertex(PoolNameOrSocket, GraphName, CollName, VertexKey, QueryPars, Headers) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary, "/", (agMiscUtils:toBinary(VertexKey))/binary, QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, Headers, undefined).
%
% PATCH /_api/gharial/{graph}/vertex/{collection}/{vertex}
%
% graph
% collection
% vertex_key属性
%
% waitForSync
% keepNullnull的值truedocumentssnullfalse
% returnOld
% returnNew
%
% if-match If-MatchEtagEtag相同HTTP 412URL的属性rev中提供Etag
%
% JSON对象
%
% HTTP 200waitForSync为true
% errortruefalse
% code
% vertex
% newArangoDB生成的所有内部属性returnNew为true时存在
% oldreturnOld为true时存在
% HTTP 202waitForSync为false
% errortruefalse
% code
% vertex
% newArangoDB生成的所有内部属性returnNew为true时存在
% oldreturnOld为true时存在
% HTTP 403
% Read Only 访
% Write 访
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 404
%
%
%
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 412if-match标头
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
updateVertex(PoolNameOrSocket, GraphName, CollName, VertexKey, MapData) ->
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary, "/", (agMiscUtils:toBinary(VertexKey))/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, [], BodyStr).
updateVertex(PoolNameOrSocket, GraphName, CollName, VertexKey, MapData, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary, "/", (agMiscUtils:toBinary(VertexKey))/binary, QueryBinary/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, [], BodyStr).
updateVertex(PoolNameOrSocket, GraphName, CollName, VertexKey, MapData, QueryPars, Headers) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary, "/", (agMiscUtils:toBinary(VertexKey))/binary, QueryBinary/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, Headers, BodyStr).
%
% PUT /_api/gharial/{graph}/vertex/{collection}/{vertex}
%
% graph
% collection
% vertex_key属性
%
% waitForSync
% keepNullnull的值
% returnOld
% returnNew
%
% if-match If-MatchEtagEtag相同HTTP 412URL的属性rev中提供Etag
%
% JSON对象
%
% HTTP 200waitForSync为true
% errortruefalse
% code
% vertex
% newArangoDB生成的所有内部属性returnNew为true时存在
% oldreturnOld为true时存在
% HTTP 202waitForSync为false
% errortruefalse
% code
% vertex
% newArangoDB生成的所有内部属性returnNew为true时存在
% oldreturnOld为true时存在
% HTTP 403
% Read Only 访
% Write 访
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 404
%
%
%
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 412 if -match标头
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
replaceVertex(PoolNameOrSocket, GraphName, CollName, VertexKey, MapData) ->
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary, "/", (agMiscUtils:toBinary(VertexKey))/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr).
replaceVertex(PoolNameOrSocket, GraphName, CollName, VertexKey, MapData, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary, "/", (agMiscUtils:toBinary(VertexKey))/binary, QueryBinary/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr).
replaceVertex(PoolNameOrSocket, GraphName, CollName, VertexKey, MapData, QueryPars, Headers) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary, "/", (agMiscUtils:toBinary(VertexKey))/binary, QueryBinary/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, Headers, BodyStr).
%
% DELETE /_api/gharial/{graph}/vertex/{collection}/{vertex}
%
% graph
% collection
% vertex_key属性
%
% waitForSync
% returnOld
%
% if-match If-MatchEtagEtag相同HTTP 412URL的属性rev中提供Etag
%
% HTTP 200
% errortruefalse
% code
% removedtrue
% oldreturnOld为true时存在
% HTTP 202waitForSync为false
% errortruefalse
% code
% removedtrue
% oldreturnOld为true时存在
%HTTP 403
% Read Only 访
% Write 访
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 404
%
%
%
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 412if-match标头
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
delVertex(PoolNameOrSocket, GraphName, CollName, VertexKey) ->
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary, "/", (agMiscUtils:toBinary(VertexKey))/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).
delVertex(PoolNameOrSocket, GraphName, CollName, VertexKey, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary, "/", (agMiscUtils:toBinary(VertexKey))/binary, QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).
delVertex(PoolNameOrSocket, GraphName, CollName, VertexKey, QueryPars, Headers) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/gharial/", GraphName/binary, "/vertex/", CollName/binary, "/", (agMiscUtils:toBinary(VertexKey))/binary, QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, Headers, undefined).
%
%
% POST /_api/gharial/{graph}/edge/{collection}
%
% graph
% collection
%
% waitForSync
% returnNew
% JSON对象是必需的
% _from使
% _to使
% _from和_to值使
% HTTP 201waitForSync为true
% errortruefalse
% code
% edge
% newArangoDB生成的所有内部属性returnNew为true时存在
% HTTP 202waitForSync为false
% errortruefalse
% code
% edge
% newArangoDB生成的所有内部属性returnNew为true时存在
% HTTP 400_from或_to丢失
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 403
% Read Only 访
% Write 访
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 404
%
%
% _from或_to顶点不存在
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
newEdge(PoolNameOrSocket, GraphName, CollName, MapData) ->
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", CollName/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr).
newEdge(PoolNameOrSocket, GraphName, CollName, MapData, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", CollName/binary, QueryBinary/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr).
%
% GET /_api/gharial/{graph}/edge/{collection}/{edge}
%
% graph
% collection
% edge_key属性
%
% revif-match标头作为替代方法
%
% if-match If-MatchEtagEtag相同HTTP 412URL的属性rev中提供Etag
% if-none-match If-None-MatchEtagEtag不同时HTTP 304
%
% HTTP 200
% errortruefalse
% code
% edge
% HTTP 304if-none-match标头
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 403
% Read Only 访
% Read Only 访
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 404
%
%
%
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 412if-match标头
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
getEdge(PoolNameOrSocket, GraphName, CollName, EdgeKey) ->
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", CollName/binary, "/", (agMiscUtils:toBinary(EdgeKey))/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
getEdge(PoolNameOrSocket, GraphName, CollName, EdgeKey, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", CollName/binary, "/", (agMiscUtils:toBinary(EdgeKey))/binary, QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
getEdge(PoolNameOrSocket, GraphName, CollName, EdgeKey, QueryPars, Headers) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", CollName/binary, "/", (agMiscUtils:toBinary(EdgeKey))/binary, QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, Headers, undefined).
%
% PATCH /_api/gharial/{graph}/edge/{collection}/{edge}
%
% graph
% collection
% edge_key属性
%
% waitForSync
% keepNullnull的值truedocumentssnullfalse
% returnOld
% returnNew
%
% if-match If-MatchEtagEtag相同HTTP 412URL的属性rev中提供Etag
%
% JSON对象
%
% HTTP 200waitForSync为false
% errortruefalse
% code
% edge
% newArangoDB生成的所有内部属性returnNew为true时存在
% oldreturnOld为true时存在
% HTTP 202waitForSync为false
% errortruefalse
% code
% edge
% newArangoDB生成的所有内部属性returnNew为true时存在
% oldreturnOld为true时存在
% HTTP 403
% Read Only 访
% Write 访
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 404
%
%
%
% _from或_to顶点不存在
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 412if-match标头
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
updateEdge(PoolNameOrSocket, GraphName, CollName, EdgeKey, MapData) ->
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", CollName/binary, "/", (agMiscUtils:toBinary(EdgeKey))/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, [], BodyStr).
updateEdge(PoolNameOrSocket, GraphName, CollName, EdgeKey, MapData, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", CollName/binary, "/", (agMiscUtils:toBinary(EdgeKey))/binary, QueryBinary/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, [], BodyStr).
updateEdge(PoolNameOrSocket, GraphName, CollName, EdgeKey, MapData, Headers, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", CollName/binary, "/", (agMiscUtils:toBinary(EdgeKey))/binary, QueryBinary/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, Headers, BodyStr).
%
% PUT /_api/gharial/{graph}/edge/{collection}/{edge}
%
% graph
% collection
% edge_key属性
%
% waitForSync
% keepNullnull的值
% returnOld
% returnNew
%
% if-match If-MatchEtagEtag相同HTTP 412URL的属性rev中提供Etag
% JSON对象是必需的
% _from使
% _to使
%
% HTTP 201waitForSync为true
% errortruefalse
% code
% edge
% newArangoDB生成的所有内部属性returnNew为true时存在
% oldreturnOld为true时存在
% HTTP 202waitForSync为false
% errortruefalse
% code
% edge
% newArangoDB生成的所有内部属性returnNew为true时存在
% oldreturnOld为true时存在
% HTTP 403
% Read Only 访
% Write 访
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 404
%
%
%
% _from或_to顶点不存在
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 412if-match标头
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
replaceEdge(PoolNameOrSocket, GraphName, CollName, EdgeKey, MapData) ->
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", CollName/binary, "/", (agMiscUtils:toBinary(EdgeKey))/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr).
replaceEdge(PoolNameOrSocket, GraphName, CollName, EdgeKey, MapData, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", CollName/binary, "/", (agMiscUtils:toBinary(EdgeKey))/binary, QueryBinary/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr).
replaceEdge(PoolNameOrSocket, GraphName, CollName, EdgeKey, MapData, QueryPars, Headers) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", CollName/binary, "/", (agMiscUtils:toBinary(EdgeKey))/binary, QueryBinary/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, Headers, BodyStr).
%
% DELETE /_api/gharial/{graph}/edge/{collection}/{edge}
%
% graph
% collection
% edge_key属性
%
% waitForSync
% returnOld
%
% if-match If-MatchEtagEtag相同HTTP 412URL的属性rev中提供Etag
%
% HTTP 200
% errortruefalse
% code
% removedtrue
% oldreturnOld为true时存在
% HTTP 202waitForSync为false
% errortruefalse
% code
% removedtrue
% oldreturnOld为true时存在
% HTTP 403
% Read Only 访
% Write 访
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 404
%
%
%
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
% HTTP 412if-match标头
% errortruefalse
% code
% errorNumArangoDB错误号
% errorMessage
delEdge(PoolNameOrSocket, GraphName, CollName, EdgeKey) ->
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", CollName/binary, "/", (agMiscUtils:toBinary(EdgeKey))/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).
delEdge(PoolNameOrSocket, GraphName, CollName, EdgeKey, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", CollName/binary, "/", (agMiscUtils:toBinary(EdgeKey))/binary, QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).
delEdge(PoolNameOrSocket, GraphName, CollName, EdgeKey, Headers, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/gharial/", GraphName/binary, "/edge/", CollName/binary, "/", (agMiscUtils:toBinary(EdgeKey))/binary, QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, Headers, undefined).

+ 116
- 0
src/agApi/agHotBackup.erl View File

@ -0,0 +1,116 @@
-module(agHotBackup).
-include("erlArango.hrl").
-compile(inline).
-compile({inline_size, 128}).
-compile([export_all, nowarn_export_all]).
% doc_address:https://www.arangodb.com/docs/stable/http/hot-backup.html
% HTTP接口进行热备份和还原
% v3.5.1
% ArangoDB HTTP接口的简介
%
%
% ArangoDB服务的即时一致快照
% UUIDAPI在这些标识符上运行
% API专用于处理POST操作
% 使API
%
% POST /_admin/backup/create
% JSON对象是必需的
% label使<timestamp>_<label>ID的此部分创建默认的UUID
% timeout120
% allowInconsistenttrue并且在给定的超时时间内无法获取全局事务锁定falseHTTP 408
% forcetrue并且在给定的超时时间内无法获取全局事务锁定JavaScript事务 timeout几秒钟使force请求超时将增加一倍使false allowInconsistent并force设置为true
% 使
%
% 201201
% 400使HTTP方法调用了create命令POSTHTTP 400
% 408HTTP 408
newBackup(PoolNameOrSocket, MapData) ->
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_admin/backup/create">>, [], BodyStr).
%
% POST /_admin/backup/restore
% JSON对象是必需的
% idID
% 使ID及时从快照恢复一致的备份ArangoDB服务上
%
% 200
% 400使HTTP方法调用了restore命令POSTHTTP 400
restoreBackup(PoolNameOrSocket, MapData) ->
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_admin/backup/restore">>, [], BodyStr).
%
% POST /_admin/backup/delete
% JSON对象是必需的
% id
% id
%
% 200200
% 400使HTTP方法调用delete命令POSTHTTP 400
% 404id找不到与该标识符相对应的备份
delBackup(PoolNameOrSocket, MapData) ->
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_admin/backup/delete">>, [], BodyStr).
%
% POST /_admin/backup/list
% JSON对象是必需的
% idattribute的对象id为字符串ID的备份
%
%
% 200200
% 400使list命令 HTTP 400
% 404id给出了ID或ID列表ID作为备份的标识符HTTP 404 NOT FOUND
% 405使HTTP方法调用了list命令POSTHTTP 405 METHOD NOT ALLOWED
getBackupList(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_admin/backup/list">>, [], undefined).
% list热备份的一个对象组成idid唯一地标识了特定的热备份version描绘了用于创建任何单个热备份的ArangoDB的版本datetime显示了创建热备份的时间
% sizeInBytesnrFiles的数量nrDBServers的数量访db服务器上找到的nrPiecesPresent
% potentiallyInconsistentavailable布尔参数被紧密地连接到备份到存在并且准备好被所有分贝服务器上恢复true 访
%
%
%
% POST /_admin/backup/upload
% JSON对象是必需的
% iduploadId
% remoteRepositoryURLuploadId属性URL进行了规范化和验证URL前缀必须作为键存在于以下配置对象中线1 ...//
% configuploadId arangobackup程序说明config
% uploadIdID
% aborttrue如果正在运行的上载操作应中止body参数是uploadId
%
%
% 200200
% 202202
% 400使HTTP方法调用了上载命令POSTHTTP 400
% 401HTTP 400
% 404id uploadId
uploadBackup(PoolNameOrSocket, MapData) ->
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_admin/backup/upload">>, [], BodyStr).
%
% POST /_admin/backup/download
% JSON对象是必需的
% iddownloadId
% remoteRepositoryURLdownloadId属性URL进行了规范化和验证URL前缀必须作为键存在于以下配置对象中线1 ...//
% configdownloadId arangobackup程序说明config
% downloadIdID
% aborttrue如果正在运行的下载操作应中止body参数是downloadId
%
%
% 200200
% 202202
% 400使HTTP方法调用了download命令POSTHTTP 400
% 401HTTP 401
% 404id downloadId
downloadBackup(PoolNameOrSocket, MapData) ->
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_admin/backup/download">>, [], BodyStr).

+ 413
- 0
src/agApi/agIndexes.erl View File

@ -0,0 +1,413 @@
-module(agIndexes).
-include("erlArango.hrl").
-compile(inline).
-compile({inline_size, 128}).
-compile([export_all, nowarn_export_all]).
% doc_address:https://www.arangodb.com/docs/stable/http/indexes.html
% HTTP接口
%
% ArangoDB索引的HTTP接口的一般介绍
%
% 访_key属性 _from和_to属性快速访问文档
%
% _id任何索引类型均不支持在用户定义的索引中使用system属性
%
% /访
%
% _keysystem属性中_key和_id属性的查询
%
%
%
%
%
%
%
%
%
% TTL
% TTL索引可用于自动从集合中删除过期的文档线
%
% 使libicu提供的单词边界分析来完成单词标记化
%
% ArangoDB中的所有索引都有唯一的句柄ArangoDB管理URI下找到
% http://server:port/_api/index/index-handle
% demo / 63563528URL为
% http://localhost:8529/_api/index/demo/63563528
%
% GET /_api/index/{index-id}
%
% index-id
%
% id
% type
% selectivityEstimate属性中提供了选择性估计
%
% 200HTTP 200
% 404 HTTP 404
getIndexInfo(PoolNameOrSocket, IndexId) ->
Path = <<"/_api/index/", (agMiscUtils:toBinary(IndexId))/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
%
% POST /_api/index#general
%
% collection
% json
% collection中创建一个新索引
% type属性中指定要创建的索引的类型
% fields属性中的被索引属性
% _id使_id作为索引属性手动创建索引将失败
% name属性中指定为字符串
% true将创建唯一索引false或忽略unique属性将创建一个非唯一索引
% 使unique属性可能会导致错误
%
%
%
%
% sparse属性设置为truenull的文档
%
% hash或skiplist的数组索引支持可选的重复数据删除属性true
%
%
% 200HTTP 200
% 201 HTTP 201
% 400使HTTP 400
% 404HTTP 404
newIndex(PoolNameOrSocket, CollName, MapData) ->
Path = <<"/_api/index?collection=", CollName/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr).
%
% DELETE /_api/index/{index-id}
%
% index-idID
% index-id的索引
%
% 200HTTP 200
% 404index-id未知HTTP 404
delIndex(PoolNameOrSocket, IndexId) ->
Path = <<"/_api/index/", (agMiscUtils:toBinary(IndexId))/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).
%
% GET /_api/index
%
% collection
% 使
%
% 200JSON对象
getIndexList(PoolNameOrSocket, CollName) ->
Path = <<"/_api/index?collection=", CollName/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
% 使
% /_api/simple/by-example则将使用该索引执行示例查询
%
% POST /_api/index#hash
%
% collection
% JSON对象是必需的
% type hash
% fields
% uniquetrue
% sparsetrue
% deduplicatefalse
% collection-name创建哈希索引
% fieldnull的文档将从索引中排除
% 使null值
%
%
% 200HTTP 200
% 201 HTTP 201
% 400使HTTP 400
% 404HTTP 404
newIndexOfHash(PoolNameOrSocket, CollName, MapData) ->
case MapData of
#{type := <<"hash">>} ->
Path = <<"/_api/index?collection=", CollName/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr);
#{<<"type">> := <<"hash">>} ->
Path = <<"/_api/index?collection=", CollName/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr);
_ ->
{error, param}
end.
%
% PUT /_api/simple/by-example
% 使3.4.0使AQL查询取代
% ArangoDB版本3.2.133.3.7API相当昂贵使HTTP Cursor API3.2.143.3.8API的内部实现已更改
% JSON对象是必需的
% collection
% example
% skip
% limit
% batchSize使BATCHSIZE的值 0
%
% HTTP Cursor
%
% 201
% 400JSON表示形式
% 404collection指定的collection未知
%
% PUT /_api/simple/first-example
% 使3.4.0使AQL查询取代
% ArangoDB版本3.2.133.3.7API相当昂贵使HTTP Cursor API3.2.143.3.8API的内部实现已更改
% JSON对象是必需的
% collection
% example
%
% HTTP 404
%
%
% 200
% 400JSON表示形式
% 404collection指定的collection未知
% 使
% /_api/simple/range其他操作将使用该索引来执行查询
%
% POST /_api/index#skiplist
%
% collection
% JSON对象是必需的
% type skiplist
% fields
% uniquetrue
% sparsetrue
% deduplicatefalse
% collection-name创建一个跳过列表索引
% fieldnull的文档将从索引中排除
% 使null值
%
%
% 200HTTP 200
% 201 HTTP 201
% 400HTTP 400
% 404HTTP 404
newIndexOfSkipList(PoolNameOrSocket, CollName, MapData) ->
case MapData of
#{type := <<"skiplist">>} ->
Path = <<"/_api/index?collection=", CollName/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr);
#{<<"type">> := <<"skiplist">>} ->
Path = <<"/_api/index?collection=", CollName/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr);
_ ->
{error, param}
end.
% 使
% /_api/simple/range其他操作将使用该索引执行查询
%
%
% POST /_api/index#persistent
%
% collection
% JSON对象是必需的
% type persistent
% fields
% uniquetrue
% sparsetrue
% collection-name创建一个持久索引
% fieldnull的文档将从索引中排除
% 使null值
%
%
% 200HTTP 200
% 201 HTTP 201
% 400HTTP 400
% 404HTTP 404
newIndexOfPersistent(PoolNameOrSocket, CollName, MapData) ->
case MapData of
#{type := <<"persistent">>} ->
Path = <<"/_api/index?collection=", CollName/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr);
#{<<"type">> := <<"persistent">>} ->
Path = <<"/_api/index?collection=", CollName/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr);
_ ->
{error, param}
end.
% 使TTL
%
% TTL
% POST /_api/index#ttl
%
% collection
% JSON对象是必需的
% type ttl
% fields
% expireAfter
% collection-name创建TTL索引
%
% 200HTTP 200
% 201 HTTP 201
% 400TTL索引HTTP 400TTL索引
% 404HTTP 404
newIndexOfTtl(PoolNameOrSocket, CollName, MapData) ->
case MapData of
#{type := <<"ttl">>} ->
Path = <<"/_api/index?collection=", CollName/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr);
#{<<"type">> := <<"ttl">>} ->
Path = <<"/_api/index?collection=", CollName/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr);
_ ->
{error, param}
end.
%
% POST /_api/index#geo
%
% collection
% JSON对象是必需的
% type geo
% fields
% location的数组使location作为坐标的路径在所有文档上创建地理空间索引double值的数组
% latitude和经度的数组使
%
% geoJsongeoJson为truehttp://geojson.org/geojson-spec.html#positions中描述的格式
% collection-name中创建地理空间索引
%
%
%
% 200HTTP 200
% 201 HTTP 201
% 404HTTP 404
newIndexOfGeo(PoolNameOrSocket, CollName, MapData) ->
case MapData of
#{type := <<"geo">>} ->
Path = <<"/_api/index?collection=", CollName/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr);
#{<<"type">> := <<"geo">>} ->
Path = <<"/_api/index?collection=", CollName/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr);
_ ->
{error, param}
end.
%
%PUT /_api/simple/near
%使3.4.0使AQL查询取代
%JSON对象是必需的
%collection
%latitude
%
%distance
%skip
%limit100
%geo使
%100
%使Near运算符使geo字段来选择特定的索引
%HTTP Cursor
%ArangoDB 2.6使ArangoDB版本中可能会删除此API使Near运算符从集合中检索文档的首选方法是使用NEAR函数发出AQL查询
%FOR doc IN NEAR(@@collection, @latitude, @longitude, @limit)
%RETURN doc`
%
%201
%400JSON表示形式
%404collection指定的collection未知
%
%
% PUT /_api/simple/within
% 使3.4.0使AQL查询取代
% JSON对象是必需的
% collection
% latitude
%
% radius
% distance
% limit100
% geo使
%
% 使使geo字段来选择特定的索引
% HTTP Cursor
% ArangoDB 2.6使ArangoDB版本中可能会删除此API使Near运算符从集合中检索文档的首选方法是使用WITHIN函数发出AQL查询
% FOR doc IN WITHIN(@@collection, @latitude, @longitude, @radius, @distanceAttributeName)
% RETURN doc
%
% 201
% 400JSON表示形式
% 404collection指定的collection未知
%
% POST /_api/index#fulltext
%
% collection
% JSON对象是必需的
% type
% fields
% minLength
% collection-name创建全文索引
%
% 200HTTP 200
% 201 HTTP 201
% 404HTTP 404
newIndexOfFulltext(PoolNameOrSocket, CollName, MapData) ->
case MapData of
#{type := <<"fulltext">>} ->
Path = <<"/_api/index?collection=", CollName/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr);
#{<<"type">> := <<"fulltext">>} ->
Path = <<"/_api/index?collection=", CollName/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], BodyStr);
_ ->
{error, param}
end.
%
%
% PUT /_api/simple/fulltext
% 使3.4.0使AQL查询取代
% JSON对象是必需的
% collection
% attribute
%
% skip
% limit
% index使
%
% 使
% HTTP Cursor
% ArangoDB 2.6使ArangoDB版本中可能会删除此API使Near运算符从集合中检索文档的首选方法是使用FULLTEXT AQL函数发出AQL查询
% FOR doc IN FULLTEXT(@@collection, @attributeName, @queryString, @limit)
% RETURN doc
%
% 201
% 400JSON表示形式
% 404collection指定的collection未知

+ 241
- 0
src/agApi/agMiscFuns.erl View File

@ -0,0 +1,241 @@
-module(agMiscFuns).
-include("erlArango.hrl").
-compile(inline).
-compile({inline_size, 128}).
-compile([export_all, nowarn_export_all]).
%% doc_address:https://www.arangodb.com/docs/stable/http/miscellaneous-functions.html
% HTTP接口
% ArangoDB的其他功能的HTTP接口的概述
%
% GET /_api/version
%
% detailstruedetails属性ArangoDB版本而异
% JSON对象
% HTTP 200
% arango
% version major major和minor将是数字sub
% detailsJSON对象details查询参数设置为true时
% architectureCPU体系结构64
% arm false-ARM cpu上运行
% asanasan地址清理器打开的情况下进行编译
% asm-crc32CRC函数吗
% assertions=>
% boost-versionboost版本
% build-date
% build-repositorygit-ID
% compiler使
% cplusplusC ++
% debug false
% endianness
% failure-tests false
% fd-client-event-handler使fd-setlinux上进行
% fd-setsizefd setsize对于文件描述符的最大数目有效
% full-version-string
% icu-versionICU
% jemalloc 使jemalloctrue
% maintenanceer-mode false
% openssl-versionopenssl版本
% platformos- linuxwindows或darwin
% reactor-type epoll TODO
% rockdb-versionrocksdb版本
% server-versionArangoDB发行版本
% sizeof int
% sizeof void * void指针的字节数
% sse42SSE 4.2CPU
% unaligned-access访
% v8-versionV8 JavaScript引擎版本
% vpack-version使velocypack实现的版本
% zlib-versionzlib的版本
% mode-[ ]
% hostID
srvVersion(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/version">>, [], undefined).
srvVersion(PoolNameOrSocket, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/version", QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
%
% GET /_api/engine
% 使JSON对象
% HTTP 200
% mmfiles或rocksdb
srvEngine(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/engine">>, [], undefined).
% WAL同步到磁盘
% PUT /_admin/wal/flush
%
% waitForSync
% waitForCollectortrue可能会阻塞很长时间
%
%
% 200
% 405使HTTP方法时返回
flushWal(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, <<"/_admin/wal/flush">>, [], undefined).
flushWal(PoolNameOrSocket, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_admin/wal/flush", QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], undefined).
%
% GET /_admin/wal/properties
% JSON对象
% allowOversizeEntries
% logfileSize
% HistoricLogfiles
% reserveLogfilesArangoDB在后台分配的最大保留日志文件数
% syncInterval
% valveWait
% acceleratorWhenPending 0
%
% 200
% 405使HTTP方法时返回
getWalProps(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/wal/properties">>, [], undefined).
% Wal的参数
% PUT /_admin/wal/properties
% JSON对象
% allowOversizeEntries
% logfileSize
% HistoricLogfiles
% reserveLogfilesArangoDB在后台分配的最大保留日志文件数
% valveWait
% acceleratorWhenPending 0
%
%
% 200
% 405使HTTP方法时返回
setWalProps(PoolNameOrSocket, MapData) ->
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, <<"/_admin/wal/properties">>, BodyStr, undefined).
%
% GET /_admin/wal/transactions
% JSON对象
% runningTransactions
% minLastCollectedIDnull
% minLastSealedIDnull
%
% 200
% 405使HTTP方法时返回
getTransactions(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/wal/transactions">>, [], undefined).
%
% GET /_admin/time
% time的对象Unix时间戳为单位
% HTTP 200
% errorfalse
% codeHTTP状态码
% timeUnix时间戳记为单位
curDbTime(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/time">>, [], undefined).
%
%
% POST /_admin/echo
%
%
%
% HTTP 200 Echo成功返回
% authorized
% user
% database
% urlURL
% protocol['http''https''velocystream']
% server
% address
% port
% client
% addressIP地址
% porttcp连接的客户端端口
% idid
% internals
% prefix
% headersHTTP标头的列表
% requestTypePOST使HTTP-Verb/...
% requestBodyPOST正文的字符串化版本
% parameters
% cookiescookie列表
% suffix
% rawSuffix
% path
% rawRequestBody
echo(PoolNameOrSocket, MapData) ->
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_admin/echo">>, [], BodyStr).
%
% GET /_admin/database/target-version
%
%
% 200
targetVersion(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/database/target-version">>, [], undefined).
%
% DELETE /_admin/shutdown
%
%
% 200OK都将返回
shutDown(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, <<"/_admin/shutdown">>, [], undefined).
%
% POST /_admin/execute
%
%
% javascript代码作为不带参数的函数主体执行return语句 application / json返回returnAsJSON设置为 trueJSON对象JSON.stringify生成的字符串
% 使option启动服务器时API端点才会存在--javascript.allow-admin-execute true
% falseAPI端点
%
% 200application / json类型的主体returnAsJSON的不同json对象或纯字符串
% 403ArangoDB不在集群模式下运行
% 404ArangoDB404
execute(PoolNameOrSocket, BodyStr) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_admin/execute">>, [], BodyStr).
%
% GET /_admin/status
%
% 使
%
% serverarango
% license
% version
% mode
% hostServerState
% serverInfo.role
% serverInfo.writeOpsEnabledtrue
% serverInfo.maintenancetrue
% agency.endpoints
% agency
% serverInfo.persistedIdide CRDN-e427b441-5087-4a9a-9983-2fb1682f3e2a
%
% serverInfo.state
% serverInfo.addresstcp// [:: 1]8530
% serverInfo.serverId CRDN-e427b441-5087-4a9a-9983-2fb1682f3e2a
%
% coordinator.foxxmasterfoxx主服务器的服务器ID
% coordinator.isFoxxmasterfoxx主服务器true
%
% agent.idID
% agent.leaderIdID
% agentLeading true
% agent.endpoint
% agent.term
%
% 200
dbStatus(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/status">>, [], undefined).

+ 75
- 0
src/agApi/agRepairJobs.erl View File

@ -0,0 +1,75 @@
-module(agRepairJobs).
-include("erlArango.hrl").
-compile(inline).
-compile({inline_size, 128}).
-compile([export_all, nowarn_export_all]).
%% doc_address:https://www.arangodb.com/docs/stable/http/repairs.html
%
% DistributionShardsLike
% 3.2.123.3.4distributeShardsLike设置中的原型集合完全一样地分布在DB-Server上
%
% 使API之前
%
%
%
% replicationFactor distributeShardsLike SmartGraphs
%
% replicationFactor在修理过程中进行更改可能会使其处于无法人工干预而无法修理的状态
% 使
%
% replicationFactor等于数据库服务器的总数replicationFactor一个DB-Server
%
%
%
% 使GET /_admin/repairs/distributeShardsLike
% 使GET将不会触发任何修复西
%%
%% GET /_admin/repairs/distributeShardsLike
checkRepair(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_admin/repairs/distributeShardsLike">>, [], undefined).
% collections<db>/<collection>error属性true如果该集合false error为trueerrorNum和 errorMessageerrorDetails
% POST /_admin/repairs/distributeShardsLike
% Async Results x-arango-async: store将作业放入后台
%
%
% $ wget --method=POST --header='x-arango-async: store' -qSO - http://localhost:8529/_admin/repair/distributeShardsLike
% HTTP/1.1 202 Accepted
% X-Content-Type-Options: nosniff
% X-Arango-Async-Id: 152223973119118
% Server: ArangoDB
% Connection: Keep-Alive
% Content-Type: text/plain; charset=utf-8
% Content-Length: 0
%
% 线
%
% X-Arango-Async-Id: 152223973119118
% IDGET婷/_api/job/pending和/_api/job/done将列出未完成或者完成ID
%
% GET测试方法来完成
%
% 使job api来获取状态和结果204api将返回404/_api/job通过 PUT
%
% $ wget --method=PUT -qSO - http://localhost:8529/_api/job/152223973119118 | jq .
% HTTP/1.1 200 OK
% X-Content-Type-Options: nosniff
% X-Arango-Async-Id: 152223973119118
% Server: ArangoDB
% Connection: Keep-Alive
% Content-Type: application/json; charset=utf-8
% Content-Length: 53
% {
% "error": false,
% "code": 200,
% "message": "Nothing to do."
% }
%%
%% POST /_admin/repairs/distributeShardsLike
doRepair(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_admin/repairs/distributeShardsLike">>, [], undefined).

+ 734
- 0
src/agApi/agReplication.erl View File

@ -0,0 +1,734 @@
-module(agReplication).
-include("erlArango.hrl").
-compile(inline).
-compile({inline_size, 128}).
-compile([export_all, nowarn_export_all]).
% doc_address:https://www.arangodb.com/docs/3.6/http/replications.html
% HTTP接口
%
% ArangoDB的HTTP复制接口的介绍
%
% HTTP复制接口有四个主要用途
%
%
%
%
% 使v3.3.0
%
% ArangoDB数据库的当前设置的集合加上他们的指标使使
%
% GET /_api/replication/inventory
%
% includeSystemtrue
% global _system默认值为false
% batchIdAPI调用的RocksDB引擎需要有效的batchId
% 使
% collection和state和 tick属性的JSON对象
%
% parameters
% indexes
%
% runningArangoDB 2.2true
% lastLogTick
% time
% lastLogTick值使lastLogTick的值
%
% / inventory API方法lastLogTick值以及集合和索引的数组
% / inventory返回的每个集合/ dump将集合数据流式传输到客户端lastLogTick的值 / inventory报告的集合上创建索引
%
% / logger-follow来获取在客户端调用/ inventory之后记录的第一批复制事件
% / logger-follow的调用应使用from参数/ inventory报告的lastLogTick的值 / logger-follow将返回 x-arango-replication-lastincluded
% / logger-follow以递增地获取上次传输后发生的新复制事件
% 使from参数 x-arango-replication-lastincluded头的值
% DBserver DBserver的IDDBserver
% global顶层对象使用参数包含一个键databases datbase名称
%
% 200
% 405使HTTP方法时返回
% 500500
getRepInventory(PoolNameOrSocket, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/replication/inventory", QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
%
%
% POST /_api/replication/batch
%
% JSON对象是必需的
% ttl
% JSON对象
% ID
% JSON对象
% idID
% DBserver DB-Server的IDDB服务器Coordinator情况下未绑定此属性
%
% 200
% 400ttl值无效Coordinator上未指定DBserver属性或该属性非法400
% 405使HTTP方法时返回
newRepBatch(PoolNameOrSocket, MapData) ->
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_api/replication/batch">>, [], BodyStr).
%
%
% DELETE /_api/replication/batch/{id}
%
%
% idID
%
% DBserver DB-Server的IDDB服务器Coordinator情况下未绑定此属性
%
% 204
% 400
% 405使HTTP方法时返回
delRepBatch(PoolNameOrSocket, BatchId) ->
Path = <<"/_api/replication/batch/", (agMiscUtils:toBinary(BatchId))/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).
%
%
% PUT /_api/replication/batch/{id}
%
%
% idID
% JSON对象是必需的
% ttl
% 使ID和提供的ttl值来扩展现有转储批次的ttl
% ttl
% DBserver DB-Server的IDDB服务器Coordinator情况下未绑定此属性
%
% 204ttl
% 400ttl值无效或未找到批次
% 405使HTTP方法时返回
% dump命令的结果可能非常庞大dump可能不会一次返回集合中的所有数据dump命令dump命令不仅会返回集合中的当前文档
% dump方法将仅返回文档
% 使
prolongRepBatch(PoolNameOrSocket, BatchId, MapData) ->
Path = <<"/_api/replication/batch/", (agMiscUtils:toBinary(BatchId))/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr).
%
%
% GET /_api/replication/dump
%
% collection ID
% chunkSize
% batchId使ID
%
% CHUNKSIZE查询参数可用于控制结果的大小CHUNKSIZE值也仅是被兑现chunkSize值太低可能导致服务器无法仅将一个条目放入结果中并返回它chunkSize值 chunkSizechunkSize
% chunkSize使
% Content-Type是application / x-arango-dump
% JSON对象
% ticktick属性
% key/使
% rev/ID
% data23002301//
% type
% 2300/
% 2301/
% 2302/
%
%
% 200 x-arango-replication-lastincluded设置为最后返回的文档的刻度
% 204x-arango-replication-lastincluded是0
% 404
% 405使HTTP方法时返回
% 500500
getRepDump(PoolNameOrSocket, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/replication/dump", QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
% Merkle树以进行收集
% Merkle树
% GET /_api/replication/revisions/tree
% RocksDB引擎以及ArangoDB v3.7.0
%
% collectionID
% batchId使ID
% Merkle树
% JSON / VelocyPack
%
% {
% version: <Number>,
% branchingFactor: <Number>
% maxDepth: <Number>,
% rangeMin: <String, revision>,
% rangeMax: <String, revision>,
% nodes: [
% { count: <Number>, hash: <String, revision> },
% { count: <Number>, hash: <String, revision> },
% ...
% { count: <Number>, hash: <String, revision> }
% ]
% }
% 1
% <String, revision>6411使_rev值相同的编码64JavaScript完整表示2^53-1
% maxDepth和 branchingFactorindex 0index [1, branchingFactor]
%
% 200
% 401
% 404
% 405使HTTP方法时返回
% 500500
% 501使mmfiles调用或在不支持按版本同步的集合上返回
getRepTree(PoolNameOrSocket, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/replication/revisions/tree", QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
% Merkle树
% Merkle树
% POST /_api/replication/revisions/tree
% RocksDB引擎以及ArangoDB v3.7.0
%
% collectionID
% Merkle树
%
%
% 204
% 401
% 404
% 405使HTTP方法时返回
% 500500
% 501使mmfiles调用或在不支持按版本同步的集合上返回
resetRepTree(PoolNameOrSocket, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/replication/revisions/tree", QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, Path, [], undefined).
% ID
% ID
% PUT /_api/replication/revisions/ranges
% RocksDB引擎以及ArangoDB v3.7.0
%
% collectionID
% batchId使ID
% resume
getRepRanges(PoolNameOrSocket, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/replication/revisions/ranges", QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], undefined).
%
%
% PUT /_api/replication/revisions/documents
% RocksDB引擎以及ArangoDB v3.7.0
%
% collectionID
% batchId使ID
%
% JSON / VelocyPackID数组
% [
% <String, revision>,
% <String, revision>,
% ...
% <String, revision>
% ]
%
% JSON / VelocyPack数组
%
% <String, revision>6411使_rev值相同的编码64JavaScript完整表示2^53-1
%
% 200
% 401
% 404
% 405使HTTP方法时返回
% 500500
% 501使mmfiles调用或在不支持按版本同步的集合上返回
getRepDoc(PoolNameOrSocket, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/replication/revisions/documents", QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], undefined).
%
%
% PUT /_api/replication/sync
% JSON对象是必需的
% endpoint tcp//192.168.173.138529
% database
% username使ArangoDB用户名
% password使
% includeSystem
% incrementaltrue使false
% strictTypeinclude或exclude
% restrictCollections使 restrictTypelimitType为includelimitType为exclude
% initialSyncMaxWaitTime0
% ArangoDB数据库的完整数据同步
% 使ArangoDB数据库连接到远程端点ArangoDB数据库上创建数据状态的本地备份
% sync首先会从远程端点获取集合和索引的列表 API来实现ArangoDB数据库中的数据ArangoDB数据库 API
% JSON对象
%
% lastLogTick使
% ArangoDB数据库
% 使
%
%
% 200
% 400
% 405使HTTP方法时返回
% 500
% 501
startRepSync(PoolNameOrSocket, MapData) ->
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, <<"/_api/replication/sync">>, [], BodyStr).
%
%
% GET /_api/replication/clusterInventory
%
% includeSystemtrue
%
% JSON对象数组// {DB-} / * arangodump的数据格式
%
% 200
% 405使HTTP方法时返回
% 500500
getRepClusterInv(PoolNameOrSocket, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/replication/clusterInventory", QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
%
% ArangoDB允许启动ArangoDB 2.2
% ArangoDB 2.2
%
%
% GET /_api/replication/logger-state
%
% JSON对象
% stateJSON对象
% running
% lastLogTick
% totalEvents
% time
% serverJSON对象
% version
% serverIdID
% clientJSON对象返回
% syncerIdID
% serverIdID
% lastServedTicklogger-follow API
% timelogger-follow API的日期和时间
%
% 200
% 405使HTTP方法时返回
% 500
getRepLoggerState(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/replication/logger-state">>, [], undefined).
%
%
% GET /_api/replication/logger-follow
% 使3.4.0
%
% from
%
% chunkSize
% includeSystemtrue
% 使
% from值
% 使from查询参数时使from from值的日志条目from的日志条目将被排除使from值
% 使使to参数
% CHUNKSIZE查询参数可用于控制结果的大小CHUNKSIZE值也仅是被兑现chunkSize值太低可能导致服务器无法仅将一个日志条目放入结果中并将其返回chunkSize值 chunkSizechunkSize
% chunkSize使
% Content-Type是application / x-arango-dumpJSON对象
% tick
% type
%
% cidID
% tidID
%
% revID
% data
%
% HTTP标头
% x-arango-replication-active使
% x-arango-replication-lastincluded0from的值
% x-arango-replication-lasttick
% x-arango-replication-checkmorelogger-follow
%
%
%
% 200
% 204
% 400from或to值无效
% 405使HTTP方法时返回
% 500500
% 501
% 使3.4.0
%
% GET /_api/replication/logger-first-tick
%
% firstTick的JSON对象
%
%
% 200
% 405使HTTP方法时返回
% 500500
% 501
getRepLoggerFirstTick(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/replication/logger-first-tick">>, [], undefined).
%
% GET /_api/replication/logger-tick-ranges
% WAL日志文件的刻度值的当前可用范围
% JSON数组
% datafile
% status
% tickMin
% tickMax
%
% 200
% 405使HTTP方法时返回
% 500
% 501
getRepLoggerTickRanges(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/replication/logger-tick-ranges">>, [], undefined).
%
% applier命令允许远程启动ArangoDB数据库复制应用程序的状态和配置
%
% applier命令允许远程启动ArangoDB数据库复制应用程序的状态和配置
%
% GET /_api/replication/applier-config
%
% global truefalse
%
% JSON对象
% tcp//192.168.173.138529
% database _system
% username使ArangoDB用户名
% password使
% maxConnectRetries
% connectTimeout
% requestTimeout
% chunkSize使
% autoStart
% adaptivePolling使
% includeSystem
% autoResync
% autoResyncRetries0autoResync
% initialSyncMaxWaitTime使autoResync设置为true时0
% connectionRetryWaitTime0
% idleMinWaitTimeHTTP日志获取请求的频率0
% idleMaxWaitTime HTTP日志获取请求的最大频率adaptivePolling设置为true时使0
% requireFromPresenttrue requireFromPresent为truefalse
% verbosetrue
% restrictTyperestrictCollections
% restrictCollectionsrestrictType
%
% 200
% 405使HTTP方法时返回
% 500500
getRepApplierConfig(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/replication/applier-config">>, [], undefined).
getRepApplierConfig(PoolNameOrSocket, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/replication/applier-config", QueryBinary/binary>>, [], undefined).
%
% PUT /_api/replication/applier-config
%
% truefalse
% JSON对象是必需的
% tcp//192.168.173.138529
% database
% username使ArangoDB用户名
% password使
% maxConnectRetries
% connectTimeout
% requestTimeout
% chunkSize使
% autoStart
% adaptivePollingtrue使使
% adaptivePolling设置为false将使复制者以固定的时间间隔与记录器服务器联系
% includeSystem
% autoResync
% autoResyncRetries0 autoResync
% initialSyncMaxWaitTime使autoResync设置为true时0
% connectionRetryWaitTime0
% idleMinWaitTimeHTTP日志获取请求的频率0
% idleMaxWaitTime HTTP日志获取请求的最大频率adaptivePolling设置为true时使 0
% requireFromPresenttrue requireFromPresent为truefalse
% verbosetrue
% restrictTyperestrictCollections ;
% restrictCollectionsrestrictType
%
% JSON对象
%
% 200
% 400400
% 405使HTTP方法时返回
% 500500
setRepApplierConfig(PoolNameOrSocket, MapData) ->
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, <<"/_api/replication/applier-config">>, [], BodyStr).
setRepApplierConfig(PoolNameOrSocket, MapData, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, <<"/_api/replication/applier-config", QueryBinary/binary>>, [], BodyStr).
%
% PUT /_api/replication/applier-start
%
% truefalse
% fromlastLogTick值使
%
% 线使
% / _api / replication / applier-state API
%
% 200
% 400
% 405使HTTP方法时返回
% 500500
startRepApplier(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, <<"/_api/replication/applier-start">>, [], undefined).
startRepApplier(PoolNameOrSocket, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/replication/applier-start", QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], undefined).
%
% PUT /_api/replication/applier-stop
%
% truefalse
%
%
% 200
% 405使HTTP方法时返回
% 500500
stopRepApplier(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, <<"/_api/replication/applier-stop">>, [], undefined).
stopRepApplier(PoolNameOrSocket, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, <<"/_api/replication/applier-stop", QueryBinary/binary>>, [], undefined).
%
% GET /_api/replication/applier-state
%
% truefalse
%
% JSON对象
% stateJSON对象
% running
% lastAppliedContinuousTick
% lastProcessedContinuousTick
%
% lastAvailableContinuousTick
% ticksBehind/
% 使
% 使
% time
% totalRequests
% totalFailedConnects
% totalEvents
% totalOperationsExcludedrestrictCollections而被排除的日志事件总数
% progressJSON对象
%
%
% failedConnects
% lastErrorJSON对象
% errorNum
% errorMessage
%
% lastError将为空
% serverJSON对象
% version
% serverIdID
%
% database
%
%
% 200
% 405使HTTP方法时返回
% 500500
getRepApplierState(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/replication/applier-state">>, [], undefined).
getRepApplierState(PoolNameOrSocket, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/replication/applier-state", QueryBinary/binary>>, [], undefined).
%
% PUT /_api/replication/make-slave
% JSON对象是必需的
% tcp//192.168.173.138529
% database
% username使ArangoDB用户名
% password使
% includeSystem
% strictTypeinclude或exclude
% restrictCollections使restrictTypelimitType为includelimitType为exclude
% maxConnectRetries
% connectTimeout
% requestTimeout
% chunkSize使
% adaptivePolling使
% autoResync
% autoResyncRetries0autoResync
% initialSyncMaxWaitTime使autoResync设置为true时0
% connectionRetryWaitTime0
% idleMinWaitTimeHTTP日志获取请求的频率0
% idleMaxWaitTime HTTP日志获取请求的最大频率adaptivePolling设置为true时使 0
% requireFromPresenttrue requireFromPresent为truefalse
% verbosetrue
% ArangoDB数据库的完整数据同步
%
% JSON对象
% stateJSON对象
% running
% lastAppliedContinuousTick
% lastProcessedContinuousTick
%
% lastAvailableContinuousTick
% ticksBehind/
% 使
% 使
% time
% totalRequests
% totalFailedConnects
% totalEvents
% totalOperationsExcludedrestrictCollections而被排除的日志事件总数
% progressJSON对象
%
%
% failedConnects
% lastErrorJSON对象
% errorNum
% errorMessage
%
% lastError将为空
% serverJSON对象
% version
% serverIdID
%
% database
%
% ArangoDB数据库
% 使
%
%
%
% 200
% 400
% 405使HTTP方法时返回
% 500
% 501
changeRepMakeSlave(PoolNameOrSocket, MapData) ->
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, <<"/_api/replication/make-slave">>, [], BodyStr).
%
%ID
%
%GET /_api/replication/server-id
%IDAPI方法也返回该IDID的简便方法
%serverId的JSON对象ID作为字符串返回
%
%200
%405使HTTP方法时返回
%500500
getRepServerId(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/replication/server-id">>, [], undefined).
% WAL
% 线
% GET /_api/wal/range
% WAL文件的当前刻度值范围
% JSON对象
% tickMin
% tickMax
% time YYYY-MM-DDTHHMMSSZ
% serverversion和serverId的对象
%
% 200
% 405使HTTP方法时返回
% 500
% 501
getWalRange(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/wal/range">>, [], undefined).
%
%
% GET /_api/wal/lastTick
%
% ticktime和server的JSON对象
% tick
% time YYYY-MM-DDTHHMMSSZ
% serverversion和serverId的对象
%
%
% 200
% 405使HTTP方法时返回
% 500500
% 501
getWalLastTick(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/wal/lastTick">>, [], undefined).
%
% GET /_api/wal/tail
%
% false时 true仅在_system数据库上有效false
% fromAPI时x-arango-replication-lastincluded标头返回的值0
%
% lastScannedx-arango-replication-lastscanned标头的值0使rocksdb引擎可以通过多个响应分解大型事务
% chunkSize
% syncerIdID使 使this或serverId来获取使用rocksdb存储引擎读取的所有操作
% serverIdIDsyncerId使 使this或syncerId才能有机会读取rocksdb存储引擎的所有操作
% clientInfo
% barrierIdWAL条目的障碍的IDMMFiles存储引擎是必需的
% 便
% from值
% 使from查询参数时使from from值的日志条目from的日志条目将被排除使from值
% 使使to参数
% CHUNKSIZE查询参数可用于控制结果的大小CHUNKSIZE值也仅是被兑现chunkSize值太低可能导致服务器无法仅将一个日志条目放入结果中并将其返回chunkSize值 chunkSizechunkSize
% chunkSize使
% Content-Type是application / x-arango-dumpJSON对象
% tick
% type
%
% cuidView或collection的全局唯一ID
% db
% tidID
% data
%
% HTTP标头
% x-arango-replication-active使
% x-arango-replication-lastincluded0from的值
% x-arango-replication-lastscanned退lastScanned标头中使用此值使rocksdb引擎分解多个响应上的请求
% x-arango-replication-lasttick
% from参数中指定的刻度开始返回所有刻度值x-arango-replication-frompresent设置为truefalse
% x-arango-replication-checkmorelogger-follow
%
%
%
% 200
% 204
% 400from或to值无效
% 405使HTTP方法时返回
% 500500
% 501
getWalTail(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/wal/tail">>, [], undefined).
getWalTail(PoolNameOrSocket, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/wal/tail", QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).

+ 10
- 0
src/agApi/agSimpleQueries.erl View File

@ -0,0 +1,10 @@
-module(agSimpleQueries).
-include("erlArango.hrl").
-compile(inline).
-compile({inline_size, 128}).
-compile([export_all, nowarn_export_all]).
% doc_address:https://www.arangodb.com/docs/stable/http/simple-query.html
% 3.4.0使API使AQL查询取代

+ 92
- 0
src/agApi/agTasks.erl View File

@ -0,0 +1,92 @@
-module(agTasks).
-include("erlArango.hrl").
-compile(inline).
-compile({inline_size, 128}).
-compile([export_all, nowarn_export_all]).
% doc_address:https://www.arangodb.com/docs/stable/http/traversal.html
% HTTP任务接口
% ArangoDB的任务HTTP接口
% API操作提供了一些示例
%
%
% GET /_api/tasks/
%
% HTTP 200
% **
getTaskList(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/tasks/">>, [], undefined).
%
% GET /_api/tasks/{id}
%
% idID
% ID指定的服务器上获取一个现有任务
% HTTP 200
% **
getTask(PoolNameOrSocket, TaskId) ->
Path = <<"/_api/tasks/", (agMiscUtils:toBinary(TaskId))/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
%
% POST /_api/tasks
% JSON对象是必需的
% name
% commandJavaScript代码
% params
% period
% offset
%
% ID创建一个新任务
% HTTP 200
% id
% created
% type[ periodictimed]
%
%
% periodperiod秒
% offset
% commandjavascript函数
% database
% code200
% errorfalse
% 400HTTP 400
newTask(PoolNameOrSocket, MapData) ->
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_api/tasks">>, [], BodyStr).
% ID的新任务
% PUT /_api/tasks/{id}
%
% idid
% JSON对象是必需的
% name
% commandJavaScript代码
% params
% period
% offset
% ID的新任务
%
% 400ID已经存在或其余主体不正确HTTP 400
newTask(PoolNameOrSocket, TaskId, MapData) ->
Path = <<"/_api/tasks/", (agMiscUtils:toBinary(TaskId))/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr).
%
% DELETE /_api/tasks/{id}
%
% idid
% ID标识的任务
% HTTP 200HTTP 200
% code200
% errorfalse
% HTTP 404ID未知HTTP 404
% code404
% errortrue
% errorMessage
delTask(PoolNameOrSocket, TaskId) ->
Path = <<"/_api/tasks/", (agMiscUtils:toBinary(TaskId))/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).

+ 204
- 0
src/agApi/agTransactions.erl View File

@ -0,0 +1,204 @@
-module(agTransactions).
-include("erlArango.hrl").
-compile(inline).
-compile({inline_size, 128}).
-compile([export_all, nowarn_export_all]).
% doc_address:https://www.arangodb.com/docs/stable/http/transaction.html
% HTTP接口
%
% ArangoDB的事务在服务器上执行
%
% API
% JavaScript交易 API
% ArangoDB中事务如何工作以及ArangoDB可以提供哪些保证的详细说明Transactions
%
%
% 使使/RDBMS使用BEGINCOMMIT和ROLLBACK操作的方式
%
% API
%
% JavaScript交易
% JS-Transactions允许您向服务器发送一段专用的JavaScript代码
%
%
%
%
%
% 10
% 128 MB
%
%
% 使
% HTTP接口
% v3.5.0
% 使使/RDBMS使用BEGINCOMMIT和ROLLBACK操作的方式
% 使 ArangoDB服务器
% JS-Transaction相反使ArangoDB支持的各种事务选项
% API ArangoDB中的其他API 使x-arango-trx-id标头中指定事务标识符使使
% API操作包括
% Document API中的所有操作
% Collection API的文档数
% Collection API截断集合
% Cursor API创建AQL游标
% / Gharial APIv3.5.1
% 访ArangoDB服务器上的资源
% MMFiles存储引擎中的读写操作以及RocksDB中的写操作
% ArangoDB中事务如何工作的更详细描述Transactions
%
%
%
%
% POST /_api/transaction/begin
% JSON对象是必需的
% collectionscollections必须是一个JSON对象readwrite或Exclusive collection名称的数组或单个collection name作为字符串使write或Exclusive属性声明将在事务中写入的集合
% waitForSync
% allowImplicit
% lockTimeout使lockTimeout设置为0将使ArangoDB不会在等待锁定时超时
% maxTransactionSizeRocksDB存储引擎的尊敬
% POST请求的正文中传递HTTP 201
% JSON对象具有以下属性
% error false
% codeHTTP状态码
%
% id
% status running
% 使HTTP 400HTTP 404
% JSON对象
% errortrue
% codeHTTP状态码
% errorNum
% errorMessage
%
% 201 HTTP 201
% 400使HTTP 400
% 404使HTTP 404
beginTransaction(PoolNameOrSocket, MapData) ->
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_api/transaction/begin">>, [], BodyStr).
%
% GET /_api/transaction/{transaction-id}
%
% transaction-id
%
% id
% status
%
% 200 HTTP 200
% 400使HTTP 400
% 404使HTTP 404
getTransactionStatus(PoolNameOrSocket, TransactionId) ->
Path = <<"/_api/transaction/", (agMiscUtils:toBinary(TransactionId))/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
% 使使
%
%
% PUT /_api/transaction/{transaction-id}
%
% transaction-id
%
% HTTP 200JSON对象具有以下属性
% error false
% codeHTTP状态码
%
% id
% status committed
% 使HTTP 400HTTP 404HTTP 409
% JSON对象
% errortrue
% codeHTTP状态码
% errorNum
% errorMessage
%
% 200 HTTP 200
% 400使HTTP 400
% 404使HTTP 404
% 409使HTTP 409
commitTransaction(PoolNameOrSocket, TransactionId) ->
Path = <<"/_api/transaction/", (agMiscUtils:toBinary(TransactionId))/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], undefined).
%
% DELETE /_api/transaction/{transaction-id}
%
% transaction-id
%
% HTTP 200JSON对象具有以下属性
% error false
% codeHTTP状态码
%
% id
% status aborted
% 使HTTP 400HTTP 404HTTP 409
% JSON对象
% errortrue
% codeHTTP状态码
% errorNum
% errorMessage
%
% 200 HTTP 200
% 400使HTTP 400
% 404使HTTP 404
% 409HTTP 409
abortTransaction(PoolNameOrSocket, TransactionId) ->
Path = <<"/_api/transaction/", (agMiscUtils:toBinary(TransactionId))/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).
%
% GET /_api/transaction
% 使transactions属性进行描述
%
% idID
% status
%
% 200HTTP 200
getTransactionList(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/transaction">>, [], undefined).
% JavaScript交易的HTTP接口
% ArangoDB的JS事务在服务器上执行
% ArangoDB中的JS事务不提供单独的BEGINCOMMIT和ROLLBACK ArangoDB JS事务由JavaScript函数描述JavaScript函数中的代码将以事务方式执行
%
% ArangoDB中事务如何工作的更详细描述Transactions
%
% POST /_api/transaction
% JSON对象是必需的
% collectionscollections必须是一个JSON对象readwrite或Exclusive collection名称的数组或单个collection name作为字符串使write或Exclusive属性声明将在事务中写入的集合allowImplicit设置为false 使
% actionJavaScript代码的形式 return语句结尾 REST API还将在result属性中返回返回的值
% waitForSync
% allowImplicit
% lockTimeout使lockTimeout设置为0将使ArangoDB不会在等待锁定时超时
% params
% maxTransactionSizeRocksDB存储引擎的尊敬
% POST请求的正文中传递
% HTTP 200result属性中返回
% JSON对象具有以下属性
% error false
% codeHTTP状态码
% result
% 使HTTP 400
% JSON对象
% errortrue
% codeHTTP状态码
% errorNum
% errorMessage
% 使 HTTP 400HTTP 409HTTP 500
%
% 200 HTTP 200
% 400使HTTP 400
% 404使HTTP 404
% 500使HTTP 500
executeTransaction(PoolNameOrSocket, MapData) ->
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_api/transaction">>, [], BodyStr).

+ 13
- 0
src/agApi/agTraversals.erl View File

@ -0,0 +1,13 @@
-module(agTraversals).
-include("erlArango.hrl").
-compile(inline).
-compile({inline_size, 128}).
-compile([export_all, nowarn_export_all]).
% doc_address:https://www.arangodb.com/docs/stable/http/traversal.html
%
%
% POST /_api/traversal
% 使3.4.0AQL图形遍历取代

+ 240
- 0
src/agApi/agUserMgr.erl View File

@ -0,0 +1,240 @@
-module(agUserMgr).
-include("erlArango.hrl").
-compile(inline).
-compile({inline_size, 128}).
-compile([export_all, nowarn_export_all]).
% doc_address:https://www.arangodb.com/docs/stable/http/user-management.html
% HTTP接口
% ArangoDB HTTP接口的简介
% _users中_users集合
% REST API中可用的所有功能
% ArangoDB的复制中不包括用户操作
%
% POST /_api/user
% JSON对象是必需的
% user
% passwd使ARANGODB_DEFAULT_ROOT_PASSWORD ARANGODB_DEFAULT_ROOT_PASSWORDArangoDBAmazon的实例标识符
% activetrue
% extraJSON对象
% 访 REST调用
%
% 201
% 400JSON格式不正确或请求中缺少必需数据
% 401_system 访访
% 403访访
% 409
newUser(PoolNameOrSocket, MapData) ->
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_api/user">>, [], BodyStr).
% 访
% PUT /_api/user/{user}/database/{dbname}
%
% user
% dbname
% JSON对象是必需的
% grant使 rw访Administrate
% 使 ro访Access
% 使none访 访
% user的数据库dbname设置数据库访问级别访REST调用
%
% 200访
% 400JSON格式不正确或请求中缺少必需数据
% 401_system 访访
% 403访访
setUserDbAccessLevel(PoolNameOrSocket, UserName, DbName, MapData) ->
Path = <<"/_api/user/", UserName/binary, "/database/", DbName/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr).
% 访
% PUT /_api/user/{user}/database/{dbname}/{collection}
%
% user
% dbname
% collection
% JSON对象是必需的
% grant使 rw访 /
% 使 ro访
% 使none访 访
%
% 访DBNAME 访REST调用
%
% 200访
% 400JSON格式不正确或请求中缺少必需数据
% 401_system 访访
% 403访访
setUserCollAccessLevel(PoolNameOrSocket, UserName, DbName, CollName, MapData) ->
Path = <<"/_api/user/", UserName/binary, "/database/", DbName/binary, "/", CollName/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr).
% 访访
% DELETE /_api/user/{user}/database/{dbname}
%
% user
% dbname
%
% user的数据库dbname的数据库访问级别使访访No access_system数据库的权限才能执行此REST调用
%
%
% 202访
% 400JSON格式不正确或请求中缺少必需数据
clearUserDbAccessLevel(PoolNameOrSocket, UserName, DbName) ->
Path = <<"/_api/user/", UserName/binary, "/database/", DbName/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).
% 访访
% DELETE /_api/user/{user}/database/{dbname}/{collection}
%
% user
% dbname
% collection
%
% user的数据库dbname中集合集合的集合访问级别使访访No access_system数据库的权限才能执行此REST调用
%
% 202访
% 400
clearUserCollAccessLevel(PoolNameOrSocket, UserName, DbName, CollName) ->
Path = <<"/_api/user/", UserName/binary, "/database/", DbName/binary, "/", CollName/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).
% 访
% GET /_api/user/{user}/database/
%
%
%
% full访
% 访REST调用
% 访JSON对象
% full
%
% 200
% 400访
% 401_system 访访
% 403访访
getUserDbList(PoolNameOrSocket, UserName) ->
Path = <<"/_api/user/", UserName/binary, "/database/">>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
getUserDbList(PoolNameOrSocket, UserName, QueryPars) ->
QueryBinary = agMiscUtils:spellQueryPars(QueryPars),
Path = <<"/_api/user/", UserName/binary, "/database/", QueryBinary/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
% 访
% GET /_api/user/{user}/database/{dbname}
%
% user
% dbname
% 访
%
% 200访
% 400访
% 401_system 访访
% 403访访
getUserDbAccessLevel(PoolNameOrSocket, UserName, DbName) ->
Path = <<"/_api/user/", UserName/binary, "/database/", DbName/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
% 访
% GET /_api/user/{user}/database/{dbname}/{collection}
%
% user
% dbname
% collection
% 访
%
% 200访
% 400访
% 401_system 访访
% 403访访
getUserCollAccessLevel(PoolNameOrSocket, UserName, DbName, CollName) ->
Path = <<"/_api/user/", UserName/binary, "/database/", DbName/binary, "/", CollName/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
%
% PUT /_api/user/{user}
%
% user
% JSON对象是必需的
% passwd使
% activetrue
% extraJSON对象
% user中指定访 REST调用/
%
% 200
% 400JSON格式不正确或请求中缺少必需数据
% 401_system 访访
% 403访访
% 404
replaceUser(PoolNameOrSocket, UserName, MapData) ->
Path = <<"/_api/user/", UserName/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr).
%
% PATCH /_api/user/{user}
%
% user
% JSON对象是必需的
% passwd使
% activetrue
% extraJSON对象
% user中指定访 REST调用/
%
% 200
% 400JSON格式不正确或请求中缺少必需数据
% 401_system 访访
% 403访访
% 404
updateUser(PoolNameOrSocket, UserName, MapData) ->
Path = <<"/_api/user/", UserName/binary>>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, [], BodyStr).
%
% DELETE /_api/user/{user}
%
% user
%user标识的现有用户访REST调用
%
% 202
% 401_system 访访
% 403访访
% 404
delUser(PoolNameOrSocket, UserName) ->
Path = <<"/_api/user/", UserName/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).
%
% GET /_api/user/{user}
%
% user
% 访REST调用
%
% 200
% 401_system 访访
% 403访访
% 404
getUser(PoolNameOrSocket, UserName) ->
Path = <<"/_api/user/", UserName/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
%
% GET /_api/user/
% 访REST调用
% JSON对象
% user
% active
% extraJSON对象
%
% 200
% 401_system 访访
% 403访访
getUserList(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/user/">>, [], undefined).

+ 216
- 0
src/agApi/agViews.erl View File

@ -0,0 +1,216 @@
-module(agViews).
-include("erlArango.hrl").
-compile(inline).
-compile({inline_size, 128}).
-compile([export_all, nowarn_export_all]).
% doc_address:https://www.arangodb.com/docs/stable/http/views.html
% HTTP接口
%
% ArangoDB Views的HTTP接口的简介
%
%
% 使访
%
% ArangoSearch
%
%
% 使ArangoDB当前使用64位无符号整数值在内部维护View IDView ID返回给客户端时ArangoDB会将它们放入字符串中View ID使ArangoDB返回的View ID视为不透明字符串
%
%
% View标识符不同View的创建者提供的_线-ArangoDB中的命名约定
%
%
% ArangoDB中的所有视图都有唯一的标识符和唯一的名称ArangoDB在内部使用视图的唯一标识符来查找视图ArangoDB管理使访使
%
% http://server:port/_api/view/<view-name>
% 7254820demoURL为
% http://localhost:8529/_api/view/demo
% ArangoSearch视图
% POST /_api/view#arangosearch
% JSON对象是必需的
% name
% type arangosearch
% links ArangoSearch查看链接属性
% primarySortAQL优化View的所有文档primarySort定义匹配SORT操作将被优化
% "asc升序,"desc"降序): [ { "field": "attr", "direction": "asc"}, … ]
% cleanupIntervalStepArangoSearch数据目录中的未使用文件之间至少等待这么多次提交2使0commit + consolidate/
% View内部数据结构的新状态//
% commitIntervalMsecView数据存储更改和使文档对查询可见之前1000使0/使/
% ArangoSearch视图遵循ArangoDB中的所有数据将通过相应的查询表达式进行匹配ArangoSearch View//ArangoDB事务中调用的查询反映ArangoDB事务仍将继续返回可重复读取状态
% integrationIntervalMsec'consolidationPolicy'View数据存储与可能释放文件系统上的空间之间至少等待这么多毫秒10000使0使
% ArangoSearch视图遵循 consolidationIntervalMsec consolidationPolicy
% consolidationPolicy{}
% ArangoDB交易进行的插入文档的一个或多个ArangoSearch内部段被创建使使
%
% type
% "tier"使segments*minScore属性可用
% "bytes_accum" {threshold} > (segment_bytes + sum_of_merge_candidate_segment_bytes) / all_segment_bytes {threshold}使threshold属性可用
% threshold[0.0, 1.0]
% segmentsBytesFloor2097152
% segmentsBytesMax5368709120
% segmentsMax10
% segmentsMin1
% minScore0
%
% writebufferIdle64使0
% writebufferActive0使0
% writebufferSizeMax0value会关闭所有写入器线ArangoDB服务器启动选项0使33554432使0
%
%
% 400name或type属性丢失或无效 HTTP 400
% 409name的视图HTTP 409
newView(PoolNameOrSocket, MapData) ->
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPost, <<"/_api/view">>, [], BodyStr).
%
% GET /_api/view/{view-name}
%
% view-name
%
% id
% name
% type
%
% 404HTTP 404
getViewInfo(PoolNameOrSocket, ViewName) ->
Path = <<"/_api/view/", ViewName/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
%
% GET /_api/view
%
% ID
%
%
%
% 200
getViewList(PoolNameOrSocket) ->
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, <<"/_api/view">>, [], undefined).
%
% GET /_api/view/{view-name}/properties
%
% view-name
% view-name标识的View的定义
%
%
% 400HTTP 400
% 404HTTP 404
getViewProps(PoolNameOrSocket, ViewName) ->
Path = <<"/_api/view/", ViewName/binary, "/properties">>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgGet, Path, [], undefined).
% ArangoSearch视图的所有属性
% PUT /_api/view/{view-name}/properties#ArangoSearch
%
% view-name
% JSON对象是必需的
% links ArangoSearch查看链接属性
% cleanupIntervalStepArangoSearch数据目录中的未使用文件之间至少等待这么多次提交2使0commit + consolidate/
% View内部数据结构的新状态//
% commitIntervalMsecView数据存储更改和使文档对查询可见之前1000使0/使/
% ArangoSearch视图遵循ArangoDB中的所有数据将通过相应的查询表达式进行匹配ArangoSearch View//ArangoDB事务中调用的查询反映ArangoDB事务仍将继续返回可重复读取状态
% integrationIntervalMsec'consolidationPolicy'View数据存储与可能释放文件系统上的空间之间至少等待这么多毫秒10000使0使
% ArangoSearch视图遵循 consolidationIntervalMsec consolidationPolicy
% consolidationPolicy{}
% ArangoDB交易进行的插入文档的一个或多个ArangoSearch内部段被创建使使
%
% type
% "tier"使segments*minScore属性可用
% "bytes_accum" {threshold} > (segment_bytes + sum_of_merge_candidate_segment_bytes) / all_segment_bytes {threshold}使threshold属性可用
% threshold[0.0, 1.0]
% segmentsBytesFloor2097152
% segmentsBytesMax5368709120
% segmentsMax10
% segmentsMin1
% minScore0
%
%
% id
% name
% type
% ArangoSearch View实施特定的属性
%
% 400HTTP 400
% 404HTTP 404
changeViewAllProps(PoolNameOrSocket, ViewName, MapData) ->
Path = <<"/_api/view/", ViewName/binary, "/properties">>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], BodyStr).
% ArangoSearch视图的属性
% PATCH /_api/view/{view-name}/properties#ArangoSearch
%
% view-name
% JSON对象是必需的
% links ArangoSearch查看链接属性
% cleanupIntervalStepArangoSearch数据目录中的未使用文件之间至少等待这么多次提交2使0commit + consolidate/
% View内部数据结构的新状态//
% commitIntervalMsecView数据存储更改和使文档对查询可见之前1000使0/使/
% ArangoSearch视图遵循ArangoDB中的所有数据将通过相应的查询表达式进行匹配ArangoSearch View//ArangoDB事务中调用的查询反映ArangoDB事务仍将继续返回可重复读取状态
% integrationIntervalMsec'consolidationPolicy'View数据存储与可能释放文件系统上的空间之间至少等待这么多毫秒10000使0使
% ArangoSearch视图遵循 consolidationIntervalMsec consolidationPolicy
% consolidationPolicy{}
% ArangoDB交易进行的插入文档的一个或多个ArangoSearch内部段被创建使使
%
% type
% "tier"使segments*minScore属性可用
% "bytes_accum" {threshold} > (segment_bytes + sum_of_merge_candidate_segment_bytes) / all_segment_bytes {threshold}使threshold属性可用
% threshold[0.0, 1.0]
% segmentsBytesFloor2097152
% segmentsBytesMax5368709120
% segmentsMax10
% segmentsMin1
% minScore0
%
%
% id
% name
% type
% ArangoSearch View实施特定的属性
%
% 400HTTP 400
% 404HTTP 404
changeViewPartProps(PoolNameOrSocket, ViewName, MapData) ->
Path = <<"/_api/view/", ViewName/binary, "/properties">>,
BodyStr = jiffy:encode(MapData),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPatch, Path, [], BodyStr).
%
% PUT /_api/view/{view-name}/rename
%
% view-nameView的名称
%
%
%
% id
% name
% type
%
%
% 400HTTP 400
% 404HTTP 404
renameView(PoolNameOrSocket, ViewName, NewViewName) ->
Path = <<"/_api/view/", ViewName/binary, "/rename">>,
NameStr = jiffy:encode(NewViewName),
agHttpCli:callAgency(PoolNameOrSocket, ?AgPut, Path, [], <<"{\"name\":", NameStr/binary, "}">>).
%
% DELETE /_api/view/{view-name}
%
% view-name
% view-name标识的View
% View
%
% id
%
% 400HTTP 400
% 404HTTP 404
delView(PoolNameOrSocket, ViewName) ->
Path = <<"/_api/view/", ViewName/binary>>,
agHttpCli:callAgency(PoolNameOrSocket, ?AgDelete, Path, [], undefined).

+ 78
- 0
src/agHttpCli/agAgencyPoolMgrExm.erl View File

@ -0,0 +1,78 @@
-module(agAgencyPoolMgrExm).
-compile(inline).
-compile({inline_size, 128}).
-export([
start_link/3
, init_it/3
, system_code_change/4
, system_continue/3
, system_get_state/1
, system_terminate/4
]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% genExm start %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec start_link(module(), term(), [proc_lib:spawn_option()]) -> {ok, pid()}.
start_link(Name, Args, SpawnOpts) ->
proc_lib:start_link(?MODULE, init_it, [Name, self(), Args], infinity, SpawnOpts).
init_it(Name, Parent, Args) ->
case safeRegister(Name) of
true ->
process_flag(trap_exit, true),
moduleInit(Parent, Args);
{false, Pid} ->
proc_lib:init_ack(Parent, {error, {alreadyStarted, Pid}})
end.
-spec system_code_change(term(), module(), undefined | term(), term()) -> {ok, term()}.
system_code_change(State, _Module, _OldVsn, _Extra) ->
{ok, State}.
-spec system_continue(pid(), [], {module(), atom(), pid(), term()}) -> ok.
system_continue(_Parent, _Debug, {Parent, State}) ->
loop(Parent, State).
-spec system_get_state(term()) -> {ok, term()}.
system_get_state(State) ->
{ok, State}.
-spec system_terminate(term(), pid(), [], term()) -> none().
system_terminate(Reason, _Parent, _Debug, _State) ->
exit(Reason).
safeRegister(Name) ->
try register(Name, self()) of
true -> true
catch
_:_ -> {false, whereis(Name)}
end.
moduleInit(Parent, Args) ->
case agAgencyPoolMgrIns:init(Args) of
{ok, State} ->
proc_lib:init_ack(Parent, {ok, self()}),
loop(Parent, State);
{stop, Reason} ->
proc_lib:init_ack(Parent, {error, Reason}),
exit(Reason)
end.
loop(Parent, State) ->
receive
{system, From, Request} ->
sys:handle_system_msg(Request, From, Parent, ?MODULE, [], {Parent, State});
{'EXIT', Parent, Reason} ->
terminate(Reason, State);
Msg ->
{ok, NewState} = agAgencyPoolMgrIns:handleMsg(Msg, State),
loop(Parent, NewState)
end.
terminate(Reason, State) ->
agAgencyPoolMgrIns:terminate(Reason, State),
exit(Reason).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% genExm end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

+ 188
- 0
src/agHttpCli/agAgencyPoolMgrIns.erl View File

@ -0,0 +1,188 @@
-module(agAgencyPoolMgrIns).
-include("agHttpCli.hrl").
-compile(inline).
-compile({inline_size, 128}).
-export([
startPool/2
, startPool/3
, stopPool/1
, getOneAgency/1
%% genExm API
, init/1
, handleMsg/2
, terminate/2
]).
%% k-v beam cache
-define(ETS_AG_Pool, ets_ag_Pool).
-define(ETS_AG_Agency, ets_ag_Agency).
%% TODO maybe spawn_opt params need optimize
-define(agencySpec(ServerMod, ServerName, Args),
#{
id => ServerName
, start => {ServerMod, start_link, [ServerName, Args, [{min_heap_size, 10240}, {min_bin_vheap_size, 524288}, {fullsweep_after, 2048}]]}
, restart => transient
, shutdown => infinity
, type => worker
, modules => [ServerMod]
}).
-spec init(Args :: term()) -> ok.
init(_Args) ->
ets:new(?ETS_AG_Pool, [named_table, set, protected]),
ets:new(?ETS_AG_Agency, [named_table, set, protected]),
agKvsToBeam:load(?agBeamPool, []),
agKvsToBeam:load(?agBeamAgency, []),
{ok, undefined}.
handleMsg({'$gen_call', From, {miStartPool, PoolName, DbCfgs, AgencyOpts}}, State) ->
dealStart(PoolName, DbCfgs, AgencyOpts),
gen_server:reply(From, ok),
{ok, State};
handleMsg({'$gen_call', From, {miStopPool, Name}}, State) ->
delaStop(Name),
gen_server:reply(From, ok),
{ok, State};
handleMsg(_Msg, State) ->
?WARN(?MODULE, "receive unexpected msg: ~p", [_Msg]),
{ok, State}.
terminate(_Reason, _State) ->
ets:delete(?ETS_AG_Pool),
ets:delete(?ETS_AG_Agency),
agKvsToBeam:load(?agBeamPool, []),
agKvsToBeam:load(?agBeamAgency, []),
ok.
-spec startPool(poolName(), dbCfgs()) -> ok | {error, poolNameUsed}.
startPool(PoolName, DbCfgs) ->
startPool(PoolName, DbCfgs, []).
-spec startPool(poolName(), dbCfgs(), agencyCfgs()) -> ok | {error, poolNameUsed}.
startPool(PoolName, DbCfgs, AgencyCfgs) ->
case ?agBeamPool:getv(PoolName) of
undefined ->
gen_server:call(?agAgencyPoolMgr, {miStartPool, PoolName, DbCfgs, AgencyCfgs});
_ ->
{error, poolNameUsed}
end.
-spec stopPool(poolName()) -> ok | {error, poolNotStarted}.
stopPool(PoolName) ->
case ?agBeamPool:getv(PoolName) of
undefined ->
{error, poolNotStarted};
_ ->
gen_server:call(?agAgencyPoolMgr, {miStopPool, PoolName}, infinity)
end.
dealStart(PoolName, DbCfgs, AgencyCfgs) ->
#dbOpts{poolSize = PoolSize, protocol = Protocol} = DbOpts = agMiscUtils:dbOpts(DbCfgs),
AgencyOpts = agMiscUtils:agencyOpts(AgencyCfgs),
cacheAddPool(PoolName, DbOpts),
startChildren(PoolName, Protocol, PoolSize, AgencyOpts),
cacheAddAgency(PoolName, PoolSize),
case persistent_term:get(PoolName, undefined) of
undefined ->
IndexRef = atomics:new(1, [{signed, false}]),
persistent_term:put(PoolName, IndexRef);
_ ->
ignore
end,
ok.
delaStop(PoolName) ->
case ?agBeamPool:getv(PoolName) of
undefined ->
{error, poolNotStarted};
#dbOpts{poolSize = PoolSize} ->
stopChildren(agencyNames(PoolName, PoolSize)),
cacheDelPool(PoolName),
cacheDelAgency(PoolName),
ok
end.
agencyName(PoolName, Index) ->
list_to_atom(atom_to_list(PoolName) ++ "_" ++ integer_to_list(Index)).
agencyNames(PoolName, PoolSize) ->
[agencyName(PoolName, N) || N <- lists:seq(1, PoolSize)].
agencyMod(tcp) ->
agTcpAgencyExm;
agencyMod(ssl) ->
agSslAgencyExm;
agencyMod(_) ->
agTcpAgencyExm.
-spec startChildren(atom(), protocol(), poolSize(), agencyOpts()) -> ok.
startChildren(PoolName, Protocol, PoolSize, AgencyOpts) ->
AgencyMod = agencyMod(Protocol),
AgencyNames = agencyNames(PoolName, PoolSize),
AgencySpecs = [?agencySpec(AgencyMod, AgencyName, {PoolName, AgencyName, AgencyOpts}) || AgencyName <- AgencyNames],
[supervisor:start_child(agAgencyPool_sup, AgencySpec) || AgencySpec <- AgencySpecs],
ok.
stopChildren([AgencyName | T]) ->
case supervisor:terminate_child(agAgencyPool_sup, AgencyName) of
ok ->
ok;
{error, TerReason} ->
?WARN(agAgencyPoolMgrIns, ":terminate_child: ~p error reason: ~p ~n", [AgencyName, TerReason])
end,
case supervisor:delete_child(agAgencyPool_sup, AgencyName) of
ok ->
ok;
{error, DelReason} ->
?WARN(agAgencyPoolMgrIns, ":delete_child: ~p error reason: ~p ~n", [AgencyName, DelReason])
end,
stopChildren(T);
stopChildren([]) ->
ok.
cacheAddPool(Key, Value) ->
ets:insert(?ETS_AG_Pool, {Key, Value}),
KVS = ets:tab2list(?ETS_AG_Pool),
agKvsToBeam:load(?agBeamPool, KVS),
ok.
cacheDelPool(Key) ->
ets:delete(?ETS_AG_Pool, Key),
KVS = ets:tab2list(?ETS_AG_Pool),
agKvsToBeam:load(?agBeamPool, KVS),
ok.
cacheAddAgency(PoolName, PoolSize) ->
NameList = [{{PoolName, N}, agencyName(PoolName, N)} || N <- lists:seq(1, PoolSize)],
ets:insert(?ETS_AG_Agency, NameList),
KVS = ets:tab2list(?ETS_AG_Agency),
agKvsToBeam:load(?agBeamAgency, KVS),
ok.
cacheDelAgency(PoolName) ->
ets:match_delete(?ETS_AG_Agency, {{PoolName, '_'}, '_'}),
KVS = ets:tab2list(?ETS_AG_Agency),
agKvsToBeam:load(?agBeamAgency, KVS),
ok.
-spec getOneAgency(atom()) -> atom() | {error, term()}.
getOneAgency(PoolName) ->
case ?agBeamPool:getv(PoolName) of
undefined ->
{error, pool_not_found};
#dbOpts{poolSize = PoolSize} ->
Ref = persistent_term:get(PoolName),
AgencyIdx = atomics:add_get(Ref, 1, 1),
case AgencyIdx >= PoolSize of
true ->
atomics:put(Ref, 1, 0),
?agBeamAgency:getv({PoolName, AgencyIdx});
_ ->
?agBeamAgency:getv({PoolName, AgencyIdx})
end
end.

+ 15
- 0
src/agHttpCli/agAgencyPool_sup.erl View File

@ -0,0 +1,15 @@
-module(agAgencyPool_sup).
-behaviour(supervisor).
-export([
start_link/0
, init/1
]).
-spec start_link() -> {ok, pid()}.
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
{ok, {{one_for_one, 100, 3600}, []}}.

+ 96
- 0
src/agHttpCli/agAgencyUtils.erl View File

@ -0,0 +1,96 @@
-module(agAgencyUtils).
-include("agHttpCli.hrl").
-compile(inline).
-compile({inline_size, 128}).
-export([
cancelTimer/1
, dealClose/3
, reconnectTimer/2
, agencyReply/2
, agencyReply/4
, initReconnectState/3
, resetReconnectState/1
, updateReconnectState/1
]).
-spec dealClose(srvState(), cliState(), term()) -> {ok, srvState(), cliState()}.
dealClose(SrvState, #cliState{requestsIns = RequestsIns, requestsOuts = RequestsOuts, curInfo = CurInfo} = ClientState, Reply) ->
agencyReply(CurInfo, Reply),
agencyReplyAll(RequestsOuts, RequestsIns, Reply),
reconnectTimer(SrvState, ClientState#cliState{requestsIns = [], requestsOuts = [], backlogNum = 0, status = leisure, curInfo = undefined, recvState = undefined}).
-spec reconnectTimer(srvState(), cliState()) -> {ok, srvState(), cliState()}.
reconnectTimer(#srvState{reconnectState = undefined} = SrvState, CliState) ->
{ok, {SrvState#srvState{socket = undefined}, CliState}};
reconnectTimer(#srvState{reconnectState = ReconnectState} = SrvState, CliState) ->
#reconnectState{current = Current} = MewReconnectState = agAgencyUtils:updateReconnectState(ReconnectState),
TimerRef = erlang:send_after(Current, self(), ?miDoNetConnect),
{ok, SrvState#srvState{reconnectState = MewReconnectState, socket = undefined, timerRef = TimerRef}, CliState}.
-spec agencyReply(term(), term()) -> ok.
agencyReply({undefined, _RequestId, TimerRef}, _Reply) ->
agAgencyUtils:cancelTimer(TimerRef);
agencyReply({PidForm, RequestId, TimerRef}, Reply) ->
agAgencyUtils:cancelTimer(TimerRef),
catch PidForm ! #miRequestRet{requestId = RequestId, reply = Reply},
ok;
agencyReply(undefined, _RequestRet) ->
ok.
-spec agencyReply(undefined | pid(), requestId(), undefined | reference(), term()) -> ok.
agencyReply(undefined, _RequestId, TimerRef, _Reply) ->
agAgencyUtils:cancelTimer(TimerRef),
ok;
agencyReply(FormPid, RequestId, TimerRef, Reply) ->
agAgencyUtils:cancelTimer(TimerRef),
catch FormPid ! #miRequestRet{requestId = RequestId, reply = Reply},
ok.
-spec agencyReplyAll(list(), list(), term()) -> ok.
agencyReplyAll(RequestsOuts, RequestsIns, Reply) ->
[agencyReply(FormPid, RequestId, undefined, Reply) || #miRequest{requestId = RequestId, fromPid = FormPid} <- RequestsOuts],
[agencyReply(FormPid, RequestId, undefined, Reply) || #miRequest{requestId = RequestId, fromPid = FormPid} <- lists:reverse(RequestsIns)],
ok.
-spec cancelTimer(undefined | reference()) -> ok.
cancelTimer(undefined) -> ok;
cancelTimer(TimerRef) ->
case erlang:cancel_timer(TimerRef) of
false ->
receive
{timeout, TimerRef, _Msg} ->
%% discard the timeout msg
ok
after 0 ->
ok
end;
_ ->
%% Timer already run
ok
end.
-spec initReconnectState(boolean(), pos_integer(), pos_integer()) -> reconnectState() | undefined.
initReconnectState(IsReconnect, Min, Max) ->
case IsReconnect of
true ->
#reconnectState{min = Min, max = Max, current = Min};
false ->
undefined
end.
-spec resetReconnectState(undefined | reconnectState()) -> reconnectState() | undefined.
resetReconnectState(#reconnectState{min = Min} = ReconnectState) ->
ReconnectState#reconnectState{current = Min}.
-spec updateReconnectState(reconnectState()) -> reconnectState().
updateReconnectState(#reconnectState{current = Current, max = Max} = ReconnectState) ->
NewCurrent = Current + Current,
ReconnectState#reconnectState{current = minCur(NewCurrent, Max)}.
minCur(A, B) when B >= A ->
A;
minCur(_, B) ->
B.

+ 278
- 0
src/agHttpCli/agHttpCli.erl View File

@ -0,0 +1,278 @@
-module(agHttpCli).
-include("agHttpCli.hrl").
-include("erlArango.hrl").
-compile(inline).
-compile({inline_size, 128}).
-export([
%% Common Request API
callAgency/5
, callAgency/6
, callAgency/7
, castAgency/5
, castAgency/6
, castAgency/7
, castAgency/8
, receiveRequestRet/2
%% Pools API
, startPool/2
, startPool/3
, stopPool/1
%% Single Process DbAPI
, connectDb/1
, disConnectDb/1
, getCurDbInfo/1
, useDatabase/2
]).
-spec callAgency(poolNameOrSocket(), method(), path(), headers(), body()) -> term() | {error, term()}.
callAgency(PoolNameOrSocket, Method, Path, Headers, Body) ->
callAgency(PoolNameOrSocket, Method, Path, Headers, Body, false, ?DEFAULT_TIMEOUT).
-spec callAgency(poolNameOrSocket(), method(), path(), headers(), body(), boolean()) -> term() | {error, atom()}.
callAgency(PoolNameOrSocket, Method, Path, Headers, Body, IsSystem) ->
callAgency(PoolNameOrSocket, Method, Path, Headers, Body, IsSystem, ?DEFAULT_TIMEOUT).
-spec callAgency(poolNameOrSocket(), method(), path(), headers(), body(), boolean(), timeout()) -> term() | {error, atom()}.
callAgency(PoolNameOrSocket, Method, Path, Headers, Body, IsSystem, Timeout) ->
case castAgency(PoolNameOrSocket, Method, Path, Headers, Body, self(), IsSystem, Timeout) of
{waitRRT, RequestId, MonitorRef} ->
receiveRequestRet(RequestId, MonitorRef);
{error, _Reason} = Err ->
Err;
Ret ->
Ret
end.
-spec castAgency(poolNameOrSocket(), method(), path(), headers(), body()) -> {ok, requestId()} | {error, atom()}.
castAgency(PoolNameOrSocket, Method, Path, Headers, Body) ->
castAgency(PoolNameOrSocket, Method, Path, Headers, Body, self(), false, ?DEFAULT_TIMEOUT).
-spec castAgency(poolNameOrSocket(), method(), path(), headers(), body(), boolean()) -> {ok, requestId()} | {error, atom()}.
castAgency(PoolNameOrSocket, Method, Path, Headers, Body, IsSystem) ->
castAgency(PoolNameOrSocket, Method, Path, Headers, Body, self(), IsSystem, ?DEFAULT_TIMEOUT).
-spec castAgency(poolNameOrSocket(), method(), path(), headers(), body(), boolean(), timeout()) -> {ok, requestId()} | {error, atom()}.
castAgency(PoolNameOrSocket, Method, Path, Headers, Body, IsSystem, Timeout) ->
castAgency(PoolNameOrSocket, Method, Path, Headers, Body, self(), IsSystem, Timeout).
-spec castAgency(poolNameOrSocket(), method(), path(), headers(), body(), pid(), boolean(), timeout()) -> {ok, requestId()} | {error, atom()}.
castAgency(PoolNameOrSocket, Method, Path, Headers, Body, Pid, IsSystem, Timeout) ->
OverTime =
case Timeout of
infinity -> infinity;
_ ->
erlang:monotonic_time(millisecond) + Timeout
end,
case erlang:is_atom(PoolNameOrSocket) of
true ->
case agAgencyPoolMgrIns:getOneAgency(PoolNameOrSocket) of
{error, pool_not_found} = Err ->
Err;
undefined ->
{error, undefined_server};
AgencyName ->
MonitorRef = erlang:monitor(process, AgencyName),
RequestId = {AgencyName, MonitorRef},
catch AgencyName ! #miRequest{method = Method, path = Path, headers = Headers, body = Body, requestId = RequestId, fromPid = Pid, overTime = OverTime, isSystem = IsSystem},
{waitRRT, RequestId, MonitorRef}
end;
_ ->
case getCurDbInfo(PoolNameOrSocket) of
{DbName, UserPassWord, Host, Protocol} ->
Request = agHttpProtocol:request(IsSystem, Body, Method, Host, DbName, Path, [UserPassWord | Headers]),
case Protocol of
tcp ->
case gen_tcp:send(PoolNameOrSocket, Request) of
ok ->
receiveTcpData(undefined, PoolNameOrSocket, binary:compile_pattern(<<"\r\n">>), binary:compile_pattern(<<"\r\n\r\n">>), Method == ?AgHead);
{error, Reason} = Err ->
?WARN(castAgency, ":gen_tcp send error: ~p ~n", [Reason]),
disConnectDb(PoolNameOrSocket),
Err
end;
ssl ->
case ssl:send(PoolNameOrSocket, Request) of
ok ->
receiveSslData(undefined, PoolNameOrSocket, binary:compile_pattern(<<"\r\n">>), binary:compile_pattern(<<"\r\n\r\n">>), Method == ?AgHead);
{error, Reason} = Err ->
?WARN(castAgency, ":ssl send error: ~p ~n", [Reason]),
disConnectDb(PoolNameOrSocket),
Err
end
end;
_ ->
{error, dbinfoNotFound}
end
end.
-spec receiveRequestRet(requestId(), reference()) -> {StatusCode :: non_neg_integer(), Body :: binary(), Headers :: binary()} | {error, term()}.
receiveRequestRet(RequestId, MonitorRef) ->
receive
#miRequestRet{requestId = RequestId, reply = Reply} ->
erlang:demonitor(MonitorRef),
case Reply of
{_StatusCode, Body, _Headers} ->
case Body of
<<>> ->
erlang:setelement(2, Reply, #{});
_ ->
erlang:setelement(2, Reply, jiffy:decode(Body, [return_maps, copy_strings]))
end;
_ ->
Reply
end;
{'DOWN', MonitorRef, process, _Pid, Reason} ->
{error, {agencyDown, Reason}}
end.
-spec receiveTcpData(recvState() | undefined, socket(), binary:cp(), binary:cp(), boolean()) -> {ok, term(), term()} | {error, term()}.
receiveTcpData(RecvState, Socket, Rn, RnRn, IsHeadMethod) ->
receive
{tcp, Socket, Data} ->
try agHttpProtocol:response(RecvState, Rn, RnRn, Data, IsHeadMethod) of
{done, #recvState{statusCode = StatusCode, headers = Headers, body = Body}} ->
case Body of
<<>> ->
{ok, #{}, StatusCode, Headers};
_ ->
{ok, jiffy:decode(Body, [return_maps, copy_strings]), StatusCode, Headers}
end;
{ok, NewRecvState} ->
receiveTcpData(NewRecvState, Socket, Rn, RnRn, IsHeadMethod);
{error, Reason} ->
?WARN(receiveTcpData, "handle tcp data error: ~p ~n", [Reason]),
disConnectDb(Socket),
{error, {tcpDataError, Reason}}
catch
E:R:S ->
?WARN(receiveTcpData, "handle tcp data crash: ~p:~p~n~p ~n ", [E, R, S]),
disConnectDb(Socket),
{error, handledataError}
end;
{tcp_closed, Socket} ->
disConnectDb(Socket),
{error, tcp_closed};
{tcp_error, Socket, Reason} ->
disConnectDb(Socket),
{error, {tcp_error, Reason}}
end.
-spec receiveSslData(recvState() | undefined, socket(), binary:cp(), binary:cp(), boolean()) -> {ok, term(), term()} | {error, term()}.
receiveSslData(RecvState, Socket, Rn, RnRn, IsHeadMethod) ->
receive
{ssl, Socket, Data} ->
try agHttpProtocol:response(RecvState, Rn, RnRn, Data, IsHeadMethod) of
{done, #recvState{statusCode = StatusCode, headers = Headers, body = Body}} ->
case Body of
<<>> ->
{ok, #{}, StatusCode, Headers};
_ ->
{ok, jiffy:decode(Body, [return_maps, copy_strings]), StatusCode, Headers}
end;
{ok, NewRecvState} ->
receiveSslData(NewRecvState, Socket, Rn, RnRn, IsHeadMethod);
{error, Reason} ->
?WARN(receiveSslData, "handle tcp data error: ~p ~n", [Reason]),
disConnectDb(Socket),
{error, {sslDataError, Reason}}
catch
E:R:S ->
?WARN(receiveSslData, "handle tcp data crash: ~p:~p~n~p ~n ", [E, R, S]),
disConnectDb(Socket),
{error, handledataError}
end;
{ssl_closed, Socket} ->
disConnectDb(Socket),
{error, ssl_closed};
{ssl_error, Socket, Reason} ->
disConnectDb(Socket),
{error, {ssl_error, Reason}}
end.
-spec startPool(poolName(), dbCfgs()) -> ok | {error, poolNameUsed}.
startPool(PoolName, DbCfgs) ->
agAgencyPoolMgrIns:startPool(PoolName, DbCfgs, []).
-spec startPool(poolName(), dbCfgs(), agencyCfgs()) -> ok | {error, poolNameUsed}.
startPool(PoolName, DbCfgs, AgencyCfgs) ->
agAgencyPoolMgrIns:startPool(PoolName, DbCfgs, AgencyCfgs).
-spec stopPool(poolName()) -> ok | {error, poolNotStarted}.
stopPool(PoolName) ->
agAgencyPoolMgrIns:stopPool(PoolName).
-spec connectDb(dbCfgs()) -> {ok, socket()} | {error, term()}.
connectDb(DbCfgs) ->
#dbOpts{
host = Host,
port = Port,
hostname = HostName,
dbName = DbName,
protocol = Protocol,
userPassword = UserPassword,
socketOpts = SocketOpts
} = agMiscUtils:dbOpts(DbCfgs),
case inet:getaddrs(HostName, inet) of
{ok, IPList} ->
Ip = agMiscUtils:randomElement(IPList),
case Protocol of
tcp ->
case gen_tcp:connect(Ip, Port, SocketOpts, ?DEFAULT_CONNECT_TIMEOUT) of
{ok, Socket} ->
setCurDbInfo(Socket, DbName, UserPassword, Host, Protocol),
{ok, Socket};
{error, Reason} = Err ->
?WARN(connectDb, "connect error: ~p~n", [Reason]),
Err
end;
ssl ->
case ssl:connect(Ip, Port, SocketOpts, ?DEFAULT_CONNECT_TIMEOUT) of
{ok, Socket} ->
setCurDbInfo(Socket, DbName, UserPassword, Host, Protocol),
{ok, Socket};
{error, Reason} = Err ->
?WARN(connectDb, "connect error: ~p~n", [Reason]),
Err
end
end;
{error, Reason} = Err ->
?WARN(connectDb, "getaddrs error: ~p~n", [Reason]),
Err
end.
-spec disConnectDb(socket()) -> ok | {error, term()}.
disConnectDb(Socket) ->
case erlang:erase({'$agDbInfo', Socket}) of
undefined ->
ignore;
{_DbName, _UserPassword, _Host, Protocol} ->
case Protocol of
tcp ->
gen_tcp:close(Socket);
ssl ->
ssl:close(Socket)
end
end.
-spec setCurDbInfo(socket(), binary(), tuple(), host(), protocol()) -> term().
setCurDbInfo(Socket, DbName, UserPassword, Host, Protocol) ->
erlang:put({'$agDbInfo', Socket}, {DbName, UserPassword, Host, Protocol}).
-spec getCurDbInfo(socket()) -> term().
getCurDbInfo(Socket) ->
erlang:get({'$agDbInfo', Socket}).
-spec useDatabase(socket(), binary()) -> ok.
useDatabase(Socket, NewDbName) ->
case erlang:get({'$agDbInfo', Socket}) of
undefined ->
ignore;
{_DbName, UserPassword, Host, Protocol} ->
erlang:put({'$agDbInfo', Socket}, {<<"/_db/", NewDbName/binary>>, UserPassword, Host, Protocol})
end,
ok.

+ 281
- 0
src/agHttpCli/agHttpProtocol.erl View File

@ -0,0 +1,281 @@
-module(agHttpProtocol).
-include("agHttpCli.hrl").
-compile(inline).
-compile({inline_size, 128}).
-export([
headers/1
, request/7
, response/2
, response/5
]).
%% <<"Content-Type: application/json; charset=utf-8">>,
-spec request(boolean(), body(), method(), host(), binary(), path(), headers()) -> iolist().
request(true, undefined, Method, Host, _DbName, Path, Headers) ->
[
Method, <<"/_db/_system">>, Path, <<" HTTP/1.1\r\nHost: ">>, Host,
<<"\r\nContent-Type: application/json; charset=utf-8\r\nContent-Length: 0\r\n">>,
spellHeaders(Headers), <<"\r\n">>
];
request(false, undefined, Method, Host, DbName, Path, Headers) ->
[
Method, DbName, Path, <<" HTTP/1.1\r\nHost: ">>, Host,
<<"\r\nContent-Type: application/json; charset=utf-8\r\nContent-Length: 0\r\n">>,
spellHeaders(Headers), <<"\r\n">>
];
request(false, Body, Method, Host, DbName, Path, Headers) ->
ContentLength = integer_to_binary(iolist_size(Body)),
NewHeaders = [{<<"Content-Length">>, ContentLength} | Headers],
[
Method, DbName, Path, <<" HTTP/1.1\r\nHost: ">>, Host,
<<"\r\nContent-Type: application/json; charset=utf-8\r\n">>,
spellHeaders(NewHeaders), <<"\r\n">>, Body
];
request(true, Body, Method, Host, _DbName, Path, Headers) ->
ContentLength = integer_to_binary(iolist_size(Body)),
NewHeaders = [{<<"Content-Length">>, ContentLength} | Headers],
[
Method, <<"/_db/_system">>, Path, <<" HTTP/1.1\r\nHost: ">>, Host,
<<"\r\nContent-Type: application/json; charset=utf-8\r\n">>,
spellHeaders(NewHeaders), <<"\r\n">>, Body
].
-spec response(binary(), boolean()) -> {ok, recvState(), binary()} | error().
response(Data, IsHeadMethod) ->
response(undefined, binary:compile_pattern(<<"\r\n">>), binary:compile_pattern(<<"\r\n\r\n">>), Data, IsHeadMethod).
-spec response(undefined | recvState(), binary:cp(), binary:cp(), binary(), boolean()) -> {ok, recvState()} | error().
response(undefined, Rn, RnRn, Data, IsHeadMethod) ->
case parseStatusLine(Data, Rn) of
{StatusCode, Rest} ->
case splitHeaders(Rest, Rn, RnRn) of
{undefined, Headers, Body} ->
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = undefined, body = Body}};
{0, Headers, Rest} ->
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = 0, body = Rest}};
{chunked, Headers, Body} ->
case IsHeadMethod orelse StatusCode == 204 orelse StatusCode == 304 orelse (StatusCode < 200 andalso StatusCode >= 100) of
true ->
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = 0, body = Body}};
_ ->
RecvState = #recvState{stage = body, contentLength = chunked, statusCode = StatusCode, headers = Headers},
response(RecvState, Rn, RnRn, Body, IsHeadMethod)
end;
{ContentLength, Headers, Body} ->
BodySize = erlang:size(Body),
if
BodySize == ContentLength ->
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = ContentLength, body = Body}};
BodySize > ContentLength ->
?WARN(agTcpAgencyIns, "11 contentLength get to long data why? more: ~p ~n", [BodySize - ContentLength]),
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = ContentLength, body = Body}};
true ->
case IsHeadMethod orelse StatusCode == 204 orelse StatusCode == 304 orelse (StatusCode < 200 andalso StatusCode >= 100) of
true ->
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = ContentLength, body = Body}};
_ ->
{ok, #recvState{stage = body, statusCode = StatusCode, headers = Headers, contentLength = ContentLength, body = Body}}
end
end;
not_enough_data ->
{ok, #recvState{stage = header, body = Data}}
end;
not_enough_data ->
{ok, #recvState{stage = header, body = Data}};
{error, Reason} ->
{error, Reason}
end;
response(#recvState{stage = body, contentLength = chunked, body = Body, buffer = Buffer} = RecvState, Rn, _RnRn, Data, _IsHeadMethod) ->
NewBuffer = <<Buffer/binary, Data/binary>>,
case parseChunks(NewBuffer, Rn, []) of
{ok, AddBody, _Rest} ->
LastBody = <<Body/binary, AddBody/binary>>,
{done, RecvState#recvState{stage = done, body = LastBody}};
{not_enough_data, AddBody, Rest} ->
NewBody = <<Body/binary, AddBody/binary>>,
{ok, RecvState#recvState{body = NewBody, buffer = Rest}};
{error, Reason} ->
{error, Reason}
end;
response(#recvState{stage = body, contentLength = ContentLength, body = Body} = RecvState, _Rn, _RnRn, Data, _IsHeadMethod) ->
CurData = <<Body/binary, Data/binary>>,
BodySize = erlang:size(CurData),
if
BodySize == ContentLength ->
{done, RecvState#recvState{stage = done, body = CurData}};
BodySize > ContentLength ->
?WARN(agTcpAgencyIns, "22 contentLength get to long data why? more: ~p ~n", [BodySize - ContentLength]),
{done, #recvState{stage = done, body = CurData}};
true ->
{ok, RecvState#recvState{body = CurData}}
end;
response(#recvState{stage = header, body = OldBody}, Rn, RnRn, Data, IsHeadMethod) ->
CurBody = <<OldBody/binary, Data/binary>>,
case parseStatusLine(CurBody, Rn) of
{StatusCode, Rest} ->
case splitHeaders(Rest, Rn, RnRn) of
{undefined, Headers, Body} ->
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = undefined, body = Body}};
{0, Headers, Body} ->
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = 0, body = Body}};
{chunked, Headers, Rest} ->
case IsHeadMethod orelse StatusCode == 204 orelse StatusCode == 304 orelse (StatusCode < 200 andalso StatusCode >= 100) of
true ->
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = 0, body = <<>>}};
_ ->
RecvState = #recvState{stage = body, contentLength = chunked, statusCode = StatusCode, headers = Headers},
response(RecvState, Rn, RnRn, Rest, IsHeadMethod)
end;
{ContentLength, Headers, Body} ->
BodySize = erlang:size(Body),
if
BodySize == ContentLength ->
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = ContentLength, body = Body}};
BodySize > ContentLength ->
?WARN(agTcpAgencyIns, "33 contentLength get to long data why? more: ~p ~n", [BodySize - ContentLength]),
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = ContentLength, body = Body}};
true ->
case IsHeadMethod orelse StatusCode == 204 orelse StatusCode == 304 orelse (StatusCode < 200 andalso StatusCode >= 100) of
true ->
{done, #recvState{stage = done, statusCode = StatusCode, headers = Headers, contentLength = ContentLength, body = Body}};
_ ->
{ok, #recvState{stage = body, statusCode = StatusCode, headers = Headers, contentLength = ContentLength, body = Body}}
end
end;
not_enough_data ->
{ok, #recvState{stage = header, body = CurBody}}
end;
not_enough_data ->
{ok, #recvState{stage = header, body = CurBody}};
{error, Reason} ->
{error, Reason}
end.
spellHeaders(Headers) ->
<<<<Key/binary, ": ", Value/binary, "\r\n">> || {Key, Value} <- Headers>>.
splitHeaders(Data, Rn, RnRn) ->
case binary:split(Data, RnRn) of
[Data] ->
not_enough_data;
[Headers, Body] ->
HeadersList = binary:split(Headers, Rn, [global]),
ContentLength = contentLength(HeadersList),
{ContentLength, Headers, Body}
end.
contentLength([]) ->
undefined;
contentLength([<<"Content-Length: ", Rest/binary>> | _T]) ->
binary_to_integer(Rest);
contentLength([<<"content-length: ", Rest/binary>> | _T]) ->
binary_to_integer(Rest);
contentLength([<<"Transfer-Encoding: chunked">> | _T]) ->
chunked;
contentLength([<<"transfer-encoding: chunked">> | _T]) ->
chunked;
contentLength([_ | T]) ->
contentLength(T).
parseStatusLine(Data, Rn) ->
case binary:split(Data, Rn) of
[Data] ->
not_enough_data;
[Line, Rest] ->
case parseStatusReason(Line) of
{ok, StatusCode} ->
{StatusCode, Rest};
{error, Reason} ->
{error, Reason}
end
end.
parseStatusReason(<<"HTTP/1.1 200 OK">>) ->
{ok, 200};
parseStatusReason(<<"HTTP/1.1 204 No Content">>) ->
{ok, 204};
parseStatusReason(<<"HTTP/1.1 301 Moved Permanently">>) ->
{ok, 301};
parseStatusReason(<<"HTTP/1.1 302 Found">>) ->
{ok, 302};
parseStatusReason(<<"HTTP/1.1 403 Forbidden">>) ->
{ok, 403};
parseStatusReason(<<"HTTP/1.1 404 Not Found">>) ->
{ok, 404};
parseStatusReason(<<"HTTP/1.1 500 Internal Server Error">>) ->
{ok, 500};
parseStatusReason(<<"HTTP/1.1 502 Bad Gateway">>) ->
{ok, 502};
parseStatusReason(<<"HTTP/1.1 ", N1, N2, N3, " ", _Reason/bits>>)
when $0 =< N1, N1 =< $9,
$0 =< N2, N2 =< $9,
$0 =< N3, N3 =< $9 ->
StatusCode = (N1 - $0) * 100 + (N2 - $0) * 10 + (N3 - $0),
{ok, StatusCode};
parseStatusReason(<<"HTTP/1.0 ", _/binary>>) ->
{error, unsupported_feature};
parseStatusReason(_) ->
{error, bad_request}.
parseChunks(Data, Rn, Acc) ->
case parseChunk(Data, Rn) of
done ->
{ok, iolist_to_binary(lists:reverse(Acc)), <<>>};
{ok, Body, Rest} ->
parseChunks(Rest, Rn, [Body | Acc]);
not_enough_data ->
{not_enough_data, iolist_to_binary(lists:reverse(Acc)), Data};
{error, Reason} ->
{error, Reason}
end.
parseChunk(Data, Rn) ->
case binary:split(Data, Rn) of
[Size, Rest] ->
case parseChunkSize(Size) of
undefined ->
{error, invalid_chunk_size};
0 ->
done;
HexSize ->
parseChunkBody(Rest, HexSize)
end;
[Data] ->
not_enough_data
end.
parseChunkBody(Data, Size) ->
case Data of
<<Body:Size/binary, "\r\n", Rest/binary>> ->
{ok, Body, Rest};
_ ->
not_enough_data
end.
parseChunkSize(Bin) ->
try
binary_to_integer(Bin, 16)
catch
error:badarg ->
undefined
end.
-spec headers(recvState()) -> {ok, headers()} | {error, invalidHeaders}.
headers(#recvState{headers = Headers}) ->
parseHeaders(Headers, []).
parseHeaders([], Acc) ->
{ok, lists:reverse(Acc)};
parseHeaders([Header | T], Acc) ->
case binary:split(Header, <<":">>) of
[Header] ->
{error, invalidHeaders};
[Key, <<>>] ->
parseHeaders(T, [{Key, undefined} | Acc]);
[Key, <<" ", Value/binary>>] ->
parseHeaders(T, [{Key, Value} | Acc])
end.

+ 45
- 0
src/agHttpCli/agKvsToBeam.erl View File

@ -0,0 +1,45 @@
-module(agKvsToBeam).
-compile(inline).
-compile({inline_size, 128}).
-export([
load/2
]).
%% attention: the map type data can not as the key
-type key() :: atom() | binary() | bitstring() | float() | integer() | list() | tuple().
-type value() :: atom() | binary() | bitstring() | float() | integer() | list() | tuple() | map().
-spec load(term(), [{key(), value()}]) -> ok.
load(Module, KVs) ->
Forms = forms(Module, KVs),
{ok, Module, Bin} = compile:forms(Forms),
code:soft_purge(Module),
{module, Module} = code:load_binary(Module, atom_to_list(Module), Bin),
ok.
forms(Module, KVs) ->
%% -module(Module).
Mod = erl_syntax:attribute(erl_syntax:atom(module), [erl_syntax:atom(Module)]),
%% -export([getv/0]).
ExportList = [erl_syntax:arity_qualifier(erl_syntax:atom(getv), erl_syntax:integer(1))],
Export = erl_syntax:attribute(erl_syntax:atom(export), [erl_syntax:list(ExportList)]),
%% getv(K) -> V
Function = erl_syntax:function(erl_syntax:atom(getv), lookup_clauses(KVs, [])),
[erl_syntax:revert(X) || X <- [Mod, Export, Function]].
lookup_clause(Key, Value) ->
Var = erl_syntax:abstract(Key),
Body = erl_syntax:abstract(Value),
erl_syntax:clause([Var], [], [Body]).
lookup_clause_anon() ->
Var = erl_syntax:variable("_"),
Body = erl_syntax:atom(undefined),
erl_syntax:clause([Var], [], [Body]).
lookup_clauses([], Acc) ->
lists:reverse(lists:flatten([lookup_clause_anon() | Acc]));
lookup_clauses([{Key, Value} | T], Acc) ->
lookup_clauses(T, [lookup_clause(Key, Value) | Acc]).

+ 127
- 0
src/agHttpCli/agMiscUtils.erl View File

@ -0,0 +1,127 @@
-module(agMiscUtils).
-include("agHttpCli.hrl").
-compile(inline).
-compile({inline_size, 128}).
-export([
parseUrl/1
, dbOpts/1
, agencyOpts/1
, warnMsg/3
, getListValue/3
, randomElement/1
, toBinary/1
, spellQueryPars/1
, getHeaderValue/2
, lookHeader/2
]).
-spec parseUrl(binary()) -> dbOpts() | {error, invalidUrl}.
parseUrl(<<"http://", Rest/binary>>) ->
parseUrl(tcp, Rest);
parseUrl(<<"https://", Rest/binary>>) ->
parseUrl(ssl, Rest);
parseUrl(_) ->
{error, invalidUrl}.
-spec parseUrl(protocol(), binary()) -> dbOpts().
parseUrl(Protocol, Rest) ->
{Host, _Path} =
case binary:split(Rest, <<"/">>, [trim]) of
[UrlHost] ->
{UrlHost, <<"/">>};
[UrlHost, UrlPath] ->
{UrlHost, <<"/", UrlPath/binary>>}
end,
{Hostname, Port} =
case binary:split(Host, <<":">>, [trim]) of
[Host] ->
case Protocol of
tcp ->
{Host, 80};
ssl ->
{Host, 443}
end;
[UrlHostname, UrlPort] ->
{UrlHostname, binary_to_integer(UrlPort)}
end,
#dbOpts{host = Host, port = Port, hostname = binary_to_list(Hostname), protocol = Protocol}.
-spec dbOpts(list()) -> dbOpts().
dbOpts(DbCfgs) ->
BaseUrl = ?GET_FROM_LIST(baseUrl, DbCfgs, ?DEFAULT_BASE_URL),
DbName = ?GET_FROM_LIST(dbName, DbCfgs, ?DEFAULT_DBNAME),
User = ?GET_FROM_LIST(user, DbCfgs, ?DEFAULT_USER),
Password = ?GET_FROM_LIST(password, DbCfgs, ?DEFAULT_PASSWORD),
PoolSize = ?GET_FROM_LIST(poolSize, DbCfgs, ?DEFAULT_POOL_SIZE),
SocketOpts = ?GET_FROM_LIST(socketOpts, DbCfgs, ?DEFAULT_SOCKET_OPTS),
DbOpts = agMiscUtils:parseUrl(BaseUrl),
UserPasswordBase64 = {<<"Authorization">>, <<"Basic ", (base64:encode(<<User/binary, ":", Password/binary>>))/binary>>},
DbOpts#dbOpts{dbName = <<"/_db/", DbName/binary>>, userPassword = UserPasswordBase64, poolSize = PoolSize, socketOpts = SocketOpts}.
-spec agencyOpts(list()) -> agencyOpts().
agencyOpts(AgencyCfgs) ->
IsReconnect = ?GET_FROM_LIST(reconnect, AgencyCfgs, ?DEFAULT_IS_RECONNECT),
BacklogSize = ?GET_FROM_LIST(backlogSize, AgencyCfgs, ?DEFAULT_BACKLOG_SIZE),
Min = ?GET_FROM_LIST(reconnectTimeMin, AgencyCfgs, ?DEFAULT_RECONNECT_MIN),
Max = ?GET_FROM_LIST(reconnectTimeMax, AgencyCfgs, ?DEFAULT_RECONNECT_MAX),
#agencyOpts{reconnect = IsReconnect, backlogSize = BacklogSize, reconnectTimeMin = Min, reconnectTimeMax = Max}.
-spec getListValue(term(), list(), term()) -> term().
getListValue(Key, List, Default) ->
case lists:keyfind(Key, 1, List) of
false ->
Default;
{Key, Value} ->
Value
end.
-spec warnMsg(term(), string(), [term()]) -> ok.
warnMsg(Tag, Format, Data) ->
error_logger:warning_msg("[~p] " ++ Format, [Tag | Data]).
-spec randomElement([term()]) -> term().
randomElement([X]) ->
X;
randomElement([_ | _] = List) ->
T = list_to_tuple(List),
element(rand:uniform(tuple_size(T)), T).
-spec toBinary(term()) -> binary().
toBinary(Value) when is_integer(Value) -> integer_to_binary(Value);
toBinary(Value) when is_list(Value) -> list_to_binary(Value);
toBinary(Value) when is_float(Value) -> float_to_binary(Value, [{decimals, 6}, compact]);
toBinary(Value) when is_atom(Value) -> atom_to_binary(Value, utf8);
toBinary(Value) when is_binary(Value) -> Value;
toBinary([Tuple | PropList] = Value) when is_list(PropList) and is_tuple(Tuple) ->
lists:map(fun({K, V}) -> {toBinary(K), toBinary(V)} end, Value);
toBinary(Value) -> term_to_binary(Value).
-spec spellQueryPars(list()) -> binary().
spellQueryPars([]) ->
<<>>;
spellQueryPars([{Key, Value}]) ->
<<"?", (toBinary(Key))/binary, "=", (toBinary(Value))/binary>>;
spellQueryPars([{Key, Value} | Tail]) ->
FirstBinary = <<"?", (toBinary(Key))/binary, "=", (toBinary(Value))/binary>>,
TailBinary = <<<<"&", (toBinary(OtherKey))/binary, "=", (toBinary(OtherValue))/binary>> || {OtherKey, OtherValue} <- Tail>>,
<<FirstBinary/binary, TailBinary/binary>>.
-spec getHeaderValue(binary(), binary()) -> binary().
getHeaderValue(Header, HeaderBin) ->
HeadersList = binary:split(HeaderBin, <<"\r\n">>, [global]),
lookHeader(Header, HeadersList).
-spec lookHeader(binary, list()) -> binary().
lookHeader(_Header, []) ->
undefined;
lookHeader(Header, [H | T]) ->
case binary:split(H, <<": ">>) of
[Header, Value] ->
Value;
_ ->
lookHeader(Header, T)
end.

+ 77
- 0
src/agHttpCli/agSslAgencyExm.erl View File

@ -0,0 +1,77 @@
-module(agSslAgencyExm).
-compile(inline).
-compile({inline_size, 128}).
-export([
start_link/3
, init_it/3
, system_code_change/4
, system_continue/3
, system_get_state/1
, system_terminate/4
]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% genExm start %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec start_link(module(), term(), [proc_lib:spawn_option()]) -> {ok, pid()}.
start_link(ServerName, Args, SpawnOpts) ->
proc_lib:start_link(?MODULE, init_it, [ServerName, self(), Args], infinity, SpawnOpts).
init_it(ServerName, Parent, Args) ->
case safeRegister(ServerName) of
true ->
process_flag(trap_exit, true),
moduleInit(Parent, Args);
{false, Pid} ->
proc_lib:init_ack(Parent, {error, {alreadyStarted, Pid}})
end.
-spec system_code_change(term(), module(), undefined | term(), term()) -> {ok, term()}.
system_code_change(MiscState, _Module, _OldVsn, _Extra) ->
{ok, MiscState}.
-spec system_continue(pid(), [], {module(), term(), term()}) -> ok.
system_continue(_Parent, _Debug, {Parent, SrvState, CliState}) ->
loop(Parent, SrvState, CliState).
-spec system_get_state(term()) -> {ok, term()}.
system_get_state({_Parent, SrvState, _CliState}) ->
{ok, SrvState}.
-spec system_terminate(term(), pid(), [], term()) -> none().
system_terminate(Reason, _Parent, _Debug, {_Parent, SrvState, CliState}) ->
terminate(Reason, SrvState, CliState).
safeRegister(ServerName) ->
try register(ServerName, self()) of
true -> true
catch
_:_ -> {false, whereis(ServerName)}
end.
moduleInit(Parent, Args) ->
case agSslAgencyIns:init(Args) of
{ok, SrvState, CliState} ->
proc_lib:init_ack(Parent, {ok, self()}),
loop(Parent, SrvState, CliState);
{stop, Reason} ->
proc_lib:init_ack(Parent, {error, Reason}),
exit(Reason)
end.
loop(Parent, SrvState, CliState) ->
receive
{system, From, Request} ->
sys:handle_system_msg(Request, From, Parent, ?MODULE, [], {Parent, SrvState, CliState});
{'EXIT', Parent, Reason} ->
terminate(Reason, SrvState, CliState);
Msg ->
{ok, NewSrvState, NewCliState} = agSslAgencyIns:handleMsg(Msg, SrvState, CliState),
loop(Parent, NewSrvState, NewCliState)
end.
terminate(Reason, SrvState, CliState) ->
agSslAgencyIns:terminate(Reason, SrvState, CliState),
exit(Reason).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% genExm end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

+ 401
- 0
src/agHttpCli/agSslAgencyIns.erl View File

@ -0,0 +1,401 @@
-module(agSslAgencyIns).
-include("agHttpCli.hrl").
-include("erlArango.hrl").
-compile(inline).
-compile({inline_size, 128}).
-export([
%% Inner Behavior API
init/1
, handleMsg/3
, terminate/3
]).
-spec init(term()) -> no_return().
init({PoolName, AgencyName, #agencyOpts{reconnect = Reconnect, backlogSize = BacklogSize, reconnectTimeMin = Min, reconnectTimeMax = Max}}) ->
ReconnectState = agAgencyUtils:initReconnectState(Reconnect, Min, Max),
self() ! ?miDoNetConnect,
{ok, #srvState{poolName = PoolName, serverName = AgencyName, rn = binary:compile_pattern(<<"\r\n">>), rnrn = binary:compile_pattern(<<"\r\n\r\n">>), reconnectState = ReconnectState}, #cliState{backlogSize = BacklogSize}}.
-spec handleMsg(term(), srvState(), cliState()) -> {ok, term(), term()}.
handleMsg(#miRequest{method = Method, path = Path, headers = Headers, body = Body, requestId = RequestId, fromPid = FromPid, overTime = OverTime, isSystem = IsSystem} = MiRequest,
#srvState{serverName = ServerName, host = Host, userPassWord = UserPassWord, dbName = DbName, socket = Socket} = SrvState,
#cliState{backlogNum = BacklogNum, backlogSize = BacklogSize, requestsIns = RequestsIns, status = Status} = CliState) ->
case Socket of
undefined ->
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, noSocket}),
{ok, SrvState, CliState};
_ ->
case BacklogNum >= BacklogSize of
true ->
?WARN(ServerName, ":backlog full curNum:~p Total: ~p ~n", [BacklogNum, BacklogSize]),
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, backlogFull}),
{ok, SrvState, CliState};
_ ->
case Status of
leisure -> %%
Request = agHttpProtocol:request(IsSystem, Body, Method, Host, DbName, Path, [UserPassWord | Headers]),
case ssl:send(Socket, Request) of
ok ->
TimerRef =
case OverTime of
infinity ->
undefined;
_ ->
erlang:start_timer(OverTime, self(), waiting_over, [{abs, true}])
end,
{ok, SrvState, CliState#cliState{isHeadMethod = Method == ?AgHead, status = waiting, backlogNum = BacklogNum + 1, curInfo = {FromPid, RequestId, TimerRef}}};
{error, Reason} ->
?WARN(ServerName, ":send error: ~p ~p ~p ~n", [Reason, FromPid, RequestId]),
ssl:close(Socket),
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, {socketSendError, Reason}}),
agAgencyUtils:dealClose(SrvState, CliState, {error, {socketSendError, Reason}})
end;
_ ->
{ok, SrvState, CliState#cliState{requestsIns = [MiRequest | RequestsIns], backlogNum = BacklogNum + 1}}
end
end
end;
handleMsg({ssl, Socket, Data},
#srvState{serverName = ServerName, rn = Rn, rnrn = RnRn, socket = Socket} = SrvState,
#cliState{isHeadMethod = IsHeadMethod, backlogNum = BacklogNum, curInfo = CurInfo, requestsIns = RequestsIns, requestsOuts = RequestsOuts, recvState = RecvState} = CliState) ->
try agHttpProtocol:response(RecvState, Rn, RnRn, Data, IsHeadMethod) of
{done, #recvState{statusCode = StatusCode, headers = Headers, body = Body}} ->
agAgencyUtils:agencyReply(CurInfo, {StatusCode, Body, Headers}),
case RequestsOuts of
[] ->
case RequestsIns of
[] ->
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}};
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined});
MiRLists ->
[MiRequest | Outs] = lists:reverse(MiRLists),
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs, backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined})
end;
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = [], backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined});
[MiRequest | Outs] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs, backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined})
end;
{ok, NewRecvState} ->
{ok, SrvState, CliState#cliState{recvState = NewRecvState}};
{error, Reason} ->
?WARN(ServerName, "handle ssl data error: ~p ~p ~n", [Reason, CurInfo]),
ssl:close(Socket),
agAgencyUtils:dealClose(SrvState, CliState, {error, {sslDataError, Reason}})
catch
E:R:S ->
?WARN(ServerName, "handle ssl data crash: ~p:~p~n~p~n ~p~n ", [E, R, S, CurInfo]),
ssl:close(Socket),
agAgencyUtils:dealClose(SrvState, CliState, {{error, agencyHandledataError}})
end;
handleMsg({timeout, TimerRef, waiting_over},
#srvState{socket = Socket} = SrvState,
#cliState{backlogNum = BacklogNum, curInfo = {FromPid, RequestId, TimerRef}} = CliState) ->
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, timeout}),
%% ssl ssl收到该次超时数据
ssl:close(Socket),
handleMsg(?miDoNetConnect, SrvState#srvState{socket = undefined}, CliState#cliState{backlogNum = BacklogNum - 1});
handleMsg({ssl_closed, Socket},
#srvState{socket = Socket, serverName = ServerName} = SrvState,
CliState) ->
?WARN(ServerName, "connection closed~n", []),
ssl:close(Socket),
agAgencyUtils:dealClose(SrvState, CliState, {error, ssl_closed});
handleMsg({ssl_error, Socket, Reason},
#srvState{socket = Socket, serverName = ServerName} = SrvState,
CliState) ->
?WARN(ServerName, "connection error: ~p~n", [Reason]),
ssl:close(Socket),
agAgencyUtils:dealClose(SrvState, CliState, {error, {ssl_error, Reason}});
handleMsg(?miDoNetConnect,
#srvState{poolName = PoolName, serverName = ServerName, reconnectState = ReconnectState} = SrvState,
#cliState{requestsIns = RequestsIns, requestsOuts = RequestsOuts} = CliState) ->
case ?agBeamPool:getv(PoolName) of
#dbOpts{host = Host, port = Port, hostname = HostName, dbName = DbName, userPassword = UserPassword, socketOpts = SocketOpts} ->
case dealConnect(ServerName, HostName, Port, SocketOpts) of
{ok, Socket} ->
NewReconnectState = agAgencyUtils:resetReconnectState(ReconnectState),
%% buff之类状态数据
case RequestsOuts of
[] ->
case RequestsIns of
[] ->
{ok, SrvState#srvState{userPassWord = UserPassword, dbName = DbName, host = Host, reconnectState = NewReconnectState, socket = Socket}, CliState#cliState{status = leisure, curInfo = undefined, recvState = undefined}};
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState#srvState{socket = Socket, reconnectState = NewReconnectState}, CliState#cliState{requestsIns = [], status = leisure, curInfo = undefined, recvState = undefined});
MiRLists ->
[MiRequest | Outs] = lists:reverse(MiRLists),
dealQueueRequest(MiRequest, SrvState#srvState{socket = Socket, reconnectState = NewReconnectState}, CliState#cliState{requestsIns = [], requestsOuts = Outs, status = leisure, curInfo = undefined, recvState = undefined})
end;
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState#srvState{socket = Socket, reconnectState = NewReconnectState}, CliState#cliState{requestsOuts = [], status = leisure, curInfo = undefined, recvState = undefined});
[MiRequest | Outs] ->
dealQueueRequest(MiRequest, SrvState#srvState{socket = Socket, reconnectState = NewReconnectState}, CliState#cliState{requestsOuts = Outs, status = leisure, curInfo = undefined, recvState = undefined})
end;
{error, _Reason} ->
agAgencyUtils:reconnectTimer(SrvState, CliState)
end;
_Ret ->
?WARN(ServerName, "deal connect not found agBeamPool:getv(~p) ret ~p is error ~n", [PoolName, _Ret])
end;
handleMsg(Msg, #srvState{serverName = ServerName} = SrvState, CliState) ->
?WARN(ServerName, "unknown msg: ~p~n", [Msg]),
{ok, SrvState, CliState}.
-spec terminate(term(), srvState(), cliState()) -> ok.
terminate(_Reason,
#srvState{socket = Socket} = SrvState,
CliState) ->
{ok, NewSrvState, NewCliState} = overAllWork(SrvState, CliState),
ssl:close(Socket),
agAgencyUtils:dealClose(NewSrvState, NewCliState, {error, shutdown}),
ok.
-spec overAllWork(srvState(), cliState()) -> {ok, srvState(), cliState()}.
overAllWork(SrvState, #cliState{requestsIns = RequestsIns, requestsOuts = RequestsOuts, status = Status} = CliState) ->
case Status of
leisure ->
case RequestsOuts of
[] ->
case RequestsIns of
[] ->
{ok, SrvState, CliState};
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = []});
MiRLists ->
[MiRequest | Outs] = lists:reverse(MiRLists),
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs})
end;
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = []});
[MiRequest | Outs] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs})
end;
_ ->
overReceiveSslData(SrvState, CliState)
end.
-spec overDealQueueRequest(miRequest(), srvState(), cliState()) -> {ok, srvState(), cliState()}.
overDealQueueRequest(#miRequest{method = Method, path = Path, headers = Headers, body = Body, requestId = RequestId, fromPid = FromPid, overTime = OverTime, isSystem = IsSystem},
#srvState{serverName = ServerName, host = Host, userPassWord = UserPassWord, dbName = DbName, socket = Socket} = SrvState,
#cliState{requestsIns = RequestsIns, requestsOuts = RequestsOuts, backlogNum = BacklogNum} = CliState) ->
case erlang:monotonic_time(millisecond) > OverTime of
true ->
%%
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, timeout}),
case RequestsOuts of
[] ->
case RequestsIns of
[] ->
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1}};
[MiRequest] ->
overDealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], backlogNum = BacklogNum - 1});
MiRLists ->
[MiRequest | Outs] = lists:reverse(MiRLists),
overDealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs, backlogNum = BacklogNum - 1})
end;
[MiRequest] ->
overDealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = [], backlogNum = BacklogNum - 1});
[MiRequest | Outs] ->
overDealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs, backlogNum = BacklogNum - 1})
end;
_ ->
Request = agHttpProtocol:request(IsSystem, Body, Method, Host, DbName, Path, [UserPassWord | Headers]),
case ssl:send(Socket, Request) of
ok ->
TimerRef =
case OverTime of
infinity ->
undefined;
_ ->
erlang:start_timer(OverTime, self(), waiting_over, [{abs, true}])
end,
overReceiveSslData(SrvState, CliState#cliState{isHeadMethod = Method == ?AgHead, status = waiting, curInfo = {FromPid, RequestId, TimerRef}});
{error, Reason} ->
?WARN(ServerName, ":send error: ~p~n", [Reason]),
ssl:close(Socket),
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, socketSendError}),
agAgencyUtils:dealClose(SrvState, CliState, {error, socketSendError})
end
end.
-spec overReceiveSslData(srvState(), cliState()) -> {ok, srvState(), cliState()}.
overReceiveSslData(#srvState{poolName = PoolName, serverName = ServerName, rn = Rn, rnrn = RnRn, socket = Socket} = SrvState,
#cliState{isHeadMethod = IsHeadMethod, backlogNum = BacklogNum, curInfo = CurInfo, requestsIns = RequestsIns, requestsOuts = RequestsOuts, recvState = RecvState} = CliState) ->
receive
{ssl, Socket, Data} ->
try agHttpProtocol:response(RecvState, Rn, RnRn, Data, IsHeadMethod) of
{done, #recvState{statusCode = StatusCode, headers = Headers, body = Body}} ->
agAgencyUtils:agencyReply(CurInfo, {StatusCode, Body, Headers}),
case RequestsOuts of
[] ->
case RequestsIns of
[] ->
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}};
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined});
MiRLists ->
[MiRequest | Outs] = lists:reverse(MiRLists),
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs, backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined})
end;
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = [], backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined});
[MiRequest | Outs] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs, backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined})
end;
{ok, NewRecvState} ->
overReceiveSslData(SrvState, CliState#cliState{recvState = NewRecvState});
{error, Reason} ->
?WARN(overReceiveSslData, "handle ssl data error: ~p ~n", [Reason]),
ssl:close(Socket),
agAgencyUtils:dealClose(SrvState, CliState, {error, {sslDataError, Reason}})
catch
E:R:S ->
?WARN(overReceiveSslData, "handle ssl data crash: ~p:~p~n~p ~n ", [E, R, S]),
ssl:close(Socket),
agAgencyUtils:dealClose(SrvState, CliState, {error, {ssl_error, handledataError}})
end;
{timeout, TimerRef, waiting_over} ->
case CurInfo of
{_PidForm, _RequestId, TimerRef} ->
ssl:close(Socket),
agAgencyUtils:agencyReply(CurInfo, {error, timeout}),
case RequestsOuts of
[] ->
case RequestsIns of
[] ->
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}};
[MiRequest] ->
case ?agBeamPool:getv(PoolName) of
#dbOpts{port = Port, hostname = HostName, socketOpts = SocketOpts} ->
case dealConnect(ServerName, HostName, Port, SocketOpts) of
{ok, NewSocket} ->
overDealQueueRequest(MiRequest, SrvState#srvState{socket = NewSocket}, CliState#cliState{requestsIns = [], status = leisure, curInfo = undefined, recvState = undefined});
{error, _Reason} ->
agAgencyUtils:dealClose(SrvState, CliState, {error, {newTcpConnectErrorOver, _Reason}})
end;
_Ret ->
agAgencyUtils:dealClose(SrvState, CliState, {error, {notFoundPoolName, PoolName}})
end;
MiRLists ->
[MiRequest | Outs] = lists:reverse(MiRLists),
case ?agBeamPool:getv(PoolName) of
#dbOpts{port = Port, hostname = HostName, socketOpts = SocketOpts} ->
case dealConnect(ServerName, HostName, Port, SocketOpts) of
{ok, NewSocket} ->
overDealQueueRequest(MiRequest, SrvState#srvState{socket = NewSocket}, CliState#cliState{requestsIns = [], requestsOuts = Outs, status = leisure, curInfo = undefined, recvState = undefined});
{error, _Reason} ->
agAgencyUtils:dealClose(SrvState, CliState, {error, {newTcpConnectErrorOver, _Reason}})
end;
_Ret ->
agAgencyUtils:dealClose(SrvState, CliState, {error, {notFoundPoolName, PoolName}})
end
end;
[MiRequest] ->
case ?agBeamPool:getv(PoolName) of
#dbOpts{port = Port, hostname = HostName, socketOpts = SocketOpts} ->
case dealConnect(ServerName, HostName, Port, SocketOpts) of
{ok, NewSocket} ->
overDealQueueRequest(MiRequest, SrvState#srvState{socket = NewSocket}, CliState#cliState{requestsOuts = [], status = leisure, curInfo = undefined, recvState = undefined});
{error, _Reason} ->
agAgencyUtils:dealClose(SrvState, CliState, {error, {new_ssl_connect_error_over, _Reason}})
end;
_Ret ->
agAgencyUtils:dealClose(SrvState, CliState, {error, {notFoundPoolName, PoolName}})
end;
[MiRequest | Outs] ->
case ?agBeamPool:getv(PoolName) of
#dbOpts{port = Port, hostname = HostName, socketOpts = SocketOpts} ->
case dealConnect(ServerName, HostName, Port, SocketOpts) of
{ok, NewSocket} ->
overDealQueueRequest(MiRequest, SrvState#srvState{socket = NewSocket}, CliState#cliState{requestsOuts = Outs, status = leisure, curInfo = undefined, recvState = undefined});
{error, _Reason} ->
agAgencyUtils:dealClose(SrvState, CliState, {error, {newTcpConnectErrorOver, _Reason}})
end;
_Ret ->
agAgencyUtils:dealClose(SrvState, CliState, {error, {notFoundPoolName, PoolName}})
end
end;
_ ->
?WARN(overReceiveSslData, "receive waiting_over TimerRef not match: ~p~n", [TimerRef]),
overReceiveSslData(SrvState, CliState)
end;
{ssl_closed, Socket} ->
ssl:close(Socket),
agAgencyUtils:dealClose(SrvState, CliState, {error, ssl_closed});
{ssl_error, Socket, Reason} ->
ssl:close(Socket),
agAgencyUtils:dealClose(SrvState, CliState, {error, {ssl_error, Reason}});
#miRequest{} = MiRequest ->
overReceiveSslData(SrvState, CliState#cliState{requestsIns = [MiRequest | RequestsIns], backlogNum = BacklogNum + 1});
_Msg ->
?WARN(overReceiveSslData, "receive unexpect msg: ~p~n", [_Msg]),
overReceiveSslData(SrvState, CliState)
end.
-spec dealConnect(atom(), hostName(), port(), socketOpts()) -> {ok, socket()} | {error, term()}.
dealConnect(ServerName, HostName, Port, SocketOptions) ->
case inet:getaddrs(HostName, inet) of
{ok, IPList} ->
Ip = agMiscUtils:randomElement(IPList),
case ssl:connect(Ip, Port, SocketOptions, ?DEFAULT_CONNECT_TIMEOUT) of
{ok, Socket} ->
{ok, Socket};
{error, Reason} ->
?WARN(ServerName, "connect error: ~p~n", [Reason]),
{error, Reason}
end;
{error, Reason} ->
?WARN(ServerName, "getaddrs error: ~p~n", [Reason]),
{error, Reason}
end.
-spec dealQueueRequest(miRequest(), srvState(), cliState()) -> {ok, srvState(), cliState()}.
dealQueueRequest(#miRequest{method = Method, path = Path, headers = Headers, body = Body, requestId = RequestId, fromPid = FromPid, overTime = OverTime, isSystem = IsSystem},
#srvState{serverName = ServerName, host = Host, userPassWord = UserPassWord, dbName = DbName, socket = Socket} = SrvState,
#cliState{requestsIns = RequestsIns, requestsOuts = RequestsOuts, backlogNum = BacklogNum} = CliState) ->
case erlang:monotonic_time(millisecond) > OverTime of
true ->
%%
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, timeout}),
case RequestsOuts of
[] ->
case RequestsIns of
[] ->
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1}};
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], backlogNum = BacklogNum - 1});
MiRLists ->
[MiRequest | Outs] = lists:reverse(MiRLists),
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs, backlogNum = BacklogNum - 1})
end;
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = [], backlogNum = BacklogNum - 1});
[MiRequest | Outs] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs, backlogNum = BacklogNum - 1})
end;
_ ->
Request = agHttpProtocol:request(IsSystem, Body, Method, Host, DbName, Path, [UserPassWord | Headers]),
case ssl:send(Socket, Request) of
ok ->
TimerRef =
case OverTime of
infinity ->
undefined;
_ ->
erlang:start_timer(OverTime, self(), waiting_over, [{abs, true}])
end,
{ok, SrvState, CliState#cliState{isHeadMethod = Method == ?AgHead, status = waiting, curInfo = {FromPid, RequestId, TimerRef}}};
{error, Reason} ->
?WARN(ServerName, ":send error: ~p~n", [Reason]),
ssl:close(Socket),
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, socketSendError}),
agAgencyUtils:dealClose(SrvState, CliState, {error, socketSendError})
end
end.

+ 77
- 0
src/agHttpCli/agTcpAgencyExm.erl View File

@ -0,0 +1,77 @@
-module(agTcpAgencyExm).
-compile(inline).
-compile({inline_size, 128}).
-export([
start_link/3
, init_it/3
, system_code_change/4
, system_continue/3
, system_get_state/1
, system_terminate/4
]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% genExm start %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec start_link(module(), term(), [proc_lib:spawn_option()]) -> {ok, pid()}.
start_link(ServerName, Args, SpawnOpts) ->
proc_lib:start_link(?MODULE, init_it, [ServerName, self(), Args], infinity, SpawnOpts).
init_it(ServerName, Parent, Args) ->
case safeRegister(ServerName) of
true ->
process_flag(trap_exit, true),
moduleInit(Parent, Args);
{false, Pid} ->
proc_lib:init_ack(Parent, {error, {alreadyStarted, Pid}})
end.
-spec system_code_change(term(), module(), undefined | term(), term()) -> {ok, term()}.
system_code_change(MiscState, _Module, _OldVsn, _Extra) ->
{ok, MiscState}.
-spec system_continue(pid(), [], {module(), term(), term()}) -> ok.
system_continue(_Parent, _Debug, {Parent, SrvState, CliState}) ->
loop(Parent, SrvState, CliState).
-spec system_get_state(term()) -> {ok, term()}.
system_get_state({_Parent, SrvState, _CliState}) ->
{ok, SrvState}.
-spec system_terminate(term(), pid(), [], term()) -> none().
system_terminate(Reason, _Parent, _Debug, {_Parent, SrvState, CliState}) ->
terminate(Reason, SrvState, CliState).
safeRegister(ServerName) ->
try register(ServerName, self()) of
true -> true
catch
_:_ -> {false, whereis(ServerName)}
end.
moduleInit(Parent, Args) ->
case agTcpAgencyIns:init(Args) of
{ok, SrvState, CliState} ->
proc_lib:init_ack(Parent, {ok, self()}),
loop(Parent, SrvState, CliState);
{stop, Reason} ->
proc_lib:init_ack(Parent, {error, Reason}),
exit(Reason)
end.
loop(Parent, SrvState, CliState) ->
receive
{system, From, Request} ->
sys:handle_system_msg(Request, From, Parent, ?MODULE, [], {Parent, SrvState, CliState});
{'EXIT', Parent, Reason} ->
terminate(Reason, SrvState, CliState);
Msg ->
{ok, NewSrvState, NewCliState} = agTcpAgencyIns:handleMsg(Msg, SrvState, CliState),
loop(Parent, NewSrvState, NewCliState)
end.
terminate(Reason, SrvState, CliState) ->
agTcpAgencyIns:terminate(Reason, SrvState, CliState),
exit(Reason).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% genExm end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

+ 400
- 0
src/agHttpCli/agTcpAgencyIns.erl View File

@ -0,0 +1,400 @@
-module(agTcpAgencyIns).
-include("agHttpCli.hrl").
-include("erlArango.hrl").
-compile(inline).
-compile({inline_size, 128}).
-export([
%% Inner Behavior API
init/1
, handleMsg/3
, terminate/3
]).
-spec init(term()) -> no_return().
init({PoolName, AgencyName, #agencyOpts{reconnect = Reconnect, backlogSize = BacklogSize, reconnectTimeMin = Min, reconnectTimeMax = Max}}) ->
ReconnectState = agAgencyUtils:initReconnectState(Reconnect, Min, Max),
self() ! ?miDoNetConnect,
{ok, #srvState{poolName = PoolName, serverName = AgencyName, rn = binary:compile_pattern(<<"\r\n">>), rnrn = binary:compile_pattern(<<"\r\n\r\n">>), reconnectState = ReconnectState}, #cliState{backlogSize = BacklogSize}}.
-spec handleMsg(term(), srvState(), cliState()) -> {ok, term(), term()}.
handleMsg(#miRequest{method = Method, path = Path, headers = Headers, body = Body, requestId = RequestId, fromPid = FromPid, overTime = OverTime, isSystem = IsSystem} = MiRequest,
#srvState{serverName = ServerName, host = Host, userPassWord = UserPassWord, dbName = DbName, socket = Socket} = SrvState,
#cliState{backlogNum = BacklogNum, backlogSize = BacklogSize, requestsIns = RequestsIns, status = Status} = CliState) ->
case Socket of
undefined ->
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, noSocket}),
{ok, SrvState, CliState};
_ ->
case BacklogNum >= BacklogSize of
true ->
?WARN(ServerName, ":backlog full curNum:~p Total: ~p ~n", [BacklogNum, BacklogSize]),
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, backlogFull}),
{ok, SrvState, CliState};
_ ->
case Status of
leisure -> %%
Request = agHttpProtocol:request(IsSystem, Body, Method, Host, DbName, Path, [UserPassWord | Headers]),
case gen_tcp:send(Socket, Request) of
ok ->
TimerRef =
case OverTime of
infinity ->
undefined;
_ ->
erlang:start_timer(OverTime, self(), waiting_over, [{abs, true}])
end,
{ok, SrvState, CliState#cliState{isHeadMethod = Method == ?AgHead, status = waiting, backlogNum = BacklogNum + 1, curInfo = {FromPid, RequestId, TimerRef}}};
{error, Reason} ->
?WARN(ServerName, ":send error: ~p ~p ~p ~n", [Reason, FromPid, RequestId]),
gen_tcp:close(Socket),
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, {socketSendError, Reason}}),
agAgencyUtils:dealClose(SrvState, CliState, {error, {socketSendError, Reason}})
end;
_ ->
{ok, SrvState, CliState#cliState{requestsIns = [MiRequest | RequestsIns], backlogNum = BacklogNum + 1}}
end
end
end;
handleMsg({tcp, Socket, Data},
#srvState{serverName = ServerName, rn = Rn, rnrn = RnRn, socket = Socket} = SrvState,
#cliState{isHeadMethod = IsHeadMethod, backlogNum = BacklogNum, curInfo = CurInfo, requestsIns = RequestsIns, requestsOuts = RequestsOuts, recvState = RecvState} = CliState) ->
try agHttpProtocol:response(RecvState, Rn, RnRn, Data, IsHeadMethod) of
{done, #recvState{statusCode = StatusCode, headers = Headers, body = Body}} ->
agAgencyUtils:agencyReply(CurInfo, {StatusCode, Body, Headers}),
case RequestsOuts of
[] ->
case RequestsIns of
[] ->
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}};
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined});
MiRLists ->
[MiRequest | Outs] = lists:reverse(MiRLists),
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs, backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined})
end;
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = [], backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined});
[MiRequest | Outs] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs, backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined})
end;
{ok, NewRecvState} ->
{ok, SrvState, CliState#cliState{recvState = NewRecvState}};
{error, Reason} ->
?WARN(ServerName, "handle tcp data error: ~p ~p ~n", [Reason, CurInfo]),
gen_tcp:close(Socket),
agAgencyUtils:dealClose(SrvState, CliState, {error, {tcpDataError, Reason}})
catch
E:R:S ->
?WARN(ServerName, "handle tcp data crash: ~p:~p~n~p~n ~p ~n ", [E, R, S, CurInfo]),
gen_tcp:close(Socket),
agAgencyUtils:dealClose(SrvState, CliState, {error, agencyHandledataError})
end;
handleMsg({timeout, TimerRef, waiting_over},
#srvState{socket = Socket} = SrvState,
#cliState{backlogNum = BacklogNum, curInfo = {FromPid, RequestId, TimerRef}} = CliState) ->
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, timeout}),
%% tcp tcp收到该次超时数据
gen_tcp:close(Socket),
handleMsg(?miDoNetConnect, SrvState#srvState{socket = undefined}, CliState#cliState{backlogNum = BacklogNum - 1});
handleMsg({tcp_closed, Socket},
#srvState{socket = Socket, serverName = ServerName} = SrvState,
CliState) ->
?WARN(ServerName, "connection closed~n", []),
gen_tcp:close(Socket),
agAgencyUtils:dealClose(SrvState, CliState, {error, tcp_closed});
handleMsg({tcp_error, Socket, Reason},
#srvState{socket = Socket, serverName = ServerName} = SrvState,
CliState) ->
?WARN(ServerName, "connection error: ~p~n", [Reason]),
gen_tcp:close(Socket),
agAgencyUtils:dealClose(SrvState, CliState, {error, {tcp_error, Reason}});
handleMsg(?miDoNetConnect,
#srvState{poolName = PoolName, serverName = ServerName, reconnectState = ReconnectState} = SrvState,
#cliState{requestsIns = RequestsIns, requestsOuts = RequestsOuts} = CliState) ->
case ?agBeamPool:getv(PoolName) of
#dbOpts{host = Host, port = Port, hostname = HostName, dbName = DbName, userPassword = UserPassword, socketOpts = SocketOpts} ->
case dealConnect(ServerName, HostName, Port, SocketOpts) of
{ok, Socket} ->
NewReconnectState = agAgencyUtils:resetReconnectState(ReconnectState),
%% buff之类状态数据
case RequestsOuts of
[] ->
case RequestsIns of
[] ->
{ok, SrvState#srvState{userPassWord = UserPassword, dbName = DbName, host = Host, reconnectState = NewReconnectState, socket = Socket}, CliState#cliState{status = leisure, curInfo = undefined, recvState = undefined}};
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState#srvState{socket = Socket, reconnectState = NewReconnectState}, CliState#cliState{requestsIns = [], status = leisure, curInfo = undefined, recvState = undefined});
MiRLists ->
[MiRequest | Outs] = lists:reverse(MiRLists),
dealQueueRequest(MiRequest, SrvState#srvState{socket = Socket, reconnectState = NewReconnectState}, CliState#cliState{requestsIns = [], requestsOuts = Outs, status = leisure, curInfo = undefined, recvState = undefined})
end;
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState#srvState{socket = Socket, reconnectState = NewReconnectState}, CliState#cliState{requestsOuts = [], status = leisure, curInfo = undefined, recvState = undefined});
[MiRequest | Outs] ->
dealQueueRequest(MiRequest, SrvState#srvState{socket = Socket, reconnectState = NewReconnectState}, CliState#cliState{requestsOuts = Outs, status = leisure, curInfo = undefined, recvState = undefined})
end;
{error, _Reason} ->
agAgencyUtils:reconnectTimer(SrvState, CliState)
end;
_Ret ->
?WARN(ServerName, "deal connect not found agBeamPool:getv(~p) ret ~p is error ~n", [PoolName, _Ret])
end;
handleMsg(Msg, #srvState{serverName = ServerName} = SrvState, CliState) ->
?WARN(ServerName, "unknown msg: ~p~n", [Msg]),
{ok, SrvState, CliState}.
-spec terminate(term(), srvState(), cliState()) -> ok.
terminate(_Reason,
#srvState{socket = Socket} = SrvState,
CliState) ->
{ok, NewSrvState, NewCliState} = overAllWork(SrvState, CliState),
gen_tcp:close(Socket),
agAgencyUtils:dealClose(NewSrvState, NewCliState, {error, shutdown}),
ok.
-spec overAllWork(srvState(), cliState()) -> {ok, srvState(), cliState()}.
overAllWork(SrvState, #cliState{requestsIns = RequestsIns, requestsOuts = RequestsOuts, status = Status} = CliState) ->
case Status of
leisure ->
case RequestsOuts of
[] ->
case RequestsIns of
[] ->
{ok, SrvState, CliState};
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = []});
MiRLists ->
[MiRequest | Outs] = lists:reverse(MiRLists),
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs})
end;
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = []});
[MiRequest | Outs] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs})
end;
_ ->
overReceiveTcpData(SrvState, CliState)
end.
-spec overDealQueueRequest(miRequest(), srvState(), cliState()) -> {ok, srvState(), cliState()}.
overDealQueueRequest(#miRequest{method = Method, path = Path, headers = Headers, body = Body, requestId = RequestId, fromPid = FromPid, overTime = OverTime, isSystem = IsSystem},
#srvState{serverName = ServerName, host = Host, userPassWord = UserPassWord, dbName = DbName, socket = Socket} = SrvState,
#cliState{requestsIns = RequestsIns, requestsOuts = RequestsOuts, backlogNum = BacklogNum} = CliState) ->
case erlang:monotonic_time(millisecond) > OverTime of
true ->
%%
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, timeout}),
case RequestsOuts of
[] ->
case RequestsIns of
[] ->
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1}};
[MiRequest] ->
overDealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], backlogNum = BacklogNum - 1});
MiRLists ->
[MiRequest | Outs] = lists:reverse(MiRLists),
overDealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs, backlogNum = BacklogNum - 1})
end;
[MiRequest] ->
overDealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = [], backlogNum = BacklogNum - 1});
[MiRequest | Outs] ->
overDealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs, backlogNum = BacklogNum - 1})
end;
_ ->
Request = agHttpProtocol:request(IsSystem, Body, Method, Host, DbName, Path, [UserPassWord | Headers]),
case gen_tcp:send(Socket, Request) of
ok ->
TimerRef =
case OverTime of
infinity ->
undefined;
_ ->
erlang:start_timer(OverTime, self(), waiting_over, [{abs, true}])
end,
overReceiveTcpData(SrvState, CliState#cliState{isHeadMethod = Method == ?AgHead, status = waiting, curInfo = {FromPid, RequestId, TimerRef}});
{error, Reason} ->
?WARN(ServerName, ":send error: ~p~n", [Reason]),
gen_tcp:close(Socket),
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, socketSendError}),
agAgencyUtils:dealClose(SrvState, CliState, {error, socketSendError})
end
end.
-spec overReceiveTcpData(srvState(), cliState()) -> {ok, srvState(), cliState()}.
overReceiveTcpData(#srvState{poolName = PoolName, serverName = ServerName, rn = Rn, rnrn = RnRn, socket = Socket} = SrvState,
#cliState{isHeadMethod = IsHeadMethod, backlogNum = BacklogNum, curInfo = CurInfo, requestsIns = RequestsIns, requestsOuts = RequestsOuts, recvState = RecvState} = CliState) ->
receive
{tcp, Socket, Data} ->
try agHttpProtocol:response(RecvState, Rn, RnRn, Data, IsHeadMethod) of
{done, #recvState{statusCode = StatusCode, headers = Headers, body = Body}} ->
agAgencyUtils:agencyReply(CurInfo, {StatusCode, Body, Headers}),
case RequestsOuts of
[] ->
case RequestsIns of
[] ->
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}};
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined});
MiRLists ->
[MiRequest | Outs] = lists:reverse(MiRLists),
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs, backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined})
end;
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = [], backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined});
[MiRequest | Outs] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs, backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined})
end;
{ok, NewRecvState} ->
overReceiveTcpData(SrvState, CliState#cliState{recvState = NewRecvState});
{error, Reason} ->
?WARN(overReceiveTcpData, "handle tcp data error: ~p ~n", [Reason]),
gen_tcp:close(Socket),
agAgencyUtils:dealClose(SrvState, CliState, {error, {tcpDataError, Reason}})
catch
E:R:S ->
?WARN(overReceiveTcpData, "handle tcp data crash: ~p:~p~n~p ~n ", [E, R, S]),
gen_tcp:close(Socket),
agAgencyUtils:dealClose(SrvState, CliState, {error, {tcp_error, handledataError}})
end;
{timeout, TimerRef, waiting_over} ->
case CurInfo of
{_PidForm, _RequestId, TimerRef} ->
gen_tcp:close(Socket),
agAgencyUtils:agencyReply(CurInfo, {error, timeout}),
case RequestsOuts of
[] ->
case RequestsIns of
[] ->
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}};
[MiRequest] ->
case ?agBeamPool:getv(PoolName) of
#dbOpts{port = Port, hostname = HostName, socketOpts = SocketOpts} ->
case dealConnect(ServerName, HostName, Port, SocketOpts) of
{ok, NewSocket} ->
overDealQueueRequest(MiRequest, SrvState#srvState{socket = NewSocket}, CliState#cliState{requestsIns = [], status = leisure, curInfo = undefined, recvState = undefined});
{error, _Reason} ->
agAgencyUtils:dealClose(SrvState, CliState, {error, {newTcpConnectErrorOver, _Reason}})
end;
_Ret ->
agAgencyUtils:dealClose(SrvState, CliState, {error, {notFoundPoolName, PoolName}})
end;
MiRLists ->
[MiRequest | Outs] = lists:reverse(MiRLists),
case ?agBeamPool:getv(PoolName) of
#dbOpts{port = Port, hostname = HostName, socketOpts = SocketOpts} ->
case dealConnect(ServerName, HostName, Port, SocketOpts) of
{ok, NewSocket} ->
overDealQueueRequest(MiRequest, SrvState#srvState{socket = NewSocket}, CliState#cliState{requestsIns = [], requestsOuts = Outs, status = leisure, curInfo = undefined, recvState = undefined});
{error, _Reason} ->
agAgencyUtils:dealClose(SrvState, CliState, {error, {newTcpConnectErrorOver, _Reason}})
end;
_Ret ->
agAgencyUtils:dealClose(SrvState, CliState, {error, {notFoundPoolName, PoolName}})
end
end;
[MiRequest] ->
case ?agBeamPool:getv(PoolName) of
#dbOpts{port = Port, hostname = HostName, socketOpts = SocketOpts} ->
case dealConnect(ServerName, HostName, Port, SocketOpts) of
{ok, NewSocket} ->
overDealQueueRequest(MiRequest, SrvState#srvState{socket = NewSocket}, CliState#cliState{requestsOuts = [], status = leisure, curInfo = undefined, recvState = undefined});
{error, _Reason} ->
agAgencyUtils:dealClose(SrvState, CliState, {error, {newTcpConnectErrorOver, _Reason}})
end;
_Ret ->
agAgencyUtils:dealClose(SrvState, CliState, {error, {notFoundPoolName, PoolName}})
end;
[MiRequest | Outs] ->
case ?agBeamPool:getv(PoolName) of
#dbOpts{port = Port, hostname = HostName, socketOpts = SocketOpts} ->
case dealConnect(ServerName, HostName, Port, SocketOpts) of
{ok, NewSocket} ->
overDealQueueRequest(MiRequest, SrvState#srvState{socket = NewSocket}, CliState#cliState{requestsOuts = Outs, status = leisure, curInfo = undefined, recvState = undefined});
{error, _Reason} ->
agAgencyUtils:dealClose(SrvState, CliState, {error, {newTcpConnectErrorOver, _Reason}})
end;
_Ret ->
agAgencyUtils:dealClose(SrvState, CliState, {error, {notFoundPoolName, PoolName}})
end
end;
_ ->
?WARN(overReceiveTcpData, "receive waiting_over TimerRef not match: ~p~n", [TimerRef]),
overReceiveTcpData(SrvState, CliState)
end;
{tcp_closed, Socket} ->
gen_tcp:close(Socket),
agAgencyUtils:dealClose(SrvState, CliState, {error, tcp_closed});
{tcp_error, Socket, Reason} ->
gen_tcp:close(Socket),
agAgencyUtils:dealClose(SrvState, CliState, {error, {tcp_error, Reason}});
#miRequest{} = MiRequest ->
overReceiveTcpData(SrvState, CliState#cliState{requestsIns = [MiRequest | RequestsIns], backlogNum = BacklogNum + 1});
_Msg ->
?WARN(overReceiveTcpData, "receive unexpect msg: ~p~n", [_Msg]),
overReceiveTcpData(SrvState, CliState)
end.
-spec dealConnect(atom(), hostName(), port(), socketOpts()) -> {ok, socket()} | {error, term()}.
dealConnect(ServerName, HostName, Port, SocketOptions) ->
case inet:getaddrs(HostName, inet) of
{ok, IPList} ->
Ip = agMiscUtils:randomElement(IPList),
case gen_tcp:connect(Ip, Port, SocketOptions, ?DEFAULT_CONNECT_TIMEOUT) of
{ok, Socket} ->
{ok, Socket};
{error, Reason} ->
?WARN(ServerName, "connect error: ~p~n", [Reason]),
{error, Reason}
end;
{error, Reason} ->
?WARN(ServerName, "getaddrs error: ~p~n", [Reason]),
{error, Reason}
end.
-spec dealQueueRequest(miRequest(), srvState(), cliState()) -> {ok, srvState(), cliState()}.
dealQueueRequest(#miRequest{method = Method, path = Path, headers = Headers, body = Body, requestId = RequestId, fromPid = FromPid, overTime = OverTime, isSystem = IsSystem},
#srvState{serverName = ServerName, host = Host, userPassWord = UserPassWord, dbName = DbName, socket = Socket} = SrvState,
#cliState{requestsIns = RequestsIns, requestsOuts = RequestsOuts, backlogNum = BacklogNum} = CliState) ->
case erlang:monotonic_time(millisecond) > OverTime of
true ->
%%
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, timeout}),
case RequestsOuts of
[] ->
case RequestsIns of
[] ->
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1}};
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], backlogNum = BacklogNum - 1});
MiRLists ->
[MiRequest | Outs] = lists:reverse(MiRLists),
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs, backlogNum = BacklogNum - 1})
end;
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = [], backlogNum = BacklogNum - 1});
[MiRequest | Outs] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs, backlogNum = BacklogNum - 1})
end;
_ ->
Request = agHttpProtocol:request(IsSystem, Body, Method, Host, DbName, Path, [UserPassWord | Headers]),
case gen_tcp:send(Socket, Request) of
ok ->
TimerRef =
case OverTime of
infinity ->
undefined;
_ ->
erlang:start_timer(OverTime, self(), waiting_over, [{abs, true}])
end,
{ok, SrvState, CliState#cliState{isHeadMethod = Method == ?AgHead, status = waiting, curInfo = {FromPid, RequestId, TimerRef}}};
{error, Reason} ->
?WARN(ServerName, ":send error: ~p~n", [Reason]),
gen_tcp:close(Socket),
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, socketSendError}),
agAgencyUtils:dealClose(SrvState, CliState, {error, socketSendError})
end
end.

+ 77
- 0
src/agHttpCli/agVstAgencyExm.erl View File

@ -0,0 +1,77 @@
-module(agVstAgencyExm).
-compile(inline).
-compile({inline_size, 128}).
-export([
start_link/3
, init_it/3
, system_code_change/4
, system_continue/3
, system_get_state/1
, system_terminate/4
]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% genExm start %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec start_link(module(), term(), [proc_lib:spawn_option()]) -> {ok, pid()}.
start_link(ServerName, Args, SpawnOpts) ->
proc_lib:start_link(?MODULE, init_it, [ServerName, self(), Args], infinity, SpawnOpts).
init_it(ServerName, Parent, Args) ->
case safeRegister(ServerName) of
true ->
process_flag(trap_exit, true),
moduleInit(Parent, Args);
{false, Pid} ->
proc_lib:init_ack(Parent, {error, {alreadyStarted, Pid}})
end.
-spec system_code_change(term(), module(), undefined | term(), term()) -> {ok, term()}.
system_code_change(MiscState, _Module, _OldVsn, _Extra) ->
{ok, MiscState}.
-spec system_continue(pid(), [], {module(), term(), term()}) -> ok.
system_continue(_Parent, _Debug, {Parent, SrvState, CliState}) ->
loop(Parent, SrvState, CliState).
-spec system_get_state(term()) -> {ok, term()}.
system_get_state({_Parent, SrvState, _CliState}) ->
{ok, SrvState}.
-spec system_terminate(term(), pid(), [], term()) -> none().
system_terminate(Reason, _Parent, _Debug, {_Parent, SrvState, CliState}) ->
terminate(Reason, SrvState, CliState).
safeRegister(ServerName) ->
try register(ServerName, self()) of
true -> true
catch
_:_ -> {false, whereis(ServerName)}
end.
moduleInit(Parent, Args) ->
case agTcpAgencyIns:init(Args) of
{ok, SrvState, CliState} ->
proc_lib:init_ack(Parent, {ok, self()}),
loop(Parent, SrvState, CliState);
{stop, Reason} ->
proc_lib:init_ack(Parent, {error, Reason}),
exit(Reason)
end.
loop(Parent, SrvState, CliState) ->
receive
{system, From, Request} ->
sys:handle_system_msg(Request, From, Parent, ?MODULE, [], {Parent, SrvState, CliState});
{'EXIT', Parent, Reason} ->
terminate(Reason, SrvState, CliState);
Msg ->
{ok, NewSrvState, NewCliState} = agVstAgencyIns:handleMsg(Msg, SrvState, CliState),
loop(Parent, NewSrvState, NewCliState)
end.
terminate(Reason, SrvState, CliState) ->
agTcpAgencyIns:terminate(Reason, SrvState, CliState),
exit(Reason).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% genExm end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

+ 400
- 0
src/agHttpCli/agVstAgencyIns.erl View File

@ -0,0 +1,400 @@
-module(agVstAgencyIns).
-include("agHttpCli.hrl").
-include("erlArango.hrl").
-compile(inline).
-compile({inline_size, 128}).
-export([
%% Inner Behavior API
init/1
, handleMsg/3
, terminate/3
]).
-spec init(term()) -> no_return().
init({PoolName, AgencyName, #agencyOpts{reconnect = Reconnect, backlogSize = BacklogSize, reconnectTimeMin = Min, reconnectTimeMax = Max}}) ->
ReconnectState = agAgencyUtils:initReconnectState(Reconnect, Min, Max),
self() ! ?miDoNetConnect,
{ok, #srvState{poolName = PoolName, serverName = AgencyName, rn = binary:compile_pattern(<<"\r\n">>), rnrn = binary:compile_pattern(<<"\r\n\r\n">>), reconnectState = ReconnectState}, #cliState{backlogSize = BacklogSize}}.
-spec handleMsg(term(), srvState(), cliState()) -> {ok, term(), term()}.
handleMsg(#miRequest{method = Method, path = Path, headers = Headers, body = Body, requestId = RequestId, fromPid = FromPid, overTime = OverTime, isSystem = IsSystem} = MiRequest,
#srvState{serverName = ServerName, host = Host, userPassWord = UserPassWord, dbName = DbName, socket = Socket} = SrvState,
#cliState{backlogNum = BacklogNum, backlogSize = BacklogSize, requestsIns = RequestsIns, status = Status} = CliState) ->
case Socket of
undefined ->
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, noSocket}),
{ok, SrvState, CliState};
_ ->
case BacklogNum >= BacklogSize of
true ->
?WARN(ServerName, ":backlog full curNum:~p Total: ~p ~n", [BacklogNum, BacklogSize]),
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, backlogFull}),
{ok, SrvState, CliState};
_ ->
case Status of
leisure -> %%
Request = agHttpProtocol:request(IsSystem, Body, Method, Host, DbName, Path, [UserPassWord | Headers]),
case gen_tcp:send(Socket, Request) of
ok ->
TimerRef =
case OverTime of
infinity ->
undefined;
_ ->
erlang:start_timer(OverTime, self(), waiting_over, [{abs, true}])
end,
{ok, SrvState, CliState#cliState{isHeadMethod = Method == ?AgHead, status = waiting, backlogNum = BacklogNum + 1, curInfo = {FromPid, RequestId, TimerRef}}};
{error, Reason} ->
?WARN(ServerName, ":send error: ~p ~p ~p ~n", [Reason, FromPid, RequestId]),
gen_tcp:close(Socket),
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, {socketSendError, Reason}}),
agAgencyUtils:dealClose(SrvState, CliState, {error, {socketSendError, Reason}})
end;
_ ->
{ok, SrvState, CliState#cliState{requestsIns = [MiRequest | RequestsIns], backlogNum = BacklogNum + 1}}
end
end
end;
handleMsg({tcp, Socket, Data},
#srvState{serverName = ServerName, rn = Rn, rnrn = RnRn, socket = Socket} = SrvState,
#cliState{isHeadMethod = IsHeadMethod, backlogNum = BacklogNum, curInfo = CurInfo, requestsIns = RequestsIns, requestsOuts = RequestsOuts, recvState = RecvState} = CliState) ->
try agHttpProtocol:response(RecvState, Rn, RnRn, Data, IsHeadMethod) of
{done, #recvState{statusCode = StatusCode, headers = Headers, body = Body}} ->
agAgencyUtils:agencyReply(CurInfo, {StatusCode, Body, Headers}),
case RequestsOuts of
[] ->
case RequestsIns of
[] ->
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}};
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined});
MiRLists ->
[MiRequest | Outs] = lists:reverse(MiRLists),
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs, backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined})
end;
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = [], backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined});
[MiRequest | Outs] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs, backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined})
end;
{ok, NewRecvState} ->
{ok, SrvState, CliState#cliState{recvState = NewRecvState}};
{error, Reason} ->
?WARN(ServerName, "handle tcp data error: ~p ~p ~n", [Reason, CurInfo]),
gen_tcp:close(Socket),
agAgencyUtils:dealClose(SrvState, CliState, {error, {tcpDataError, Reason}})
catch
E:R:S ->
?WARN(ServerName, "handle tcp data crash: ~p:~p~n~p~n ~p ~n ", [E, R, S, CurInfo]),
gen_tcp:close(Socket),
agAgencyUtils:dealClose(SrvState, CliState, {error, agencyHandledataError})
end;
handleMsg({timeout, TimerRef, waiting_over},
#srvState{socket = Socket} = SrvState,
#cliState{backlogNum = BacklogNum, curInfo = {FromPid, RequestId, TimerRef}} = CliState) ->
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, timeout}),
%% tcp tcp收到该次超时数据
gen_tcp:close(Socket),
handleMsg(?miDoNetConnect, SrvState#srvState{socket = undefined}, CliState#cliState{backlogNum = BacklogNum - 1});
handleMsg({tcp_closed, Socket},
#srvState{socket = Socket, serverName = ServerName} = SrvState,
CliState) ->
?WARN(ServerName, "connection closed~n", []),
gen_tcp:close(Socket),
agAgencyUtils:dealClose(SrvState, CliState, {error, tcp_closed});
handleMsg({tcp_error, Socket, Reason},
#srvState{socket = Socket, serverName = ServerName} = SrvState,
CliState) ->
?WARN(ServerName, "connection error: ~p~n", [Reason]),
gen_tcp:close(Socket),
agAgencyUtils:dealClose(SrvState, CliState, {error, {tcp_error, Reason}});
handleMsg(?miDoNetConnect,
#srvState{poolName = PoolName, serverName = ServerName, reconnectState = ReconnectState} = SrvState,
#cliState{requestsIns = RequestsIns, requestsOuts = RequestsOuts} = CliState) ->
case ?agBeamPool:getv(PoolName) of
#dbOpts{host = Host, port = Port, hostname = HostName, dbName = DbName, userPassword = UserPassword, socketOpts = SocketOpts} ->
case dealConnect(ServerName, HostName, Port, SocketOpts) of
{ok, Socket} ->
NewReconnectState = agAgencyUtils:resetReconnectState(ReconnectState),
%% buff之类状态数据
case RequestsOuts of
[] ->
case RequestsIns of
[] ->
{ok, SrvState#srvState{userPassWord = UserPassword, dbName = DbName, host = Host, reconnectState = NewReconnectState, socket = Socket}, CliState#cliState{status = leisure, curInfo = undefined, recvState = undefined}};
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState#srvState{socket = Socket, reconnectState = NewReconnectState}, CliState#cliState{requestsIns = [], status = leisure, curInfo = undefined, recvState = undefined});
MiRLists ->
[MiRequest | Outs] = lists:reverse(MiRLists),
dealQueueRequest(MiRequest, SrvState#srvState{socket = Socket, reconnectState = NewReconnectState}, CliState#cliState{requestsIns = [], requestsOuts = Outs, status = leisure, curInfo = undefined, recvState = undefined})
end;
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState#srvState{socket = Socket, reconnectState = NewReconnectState}, CliState#cliState{requestsOuts = [], status = leisure, curInfo = undefined, recvState = undefined});
[MiRequest | Outs] ->
dealQueueRequest(MiRequest, SrvState#srvState{socket = Socket, reconnectState = NewReconnectState}, CliState#cliState{requestsOuts = Outs, status = leisure, curInfo = undefined, recvState = undefined})
end;
{error, _Reason} ->
agAgencyUtils:reconnectTimer(SrvState, CliState)
end;
_Ret ->
?WARN(ServerName, "deal connect not found agBeamPool:getv(~p) ret ~p is error ~n", [PoolName, _Ret])
end;
handleMsg(Msg, #srvState{serverName = ServerName} = SrvState, CliState) ->
?WARN(ServerName, "unknown msg: ~p~n", [Msg]),
{ok, SrvState, CliState}.
-spec terminate(term(), srvState(), cliState()) -> ok.
terminate(_Reason,
#srvState{socket = Socket} = SrvState,
CliState) ->
{ok, NewSrvState, NewCliState} = overAllWork(SrvState, CliState),
gen_tcp:close(Socket),
agAgencyUtils:dealClose(NewSrvState, NewCliState, {error, shutdown}),
ok.
-spec overAllWork(srvState(), cliState()) -> {ok, srvState(), cliState()}.
overAllWork(SrvState, #cliState{requestsIns = RequestsIns, requestsOuts = RequestsOuts, status = Status} = CliState) ->
case Status of
leisure ->
case RequestsOuts of
[] ->
case RequestsIns of
[] ->
{ok, SrvState, CliState};
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = []});
MiRLists ->
[MiRequest | Outs] = lists:reverse(MiRLists),
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs})
end;
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = []});
[MiRequest | Outs] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs})
end;
_ ->
overReceiveTcpData(SrvState, CliState)
end.
-spec overDealQueueRequest(miRequest(), srvState(), cliState()) -> {ok, srvState(), cliState()}.
overDealQueueRequest(#miRequest{method = Method, path = Path, headers = Headers, body = Body, requestId = RequestId, fromPid = FromPid, overTime = OverTime, isSystem = IsSystem},
#srvState{serverName = ServerName, host = Host, userPassWord = UserPassWord, dbName = DbName, socket = Socket} = SrvState,
#cliState{requestsIns = RequestsIns, requestsOuts = RequestsOuts, backlogNum = BacklogNum} = CliState) ->
case erlang:monotonic_time(millisecond) > OverTime of
true ->
%%
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, timeout}),
case RequestsOuts of
[] ->
case RequestsIns of
[] ->
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1}};
[MiRequest] ->
overDealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], backlogNum = BacklogNum - 1});
MiRLists ->
[MiRequest | Outs] = lists:reverse(MiRLists),
overDealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs, backlogNum = BacklogNum - 1})
end;
[MiRequest] ->
overDealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = [], backlogNum = BacklogNum - 1});
[MiRequest | Outs] ->
overDealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs, backlogNum = BacklogNum - 1})
end;
_ ->
Request = agHttpProtocol:request(IsSystem, Body, Method, Host, DbName, Path, [UserPassWord | Headers]),
case gen_tcp:send(Socket, Request) of
ok ->
TimerRef =
case OverTime of
infinity ->
undefined;
_ ->
erlang:start_timer(OverTime, self(), waiting_over, [{abs, true}])
end,
overReceiveTcpData(SrvState, CliState#cliState{isHeadMethod = Method == ?AgHead, status = waiting, curInfo = {FromPid, RequestId, TimerRef}});
{error, Reason} ->
?WARN(ServerName, ":send error: ~p~n", [Reason]),
gen_tcp:close(Socket),
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, socketSendError}),
agAgencyUtils:dealClose(SrvState, CliState, {error, socketSendError})
end
end.
-spec overReceiveTcpData(srvState(), cliState()) -> {ok, srvState(), cliState()}.
overReceiveTcpData(#srvState{poolName = PoolName, serverName = ServerName, rn = Rn, rnrn = RnRn, socket = Socket} = SrvState,
#cliState{isHeadMethod = IsHeadMethod, backlogNum = BacklogNum, curInfo = CurInfo, requestsIns = RequestsIns, requestsOuts = RequestsOuts, recvState = RecvState} = CliState) ->
receive
{tcp, Socket, Data} ->
try agHttpProtocol:response(RecvState, Rn, RnRn, Data, IsHeadMethod) of
{done, #recvState{statusCode = StatusCode, headers = Headers, body = Body}} ->
agAgencyUtils:agencyReply(CurInfo, {StatusCode, Body, Headers}),
case RequestsOuts of
[] ->
case RequestsIns of
[] ->
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}};
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined});
MiRLists ->
[MiRequest | Outs] = lists:reverse(MiRLists),
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs, backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined})
end;
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = [], backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined});
[MiRequest | Outs] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs, backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined})
end;
{ok, NewRecvState} ->
overReceiveTcpData(SrvState, CliState#cliState{recvState = NewRecvState});
{error, Reason} ->
?WARN(overReceiveTcpData, "handle tcp data error: ~p ~n", [Reason]),
gen_tcp:close(Socket),
agAgencyUtils:dealClose(SrvState, CliState, {error, {tcpDataError, Reason}})
catch
E:R:S ->
?WARN(overReceiveTcpData, "handle tcp data crash: ~p:~p~n~p ~n ", [E, R, S]),
gen_tcp:close(Socket),
agAgencyUtils:dealClose(SrvState, CliState, {error, {tcp_error, handledataError}})
end;
{timeout, TimerRef, waiting_over} ->
case CurInfo of
{_PidForm, _RequestId, TimerRef} ->
gen_tcp:close(Socket),
agAgencyUtils:agencyReply(CurInfo, {error, timeout}),
case RequestsOuts of
[] ->
case RequestsIns of
[] ->
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1, status = leisure, curInfo = undefined, recvState = undefined}};
[MiRequest] ->
case ?agBeamPool:getv(PoolName) of
#dbOpts{port = Port, hostname = HostName, socketOpts = SocketOpts} ->
case dealConnect(ServerName, HostName, Port, SocketOpts) of
{ok, NewSocket} ->
overDealQueueRequest(MiRequest, SrvState#srvState{socket = NewSocket}, CliState#cliState{requestsIns = [], status = leisure, curInfo = undefined, recvState = undefined});
{error, _Reason} ->
agAgencyUtils:dealClose(SrvState, CliState, {error, {newTcpConnectErrorOver, _Reason}})
end;
_Ret ->
agAgencyUtils:dealClose(SrvState, CliState, {error, {notFoundPoolName, PoolName}})
end;
MiRLists ->
[MiRequest | Outs] = lists:reverse(MiRLists),
case ?agBeamPool:getv(PoolName) of
#dbOpts{port = Port, hostname = HostName, socketOpts = SocketOpts} ->
case dealConnect(ServerName, HostName, Port, SocketOpts) of
{ok, NewSocket} ->
overDealQueueRequest(MiRequest, SrvState#srvState{socket = NewSocket}, CliState#cliState{requestsIns = [], requestsOuts = Outs, status = leisure, curInfo = undefined, recvState = undefined});
{error, _Reason} ->
agAgencyUtils:dealClose(SrvState, CliState, {error, {newTcpConnectErrorOver, _Reason}})
end;
_Ret ->
agAgencyUtils:dealClose(SrvState, CliState, {error, {notFoundPoolName, PoolName}})
end
end;
[MiRequest] ->
case ?agBeamPool:getv(PoolName) of
#dbOpts{port = Port, hostname = HostName, socketOpts = SocketOpts} ->
case dealConnect(ServerName, HostName, Port, SocketOpts) of
{ok, NewSocket} ->
overDealQueueRequest(MiRequest, SrvState#srvState{socket = NewSocket}, CliState#cliState{requestsOuts = [], status = leisure, curInfo = undefined, recvState = undefined});
{error, _Reason} ->
agAgencyUtils:dealClose(SrvState, CliState, {error, {newTcpConnectErrorOver, _Reason}})
end;
_Ret ->
agAgencyUtils:dealClose(SrvState, CliState, {error, {notFoundPoolName, PoolName}})
end;
[MiRequest | Outs] ->
case ?agBeamPool:getv(PoolName) of
#dbOpts{port = Port, hostname = HostName, socketOpts = SocketOpts} ->
case dealConnect(ServerName, HostName, Port, SocketOpts) of
{ok, NewSocket} ->
overDealQueueRequest(MiRequest, SrvState#srvState{socket = NewSocket}, CliState#cliState{requestsOuts = Outs, status = leisure, curInfo = undefined, recvState = undefined});
{error, _Reason} ->
agAgencyUtils:dealClose(SrvState, CliState, {error, {newTcpConnectErrorOver, _Reason}})
end;
_Ret ->
agAgencyUtils:dealClose(SrvState, CliState, {error, {notFoundPoolName, PoolName}})
end
end;
_ ->
?WARN(overReceiveTcpData, "receive waiting_over TimerRef not match: ~p~n", [TimerRef]),
overReceiveTcpData(SrvState, CliState)
end;
{tcp_closed, Socket} ->
gen_tcp:close(Socket),
agAgencyUtils:dealClose(SrvState, CliState, {error, tcp_closed});
{tcp_error, Socket, Reason} ->
gen_tcp:close(Socket),
agAgencyUtils:dealClose(SrvState, CliState, {error, {tcp_error, Reason}});
#miRequest{} = MiRequest ->
overReceiveTcpData(SrvState, CliState#cliState{requestsIns = [MiRequest | RequestsIns], backlogNum = BacklogNum + 1});
_Msg ->
?WARN(overReceiveTcpData, "receive unexpect msg: ~p~n", [_Msg]),
overReceiveTcpData(SrvState, CliState)
end.
-spec dealConnect(atom(), hostName(), port(), socketOpts()) -> {ok, socket()} | {error, term()}.
dealConnect(ServerName, HostName, Port, SocketOptions) ->
case inet:getaddrs(HostName, inet) of
{ok, IPList} ->
Ip = agMiscUtils:randomElement(IPList),
case gen_tcp:connect(Ip, Port, SocketOptions, ?DEFAULT_CONNECT_TIMEOUT) of
{ok, Socket} ->
{ok, Socket};
{error, Reason} ->
?WARN(ServerName, "connect error: ~p~n", [Reason]),
{error, Reason}
end;
{error, Reason} ->
?WARN(ServerName, "getaddrs error: ~p~n", [Reason]),
{error, Reason}
end.
-spec dealQueueRequest(miRequest(), srvState(), cliState()) -> {ok, srvState(), cliState()}.
dealQueueRequest(#miRequest{method = Method, path = Path, headers = Headers, body = Body, requestId = RequestId, fromPid = FromPid, overTime = OverTime, isSystem = IsSystem},
#srvState{serverName = ServerName, host = Host, userPassWord = UserPassWord, dbName = DbName, socket = Socket} = SrvState,
#cliState{requestsIns = RequestsIns, requestsOuts = RequestsOuts, backlogNum = BacklogNum} = CliState) ->
case erlang:monotonic_time(millisecond) > OverTime of
true ->
%%
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, timeout}),
case RequestsOuts of
[] ->
case RequestsIns of
[] ->
{ok, SrvState, CliState#cliState{backlogNum = BacklogNum - 1}};
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], backlogNum = BacklogNum - 1});
MiRLists ->
[MiRequest | Outs] = lists:reverse(MiRLists),
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsIns = [], requestsOuts = Outs, backlogNum = BacklogNum - 1})
end;
[MiRequest] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = [], backlogNum = BacklogNum - 1});
[MiRequest | Outs] ->
dealQueueRequest(MiRequest, SrvState, CliState#cliState{requestsOuts = Outs, backlogNum = BacklogNum - 1})
end;
_ ->
Request = agHttpProtocol:request(IsSystem, Body, Method, Host, DbName, Path, [UserPassWord | Headers]),
case gen_tcp:send(Socket, Request) of
ok ->
TimerRef =
case OverTime of
infinity ->
undefined;
_ ->
erlang:start_timer(OverTime, self(), waiting_over, [{abs, true}])
end,
{ok, SrvState, CliState#cliState{isHeadMethod = Method == ?AgHead, status = waiting, curInfo = {FromPid, RequestId, TimerRef}}};
{error, Reason} ->
?WARN(ServerName, ":send error: ~p~n", [Reason]),
gen_tcp:close(Socket),
agAgencyUtils:agencyReply(FromPid, RequestId, undefined, {error, socketSendError}),
agAgencyUtils:dealClose(SrvState, CliState, {error, socketSendError})
end
end.

+ 438
- 0
src/agHttpCli/vst.erl View File

@ -0,0 +1,438 @@
-module(vst).
-export([
authorize/1,
connect/2,
request/2,
vst_maxsize/0
]).
assign_map(_acc@1, _key@1, _value@1) ->
case _acc@1 of
#{_key@1 := _} -> _acc@1;
#{} -> _acc@1#{_key@1 => _value@1};
_ -> #{_key@1 => _value@1}
end.
assign_split([<<>>, _rest@1], _value@1, _acc@1, _pattern@1) ->
_parts@1 = binary:split(_rest@1, _pattern@1),
case _acc@1 of
[_ | _] ->
[assign_split(_parts@1, _value@1, none, _pattern@1) | _acc@1];
none ->
[assign_split(_parts@1, _value@1, none, _pattern@1)];
_ -> _acc@1
end;
assign_split([_key@1, _rest@1], _value@1, _acc@1, _pattern@1) ->
_parts@1 = binary:split(_rest@1, _pattern@1),
case _acc@1 of
#{_key@1 := _current@1} ->
_acc@1#{_key@1 =>
assign_split(_parts@1, _value@1, _current@1, _pattern@1)};
#{} ->
_acc@1#{_key@1 => assign_split(_parts@1, _value@1, none, _pattern@1)};
_ ->
#{_key@1 => assign_split(_parts@1, _value@1, none, _pattern@1)}
end;
assign_split([<<>>], nil, _acc@1, __pattern@1) ->
case _acc@1 of
[_ | _] -> _acc@1;
_ -> []
end;
assign_split([<<>>], _value@1, _acc@1, __pattern@1) ->
case _acc@1 of
[_ | _] -> [_value@1 | _acc@1];
none -> [_value@1];
_ -> _acc@1
end;
assign_split([_key@1], _value@1, _acc@1, __pattern@1) ->
assign_map(_acc@1, _key@1, _value@1).
authorize(#{socket := _socket@1, username := _un@1, password := _pw@1} = _state@1) ->
case eVPack:encode([1, 1000, <<"plain">>, _un@1, _pw@1]) of
{ok, _auth@1} ->
case send_stream(_socket@1, build_stream(_auth@1)) of
ok ->
case recv_header(_socket@1) of
{ok, _header@1} ->
case recv_stream(_socket@1, _header@1) of
{ok, _stream@1} ->
case decode_stream(_stream@1) of
{ok, [[1, 2, 200, __headers@2] | __body@1]} ->
ok;
_@1 ->
case _@1 of
{ok, [[1, 2, _status@1, __headers@1], _body@1 | _]} ->
{error, #{
'__exception__' => true,
error_num => nil,
status => _status@1,
message => proplists:get_value(<<"errorMessage">>, _body@1),
endpoint =>
case _state@1 of
#{endpoint := _@3} ->
_@3;
_@3 when erlang:is_map(_@3) ->
erlang:error({badkey, endpoint, _@3});
_@3 ->
_@3:endpoint()
end}};
{error, _reason@1} ->
{error, _reason@1};
_@2 ->
erlang:error({with_clause, _@2})
end
end;
_@1 ->
case _@1 of
{ok,
[[1, 2, _status@1, __headers@1], _body@1 | _]} ->
{error, #{
'__exception__' => true,
error_num => nil,
status => _status@1,
message => proplists:get_value(<<"errorMessage">>, _body@1),
endpoint =>
case _state@1 of
#{endpoint := _@3} ->
_@3;
_@3 when erlang:is_map(_@3) ->
erlang:error({badkey, endpoint, _@3});
_@3 -> _@3:endpoint()
end}};
{error, _reason@1} ->
{error, _reason@1};
_@2 -> erlang:error({with_clause, _@2})
end
end;
_@1 ->
case _@1 of
{ok,
[[1, 2, _status@1, __headers@1], _body@1
| _]} ->
{error, #{
'__exception__' => true,
error_num => nil, status => _status@1,
message => proplists:get_value(<<"errorMessage">>, _body@1),
endpoint =>
case _state@1 of
#{endpoint := _@3} -> _@3;
_@3 when erlang:is_map(_@3) ->
erlang:error({badkey,
endpoint,
_@3});
_@3 -> _@3:endpoint()
end}};
{error, _reason@1} -> {error, _reason@1};
_@2 -> erlang:error({with_clause, _@2})
end
end;
_@1 ->
case _@1 of
{ok, [[1, 2, _status@1, __headers@1], _body@1 | _]} ->
{error,
#{
'__exception__' => true, error_num => nil,
status => _status@1,
message => proplists:get_value(<<"errorMessage">>, _body@1),
endpoint =>
case _state@1 of
#{endpoint := _@3} -> _@3;
_@3 when erlang:is_map(_@3) ->
erlang:error({badkey,
endpoint,
_@3});
_@3 -> _@3:endpoint()
end}};
{error, _reason@1} -> {error, _reason@1};
_@2 -> erlang:error({with_clause, _@2})
end
end;
_@1 ->
case _@1 of
{ok, [[1, 2, _status@1, __headers@1], _body@1 | _]} ->
{error, #{
'__exception__' => true,
error_num => nil,
status => _status@1,
message => proplists:get_value(<<"errorMessage">>, _body@1),
endpoint =>
case _state@1 of
#{endpoint := _@3} -> _@3;
_@3 when erlang:is_map(_@3) ->
erlang:error({badkey, endpoint, _@3});
_@3 -> _@3:endpoint()
end}};
{error, _reason@1} -> {error, _reason@1};
_@2 -> erlang:error({with_clause, _@2})
end
end.
body_for(<<>>) -> {ok, <<>>};
body_for(_body@1) ->
eVPack:encode(_body@1).
body_from([]) -> nil;
body_from([_body@1]) -> _body@1;
body_from(_body@1) -> _body@1.
build_stream(_message@1) ->
case chunk_every(_message@1, 30696) of
[_first_chunk@1 | _rest_chunks@1] ->
_n_chunks@1 = erlang:length([_first_chunk@1
| _rest_chunks@1]),
_msg_length@1 = erlang:byte_size(_message@1) +
_n_chunks@1 * 24,
_rest_chunks@2 =
lists:reverse(lists:flodl(
fun(_n@1, _@1) ->
case _rest_chunks@1 /= [] of
true ->
[prepend_chunk(lists:nth(_n@1, _rest_chunks@1), _n@1, 0, 0, _msg_length@1) | _@1];
false -> _@1
end
end, [], lists:seq(1, erlang:length(_rest_chunks@1)))),
[prepend_chunk(_first_chunk@1, _n_chunks@1, 1, 0, _msg_length@1) | _rest_chunks@2];
_only_chunk@1 ->
prepend_chunk(_only_chunk@1, 1, 1, 0, erlang:byte_size(_message@1) + 24)
end.
chunk_every(_bytes@1, _size@1) when erlang:byte_size(_bytes@1) =< _size@1 ->
_bytes@1;
chunk_every(_bytes@1, _size@1) ->
<<_chunk@1:_size@1/binary, _rest@1/binary>> = _bytes@1,
[_chunk@1 | (chunk_every(_rest@1, _size@1))].
connect(#{addr := _addr@1, 'ssl?' := _ssl}, _opts@1) ->
_mod@1 = case _ssl of
_@1 when _@1 =:= false orelse _@1 =:= nil -> gen_tcp;
_ -> ssl
end,
_transport_opts@1 = case _ssl of
_@2 when _@2 =:= false orelse _@2 =:= nil ->
tcp_opts;
_ -> ssl_opts
end,
_transport_opts@2 = proplists:get_value(_transport_opts@1, _opts@1, []),
_connect_timeout@1 = proplists:get(connect_timeout, _opts@1, 5000),
_options@1 = lists:merge(_transport_opts@2, [{packet, raw}, {mode, binary}, {active, false}]),
case _mod@1:connect(addr_for(_addr@1), port_for(_addr@1), _options@1, _connect_timeout@1) of
{ok, _port@1} ->
case _mod@1:send(_port@1, <<"VST/1.1\r\n\r\n">>) of
ok -> {ok, {_mod@1, _port@1}};
_@3 -> _@3
end;
_@3 -> _@3
end.
decode_pair({_key@1, _value@1}, _acc@1) ->
case case _key@1 /= <<>> of
false -> false;
true -> binary:last(_key@1) == 93
end
of
false -> assign_map(_acc@1, _key@1, _value@1);
true ->
_subkey@1 = binary:part(_key@1,
0,
erlang:byte_size(_key@1) - 1),
assign_split(binary:split(_subkey@1, <<"[">>),
_value@1,
_acc@1,
binary:compile_pattern(<<"][">>))
end.
decode_stream(_@1) -> decode_stream(_@1, []).
decode_stream(<<>>, _acc@1) -> {ok, _acc@1};
decode_stream(_stream@1, _acc@1) ->
case eVPack:decode(_stream@1) of
{ok, {_term@1, _rest@1}} ->
decode_stream(_rest@1, erlang:'++'(_acc@1, [_term@1]));
{ok, _term@2} -> {ok, erlang:'++'(_acc@1, [_term@2])};
{error, _reason@1} -> {error, _reason@1}
end.
headers_for(#{} = _headers@1) -> _headers@1;
headers_for(_headers@1)
when erlang:is_list(_headers@1) ->
maps:from_list(_headers@1).
method_for(delete) -> 0;
method_for(get) -> 1;
method_for(post) -> 2;
method_for(put) -> 3;
method_for(head) -> 4;
method_for(patch) -> 5;
method_for(options) -> 6;
method_for(_) -> -1.
port_for({unix, __path@1}) -> 0;
port_for({tcp, __host@1, _port@1}) -> _port@1.
prepend_chunk(_chunk@1, _chunk_n@1, _is_first@1,
_msg_id@1, _msg_length@1) ->
<<(24 + erlang:byte_size(_chunk@1)):32/integer-little,
(binary:decode_unsigned(<<_chunk_n@1:31/integer,
_is_first@1:1/integer>>,
little)):32/integer,
_msg_id@1:64/integer-little,
_msg_length@1:64/integer-little, _chunk@1/binary>>.
query_for(nil) -> #{}.
recv_chunk({_mod@1, _port@1}, _chunk_length@1) ->
_mod@1:recv(_port@1, _chunk_length@1 - 24).
recv_header({_mod@1, _port@1}) ->
case _mod@1:recv(_port@1, 24) of
{ok,
<<_chunk_length@1:32/integer-little,
_chunk_x@1:32/integer, _msg_id@1:64/integer-little,
_msg_length@1:64/integer-little>>} ->
<<_chunk_n@1:31/integer, _is_first@1:1/integer>> =
<<_chunk_x@1:32/integer-little>>,
{ok,
[_chunk_length@1,
_chunk_n@1,
_is_first@1,
_msg_id@1,
_msg_length@1]};
{error, _reason@1} -> {error, _reason@1}
end.
recv_stream(_socket@1,
[_chunk_length@1, 1, 1, __msg_id@1, __msg_length@1]) ->
recv_chunk(_socket@1, _chunk_length@1);
recv_stream(_socket@1,
[_chunk_length@1,
_n_chunks@1,
1,
__msg_id@1,
__msg_length@1]) ->
case recv_chunk(_socket@1, _chunk_length@1) of
{ok, _buffer@1} ->
case recv_stream(_socket@1, _n_chunks@1, _buffer@1) of
{ok, _stream@1} -> {ok, _stream@1};
_@1 -> _@1
end;
_@1 -> _@1
end.
recv_stream(_socket@1, _n_chunks@1, _buffer@1) ->
lists:reduce_while(lists:seq(1, _n_chunks@1 - 1),
_buffer@1,
fun(_n@1, _buffer@2) ->
case recv_header(_socket@1) of
{ok, [_chunk_length@1, _, _, _, _]} ->
case recv_chunk(_socket@1, _chunk_length@1) of
{ok, _chunk@1} ->
case _n@1 == _n_chunks@1 - 1 of
false ->
{cont, <<_buffer@2/binary, _chunk@1/binary>>};
true ->
{halt, {ok, <<_buffer@2/binary, _chunk@1/binary>>}}
end;
_@1 ->
case _@1 of
{error, _reason@1} ->
{halt, {error, _reason@1}};
_@2 ->
erlang:error({with_clause, _@2})
end
end;
_@1 ->
case _@1 of
{error, _reason@1} ->
{halt, {error, _reason@1}};
_@2 ->
erlang:error({with_clause, _@2})
end
end
end).
request(#{method := _method@1, path := _path@1, headers := _headers@1, body := _body@1}, #{socket := _socket@1, database := _database@1} = _state@1) ->
#{path := _path@2, query := _query@1} = http_uri:parse(_path@1),
{_database@3, _path@4} =
case _path@2 of
<<"/_db/", _rest@1/binary>> ->
[_database@2, _path@3] = binary:split(_rest@1, <<"/">>),
{_database@2, <<"/", _path@3/binary>>};
_ ->
{case _database@1 of
_@1 when _@1 =:= false orelse _@1 =:= nil ->
<<>>;
_@2 -> _@2
end, _path@2}
end,
_request@1 = [1, 1, _database@3, method_for(_method@1), _path@4, query_for(_query@1), headers_for(_headers@1)],
case eVPack:encode(_request@1) of
{ok, _request@2} ->
case body_for(_body@1) of
{ok, _body@2} ->
case send_stream(_socket@1, build_stream(<<_request@2/binary, _body@2/binary>>))
of
ok ->
case recv_header(_socket@1) of
{ok, _header@1} ->
case recv_stream(_socket@1, _header@1) of
{ok, _stream@1} ->
case decode_stream(_stream@1) of
{ok, [[1, 2, _status@1, _headers@2] | _body@3]} ->
{ok, #{status => _status@1, headers => _headers@2, body => body_from(_body@3)}, _state@1};
_@3 ->
case _@3 of
{error, closed} ->
{error, noproc, _state@1};
{error, _reason@1} ->
{error, _reason@1, _state@1};
_@4 ->
erlang:error({with_clause, _@4})
end
end;
_@3 ->
case _@3 of
{error, closed} ->
{error, noproc, _state@1};
{error, _reason@1} ->
{error, _reason@1, _state@1};
_@4 ->
erlang:error({with_clause, _@4})
end
end;
_@3 ->
case _@3 of
{error, closed} ->
{error, noproc, _state@1};
{error, _reason@1} ->
{error, _reason@1, _state@1};
_@4 -> erlang:error({with_clause, _@4})
end
end;
_@3 ->
case _@3 of
{error, closed} -> {error, noproc, _state@1};
{error, _reason@1} ->
{error, _reason@1, _state@1};
_@4 -> erlang:error({with_clause, _@4})
end
end;
_@3 ->
case _@3 of
{error, closed} -> {error, noproc, _state@1};
{error, _reason@1} -> {error, _reason@1, _state@1};
_@4 -> erlang:error({with_clause, _@4})
end
end;
_@3 ->
case _@3 of
{error, closed} -> {error, noproc, _state@1};
{error, _reason@1} -> {error, _reason@1, _state@1};
_@4 -> erlang:error({with_clause, _@4})
end
end.
send_stream(Socket, IoData) ->
tcp:send(Socket, IoData).
vst_maxsize() -> 30720.

+ 11
- 0
src/erlArango.app.src View File

@ -0,0 +1,11 @@
{application, erlArango,
[{description, "An OTP application"},
{vsn, "0.1.0"},
{registered, []},
{mod, {erlArango_app, []}},
{applications, [kernel, stdlib, jiffy]},
{env, []},
{modules, []},
{licenses, ["MIT License"]},
{links, []}
]}.

+ 11
- 0
src/erlArango_app.erl View File

@ -0,0 +1,11 @@
-module(erlArango_app).
-behaviour(application).
-export([start/2, stop/1]).
start(_StartType, _StartArgs) ->
erlArango_sup:start_link().
stop(_State) ->
ok.

+ 30
- 0
src/erlArango_sup.erl View File

@ -0,0 +1,30 @@
-module(erlArango_sup).
-include("agHttpCli.hrl").
-include("erlArango.hrl").
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1]).
-define(SERVER, ?MODULE).
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
%% sup_flags() = #{strategy => strategy(), % optional
%% intensity => non_neg_integer(), % optional
%% period => pos_integer()} % optional
%% child_spec() = #{id => child_id(), % mandatory
%% start => mfargs(), % mandatory
%% restart => restart(), % optional
%% shutdown => shutdown(), % optional
%% type => worker(), % optional
%% modules => modules()} % optional
init([]) ->
SupFlags = #{strategy => one_for_one, intensity => 100, period => 3600},
PoolMgrSpec = #{id => agAgencyPoolMgrExm, start => {agAgencyPoolMgrExm, start_link, [?agAgencyPoolMgr, [], []]}, restart => permanent, shutdown => infinity, type => worker, modules => [agAgencyPoolMgrExm]},
HttpCliSupSpec = #{id => agAgencyPool_sup, start => {agAgencyPool_sup, start_link, []}, restart => permanent, shutdown => infinity, type => supervisor, modules => [agAgencyPool_sup]},
{ok, {SupFlags, [PoolMgrSpec, HttpCliSupSpec]}}.

+ 155
- 0
src/user_default.erl View File

@ -0,0 +1,155 @@
-module(user_default).
-include("agHttpCli.hrl").
-compile([export_all, nowarn_export_all]).
start() ->
erlSync:run(),
application:ensure_all_started(erlArango),
agHttpCli:startPool(tt, [{poolSize, 10}], []).
tt(C, N) ->
application:ensure_all_started(erlArango),
agHttpCli:startPool(tt, [{poolSize, 16}], []),
StartTime = erlang:system_time(millisecond),
io:format("IMY********************** started~n"),
[spawn(?MODULE, test, [N, StartTime]) || _Idx <- lists:seq(1, C)].
%%test(N, Request).
%% /_api/database
test(0, StartTime) ->
agMiscFuns:curDbTime(tt),
io:format("IMY******test over use time ~pms~n", [erlang:system_time(millisecond) - StartTime]);
test(N, StartTime) ->
agMiscFuns:curDbTime(tt),
test(N - 1, StartTime).
%% tt(C, N) ->
%% application:start(erlArango),
%% agHttpCli:startPool(tt, [{poolSize, 1}, {baseUrl, <<"http://localhost:8181">>}], []),
%% Request = {<<"GET">>, <<"/_api/database/current">>, [], []},
%% io:format("IMY********************** start time ~p~n",[erlang:system_time(millisecond)]),
%% [spawn(test, test, [N, Request]) || _Idx <- lists:seq(1, C)].
%% %%test(N, Request).
%%
%% %% /_api/database
%%
%% test(0, Request) ->
%% R1 = {<<"POST">>, <<"/echo_body">>, [], []},
%% agHttpCli:callAgency(tt, {<<"GET">>, <<"/ibrowse_stream_once_chunk_pipeline_test">>, [], []}, infinity),
%% agHttpCli:callAgency(tt, {<<"POST">>, <<"/echo_body">>, [], []}, infinity),
%% io:format("IMY********************** test over ~p~n",[erlang:system_time(millisecond)]);
%% test(N, Request) ->
%% erlang:put(cnt, N),
%% agHttpCli:callAgency(tt, Request, 5000),
%% test(N - 1, Request).
-define(HeadBin, <<"X-Content-Type-Options: nosniff\r\nEtag: \"_aKwJ_tm--E\"\r\nServer: ArangoDB\r\nConnection: Keep-Alive\r\nContent-Type: application/json; charset=utf-8\r\nContent-Length: 178">>).
th1(0, Fun, Rn) ->
?MODULE:Fun(?HeadBin, Rn);
th1(N, Fun, Rn) ->
?MODULE:Fun(?HeadBin, Rn),
th1(N - 1, Fun, Rn).
th2(0, Fun, Cl, Rn) ->
?MODULE:Fun(?HeadBin, Cl, Rn);
th2(N, Fun, Cl, Rn) ->
?MODULE:Fun(?HeadBin, Cl, Rn),
th2(N - 1, Fun, Cl, Rn).
head1(Headers, Rn) ->
HeadersList = binary:split(Headers, Rn, [global]),
contentLength(HeadersList).
head2(Headers, _Rn) ->
HeadersList = binary:split(Headers, <<"\r\n">>, [global]),
contentLength(HeadersList).
head3(Headers, CL, Rn) ->
case binary:split(Headers, CL) of
[_, Rest1] ->
case binary:split(Rest1, Rn) of
[InBin, _Rest2] ->
binary_to_integer(InBin);
[InBin] ->
binary_to_integer(InBin)
end;
_ ->
0
end.
%% binary:compile_pattern(<<"\r\n">>)
%% binary:compile_pattern(<<"Content-Length: ">>)
head4(Headers, _CL, _Rn) ->
case binary:split(Headers, <<"Content-Length: ">>) of
[_, Rest1] ->
case binary:split(Rest1, <<"\r\n">>) of
[InBin, _Rest2] ->
binary_to_integer(InBin);
[InBin] ->
binary_to_integer(InBin)
end;
_ ->
0
end.
contentLength([]) ->
undefined;
contentLength([<<"Content-Length: ", Rest/binary>> | _T]) ->
binary_to_integer(Rest);
contentLength([<<"content-length: ", Rest/binary>> | _T]) ->
binary_to_integer(Rest);
contentLength([<<"Transfer-Encoding: chunked">> | _T]) ->
chunked;
contentLength([<<"transfer-encoding: chunked">> | _T]) ->
chunked;
contentLength([_ | T]) ->
contentLength(T).
%% jiffy jsx
tcjf(0, _Args1) ->
Args = #{name => ffd, tet => "fdsff", <<"dfdf">> => 131245435346},
jiffy:encode(Args);
tcjf(N, Args1) ->
Args = #{name => ffd, tet => "fdsff", <<"dfdf">> => 131245435346},
jiffy:encode(Args),
tcjf(N - 1, Args1).
tcjx(0, _Args1) ->
Args = {[{name, ffd}, {tet, "fdsff"}, {<<"dfdf">>, 131245435346}]},
jiffy:encode(Args);
tcjx(N, Args1) ->
Args = {[{name, ffd}, {tet, "fdsff"}, {<<"dfdf">>, 131245435346}]},
jiffy:encode(Args),
tcjx(N - 1, Args1).
-define(BodyBin1, <<"{\"_key\":\"01J\",\"_id\":\"airports/01J\",\"_rev\":\"_aKwJ_tm--E\",\"name\":\"Hilliard Airpark\",\"city\":\"Hilliard\",\"state\":\"FL\",\"country\":\"USA\",\"lat\":30.6880125,\"long\":-81.90594389,\"vip\":false}">>).
-define(BodyBin2, <<"{\"_key\":\"01J\",\"_id\":\"airports/01J\",\"_rev\":\"_aPaBl7O--_\",\"name\":\"Hilliard Airpark\",\"city\":\"Hilliardfdfsdfdsffffffffffffffffffffffffffffffffffffffffffffffffffffffffafdsfasdfdafsdafdsfsdafdsafdsfdsfdsafdsfdsfdsfhghfghfghgfhsdsdfdsfdsfdsffdfddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddggggggggggggggggggggggggggggggggggggggggg\",\"state\":\"FL\",\"country\":\"USAjjkjkjkfgjkgjfkdjgldgjldjglfdjglfjdljljrlejtrltjewltjrelwtjrletjrletrletjlrejtjtrlwjrejwlrjjreljtljelwjrtlwjtreljrlewjrlwjrlwejrlejltkdfsafd\",\"lat\":30.6880125,\"long\":-81.90594389,\"vip\":false}">>).
jd1(0, Fun) ->
?MODULE:Fun(?BodyBin1);
jd1(N, Fun) ->
?MODULE:Fun(?BodyBin1),
jd1(N - 1, Fun).
jd2(0, Fun) ->
?MODULE:Fun(?BodyBin2);
jd2(N, Fun) ->
?MODULE:Fun(?BodyBin2),
jd2(N - 1, Fun).
decodeJy1(Bin) ->
jiffy:decode(Bin, [return_maps]).
decodeJy2(Bin) ->
jiffy:decode(Bin, [return_maps, copy_strings]).
decodeJx1(Bin) ->
jsx:decode(Bin, [return_maps]).
decodeJx2(Bin) ->
jsx:decode(Bin, []).

+ 1
- 0
start.sh View File

@ -0,0 +1 @@
erl -sname arango -setcookie 123 -pa ./ebin -pa ./deps/*/ebin

Loading…
Cancel
Save