/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package keywhiz.service.daos;
import com.google.common.collect.ImmutableSet;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Optional;
import javax.inject.Inject;
import keywhiz.api.ApiDate;
import keywhiz.api.model.Client;
import keywhiz.jooq.tables.records.ClientsRecord;
import keywhiz.service.config.Readonly;
import org.jooq.Configuration;
import org.jooq.DSLContext;
import org.jooq.Param;
import org.jooq.impl.DSL;
import static com.google.common.base.Preconditions.checkNotNull;
import static java.time.temporal.ChronoUnit.SECONDS;
import static keywhiz.jooq.tables.Clients.CLIENTS;
import static keywhiz.jooq.tables.Memberships.MEMBERSHIPS;
public class ClientDAO {
private final static long lastSeenThreshold = (24 * 60 * 60);
private final DSLContext dslContext;
private final ClientMapper clientMapper;
private ClientDAO(DSLContext dslContext, ClientMapper clientMapper) {
this.dslContext = dslContext;
this.clientMapper = clientMapper;
}
public long createClient(String name, String user, String description) {
ClientsRecord r = dslContext.newRecord(CLIENTS);
long now = OffsetDateTime.now().toEpochSecond();
r.setName(name);
r.setCreatedby(user);
r.setCreatedat(now);
r.setUpdatedby(user);
r.setUpdatedat(now);
r.setLastseen(null);
r.setDescription(description);
r.setEnabled(true);
r.setAutomationallowed(false);
r.store();
return r.getId();
}
public void deleteClient(Client client) {
dslContext.transaction(configuration -> {
DSL.using(configuration)
.delete(CLIENTS)
.where(CLIENTS.ID.eq(client.getId()))
.execute();
DSL.using(configuration)
.delete(MEMBERSHIPS)
.where(MEMBERSHIPS.CLIENTID.eq(client.getId()))
.execute();
});
}
public void sawClient(Client client) {
Instant now = Instant.now();
Instant lastSeen = Optional.ofNullable(client.getLastSeen()).map(ls -> Instant.ofEpochSecond(ls.toEpochSecond())).orElse(null);
// only update last seen if it's been more than `lastSeenThreshold` seconds
// this way we can have less granularity on lastSeen and save db writes
if (lastSeen == null || now.isAfter(lastSeen.plus(lastSeenThreshold, SECONDS))) {
dslContext.transaction(configuration -> {
Param<Long> val = DSL.val(now.getEpochSecond(), CLIENTS.LASTSEEN);
DSL.using(configuration)
.update(CLIENTS)
.set(CLIENTS.LASTSEEN, DSL.when(CLIENTS.LASTSEEN.isNull(), val).otherwise(DSL.greatest(CLIENTS.LASTSEEN, val)))
.where(CLIENTS.ID.eq(client.getId()))
.execute();
});
}
}
public Optional<Client> getClient(String name) {
ClientsRecord r = dslContext.fetchOne(CLIENTS, CLIENTS.NAME.eq(name));
return Optional.ofNullable(r).map(clientMapper::map);
}
public Optional<Client> getClientById(long id) {
ClientsRecord r = dslContext.fetchOne(CLIENTS, CLIENTS.ID.eq(id));
return Optional.ofNullable(r).map(clientMapper::map);
}
public ImmutableSet<Client> getClients() {
List<Client> r = dslContext
.selectFrom(CLIENTS)
.fetch()
.map(clientMapper);
return ImmutableSet.copyOf(r);
}
public static class ClientDAOFactory implements DAOFactory<ClientDAO> {
private final DSLContext jooq;
private final DSLContext readonlyJooq;
private final ClientMapper clientMapper;
@Inject public ClientDAOFactory(DSLContext jooq, @Readonly DSLContext readonlyJooq,
ClientMapper clientMapper) {
this.jooq = jooq;
this.readonlyJooq = readonlyJooq;
this.clientMapper = clientMapper;
}
@Override public ClientDAO readwrite() {
return new ClientDAO(jooq, clientMapper);
}
@Override public ClientDAO readonly() {
return new ClientDAO(readonlyJooq, clientMapper);
}
@Override public ClientDAO using(Configuration configuration) {
DSLContext dslContext = DSL.using(checkNotNull(configuration));
return new ClientDAO(dslContext, clientMapper);
}
}
}