001package com.box.sdk;
002
003import java.net.MalformedURLException;
004import java.net.URL;
005import java.util.Date;
006import java.util.Iterator;
007import java.util.LinkedHashSet;
008import java.util.Set;
009
010import com.eclipsesource.json.JsonArray;
011import com.eclipsesource.json.JsonObject;
012import com.eclipsesource.json.JsonValue;
013
014/**
015 * A log of events that were retrieved from the events endpoint.
016 *
017 * <p>An EventLog cannot be instantiated directly. Instead, use one of the static methods to retrieve a log of events.
018 * Unlike the {@link EventStream} class, EventLog doesn't support retrieving events in real-time.
019 * </p>
020 */
021public class EventLog implements Iterable<BoxEvent> {
022    private static final int ENTERPRISE_LIMIT = 500;
023    private static final URLTemplate ENTERPRISE_EVENT_URL_TEMPLATE = new URLTemplate("events?stream_type=admin_logs&"
024        + "limit=" + ENTERPRISE_LIMIT + "&created_after=%s&created_before=%s");
025
026    private final int chunkSize;
027    private final int limit;
028    private final String nextStreamPosition;
029    private final String streamPosition;
030    private final Set<BoxEvent> set;
031
032    private Date startDate;
033    private Date endDate;
034
035    EventLog(BoxAPIConnection api, JsonObject json, String streamPosition, int limit) {
036        this.streamPosition = streamPosition;
037        this.limit = limit;
038        this.nextStreamPosition = json.get("next_stream_position").asString();
039        this.chunkSize = json.get("chunk_size").asInt();
040
041        this.set = new LinkedHashSet<BoxEvent>(this.chunkSize);
042        JsonArray entries = json.get("entries").asArray();
043        for (JsonValue entry : entries) {
044            this.set.add(new BoxEvent(api, entry.asObject()));
045        }
046    }
047
048    /**
049     * Gets all the enterprise events that occurred within a specified date range.
050     * @param  api    the API connection to use.
051     * @param  after  the lower bound on the timestamp of the events returned.
052     * @param  before the upper bound on the timestamp of the events returned.
053     * @param  types  an optional list of event types to filter by.
054     * @return        a log of all the events that met the given criteria.
055     */
056    public static EventLog getEnterpriseEvents(BoxAPIConnection api, Date after, Date before, BoxEvent.Type... types) {
057        return getEnterpriseEvents(api, null, after, before, types);
058    }
059
060    /**
061     * Gets all the enterprise events that occurred within a specified date range, starting from a given position
062     * within the event stream.
063     * @param  api      the API connection to use.
064     * @param  position the starting position of the event stream.
065     * @param  after    the lower bound on the timestamp of the events returned.
066     * @param  before   the upper bound on the timestamp of the events returned.
067     * @param  types    an optional list of event types to filter by.
068     * @return          a log of all the events that met the given criteria.
069     */
070    public static EventLog getEnterpriseEvents(BoxAPIConnection api, String position, Date after, Date before,
071        BoxEvent.Type... types) {
072
073        String afterString = BoxDateFormat.format(after);
074        String beforeString = BoxDateFormat.format(before);
075        URL url = ENTERPRISE_EVENT_URL_TEMPLATE.build(api.getBaseURL(), afterString, beforeString);
076
077        if (position != null || types.length > 0) {
078            QueryStringBuilder queryBuilder = new QueryStringBuilder(url.getQuery());
079
080            if (position != null) {
081                queryBuilder.appendParam("stream_position", position);
082            }
083
084            if (types.length > 0) {
085                StringBuilder filterBuilder = new StringBuilder();
086                for (BoxEvent.Type filterType : types) {
087                    filterBuilder.append(filterType.name());
088                    filterBuilder.append(',');
089                }
090                filterBuilder.deleteCharAt(filterBuilder.length() - 1);
091                queryBuilder.appendParam("event_type", filterBuilder.toString());
092            }
093
094            try {
095                url = queryBuilder.addToURL(url);
096            } catch (MalformedURLException e) {
097                throw new BoxAPIException("Couldn't append a query string to the provided URL.");
098            }
099        }
100
101        BoxAPIRequest request = new BoxAPIRequest(api, url, "GET");
102        BoxJSONResponse response = (BoxJSONResponse) request.send();
103        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
104        EventLog log = new EventLog(api, responseJSON, position, ENTERPRISE_LIMIT);
105        log.setStartDate(after);
106        log.setEndDate(before);
107        return log;
108    }
109
110    void setStartDate(Date startDate) {
111        this.startDate = startDate;
112    }
113
114    void setEndDate(Date endDate) {
115        this.endDate = endDate;
116    }
117
118    /**
119     * Returns an iterator over the events in this log.
120     * @return an iterator over the events in this log.
121     */
122    @Override
123    public Iterator<BoxEvent> iterator() {
124        return this.set.iterator();
125    }
126
127    /**
128     * Gets the date of the earliest event in this log.
129     *
130     * <p>The value returned by this method corresponds to the <code>created_after</code> URL parameter that was used
131     * when retrieving the events in this EventLog.</p>
132     *
133     * @return the date of the earliest event in this log.
134     */
135    public Date getStartDate() {
136        return this.startDate;
137    }
138
139    /**
140     * Gets the date of the latest event in this log.
141     *
142     * <p>The value returned by this method corresponds to the <code>created_before</code> URL parameter that was used
143     * when retrieving the events in this EventLog.</p>
144     *
145     * @return the date of the latest event in this log.
146     */
147    public Date getEndDate() {
148        return this.endDate;
149    }
150
151    /**
152     * Gets the maximum number of events that this event log could contain given its start date, end date, and stream
153     * position.
154     *
155     * <p>The value returned by this method corresponds to the <code>limit</code> URL parameter that was used when
156     * retrieving the events in this EventLog.</p>
157     *
158     * @return the maximum number of events.
159     */
160    public int getLimit() {
161        return this.limit;
162    }
163
164    /**
165     * Gets the starting position of the events in this log within the event stream.
166     *
167     * <p>The value returned by this method corresponds to the <code>stream_position</code> URL parameter that was used
168     * when retrieving the events in this EventLog.</p>
169     *
170     * @return the starting position within the event stream.
171     */
172    public String getStreamPosition() {
173        return this.streamPosition;
174    }
175
176    /**
177     * Gets the next position within the event stream for retrieving subsequent events.
178     *
179     * <p>The value returned by this method corresponds to the <code>next_stream_position</code> field returned by the
180     * API's events endpoint.</p>
181     *
182     * @return the next position within the event stream.
183     */
184    public String getNextStreamPosition() {
185        return this.nextStreamPosition;
186    }
187
188    /**
189     * Gets the number of events in this log, including duplicate events.
190     *
191     * <p>The chunk size may not be representative of the number of events returned by this EventLog's iterator because
192     * the iterator will automatically ignore duplicate events.</p>
193     *
194     * <p>The value returned by this method corresponds to the <code>chunk_size</code> field returned by the API's
195     * events endpoint.</p>
196     *
197     * @return the number of events, including duplicates.
198     */
199    public int getChunkSize() {
200        return this.chunkSize;
201    }
202
203    /**
204     * Gets the number of events in this list, excluding duplicate events.
205     *
206     * <p>The size is guaranteed to be representative of the number of events returned by this EventLog's iterator.</p>
207     *
208     * @return the number of events, excluding duplicates.
209     */
210    public int getSize() {
211        return this.set.size();
212    }
213}